1 //
2 // Copyright 2015 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 // compiler_test.cpp:
7 //     utilities for compiler unit tests.
8 
9 #include "tests/test_utils/compiler_test.h"
10 
11 #include "angle_gl.h"
12 #include "compiler/translator/Compiler.h"
13 #include "compiler/translator/FunctionLookup.h"
14 #include "compiler/translator/tree_util/IntermTraverse.h"
15 
16 namespace sh
17 {
18 
19 namespace
20 {
21 constexpr char kBinaryBlob[] = "<binary blob>";
IsBinaryBlob(const std::string & code)22 bool IsBinaryBlob(const std::string &code)
23 {
24     return code == kBinaryBlob;
25 }
26 
GetSymbolTableMangledName(TIntermAggregate * node)27 ImmutableString GetSymbolTableMangledName(TIntermAggregate *node)
28 {
29     ASSERT(!node->isConstructor());
30     return TFunctionLookup::GetMangledName(node->getFunction()->name().data(),
31                                            *node->getSequence());
32 }
33 
34 class FunctionCallFinder : public TIntermTraverser
35 {
36   public:
FunctionCallFinder(const char * functionMangledName)37     FunctionCallFinder(const char *functionMangledName)
38         : TIntermTraverser(true, false, false),
39           mFunctionMangledName(functionMangledName),
40           mNodeFound(nullptr)
41     {}
42 
visitAggregate(Visit visit,TIntermAggregate * node)43     bool visitAggregate(Visit visit, TIntermAggregate *node) override
44     {
45         if (!node->isConstructor() && GetSymbolTableMangledName(node) == mFunctionMangledName)
46         {
47             mNodeFound = node;
48             return false;
49         }
50         return true;
51     }
52 
isFound() const53     bool isFound() const { return mNodeFound != nullptr; }
getNode() const54     const TIntermAggregate *getNode() const { return mNodeFound; }
55 
56   private:
57     const char *mFunctionMangledName;
58     TIntermAggregate *mNodeFound;
59 };
60 
61 }  // anonymous namespace
62 
compileTestShader(GLenum type,ShShaderSpec spec,ShShaderOutput output,const std::string & shaderString,ShBuiltInResources * resources,ShCompileOptions compileOptions,std::string * translatedCode,std::string * infoLog)63 bool compileTestShader(GLenum type,
64                        ShShaderSpec spec,
65                        ShShaderOutput output,
66                        const std::string &shaderString,
67                        ShBuiltInResources *resources,
68                        ShCompileOptions compileOptions,
69                        std::string *translatedCode,
70                        std::string *infoLog)
71 {
72     sh::TCompiler *translator = sh::ConstructCompiler(type, spec, output);
73     if (!translator->Init(*resources))
74     {
75         SafeDelete(translator);
76         return false;
77     }
78 
79     const char *shaderStrings[] = {shaderString.c_str()};
80 
81     bool compilationSuccess =
82         translator->compile(shaderStrings, 1, SH_OBJECT_CODE | compileOptions);
83     TInfoSink &infoSink = translator->getInfoSink();
84     if (translatedCode)
85     {
86         *translatedCode = infoSink.obj.isBinary() ? kBinaryBlob : infoSink.obj.c_str();
87     }
88     if (infoLog)
89     {
90         *infoLog = infoSink.info.c_str();
91     }
92     SafeDelete(translator);
93     return compilationSuccess;
94 }
95 
compileTestShader(GLenum type,ShShaderSpec spec,ShShaderOutput output,const std::string & shaderString,ShCompileOptions compileOptions,std::string * translatedCode,std::string * infoLog)96 bool compileTestShader(GLenum type,
97                        ShShaderSpec spec,
98                        ShShaderOutput output,
99                        const std::string &shaderString,
100                        ShCompileOptions compileOptions,
101                        std::string *translatedCode,
102                        std::string *infoLog)
103 {
104     ShBuiltInResources resources;
105     sh::InitBuiltInResources(&resources);
106     return compileTestShader(type, spec, output, shaderString, &resources, compileOptions,
107                              translatedCode, infoLog);
108 }
109 
MatchOutputCodeTest(GLenum shaderType,ShCompileOptions defaultCompileOptions,ShShaderOutput outputType)110 MatchOutputCodeTest::MatchOutputCodeTest(GLenum shaderType,
111                                          ShCompileOptions defaultCompileOptions,
112                                          ShShaderOutput outputType)
113     : mShaderType(shaderType), mDefaultCompileOptions(defaultCompileOptions)
114 {
115     sh::InitBuiltInResources(&mResources);
116     mOutputCode[outputType] = std::string();
117 }
118 
addOutputType(const ShShaderOutput outputType)119 void MatchOutputCodeTest::addOutputType(const ShShaderOutput outputType)
120 {
121     mOutputCode[outputType] = std::string();
122 }
123 
getResources()124 ShBuiltInResources *MatchOutputCodeTest::getResources()
125 {
126     return &mResources;
127 }
128 
compile(const std::string & shaderString)129 void MatchOutputCodeTest::compile(const std::string &shaderString)
130 {
131     compile(shaderString, mDefaultCompileOptions);
132 }
133 
compile(const std::string & shaderString,const ShCompileOptions compileOptions)134 void MatchOutputCodeTest::compile(const std::string &shaderString,
135                                   const ShCompileOptions compileOptions)
136 {
137     std::string infoLog;
138     for (auto &code : mOutputCode)
139     {
140         bool compilationSuccess =
141             compileWithSettings(code.first, shaderString, compileOptions, &code.second, &infoLog);
142         if (!compilationSuccess)
143         {
144             FAIL() << "Shader compilation failed:\n" << infoLog;
145         }
146     }
147 }
148 
compileWithSettings(ShShaderOutput output,const std::string & shaderString,const ShCompileOptions compileOptions,std::string * translatedCode,std::string * infoLog)149 bool MatchOutputCodeTest::compileWithSettings(ShShaderOutput output,
150                                               const std::string &shaderString,
151                                               const ShCompileOptions compileOptions,
152                                               std::string *translatedCode,
153                                               std::string *infoLog)
154 {
155     return compileTestShader(mShaderType, SH_GLES3_1_SPEC, output, shaderString, &mResources,
156                              compileOptions, translatedCode, infoLog);
157 }
158 
foundInCodeRegex(ShShaderOutput output,const std::regex & regexToFind,std::smatch * match) const159 bool MatchOutputCodeTest::foundInCodeRegex(ShShaderOutput output,
160                                            const std::regex &regexToFind,
161                                            std::smatch *match) const
162 {
163     const auto code = mOutputCode.find(output);
164     EXPECT_NE(mOutputCode.end(), code);
165     if (code == mOutputCode.end())
166     {
167         return std::string::npos;
168     }
169 
170     // No meaningful check for binary blobs
171     if (IsBinaryBlob(code->second))
172     {
173         return true;
174     }
175 
176     if (match)
177     {
178         return std::regex_search(code->second, *match, regexToFind);
179     }
180     else
181     {
182         return std::regex_search(code->second, regexToFind);
183     }
184 }
185 
foundInCode(ShShaderOutput output,const char * stringToFind) const186 bool MatchOutputCodeTest::foundInCode(ShShaderOutput output, const char *stringToFind) const
187 {
188     const auto code = mOutputCode.find(output);
189     EXPECT_NE(mOutputCode.end(), code);
190     if (code == mOutputCode.end())
191     {
192         return std::string::npos;
193     }
194 
195     // No meaningful check for binary blobs
196     if (IsBinaryBlob(code->second))
197     {
198         return true;
199     }
200 
201     return code->second.find(stringToFind) != std::string::npos;
202 }
203 
foundInCodeInOrder(ShShaderOutput output,std::vector<const char * > stringsToFind)204 bool MatchOutputCodeTest::foundInCodeInOrder(ShShaderOutput output,
205                                              std::vector<const char *> stringsToFind)
206 {
207     const auto code = mOutputCode.find(output);
208     EXPECT_NE(mOutputCode.end(), code);
209     if (code == mOutputCode.end())
210     {
211         return false;
212     }
213 
214     // No meaningful check for binary blobs
215     if (IsBinaryBlob(code->second))
216     {
217         return true;
218     }
219 
220     size_t currentPos = 0;
221     for (const char *stringToFind : stringsToFind)
222     {
223         auto position = code->second.find(stringToFind, currentPos);
224         if (position == std::string::npos)
225         {
226             return false;
227         }
228         currentPos = position + strlen(stringToFind);
229     }
230     return true;
231 }
232 
foundInCode(ShShaderOutput output,const char * stringToFind,const int expectedOccurrences) const233 bool MatchOutputCodeTest::foundInCode(ShShaderOutput output,
234                                       const char *stringToFind,
235                                       const int expectedOccurrences) const
236 {
237     const auto code = mOutputCode.find(output);
238     EXPECT_NE(mOutputCode.end(), code);
239     if (code == mOutputCode.end())
240     {
241         return false;
242     }
243 
244     // No meaningful check for binary blobs
245     if (IsBinaryBlob(code->second))
246     {
247         return true;
248     }
249 
250     size_t currentPos  = 0;
251     int occurencesLeft = expectedOccurrences;
252 
253     const size_t searchStringLength = strlen(stringToFind);
254 
255     while (occurencesLeft-- > 0)
256     {
257         auto position = code->second.find(stringToFind, currentPos);
258         if (position == std::string::npos)
259         {
260             return false;
261         }
262         // Search strings should not overlap.
263         currentPos = position + searchStringLength;
264     }
265     // Make sure that there aren't extra occurrences.
266     return code->second.find(stringToFind, currentPos) == std::string::npos;
267 }
268 
foundInCode(const char * stringToFind) const269 bool MatchOutputCodeTest::foundInCode(const char *stringToFind) const
270 {
271     for (auto &code : mOutputCode)
272     {
273         if (!foundInCode(code.first, stringToFind))
274         {
275             return false;
276         }
277     }
278     return true;
279 }
280 
foundInCodeRegex(const std::regex & regexToFind,std::smatch * match) const281 bool MatchOutputCodeTest::foundInCodeRegex(const std::regex &regexToFind, std::smatch *match) const
282 {
283     for (auto &code : mOutputCode)
284     {
285         if (!foundInCodeRegex(code.first, regexToFind, match))
286         {
287             return false;
288         }
289     }
290     return true;
291 }
292 
foundInCode(const char * stringToFind,const int expectedOccurrences) const293 bool MatchOutputCodeTest::foundInCode(const char *stringToFind, const int expectedOccurrences) const
294 {
295     for (auto &code : mOutputCode)
296     {
297         if (!foundInCode(code.first, stringToFind, expectedOccurrences))
298         {
299             return false;
300         }
301     }
302     return true;
303 }
304 
foundInCodeInOrder(std::vector<const char * > stringsToFind)305 bool MatchOutputCodeTest::foundInCodeInOrder(std::vector<const char *> stringsToFind)
306 {
307     for (auto &code : mOutputCode)
308     {
309         if (!foundInCodeInOrder(code.first, stringsToFind))
310         {
311             return false;
312         }
313     }
314     return true;
315 }
316 
notFoundInCode(const char * stringToFind) const317 bool MatchOutputCodeTest::notFoundInCode(const char *stringToFind) const
318 {
319     for (auto &code : mOutputCode)
320     {
321         // No meaningful check for binary blobs
322         if (IsBinaryBlob(code.second))
323         {
324             continue;
325         }
326 
327         if (foundInCode(code.first, stringToFind))
328         {
329             return false;
330         }
331     }
332     return true;
333 }
334 
FindFunctionCallNode(TIntermNode * root,const TString & functionMangledName)335 const TIntermAggregate *FindFunctionCallNode(TIntermNode *root, const TString &functionMangledName)
336 {
337     FunctionCallFinder finder(functionMangledName.c_str());
338     root->traverse(&finder);
339     return finder.getNode();
340 }
341 
342 }  // namespace sh
343