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 
12 namespace mozilla {
13 
14 /* GLSL ES 3.00 p17:
15   - Comments are delimited by / * and * /, or by // and a newline.
16 
17   - '//' style comments include the initial '//' marker and continue up to, but
18 not including, the terminating newline.
19 
20   - '/ * ... * /' comments include both the start and end marker.
21 
22   - The begin comment delimiters (/ * or //) are not recognized as comment
23 delimiters inside of a comment, hence comments cannot be nested.
24 
25   - Comments are treated syntactically as a single space.
26 */
27 
CommentsToSpaces(const std::string & src)28 std::string CommentsToSpaces(const std::string& src) {
29   constexpr auto flags =
30       std::regex::ECMAScript | std::regex::nosubs | std::regex::optimize;
31 
32   static const auto RE_COMMENT_BEGIN = std::regex("/[*/]", flags);
33   static const auto RE_LINE_COMMENT_END = std::regex(R"([^\\]\n)", flags);
34   static const auto RE_BLOCK_COMMENT_END = std::regex(R"(\*/)", flags);
35 
36   std::string ret;
37   ret.reserve(src.size());
38 
39   // Replace all comments with block comments with the right number of newlines.
40   // Line positions may be off, but line numbers will be accurate, which is more
41   // important.
42 
43   auto itr = src.begin();
44   const auto end = src.end();
45   std::smatch match;
46   while (std::regex_search(itr, end, match, RE_COMMENT_BEGIN)) {
47     MOZ_ASSERT(match.length() == 2);
48     const auto commentBegin = itr + match.position();
49     ret.append(itr, commentBegin);
50 
51     itr = commentBegin + match.length();
52 
53     const bool isBlockComment = (*(commentBegin + 1) == '*');
54     const auto* endRegex = &RE_LINE_COMMENT_END;
55     if (isBlockComment) {
56       endRegex = &RE_BLOCK_COMMENT_END;
57     }
58 
59     if (isBlockComment) {
60       ret += "/*";
61     }
62 
63     auto commentEnd = end;
64     if (!isBlockComment && itr != end && *itr == '\n') {
65       commentEnd = itr + 1;  // '//\n'
66     } else if (std::regex_search(itr, end, match, *endRegex)) {
67       commentEnd = itr + match.position() + match.length();
68     } else {
69       return ret;
70     }
71 
72     for (; itr != commentEnd; ++itr) {
73       const auto cur = *itr;
74       if (cur == '\n') {
75         ret += cur;
76       }
77     }
78     if (isBlockComment) {
79       ret += "*/";
80     }
81   }
82 
83   ret.append(itr, end);
84   return ret;
85 }
86 
87 ////////////////////////////////////////////////////////////////////////////////
88 
IsValidGLSLChar(const char c)89 static bool IsValidGLSLChar(const char c) {
90   if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') ||
91       ('0' <= c && c <= '9')) {
92     return true;
93   }
94 
95   switch (c) {
96     case ' ':
97     case '\t':
98     case '\v':
99     case '\f':
100     case '\r':
101     case '\n':
102     case '_':
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       return true;
128 
129     default:
130       return false;
131   }
132 }
133 
134 ////
135 
CheckGLSLPreprocString(const bool webgl2,const std::string & string)136 Maybe<char> CheckGLSLPreprocString(const bool webgl2,
137                                    const std::string& string) {
138   for (const auto c : string) {
139     if (IsValidGLSLChar(c)) continue;
140     if (c == '#') continue;
141     if (c == '\\' && webgl2) continue;
142 
143     return Some(c);
144   }
145   return {};
146 }
147 
CheckGLSLVariableName(const bool webgl2,const std::string & name)148 Maybe<webgl::ErrorInfo> CheckGLSLVariableName(const bool webgl2,
149                                               const std::string& name) {
150   if (name.empty()) return {};
151 
152   const uint32_t maxSize = webgl2 ? 1024 : 256;
153   if (name.size() > maxSize) {
154     const auto info = nsPrintfCString(
155         "Identifier is %zu characters long, exceeds the"
156         " maximum allowed length of %u characters.",
157         name.size(), maxSize);
158     return Some(webgl::ErrorInfo{LOCAL_GL_INVALID_VALUE, info.BeginReading()});
159   }
160 
161   for (const auto cur : name) {
162     if (!IsValidGLSLChar(cur)) {
163       const auto info =
164           nsPrintfCString("String contains the illegal character 0x%x'.", cur);
165       return Some(
166           webgl::ErrorInfo{LOCAL_GL_INVALID_VALUE, info.BeginReading()});
167     }
168   }
169 
170   if (name.find("webgl_") == 0 || name.find("_webgl_") == 0) {
171     return Some(webgl::ErrorInfo{
172         LOCAL_GL_INVALID_OPERATION,
173         "String matches reserved GLSL prefix pattern /_?webgl_/."});
174   }
175 
176   return {};
177 }
178 
179 }  // namespace mozilla
180