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 38using namespace v8::internal; 39 40static 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 47static 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 58static 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 71static 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 85TEST(Inc) { 86 CcTest::InitializeVM(); 87 v8::HandleScope scope(CcTest::isolate()); 88 CHECK_EQ(4.0, Inc(CcTest::i_isolate(), 3)); 89} 90 91 92static 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 104TEST(Add) { 105 CcTest::InitializeVM(); 106 v8::HandleScope scope(CcTest::isolate()); 107 CHECK_EQ(5.0, Add(CcTest::i_isolate(), 2, 3)); 108} 109 110 111static 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 122TEST(Abs) { 123 CcTest::InitializeVM(); 124 v8::HandleScope scope(CcTest::isolate()); 125 CHECK_EQ(3.0, Abs(CcTest::i_isolate(), -3)); 126} 127 128 129static 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 141TEST(Sum) { 142 CcTest::InitializeVM(); 143 v8::HandleScope scope(CcTest::isolate()); 144 CHECK_EQ(5050.0, Sum(CcTest::i_isolate(), 100)); 145} 146 147 148TEST(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 162TEST(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 193TEST(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 | 213TEST(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. 248TEST(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 262TEST(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 289TEST(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 327TEST(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. 364TEST(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 399static 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 407static 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 436TEST(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