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