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