1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 //     * Redistributions of source code must retain the above copyright
7 //       notice, this list of conditions and the following disclaimer.
8 //     * Redistributions in binary form must reproduce the above
9 //       copyright notice, this list of conditions and the following
10 //       disclaimer in the documentation and/or other materials provided
11 //       with the distribution.
12 //     * Neither the name of Google Inc. nor the names of its
13 //       contributors may be used to endorse or promote products derived
14 //       from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 #include <stdlib.h>
29 #include <wchar.h>
30 
31 #include "src/v8.h"
32 
33 #include "src/compiler.h"
34 #include "src/disasm.h"
35 #include "src/parser.h"
36 #include "test/cctest/cctest.h"
37 
38 using namespace v8::internal;
39 
GetGlobalProperty(const char * name)40 static Handle<Object> GetGlobalProperty(const char* name) {
41   Isolate* isolate = CcTest::i_isolate();
42   return Object::GetProperty(
43       isolate, isolate->global_object(), name).ToHandleChecked();
44 }
45 
46 
SetGlobalProperty(const char * name,Object * value)47 static void SetGlobalProperty(const char* name, Object* value) {
48   Isolate* isolate = CcTest::i_isolate();
49   Handle<Object> object(value, isolate);
50   Handle<String> internalized_name =
51       isolate->factory()->InternalizeUtf8String(name);
52   Handle<JSObject> global(isolate->context()->global_object());
53   Runtime::SetObjectProperty(isolate, global, internalized_name, object,
54                              SLOPPY).Check();
55 }
56 
57 
Compile(const char * source)58 static Handle<JSFunction> Compile(const char* source) {
59   Isolate* isolate = CcTest::i_isolate();
60   Handle<String> source_code = isolate->factory()->NewStringFromUtf8(
61       CStrVector(source)).ToHandleChecked();
62   Handle<SharedFunctionInfo> shared_function = Compiler::CompileScript(
63       source_code, Handle<String>(), 0, 0, false,
64       Handle<Context>(isolate->native_context()), NULL, NULL,
65       v8::ScriptCompiler::kNoCompileOptions, NOT_NATIVES_CODE);
66   return isolate->factory()->NewFunctionFromSharedFunctionInfo(
67       shared_function, isolate->native_context());
68 }
69 
70 
Inc(Isolate * isolate,int x)71 static double Inc(Isolate* isolate, int x) {
72   const char* source = "result = %d + 1;";
73   EmbeddedVector<char, 512> buffer;
74   SNPrintF(buffer, source, x);
75 
76   Handle<JSFunction> fun = Compile(buffer.start());
77   if (fun.is_null()) return -1;
78 
79   Handle<JSObject> global(isolate->context()->global_object());
80   Execution::Call(isolate, fun, global, 0, NULL).Check();
81   return GetGlobalProperty("result")->Number();
82 }
83 
84 
TEST(Inc)85 TEST(Inc) {
86   CcTest::InitializeVM();
87   v8::HandleScope scope(CcTest::isolate());
88   CHECK_EQ(4.0, Inc(CcTest::i_isolate(), 3));
89 }
90 
91 
Add(Isolate * isolate,int x,int y)92 static double Add(Isolate* isolate, int x, int y) {
93   Handle<JSFunction> fun = Compile("result = x + y;");
94   if (fun.is_null()) return -1;
95 
96   SetGlobalProperty("x", Smi::FromInt(x));
97   SetGlobalProperty("y", Smi::FromInt(y));
98   Handle<JSObject> global(isolate->context()->global_object());
99   Execution::Call(isolate, fun, global, 0, NULL).Check();
100   return GetGlobalProperty("result")->Number();
101 }
102 
103 
TEST(Add)104 TEST(Add) {
105   CcTest::InitializeVM();
106   v8::HandleScope scope(CcTest::isolate());
107   CHECK_EQ(5.0, Add(CcTest::i_isolate(), 2, 3));
108 }
109 
110 
Abs(Isolate * isolate,int x)111 static double Abs(Isolate* isolate, int x) {
112   Handle<JSFunction> fun = Compile("if (x < 0) result = -x; else result = x;");
113   if (fun.is_null()) return -1;
114 
115   SetGlobalProperty("x", Smi::FromInt(x));
116   Handle<JSObject> global(isolate->context()->global_object());
117   Execution::Call(isolate, fun, global, 0, NULL).Check();
118   return GetGlobalProperty("result")->Number();
119 }
120 
121 
TEST(Abs)122 TEST(Abs) {
123   CcTest::InitializeVM();
124   v8::HandleScope scope(CcTest::isolate());
125   CHECK_EQ(3.0, Abs(CcTest::i_isolate(), -3));
126 }
127 
128 
Sum(Isolate * isolate,int n)129 static double Sum(Isolate* isolate, int n) {
130   Handle<JSFunction> fun =
131       Compile("s = 0; while (n > 0) { s += n; n -= 1; }; result = s;");
132   if (fun.is_null()) return -1;
133 
134   SetGlobalProperty("n", Smi::FromInt(n));
135   Handle<JSObject> global(isolate->context()->global_object());
136   Execution::Call(isolate, fun, global, 0, NULL).Check();
137   return GetGlobalProperty("result")->Number();
138 }
139 
140 
TEST(Sum)141 TEST(Sum) {
142   CcTest::InitializeVM();
143   v8::HandleScope scope(CcTest::isolate());
144   CHECK_EQ(5050.0, Sum(CcTest::i_isolate(), 100));
145 }
146 
147 
TEST(Print)148 TEST(Print) {
149   v8::HandleScope scope(CcTest::isolate());
150   v8::Local<v8::Context> context = CcTest::NewContext(PRINT_EXTENSION);
151   v8::Context::Scope context_scope(context);
152   const char* source = "for (n = 0; n < 100; ++n) print(n, 1, 2);";
153   Handle<JSFunction> fun = Compile(source);
154   if (fun.is_null()) return;
155   Handle<JSObject> global(CcTest::i_isolate()->context()->global_object());
156   Execution::Call(CcTest::i_isolate(), fun, global, 0, NULL).Check();
157 }
158 
159 
160 // The following test method stems from my coding efforts today. It
161 // tests all the functionality I have added to the compiler today
TEST(Stuff)162 TEST(Stuff) {
163   CcTest::InitializeVM();
164   v8::HandleScope scope(CcTest::isolate());
165   const char* source =
166     "r = 0;\n"
167     "a = new Object;\n"
168     "if (a == a) r+=1;\n"  // 1
169     "if (a != new Object()) r+=2;\n"  // 2
170     "a.x = 42;\n"
171     "if (a.x == 42) r+=4;\n"  // 4
172     "function foo() { var x = 87; return x; }\n"
173     "if (foo() == 87) r+=8;\n"  // 8
174     "function bar() { var x; x = 99; return x; }\n"
175     "if (bar() == 99) r+=16;\n"  // 16
176     "function baz() { var x = 1, y, z = 2; y = 3; return x + y + z; }\n"
177     "if (baz() == 6) r+=32;\n"  // 32
178     "function Cons0() { this.x = 42; this.y = 87; }\n"
179     "if (new Cons0().x == 42) r+=64;\n"  // 64
180     "if (new Cons0().y == 87) r+=128;\n"  // 128
181     "function Cons2(x, y) { this.sum = x + y; }\n"
182     "if (new Cons2(3,4).sum == 7) r+=256;";  // 256
183 
184   Handle<JSFunction> fun = Compile(source);
185   CHECK(!fun.is_null());
186   Handle<JSObject> global(CcTest::i_isolate()->context()->global_object());
187   Execution::Call(
188       CcTest::i_isolate(), fun, global, 0, NULL).Check();
189   CHECK_EQ(511.0, GetGlobalProperty("r")->Number());
190 }
191 
192 
TEST(UncaughtThrow)193 TEST(UncaughtThrow) {
194   CcTest::InitializeVM();
195   v8::HandleScope scope(CcTest::isolate());
196 
197   const char* source = "throw 42;";
198   Handle<JSFunction> fun = Compile(source);
199   CHECK(!fun.is_null());
200   Isolate* isolate = fun->GetIsolate();
201   Handle<JSObject> global(isolate->context()->global_object());
202   CHECK(Execution::Call(isolate, fun, global, 0, NULL).is_null());
203   CHECK_EQ(42.0, isolate->pending_exception()->Number());
204 }
205 
206 
207 // Tests calling a builtin function from C/C++ code, and the builtin function
208 // performs GC. It creates a stack frame looks like following:
209 //   | C (PerformGC) |
210 //   |   JS-to-C     |
211 //   |      JS       |
212 //   |   C-to-JS     |
TEST(C2JSFrames)213 TEST(C2JSFrames) {
214   FLAG_expose_gc = true;
215   v8::HandleScope scope(CcTest::isolate());
216   v8::Local<v8::Context> context =
217     CcTest::NewContext(PRINT_EXTENSION | GC_EXTENSION);
218   v8::Context::Scope context_scope(context);
219 
220   const char* source = "function foo(a) { gc(), print(a); }";
221 
222   Handle<JSFunction> fun0 = Compile(source);
223   CHECK(!fun0.is_null());
224   Isolate* isolate = fun0->GetIsolate();
225 
226   // Run the generated code to populate the global object with 'foo'.
227   Handle<JSObject> global(isolate->context()->global_object());
228   Execution::Call(isolate, fun0, global, 0, NULL).Check();
229 
230   Handle<String> foo_string =
231       isolate->factory()->InternalizeOneByteString(STATIC_CHAR_VECTOR("foo"));
232   Handle<Object> fun1 = Object::GetProperty(
233       isolate->global_object(), foo_string).ToHandleChecked();
234   CHECK(fun1->IsJSFunction());
235 
236   Handle<Object> argv[] = {isolate->factory()->InternalizeOneByteString(
237       STATIC_CHAR_VECTOR("hello"))};
238   Execution::Call(isolate,
239                   Handle<JSFunction>::cast(fun1),
240                   global,
241                   arraysize(argv),
242                   argv).Check();
243 }
244 
245 
246 // Regression 236. Calling InitLineEnds on a Script with undefined
247 // source resulted in crash.
TEST(Regression236)248 TEST(Regression236) {
249   CcTest::InitializeVM();
250   Isolate* isolate = CcTest::i_isolate();
251   Factory* factory = isolate->factory();
252   v8::HandleScope scope(CcTest::isolate());
253 
254   Handle<Script> script = factory->NewScript(factory->empty_string());
255   script->set_source(CcTest::heap()->undefined_value());
256   CHECK_EQ(-1, Script::GetLineNumber(script, 0));
257   CHECK_EQ(-1, Script::GetLineNumber(script, 100));
258   CHECK_EQ(-1, Script::GetLineNumber(script, -1));
259 }
260 
261 
TEST(GetScriptLineNumber)262 TEST(GetScriptLineNumber) {
263   LocalContext context;
264   v8::HandleScope scope(CcTest::isolate());
265   v8::ScriptOrigin origin =
266       v8::ScriptOrigin(v8::String::NewFromUtf8(CcTest::isolate(), "test"));
267   const char function_f[] = "function f() {}";
268   const int max_rows = 1000;
269   const int buffer_size = max_rows + sizeof(function_f);
270   ScopedVector<char> buffer(buffer_size);
271   memset(buffer.start(), '\n', buffer_size - 1);
272   buffer[buffer_size - 1] = '\0';
273 
274   for (int i = 0; i < max_rows; ++i) {
275     if (i > 0)
276       buffer[i - 1] = '\n';
277     MemCopy(&buffer[i], function_f, sizeof(function_f) - 1);
278     v8::Handle<v8::String> script_body =
279         v8::String::NewFromUtf8(CcTest::isolate(), buffer.start());
280     v8::Script::Compile(script_body, &origin)->Run();
281     v8::Local<v8::Function> f =
282         v8::Local<v8::Function>::Cast(context->Global()->Get(
283             v8::String::NewFromUtf8(CcTest::isolate(), "f")));
284     CHECK_EQ(i, f->GetScriptLineNumber());
285   }
286 }
287 
288 
TEST(FeedbackVectorPreservedAcrossRecompiles)289 TEST(FeedbackVectorPreservedAcrossRecompiles) {
290   if (i::FLAG_always_opt || !i::FLAG_crankshaft) return;
291   i::FLAG_allow_natives_syntax = true;
292   CcTest::InitializeVM();
293   if (!CcTest::i_isolate()->use_crankshaft()) return;
294   v8::HandleScope scope(CcTest::isolate());
295 
296   // Make sure function f has a call that uses a type feedback slot.
297   CompileRun("function fun() {};"
298              "fun1 = fun;"
299              "function f(a) { a(); } f(fun1);");
300 
301   Handle<JSFunction> f =
302       v8::Utils::OpenHandle(
303           *v8::Handle<v8::Function>::Cast(
304               CcTest::global()->Get(v8_str("f"))));
305 
306   // We shouldn't have deoptimization support. We want to recompile and
307   // verify that our feedback vector preserves information.
308   CHECK(!f->shared()->has_deoptimization_support());
309   Handle<FixedArray> feedback_vector(f->shared()->feedback_vector());
310 
311   // Verify that we gathered feedback.
312   int expected_count = FLAG_vector_ics ? 2 : 1;
313   CHECK_EQ(expected_count, feedback_vector->length());
314   CHECK(feedback_vector->get(expected_count - 1)->IsJSFunction());
315 
316   CompileRun("%OptimizeFunctionOnNextCall(f); f(fun1);");
317 
318   // Verify that the feedback is still "gathered" despite a recompilation
319   // of the full code.
320   CHECK(f->IsOptimized());
321   CHECK(f->shared()->has_deoptimization_support());
322   CHECK(f->shared()->feedback_vector()->
323         get(expected_count - 1)->IsJSFunction());
324 }
325 
326 
TEST(FeedbackVectorUnaffectedByScopeChanges)327 TEST(FeedbackVectorUnaffectedByScopeChanges) {
328   if (i::FLAG_always_opt || !i::FLAG_lazy) return;
329   CcTest::InitializeVM();
330   v8::HandleScope scope(CcTest::isolate());
331 
332   CompileRun("function builder() {"
333              "  call_target = function() { return 3; };"
334              "  return (function() {"
335              "    eval('');"
336              "    return function() {"
337              "      'use strict';"
338              "      call_target();"
339              "    }"
340              "  })();"
341              "}"
342              "morphing_call = builder();");
343 
344   Handle<JSFunction> f =
345       v8::Utils::OpenHandle(
346           *v8::Handle<v8::Function>::Cast(
347               CcTest::global()->Get(v8_str("morphing_call"))));
348 
349   int expected_count = FLAG_vector_ics ? 2 : 1;
350   CHECK_EQ(expected_count, f->shared()->feedback_vector()->length());
351   // And yet it's not compiled.
352   CHECK(!f->shared()->is_compiled());
353 
354   CompileRun("morphing_call();");
355 
356   // The vector should have the same size despite the new scoping.
357   CHECK_EQ(expected_count, f->shared()->feedback_vector()->length());
358   CHECK(f->shared()->is_compiled());
359 }
360 
361 
362 // Test that optimized code for different closures is actually shared
363 // immediately by the FastNewClosureStub when run in the same context.
TEST(OptimizedCodeSharing)364 TEST(OptimizedCodeSharing) {
365   // Skip test if --cache-optimized-code is not activated by default because
366   // FastNewClosureStub that is baked into the snapshot is incorrect.
367   if (!FLAG_cache_optimized_code) return;
368   FLAG_stress_compaction = false;
369   FLAG_allow_natives_syntax = true;
370   CcTest::InitializeVM();
371   v8::HandleScope scope(CcTest::isolate());
372   for (int i = 0; i < 10; i++) {
373     LocalContext env;
374     env->Global()->Set(v8::String::NewFromUtf8(CcTest::isolate(), "x"),
375                        v8::Integer::New(CcTest::isolate(), i));
376     CompileRun("function MakeClosure() {"
377                "  return function() { return x; };"
378                "}"
379                "var closure0 = MakeClosure();"
380                "%DebugPrint(closure0());"
381                "%OptimizeFunctionOnNextCall(closure0);"
382                "%DebugPrint(closure0());"
383                "var closure1 = MakeClosure();"
384                "var closure2 = MakeClosure();");
385     Handle<JSFunction> fun1 = v8::Utils::OpenHandle(
386         *v8::Local<v8::Function>::Cast(env->Global()->Get(v8_str("closure1"))));
387     Handle<JSFunction> fun2 = v8::Utils::OpenHandle(
388         *v8::Local<v8::Function>::Cast(env->Global()->Get(v8_str("closure2"))));
389     CHECK(fun1->IsOptimized()
390           || !CcTest::i_isolate()->use_crankshaft() || !fun1->IsOptimizable());
391     CHECK(fun2->IsOptimized()
392           || !CcTest::i_isolate()->use_crankshaft() || !fun2->IsOptimizable());
393     CHECK_EQ(fun1->code(), fun2->code());
394   }
395 }
396 
397 
398 #ifdef ENABLE_DISASSEMBLER
GetJSFunction(v8::Handle<v8::Object> obj,const char * property_name)399 static Handle<JSFunction> GetJSFunction(v8::Handle<v8::Object> obj,
400                                  const char* property_name) {
401   v8::Local<v8::Function> fun =
402       v8::Local<v8::Function>::Cast(obj->Get(v8_str(property_name)));
403   return v8::Utils::OpenHandle(*fun);
404 }
405 
406 
CheckCodeForUnsafeLiteral(Handle<JSFunction> f)407 static void CheckCodeForUnsafeLiteral(Handle<JSFunction> f) {
408   // Create a disassembler with default name lookup.
409   disasm::NameConverter name_converter;
410   disasm::Disassembler d(name_converter);
411 
412   if (f->code()->kind() == Code::FUNCTION) {
413     Address pc = f->code()->instruction_start();
414     int decode_size =
415         Min(f->code()->instruction_size(),
416             static_cast<int>(f->code()->back_edge_table_offset()));
417     Address end = pc + decode_size;
418 
419     v8::internal::EmbeddedVector<char, 128> decode_buffer;
420     v8::internal::EmbeddedVector<char, 128> smi_hex_buffer;
421     Smi* smi = Smi::FromInt(12345678);
422     SNPrintF(smi_hex_buffer, "0x%" V8PRIxPTR, reinterpret_cast<intptr_t>(smi));
423     while (pc < end) {
424       int num_const = d.ConstantPoolSizeAt(pc);
425       if (num_const >= 0) {
426         pc += (num_const + 1) * kPointerSize;
427       } else {
428         pc += d.InstructionDecode(decode_buffer, pc);
429         CHECK(strstr(decode_buffer.start(), smi_hex_buffer.start()) == NULL);
430       }
431     }
432   }
433 }
434 
435 
TEST(SplitConstantsInFullCompiler)436 TEST(SplitConstantsInFullCompiler) {
437   LocalContext context;
438   v8::HandleScope scope(CcTest::isolate());
439 
440   CompileRun("function f() { a = 12345678 }; f();");
441   CheckCodeForUnsafeLiteral(GetJSFunction(context->Global(), "f"));
442   CompileRun("function f(x) { a = 12345678 + x}; f(1);");
443   CheckCodeForUnsafeLiteral(GetJSFunction(context->Global(), "f"));
444   CompileRun("function f(x) { var arguments = 1; x += 12345678}; f(1);");
445   CheckCodeForUnsafeLiteral(GetJSFunction(context->Global(), "f"));
446   CompileRun("function f(x) { var arguments = 1; x = 12345678}; f(1);");
447   CheckCodeForUnsafeLiteral(GetJSFunction(context->Global(), "f"));
448 }
449 #endif
450