Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Side by Side Diff: test/cctest/test-object-observe.cc

Issue 1909433003: Remove support for Object.observe (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Rebased Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « test/cctest/test-microtask-delivery.cc ('k') | test/mjsunit/array-push7.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 inline int32_t ToInt32(v8::Local<v8::Value> value) {
36 return value->Int32Value(v8::Isolate::GetCurrent()->GetCurrentContext())
37 .FromJust();
38 }
39
40
41 TEST(PerIsolateState) {
42 i::FLAG_harmony_object_observe = true;
43 HandleScope scope(CcTest::isolate());
44 LocalContext context1(CcTest::isolate());
45
46 Local<Value> foo = v8_str("foo");
47 context1->SetSecurityToken(foo);
48
49 CompileRun(
50 "var count = 0;"
51 "var calls = 0;"
52 "var observer = function(records) { count = records.length; calls++ };"
53 "var obj = {};"
54 "Object.observe(obj, observer);");
55 Local<Value> observer = CompileRun("observer");
56 Local<Value> obj = CompileRun("obj");
57 Local<Value> notify_fun1 = CompileRun("(function() { obj.foo = 'bar'; })");
58 Local<Value> notify_fun2;
59 {
60 LocalContext context2(CcTest::isolate());
61 context2->SetSecurityToken(foo);
62 context2->Global()
63 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("obj"),
64 obj)
65 .FromJust();
66 notify_fun2 = CompileRun(
67 "(function() { obj.foo = 'baz'; })");
68 }
69 Local<Value> notify_fun3;
70 {
71 LocalContext context3(CcTest::isolate());
72 context3->SetSecurityToken(foo);
73 context3->Global()
74 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("obj"),
75 obj)
76 .FromJust();
77 notify_fun3 = CompileRun("(function() { obj.foo = 'bat'; })");
78 }
79 {
80 LocalContext context4(CcTest::isolate());
81 context4->SetSecurityToken(foo);
82 context4->Global()
83 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(),
84 v8_str("observer"), observer)
85 .FromJust();
86 context4->Global()
87 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("fun1"),
88 notify_fun1)
89 .FromJust();
90 context4->Global()
91 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("fun2"),
92 notify_fun2)
93 .FromJust();
94 context4->Global()
95 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("fun3"),
96 notify_fun3)
97 .FromJust();
98 CompileRun("fun1(); fun2(); fun3(); Object.deliverChangeRecords(observer)");
99 }
100 CHECK_EQ(1, ToInt32(CompileRun("calls")));
101 CHECK_EQ(3, ToInt32(CompileRun("count")));
102 }
103
104
105 TEST(EndOfMicrotaskDelivery) {
106 i::FLAG_harmony_object_observe = true;
107 HandleScope scope(CcTest::isolate());
108 LocalContext context(CcTest::isolate());
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, ToInt32(CompileRun("count")));
116 }
117
118
119 TEST(DeliveryOrdering) {
120 i::FLAG_harmony_object_observe = true;
121 HandleScope scope(CcTest::isolate());
122 LocalContext context(CcTest::isolate());
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, ToInt32(CompileRun("ordering.length")));
135 CHECK_EQ(1, ToInt32(CompileRun("ordering[0]")));
136 CHECK_EQ(2, ToInt32(CompileRun("ordering[1]")));
137 CHECK_EQ(3, ToInt32(CompileRun("ordering[2]")));
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, ToInt32(CompileRun("ordering.length")));
145 CHECK_EQ(1, ToInt32(CompileRun("ordering[0]")));
146 CHECK_EQ(2, ToInt32(CompileRun("ordering[1]")));
147 CHECK_EQ(3, ToInt32(CompileRun("ordering[2]")));
148 }
149
150
151 TEST(DeliveryCallbackThrows) {
152 i::FLAG_harmony_object_observe = true;
153 HandleScope scope(CcTest::isolate());
154 LocalContext context(CcTest::isolate());
155 CompileRun(
156 "var obj = {};"
157 "var ordering = [];"
158 "function observer1() { ordering.push(1); };"
159 "function observer2() { ordering.push(2); };"
160 "function observer_throws() {"
161 " ordering.push(0);"
162 " throw new Error();"
163 " ordering.push(-1);"
164 "};"
165 "Object.observe(obj, observer_throws.bind());"
166 "Object.observe(obj, observer1);"
167 "Object.observe(obj, observer_throws.bind());"
168 "Object.observe(obj, observer2);"
169 "Object.observe(obj, observer_throws.bind());"
170 "obj.foo = 'bar';");
171 CHECK_EQ(5, ToInt32(CompileRun("ordering.length")));
172 CHECK_EQ(0, ToInt32(CompileRun("ordering[0]")));
173 CHECK_EQ(1, ToInt32(CompileRun("ordering[1]")));
174 CHECK_EQ(0, ToInt32(CompileRun("ordering[2]")));
175 CHECK_EQ(2, ToInt32(CompileRun("ordering[3]")));
176 CHECK_EQ(0, ToInt32(CompileRun("ordering[4]")));
177 }
178
179
180 TEST(DeliveryChangesMutationInCallback) {
181 i::FLAG_harmony_object_observe = true;
182 HandleScope scope(CcTest::isolate());
183 LocalContext context(CcTest::isolate());
184 CompileRun(
185 "var obj = {};"
186 "var ordering = [];"
187 "function observer1(records) {"
188 " ordering.push(100 + records.length);"
189 " records.push(11);"
190 " records.push(22);"
191 "};"
192 "function observer2(records) {"
193 " ordering.push(200 + records.length);"
194 " records.push(33);"
195 " records.push(44);"
196 "};"
197 "Object.observe(obj, observer1);"
198 "Object.observe(obj, observer2);"
199 "obj.foo = 'bar';");
200 CHECK_EQ(2, ToInt32(CompileRun("ordering.length")));
201 CHECK_EQ(101, ToInt32(CompileRun("ordering[0]")));
202 CHECK_EQ(201, ToInt32(CompileRun("ordering[1]")));
203 }
204
205
206 TEST(DeliveryOrderingReentrant) {
207 i::FLAG_harmony_object_observe = true;
208 HandleScope scope(CcTest::isolate());
209 LocalContext context(CcTest::isolate());
210 CompileRun(
211 "var obj = {};"
212 "var reentered = false;"
213 "var ordering = [];"
214 "function observer1() { ordering.push(1); };"
215 "function observer2() {"
216 " if (!reentered) {"
217 " obj.foo = 'baz';"
218 " reentered = true;"
219 " }"
220 " ordering.push(2);"
221 "};"
222 "function observer3() { ordering.push(3); };"
223 "Object.observe(obj, observer1);"
224 "Object.observe(obj, observer2);"
225 "Object.observe(obj, observer3);"
226 "obj.foo = 'bar';");
227 CHECK_EQ(5, ToInt32(CompileRun("ordering.length")));
228 CHECK_EQ(1, ToInt32(CompileRun("ordering[0]")));
229 CHECK_EQ(2, ToInt32(CompileRun("ordering[1]")));
230 CHECK_EQ(3, ToInt32(CompileRun("ordering[2]")));
231 // Note that we re-deliver to observers 1 and 2, while observer3
232 // already received the second record during the first round.
233 CHECK_EQ(1, ToInt32(CompileRun("ordering[3]")));
234 CHECK_EQ(2, ToInt32(CompileRun("ordering[1]")));
235 }
236
237
238 TEST(DeliveryOrderingDeliverChangeRecords) {
239 i::FLAG_harmony_object_observe = true;
240 HandleScope scope(CcTest::isolate());
241 LocalContext context(CcTest::isolate());
242 CompileRun(
243 "var obj = {};"
244 "var ordering = [];"
245 "function observer1() { ordering.push(1); if (!obj.b) obj.b = true };"
246 "function observer2() { ordering.push(2); };"
247 "Object.observe(obj, observer1);"
248 "Object.observe(obj, observer2);"
249 "obj.a = 1;"
250 "Object.deliverChangeRecords(observer2);");
251 CHECK_EQ(4, ToInt32(CompileRun("ordering.length")));
252 // First, observer2 is called due to deliverChangeRecords
253 CHECK_EQ(2, ToInt32(CompileRun("ordering[0]")));
254 // Then, observer1 is called when the stack unwinds
255 CHECK_EQ(1, ToInt32(CompileRun("ordering[1]")));
256 // observer1's mutation causes both 1 and 2 to be reactivated,
257 // with 1 having priority.
258 CHECK_EQ(1, ToInt32(CompileRun("ordering[2]")));
259 CHECK_EQ(2, ToInt32(CompileRun("ordering[3]")));
260 }
261
262
263 TEST(ObjectHashTableGrowth) {
264 i::FLAG_harmony_object_observe = true;
265 HandleScope scope(CcTest::isolate());
266 // Initializing this context sets up initial hash tables.
267 LocalContext context(CcTest::isolate());
268 Local<Value> obj = CompileRun("obj = {};");
269 Local<Value> observer = CompileRun(
270 "var ran = false;"
271 "(function() { ran = true })");
272 {
273 // As does initializing this context.
274 LocalContext context2(CcTest::isolate());
275 context2->Global()
276 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("obj"),
277 obj)
278 .FromJust();
279 context2->Global()
280 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(),
281 v8_str("observer"), observer)
282 .FromJust();
283 CompileRun(
284 "var objArr = [];"
285 // 100 objects should be enough to make the hash table grow
286 // (and thus relocate).
287 "for (var i = 0; i < 100; ++i) {"
288 " objArr.push({});"
289 " Object.observe(objArr[objArr.length-1], function(){});"
290 "}"
291 "Object.observe(obj, observer);");
292 }
293 // obj is now marked "is_observed", but our map has moved.
294 CompileRun("obj.foo = 'bar'");
295 CHECK(CompileRun("ran")
296 ->BooleanValue(v8::Isolate::GetCurrent()->GetCurrentContext())
297 .FromJust());
298 }
299
300
301 struct RecordExpectation {
302 Local<Value> object;
303 const char* type;
304 const char* name;
305 Local<Value> old_value;
306 };
307
308
309 // TODO(adamk): Use this helper elsewhere in this file.
310 static void ExpectRecords(v8::Isolate* isolate, Local<Value> records,
311 const RecordExpectation expectations[], int num) {
312 CHECK(records->IsArray());
313 Local<Array> recordArray = records.As<Array>();
314 CHECK_EQ(num, static_cast<int>(recordArray->Length()));
315 for (int i = 0; i < num; ++i) {
316 Local<Value> record =
317 recordArray->Get(v8::Isolate::GetCurrent()->GetCurrentContext(), i)
318 .ToLocalChecked();
319 CHECK(record->IsObject());
320 Local<Object> recordObj = record.As<Object>();
321 Local<Value> value =
322 recordObj->Get(v8::Isolate::GetCurrent()->GetCurrentContext(),
323 v8_str("object"))
324 .ToLocalChecked();
325 CHECK(expectations[i].object->StrictEquals(value));
326 value = recordObj->Get(v8::Isolate::GetCurrent()->GetCurrentContext(),
327 v8_str("type"))
328 .ToLocalChecked();
329 CHECK(v8_str(expectations[i].type)
330 ->Equals(v8::Isolate::GetCurrent()->GetCurrentContext(), value)
331 .FromJust());
332 if (strcmp("splice", expectations[i].type) != 0) {
333 Local<Value> name =
334 recordObj->Get(v8::Isolate::GetCurrent()->GetCurrentContext(),
335 v8_str("name"))
336 .ToLocalChecked();
337 CHECK(v8_str(expectations[i].name)
338 ->Equals(v8::Isolate::GetCurrent()->GetCurrentContext(), name)
339 .FromJust());
340 if (!expectations[i].old_value.IsEmpty()) {
341 Local<Value> old_value =
342 recordObj->Get(v8::Isolate::GetCurrent()->GetCurrentContext(),
343 v8_str("oldValue"))
344 .ToLocalChecked();
345 CHECK(expectations[i]
346 .old_value->Equals(
347 v8::Isolate::GetCurrent()->GetCurrentContext(),
348 old_value)
349 .FromJust());
350 }
351 }
352 }
353 }
354
355 #define EXPECT_RECORDS(records, expectations) \
356 ExpectRecords(CcTest::isolate(), records, expectations, \
357 arraysize(expectations))
358
359 TEST(APITestBasicMutation) {
360 i::FLAG_harmony_object_observe = true;
361 v8::Isolate* v8_isolate = CcTest::isolate();
362 HandleScope scope(v8_isolate);
363 LocalContext context(v8_isolate);
364 Local<Object> obj = Local<Object>::Cast(
365 CompileRun("var records = [];"
366 "var obj = {};"
367 "function observer(r) { [].push.apply(records, r); };"
368 "Object.observe(obj, observer);"
369 "obj"));
370 obj->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("foo"),
371 Number::New(v8_isolate, 7))
372 .FromJust();
373 obj->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), 1,
374 Number::New(v8_isolate, 2))
375 .FromJust();
376 // CreateDataProperty should work just as well as Set
377 obj->CreateDataProperty(v8::Isolate::GetCurrent()->GetCurrentContext(),
378 v8_str("foo"), Number::New(v8_isolate, 3))
379 .FromJust();
380 obj->CreateDataProperty(v8::Isolate::GetCurrent()->GetCurrentContext(), 1,
381 Number::New(v8_isolate, 4))
382 .FromJust();
383 // Setting an indexed element via the property setting method
384 obj->Set(v8::Isolate::GetCurrent()->GetCurrentContext(),
385 Number::New(v8_isolate, 1), Number::New(v8_isolate, 5))
386 .FromJust();
387 // Setting with a non-String, non-uint32 key
388 obj->Set(v8::Isolate::GetCurrent()->GetCurrentContext(),
389 Number::New(v8_isolate, 1.1), Number::New(v8_isolate, 6))
390 .FromJust();
391 obj->Delete(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("foo"))
392 .FromJust();
393 obj->Delete(v8::Isolate::GetCurrent()->GetCurrentContext(), 1).FromJust();
394 obj->Delete(v8::Isolate::GetCurrent()->GetCurrentContext(),
395 Number::New(v8_isolate, 1.1))
396 .FromJust();
397
398 // Force delivery
399 // TODO(adamk): Should the above set methods trigger delivery themselves?
400 CompileRun("void 0");
401 CHECK_EQ(9, ToInt32(CompileRun("records.length")));
402 const RecordExpectation expected_records[] = {
403 {obj, "add", "foo", Local<Value>()},
404 {obj, "add", "1", Local<Value>()},
405 // Note: use 7 not 1 below, as the latter triggers a nifty VS10 compiler
406 // bug
407 // where instead of 1.0, a garbage value would be passed into Number::New.
408 {obj, "update", "foo", Number::New(v8_isolate, 7)},
409 {obj, "update", "1", Number::New(v8_isolate, 2)},
410 {obj, "update", "1", Number::New(v8_isolate, 4)},
411 {obj, "add", "1.1", Local<Value>()},
412 {obj, "delete", "foo", Number::New(v8_isolate, 3)},
413 {obj, "delete", "1", Number::New(v8_isolate, 5)},
414 {obj, "delete", "1.1", Number::New(v8_isolate, 6)}};
415 EXPECT_RECORDS(CompileRun("records"), expected_records);
416 }
417
418
419 TEST(HiddenPrototypeObservation) {
420 i::FLAG_harmony_object_observe = true;
421 v8::Isolate* v8_isolate = CcTest::isolate();
422 HandleScope scope(v8_isolate);
423 LocalContext context(v8_isolate);
424 Local<FunctionTemplate> tmpl = FunctionTemplate::New(v8_isolate);
425 tmpl->SetHiddenPrototype(true);
426 tmpl->InstanceTemplate()->Set(v8_str("foo"), Number::New(v8_isolate, 75));
427 Local<Function> function =
428 tmpl->GetFunction(v8::Isolate::GetCurrent()->GetCurrentContext())
429 .ToLocalChecked();
430 Local<Object> proto =
431 function->NewInstance(v8::Isolate::GetCurrent()->GetCurrentContext())
432 .ToLocalChecked();
433 Local<Object> obj = Object::New(v8_isolate);
434 obj->SetPrototype(v8::Isolate::GetCurrent()->GetCurrentContext(), proto)
435 .FromJust();
436 context->Global()
437 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("obj"), obj)
438 .FromJust();
439 context->Global()
440 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("proto"),
441 proto)
442 .FromJust();
443 CompileRun(
444 "var records;"
445 "function observer(r) { records = r; };"
446 "Object.observe(obj, observer);"
447 "obj.foo = 41;" // triggers a notification
448 "proto.foo = 42;"); // does not trigger a notification
449 const RecordExpectation expected_records[] = {
450 { obj, "update", "foo", Number::New(v8_isolate, 75) }
451 };
452 EXPECT_RECORDS(CompileRun("records"), expected_records);
453 obj->SetPrototype(v8::Isolate::GetCurrent()->GetCurrentContext(),
454 Null(v8_isolate))
455 .FromJust();
456 CompileRun("obj.foo = 43");
457 const RecordExpectation expected_records2[] = {
458 {obj, "add", "foo", Local<Value>()}};
459 EXPECT_RECORDS(CompileRun("records"), expected_records2);
460 obj->SetPrototype(v8::Isolate::GetCurrent()->GetCurrentContext(), proto)
461 .FromJust();
462 CompileRun(
463 "Object.observe(proto, observer);"
464 "proto.bar = 1;"
465 "Object.unobserve(obj, observer);"
466 "obj.foo = 44;");
467 const RecordExpectation expected_records3[] = {
468 {proto, "add", "bar", Local<Value>()}
469 // TODO(adamk): The below record should be emitted since proto is observed
470 // and has been modified. Not clear if this happens in practice.
471 // { proto, "update", "foo", Number::New(43) }
472 };
473 EXPECT_RECORDS(CompileRun("records"), expected_records3);
474 }
475
476
477 static int NumberOfElements(i::Handle<i::JSWeakMap> map) {
478 return i::ObjectHashTable::cast(map->table())->NumberOfElements();
479 }
480
481
482 TEST(ObservationWeakMap) {
483 i::FLAG_harmony_object_observe = true;
484 HandleScope scope(CcTest::isolate());
485 LocalContext context(CcTest::isolate());
486 CompileRun(
487 "var obj = {};"
488 "Object.observe(obj, function(){});"
489 "Object.getNotifier(obj);"
490 "obj = null;");
491 i::Isolate* i_isolate = CcTest::i_isolate();
492 i::Handle<i::JSObject> observation_state =
493 i_isolate->factory()->observation_state();
494 i::Handle<i::JSWeakMap> callbackInfoMap = i::Handle<i::JSWeakMap>::cast(
495 i::JSReceiver::GetProperty(i_isolate, observation_state,
496 "callbackInfoMap")
497 .ToHandleChecked());
498 i::Handle<i::JSWeakMap> objectInfoMap = i::Handle<i::JSWeakMap>::cast(
499 i::JSReceiver::GetProperty(i_isolate, observation_state, "objectInfoMap")
500 .ToHandleChecked());
501 i::Handle<i::JSWeakMap> notifierObjectInfoMap = i::Handle<i::JSWeakMap>::cast(
502 i::JSReceiver::GetProperty(i_isolate, observation_state,
503 "notifierObjectInfoMap")
504 .ToHandleChecked());
505 CHECK_EQ(1, NumberOfElements(callbackInfoMap));
506 CHECK_EQ(1, NumberOfElements(objectInfoMap));
507 CHECK_EQ(1, NumberOfElements(notifierObjectInfoMap));
508 i_isolate->heap()->CollectAllGarbage();
509 CHECK_EQ(0, NumberOfElements(callbackInfoMap));
510 CHECK_EQ(0, NumberOfElements(objectInfoMap));
511 CHECK_EQ(0, NumberOfElements(notifierObjectInfoMap));
512 }
513
514
515 static int TestObserveSecurity(Local<Context> observer_context,
516 Local<Context> object_context,
517 Local<Context> mutation_context) {
518 Context::Scope observer_scope(observer_context);
519 CompileRun("var records = null;"
520 "var observer = function(r) { records = r };");
521 Local<Value> observer = CompileRun("observer");
522 {
523 Context::Scope object_scope(object_context);
524 object_context->Global()
525 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(),
526 v8_str("observer"), observer)
527 .FromJust();
528 CompileRun("var obj = {};"
529 "obj.length = 0;"
530 "Object.observe(obj, observer,"
531 "['add', 'update', 'delete','reconfigure','splice']"
532 ");");
533 Local<Value> obj = CompileRun("obj");
534 {
535 Context::Scope mutation_scope(mutation_context);
536 mutation_context->Global()
537 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("obj"),
538 obj)
539 .FromJust();
540 CompileRun("obj.foo = 'bar';"
541 "obj.foo = 'baz';"
542 "delete obj.foo;"
543 "Object.defineProperty(obj, 'bar', {value: 'bot'});"
544 "Array.prototype.push.call(obj, 1, 2, 3);"
545 "Array.prototype.splice.call(obj, 1, 2, 2, 4);"
546 "Array.prototype.pop.call(obj);"
547 "Array.prototype.shift.call(obj);");
548 }
549 }
550 return ToInt32(CompileRun("records ? records.length : 0"));
551 }
552
553
554 TEST(ObserverSecurityAAA) {
555 i::FLAG_harmony_object_observe = true;
556 v8::Isolate* isolate = CcTest::isolate();
557 v8::HandleScope scope(isolate);
558 v8::Local<Context> contextA = Context::New(isolate);
559 CHECK_EQ(8, TestObserveSecurity(contextA, contextA, contextA));
560 }
561
562
563 TEST(ObserverSecurityA1A2A3) {
564 i::FLAG_harmony_object_observe = true;
565 v8::Isolate* isolate = CcTest::isolate();
566 v8::HandleScope scope(isolate);
567
568 v8::Local<Context> contextA1 = Context::New(isolate);
569 v8::Local<Context> contextA2 = Context::New(isolate);
570 v8::Local<Context> contextA3 = Context::New(isolate);
571
572 Local<Value> foo = v8_str("foo");
573 contextA1->SetSecurityToken(foo);
574 contextA2->SetSecurityToken(foo);
575 contextA3->SetSecurityToken(foo);
576
577 CHECK_EQ(8, TestObserveSecurity(contextA1, contextA2, contextA3));
578 }
579
580
581 TEST(ObserverSecurityAAB) {
582 i::FLAG_harmony_object_observe = true;
583 v8::Isolate* isolate = CcTest::isolate();
584 v8::HandleScope scope(isolate);
585 v8::Local<Context> contextA = Context::New(isolate);
586 v8::Local<Context> contextB = Context::New(isolate);
587 CHECK_EQ(0, TestObserveSecurity(contextA, contextA, contextB));
588 }
589
590
591 TEST(ObserverSecurityA1A2B) {
592 i::FLAG_harmony_object_observe = true;
593 v8::Isolate* isolate = CcTest::isolate();
594 v8::HandleScope scope(isolate);
595
596 v8::Local<Context> contextA1 = Context::New(isolate);
597 v8::Local<Context> contextA2 = Context::New(isolate);
598 v8::Local<Context> contextB = Context::New(isolate);
599
600 Local<Value> foo = v8_str("foo");
601 contextA1->SetSecurityToken(foo);
602 contextA2->SetSecurityToken(foo);
603
604 CHECK_EQ(0, TestObserveSecurity(contextA1, contextA2, contextB));
605 }
606
607
608 TEST(ObserverSecurityABA) {
609 i::FLAG_harmony_object_observe = true;
610 v8::Isolate* isolate = CcTest::isolate();
611 v8::HandleScope scope(isolate);
612 v8::Local<Context> contextA = Context::New(isolate);
613 v8::Local<Context> contextB = Context::New(isolate);
614 CHECK_EQ(0, TestObserveSecurity(contextA, contextB, contextA));
615 }
616
617
618 TEST(ObserverSecurityA1BA2) {
619 i::FLAG_harmony_object_observe = true;
620 v8::Isolate* isolate = CcTest::isolate();
621 v8::HandleScope scope(isolate);
622 v8::Local<Context> contextA1 = Context::New(isolate);
623 v8::Local<Context> contextA2 = Context::New(isolate);
624 v8::Local<Context> contextB = Context::New(isolate);
625
626 Local<Value> foo = v8_str("foo");
627 contextA1->SetSecurityToken(foo);
628 contextA2->SetSecurityToken(foo);
629
630 CHECK_EQ(0, TestObserveSecurity(contextA1, contextB, contextA2));
631 }
632
633
634 TEST(ObserverSecurityBAA) {
635 i::FLAG_harmony_object_observe = true;
636 v8::Isolate* isolate = CcTest::isolate();
637 v8::HandleScope scope(isolate);
638 v8::Local<Context> contextA = Context::New(isolate);
639 v8::Local<Context> contextB = Context::New(isolate);
640 CHECK_EQ(0, TestObserveSecurity(contextB, contextA, contextA));
641 }
642
643
644 TEST(ObserverSecurityBA1A2) {
645 i::FLAG_harmony_object_observe = true;
646 v8::Isolate* isolate = CcTest::isolate();
647 v8::HandleScope scope(isolate);
648 v8::Local<Context> contextA1 = Context::New(isolate);
649 v8::Local<Context> contextA2 = Context::New(isolate);
650 v8::Local<Context> contextB = Context::New(isolate);
651
652 Local<Value> foo = v8_str("foo");
653 contextA1->SetSecurityToken(foo);
654 contextA2->SetSecurityToken(foo);
655
656 CHECK_EQ(0, TestObserveSecurity(contextB, contextA1, contextA2));
657 }
658
659
660 TEST(ObserverSecurityNotify) {
661 i::FLAG_harmony_object_observe = true;
662 v8::Isolate* isolate = CcTest::isolate();
663 v8::HandleScope scope(isolate);
664 v8::Local<Context> contextA = Context::New(isolate);
665 v8::Local<Context> contextB = Context::New(isolate);
666
667 Context::Scope scopeA(contextA);
668 CompileRun("var obj = {};"
669 "var recordsA = null;"
670 "var observerA = function(r) { recordsA = r };"
671 "Object.observe(obj, observerA);");
672 Local<Value> obj = CompileRun("obj");
673
674 {
675 Context::Scope scopeB(contextB);
676 contextB->Global()
677 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("obj"),
678 obj)
679 .FromJust();
680 CompileRun("var recordsB = null;"
681 "var observerB = function(r) { recordsB = r };"
682 "Object.observe(obj, observerB);");
683 }
684
685 CompileRun("var notifier = Object.getNotifier(obj);"
686 "notifier.notify({ type: 'update' });");
687 CHECK_EQ(1, ToInt32(CompileRun("recordsA ? recordsA.length : 0")));
688
689 {
690 Context::Scope scopeB(contextB);
691 CHECK_EQ(0, ToInt32(CompileRun("recordsB ? recordsB.length : 0")));
692 }
693 }
694
695
696 TEST(HiddenPropertiesLeakage) {
697 i::FLAG_harmony_object_observe = true;
698 HandleScope scope(CcTest::isolate());
699 LocalContext context(CcTest::isolate());
700 CompileRun("var obj = {};"
701 "var records = null;"
702 "var observer = function(r) { records = r };"
703 "Object.observe(obj, observer);");
704 Local<Value> obj =
705 context->Global()
706 ->Get(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("obj"))
707 .ToLocalChecked();
708 Local<Object>::Cast(obj)
709 ->SetPrivate(v8::Isolate::GetCurrent()->GetCurrentContext(),
710 v8::Private::New(CcTest::isolate(), v8_str("foo")),
711 Null(CcTest::isolate()))
712 .FromJust();
713 CompileRun(""); // trigger delivery
714 CHECK(CompileRun("records")->IsNull());
715 }
716
717
718 TEST(GetNotifierFromOtherContext) {
719 i::FLAG_harmony_object_observe = true;
720 HandleScope scope(CcTest::isolate());
721 LocalContext context(CcTest::isolate());
722 CompileRun("var obj = {};");
723 Local<Value> instance = CompileRun("obj");
724 {
725 LocalContext context2(CcTest::isolate());
726 context2->Global()
727 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("obj"),
728 instance)
729 .FromJust();
730 CHECK(CompileRun("Object.getNotifier(obj)")->IsNull());
731 }
732 }
733
734
735 TEST(GetNotifierFromOtherOrigin) {
736 i::FLAG_harmony_object_observe = true;
737 HandleScope scope(CcTest::isolate());
738 Local<Value> foo = v8_str("foo");
739 Local<Value> bar = v8_str("bar");
740 LocalContext context(CcTest::isolate());
741 context->SetSecurityToken(foo);
742 CompileRun("var obj = {};");
743 Local<Value> instance = CompileRun("obj");
744 {
745 LocalContext context2(CcTest::isolate());
746 context2->SetSecurityToken(bar);
747 context2->Global()
748 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("obj"),
749 instance)
750 .FromJust();
751 CHECK(CompileRun("Object.getNotifier(obj)")->IsNull());
752 }
753 }
754
755
756 TEST(GetNotifierFromSameOrigin) {
757 i::FLAG_harmony_object_observe = true;
758 HandleScope scope(CcTest::isolate());
759 Local<Value> foo = v8_str("foo");
760 LocalContext context(CcTest::isolate());
761 context->SetSecurityToken(foo);
762 CompileRun("var obj = {};");
763 Local<Value> instance = CompileRun("obj");
764 {
765 LocalContext context2(CcTest::isolate());
766 context2->SetSecurityToken(foo);
767 context2->Global()
768 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("obj"),
769 instance)
770 .FromJust();
771 CHECK(CompileRun("Object.getNotifier(obj)")->IsObject());
772 }
773 }
774
775
776 static int GetGlobalObjectsCount() {
777 int count = 0;
778 i::HeapIterator it(CcTest::heap());
779 for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
780 if (object->IsJSGlobalObject()) {
781 i::JSGlobalObject* g = i::JSGlobalObject::cast(object);
782 // Skip dummy global object.
783 if (i::GlobalDictionary::cast(g->properties())->NumberOfElements() != 0) {
784 count++;
785 }
786 }
787 // Subtract one to compensate for the code stub context that is always present
788 return count - 1;
789 }
790
791
792 static void CheckSurvivingGlobalObjectsCount(int expected) {
793 // We need to collect all garbage twice to be sure that everything
794 // has been collected. This is because inline caches are cleared in
795 // the first garbage collection but some of the maps have already
796 // been marked at that point. Therefore some of the maps are not
797 // collected until the second garbage collection.
798 CcTest::heap()->CollectAllGarbage();
799 CcTest::heap()->CollectAllGarbage(i::Heap::kMakeHeapIterableMask);
800 int count = GetGlobalObjectsCount();
801 #ifdef DEBUG
802 if (count != expected) CcTest::heap()->TracePathToGlobal();
803 #endif
804 CHECK_EQ(expected, count);
805 }
806
807
808 TEST(DontLeakContextOnObserve) {
809 i::FLAG_harmony_object_observe = true;
810 HandleScope scope(CcTest::isolate());
811 Local<Value> foo = v8_str("foo");
812 LocalContext context(CcTest::isolate());
813 context->SetSecurityToken(foo);
814 CompileRun("var obj = {};");
815 Local<Value> object = CompileRun("obj");
816 {
817 HandleScope scope(CcTest::isolate());
818 LocalContext context2(CcTest::isolate());
819 context2->SetSecurityToken(foo);
820 context2->Global()
821 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("obj"),
822 object)
823 .FromJust();
824 CompileRun("function observer() {};"
825 "Object.observe(obj, observer, ['foo', 'bar', 'baz']);"
826 "Object.unobserve(obj, observer);");
827 }
828
829 CcTest::isolate()->ContextDisposedNotification();
830 CheckSurvivingGlobalObjectsCount(0);
831 }
832
833
834 TEST(DontLeakContextOnGetNotifier) {
835 i::FLAG_harmony_object_observe = true;
836 HandleScope scope(CcTest::isolate());
837 Local<Value> foo = v8_str("foo");
838 LocalContext context(CcTest::isolate());
839 context->SetSecurityToken(foo);
840 CompileRun("var obj = {};");
841 Local<Value> object = CompileRun("obj");
842 {
843 HandleScope scope(CcTest::isolate());
844 LocalContext context2(CcTest::isolate());
845 context2->SetSecurityToken(foo);
846 context2->Global()
847 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("obj"),
848 object)
849 .FromJust();
850 CompileRun("Object.getNotifier(obj);");
851 }
852
853 CcTest::isolate()->ContextDisposedNotification();
854 CheckSurvivingGlobalObjectsCount(0);
855 }
856
857
858 TEST(DontLeakContextOnNotifierPerformChange) {
859 i::FLAG_harmony_object_observe = true;
860 HandleScope scope(CcTest::isolate());
861 Local<Value> foo = v8_str("foo");
862 LocalContext context(CcTest::isolate());
863 context->SetSecurityToken(foo);
864 CompileRun("var obj = {};");
865 Local<Value> object = CompileRun("obj");
866 Local<Value> notifier = CompileRun("Object.getNotifier(obj)");
867 {
868 HandleScope scope(CcTest::isolate());
869 LocalContext context2(CcTest::isolate());
870 context2->SetSecurityToken(foo);
871 context2->Global()
872 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("obj"),
873 object)
874 .FromJust();
875 context2->Global()
876 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(),
877 v8_str("notifier"), notifier)
878 .FromJust();
879 CompileRun("var obj2 = {};"
880 "var notifier2 = Object.getNotifier(obj2);"
881 "notifier2.performChange.call("
882 "notifier, 'foo', function(){})");
883 }
884
885 CcTest::isolate()->ContextDisposedNotification();
886 CheckSurvivingGlobalObjectsCount(0);
887 }
888
889
890 static void ObserverCallback(const FunctionCallbackInfo<Value>& args) {
891 *static_cast<int*>(Local<External>::Cast(args.Data())->Value()) =
892 Local<Array>::Cast(args[0])->Length();
893 }
894
895
896 TEST(ObjectObserveCallsCppFunction) {
897 i::FLAG_harmony_object_observe = true;
898 Isolate* isolate = CcTest::isolate();
899 HandleScope scope(isolate);
900 LocalContext context(isolate);
901 int numRecordsSent = 0;
902 Local<Function> observer =
903 Function::New(CcTest::isolate()->GetCurrentContext(), ObserverCallback,
904 External::New(isolate, &numRecordsSent))
905 .ToLocalChecked();
906 context->Global()
907 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("observer"),
908 observer)
909 .FromJust();
910 CompileRun(
911 "var obj = {};"
912 "Object.observe(obj, observer);"
913 "obj.foo = 1;"
914 "obj.bar = 2;");
915 CHECK_EQ(2, numRecordsSent);
916 }
917
918
919 TEST(ObjectObserveCallsFunctionTemplateInstance) {
920 i::FLAG_harmony_object_observe = true;
921 Isolate* isolate = CcTest::isolate();
922 HandleScope scope(isolate);
923 LocalContext context(isolate);
924 int numRecordsSent = 0;
925 Local<FunctionTemplate> tmpl = FunctionTemplate::New(
926 isolate, ObserverCallback, External::New(isolate, &numRecordsSent));
927 Local<Function> function =
928 tmpl->GetFunction(v8::Isolate::GetCurrent()->GetCurrentContext())
929 .ToLocalChecked();
930 context->Global()
931 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("observer"),
932 function)
933 .FromJust();
934 CompileRun(
935 "var obj = {};"
936 "Object.observe(obj, observer);"
937 "obj.foo = 1;"
938 "obj.bar = 2;");
939 CHECK_EQ(2, numRecordsSent);
940 }
941
942
943 static void AccessorGetter(Local<Name> property,
944 const PropertyCallbackInfo<Value>& info) {
945 info.GetReturnValue().Set(Integer::New(info.GetIsolate(), 42));
946 }
947
948
949 static void AccessorSetter(Local<Name> property, Local<Value> value,
950 const PropertyCallbackInfo<void>& info) {
951 info.GetReturnValue().SetUndefined();
952 }
953
954
955 TEST(APIAccessorsShouldNotNotify) {
956 i::FLAG_harmony_object_observe = true;
957 Isolate* isolate = CcTest::isolate();
958 HandleScope handle_scope(isolate);
959 LocalContext context(isolate);
960 Local<Object> object = Object::New(isolate);
961 object->SetAccessor(v8::Isolate::GetCurrent()->GetCurrentContext(),
962 v8_str("accessor"), &AccessorGetter, &AccessorSetter)
963 .FromJust();
964 context->Global()
965 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("obj"),
966 object)
967 .FromJust();
968 CompileRun(
969 "var records = null;"
970 "Object.observe(obj, function(r) { records = r });"
971 "obj.accessor = 43;");
972 CHECK(CompileRun("records")->IsNull());
973 CompileRun("Object.defineProperty(obj, 'accessor', { value: 44 });");
974 CHECK(CompileRun("records")->IsNull());
975 }
976
977
978 namespace {
979
980 int* global_use_counts = NULL;
981
982 void MockUseCounterCallback(v8::Isolate* isolate,
983 v8::Isolate::UseCounterFeature feature) {
984 ++global_use_counts[feature];
985 }
986 }
987
988
989 TEST(UseCountObjectObserve) {
990 i::FLAG_harmony_object_observe = true;
991 i::Isolate* isolate = CcTest::i_isolate();
992 i::HandleScope scope(isolate);
993 LocalContext env;
994 int use_counts[v8::Isolate::kUseCounterFeatureCount] = {};
995 global_use_counts = use_counts;
996 CcTest::isolate()->SetUseCounterCallback(MockUseCounterCallback);
997 CompileRun(
998 "var obj = {};"
999 "Object.observe(obj, function(){})");
1000 CHECK_EQ(1, use_counts[v8::Isolate::kObjectObserve]);
1001 CompileRun(
1002 "var obj2 = {};"
1003 "Object.observe(obj2, function(){})");
1004 // Only counts the first use of observe in a given context.
1005 CHECK_EQ(1, use_counts[v8::Isolate::kObjectObserve]);
1006 {
1007 LocalContext env2;
1008 CompileRun(
1009 "var obj = {};"
1010 "Object.observe(obj, function(){})");
1011 }
1012 // Counts different contexts separately.
1013 CHECK_EQ(2, use_counts[v8::Isolate::kObjectObserve]);
1014 }
1015
1016
1017 TEST(UseCountObjectGetNotifier) {
1018 i::FLAG_harmony_object_observe = true;
1019 i::Isolate* isolate = CcTest::i_isolate();
1020 i::HandleScope scope(isolate);
1021 LocalContext env;
1022 int use_counts[v8::Isolate::kUseCounterFeatureCount] = {};
1023 global_use_counts = use_counts;
1024 CcTest::isolate()->SetUseCounterCallback(MockUseCounterCallback);
1025 CompileRun("var obj = {}");
1026 CompileRun("Object.getNotifier(obj)");
1027 CHECK_EQ(1, use_counts[v8::Isolate::kObjectObserve]);
1028 }
1029
1030 static bool NamedAccessCheckAlwaysAllow(Local<v8::Context> accessing_context,
1031 Local<v8::Object> accessed_object,
1032 Local<v8::Value> data) {
1033 return true;
1034 }
1035
1036
1037 TEST(DisallowObserveAccessCheckedObject) {
1038 i::FLAG_harmony_object_observe = true;
1039 v8::Isolate* isolate = CcTest::isolate();
1040 v8::HandleScope scope(isolate);
1041 LocalContext env;
1042 v8::Local<v8::ObjectTemplate> object_template =
1043 v8::ObjectTemplate::New(isolate);
1044 object_template->SetAccessCheckCallback(NamedAccessCheckAlwaysAllow);
1045 Local<Object> new_instance =
1046 object_template->NewInstance(
1047 v8::Isolate::GetCurrent()->GetCurrentContext())
1048 .ToLocalChecked();
1049 env->Global()
1050 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("obj"),
1051 new_instance)
1052 .FromJust();
1053 v8::TryCatch try_catch(isolate);
1054 CompileRun("Object.observe(obj, function(){})");
1055 CHECK(try_catch.HasCaught());
1056 }
1057
1058
1059 TEST(DisallowGetNotifierAccessCheckedObject) {
1060 i::FLAG_harmony_object_observe = true;
1061 v8::Isolate* isolate = CcTest::isolate();
1062 v8::HandleScope scope(isolate);
1063 LocalContext env;
1064 v8::Local<v8::ObjectTemplate> object_template =
1065 v8::ObjectTemplate::New(isolate);
1066 object_template->SetAccessCheckCallback(NamedAccessCheckAlwaysAllow);
1067 Local<Object> new_instance =
1068 object_template->NewInstance(
1069 v8::Isolate::GetCurrent()->GetCurrentContext())
1070 .ToLocalChecked();
1071 env->Global()
1072 ->Set(v8::Isolate::GetCurrent()->GetCurrentContext(), v8_str("obj"),
1073 new_instance)
1074 .FromJust();
1075 v8::TryCatch try_catch(isolate);
1076 CompileRun("Object.getNotifier(obj)");
1077 CHECK(try_catch.HasCaught());
1078 }
OLDNEW
« no previous file with comments | « test/cctest/test-microtask-delivery.cc ('k') | test/mjsunit/array-push7.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698