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 41using namespace v8::internal; 42 43// --- P r i n t E x t e n s i o n --- 44 45class PrintExtension : public v8::Extension { 46 public: 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 57const char* PrintExtension::kSource = "native function print();"; 58 59 60v8::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 67void 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 79static PrintExtension kPrintExtension; 80v8::DeclareExtension kPrintExtensionDeclaration(&kPrintExtension); 81 82 83static 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 91static 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 102static 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 122static 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 138TEST(Inc) { 139 CcTest::InitializeVM(); 140 v8::HandleScope scope(CcTest::isolate()); 141 CHECK_EQ(4.0, Inc(CcTest::i_isolate(), 3)); 142} 143 144 145static 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 159TEST(Add) { 160 CcTest::InitializeVM(); 161 v8::HandleScope scope(CcTest::isolate()); 162 CHECK_EQ(5.0, Add(CcTest::i_isolate(), 2, 3)); 163} 164 165 166static 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 179TEST(Abs) { 180 CcTest::InitializeVM(); 181 v8::HandleScope scope(CcTest::isolate()); 182 CHECK_EQ(3.0, Abs(CcTest::i_isolate(), -3)); 183} 184 185 186static 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 200TEST(Sum) { 201 CcTest::InitializeVM(); 202 v8::HandleScope scope(CcTest::isolate()); 203 CHECK_EQ(5050.0, Sum(CcTest::i_isolate(), 100)); 204} 205 206 207TEST(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 224TEST(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 257TEST(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 | 279TEST(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. 319TEST(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 333TEST(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. 362TEST(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 397static 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 405static 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 434TEST(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