Home | History | Annotate | Download | only in cctest
      1 // Copyright 2006-2008 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>  // wint_t
     30 
     31 #include "v8.h"
     32 
     33 #include "compiler.h"
     34 #include "execution.h"
     35 #include "factory.h"
     36 #include "platform.h"
     37 #include "top.h"
     38 #include "cctest.h"
     39 
     40 using namespace v8::internal;
     41 
     42 static v8::Persistent<v8::Context> env;
     43 
     44 // --- P r i n t   E x t e n s i o n ---
     45 
     46 class PrintExtension : public v8::Extension {
     47  public:
     48   PrintExtension() : v8::Extension("v8/print", kSource) { }
     49   virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction(
     50       v8::Handle<v8::String> name);
     51   static v8::Handle<v8::Value> Print(const v8::Arguments& args);
     52  private:
     53   static const char* kSource;
     54 };
     55 
     56 
     57 const char* PrintExtension::kSource = "native function print();";
     58 
     59 
     60 v8::Handle<v8::FunctionTemplate> PrintExtension::GetNativeFunction(
     61     v8::Handle<v8::String> str) {
     62   return v8::FunctionTemplate::New(PrintExtension::Print);
     63 }
     64 
     65 
     66 v8::Handle<v8::Value> PrintExtension::Print(const v8::Arguments& args) {
     67   for (int i = 0; i < args.Length(); i++) {
     68     if (i != 0) printf(" ");
     69     v8::HandleScope scope;
     70     v8::Handle<v8::Value> arg = args[i];
     71     v8::Handle<v8::String> string_obj = arg->ToString();
     72     if (string_obj.IsEmpty()) return string_obj;
     73     int length = string_obj->Length();
     74     uint16_t* string = NewArray<uint16_t>(length + 1);
     75     string_obj->Write(string);
     76     for (int j = 0; j < length; j++)
     77       printf("%lc", static_cast<wint_t>(string[j]));
     78     DeleteArray(string);
     79   }
     80   printf("\n");
     81   return v8::Undefined();
     82 }
     83 
     84 
     85 static PrintExtension kPrintExtension;
     86 v8::DeclareExtension kPrintExtensionDeclaration(&kPrintExtension);
     87 
     88 
     89 static void InitializeVM() {
     90   if (env.IsEmpty()) {
     91     v8::HandleScope scope;
     92     const char* extensions[] = { "v8/print", "v8/gc" };
     93     v8::ExtensionConfiguration config(2, extensions);
     94     env = v8::Context::New(&config);
     95   }
     96   v8::HandleScope scope;
     97   env->Enter();
     98 }
     99 
    100 
    101 static Object* GetGlobalProperty(const char* name) {
    102   Handle<String> symbol = Factory::LookupAsciiSymbol(name);
    103   return Top::context()->global()->GetProperty(*symbol);
    104 }
    105 
    106 
    107 static void SetGlobalProperty(const char* name, Object* value) {
    108   Handle<Object> object(value);
    109   Handle<String> symbol = Factory::LookupAsciiSymbol(name);
    110   Handle<JSObject> global(Top::context()->global());
    111   SetProperty(global, symbol, object, NONE);
    112 }
    113 
    114 
    115 static Handle<JSFunction> Compile(const char* source) {
    116   Handle<String> source_code(Factory::NewStringFromUtf8(CStrVector(source)));
    117   Handle<JSFunction> boilerplate = Compiler::Compile(source_code,
    118                                                      Handle<String>(),
    119                                                      0,
    120                                                      0,
    121                                                      NULL,
    122                                                      NULL,
    123                                                      Handle<String>::null(),
    124                                                      NOT_NATIVES_CODE);
    125   return Factory::NewFunctionFromBoilerplate(boilerplate,
    126                                              Top::global_context());
    127 }
    128 
    129 
    130 static double Inc(int x) {
    131   const char* source = "result = %d + 1;";
    132   EmbeddedVector<char, 512> buffer;
    133   OS::SNPrintF(buffer, source, x);
    134 
    135   Handle<JSFunction> fun = Compile(buffer.start());
    136   if (fun.is_null()) return -1;
    137 
    138   bool has_pending_exception;
    139   Handle<JSObject> global(Top::context()->global());
    140   Execution::Call(fun, global, 0, NULL, &has_pending_exception);
    141   CHECK(!has_pending_exception);
    142   return GetGlobalProperty("result")->Number();
    143 }
    144 
    145 
    146 TEST(Inc) {
    147   InitializeVM();
    148   v8::HandleScope scope;
    149   CHECK_EQ(4.0, Inc(3));
    150 }
    151 
    152 
    153 static double Add(int x, int y) {
    154   Handle<JSFunction> fun = Compile("result = x + y;");
    155   if (fun.is_null()) return -1;
    156 
    157   SetGlobalProperty("x", Smi::FromInt(x));
    158   SetGlobalProperty("y", Smi::FromInt(y));
    159   bool has_pending_exception;
    160   Handle<JSObject> global(Top::context()->global());
    161   Execution::Call(fun, global, 0, NULL, &has_pending_exception);
    162   CHECK(!has_pending_exception);
    163   return GetGlobalProperty("result")->Number();
    164 }
    165 
    166 
    167 TEST(Add) {
    168   InitializeVM();
    169   v8::HandleScope scope;
    170   CHECK_EQ(5.0, Add(2, 3));
    171 }
    172 
    173 
    174 static double Abs(int x) {
    175   Handle<JSFunction> fun = Compile("if (x < 0) result = -x; else result = x;");
    176   if (fun.is_null()) return -1;
    177 
    178   SetGlobalProperty("x", Smi::FromInt(x));
    179   bool has_pending_exception;
    180   Handle<JSObject> global(Top::context()->global());
    181   Execution::Call(fun, global, 0, NULL, &has_pending_exception);
    182   CHECK(!has_pending_exception);
    183   return GetGlobalProperty("result")->Number();
    184 }
    185 
    186 
    187 TEST(Abs) {
    188   InitializeVM();
    189   v8::HandleScope scope;
    190   CHECK_EQ(3.0, Abs(-3));
    191 }
    192 
    193 
    194 static double Sum(int n) {
    195   Handle<JSFunction> fun =
    196       Compile("s = 0; while (n > 0) { s += n; n -= 1; }; result = s;");
    197   if (fun.is_null()) return -1;
    198 
    199   SetGlobalProperty("n", Smi::FromInt(n));
    200   bool has_pending_exception;
    201   Handle<JSObject> global(Top::context()->global());
    202   Execution::Call(fun, global, 0, NULL, &has_pending_exception);
    203   CHECK(!has_pending_exception);
    204   return GetGlobalProperty("result")->Number();
    205 }
    206 
    207 
    208 TEST(Sum) {
    209   InitializeVM();
    210   v8::HandleScope scope;
    211   CHECK_EQ(5050.0, Sum(100));
    212 }
    213 
    214 
    215 TEST(Print) {
    216   InitializeVM();
    217   v8::HandleScope scope;
    218   const char* source = "for (n = 0; n < 100; ++n) print(n, 1, 2);";
    219   Handle<JSFunction> fun = Compile(source);
    220   if (fun.is_null()) return;
    221   bool has_pending_exception;
    222   Handle<JSObject> global(Top::context()->global());
    223   Execution::Call(fun, global, 0, NULL, &has_pending_exception);
    224   CHECK(!has_pending_exception);
    225 }
    226 
    227 
    228 // The following test method stems from my coding efforts today. It
    229 // tests all the functionality I have added to the compiler today
    230 TEST(Stuff) {
    231   InitializeVM();
    232   v8::HandleScope scope;
    233   const char* source =
    234     "r = 0;\n"
    235     "a = new Object;\n"
    236     "if (a == a) r+=1;\n"  // 1
    237     "if (a != new Object()) r+=2;\n"  // 2
    238     "a.x = 42;\n"
    239     "if (a.x == 42) r+=4;\n"  // 4
    240     "function foo() { var x = 87; return x; }\n"
    241     "if (foo() == 87) r+=8;\n"  // 8
    242     "function bar() { var x; x = 99; return x; }\n"
    243     "if (bar() == 99) r+=16;\n"  // 16
    244     "function baz() { var x = 1, y, z = 2; y = 3; return x + y + z; }\n"
    245     "if (baz() == 6) r+=32;\n"  // 32
    246     "function Cons0() { this.x = 42; this.y = 87; }\n"
    247     "if (new Cons0().x == 42) r+=64;\n"  // 64
    248     "if (new Cons0().y == 87) r+=128;\n"  // 128
    249     "function Cons2(x, y) { this.sum = x + y; }\n"
    250     "if (new Cons2(3,4).sum == 7) r+=256;";  // 256
    251 
    252   Handle<JSFunction> fun = Compile(source);
    253   CHECK(!fun.is_null());
    254   bool has_pending_exception;
    255   Handle<JSObject> global(Top::context()->global());
    256   Execution::Call(fun, global, 0, NULL, &has_pending_exception);
    257   CHECK(!has_pending_exception);
    258   CHECK_EQ(511.0, GetGlobalProperty("r")->Number());
    259 }
    260 
    261 
    262 TEST(UncaughtThrow) {
    263   InitializeVM();
    264   v8::HandleScope scope;
    265 
    266   const char* source = "throw 42;";
    267   Handle<JSFunction> fun = Compile(source);
    268   CHECK(!fun.is_null());
    269   bool has_pending_exception;
    270   Handle<JSObject> global(Top::context()->global());
    271   Handle<Object> result =
    272       Execution::Call(fun, global, 0, NULL, &has_pending_exception);
    273   CHECK(has_pending_exception);
    274   CHECK_EQ(42.0, Top::pending_exception()->Number());
    275 }
    276 
    277 
    278 // Tests calling a builtin function from C/C++ code, and the builtin function
    279 // performs GC. It creates a stack frame looks like following:
    280 //   | C (PerformGC) |
    281 //   |   JS-to-C     |
    282 //   |      JS       |
    283 //   |   C-to-JS     |
    284 TEST(C2JSFrames) {
    285   InitializeVM();
    286   v8::HandleScope scope;
    287 
    288   const char* source = "function foo(a) { gc(), print(a); }";
    289 
    290   Handle<JSFunction> fun0 = Compile(source);
    291   CHECK(!fun0.is_null());
    292 
    293   // Run the generated code to populate the global object with 'foo'.
    294   bool has_pending_exception;
    295   Handle<JSObject> global(Top::context()->global());
    296   Execution::Call(fun0, global, 0, NULL, &has_pending_exception);
    297   CHECK(!has_pending_exception);
    298 
    299   Handle<Object> fun1 =
    300       Handle<Object>(
    301           Top::context()->global()->GetProperty(
    302               *Factory::LookupAsciiSymbol("foo")));
    303   CHECK(fun1->IsJSFunction());
    304 
    305   Object** argv[1] = {
    306     Handle<Object>::cast(Factory::LookupAsciiSymbol("hello")).location()
    307   };
    308   Execution::Call(Handle<JSFunction>::cast(fun1), global, 1, argv,
    309                   &has_pending_exception);
    310   CHECK(!has_pending_exception);
    311 }
    312 
    313 
    314 // Regression 236. Calling InitLineEnds on a Script with undefined
    315 // source resulted in crash.
    316 TEST(Regression236) {
    317   InitializeVM();
    318   v8::HandleScope scope;
    319 
    320   Handle<Script> script = Factory::NewScript(Factory::empty_string());
    321   script->set_source(Heap::undefined_value());
    322   CHECK_EQ(-1, GetScriptLineNumber(script, 0));
    323   CHECK_EQ(-1, GetScriptLineNumber(script, 100));
    324   CHECK_EQ(-1, GetScriptLineNumber(script, -1));
    325 }
    326 
    327 
    328 TEST(GetScriptLineNumber) {
    329   LocalContext env;
    330   v8::HandleScope scope;
    331   v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
    332   const char function_f[] = "function f() {}";
    333   const int max_rows = 1000;
    334   const int buffer_size = max_rows + sizeof(function_f);
    335   ScopedVector<char> buffer(buffer_size);
    336   memset(buffer.start(), '\n', buffer_size - 1);
    337   buffer[buffer_size - 1] = '\0';
    338 
    339   for (int i = 0; i < max_rows; ++i) {
    340     if (i > 0)
    341       buffer[i - 1] = '\n';
    342     memcpy(&buffer[i], function_f, sizeof(function_f) - 1);
    343     v8::Handle<v8::String> script_body = v8::String::New(buffer.start());
    344     v8::Script::Compile(script_body, &origin)->Run();
    345     v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
    346         env->Global()->Get(v8::String::New("f")));
    347     CHECK_EQ(i, f->GetScriptLineNumber());
    348   }
    349 }
    350