• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  
TEST(PerIsolateState)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  
TEST(EndOfMicrotaskDelivery)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  
TEST(DeliveryOrdering)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  
TEST(DeliveryOrderingReentrant)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  
TEST(DeliveryOrderingDeliverChangeRecords)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  
TEST(ObjectHashTableGrowth)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.
ExpectRecords(v8::Isolate * isolate,Handle<Value> records,const RecordExpectation expectations[],int num)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  
TEST(APITestBasicMutation)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  
TEST(HiddenPrototypeObservation)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  
NumberOfElements(i::Handle<i::JSWeakMap> map)351  static int NumberOfElements(i::Handle<i::JSWeakMap> map) {
352    return i::ObjectHashTable::cast(map->table())->NumberOfElements();
353  }
354  
355  
TEST(ObservationWeakMap)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  
TestObserveSecurity(Handle<Context> observer_context,Handle<Context> object_context,Handle<Context> mutation_context)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  
TEST(ObserverSecurityAAA)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  
TEST(ObserverSecurityA1A2A3)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  
TEST(ObserverSecurityAAB)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  
TEST(ObserverSecurityA1A2B)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  
TEST(ObserverSecurityABA)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  
TEST(ObserverSecurityA1BA2)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  
TEST(ObserverSecurityBAA)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  
TEST(ObserverSecurityBA1A2)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  
TEST(ObserverSecurityNotify)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  
TEST(HiddenPropertiesLeakage)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  
TEST(GetNotifierFromOtherContext)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  
TEST(GetNotifierFromOtherOrigin)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  
TEST(GetNotifierFromSameOrigin)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  
GetGlobalObjectsCount()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  
CheckSurvivingGlobalObjectsCount(int expected)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  
TEST(DontLeakContextOnObserve)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  
TEST(DontLeakContextOnGetNotifier)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  
TEST(DontLeakContextOnNotifierPerformChange)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