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 "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 
     41 using namespace v8::internal;
     42 
     43 // --- P r i n t   E x t e n s i o n ---
     44 
     45 class 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 
     56 const char* PrintExtension::kSource = "native function print();";
     57 
     58 
     59 v8::Handle<v8::FunctionTemplate> PrintExtension::GetNativeFunction(
     60     v8::Handle<v8::String> str) {
     61   return v8::FunctionTemplate::New(PrintExtension::Print);
     62 }
     63 
     64 
     65 void 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 
     77 static PrintExtension kPrintExtension;
     78 v8::DeclareExtension kPrintExtensionDeclaration(&kPrintExtension);
     79 
     80 
     81 static 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 
     89 static 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 
     99 static 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 
    119 static 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 
    135 TEST(Inc) {
    136   CcTest::InitializeVM();
    137   v8::HandleScope scope(CcTest::isolate());
    138   CHECK_EQ(4.0, Inc(3));
    139 }
    140 
    141 
    142 static 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 
    156 TEST(Add) {
    157   CcTest::InitializeVM();
    158   v8::HandleScope scope(CcTest::isolate());
    159   CHECK_EQ(5.0, Add(2, 3));
    160 }
    161 
    162 
    163 static 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 
    176 TEST(Abs) {
    177   CcTest::InitializeVM();
    178   v8::HandleScope scope(CcTest::isolate());
    179   CHECK_EQ(3.0, Abs(-3));
    180 }
    181 
    182 
    183 static 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 
    197 TEST(Sum) {
    198   CcTest::InitializeVM();
    199   v8::HandleScope scope(CcTest::isolate());
    200   CHECK_EQ(5050.0, Sum(100));
    201 }
    202 
    203 
    204 TEST(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
    219 TEST(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 
    251 TEST(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     |
    273 TEST(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.
    309 TEST(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 
    323 TEST(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.
    349 TEST(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
    381 static 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 
    389 static 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 
    418 TEST(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