OLD | NEW |
| (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 } | |
OLD | NEW |