WebGLValidateStrings.cpp (4848B)
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "WebGLValidateStrings.h" 7 8 #include <regex> 9 10 #include "WebGLTypes.h" 11 #include "nsPrintfCString.h" 12 13 namespace mozilla { 14 15 /* GLSL ES 3.00 p17: 16 - Comments are delimited by / * and * /, or by // and a newline. 17 18 - '//' style comments include the initial '//' marker and continue up to, but 19 not including, the terminating newline. 20 21 - '/ * ... * /' comments include both the start and end marker. 22 23 - The begin comment delimiters (/ * or //) are not recognized as comment 24 delimiters inside of a comment, hence comments cannot be nested. 25 26 - Comments are treated syntactically as a single space. 27 */ 28 29 std::string CommentsToSpaces(const std::string& src) { 30 constexpr auto flags = 31 std::regex::ECMAScript | std::regex::nosubs | std::regex::optimize; 32 33 static const auto RE_COMMENT_BEGIN = std::regex("/[*/]", flags); 34 static const auto RE_LINE_COMMENT_END = std::regex(R"([^\\]\n)", flags); 35 static const auto RE_BLOCK_COMMENT_END = std::regex(R"(\*/)", flags); 36 37 std::string ret; 38 ret.reserve(src.size()); 39 40 // Replace all comments with block comments with the right number of newlines. 41 // Line positions may be off, but line numbers will be accurate, which is more 42 // important. 43 44 auto itr = src.begin(); 45 const auto end = src.end(); 46 std::smatch match; 47 while (std::regex_search(itr, end, match, RE_COMMENT_BEGIN)) { 48 MOZ_ASSERT(match.length() == 2); 49 const auto commentBegin = itr + match.position(); 50 ret.append(itr, commentBegin); 51 52 itr = commentBegin + match.length(); 53 54 const bool isBlockComment = (*(commentBegin + 1) == '*'); 55 const auto* endRegex = &RE_LINE_COMMENT_END; 56 if (isBlockComment) { 57 endRegex = &RE_BLOCK_COMMENT_END; 58 } 59 60 if (isBlockComment) { 61 ret += "/*"; 62 } 63 64 auto commentEnd = end; 65 if (!isBlockComment && itr != end && *itr == '\n') { 66 commentEnd = itr + 1; // '//\n' 67 } else if (std::regex_search(itr, end, match, *endRegex)) { 68 commentEnd = itr + match.position() + match.length(); 69 } else { 70 return ret; 71 } 72 73 for (; itr != commentEnd; ++itr) { 74 const auto cur = *itr; 75 if (cur == '\n') { 76 ret += cur; 77 } 78 } 79 if (isBlockComment) { 80 ret += "*/"; 81 } 82 } 83 84 ret.append(itr, end); 85 return ret; 86 } 87 88 //////////////////////////////////////////////////////////////////////////////// 89 90 static constexpr bool IsValidGLSLChar(const char c) { 91 if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || 92 ('0' <= c && c <= '9')) { 93 return true; 94 } 95 96 switch (c) { 97 case ' ': 98 case '\t': 99 case '\v': 100 case '\f': 101 case '\r': 102 case '\n': 103 case '_': 104 case '.': 105 case '+': 106 case '-': 107 case '/': 108 case '*': 109 case '%': 110 case '<': 111 case '>': 112 case '[': 113 case ']': 114 case '(': 115 case ')': 116 case '{': 117 case '}': 118 case '^': 119 case '|': 120 case '&': 121 case '~': 122 case '=': 123 case '!': 124 case ':': 125 case ';': 126 case ',': 127 case '?': 128 return true; 129 130 default: 131 return false; 132 } 133 } 134 135 static constexpr bool IsValidForPreprocOrGlsl(const char c) { 136 switch (c) { 137 case '#': 138 case '\\': 139 return true; 140 141 default: 142 return IsValidGLSLChar(c); 143 } 144 } 145 146 //// 147 148 static constexpr char INVALID_GLSL_CHAR = '$'; 149 150 std::string CrushGlslToAscii(const std::string& u8) { 151 static_assert(!IsValidForPreprocOrGlsl(INVALID_GLSL_CHAR)); 152 auto ascii = u8; 153 for (auto& c : ascii) { 154 if (MOZ_UNLIKELY(!IsValidForPreprocOrGlsl(c))) { 155 c = INVALID_GLSL_CHAR; 156 } 157 } 158 return ascii; 159 } 160 161 Maybe<webgl::ErrorInfo> CheckGLSLVariableName(const bool webgl2, 162 const std::string& name) { 163 if (name.empty()) return {}; 164 165 const uint32_t maxSize = webgl2 ? 1024 : 256; 166 if (name.size() > maxSize) { 167 const auto info = nsPrintfCString( 168 "Identifier is %zu characters long, exceeds the" 169 " maximum allowed length of %u characters.", 170 name.size(), maxSize); 171 return Some(webgl::ErrorInfo{LOCAL_GL_INVALID_VALUE, info.BeginReading()}); 172 } 173 174 for (const auto cur : name) { 175 if (!IsValidGLSLChar(cur)) { 176 const auto info = 177 nsPrintfCString("String contains the illegal character 0x%x'.", cur); 178 return Some( 179 webgl::ErrorInfo{LOCAL_GL_INVALID_VALUE, info.BeginReading()}); 180 } 181 } 182 183 if (name.find("webgl_") == 0 || name.find("_webgl_") == 0) { 184 return Some(webgl::ErrorInfo{ 185 LOCAL_GL_INVALID_OPERATION, 186 "String matches reserved GLSL prefix pattern /_?webgl_/."}); 187 } 188 189 return {}; 190 } 191 192 } // namespace mozilla