1 //
2 // Copyright 2002 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 #include <sstream>
7 #include <string>
8 #include <vector>
9 #include "GLSLANG/ShaderLang.h"
10 #include "angle_gl.h"
11 #include "gtest/gtest.h"
12
13 class ExpressionLimitTest : public testing::Test
14 {
15 protected:
16 static const int kMaxExpressionComplexity = 16;
17 static const int kMaxCallStackDepth = 16;
18 static const int kMaxFunctionParameters = 16;
19
SetUp()20 virtual void SetUp()
21 {
22 memset(&resources, 0, sizeof(resources));
23
24 GenerateResources(&resources);
25 }
26
27 // Set up the per compile resources
GenerateResources(ShBuiltInResources * res)28 static void GenerateResources(ShBuiltInResources *res)
29 {
30 sh::InitBuiltInResources(res);
31
32 res->MaxVertexAttribs = 8;
33 res->MaxVertexUniformVectors = 128;
34 res->MaxVaryingVectors = 8;
35 res->MaxVertexTextureImageUnits = 0;
36 res->MaxCombinedTextureImageUnits = 8;
37 res->MaxTextureImageUnits = 8;
38 res->MaxFragmentUniformVectors = 16;
39 res->MaxDrawBuffers = 1;
40
41 res->OES_standard_derivatives = 0;
42 res->OES_EGL_image_external = 0;
43
44 res->MaxExpressionComplexity = kMaxExpressionComplexity;
45 res->MaxCallStackDepth = kMaxCallStackDepth;
46 res->MaxFunctionParameters = kMaxFunctionParameters;
47 }
48
GenerateLongExpression(int length,std::stringstream * ss)49 static void GenerateLongExpression(int length, std::stringstream *ss)
50 {
51 for (int ii = 0; ii < length; ++ii)
52 {
53 *ss << "+ vec4(" << ii << ")";
54 }
55 }
56
GenerateShaderWithLongExpression(int length)57 static std::string GenerateShaderWithLongExpression(int length)
58 {
59 static const char *shaderStart =
60 R"(precision mediump float;
61 uniform vec4 u_color;
62 void main()
63 {
64 gl_FragColor = u_color
65 )";
66
67 std::stringstream ss;
68 ss << shaderStart;
69 GenerateLongExpression(length, &ss);
70 ss << "; }";
71
72 return ss.str();
73 }
74
GenerateShaderWithUnusedLongExpression(int length)75 static std::string GenerateShaderWithUnusedLongExpression(int length)
76 {
77 static const char *shaderStart =
78 R"(precision mediump float;
79 uniform vec4 u_color;
80 void main()
81 {
82 gl_FragColor = u_color;
83 }
84 vec4 someFunction() {
85 return u_color
86 )";
87
88 std::stringstream ss;
89
90 ss << shaderStart;
91 GenerateLongExpression(length, &ss);
92 ss << "; }";
93
94 return ss.str();
95 }
96
GenerateDeepFunctionStack(int length,std::stringstream * ss)97 static void GenerateDeepFunctionStack(int length, std::stringstream *ss)
98 {
99 static const char *shaderStart =
100 R"(precision mediump float;
101 uniform vec4 u_color;
102 vec4 function0() {
103 return u_color;
104 }
105 )";
106
107 *ss << shaderStart;
108 for (int ii = 0; ii < length; ++ii)
109 {
110 *ss << "vec4 function" << (ii + 1) << "() {\n"
111 << " return function" << ii << "();\n"
112 << "}\n";
113 }
114 }
115
GenerateShaderWithDeepFunctionStack(int length)116 static std::string GenerateShaderWithDeepFunctionStack(int length)
117 {
118 std::stringstream ss;
119
120 GenerateDeepFunctionStack(length, &ss);
121
122 ss << "void main() {\n" << " gl_FragColor = function" << length << "();\n" << "}";
123
124 return ss.str();
125 }
126
GenerateShaderWithUnusedDeepFunctionStack(int length)127 static std::string GenerateShaderWithUnusedDeepFunctionStack(int length)
128 {
129 std::stringstream ss;
130
131 GenerateDeepFunctionStack(length, &ss);
132
133 ss << "void main() {\n" << " gl_FragColor = vec4(0,0,0,0);\n" << "}";
134
135 return ss.str();
136 }
137
GenerateShaderWithFunctionParameters(int parameters)138 static std::string GenerateShaderWithFunctionParameters(int parameters)
139 {
140 std::stringstream ss;
141
142 ss << "precision mediump float;\n" << "\n" << "float foo(";
143 for (int i = 0; i < parameters; ++i)
144 {
145 ss << "float f" << i;
146 if (i + 1 < parameters)
147 {
148 ss << ", ";
149 }
150 }
151 ss << ")\n"
152 << "{\n"
153 << " return f0;\n"
154 << "}\n"
155 << "\n"
156 << "void main()\n"
157 << "{\n"
158 << " gl_FragColor = vec4(0,0,0,0);\n"
159 << "}";
160
161 return ss.str();
162 }
163
GenerateShaderWithNestingInsideSwitch(int nesting)164 static std::string GenerateShaderWithNestingInsideSwitch(int nesting)
165 {
166 std::stringstream shaderString;
167 shaderString <<
168 R"(#version 300 es
169 uniform int u;
170
171 void main()
172 {
173 int x;
174 switch (u)
175 {
176 case 0:
177 x = x)";
178 for (int i = 0; i < nesting; ++i)
179 {
180 shaderString << " + x";
181 }
182 shaderString <<
183 R"(;
184 } // switch (u)
185 })";
186 return shaderString.str();
187 }
188
GenerateShaderWithNestingInsideGlobalInitializer(int nesting)189 static std::string GenerateShaderWithNestingInsideGlobalInitializer(int nesting)
190 {
191 std::stringstream shaderString;
192 shaderString <<
193 R"(uniform int u;
194 int x = u)";
195
196 for (int i = 0; i < nesting; ++i)
197 {
198 shaderString << " + u";
199 }
200 shaderString << R"(;
201 void main()
202 {
203 gl_FragColor = vec4(0.0);
204 })";
205 return shaderString.str();
206 }
207
208 // Compiles a shader and if there's an error checks for a specific
209 // substring in the error log. This way we know the error is specific
210 // to the issue we are testing.
CheckShaderCompilation(ShHandle compiler,const char * source,const ShCompileOptions & compileOptions,const char * expected_error)211 bool CheckShaderCompilation(ShHandle compiler,
212 const char *source,
213 const ShCompileOptions &compileOptions,
214 const char *expected_error)
215 {
216 bool success = sh::Compile(compiler, &source, 1, compileOptions) != 0;
217 if (success)
218 {
219 success = !expected_error;
220 }
221 else
222 {
223 std::string log = sh::GetInfoLog(compiler);
224 if (expected_error)
225 success = log.find(expected_error) != std::string::npos;
226
227 EXPECT_TRUE(success) << log << "\n----shader----\n" << source;
228 }
229 return success;
230 }
231
232 ShBuiltInResources resources;
233 };
234
235 constexpr char kExpressionTooComplex[] = "Expression too complex";
236 constexpr char kCallStackTooDeep[] = "Call stack too deep";
237 constexpr char kHasRecursion[] = "Recursive function call in the following call chain";
238 constexpr char kTooManyParameters[] = "Function has too many parameters";
239 constexpr char kTooComplexSwitch[] = "too complex expressions inside a switch statement";
240 constexpr char kGlobalVariableInit[] = "global variable initializers must be constant expressions";
241 constexpr char kTooManyFields[] = "Too many fields in the struct";
242
TEST_F(ExpressionLimitTest,ExpressionComplexity)243 TEST_F(ExpressionLimitTest, ExpressionComplexity)
244 {
245 ShShaderSpec spec = SH_WEBGL_SPEC;
246 ShShaderOutput output = SH_ESSL_OUTPUT;
247 ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
248 ShCompileOptions compileOptions = {};
249 compileOptions.limitExpressionComplexity = true;
250
251 // Test expression under the limit passes.
252 EXPECT_TRUE(CheckShaderCompilation(
253 vertexCompiler, GenerateShaderWithLongExpression(kMaxExpressionComplexity - 10).c_str(),
254 compileOptions, nullptr));
255 // Test expression over the limit fails.
256 EXPECT_TRUE(CheckShaderCompilation(
257 vertexCompiler, GenerateShaderWithLongExpression(kMaxExpressionComplexity + 10).c_str(),
258 compileOptions, kExpressionTooComplex));
259 // Test expression over the limit without a limit does not fail.
260 compileOptions.limitExpressionComplexity = false;
261 EXPECT_TRUE(CheckShaderCompilation(
262 vertexCompiler, GenerateShaderWithLongExpression(kMaxExpressionComplexity + 10).c_str(),
263 compileOptions, nullptr));
264 sh::Destruct(vertexCompiler);
265 }
266
TEST_F(ExpressionLimitTest,UnusedExpressionComplexity)267 TEST_F(ExpressionLimitTest, UnusedExpressionComplexity)
268 {
269 ShShaderSpec spec = SH_WEBGL_SPEC;
270 ShShaderOutput output = SH_ESSL_OUTPUT;
271 ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
272 ShCompileOptions compileOptions = {};
273 compileOptions.limitExpressionComplexity = true;
274
275 // Test expression under the limit passes.
276 EXPECT_TRUE(CheckShaderCompilation(
277 vertexCompiler,
278 GenerateShaderWithUnusedLongExpression(kMaxExpressionComplexity - 10).c_str(),
279 compileOptions, nullptr));
280 // Test expression over the limit fails.
281 EXPECT_TRUE(CheckShaderCompilation(
282 vertexCompiler,
283 GenerateShaderWithUnusedLongExpression(kMaxExpressionComplexity + 10).c_str(),
284 compileOptions, kExpressionTooComplex));
285 // Test expression over the limit without a limit does not fail.
286 compileOptions.limitExpressionComplexity = false;
287 EXPECT_TRUE(CheckShaderCompilation(
288 vertexCompiler,
289 GenerateShaderWithUnusedLongExpression(kMaxExpressionComplexity + 10).c_str(),
290 compileOptions, nullptr));
291 sh::Destruct(vertexCompiler);
292 }
293
TEST_F(ExpressionLimitTest,CallStackDepth)294 TEST_F(ExpressionLimitTest, CallStackDepth)
295 {
296 ShShaderSpec spec = SH_WEBGL_SPEC;
297 ShShaderOutput output = SH_ESSL_OUTPUT;
298 ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
299 ShCompileOptions compileOptions = {};
300 compileOptions.limitCallStackDepth = true;
301
302 // Test call stack under the limit passes.
303 EXPECT_TRUE(CheckShaderCompilation(
304 vertexCompiler, GenerateShaderWithDeepFunctionStack(kMaxCallStackDepth - 10).c_str(),
305 compileOptions, nullptr));
306 // Test call stack over the limit fails.
307 EXPECT_TRUE(CheckShaderCompilation(
308 vertexCompiler, GenerateShaderWithDeepFunctionStack(kMaxCallStackDepth + 10).c_str(),
309 compileOptions, kCallStackTooDeep));
310 // Test call stack over the limit without limit does not fail.
311 compileOptions.limitCallStackDepth = false;
312 EXPECT_TRUE(CheckShaderCompilation(
313 vertexCompiler, GenerateShaderWithDeepFunctionStack(kMaxCallStackDepth + 10).c_str(),
314 compileOptions, nullptr));
315 sh::Destruct(vertexCompiler);
316 }
317
TEST_F(ExpressionLimitTest,UnusedCallStackDepth)318 TEST_F(ExpressionLimitTest, UnusedCallStackDepth)
319 {
320 ShShaderSpec spec = SH_WEBGL_SPEC;
321 ShShaderOutput output = SH_ESSL_OUTPUT;
322 ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
323 ShCompileOptions compileOptions = {};
324 compileOptions.limitCallStackDepth = true;
325
326 // Test call stack under the limit passes.
327 EXPECT_TRUE(CheckShaderCompilation(
328 vertexCompiler, GenerateShaderWithUnusedDeepFunctionStack(kMaxCallStackDepth - 10).c_str(),
329 compileOptions, nullptr));
330 // Test call stack over the limit fails.
331 EXPECT_TRUE(CheckShaderCompilation(
332 vertexCompiler, GenerateShaderWithUnusedDeepFunctionStack(kMaxCallStackDepth + 10).c_str(),
333 compileOptions, kCallStackTooDeep));
334 // Test call stack over the limit without limit does not fail.
335 compileOptions.limitCallStackDepth = false;
336 EXPECT_TRUE(CheckShaderCompilation(
337 vertexCompiler, GenerateShaderWithUnusedDeepFunctionStack(kMaxCallStackDepth + 10).c_str(),
338 compileOptions, nullptr));
339 sh::Destruct(vertexCompiler);
340 }
341
TEST_F(ExpressionLimitTest,Recursion)342 TEST_F(ExpressionLimitTest, Recursion)
343 {
344 ShShaderSpec spec = SH_WEBGL_SPEC;
345 ShShaderOutput output = SH_ESSL_OUTPUT;
346 ShHandle vertexCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
347 ShCompileOptions compileOptions = {};
348
349 static const char *shaderWithRecursion0 =
350 R"(precision mediump float;
351 uniform vec4 u_color;
352 vec4 someFunc() {
353 return someFunc();
354 }
355
356 void main() {
357 gl_FragColor = u_color * someFunc();
358 }
359 )";
360
361 static const char *shaderWithRecursion1 =
362 R"(precision mediump float;
363 uniform vec4 u_color;
364
365 vec4 someFunc();
366
367 vec4 someFunc1() {
368 return someFunc();
369 }
370
371 vec4 someFunc() {
372 return someFunc1();
373 }
374
375 void main() {
376 gl_FragColor = u_color * someFunc();
377 }
378 )";
379
380 static const char *shaderWithRecursion2 =
381 R"(precision mediump float;
382 uniform vec4 u_color;
383 vec4 someFunc() {
384 if (u_color.x > 0.5) {
385 return someFunc();
386 } else {
387 return vec4(1);
388 }
389 }
390
391 void main() {
392 gl_FragColor = someFunc();
393 }
394 )";
395
396 static const char *shaderWithRecursion3 =
397 R"(precision mediump float;
398 uniform vec4 u_color;
399 vec4 someFunc() {
400 if (u_color.x > 0.5) {
401 return vec4(1);
402 } else {
403 return someFunc();
404 }
405 }
406
407 void main() {
408 gl_FragColor = someFunc();
409 }
410 )";
411
412 static const char *shaderWithRecursion4 =
413 R"(precision mediump float;
414 uniform vec4 u_color;
415 vec4 someFunc() {
416 return (u_color.x > 0.5) ? vec4(1) : someFunc();
417 }
418
419 void main() {
420 gl_FragColor = someFunc();
421 }
422 )";
423
424 static const char *shaderWithRecursion5 =
425 R"(precision mediump float;
426 uniform vec4 u_color;
427 vec4 someFunc() {
428 return (u_color.x > 0.5) ? someFunc() : vec4(1);
429 }
430
431 void main() {
432 gl_FragColor = someFunc();
433 }
434 )";
435
436 static const char *shaderWithRecursion6 =
437 R"(precision mediump float;
438 uniform vec4 u_color;
439 vec4 someFunc() {
440 return someFunc();
441 }
442
443 void main() {
444 gl_FragColor = u_color;
445 }
446 )";
447
448 static const char *shaderWithNoRecursion =
449 R"(precision mediump float;
450 uniform vec4 u_color;
451
452 vec3 rgb(int r, int g, int b) {
453 return vec3(float(r) / 255.0, float(g) / 255.0, float(b) / 255.0);
454 }
455
456 void main() {
457 vec3 hairColor0 = rgb(151, 200, 234);
458 vec3 faceColor2 = rgb(183, 148, 133);
459 gl_FragColor = u_color + vec4(hairColor0 + faceColor2, 0);
460 }
461 )";
462
463 static const char *shaderWithRecursion7 =
464 R"(precision mediump float;
465 uniform vec4 u_color;
466
467 vec4 function2() {
468 return u_color;
469 }
470
471 vec4 function1() {
472 vec4 a = function2();
473 vec4 b = function1();
474 return a + b;
475 }
476
477 void main() {
478 gl_FragColor = function1();
479 }
480 )";
481
482 static const char *shaderWithRecursion8 =
483 R"(precision mediump float;
484 uniform vec4 u_color;
485
486 vec4 function1();
487
488 vec4 function3() {
489 return function1();
490 }
491
492 vec4 function2() {
493 return function3();
494 }
495
496 vec4 function1() {
497 return function2();
498 }
499
500 void main() {
501 gl_FragColor = function1();
502 }
503 )";
504
505 // Check simple recursions fails.
506 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion0, compileOptions,
507 kHasRecursion));
508 // Check simple recursions fails.
509 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion1, compileOptions,
510 kHasRecursion));
511 // Check if recursions fails.
512 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion2, compileOptions,
513 kHasRecursion));
514 // Check if recursions fails.
515 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion3, compileOptions,
516 kHasRecursion));
517 // Check ternary recursions fails.
518 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion4, compileOptions,
519 kHasRecursion));
520 // Check ternary recursions fails.
521 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion5, compileOptions,
522 kHasRecursion));
523
524 // Check some more forms of recursion
525 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion6, compileOptions,
526 kHasRecursion));
527 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion7, compileOptions,
528 kHasRecursion));
529 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion8, compileOptions,
530 kHasRecursion));
531 // Check unused recursions fails if limiting call stack
532 // since we check all paths.
533 compileOptions.limitCallStackDepth = true;
534 EXPECT_TRUE(CheckShaderCompilation(vertexCompiler, shaderWithRecursion6, compileOptions,
535 kHasRecursion));
536
537 // Check unused recursions passes.
538 EXPECT_TRUE(
539 CheckShaderCompilation(vertexCompiler, shaderWithNoRecursion, compileOptions, nullptr));
540 // Check unused recursions passes if limiting call stack.
541 EXPECT_TRUE(
542 CheckShaderCompilation(vertexCompiler, shaderWithNoRecursion, compileOptions, nullptr));
543 sh::Destruct(vertexCompiler);
544 }
545
TEST_F(ExpressionLimitTest,FunctionParameterCount)546 TEST_F(ExpressionLimitTest, FunctionParameterCount)
547 {
548 ShShaderSpec spec = SH_WEBGL_SPEC;
549 ShShaderOutput output = SH_ESSL_OUTPUT;
550 ShHandle compiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
551 ShCompileOptions compileOptions = {};
552 compileOptions.limitExpressionComplexity = true;
553
554 // Test parameters under the limit succeeds.
555 EXPECT_TRUE(CheckShaderCompilation(
556 compiler, GenerateShaderWithFunctionParameters(kMaxFunctionParameters).c_str(),
557 compileOptions, nullptr));
558 // Test parameters over the limit fails.
559 EXPECT_TRUE(CheckShaderCompilation(
560 compiler, GenerateShaderWithFunctionParameters(kMaxFunctionParameters + 1).c_str(),
561 compileOptions, kTooManyParameters));
562 // Test parameters over the limit without limit does not fail.
563 compileOptions.limitExpressionComplexity = false;
564 EXPECT_TRUE(CheckShaderCompilation(
565 compiler, GenerateShaderWithFunctionParameters(kMaxFunctionParameters + 1).c_str(),
566 compileOptions, nullptr));
567 sh::Destruct(compiler);
568 }
569
TEST_F(ExpressionLimitTest,NestingInsideSwitch)570 TEST_F(ExpressionLimitTest, NestingInsideSwitch)
571 {
572 ShShaderSpec spec = SH_WEBGL2_SPEC;
573 ShShaderOutput output = SH_ESSL_OUTPUT;
574 ShHandle compiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
575 ShCompileOptions compileOptions = {};
576 compileOptions.limitExpressionComplexity = true;
577
578 // Test nesting over the limit fails.
579 EXPECT_TRUE(CheckShaderCompilation(
580 compiler, GenerateShaderWithNestingInsideSwitch(kMaxExpressionComplexity + 1).c_str(),
581 compileOptions, kExpressionTooComplex));
582 // Test that nesting way over the limit doesn't cause stack overflow but is handled
583 // gracefully.
584 EXPECT_TRUE(CheckShaderCompilation(compiler,
585 GenerateShaderWithNestingInsideSwitch(5000).c_str(),
586 compileOptions, kTooComplexSwitch));
587 // Test nesting over the limit without limit does not fail.
588 compileOptions.limitExpressionComplexity = false;
589 EXPECT_TRUE(CheckShaderCompilation(
590 compiler, GenerateShaderWithNestingInsideSwitch(kMaxExpressionComplexity + 1).c_str(),
591 compileOptions, nullptr));
592 sh::Destruct(compiler);
593 }
594
TEST_F(ExpressionLimitTest,NestingInsideGlobalInitializer)595 TEST_F(ExpressionLimitTest, NestingInsideGlobalInitializer)
596 {
597 ShShaderSpec spec = SH_WEBGL_SPEC;
598 ShShaderOutput output = SH_ESSL_OUTPUT;
599 ShHandle compiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
600 ShCompileOptions compileOptions = {};
601 compileOptions.limitExpressionComplexity = true;
602
603 // Test nesting over the limit fails.
604 EXPECT_TRUE(CheckShaderCompilation(
605 compiler,
606 GenerateShaderWithNestingInsideGlobalInitializer(kMaxExpressionComplexity + 1).c_str(),
607 compileOptions, kExpressionTooComplex));
608 // Test that nesting way over the limit doesn't cause stack overflow but is handled
609 // gracefully.
610 EXPECT_TRUE(CheckShaderCompilation(
611 compiler, GenerateShaderWithNestingInsideGlobalInitializer(5000).c_str(), compileOptions,
612 kGlobalVariableInit));
613 // Test nesting over the limit without limit does not fail.
614 compileOptions.limitExpressionComplexity = false;
615 EXPECT_TRUE(CheckShaderCompilation(
616 compiler,
617 GenerateShaderWithNestingInsideGlobalInitializer(kMaxExpressionComplexity + 1).c_str(),
618 compileOptions, nullptr));
619 sh::Destruct(compiler);
620 }
621
TEST_F(ExpressionLimitTest,TooManyStructFields)622 TEST_F(ExpressionLimitTest, TooManyStructFields)
623 {
624 ShShaderSpec spec = SH_WEBGL2_SPEC;
625 ShShaderOutput output = SH_ESSL_OUTPUT;
626 ShHandle compiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &resources);
627 ShCompileOptions compileOptions = {};
628
629 std::ostringstream fs;
630 fs << R"(#version 300 es
631 precision highp float;
632 struct TooManyFields
633 {
634 )";
635 for (uint32_t i = 0; i < (1 << 16); ++i)
636 {
637 fs << " float field" << i << ";\n";
638 }
639 fs << R"(};
640 uniform B { TooManyFields s; };
641 out vec4 color;
642 void main() {
643 color = vec4(s.field0, 0.0, 0.0, 1.0);
644 })";
645
646 EXPECT_TRUE(CheckShaderCompilation(compiler, fs.str().c_str(), compileOptions, kTooManyFields));
647 sh::Destruct(compiler);
648 }
649