• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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