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