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 "v8.h"
     29 
     30 #include "cctest.h"
     31 
     32 using namespace v8;
     33 namespace i = v8::internal;
     34 
     35 namespace {
     36 // Need to create a new isolate when FLAG_harmony_observation is on.
     37 class HarmonyIsolate {
     38  public:
     39   HarmonyIsolate() {
     40     i::FLAG_harmony_observation = true;
     41     isolate_ = Isolate::New();
     42     isolate_->Enter();
     43   }
     44 
     45   ~HarmonyIsolate() {
     46     isolate_->Exit();
     47     isolate_->Dispose();
     48   }
     49 
     50   Isolate* GetIsolate() const { return isolate_; }
     51 
     52  private:
     53   Isolate* isolate_;
     54 };
     55 }
     56 
     57 
     58 TEST(PerIsolateState) {
     59   HarmonyIsolate isolate;
     60   HandleScope scope(isolate.GetIsolate());
     61   LocalContext context1(isolate.GetIsolate());
     62   CompileRun(
     63       "var count = 0;"
     64       "var calls = 0;"
     65       "var observer = function(records) { count = records.length; calls++ };"
     66       "var obj = {};"
     67       "Object.observe(obj, observer);");
     68   Handle<Value> observer = CompileRun("observer");
     69   Handle<Value> obj = CompileRun("obj");
     70   Handle<Value> notify_fun1 = CompileRun(
     71       "(function() { obj.foo = 'bar'; })");
     72   Handle<Value> notify_fun2;
     73   {
     74     LocalContext context2(isolate.GetIsolate());
     75     context2->Global()->Set(String::NewFromUtf8(isolate.GetIsolate(), "obj"),
     76                             obj);
     77     notify_fun2 = CompileRun(
     78         "(function() { obj.foo = 'baz'; })");
     79   }
     80   Handle<Value> notify_fun3;
     81   {
     82     LocalContext context3(isolate.GetIsolate());
     83     context3->Global()->Set(String::NewFromUtf8(isolate.GetIsolate(), "obj"),
     84                             obj);
     85     notify_fun3 = CompileRun(
     86         "(function() { obj.foo = 'bat'; })");
     87   }
     88   {
     89     LocalContext context4(isolate.GetIsolate());
     90     context4->Global()->Set(
     91         String::NewFromUtf8(isolate.GetIsolate(), "observer"), observer);
     92     context4->Global()->Set(String::NewFromUtf8(isolate.GetIsolate(), "fun1"),
     93                             notify_fun1);
     94     context4->Global()->Set(String::NewFromUtf8(isolate.GetIsolate(), "fun2"),
     95                             notify_fun2);
     96     context4->Global()->Set(String::NewFromUtf8(isolate.GetIsolate(), "fun3"),
     97                             notify_fun3);
     98     CompileRun("fun1(); fun2(); fun3(); Object.deliverChangeRecords(observer)");
     99   }
    100   CHECK_EQ(1, CompileRun("calls")->Int32Value());
    101   CHECK_EQ(3, CompileRun("count")->Int32Value());
    102 }
    103 
    104 
    105 TEST(EndOfMicrotaskDelivery) {
    106   HarmonyIsolate isolate;
    107   HandleScope scope(isolate.GetIsolate());
    108   LocalContext context(isolate.GetIsolate());
    109   CompileRun(
    110       "var obj = {};"
    111       "var count = 0;"
    112       "var observer = function(records) { count = records.length };"
    113       "Object.observe(obj, observer);"
    114       "obj.foo = 'bar';");
    115   CHECK_EQ(1, CompileRun("count")->Int32Value());
    116 }
    117 
    118 
    119 TEST(DeliveryOrdering) {
    120   HarmonyIsolate isolate;
    121   HandleScope scope(isolate.GetIsolate());
    122   LocalContext context(isolate.GetIsolate());
    123   CompileRun(
    124       "var obj1 = {};"
    125       "var obj2 = {};"
    126       "var ordering = [];"
    127       "function observer2() { ordering.push(2); };"
    128       "function observer1() { ordering.push(1); };"
    129       "function observer3() { ordering.push(3); };"
    130       "Object.observe(obj1, observer1);"
    131       "Object.observe(obj1, observer2);"
    132       "Object.observe(obj1, observer3);"
    133       "obj1.foo = 'bar';");
    134   CHECK_EQ(3, CompileRun("ordering.length")->Int32Value());
    135   CHECK_EQ(1, CompileRun("ordering[0]")->Int32Value());
    136   CHECK_EQ(2, CompileRun("ordering[1]")->Int32Value());
    137   CHECK_EQ(3, CompileRun("ordering[2]")->Int32Value());
    138   CompileRun(
    139       "ordering = [];"
    140       "Object.observe(obj2, observer3);"
    141       "Object.observe(obj2, observer2);"
    142       "Object.observe(obj2, observer1);"
    143       "obj2.foo = 'baz'");
    144   CHECK_EQ(3, CompileRun("ordering.length")->Int32Value());
    145   CHECK_EQ(1, CompileRun("ordering[0]")->Int32Value());
    146   CHECK_EQ(2, CompileRun("ordering[1]")->Int32Value());
    147   CHECK_EQ(3, CompileRun("ordering[2]")->Int32Value());
    148 }
    149 
    150 
    151 TEST(DeliveryOrderingReentrant) {
    152   HarmonyIsolate isolate;
    153   HandleScope scope(isolate.GetIsolate());
    154   LocalContext context(isolate.GetIsolate());
    155   CompileRun(
    156       "var obj = {};"
    157       "var reentered = false;"
    158       "var ordering = [];"
    159       "function observer1() { ordering.push(1); };"
    160       "function observer2() {"
    161       "  if (!reentered) {"
    162       "    obj.foo = 'baz';"
    163       "    reentered = true;"
    164       "  }"
    165       "  ordering.push(2);"
    166       "};"
    167       "function observer3() { ordering.push(3); };"
    168       "Object.observe(obj, observer1);"
    169       "Object.observe(obj, observer2);"
    170       "Object.observe(obj, observer3);"
    171       "obj.foo = 'bar';");
    172   CHECK_EQ(5, CompileRun("ordering.length")->Int32Value());
    173   CHECK_EQ(1, CompileRun("ordering[0]")->Int32Value());
    174   CHECK_EQ(2, CompileRun("ordering[1]")->Int32Value());
    175   CHECK_EQ(3, CompileRun("ordering[2]")->Int32Value());
    176   // Note that we re-deliver to observers 1 and 2, while observer3
    177   // already received the second record during the first round.
    178   CHECK_EQ(1, CompileRun("ordering[3]")->Int32Value());
    179   CHECK_EQ(2, CompileRun("ordering[1]")->Int32Value());
    180 }
    181 
    182 
    183 TEST(DeliveryOrderingDeliverChangeRecords) {
    184   HarmonyIsolate isolate;
    185   HandleScope scope(isolate.GetIsolate());
    186   LocalContext context(isolate.GetIsolate());
    187   CompileRun(
    188       "var obj = {};"
    189       "var ordering = [];"
    190       "function observer1() { ordering.push(1); if (!obj.b) obj.b = true };"
    191       "function observer2() { ordering.push(2); };"
    192       "Object.observe(obj, observer1);"
    193       "Object.observe(obj, observer2);"
    194       "obj.a = 1;"
    195       "Object.deliverChangeRecords(observer2);");
    196   CHECK_EQ(4, CompileRun("ordering.length")->Int32Value());
    197   // First, observer2 is called due to deliverChangeRecords
    198   CHECK_EQ(2, CompileRun("ordering[0]")->Int32Value());
    199   // Then, observer1 is called when the stack unwinds
    200   CHECK_EQ(1, CompileRun("ordering[1]")->Int32Value());
    201   // observer1's mutation causes both 1 and 2 to be reactivated,
    202   // with 1 having priority.
    203   CHECK_EQ(1, CompileRun("ordering[2]")->Int32Value());
    204   CHECK_EQ(2, CompileRun("ordering[3]")->Int32Value());
    205 }
    206 
    207 
    208 TEST(ObjectHashTableGrowth) {
    209   HarmonyIsolate isolate;
    210   HandleScope scope(isolate.GetIsolate());
    211   // Initializing this context sets up initial hash tables.
    212   LocalContext context(isolate.GetIsolate());
    213   Handle<Value> obj = CompileRun("obj = {};");
    214   Handle<Value> observer = CompileRun(
    215       "var ran = false;"
    216       "(function() { ran = true })");
    217   {
    218     // As does initializing this context.
    219     LocalContext context2(isolate.GetIsolate());
    220     context2->Global()->Set(String::NewFromUtf8(isolate.GetIsolate(), "obj"),
    221                             obj);
    222     context2->Global()->Set(
    223         String::NewFromUtf8(isolate.GetIsolate(), "observer"), observer);
    224     CompileRun(
    225         "var objArr = [];"
    226         // 100 objects should be enough to make the hash table grow
    227         // (and thus relocate).
    228         "for (var i = 0; i < 100; ++i) {"
    229         "  objArr.push({});"
    230         "  Object.observe(objArr[objArr.length-1], function(){});"
    231         "}"
    232         "Object.observe(obj, observer);");
    233   }
    234   // obj is now marked "is_observed", but our map has moved.
    235   CompileRun("obj.foo = 'bar'");
    236   CHECK(CompileRun("ran")->BooleanValue());
    237 }
    238 
    239 
    240 TEST(GlobalObjectObservation) {
    241   HarmonyIsolate isolate;
    242   LocalContext context(isolate.GetIsolate());
    243   HandleScope scope(isolate.GetIsolate());
    244   Handle<Object> global_proxy = context->Global();
    245   CompileRun(
    246       "var records = [];"
    247       "var global = this;"
    248       "Object.observe(global, function(r) { [].push.apply(records, r) });"
    249       "global.foo = 'hello';");
    250   CHECK_EQ(1, CompileRun("records.length")->Int32Value());
    251   CHECK(global_proxy->StrictEquals(CompileRun("records[0].object")));
    252 
    253   // Detached, mutating the proxy has no effect.
    254   context->DetachGlobal();
    255   CompileRun("global.bar = 'goodbye';");
    256   CHECK_EQ(1, CompileRun("records.length")->Int32Value());
    257   CompileRun("this.baz = 'goodbye';");
    258   CHECK_EQ(1, CompileRun("records.length")->Int32Value());
    259 
    260   // Attached to a different context, should not leak mutations
    261   // to the old context.
    262   context->DetachGlobal();
    263   {
    264     LocalContext context2(isolate.GetIsolate());
    265     CompileRun(
    266         "var records2 = [];"
    267         "var global = this;"
    268         "Object.observe(this, function(r) { [].push.apply(records2, r) });"
    269         "this.v1 = 'context2';");
    270     context2->DetachGlobal();
    271     CompileRun(
    272         "global.v2 = 'context2';"
    273         "this.v3 = 'context2';");
    274     CHECK_EQ(1, CompileRun("records2.length")->Int32Value());
    275   }
    276   CHECK_EQ(1, CompileRun("records.length")->Int32Value());
    277 
    278   // Attaching by passing to Context::New
    279   {
    280     // Delegates to Context::New
    281     LocalContext context3(
    282         isolate.GetIsolate(), NULL, Handle<ObjectTemplate>(), global_proxy);
    283     CompileRun(
    284         "var records3 = [];"
    285         "Object.observe(this, function(r) { [].push.apply(records3, r) });"
    286         "this.qux = 'context3';");
    287     CHECK_EQ(1, CompileRun("records3.length")->Int32Value());
    288     CHECK(global_proxy->StrictEquals(CompileRun("records3[0].object")));
    289   }
    290   CHECK_EQ(1, CompileRun("records.length")->Int32Value());
    291 }
    292 
    293 
    294 struct RecordExpectation {
    295   Handle<Value> object;
    296   const char* type;
    297   const char* name;
    298   Handle<Value> old_value;
    299 };
    300 
    301 
    302 // TODO(adamk): Use this helper elsewhere in this file.
    303 static void ExpectRecords(v8::Isolate* isolate,
    304                           Handle<Value> records,
    305                           const RecordExpectation expectations[],
    306                           int num) {
    307   CHECK(records->IsArray());
    308   Handle<Array> recordArray = records.As<Array>();
    309   CHECK_EQ(num, static_cast<int>(recordArray->Length()));
    310   for (int i = 0; i < num; ++i) {
    311     Handle<Value> record = recordArray->Get(i);
    312     CHECK(record->IsObject());
    313     Handle<Object> recordObj = record.As<Object>();
    314     CHECK(expectations[i].object->StrictEquals(
    315         recordObj->Get(String::NewFromUtf8(isolate, "object"))));
    316     CHECK(String::NewFromUtf8(isolate, expectations[i].type)->Equals(
    317         recordObj->Get(String::NewFromUtf8(isolate, "type"))));
    318     if (strcmp("splice", expectations[i].type) != 0) {
    319       CHECK(String::NewFromUtf8(isolate, expectations[i].name)->Equals(
    320           recordObj->Get(String::NewFromUtf8(isolate, "name"))));
    321       if (!expectations[i].old_value.IsEmpty()) {
    322         CHECK(expectations[i].old_value->Equals(
    323             recordObj->Get(String::NewFromUtf8(isolate, "oldValue"))));
    324       }
    325     }
    326   }
    327 }
    328 
    329 #define EXPECT_RECORDS(records, expectations)                \
    330   ExpectRecords(isolate.GetIsolate(), records, expectations, \
    331                 ARRAY_SIZE(expectations))
    332 
    333 TEST(APITestBasicMutation) {
    334   HarmonyIsolate isolate;
    335   HandleScope scope(isolate.GetIsolate());
    336   LocalContext context(isolate.GetIsolate());
    337   Handle<Object> obj = Handle<Object>::Cast(CompileRun(
    338       "var records = [];"
    339       "var obj = {};"
    340       "function observer(r) { [].push.apply(records, r); };"
    341       "Object.observe(obj, observer);"
    342       "obj"));
    343   obj->Set(String::NewFromUtf8(isolate.GetIsolate(), "foo"), Number::New(7));
    344   obj->Set(1, Number::New(2));
    345   // ForceSet should work just as well as Set
    346   obj->ForceSet(String::NewFromUtf8(isolate.GetIsolate(), "foo"),
    347                 Number::New(3));
    348   obj->ForceSet(Number::New(1), Number::New(4));
    349   // Setting an indexed element via the property setting method
    350   obj->Set(Number::New(1), Number::New(5));
    351   // Setting with a non-String, non-uint32 key
    352   obj->Set(Number::New(1.1), Number::New(6), DontDelete);
    353   obj->Delete(String::NewFromUtf8(isolate.GetIsolate(), "foo"));
    354   obj->Delete(1);
    355   obj->ForceDelete(Number::New(1.1));
    356 
    357   // Force delivery
    358   // TODO(adamk): Should the above set methods trigger delivery themselves?
    359   CompileRun("void 0");
    360   CHECK_EQ(9, CompileRun("records.length")->Int32Value());
    361   const RecordExpectation expected_records[] = {
    362     { obj, "add", "foo", Handle<Value>() },
    363     { obj, "add", "1", Handle<Value>() },
    364     // Note: use 7 not 1 below, as the latter triggers a nifty VS10 compiler bug
    365     // where instead of 1.0, a garbage value would be passed into Number::New.
    366     { obj, "update", "foo", Number::New(7) },
    367     { obj, "update", "1", Number::New(2) },
    368     { obj, "update", "1", Number::New(4) },
    369     { obj, "add", "1.1", Handle<Value>() },
    370     { obj, "delete", "foo", Number::New(3) },
    371     { obj, "delete", "1", Number::New(5) },
    372     { obj, "delete", "1.1", Number::New(6) }
    373   };
    374   EXPECT_RECORDS(CompileRun("records"), expected_records);
    375 }
    376 
    377 
    378 TEST(HiddenPrototypeObservation) {
    379   HarmonyIsolate isolate;
    380   HandleScope scope(isolate.GetIsolate());
    381   LocalContext context(isolate.GetIsolate());
    382   Handle<FunctionTemplate> tmpl = FunctionTemplate::New();
    383   tmpl->SetHiddenPrototype(true);
    384   tmpl->InstanceTemplate()->Set(
    385       String::NewFromUtf8(isolate.GetIsolate(), "foo"), Number::New(75));
    386   Handle<Object> proto = tmpl->GetFunction()->NewInstance();
    387   Handle<Object> obj = Object::New();
    388   obj->SetPrototype(proto);
    389   context->Global()->Set(String::NewFromUtf8(isolate.GetIsolate(), "obj"), obj);
    390   context->Global()->Set(String::NewFromUtf8(isolate.GetIsolate(), "proto"),
    391                          proto);
    392   CompileRun(
    393       "var records;"
    394       "function observer(r) { records = r; };"
    395       "Object.observe(obj, observer);"
    396       "obj.foo = 41;"  // triggers a notification
    397       "proto.foo = 42;");  // does not trigger a notification
    398   const RecordExpectation expected_records[] = {
    399     { obj, "update", "foo", Number::New(75) }
    400   };
    401   EXPECT_RECORDS(CompileRun("records"), expected_records);
    402   obj->SetPrototype(Null(isolate.GetIsolate()));
    403   CompileRun("obj.foo = 43");
    404   const RecordExpectation expected_records2[] = {
    405     { obj, "add", "foo", Handle<Value>() }
    406   };
    407   EXPECT_RECORDS(CompileRun("records"), expected_records2);
    408   obj->SetPrototype(proto);
    409   CompileRun(
    410       "Object.observe(proto, observer);"
    411       "proto.bar = 1;"
    412       "Object.unobserve(obj, observer);"
    413       "obj.foo = 44;");
    414   const RecordExpectation expected_records3[] = {
    415     { proto, "add", "bar", Handle<Value>() }
    416     // TODO(adamk): The below record should be emitted since proto is observed
    417     // and has been modified. Not clear if this happens in practice.
    418     // { proto, "update", "foo", Number::New(43) }
    419   };
    420   EXPECT_RECORDS(CompileRun("records"), expected_records3);
    421 }
    422 
    423 
    424 static int NumberOfElements(i::Handle<i::JSWeakMap> map) {
    425   return i::ObjectHashTable::cast(map->table())->NumberOfElements();
    426 }
    427 
    428 
    429 TEST(ObservationWeakMap) {
    430   HarmonyIsolate isolate;
    431   HandleScope scope(isolate.GetIsolate());
    432   LocalContext context(isolate.GetIsolate());
    433   CompileRun(
    434       "var obj = {};"
    435       "Object.observe(obj, function(){});"
    436       "Object.getNotifier(obj);"
    437       "obj = null;");
    438   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate.GetIsolate());
    439   i::Handle<i::JSObject> observation_state =
    440       i_isolate->factory()->observation_state();
    441   i::Handle<i::JSWeakMap> callbackInfoMap =
    442       i::Handle<i::JSWeakMap>::cast(
    443           i::GetProperty(observation_state, "callbackInfoMap"));
    444   i::Handle<i::JSWeakMap> objectInfoMap =
    445       i::Handle<i::JSWeakMap>::cast(
    446           i::GetProperty(observation_state, "objectInfoMap"));
    447   i::Handle<i::JSWeakMap> notifierObjectInfoMap =
    448       i::Handle<i::JSWeakMap>::cast(
    449           i::GetProperty(observation_state, "notifierObjectInfoMap"));
    450   CHECK_EQ(1, NumberOfElements(callbackInfoMap));
    451   CHECK_EQ(1, NumberOfElements(objectInfoMap));
    452   CHECK_EQ(1, NumberOfElements(notifierObjectInfoMap));
    453   i_isolate->heap()->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask);
    454   CHECK_EQ(0, NumberOfElements(callbackInfoMap));
    455   CHECK_EQ(0, NumberOfElements(objectInfoMap));
    456   CHECK_EQ(0, NumberOfElements(notifierObjectInfoMap));
    457 }
    458 
    459 
    460 static bool NamedAccessAlwaysAllowed(Local<Object>, Local<Value>, AccessType,
    461                                      Local<Value>) {
    462   return true;
    463 }
    464 
    465 
    466 static bool IndexedAccessAlwaysAllowed(Local<Object>, uint32_t, AccessType,
    467                                        Local<Value>) {
    468   return true;
    469 }
    470 
    471 
    472 static AccessType g_access_block_type = ACCESS_GET;
    473 static const uint32_t kBlockedContextIndex = 1337;
    474 
    475 
    476 static bool NamedAccessAllowUnlessBlocked(Local<Object> host,
    477                                           Local<Value> key,
    478                                           AccessType type,
    479                                           Local<Value> data) {
    480   if (type != g_access_block_type) return true;
    481   v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(
    482       Utils::OpenHandle(*host)->GetIsolate());
    483   Handle<Object> global = isolate->GetCurrentContext()->Global();
    484   if (!global->Has(kBlockedContextIndex)) return true;
    485   return !key->IsString() || !key->Equals(data);
    486 }
    487 
    488 
    489 static bool IndexedAccessAllowUnlessBlocked(Local<Object> host,
    490                                             uint32_t index,
    491                                             AccessType type,
    492                                             Local<Value> data) {
    493   if (type != g_access_block_type) return true;
    494   v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(
    495       Utils::OpenHandle(*host)->GetIsolate());
    496   Handle<Object> global = isolate->GetCurrentContext()->Global();
    497   if (!global->Has(kBlockedContextIndex)) return true;
    498   return index != data->Uint32Value();
    499 }
    500 
    501 
    502 static bool BlockAccessKeys(Local<Object> host, Local<Value> key,
    503                             AccessType type, Local<Value>) {
    504   v8::Isolate* isolate = reinterpret_cast<v8::Isolate*>(
    505       Utils::OpenHandle(*host)->GetIsolate());
    506   Handle<Object> global = isolate->GetCurrentContext()->Global();
    507   return type != ACCESS_KEYS || !global->Has(kBlockedContextIndex);
    508 }
    509 
    510 
    511 static Handle<Object> CreateAccessCheckedObject(
    512     v8::Isolate* isolate,
    513     NamedSecurityCallback namedCallback,
    514     IndexedSecurityCallback indexedCallback,
    515     Handle<Value> data = Handle<Value>()) {
    516   Handle<ObjectTemplate> tmpl = ObjectTemplate::New();
    517   tmpl->SetAccessCheckCallbacks(namedCallback, indexedCallback, data);
    518   Handle<Object> instance = tmpl->NewInstance();
    519   Handle<Object> global = instance->CreationContext()->Global();
    520   global->Set(String::NewFromUtf8(isolate, "obj"), instance);
    521   global->Set(kBlockedContextIndex, v8::True(isolate));
    522   return instance;
    523 }
    524 
    525 
    526 TEST(NamedAccessCheck) {
    527   HarmonyIsolate isolate;
    528   const AccessType types[] = { ACCESS_GET, ACCESS_HAS };
    529   for (size_t i = 0; i < ARRAY_SIZE(types); ++i) {
    530     HandleScope scope(isolate.GetIsolate());
    531     LocalContext context(isolate.GetIsolate());
    532     g_access_block_type = types[i];
    533     Handle<Object> instance = CreateAccessCheckedObject(
    534         isolate.GetIsolate(),
    535         NamedAccessAllowUnlessBlocked,
    536         IndexedAccessAlwaysAllowed,
    537         String::NewFromUtf8(isolate.GetIsolate(), "foo"));
    538     CompileRun("var records = null;"
    539                "var objNoCheck = {};"
    540                "var observer = function(r) { records = r };"
    541                "Object.observe(obj, observer);"
    542                "Object.observe(objNoCheck, observer);");
    543     Handle<Value> obj_no_check = CompileRun("objNoCheck");
    544     {
    545       LocalContext context2(isolate.GetIsolate());
    546       context2->Global()->Set(String::NewFromUtf8(isolate.GetIsolate(), "obj"),
    547                               instance);
    548       context2->Global()->Set(
    549           String::NewFromUtf8(isolate.GetIsolate(), "objNoCheck"),
    550           obj_no_check);
    551       CompileRun("var records2 = null;"
    552                  "var observer2 = function(r) { records2 = r };"
    553                  "Object.observe(obj, observer2);"
    554                  "Object.observe(objNoCheck, observer2);"
    555                  "obj.foo = 'bar';"
    556                  "Object.defineProperty(obj, 'foo', {value: 5});"
    557                  "Object.defineProperty(obj, 'foo', {get: function(){}});"
    558                  "obj.bar = 'baz';"
    559                  "objNoCheck.baz = 'quux'");
    560       const RecordExpectation expected_records2[] = {
    561         { instance, "add", "foo", Handle<Value>() },
    562         { instance, "update", "foo",
    563           String::NewFromUtf8(isolate.GetIsolate(), "bar") },
    564         { instance, "reconfigure", "foo", Number::New(5) },
    565         { instance, "add", "bar", Handle<Value>() },
    566         { obj_no_check, "add", "baz", Handle<Value>() },
    567       };
    568       EXPECT_RECORDS(CompileRun("records2"), expected_records2);
    569     }
    570     const RecordExpectation expected_records[] = {
    571       { instance, "add", "bar", Handle<Value>() },
    572       { obj_no_check, "add", "baz", Handle<Value>() }
    573     };
    574     EXPECT_RECORDS(CompileRun("records"), expected_records);
    575   }
    576 }
    577 
    578 
    579 TEST(IndexedAccessCheck) {
    580   HarmonyIsolate isolate;
    581   const AccessType types[] = { ACCESS_GET, ACCESS_HAS };
    582   for (size_t i = 0; i < ARRAY_SIZE(types); ++i) {
    583     HandleScope scope(isolate.GetIsolate());
    584     LocalContext context(isolate.GetIsolate());
    585     g_access_block_type = types[i];
    586     Handle<Object> instance = CreateAccessCheckedObject(
    587         isolate.GetIsolate(), NamedAccessAlwaysAllowed,
    588         IndexedAccessAllowUnlessBlocked, Number::New(7));
    589     CompileRun("var records = null;"
    590                "var objNoCheck = {};"
    591                "var observer = function(r) { records = r };"
    592                "Object.observe(obj, observer);"
    593                "Object.observe(objNoCheck, observer);");
    594     Handle<Value> obj_no_check = CompileRun("objNoCheck");
    595     {
    596       LocalContext context2(isolate.GetIsolate());
    597       context2->Global()->Set(String::NewFromUtf8(isolate.GetIsolate(), "obj"),
    598                               instance);
    599       context2->Global()->Set(
    600           String::NewFromUtf8(isolate.GetIsolate(), "objNoCheck"),
    601           obj_no_check);
    602       CompileRun("var records2 = null;"
    603                  "var observer2 = function(r) { records2 = r };"
    604                  "Object.observe(obj, observer2);"
    605                  "Object.observe(objNoCheck, observer2);"
    606                  "obj[7] = 'foo';"
    607                  "Object.defineProperty(obj, '7', {value: 5});"
    608                  "Object.defineProperty(obj, '7', {get: function(){}});"
    609                  "obj[8] = 'bar';"
    610                  "objNoCheck[42] = 'quux'");
    611       const RecordExpectation expected_records2[] = {
    612         { instance, "add", "7", Handle<Value>() },
    613         { instance, "update", "7",
    614           String::NewFromUtf8(isolate.GetIsolate(), "foo") },
    615         { instance, "reconfigure", "7", Number::New(5) },
    616         { instance, "add", "8", Handle<Value>() },
    617         { obj_no_check, "add", "42", Handle<Value>() }
    618       };
    619       EXPECT_RECORDS(CompileRun("records2"), expected_records2);
    620     }
    621     const RecordExpectation expected_records[] = {
    622       { instance, "add", "8", Handle<Value>() },
    623       { obj_no_check, "add", "42", Handle<Value>() }
    624     };
    625     EXPECT_RECORDS(CompileRun("records"), expected_records);
    626   }
    627 }
    628 
    629 
    630 TEST(SpliceAccessCheck) {
    631   HarmonyIsolate isolate;
    632   HandleScope scope(isolate.GetIsolate());
    633   LocalContext context(isolate.GetIsolate());
    634   g_access_block_type = ACCESS_GET;
    635   Handle<Object> instance = CreateAccessCheckedObject(
    636       isolate.GetIsolate(), NamedAccessAlwaysAllowed,
    637       IndexedAccessAllowUnlessBlocked, Number::New(1));
    638   CompileRun("var records = null;"
    639              "obj[1] = 'foo';"
    640              "obj.length = 2;"
    641              "var objNoCheck = {1: 'bar', length: 2};"
    642              "observer = function(r) { records = r };"
    643              "Array.observe(obj, observer);"
    644              "Array.observe(objNoCheck, observer);");
    645   Handle<Value> obj_no_check = CompileRun("objNoCheck");
    646   {
    647     LocalContext context2(isolate.GetIsolate());
    648     context2->Global()->Set(String::NewFromUtf8(isolate.GetIsolate(), "obj"),
    649                             instance);
    650     context2->Global()->Set(
    651         String::NewFromUtf8(isolate.GetIsolate(), "objNoCheck"), obj_no_check);
    652     CompileRun("var records2 = null;"
    653                "var observer2 = function(r) { records2 = r };"
    654                "Array.observe(obj, observer2);"
    655                "Array.observe(objNoCheck, observer2);"
    656                // No one should hear about this: no splice records are emitted
    657                // for access-checked objects
    658                "[].push.call(obj, 5);"
    659                "[].splice.call(obj, 1, 1);"
    660                "[].pop.call(obj);"
    661                "[].pop.call(objNoCheck);");
    662     // TODO(adamk): Extend EXPECT_RECORDS to be able to assert more things
    663     // about splice records. For this test it's not so important since
    664     // we just want to guarantee the machinery is in operation at all.
    665     const RecordExpectation expected_records2[] = {
    666       { obj_no_check, "splice", "", Handle<Value>() }
    667     };
    668     EXPECT_RECORDS(CompileRun("records2"), expected_records2);
    669   }
    670   const RecordExpectation expected_records[] = {
    671     { obj_no_check, "splice", "", Handle<Value>() }
    672   };
    673   EXPECT_RECORDS(CompileRun("records"), expected_records);
    674 }
    675 
    676 
    677 TEST(DisallowAllForAccessKeys) {
    678   HarmonyIsolate isolate;
    679   HandleScope scope(isolate.GetIsolate());
    680   LocalContext context(isolate.GetIsolate());
    681   Handle<Object> instance = CreateAccessCheckedObject(
    682       isolate.GetIsolate(), BlockAccessKeys, IndexedAccessAlwaysAllowed);
    683   CompileRun("var records = null;"
    684              "var objNoCheck = {};"
    685              "var observer = function(r) { records = r };"
    686              "Object.observe(obj, observer);"
    687              "Object.observe(objNoCheck, observer);");
    688   Handle<Value> obj_no_check = CompileRun("objNoCheck");
    689   {
    690     LocalContext context2(isolate.GetIsolate());
    691     context2->Global()->Set(String::NewFromUtf8(isolate.GetIsolate(), "obj"),
    692                             instance);
    693     context2->Global()->Set(
    694         String::NewFromUtf8(isolate.GetIsolate(), "objNoCheck"), obj_no_check);
    695     CompileRun("var records2 = null;"
    696                "var observer2 = function(r) { records2 = r };"
    697                "Object.observe(obj, observer2);"
    698                "Object.observe(objNoCheck, observer2);"
    699                "obj.foo = 'bar';"
    700                "obj[5] = 'baz';"
    701                "objNoCheck.baz = 'quux'");
    702     const RecordExpectation expected_records2[] = {
    703       { instance, "add", "foo", Handle<Value>() },
    704       { instance, "add", "5", Handle<Value>() },
    705       { obj_no_check, "add", "baz", Handle<Value>() },
    706     };
    707     EXPECT_RECORDS(CompileRun("records2"), expected_records2);
    708   }
    709   const RecordExpectation expected_records[] = {
    710     { obj_no_check, "add", "baz", Handle<Value>() }
    711   };
    712   EXPECT_RECORDS(CompileRun("records"), expected_records);
    713 }
    714 
    715 
    716 TEST(AccessCheckDisallowApiModifications) {
    717   HarmonyIsolate isolate;
    718   HandleScope scope(isolate.GetIsolate());
    719   LocalContext context(isolate.GetIsolate());
    720   Handle<Object> instance = CreateAccessCheckedObject(
    721       isolate.GetIsolate(), BlockAccessKeys, IndexedAccessAlwaysAllowed);
    722   CompileRun("var records = null;"
    723              "var observer = function(r) { records = r };"
    724              "Object.observe(obj, observer);");
    725   {
    726     LocalContext context2(isolate.GetIsolate());
    727     context2->Global()->Set(String::NewFromUtf8(isolate.GetIsolate(), "obj"),
    728                             instance);
    729     CompileRun("var records2 = null;"
    730                "var observer2 = function(r) { records2 = r };"
    731                "Object.observe(obj, observer2);");
    732     instance->Set(5, String::NewFromUtf8(isolate.GetIsolate(), "bar"));
    733     instance->Set(String::NewFromUtf8(isolate.GetIsolate(), "foo"),
    734                   String::NewFromUtf8(isolate.GetIsolate(), "bar"));
    735     CompileRun("");  // trigger delivery
    736     const RecordExpectation expected_records2[] = {
    737       { instance, "add", "5", Handle<Value>() },
    738       { instance, "add", "foo", Handle<Value>() }
    739     };
    740     EXPECT_RECORDS(CompileRun("records2"), expected_records2);
    741   }
    742   CHECK(CompileRun("records")->IsNull());
    743 }
    744 
    745 
    746 TEST(HiddenPropertiesLeakage) {
    747   HarmonyIsolate isolate;
    748   HandleScope scope(isolate.GetIsolate());
    749   LocalContext context(isolate.GetIsolate());
    750   CompileRun("var obj = {};"
    751              "var records = null;"
    752              "var observer = function(r) { records = r };"
    753              "Object.observe(obj, observer);");
    754   Handle<Value> obj =
    755       context->Global()->Get(String::NewFromUtf8(isolate.GetIsolate(), "obj"));
    756   Handle<Object>::Cast(obj)
    757       ->SetHiddenValue(String::NewFromUtf8(isolate.GetIsolate(), "foo"),
    758                        Null(isolate.GetIsolate()));
    759   CompileRun("");  // trigger delivery
    760   CHECK(CompileRun("records")->IsNull());
    761 }
    762