1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <stdlib.h>
6 
7 #include "src/v8.h"
8 
9 #include "src/ast/ast.h"
10 #include "src/ast/ast-expression-visitor.h"
11 #include "src/ast/scopes.h"
12 #include "src/parsing/parser.h"
13 #include "src/parsing/rewriter.h"
14 #include "test/cctest/cctest.h"
15 #include "test/cctest/expression-type-collector.h"
16 #include "test/cctest/expression-type-collector-macros.h"
17 
18 using namespace v8::internal;
19 
20 namespace {
21 
CollectTypes(HandleAndZoneScope * handles,const char * source,ZoneVector<ExpressionTypeEntry> * dst)22 static void CollectTypes(HandleAndZoneScope* handles, const char* source,
23                          ZoneVector<ExpressionTypeEntry>* dst) {
24   i::Isolate* isolate = CcTest::i_isolate();
25   i::Factory* factory = isolate->factory();
26 
27   i::Handle<i::String> source_code =
28       factory->NewStringFromUtf8(i::CStrVector(source)).ToHandleChecked();
29 
30   i::Handle<i::Script> script = factory->NewScript(source_code);
31 
32   i::ParseInfo info(handles->main_zone(), script);
33   i::Parser parser(&info);
34   parser.set_allow_harmony_sloppy(true);
35   info.set_global();
36   info.set_lazy(false);
37   info.set_allow_lazy_parsing(false);
38   info.set_toplevel(true);
39 
40   CHECK(i::Compiler::ParseAndAnalyze(&info));
41 
42   ExpressionTypeCollector(
43       isolate,
44       info.scope()->declarations()->at(0)->AsFunctionDeclaration()->fun(), dst)
45       .Run();
46 }
47 
48 }  // namespace
49 
50 
TEST(VisitExpressions)51 TEST(VisitExpressions) {
52   v8::V8::Initialize();
53   HandleAndZoneScope handles;
54   ZoneVector<ExpressionTypeEntry> types(handles.main_zone());
55   const char test_function[] =
56       "function GeometricMean(stdlib, foreign, buffer) {\n"
57       "  \"use asm\";\n"
58       "\n"
59       "  var exp = stdlib.Math.exp;\n"
60       "  var log = stdlib.Math.log;\n"
61       "  var values = new stdlib.Float64Array(buffer);\n"
62       "\n"
63       "  function logSum(start, end) {\n"
64       "    start = start|0;\n"
65       "    end = end|0;\n"
66       "\n"
67       "    var sum = 0.0, p = 0, q = 0;\n"
68       "\n"
69       "    // asm.js forces byte addressing of the heap by requiring shifting "
70       "by 3\n"
71       "    for (p = start << 3, q = end << 3; (p|0) < (q|0); p = (p + 8)|0) {\n"
72       "      sum = sum + +log(values[p>>3]);\n"
73       "    }\n"
74       "\n"
75       "    return +sum;\n"
76       "  }\n"
77       "\n"
78       " function geometricMean(start, end) {\n"
79       "    start = start|0;\n"
80       "    end = end|0;\n"
81       "\n"
82       "    return +exp(+logSum(start, end) / +((end - start)|0));\n"
83       "  }\n"
84       "\n"
85       "  return { geometricMean: geometricMean };\n"
86       "}\n";
87 
88   CollectTypes(&handles, test_function, &types);
89   CHECK_TYPES_BEGIN {
90     // function logSum
91     CHECK_EXPR(FunctionLiteral, Bounds::Unbounded()) {
92       CHECK_EXPR(FunctionLiteral, Bounds::Unbounded()) {
93         CHECK_EXPR(Assignment, Bounds::Unbounded()) {
94           CHECK_VAR(start, Bounds::Unbounded());
95           CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
96             CHECK_VAR(start, Bounds::Unbounded());
97             CHECK_EXPR(Literal, Bounds::Unbounded());
98           }
99         }
100         CHECK_EXPR(Assignment, Bounds::Unbounded()) {
101           CHECK_VAR(end, Bounds::Unbounded());
102           CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
103             CHECK_VAR(end, Bounds::Unbounded());
104             CHECK_EXPR(Literal, Bounds::Unbounded());
105           }
106         }
107         CHECK_EXPR(Assignment, Bounds::Unbounded()) {
108           CHECK_VAR(sum, Bounds::Unbounded());
109           CHECK_EXPR(Literal, Bounds::Unbounded());
110         }
111         CHECK_EXPR(Assignment, Bounds::Unbounded()) {
112           CHECK_VAR(p, Bounds::Unbounded());
113           CHECK_EXPR(Literal, Bounds::Unbounded());
114         }
115         CHECK_EXPR(Assignment, Bounds::Unbounded()) {
116           CHECK_VAR(q, Bounds::Unbounded());
117           CHECK_EXPR(Literal, Bounds::Unbounded());
118         }
119         // for (p = start << 3, q = end << 3;
120         CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
121           CHECK_EXPR(Assignment, Bounds::Unbounded()) {
122             CHECK_VAR(p, Bounds::Unbounded());
123             CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
124               CHECK_VAR(start, Bounds::Unbounded());
125               CHECK_EXPR(Literal, Bounds::Unbounded());
126             }
127           }
128           CHECK_EXPR(Assignment, Bounds::Unbounded()) {
129             CHECK_VAR(q, Bounds::Unbounded());
130             CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
131               CHECK_VAR(end, Bounds::Unbounded());
132               CHECK_EXPR(Literal, Bounds::Unbounded());
133             }
134           }
135         }
136         // (p|0) < (q|0);
137         CHECK_EXPR(CompareOperation, Bounds::Unbounded()) {
138           CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
139             CHECK_VAR(p, Bounds::Unbounded());
140             CHECK_EXPR(Literal, Bounds::Unbounded());
141           }
142           CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
143             CHECK_VAR(q, Bounds::Unbounded());
144             CHECK_EXPR(Literal, Bounds::Unbounded());
145           }
146         }
147         // p = (p + 8)|0) {\n"
148         CHECK_EXPR(Assignment, Bounds::Unbounded()) {
149           CHECK_VAR(p, Bounds::Unbounded());
150           CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
151             CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
152               CHECK_VAR(p, Bounds::Unbounded());
153               CHECK_EXPR(Literal, Bounds::Unbounded());
154             }
155             CHECK_EXPR(Literal, Bounds::Unbounded());
156           }
157         }
158         // sum = sum + +log(values[p>>3]);
159         CHECK_EXPR(Assignment, Bounds::Unbounded()) {
160           CHECK_VAR(sum, Bounds::Unbounded());
161           CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
162             CHECK_VAR(sum, Bounds::Unbounded());
163             CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
164               CHECK_EXPR(Call, Bounds::Unbounded()) {
165                 CHECK_VAR(log, Bounds::Unbounded());
166                 CHECK_EXPR(Property, Bounds::Unbounded()) {
167                   CHECK_VAR(values, Bounds::Unbounded());
168                   CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
169                     CHECK_VAR(p, Bounds::Unbounded());
170                     CHECK_EXPR(Literal, Bounds::Unbounded());
171                   }
172                 }
173               }
174               CHECK_EXPR(Literal, Bounds::Unbounded());
175             }
176           }
177         }
178         // return +sum;
179         CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
180           CHECK_VAR(sum, Bounds::Unbounded());
181           CHECK_EXPR(Literal, Bounds::Unbounded());
182         }
183       }
184       // function geometricMean
185       CHECK_EXPR(FunctionLiteral, Bounds::Unbounded()) {
186         CHECK_EXPR(Assignment, Bounds::Unbounded()) {
187           CHECK_VAR(start, Bounds::Unbounded());
188           CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
189             CHECK_VAR(start, Bounds::Unbounded());
190             CHECK_EXPR(Literal, Bounds::Unbounded());
191           }
192         }
193         CHECK_EXPR(Assignment, Bounds::Unbounded()) {
194           CHECK_VAR(end, Bounds::Unbounded());
195           CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
196             CHECK_VAR(end, Bounds::Unbounded());
197             CHECK_EXPR(Literal, Bounds::Unbounded());
198           }
199         }
200         // return +exp(+logSum(start, end) / +((end - start)|0));
201         CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
202           CHECK_EXPR(Call, Bounds::Unbounded()) {
203             CHECK_VAR(exp, Bounds::Unbounded());
204             CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
205               CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
206                 CHECK_EXPR(Call, Bounds::Unbounded()) {
207                   CHECK_VAR(logSum, Bounds::Unbounded());
208                   CHECK_VAR(start, Bounds::Unbounded());
209                   CHECK_VAR(end, Bounds::Unbounded());
210                 }
211                 CHECK_EXPR(Literal, Bounds::Unbounded());
212               }
213               CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
214                 CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
215                   CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
216                     CHECK_VAR(end, Bounds::Unbounded());
217                     CHECK_VAR(start, Bounds::Unbounded());
218                   }
219                   CHECK_EXPR(Literal, Bounds::Unbounded());
220                 }
221                 CHECK_EXPR(Literal, Bounds::Unbounded());
222               }
223             }
224           }
225           CHECK_EXPR(Literal, Bounds::Unbounded());
226         }
227       }
228       // "use asm";
229       CHECK_EXPR(Literal, Bounds::Unbounded());
230       // var exp = stdlib.Math.exp;
231       CHECK_EXPR(Assignment, Bounds::Unbounded()) {
232         CHECK_VAR(exp, Bounds::Unbounded());
233         CHECK_EXPR(Property, Bounds::Unbounded()) {
234           CHECK_EXPR(Property, Bounds::Unbounded()) {
235             CHECK_VAR(stdlib, Bounds::Unbounded());
236             CHECK_EXPR(Literal, Bounds::Unbounded());
237           }
238           CHECK_EXPR(Literal, Bounds::Unbounded());
239         }
240       }
241       // var log = stdlib.Math.log;
242       CHECK_EXPR(Assignment, Bounds::Unbounded()) {
243         CHECK_VAR(log, Bounds::Unbounded());
244         CHECK_EXPR(Property, Bounds::Unbounded()) {
245           CHECK_EXPR(Property, Bounds::Unbounded()) {
246             CHECK_VAR(stdlib, Bounds::Unbounded());
247             CHECK_EXPR(Literal, Bounds::Unbounded());
248           }
249           CHECK_EXPR(Literal, Bounds::Unbounded());
250         }
251       }
252       // var values = new stdlib.Float64Array(buffer);
253       CHECK_EXPR(Assignment, Bounds::Unbounded()) {
254         CHECK_VAR(values, Bounds::Unbounded());
255         CHECK_EXPR(CallNew, Bounds::Unbounded()) {
256           CHECK_EXPR(Property, Bounds::Unbounded()) {
257             CHECK_VAR(stdlib, Bounds::Unbounded());
258             CHECK_EXPR(Literal, Bounds::Unbounded());
259           }
260           CHECK_VAR(buffer, Bounds::Unbounded());
261         }
262       }
263       // return { geometricMean: geometricMean };
264       CHECK_EXPR(ObjectLiteral, Bounds::Unbounded()) {
265         CHECK_VAR(geometricMean, Bounds::Unbounded());
266       }
267     }
268   }
269   CHECK_TYPES_END
270 }
271 
272 
TEST(VisitConditional)273 TEST(VisitConditional) {
274   v8::V8::Initialize();
275   HandleAndZoneScope handles;
276   ZoneVector<ExpressionTypeEntry> types(handles.main_zone());
277   // Check that traversing the ternary operator works.
278   const char test_function[] =
279       "function foo() {\n"
280       "  var a, b, c;\n"
281       "  var x = a ? b : c;\n"
282       "}\n";
283   CollectTypes(&handles, test_function, &types);
284   CHECK_TYPES_BEGIN {
285     CHECK_EXPR(FunctionLiteral, Bounds::Unbounded()) {
286       CHECK_EXPR(Assignment, Bounds::Unbounded()) {
287         CHECK_VAR(x, Bounds::Unbounded());
288         CHECK_EXPR(Conditional, Bounds::Unbounded()) {
289           CHECK_VAR(a, Bounds::Unbounded());
290           CHECK_VAR(b, Bounds::Unbounded());
291           CHECK_VAR(c, Bounds::Unbounded());
292         }
293       }
294     }
295   }
296   CHECK_TYPES_END
297 }
298 
299 
TEST(VisitEmptyForStatment)300 TEST(VisitEmptyForStatment) {
301   v8::V8::Initialize();
302   HandleAndZoneScope handles;
303   ZoneVector<ExpressionTypeEntry> types(handles.main_zone());
304   // Check that traversing an empty for statement works.
305   const char test_function[] =
306       "function foo() {\n"
307       "  for (;;) {}\n"
308       "}\n";
309   CollectTypes(&handles, test_function, &types);
310   CHECK_TYPES_BEGIN {
311     CHECK_EXPR(FunctionLiteral, Bounds::Unbounded()) {}
312   }
313   CHECK_TYPES_END
314 }
315 
316 
TEST(VisitSwitchStatment)317 TEST(VisitSwitchStatment) {
318   v8::V8::Initialize();
319   HandleAndZoneScope handles;
320   ZoneVector<ExpressionTypeEntry> types(handles.main_zone());
321   // Check that traversing a switch with a default works.
322   const char test_function[] =
323       "function foo() {\n"
324       "  switch (0) { case 1: break; default: break; }\n"
325       "}\n";
326   CollectTypes(&handles, test_function, &types);
327   CHECK_TYPES_BEGIN {
328     CHECK_EXPR(FunctionLiteral, Bounds::Unbounded()) {
329       CHECK_EXPR(Assignment, Bounds::Unbounded()) {
330         CHECK_VAR(.switch_tag, Bounds::Unbounded());
331         CHECK_EXPR(Literal, Bounds::Unbounded());
332       }
333       CHECK_EXPR(Literal, Bounds::Unbounded());
334       CHECK_VAR(.switch_tag, Bounds::Unbounded());
335       CHECK_EXPR(Literal, Bounds::Unbounded());
336     }
337   }
338   CHECK_TYPES_END
339 }
340 
341 
TEST(VisitThrow)342 TEST(VisitThrow) {
343   v8::V8::Initialize();
344   HandleAndZoneScope handles;
345   ZoneVector<ExpressionTypeEntry> types(handles.main_zone());
346   // Check that traversing an empty for statement works.
347   const char test_function[] =
348       "function foo() {\n"
349       "  throw 123;\n"
350       "}\n";
351   CollectTypes(&handles, test_function, &types);
352   CHECK_TYPES_BEGIN {
353     CHECK_EXPR(FunctionLiteral, Bounds::Unbounded()) {
354       CHECK_EXPR(Throw, Bounds::Unbounded()) {
355         CHECK_EXPR(Literal, Bounds::Unbounded());
356       }
357     }
358   }
359   CHECK_TYPES_END
360 }
361 
362 
TEST(VisitYield)363 TEST(VisitYield) {
364   v8::V8::Initialize();
365   HandleAndZoneScope handles;
366   ZoneVector<ExpressionTypeEntry> types(handles.main_zone());
367   // Check that traversing an empty for statement works.
368   const char test_function[] =
369       "function* foo() {\n"
370       "  yield 123;\n"
371       "}\n";
372   CollectTypes(&handles, test_function, &types);
373   CHECK_TYPES_BEGIN {
374     CHECK_EXPR(FunctionLiteral, Bounds::Unbounded()) {
375       // Generator function yields generator on entry.
376       CHECK_EXPR(Yield, Bounds::Unbounded()) {
377         CHECK_VAR(.generator_object, Bounds::Unbounded());
378         CHECK_EXPR(Assignment, Bounds::Unbounded()) {
379           CHECK_VAR(.generator_object, Bounds::Unbounded());
380           CHECK_EXPR(CallRuntime, Bounds::Unbounded());
381         }
382       }
383       // Then yields undefined.
384       CHECK_EXPR(Yield, Bounds::Unbounded()) {
385         CHECK_VAR(.generator_object, Bounds::Unbounded());
386         CHECK_EXPR(Literal, Bounds::Unbounded());
387       }
388       // Then yields 123.
389       CHECK_EXPR(Yield, Bounds::Unbounded()) {
390         CHECK_VAR(.generator_object, Bounds::Unbounded());
391         CHECK_EXPR(Literal, Bounds::Unbounded());
392       }
393     }
394   }
395   CHECK_TYPES_END
396 }
397 
398 
TEST(VisitSkipping)399 TEST(VisitSkipping) {
400   v8::V8::Initialize();
401   HandleAndZoneScope handles;
402   ZoneVector<ExpressionTypeEntry> types(handles.main_zone());
403   // Check that traversing an empty for statement works.
404   const char test_function[] =
405       "function foo(x) {\n"
406       "  return (x + x) + 1;\n"
407       "}\n";
408   CollectTypes(&handles, test_function, &types);
409   CHECK_TYPES_BEGIN {
410     CHECK_EXPR(FunctionLiteral, Bounds::Unbounded()) {
411       CHECK_EXPR(BinaryOperation, Bounds::Unbounded()) {
412         // Skip x + x
413         CHECK_SKIP();
414         CHECK_EXPR(Literal, Bounds::Unbounded());
415       }
416     }
417   }
418   CHECK_TYPES_END
419 }
420