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