• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "v8.h"
32 
33 #include "compiler.h"
34 #include "disasm.h"
35 #include "disassembler.h"
36 #include "execution.h"
37 #include "factory.h"
38 #include "platform.h"
39 #include "cctest.h"
40 
41 using namespace v8::internal;
42 
43 // --- P r i n t   E x t e n s i o n ---
44 
45 class PrintExtension : public v8::Extension {
46  public:
PrintExtension()47   PrintExtension() : v8::Extension("v8/print", kSource) { }
48   virtual v8::Handle<v8::FunctionTemplate> GetNativeFunctionTemplate(
49       v8::Isolate* isolate,
50       v8::Handle<v8::String> name);
51   static void Print(const v8::FunctionCallbackInfo<v8::Value>& args);
52  private:
53   static const char* kSource;
54 };
55 
56 
57 const char* PrintExtension::kSource = "native function print();";
58 
59 
GetNativeFunctionTemplate(v8::Isolate * isolate,v8::Handle<v8::String> str)60 v8::Handle<v8::FunctionTemplate> PrintExtension::GetNativeFunctionTemplate(
61     v8::Isolate* isolate,
62     v8::Handle<v8::String> str) {
63   return v8::FunctionTemplate::New(PrintExtension::Print);
64 }
65 
66 
Print(const v8::FunctionCallbackInfo<v8::Value> & args)67 void PrintExtension::Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
68   for (int i = 0; i < args.Length(); i++) {
69     if (i != 0) printf(" ");
70     v8::HandleScope scope(args.GetIsolate());
71     v8::String::Utf8Value str(args[i]);
72     if (*str == NULL) return;
73     printf("%s", *str);
74   }
75   printf("\n");
76 }
77 
78 
79 static PrintExtension kPrintExtension;
80 v8::DeclareExtension kPrintExtensionDeclaration(&kPrintExtension);
81 
82 
GetGlobalProperty(const char * name)83 static MaybeObject* GetGlobalProperty(const char* name) {
84   Isolate* isolate = CcTest::i_isolate();
85   Handle<String> internalized_name =
86       isolate->factory()->InternalizeUtf8String(name);
87   return isolate->context()->global_object()->GetProperty(*internalized_name);
88 }
89 
90 
SetGlobalProperty(const char * name,Object * value)91 static void SetGlobalProperty(const char* name, Object* value) {
92   Isolate* isolate = CcTest::i_isolate();
93   Handle<Object> object(value, isolate);
94   Handle<String> internalized_name =
95       isolate->factory()->InternalizeUtf8String(name);
96   Handle<JSObject> global(isolate->context()->global_object());
97   Runtime::SetObjectProperty(isolate, global, internalized_name, object, NONE,
98                              kNonStrictMode);
99 }
100 
101 
Compile(const char * source)102 static Handle<JSFunction> Compile(const char* source) {
103   Isolate* isolate = CcTest::i_isolate();
104   Handle<String> source_code(
105       isolate->factory()->NewStringFromUtf8(CStrVector(source)));
106   Handle<SharedFunctionInfo> shared_function =
107       Compiler::Compile(source_code,
108                         Handle<String>(),
109                         0,
110                         0,
111                         false,
112                         Handle<Context>(isolate->native_context()),
113                         NULL,
114                         NULL,
115                         Handle<String>::null(),
116                         NOT_NATIVES_CODE);
117   return isolate->factory()->NewFunctionFromSharedFunctionInfo(
118       shared_function, isolate->native_context());
119 }
120 
121 
Inc(Isolate * isolate,int x)122 static double Inc(Isolate* isolate, int x) {
123   const char* source = "result = %d + 1;";
124   EmbeddedVector<char, 512> buffer;
125   OS::SNPrintF(buffer, source, x);
126 
127   Handle<JSFunction> fun = Compile(buffer.start());
128   if (fun.is_null()) return -1;
129 
130   bool has_pending_exception;
131   Handle<JSObject> global(isolate->context()->global_object());
132   Execution::Call(isolate, fun, global, 0, NULL, &has_pending_exception);
133   CHECK(!has_pending_exception);
134   return GetGlobalProperty("result")->ToObjectChecked()->Number();
135 }
136 
137 
TEST(Inc)138 TEST(Inc) {
139   CcTest::InitializeVM();
140   v8::HandleScope scope(CcTest::isolate());
141   CHECK_EQ(4.0, Inc(CcTest::i_isolate(), 3));
142 }
143 
144 
Add(Isolate * isolate,int x,int y)145 static double Add(Isolate* isolate, int x, int y) {
146   Handle<JSFunction> fun = Compile("result = x + y;");
147   if (fun.is_null()) return -1;
148 
149   SetGlobalProperty("x", Smi::FromInt(x));
150   SetGlobalProperty("y", Smi::FromInt(y));
151   bool has_pending_exception;
152   Handle<JSObject> global(isolate->context()->global_object());
153   Execution::Call(isolate, fun, global, 0, NULL, &has_pending_exception);
154   CHECK(!has_pending_exception);
155   return GetGlobalProperty("result")->ToObjectChecked()->Number();
156 }
157 
158 
TEST(Add)159 TEST(Add) {
160   CcTest::InitializeVM();
161   v8::HandleScope scope(CcTest::isolate());
162   CHECK_EQ(5.0, Add(CcTest::i_isolate(), 2, 3));
163 }
164 
165 
Abs(Isolate * isolate,int x)166 static double Abs(Isolate* isolate, int x) {
167   Handle<JSFunction> fun = Compile("if (x < 0) result = -x; else result = x;");
168   if (fun.is_null()) return -1;
169 
170   SetGlobalProperty("x", Smi::FromInt(x));
171   bool has_pending_exception;
172   Handle<JSObject> global(isolate->context()->global_object());
173   Execution::Call(isolate, fun, global, 0, NULL, &has_pending_exception);
174   CHECK(!has_pending_exception);
175   return GetGlobalProperty("result")->ToObjectChecked()->Number();
176 }
177 
178 
TEST(Abs)179 TEST(Abs) {
180   CcTest::InitializeVM();
181   v8::HandleScope scope(CcTest::isolate());
182   CHECK_EQ(3.0, Abs(CcTest::i_isolate(), -3));
183 }
184 
185 
Sum(Isolate * isolate,int n)186 static double Sum(Isolate* isolate, int n) {
187   Handle<JSFunction> fun =
188       Compile("s = 0; while (n > 0) { s += n; n -= 1; }; result = s;");
189   if (fun.is_null()) return -1;
190 
191   SetGlobalProperty("n", Smi::FromInt(n));
192   bool has_pending_exception;
193   Handle<JSObject> global(isolate->context()->global_object());
194   Execution::Call(isolate, fun, global, 0, NULL, &has_pending_exception);
195   CHECK(!has_pending_exception);
196   return GetGlobalProperty("result")->ToObjectChecked()->Number();
197 }
198 
199 
TEST(Sum)200 TEST(Sum) {
201   CcTest::InitializeVM();
202   v8::HandleScope scope(CcTest::isolate());
203   CHECK_EQ(5050.0, Sum(CcTest::i_isolate(), 100));
204 }
205 
206 
TEST(Print)207 TEST(Print) {
208   v8::HandleScope scope(CcTest::isolate());
209   v8::Local<v8::Context> context = CcTest::NewContext(PRINT_EXTENSION);
210   v8::Context::Scope context_scope(context);
211   const char* source = "for (n = 0; n < 100; ++n) print(n, 1, 2);";
212   Handle<JSFunction> fun = Compile(source);
213   if (fun.is_null()) return;
214   bool has_pending_exception;
215   Handle<JSObject> global(CcTest::i_isolate()->context()->global_object());
216   Execution::Call(
217       CcTest::i_isolate(), fun, global, 0, NULL, &has_pending_exception);
218   CHECK(!has_pending_exception);
219 }
220 
221 
222 // The following test method stems from my coding efforts today. It
223 // tests all the functionality I have added to the compiler today
TEST(Stuff)224 TEST(Stuff) {
225   CcTest::InitializeVM();
226   v8::HandleScope scope(CcTest::isolate());
227   const char* source =
228     "r = 0;\n"
229     "a = new Object;\n"
230     "if (a == a) r+=1;\n"  // 1
231     "if (a != new Object()) r+=2;\n"  // 2
232     "a.x = 42;\n"
233     "if (a.x == 42) r+=4;\n"  // 4
234     "function foo() { var x = 87; return x; }\n"
235     "if (foo() == 87) r+=8;\n"  // 8
236     "function bar() { var x; x = 99; return x; }\n"
237     "if (bar() == 99) r+=16;\n"  // 16
238     "function baz() { var x = 1, y, z = 2; y = 3; return x + y + z; }\n"
239     "if (baz() == 6) r+=32;\n"  // 32
240     "function Cons0() { this.x = 42; this.y = 87; }\n"
241     "if (new Cons0().x == 42) r+=64;\n"  // 64
242     "if (new Cons0().y == 87) r+=128;\n"  // 128
243     "function Cons2(x, y) { this.sum = x + y; }\n"
244     "if (new Cons2(3,4).sum == 7) r+=256;";  // 256
245 
246   Handle<JSFunction> fun = Compile(source);
247   CHECK(!fun.is_null());
248   bool has_pending_exception;
249   Handle<JSObject> global(CcTest::i_isolate()->context()->global_object());
250   Execution::Call(
251       CcTest::i_isolate(), fun, global, 0, NULL, &has_pending_exception);
252   CHECK(!has_pending_exception);
253   CHECK_EQ(511.0, GetGlobalProperty("r")->ToObjectChecked()->Number());
254 }
255 
256 
TEST(UncaughtThrow)257 TEST(UncaughtThrow) {
258   CcTest::InitializeVM();
259   v8::HandleScope scope(CcTest::isolate());
260 
261   const char* source = "throw 42;";
262   Handle<JSFunction> fun = Compile(source);
263   CHECK(!fun.is_null());
264   bool has_pending_exception;
265   Isolate* isolate = fun->GetIsolate();
266   Handle<JSObject> global(isolate->context()->global_object());
267   Execution::Call(isolate, fun, global, 0, NULL, &has_pending_exception);
268   CHECK(has_pending_exception);
269   CHECK_EQ(42.0, isolate->pending_exception()->ToObjectChecked()->Number());
270 }
271 
272 
273 // Tests calling a builtin function from C/C++ code, and the builtin function
274 // performs GC. It creates a stack frame looks like following:
275 //   | C (PerformGC) |
276 //   |   JS-to-C     |
277 //   |      JS       |
278 //   |   C-to-JS     |
TEST(C2JSFrames)279 TEST(C2JSFrames) {
280   v8::HandleScope scope(CcTest::isolate());
281   v8::Local<v8::Context> context =
282     CcTest::NewContext(PRINT_EXTENSION | GC_EXTENSION);
283   v8::Context::Scope context_scope(context);
284 
285   const char* source = "function foo(a) { gc(), print(a); }";
286 
287   Handle<JSFunction> fun0 = Compile(source);
288   CHECK(!fun0.is_null());
289   Isolate* isolate = fun0->GetIsolate();
290 
291   // Run the generated code to populate the global object with 'foo'.
292   bool has_pending_exception;
293   Handle<JSObject> global(isolate->context()->global_object());
294   Execution::Call(
295       isolate, fun0, global, 0, NULL, &has_pending_exception);
296   CHECK(!has_pending_exception);
297 
298   Object* foo_string = isolate->factory()->InternalizeOneByteString(
299       STATIC_ASCII_VECTOR("foo"))->ToObjectChecked();
300   MaybeObject* fun1_object = isolate->context()->global_object()->
301       GetProperty(String::cast(foo_string));
302   Handle<Object> fun1(fun1_object->ToObjectChecked(), isolate);
303   CHECK(fun1->IsJSFunction());
304 
305   Handle<Object> argv[] = { isolate->factory()->InternalizeOneByteString(
306       STATIC_ASCII_VECTOR("hello")) };
307   Execution::Call(isolate,
308                   Handle<JSFunction>::cast(fun1),
309                   global,
310                   ARRAY_SIZE(argv),
311                   argv,
312                   &has_pending_exception);
313   CHECK(!has_pending_exception);
314 }
315 
316 
317 // Regression 236. Calling InitLineEnds on a Script with undefined
318 // source resulted in crash.
TEST(Regression236)319 TEST(Regression236) {
320   CcTest::InitializeVM();
321   Isolate* isolate = CcTest::i_isolate();
322   Factory* factory = isolate->factory();
323   v8::HandleScope scope(CcTest::isolate());
324 
325   Handle<Script> script = factory->NewScript(factory->empty_string());
326   script->set_source(CcTest::heap()->undefined_value());
327   CHECK_EQ(-1, GetScriptLineNumber(script, 0));
328   CHECK_EQ(-1, GetScriptLineNumber(script, 100));
329   CHECK_EQ(-1, GetScriptLineNumber(script, -1));
330 }
331 
332 
TEST(GetScriptLineNumber)333 TEST(GetScriptLineNumber) {
334   LocalContext context;
335   v8::HandleScope scope(CcTest::isolate());
336   v8::ScriptOrigin origin =
337       v8::ScriptOrigin(v8::String::NewFromUtf8(CcTest::isolate(), "test"));
338   const char function_f[] = "function f() {}";
339   const int max_rows = 1000;
340   const int buffer_size = max_rows + sizeof(function_f);
341   ScopedVector<char> buffer(buffer_size);
342   memset(buffer.start(), '\n', buffer_size - 1);
343   buffer[buffer_size - 1] = '\0';
344 
345   for (int i = 0; i < max_rows; ++i) {
346     if (i > 0)
347       buffer[i - 1] = '\n';
348     OS::MemCopy(&buffer[i], function_f, sizeof(function_f) - 1);
349     v8::Handle<v8::String> script_body =
350         v8::String::NewFromUtf8(CcTest::isolate(), buffer.start());
351     v8::Script::Compile(script_body, &origin)->Run();
352     v8::Local<v8::Function> f =
353         v8::Local<v8::Function>::Cast(context->Global()->Get(
354             v8::String::NewFromUtf8(CcTest::isolate(), "f")));
355     CHECK_EQ(i, f->GetScriptLineNumber());
356   }
357 }
358 
359 
360 // Test that optimized code for different closures is actually shared
361 // immediately by the FastNewClosureStub when run in the same context.
TEST(OptimizedCodeSharing)362 TEST(OptimizedCodeSharing) {
363   // Skip test if --cache-optimized-code is not activated by default because
364   // FastNewClosureStub that is baked into the snapshot is incorrect.
365   if (!FLAG_cache_optimized_code) return;
366   FLAG_stress_compaction = false;
367   FLAG_allow_natives_syntax = true;
368   CcTest::InitializeVM();
369   v8::HandleScope scope(CcTest::isolate());
370   for (int i = 0; i < 10; i++) {
371     LocalContext env;
372     env->Global()->Set(v8::String::NewFromUtf8(CcTest::isolate(), "x"),
373                        v8::Integer::New(i));
374     CompileRun("function MakeClosure() {"
375                "  return function() { return x; };"
376                "}"
377                "var closure0 = MakeClosure();"
378                "%DebugPrint(closure0());"
379                "%OptimizeFunctionOnNextCall(closure0);"
380                "%DebugPrint(closure0());"
381                "var closure1 = MakeClosure();"
382                "var closure2 = MakeClosure();");
383     Handle<JSFunction> fun1 = v8::Utils::OpenHandle(
384         *v8::Local<v8::Function>::Cast(env->Global()->Get(v8_str("closure1"))));
385     Handle<JSFunction> fun2 = v8::Utils::OpenHandle(
386         *v8::Local<v8::Function>::Cast(env->Global()->Get(v8_str("closure2"))));
387     CHECK(fun1->IsOptimized()
388           || !CcTest::i_isolate()->use_crankshaft() || !fun1->IsOptimizable());
389     CHECK(fun2->IsOptimized()
390           || !CcTest::i_isolate()->use_crankshaft() || !fun2->IsOptimizable());
391     CHECK_EQ(fun1->code(), fun2->code());
392   }
393 }
394 
395 
396 #ifdef ENABLE_DISASSEMBLER
GetJSFunction(v8::Handle<v8::Object> obj,const char * property_name)397 static Handle<JSFunction> GetJSFunction(v8::Handle<v8::Object> obj,
398                                  const char* property_name) {
399   v8::Local<v8::Function> fun =
400       v8::Local<v8::Function>::Cast(obj->Get(v8_str(property_name)));
401   return v8::Utils::OpenHandle(*fun);
402 }
403 
404 
CheckCodeForUnsafeLiteral(Handle<JSFunction> f)405 static void CheckCodeForUnsafeLiteral(Handle<JSFunction> f) {
406   // Create a disassembler with default name lookup.
407   disasm::NameConverter name_converter;
408   disasm::Disassembler d(name_converter);
409 
410   if (f->code()->kind() == Code::FUNCTION) {
411     Address pc = f->code()->instruction_start();
412     int decode_size =
413         Min(f->code()->instruction_size(),
414             static_cast<int>(f->code()->back_edge_table_offset()));
415     Address end = pc + decode_size;
416 
417     v8::internal::EmbeddedVector<char, 128> decode_buffer;
418     v8::internal::EmbeddedVector<char, 128> smi_hex_buffer;
419     Smi* smi = Smi::FromInt(12345678);
420     OS::SNPrintF(smi_hex_buffer, "0x%lx", reinterpret_cast<intptr_t>(smi));
421     while (pc < end) {
422       int num_const = d.ConstantPoolSizeAt(pc);
423       if (num_const >= 0) {
424         pc += (num_const + 1) * kPointerSize;
425       } else {
426         pc += d.InstructionDecode(decode_buffer, pc);
427         CHECK(strstr(decode_buffer.start(), smi_hex_buffer.start()) == NULL);
428       }
429     }
430   }
431 }
432 
433 
TEST(SplitConstantsInFullCompiler)434 TEST(SplitConstantsInFullCompiler) {
435   LocalContext context;
436   v8::HandleScope scope(CcTest::isolate());
437 
438   CompileRun("function f() { a = 12345678 }; f();");
439   CheckCodeForUnsafeLiteral(GetJSFunction(context->Global(), "f"));
440   CompileRun("function f(x) { a = 12345678 + x}; f(1);");
441   CheckCodeForUnsafeLiteral(GetJSFunction(context->Global(), "f"));
442   CompileRun("function f(x) { var arguments = 1; x += 12345678}; 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 }
447 #endif
448