Home | History | Annotate | Download | only in cctest
      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