1 //
2 // Copyright 2024 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // Parse_test.cpp:
7 // Test for parsing erroneous and correct GLSL input.
8 //
9
10 #include <memory>
11 #include "GLSLANG/ShaderLang.h"
12 #include "angle_gl.h"
13 #include "compiler/translator/glsl/TranslatorESSL.h"
14 #include "gtest/gtest.h"
15
16 using namespace sh;
17
18 class ParseTest : public testing::Test
19 {
20 public:
ParseTest()21 ParseTest()
22 {
23 InitBuiltInResources(&mResources);
24 mResources.FragmentPrecisionHigh = 1;
25 mCompileOptions.intermediateTree = true;
26 }
27
28 protected:
TearDown()29 void TearDown() override { mTranslator.reset(); }
30
compile(const std::string & shaderString)31 testing::AssertionResult compile(const std::string &shaderString)
32 {
33 if (mTranslator == nullptr)
34 {
35 std::unique_ptr<TranslatorESSL> translator =
36 std::make_unique<TranslatorESSL>(GL_FRAGMENT_SHADER, mShaderSpec);
37 if (!translator->Init(mResources))
38 {
39 return testing::AssertionFailure() << "Failed to initialize translator";
40 }
41 mTranslator = std::move(translator);
42 }
43
44 const char *shaderStrings[] = {shaderString.c_str()};
45 bool compilationSuccess = mTranslator->compile(shaderStrings, 1, mCompileOptions);
46 TInfoSink &infoSink = mTranslator->getInfoSink();
47 mInfoLog = RemoveSymbolIdsFromInfoLog(infoSink.info.c_str());
48 if (!compilationSuccess)
49 {
50 return testing::AssertionFailure() << "Shader compilation failed " << mInfoLog;
51 }
52 return testing::AssertionSuccess();
53 }
54
foundErrorInIntermediateTree() const55 bool foundErrorInIntermediateTree() const { return foundInIntermediateTree("ERROR:"); }
56
foundInIntermediateTree(const char * stringToFind) const57 bool foundInIntermediateTree(const char *stringToFind) const
58 {
59 return mInfoLog.find(stringToFind) != std::string::npos;
60 }
61
62 ShBuiltInResources mResources;
63 ShCompileOptions mCompileOptions{};
64 ShShaderSpec mShaderSpec = SH_WEBGL_SPEC;
65
66 private:
67 // Remove symbol ids from info log - the tests don't care about them.
RemoveSymbolIdsFromInfoLog(const char * infoLog)68 static std::string RemoveSymbolIdsFromInfoLog(const char *infoLog)
69 {
70 std::string filteredLog(infoLog);
71 size_t idPrefixPos = 0u;
72 do
73 {
74 idPrefixPos = filteredLog.find(" (symbol id");
75 if (idPrefixPos != std::string::npos)
76 {
77 size_t idSuffixPos = filteredLog.find(")", idPrefixPos);
78 filteredLog.erase(idPrefixPos, idSuffixPos - idPrefixPos + 1u);
79 }
80 } while (idPrefixPos != std::string::npos);
81 return filteredLog;
82 }
83
84 std::unique_ptr<TranslatorESSL> mTranslator;
85 std::string mInfoLog;
86 };
87
TEST_F(ParseTest,UnsizedArrayConstructorNoCrash)88 TEST_F(ParseTest, UnsizedArrayConstructorNoCrash)
89 {
90 const char kShader[] = R"(#version 310 es\n"
91 int A[];
92 int B[int[][](A)];)";
93 EXPECT_FALSE(compile(kShader));
94 EXPECT_TRUE(foundErrorInIntermediateTree());
95 EXPECT_TRUE(foundInIntermediateTree("constructing from an unsized array"));
96 }
97
98 TEST_F(ParseTest, UniformBlockNameReferenceNoCrash)
99 {
100 const char kShader[] = R"(#version 300 es
101 precision mediump float;
102 out float o;
103 uniform a { float r; } UBOA;
104 void main() {
105 o = float(UBOA);
106 })";
107 EXPECT_FALSE(compile(kShader));
108 EXPECT_TRUE(foundErrorInIntermediateTree());
109 EXPECT_TRUE(foundInIntermediateTree(
110 "interface block cannot be used as a constructor argument for this type"));
111 }
112
113 TEST_F(ParseTest, Precise320NoCrash)
114 {
115 const char kShader[] = R"(#version 320 es
116 precision mediump float;
117 void main(){
118 float t;
119 precise t;
120 })";
121 EXPECT_FALSE(compile(kShader));
122 EXPECT_TRUE(foundErrorInIntermediateTree());
123 EXPECT_TRUE(foundInIntermediateTree("unsupported shader version"));
124 }
125
126 // Tests that layout(index=0) is parsed in es 100 shaders if an extension like
127 // EXT_shader_framebuffer_fetch is enabled, but this does not cause a crash.
128 TEST_F(ParseTest, ShaderFramebufferFetchLayoutIndexNoCrash)
129 {
130 mResources.EXT_blend_func_extended = 1;
131 mResources.MaxDualSourceDrawBuffers = 1;
132 mResources.EXT_shader_framebuffer_fetch = 1;
133 const char kShader[] = R"(
134 #extension GL_EXT_blend_func_extended: require
135 #extension GL_EXT_shader_framebuffer_fetch : require
136 layout(index=0)mediump vec4 c;
137 void main() { }
138 )";
139 EXPECT_FALSE(compile(kShader));
140 EXPECT_TRUE(foundErrorInIntermediateTree());
141 EXPECT_TRUE(foundInIntermediateTree("'index' : invalid layout qualifier"));
142 }
143
144 TEST_F(ParseTest, Radians320NoCrash)
145 {
146 const char kShader[] = R"(#version 320 es
147 precision mediump float;
148 vec4 s() { writeonly vec4 color; radians(color); return vec4(1); })";
149 EXPECT_FALSE(compile(kShader));
150 EXPECT_TRUE(foundErrorInIntermediateTree());
151 EXPECT_TRUE(foundInIntermediateTree("'writeonly' : Only allowed with shader storage blocks,"));
152 EXPECT_TRUE(foundInIntermediateTree(
153 "'radians' : wrong operand type - no operation 'radians' exists that"));
154 }
155
156 TEST_F(ParseTest, CoherentCoherentNoCrash)
157 {
158 const char kShader[] = R"(#version 310 es
159 uniform highp coherent coherent readonly image2D image1;\n"
160 void main() {
161 })";
162 EXPECT_FALSE(compile(kShader));
163 EXPECT_TRUE(foundErrorInIntermediateTree());
164 EXPECT_TRUE(foundInIntermediateTree("coherent specified multiple times"));
165 }
166
TEST_F(ParseTest,LargeArrayIndexNoCrash)167 TEST_F(ParseTest, LargeArrayIndexNoCrash)
168 {
169 mShaderSpec = SH_WEBGL2_SPEC;
170 const char kShader[] = R"(#version 300 es
171 int rr[~1U];
172 out int o;
173 void main() {
174 o = rr[1];
175 })";
176 EXPECT_FALSE(compile(kShader));
177 EXPECT_TRUE(foundErrorInIntermediateTree());
178 EXPECT_TRUE(
179 foundInIntermediateTree("Size of declared variable exceeds implementation-defined limit"));
180 }
181
182 // Tests that separating variable declaration of multiple instances of a anonymous structure
183 // rewrites the expression types for expressions that use the variables. At the time of writing
184 // the expression types were left referencing the original anonymous function.
TEST_F(ParseTest,SeparateAnonymousFunctionsRewritesExpressions)185 TEST_F(ParseTest, SeparateAnonymousFunctionsRewritesExpressions)
186 {
187 const char kShader[] = R"(
188 struct {
189 mediump vec2 d;
190 } s0, s1;
191 void main() {
192 s0 = s0;
193 s1 = s1;
194 })";
195 EXPECT_TRUE(compile(kShader));
196 EXPECT_FALSE(foundInIntermediateTree("anonymous"));
197 }
198
199 // Tests that constant folding a division of a void variable does not crash during parsing.
TEST_F(ParseTest,ConstStructWithVoidAndDivNoCrash)200 TEST_F(ParseTest, ConstStructWithVoidAndDivNoCrash)
201 {
202 const char kShader[] = R"(
203 const struct s { void i; } ss = s();
204 void main() {
205 highp vec3 q = ss.i / ss.i;
206 })";
207 EXPECT_FALSE(compile(kShader));
208 EXPECT_TRUE(foundErrorInIntermediateTree());
209 EXPECT_TRUE(foundInIntermediateTree("illegal use of type 'void'"));
210 EXPECT_TRUE(foundInIntermediateTree("constructor does not have any arguments"));
211 EXPECT_TRUE(foundInIntermediateTree("operation with void operands"));
212 EXPECT_TRUE(foundInIntermediateTree(
213 "wrong operand types - no operation '/' exists that takes a left-hand operand of type "
214 "'const void' and a right operand of type 'const void'"));
215 EXPECT_TRUE(foundInIntermediateTree(
216 "cannot convert from 'const void' to 'highp 3-component vector of float'"));
217 }
218
219 // Tests that division of void variable returns the same errors as division of constant
220 // void variable (see above).
TEST_F(ParseTest,StructWithVoidAndDivErrorCheck)221 TEST_F(ParseTest, StructWithVoidAndDivErrorCheck)
222 {
223 const char kShader[] = R"(
224 struct s { void i; } ss = s();
225 void main() {
226 highp vec3 q = ss.i / ss.i;
227 })";
228 EXPECT_FALSE(compile(kShader));
229 EXPECT_TRUE(foundErrorInIntermediateTree());
230 EXPECT_TRUE(foundInIntermediateTree("illegal use of type 'void'"));
231 EXPECT_TRUE(foundInIntermediateTree("constructor does not have any arguments"));
232 EXPECT_TRUE(foundInIntermediateTree("operation with void operands"));
233 EXPECT_TRUE(foundInIntermediateTree(
234 "wrong operand types - no operation '/' exists that takes a left-hand operand of type "
235 "'void' and a right operand of type 'void'"));
236 EXPECT_TRUE(foundInIntermediateTree(
237 "cannot convert from 'void' to 'highp 3-component vector of float'"));
238 }
239
240 // Tests that imod of const void variable does not crash during parsing.
TEST_F(ParseTest,ConstStructVoidAndImodAndNoCrash)241 TEST_F(ParseTest, ConstStructVoidAndImodAndNoCrash)
242 {
243 const char kShader[] = R"(#version 310 es
244 const struct s { void i; } ss = s();
245 void main() {
246 highp vec3 q = ss.i % ss.i;
247 })";
248 EXPECT_FALSE(compile(kShader));
249 EXPECT_TRUE(foundErrorInIntermediateTree());
250 EXPECT_TRUE(foundInIntermediateTree("illegal use of type 'void'"));
251 EXPECT_TRUE(foundInIntermediateTree("constructor does not have any arguments"));
252 EXPECT_TRUE(foundInIntermediateTree("operation with void operands"));
253 EXPECT_TRUE(foundInIntermediateTree(
254 "wrong operand types - no operation '%' exists that takes a left-hand operand of type "
255 "'const void' and a right operand of type 'const void'"));
256 EXPECT_TRUE(foundInIntermediateTree(
257 "cannot convert from 'const void' to 'highp 3-component vector of float'"));
258 }
259
TEST_F(ParseTest,HugeUnsizedMultidimensionalArrayConstructorNoCrash)260 TEST_F(ParseTest, HugeUnsizedMultidimensionalArrayConstructorNoCrash)
261 {
262 mCompileOptions.limitExpressionComplexity = true;
263 std::ostringstream shader;
264 shader << R"(#version 310 es
265 int E=int)";
266 for (int i = 0; i < 10000; ++i)
267 {
268 shader << "[]";
269 }
270 shader << "()";
271 EXPECT_FALSE(compile(shader.str()));
272 EXPECT_TRUE(foundErrorInIntermediateTree());
273 EXPECT_TRUE(foundInIntermediateTree("array has too many dimensions"));
274 }
275
TEST_F(ParseTest,HugeMultidimensionalArrayConstructorNoCrash)276 TEST_F(ParseTest, HugeMultidimensionalArrayConstructorNoCrash)
277 {
278 mCompileOptions.limitExpressionComplexity = true;
279 std::ostringstream shader;
280 shader << R"(#version 310 es
281 int E=int)";
282 for (int i = 0; i < 10000; ++i)
283 {
284 shader << "[1]";
285 }
286
287 for (int i = 0; i < 10000; ++i)
288 {
289 shader << "(2)";
290 }
291 EXPECT_FALSE(compile(shader.str()));
292 EXPECT_TRUE(foundErrorInIntermediateTree());
293 EXPECT_TRUE(foundInIntermediateTree("array has too many dimensions"));
294 }
295
TEST_F(ParseTest,DeeplyNestedWhileStatementsNoCrash)296 TEST_F(ParseTest, DeeplyNestedWhileStatementsNoCrash)
297 {
298 mShaderSpec = SH_WEBGL2_SPEC;
299 std::ostringstream shader;
300 shader << R"(#version 300 es
301 void main() {
302 )";
303 for (int i = 0; i < 1700; ++i)
304 {
305 shader << " while(true)";
306 }
307 shader << "; }";
308 EXPECT_FALSE(compile(shader.str()));
309 EXPECT_TRUE(foundErrorInIntermediateTree());
310 EXPECT_TRUE(foundInIntermediateTree("statement is too deeply nested"));
311 }
312
TEST_F(ParseTest,DeeplyNestedForStatementsNoCrash)313 TEST_F(ParseTest, DeeplyNestedForStatementsNoCrash)
314 {
315 mShaderSpec = SH_WEBGL2_SPEC;
316 std::ostringstream shader;
317 shader << R"(#version 300 es
318 void main() {
319 )";
320 for (int i = 0; i < 1700; ++i)
321 {
322 shader << " for(int i = 0; i < 10; i++)";
323 }
324 shader << "; }";
325 EXPECT_FALSE(compile(shader.str()));
326 EXPECT_TRUE(foundErrorInIntermediateTree());
327 EXPECT_TRUE(foundInIntermediateTree("statement is too deeply nested"));
328 }
329
TEST_F(ParseTest,DeeplyNestedDoWhileStatementsNoCrash)330 TEST_F(ParseTest, DeeplyNestedDoWhileStatementsNoCrash)
331 {
332 mShaderSpec = SH_WEBGL2_SPEC;
333 std::ostringstream shader;
334 shader << R"(#version 300 es
335 void main() {
336 )";
337 for (int i = 0; i < 1700; ++i)
338 {
339 shader << " do {";
340 }
341 for (int i = 0; i < 1700; ++i)
342 {
343 shader << "} while(true);";
344 }
345 shader << "}";
346 EXPECT_FALSE(compile(shader.str()));
347 EXPECT_TRUE(foundErrorInIntermediateTree());
348 EXPECT_TRUE(foundInIntermediateTree("statement is too deeply nested"));
349 }
350
TEST_F(ParseTest,DeeplyNestedSwitchStatementsNoCrash)351 TEST_F(ParseTest, DeeplyNestedSwitchStatementsNoCrash)
352 {
353 mShaderSpec = SH_WEBGL2_SPEC;
354 std::ostringstream shader;
355 shader << R"(#version 300 es
356 void main() {
357 )";
358 for (int i = 0; i < 1700; ++i)
359 {
360 shader << " switch(1) { default: int i=0;";
361 }
362 for (int i = 0; i < 1700; ++i)
363 {
364 shader << "}";
365 }
366 shader << "}";
367 EXPECT_FALSE(compile(shader.str()));
368 EXPECT_TRUE(foundErrorInIntermediateTree());
369 EXPECT_TRUE(foundInIntermediateTree("statement is too deeply nested"));
370 }
371
TEST_F(ParseTest,ManyChainedUnaryExpressionsNoCrash)372 TEST_F(ParseTest, ManyChainedUnaryExpressionsNoCrash)
373 {
374 mCompileOptions.limitExpressionComplexity = true;
375 mShaderSpec = SH_WEBGL2_SPEC;
376 std::ostringstream shader;
377 shader << R"(#version 300 es
378 precision mediump float;
379 void main() {
380 int iterations=0;)";
381 for (int i = 0; i < 6000; ++i)
382 {
383 shader << "~";
384 }
385 shader << R"(++iterations;
386 }
387 )";
388 EXPECT_FALSE(compile(shader.str()));
389 EXPECT_TRUE(foundErrorInIntermediateTree());
390 EXPECT_TRUE(foundInIntermediateTree("Expression too complex"));
391 }
392
TEST_F(ParseTest,ManyChainedAssignmentsNoCrash)393 TEST_F(ParseTest, ManyChainedAssignmentsNoCrash)
394 {
395 mCompileOptions.limitExpressionComplexity = true;
396 mShaderSpec = SH_WEBGL2_SPEC;
397 std::ostringstream shader;
398 shader << R"(#version 300 es
399 void main() {
400 int c = 0;
401 )";
402 for (int i = 0; i < 3750; ++i)
403 {
404 shader << "c=\n";
405 }
406 shader << "c+1; }";
407 EXPECT_FALSE(compile(shader.str()));
408 EXPECT_TRUE(foundErrorInIntermediateTree());
409 EXPECT_TRUE(foundInIntermediateTree("Expression too complex"));
410 }
411