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