Home | History | Annotate | Download | only in cctest
      1 // Copyright 2009 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 
     30 #include "v8.h"
     31 
     32 #include "api.h"
     33 #include "cctest.h"
     34 #include "frames-inl.h"
     35 #include "string-stream.h"
     36 
     37 using ::v8::ObjectTemplate;
     38 using ::v8::Value;
     39 using ::v8::Context;
     40 using ::v8::Local;
     41 using ::v8::String;
     42 using ::v8::Script;
     43 using ::v8::Function;
     44 using ::v8::AccessorInfo;
     45 using ::v8::Extension;
     46 
     47 static v8::Handle<Value> handle_property(Local<String> name,
     48                                          const AccessorInfo&) {
     49   ApiTestFuzzer::Fuzz();
     50   return v8_num(900);
     51 }
     52 
     53 
     54 THREADED_TEST(PropertyHandler) {
     55   v8::HandleScope scope;
     56   Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
     57   fun_templ->InstanceTemplate()->SetAccessor(v8_str("foo"), handle_property);
     58   LocalContext env;
     59   Local<Function> fun = fun_templ->GetFunction();
     60   env->Global()->Set(v8_str("Fun"), fun);
     61   Local<Script> getter = v8_compile("var obj = new Fun(); obj.foo;");
     62   CHECK_EQ(900, getter->Run()->Int32Value());
     63   Local<Script> setter = v8_compile("obj.foo = 901;");
     64   CHECK_EQ(901, setter->Run()->Int32Value());
     65 }
     66 
     67 
     68 static v8::Handle<Value> GetIntValue(Local<String> property,
     69                                      const AccessorInfo& info) {
     70   ApiTestFuzzer::Fuzz();
     71   int* value =
     72       static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
     73   return v8_num(*value);
     74 }
     75 
     76 
     77 static void SetIntValue(Local<String> property,
     78                         Local<Value> value,
     79                         const AccessorInfo& info) {
     80   int* field =
     81       static_cast<int*>(v8::Handle<v8::External>::Cast(info.Data())->Value());
     82   *field = value->Int32Value();
     83 }
     84 
     85 int foo, bar, baz;
     86 
     87 THREADED_TEST(GlobalVariableAccess) {
     88   foo = 0;
     89   bar = -4;
     90   baz = 10;
     91   v8::HandleScope scope;
     92   v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New();
     93   templ->InstanceTemplate()->SetAccessor(v8_str("foo"),
     94                                          GetIntValue,
     95                                          SetIntValue,
     96                                          v8::External::New(&foo));
     97   templ->InstanceTemplate()->SetAccessor(v8_str("bar"),
     98                                          GetIntValue,
     99                                          SetIntValue,
    100                                          v8::External::New(&bar));
    101   templ->InstanceTemplate()->SetAccessor(v8_str("baz"),
    102                                          GetIntValue,
    103                                          SetIntValue,
    104                                          v8::External::New(&baz));
    105   LocalContext env(0, templ->InstanceTemplate());
    106   v8_compile("foo = (++bar) + baz")->Run();
    107   CHECK_EQ(bar, -3);
    108   CHECK_EQ(foo, 7);
    109 }
    110 
    111 
    112 static int x_register = 0;
    113 static v8::Handle<v8::Object> x_receiver;
    114 static v8::Handle<v8::Object> x_holder;
    115 
    116 
    117 static v8::Handle<Value> XGetter(Local<String> name, const AccessorInfo& info) {
    118   ApiTestFuzzer::Fuzz();
    119   CHECK_EQ(x_receiver, info.This());
    120   CHECK_EQ(x_holder, info.Holder());
    121   return v8_num(x_register);
    122 }
    123 
    124 
    125 static void XSetter(Local<String> name,
    126                     Local<Value> value,
    127                     const AccessorInfo& info) {
    128   CHECK_EQ(x_holder, info.This());
    129   CHECK_EQ(x_holder, info.Holder());
    130   x_register = value->Int32Value();
    131 }
    132 
    133 
    134 THREADED_TEST(AccessorIC) {
    135   v8::HandleScope scope;
    136   v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
    137   obj->SetAccessor(v8_str("x"), XGetter, XSetter);
    138   LocalContext context;
    139   x_holder = obj->NewInstance();
    140   context->Global()->Set(v8_str("holder"), x_holder);
    141   x_receiver = v8::Object::New();
    142   context->Global()->Set(v8_str("obj"), x_receiver);
    143   v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(CompileRun(
    144     "obj.__proto__ = holder;"
    145     "var result = [];"
    146     "for (var i = 0; i < 10; i++) {"
    147     "  holder.x = i;"
    148     "  result.push(obj.x);"
    149     "}"
    150     "result"));
    151   CHECK_EQ(10, array->Length());
    152   for (int i = 0; i < 10; i++) {
    153     v8::Handle<Value> entry = array->Get(v8::Integer::New(i));
    154     CHECK_EQ(v8::Integer::New(i), entry);
    155   }
    156 }
    157 
    158 
    159 static v8::Handle<Value> AccessorProhibitsOverwritingGetter(
    160     Local<String> name,
    161     const AccessorInfo& info) {
    162   ApiTestFuzzer::Fuzz();
    163   return v8::True();
    164 }
    165 
    166 
    167 THREADED_TEST(AccessorProhibitsOverwriting) {
    168   v8::HandleScope scope;
    169   LocalContext context;
    170   Local<ObjectTemplate> templ = ObjectTemplate::New();
    171   templ->SetAccessor(v8_str("x"),
    172                      AccessorProhibitsOverwritingGetter,
    173                      0,
    174                      v8::Handle<Value>(),
    175                      v8::PROHIBITS_OVERWRITING,
    176                      v8::ReadOnly);
    177   Local<v8::Object> instance = templ->NewInstance();
    178   context->Global()->Set(v8_str("obj"), instance);
    179   Local<Value> value = CompileRun(
    180       "obj.__defineGetter__('x', function() { return false; });"
    181       "obj.x");
    182   CHECK(value->BooleanValue());
    183   value = CompileRun(
    184       "var setter_called = false;"
    185       "obj.__defineSetter__('x', function() { setter_called = true; });"
    186       "obj.x = 42;"
    187       "setter_called");
    188   CHECK(!value->BooleanValue());
    189   value = CompileRun(
    190       "obj2 = {};"
    191       "obj2.__proto__ = obj;"
    192       "obj2.__defineGetter__('x', function() { return false; });"
    193       "obj2.x");
    194   CHECK(value->BooleanValue());
    195   value = CompileRun(
    196       "var setter_called = false;"
    197       "obj2 = {};"
    198       "obj2.__proto__ = obj;"
    199       "obj2.__defineSetter__('x', function() { setter_called = true; });"
    200       "obj2.x = 42;"
    201       "setter_called");
    202   CHECK(!value->BooleanValue());
    203 }
    204 
    205 
    206 template <int C>
    207 static v8::Handle<Value> HandleAllocatingGetter(Local<String> name,
    208                                                 const AccessorInfo& info) {
    209   ApiTestFuzzer::Fuzz();
    210   for (int i = 0; i < C; i++)
    211     v8::String::New("foo");
    212   return v8::String::New("foo");
    213 }
    214 
    215 
    216 THREADED_TEST(HandleScopePop) {
    217   v8::HandleScope scope;
    218   v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
    219   obj->SetAccessor(v8_str("one"), HandleAllocatingGetter<1>);
    220   obj->SetAccessor(v8_str("many"), HandleAllocatingGetter<1024>);
    221   LocalContext context;
    222   v8::Handle<v8::Object> inst = obj->NewInstance();
    223   context->Global()->Set(v8::String::New("obj"), inst);
    224   int count_before = i::HandleScope::NumberOfHandles();
    225   {
    226     v8::HandleScope scope;
    227     CompileRun(
    228         "for (var i = 0; i < 1000; i++) {"
    229         "  obj.one;"
    230         "  obj.many;"
    231         "}");
    232   }
    233   int count_after = i::HandleScope::NumberOfHandles();
    234   CHECK_EQ(count_before, count_after);
    235 }
    236 
    237 static v8::Handle<Value> CheckAccessorArgsCorrect(Local<String> name,
    238                                                   const AccessorInfo& info) {
    239   CHECK(info.This() == info.Holder());
    240   CHECK(info.Data()->Equals(v8::String::New("data")));
    241   ApiTestFuzzer::Fuzz();
    242   CHECK(info.This() == info.Holder());
    243   CHECK(info.Data()->Equals(v8::String::New("data")));
    244   HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
    245   CHECK(info.This() == info.Holder());
    246   CHECK(info.Data()->Equals(v8::String::New("data")));
    247   return v8::Integer::New(17);
    248 }
    249 
    250 THREADED_TEST(DirectCall) {
    251   v8::HandleScope scope;
    252   v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
    253   obj->SetAccessor(v8_str("xxx"),
    254                    CheckAccessorArgsCorrect,
    255                    NULL,
    256                    v8::String::New("data"));
    257   LocalContext context;
    258   v8::Handle<v8::Object> inst = obj->NewInstance();
    259   context->Global()->Set(v8::String::New("obj"), inst);
    260   Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx"));
    261   for (int i = 0; i < 10; i++) {
    262     Local<Value> result = scr->Run();
    263     CHECK(!result.IsEmpty());
    264     CHECK_EQ(17, result->Int32Value());
    265   }
    266 }
    267 
    268 static v8::Handle<Value> EmptyGetter(Local<String> name,
    269                                      const AccessorInfo& info) {
    270   CheckAccessorArgsCorrect(name, info);
    271   ApiTestFuzzer::Fuzz();
    272   CheckAccessorArgsCorrect(name, info);
    273   return v8::Handle<v8::Value>();
    274 }
    275 
    276 THREADED_TEST(EmptyResult) {
    277   v8::HandleScope scope;
    278   v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
    279   obj->SetAccessor(v8_str("xxx"), EmptyGetter, NULL, v8::String::New("data"));
    280   LocalContext context;
    281   v8::Handle<v8::Object> inst = obj->NewInstance();
    282   context->Global()->Set(v8::String::New("obj"), inst);
    283   Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx"));
    284   for (int i = 0; i < 10; i++) {
    285     Local<Value> result = scr->Run();
    286     CHECK(result == v8::Undefined());
    287   }
    288 }
    289 
    290 
    291 THREADED_TEST(NoReuseRegress) {
    292   // Check that the IC generated for the one test doesn't get reused
    293   // for the other.
    294   v8::HandleScope scope;
    295   {
    296     v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
    297     obj->SetAccessor(v8_str("xxx"), EmptyGetter, NULL, v8::String::New("data"));
    298     LocalContext context;
    299     v8::Handle<v8::Object> inst = obj->NewInstance();
    300     context->Global()->Set(v8::String::New("obj"), inst);
    301     Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx"));
    302     for (int i = 0; i < 2; i++) {
    303       Local<Value> result = scr->Run();
    304       CHECK(result == v8::Undefined());
    305     }
    306   }
    307   {
    308     v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
    309     obj->SetAccessor(v8_str("xxx"),
    310                      CheckAccessorArgsCorrect,
    311                      NULL,
    312                      v8::String::New("data"));
    313     LocalContext context;
    314     v8::Handle<v8::Object> inst = obj->NewInstance();
    315     context->Global()->Set(v8::String::New("obj"), inst);
    316     Local<Script> scr = v8::Script::Compile(v8::String::New("obj.xxx"));
    317     for (int i = 0; i < 10; i++) {
    318       Local<Value> result = scr->Run();
    319       CHECK(!result.IsEmpty());
    320       CHECK_EQ(17, result->Int32Value());
    321     }
    322   }
    323 }
    324 
    325 static v8::Handle<Value> ThrowingGetAccessor(Local<String> name,
    326                                              const AccessorInfo& info) {
    327   ApiTestFuzzer::Fuzz();
    328   return v8::ThrowException(v8_str("g"));
    329 }
    330 
    331 
    332 static void ThrowingSetAccessor(Local<String> name,
    333                                 Local<Value> value,
    334                                 const AccessorInfo& info) {
    335   v8::ThrowException(value);
    336 }
    337 
    338 
    339 THREADED_TEST(Regress1054726) {
    340   v8::HandleScope scope;
    341   v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
    342   obj->SetAccessor(v8_str("x"),
    343                    ThrowingGetAccessor,
    344                    ThrowingSetAccessor,
    345                    Local<Value>());
    346 
    347   LocalContext env;
    348   env->Global()->Set(v8_str("obj"), obj->NewInstance());
    349 
    350   // Use the throwing property setter/getter in a loop to force
    351   // the accessor ICs to be initialized.
    352   v8::Handle<Value> result;
    353   result = Script::Compile(v8_str(
    354       "var result = '';"
    355       "for (var i = 0; i < 5; i++) {"
    356       "  try { obj.x; } catch (e) { result += e; }"
    357       "}; result"))->Run();
    358   CHECK_EQ(v8_str("ggggg"), result);
    359 
    360   result = Script::Compile(String::New(
    361       "var result = '';"
    362       "for (var i = 0; i < 5; i++) {"
    363       "  try { obj.x = i; } catch (e) { result += e; }"
    364       "}; result"))->Run();
    365   CHECK_EQ(v8_str("01234"), result);
    366 }
    367 
    368 
    369 static v8::Handle<Value> AllocGetter(Local<String> name,
    370                                      const AccessorInfo& info) {
    371   ApiTestFuzzer::Fuzz();
    372   return v8::Array::New(1000);
    373 }
    374 
    375 
    376 THREADED_TEST(Gc) {
    377   v8::HandleScope scope;
    378   v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
    379   obj->SetAccessor(v8_str("xxx"), AllocGetter);
    380   LocalContext env;
    381   env->Global()->Set(v8_str("obj"), obj->NewInstance());
    382   Script::Compile(String::New(
    383       "var last = [];"
    384       "for (var i = 0; i < 2048; i++) {"
    385       "  var result = obj.xxx;"
    386       "  result[0] = last;"
    387       "  last = result;"
    388       "}"))->Run();
    389 }
    390 
    391 
    392 static v8::Handle<Value> StackCheck(Local<String> name,
    393                                     const AccessorInfo& info) {
    394   i::StackFrameIterator iter;
    395   for (int i = 0; !iter.done(); i++) {
    396     i::StackFrame* frame = iter.frame();
    397     CHECK(i != 0 || (frame->type() == i::StackFrame::EXIT));
    398     i::Code* code = frame->LookupCode();
    399     CHECK(code->IsCode());
    400     i::Address pc = frame->pc();
    401     CHECK(code->contains(pc));
    402     iter.Advance();
    403   }
    404   return v8::Undefined();
    405 }
    406 
    407 
    408 THREADED_TEST(StackIteration) {
    409   v8::HandleScope scope;
    410   v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
    411   i::StringStream::ClearMentionedObjectCache();
    412   obj->SetAccessor(v8_str("xxx"), StackCheck);
    413   LocalContext env;
    414   env->Global()->Set(v8_str("obj"), obj->NewInstance());
    415   Script::Compile(String::New(
    416       "function foo() {"
    417       "  return obj.xxx;"
    418       "}"
    419       "for (var i = 0; i < 100; i++) {"
    420       "  foo();"
    421       "}"))->Run();
    422 }
    423 
    424 
    425 static v8::Handle<Value> AllocateHandles(Local<String> name,
    426                                          const AccessorInfo& info) {
    427   for (int i = 0; i < i::kHandleBlockSize + 1; i++) {
    428     v8::Local<v8::Value>::New(name);
    429   }
    430   return v8::Integer::New(100);
    431 }
    432 
    433 
    434 THREADED_TEST(HandleScopeSegment) {
    435   // Check that we can return values past popping of handle scope
    436   // segments.
    437   v8::HandleScope scope;
    438   v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
    439   obj->SetAccessor(v8_str("xxx"), AllocateHandles);
    440   LocalContext env;
    441   env->Global()->Set(v8_str("obj"), obj->NewInstance());
    442   v8::Handle<v8::Value> result = Script::Compile(String::New(
    443       "var result;"
    444       "for (var i = 0; i < 4; i++)"
    445       "  result = obj.xxx;"
    446       "result;"))->Run();
    447   CHECK_EQ(100, result->Int32Value());
    448 }
    449