1 // Copyright 2011 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 <limits.h> 29 30 #include "v8.h" 31 32 #include "api.h" 33 #include "compilation-cache.h" 34 #include "execution.h" 35 #include "snapshot.h" 36 #include "platform.h" 37 #include "utils.h" 38 #include "cctest.h" 39 #include "parser.h" 40 #include "unicode-inl.h" 41 42 static const bool kLogThreading = true; 43 44 static bool IsNaN(double x) { 45 #ifdef WIN32 46 return _isnan(x); 47 #else 48 return isnan(x); 49 #endif 50 } 51 52 using ::v8::AccessorInfo; 53 using ::v8::Arguments; 54 using ::v8::Context; 55 using ::v8::Extension; 56 using ::v8::Function; 57 using ::v8::FunctionTemplate; 58 using ::v8::Handle; 59 using ::v8::HandleScope; 60 using ::v8::Local; 61 using ::v8::Message; 62 using ::v8::MessageCallback; 63 using ::v8::Object; 64 using ::v8::ObjectTemplate; 65 using ::v8::Persistent; 66 using ::v8::Script; 67 using ::v8::StackTrace; 68 using ::v8::String; 69 using ::v8::TryCatch; 70 using ::v8::Undefined; 71 using ::v8::V8; 72 using ::v8::Value; 73 74 namespace i = ::i; 75 76 77 static void ExpectString(const char* code, const char* expected) { 78 Local<Value> result = CompileRun(code); 79 CHECK(result->IsString()); 80 String::AsciiValue ascii(result); 81 CHECK_EQ(expected, *ascii); 82 } 83 84 85 static void ExpectBoolean(const char* code, bool expected) { 86 Local<Value> result = CompileRun(code); 87 CHECK(result->IsBoolean()); 88 CHECK_EQ(expected, result->BooleanValue()); 89 } 90 91 92 static void ExpectTrue(const char* code) { 93 ExpectBoolean(code, true); 94 } 95 96 97 static void ExpectFalse(const char* code) { 98 ExpectBoolean(code, false); 99 } 100 101 102 static void ExpectObject(const char* code, Local<Value> expected) { 103 Local<Value> result = CompileRun(code); 104 CHECK(result->Equals(expected)); 105 } 106 107 108 static void ExpectUndefined(const char* code) { 109 Local<Value> result = CompileRun(code); 110 CHECK(result->IsUndefined()); 111 } 112 113 114 static int signature_callback_count; 115 static v8::Handle<Value> IncrementingSignatureCallback( 116 const v8::Arguments& args) { 117 ApiTestFuzzer::Fuzz(); 118 signature_callback_count++; 119 v8::Handle<v8::Array> result = v8::Array::New(args.Length()); 120 for (int i = 0; i < args.Length(); i++) 121 result->Set(v8::Integer::New(i), args[i]); 122 return result; 123 } 124 125 126 static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) { 127 ApiTestFuzzer::Fuzz(); 128 v8::Handle<v8::Array> result = v8::Array::New(args.Length()); 129 for (int i = 0; i < args.Length(); i++) { 130 result->Set(v8::Integer::New(i), args[i]); 131 } 132 return result; 133 } 134 135 136 THREADED_TEST(Handles) { 137 v8::HandleScope scope; 138 Local<Context> local_env; 139 { 140 LocalContext env; 141 local_env = env.local(); 142 } 143 144 // Local context should still be live. 145 CHECK(!local_env.IsEmpty()); 146 local_env->Enter(); 147 148 v8::Handle<v8::Primitive> undef = v8::Undefined(); 149 CHECK(!undef.IsEmpty()); 150 CHECK(undef->IsUndefined()); 151 152 const char* c_source = "1 + 2 + 3"; 153 Local<String> source = String::New(c_source); 154 Local<Script> script = Script::Compile(source); 155 CHECK_EQ(6, script->Run()->Int32Value()); 156 157 local_env->Exit(); 158 } 159 160 161 THREADED_TEST(ReceiverSignature) { 162 v8::HandleScope scope; 163 LocalContext env; 164 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); 165 v8::Handle<v8::Signature> sig = v8::Signature::New(fun); 166 fun->PrototypeTemplate()->Set( 167 v8_str("m"), 168 v8::FunctionTemplate::New(IncrementingSignatureCallback, 169 v8::Handle<Value>(), 170 sig)); 171 env->Global()->Set(v8_str("Fun"), fun->GetFunction()); 172 signature_callback_count = 0; 173 CompileRun( 174 "var o = new Fun();" 175 "o.m();"); 176 CHECK_EQ(1, signature_callback_count); 177 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New(); 178 sub_fun->Inherit(fun); 179 env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction()); 180 CompileRun( 181 "var o = new SubFun();" 182 "o.m();"); 183 CHECK_EQ(2, signature_callback_count); 184 185 v8::TryCatch try_catch; 186 CompileRun( 187 "var o = { };" 188 "o.m = Fun.prototype.m;" 189 "o.m();"); 190 CHECK_EQ(2, signature_callback_count); 191 CHECK(try_catch.HasCaught()); 192 try_catch.Reset(); 193 v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New(); 194 sub_fun->Inherit(fun); 195 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction()); 196 CompileRun( 197 "var o = new UnrelFun();" 198 "o.m = Fun.prototype.m;" 199 "o.m();"); 200 CHECK_EQ(2, signature_callback_count); 201 CHECK(try_catch.HasCaught()); 202 } 203 204 205 206 207 THREADED_TEST(ArgumentSignature) { 208 v8::HandleScope scope; 209 LocalContext env; 210 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New(); 211 cons->SetClassName(v8_str("Cons")); 212 v8::Handle<v8::Signature> sig = 213 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons); 214 v8::Handle<v8::FunctionTemplate> fun = 215 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig); 216 env->Global()->Set(v8_str("Cons"), cons->GetFunction()); 217 env->Global()->Set(v8_str("Fun1"), fun->GetFunction()); 218 219 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';"); 220 CHECK(value1->IsTrue()); 221 222 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';"); 223 CHECK(value2->IsTrue()); 224 225 v8::Handle<Value> value3 = CompileRun("Fun1() == '';"); 226 CHECK(value3->IsTrue()); 227 228 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New(); 229 cons1->SetClassName(v8_str("Cons1")); 230 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New(); 231 cons2->SetClassName(v8_str("Cons2")); 232 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New(); 233 cons3->SetClassName(v8_str("Cons3")); 234 235 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 }; 236 v8::Handle<v8::Signature> wsig = 237 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args); 238 v8::Handle<v8::FunctionTemplate> fun2 = 239 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig); 240 241 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction()); 242 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction()); 243 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction()); 244 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction()); 245 v8::Handle<Value> value4 = CompileRun( 246 "Fun2(new Cons1(), new Cons2(), new Cons3()) ==" 247 "'[object Cons1],[object Cons2],[object Cons3]'"); 248 CHECK(value4->IsTrue()); 249 250 v8::Handle<Value> value5 = CompileRun( 251 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'"); 252 CHECK(value5->IsTrue()); 253 254 v8::Handle<Value> value6 = CompileRun( 255 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'"); 256 CHECK(value6->IsTrue()); 257 258 v8::Handle<Value> value7 = CompileRun( 259 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == " 260 "'[object Cons1],[object Cons2],[object Cons3],d';"); 261 CHECK(value7->IsTrue()); 262 263 v8::Handle<Value> value8 = CompileRun( 264 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'"); 265 CHECK(value8->IsTrue()); 266 } 267 268 269 THREADED_TEST(HulIgennem) { 270 v8::HandleScope scope; 271 LocalContext env; 272 v8::Handle<v8::Primitive> undef = v8::Undefined(); 273 Local<String> undef_str = undef->ToString(); 274 char* value = i::NewArray<char>(undef_str->Length() + 1); 275 undef_str->WriteAscii(value); 276 CHECK_EQ(0, strcmp(value, "undefined")); 277 i::DeleteArray(value); 278 } 279 280 281 THREADED_TEST(Access) { 282 v8::HandleScope scope; 283 LocalContext env; 284 Local<v8::Object> obj = v8::Object::New(); 285 Local<Value> foo_before = obj->Get(v8_str("foo")); 286 CHECK(foo_before->IsUndefined()); 287 Local<String> bar_str = v8_str("bar"); 288 obj->Set(v8_str("foo"), bar_str); 289 Local<Value> foo_after = obj->Get(v8_str("foo")); 290 CHECK(!foo_after->IsUndefined()); 291 CHECK(foo_after->IsString()); 292 CHECK_EQ(bar_str, foo_after); 293 } 294 295 296 THREADED_TEST(AccessElement) { 297 v8::HandleScope scope; 298 LocalContext env; 299 Local<v8::Object> obj = v8::Object::New(); 300 Local<Value> before = obj->Get(1); 301 CHECK(before->IsUndefined()); 302 Local<String> bar_str = v8_str("bar"); 303 obj->Set(1, bar_str); 304 Local<Value> after = obj->Get(1); 305 CHECK(!after->IsUndefined()); 306 CHECK(after->IsString()); 307 CHECK_EQ(bar_str, after); 308 309 Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>(); 310 CHECK_EQ(v8_str("a"), value->Get(0)); 311 CHECK_EQ(v8_str("b"), value->Get(1)); 312 } 313 314 315 THREADED_TEST(Script) { 316 v8::HandleScope scope; 317 LocalContext env; 318 const char* c_source = "1 + 2 + 3"; 319 Local<String> source = String::New(c_source); 320 Local<Script> script = Script::Compile(source); 321 CHECK_EQ(6, script->Run()->Int32Value()); 322 } 323 324 325 static uint16_t* AsciiToTwoByteString(const char* source) { 326 int array_length = i::StrLength(source) + 1; 327 uint16_t* converted = i::NewArray<uint16_t>(array_length); 328 for (int i = 0; i < array_length; i++) converted[i] = source[i]; 329 return converted; 330 } 331 332 333 class TestResource: public String::ExternalStringResource { 334 public: 335 static int dispose_count; 336 337 explicit TestResource(uint16_t* data) 338 : data_(data), length_(0) { 339 while (data[length_]) ++length_; 340 } 341 342 ~TestResource() { 343 i::DeleteArray(data_); 344 ++dispose_count; 345 } 346 347 const uint16_t* data() const { 348 return data_; 349 } 350 351 size_t length() const { 352 return length_; 353 } 354 private: 355 uint16_t* data_; 356 size_t length_; 357 }; 358 359 360 int TestResource::dispose_count = 0; 361 362 363 class TestAsciiResource: public String::ExternalAsciiStringResource { 364 public: 365 static int dispose_count; 366 367 explicit TestAsciiResource(const char* data) 368 : data_(data), 369 length_(strlen(data)) { } 370 371 ~TestAsciiResource() { 372 i::DeleteArray(data_); 373 ++dispose_count; 374 } 375 376 const char* data() const { 377 return data_; 378 } 379 380 size_t length() const { 381 return length_; 382 } 383 private: 384 const char* data_; 385 size_t length_; 386 }; 387 388 389 int TestAsciiResource::dispose_count = 0; 390 391 392 THREADED_TEST(ScriptUsingStringResource) { 393 TestResource::dispose_count = 0; 394 const char* c_source = "1 + 2 * 3"; 395 uint16_t* two_byte_source = AsciiToTwoByteString(c_source); 396 { 397 v8::HandleScope scope; 398 LocalContext env; 399 TestResource* resource = new TestResource(two_byte_source); 400 Local<String> source = String::NewExternal(resource); 401 Local<Script> script = Script::Compile(source); 402 Local<Value> value = script->Run(); 403 CHECK(value->IsNumber()); 404 CHECK_EQ(7, value->Int32Value()); 405 CHECK(source->IsExternal()); 406 CHECK_EQ(resource, 407 static_cast<TestResource*>(source->GetExternalStringResource())); 408 HEAP->CollectAllGarbage(false); 409 CHECK_EQ(0, TestResource::dispose_count); 410 } 411 v8::internal::Isolate::Current()->compilation_cache()->Clear(); 412 HEAP->CollectAllGarbage(false); 413 CHECK_EQ(1, TestResource::dispose_count); 414 } 415 416 417 THREADED_TEST(ScriptUsingAsciiStringResource) { 418 TestAsciiResource::dispose_count = 0; 419 const char* c_source = "1 + 2 * 3"; 420 { 421 v8::HandleScope scope; 422 LocalContext env; 423 Local<String> source = 424 String::NewExternal(new TestAsciiResource(i::StrDup(c_source))); 425 Local<Script> script = Script::Compile(source); 426 Local<Value> value = script->Run(); 427 CHECK(value->IsNumber()); 428 CHECK_EQ(7, value->Int32Value()); 429 HEAP->CollectAllGarbage(false); 430 CHECK_EQ(0, TestAsciiResource::dispose_count); 431 } 432 i::Isolate::Current()->compilation_cache()->Clear(); 433 HEAP->CollectAllGarbage(false); 434 CHECK_EQ(1, TestAsciiResource::dispose_count); 435 } 436 437 438 THREADED_TEST(ScriptMakingExternalString) { 439 TestResource::dispose_count = 0; 440 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3"); 441 { 442 v8::HandleScope scope; 443 LocalContext env; 444 Local<String> source = String::New(two_byte_source); 445 // Trigger GCs so that the newly allocated string moves to old gen. 446 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now 447 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now 448 bool success = source->MakeExternal(new TestResource(two_byte_source)); 449 CHECK(success); 450 Local<Script> script = Script::Compile(source); 451 Local<Value> value = script->Run(); 452 CHECK(value->IsNumber()); 453 CHECK_EQ(7, value->Int32Value()); 454 HEAP->CollectAllGarbage(false); 455 CHECK_EQ(0, TestResource::dispose_count); 456 } 457 i::Isolate::Current()->compilation_cache()->Clear(); 458 HEAP->CollectAllGarbage(false); 459 CHECK_EQ(1, TestResource::dispose_count); 460 } 461 462 463 THREADED_TEST(ScriptMakingExternalAsciiString) { 464 TestAsciiResource::dispose_count = 0; 465 const char* c_source = "1 + 2 * 3"; 466 { 467 v8::HandleScope scope; 468 LocalContext env; 469 Local<String> source = v8_str(c_source); 470 // Trigger GCs so that the newly allocated string moves to old gen. 471 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now 472 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now 473 bool success = source->MakeExternal( 474 new TestAsciiResource(i::StrDup(c_source))); 475 CHECK(success); 476 Local<Script> script = Script::Compile(source); 477 Local<Value> value = script->Run(); 478 CHECK(value->IsNumber()); 479 CHECK_EQ(7, value->Int32Value()); 480 HEAP->CollectAllGarbage(false); 481 CHECK_EQ(0, TestAsciiResource::dispose_count); 482 } 483 i::Isolate::Current()->compilation_cache()->Clear(); 484 HEAP->CollectAllGarbage(false); 485 CHECK_EQ(1, TestAsciiResource::dispose_count); 486 } 487 488 489 TEST(MakingExternalStringConditions) { 490 v8::HandleScope scope; 491 LocalContext env; 492 493 // Free some space in the new space so that we can check freshness. 494 HEAP->CollectGarbage(i::NEW_SPACE); 495 HEAP->CollectGarbage(i::NEW_SPACE); 496 497 uint16_t* two_byte_string = AsciiToTwoByteString("small"); 498 Local<String> small_string = String::New(two_byte_string); 499 i::DeleteArray(two_byte_string); 500 501 // We should refuse to externalize newly created small string. 502 CHECK(!small_string->CanMakeExternal()); 503 // Trigger GCs so that the newly allocated string moves to old gen. 504 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now 505 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now 506 // Old space strings should be accepted. 507 CHECK(small_string->CanMakeExternal()); 508 509 two_byte_string = AsciiToTwoByteString("small 2"); 510 small_string = String::New(two_byte_string); 511 i::DeleteArray(two_byte_string); 512 513 // We should refuse externalizing newly created small string. 514 CHECK(!small_string->CanMakeExternal()); 515 for (int i = 0; i < 100; i++) { 516 String::Value value(small_string); 517 } 518 // Frequently used strings should be accepted. 519 CHECK(small_string->CanMakeExternal()); 520 521 const int buf_size = 10 * 1024; 522 char* buf = i::NewArray<char>(buf_size); 523 memset(buf, 'a', buf_size); 524 buf[buf_size - 1] = '\0'; 525 526 two_byte_string = AsciiToTwoByteString(buf); 527 Local<String> large_string = String::New(two_byte_string); 528 i::DeleteArray(buf); 529 i::DeleteArray(two_byte_string); 530 // Large strings should be immediately accepted. 531 CHECK(large_string->CanMakeExternal()); 532 } 533 534 535 TEST(MakingExternalAsciiStringConditions) { 536 v8::HandleScope scope; 537 LocalContext env; 538 539 // Free some space in the new space so that we can check freshness. 540 HEAP->CollectGarbage(i::NEW_SPACE); 541 HEAP->CollectGarbage(i::NEW_SPACE); 542 543 Local<String> small_string = String::New("small"); 544 // We should refuse to externalize newly created small string. 545 CHECK(!small_string->CanMakeExternal()); 546 // Trigger GCs so that the newly allocated string moves to old gen. 547 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now 548 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now 549 // Old space strings should be accepted. 550 CHECK(small_string->CanMakeExternal()); 551 552 small_string = String::New("small 2"); 553 // We should refuse externalizing newly created small string. 554 CHECK(!small_string->CanMakeExternal()); 555 for (int i = 0; i < 100; i++) { 556 String::Value value(small_string); 557 } 558 // Frequently used strings should be accepted. 559 CHECK(small_string->CanMakeExternal()); 560 561 const int buf_size = 10 * 1024; 562 char* buf = i::NewArray<char>(buf_size); 563 memset(buf, 'a', buf_size); 564 buf[buf_size - 1] = '\0'; 565 Local<String> large_string = String::New(buf); 566 i::DeleteArray(buf); 567 // Large strings should be immediately accepted. 568 CHECK(large_string->CanMakeExternal()); 569 } 570 571 572 THREADED_TEST(UsingExternalString) { 573 { 574 v8::HandleScope scope; 575 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 576 Local<String> string = 577 String::NewExternal(new TestResource(two_byte_string)); 578 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 579 // Trigger GCs so that the newly allocated string moves to old gen. 580 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now 581 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now 582 i::Handle<i::String> isymbol = FACTORY->SymbolFromString(istring); 583 CHECK(isymbol->IsSymbol()); 584 } 585 HEAP->CollectAllGarbage(false); 586 HEAP->CollectAllGarbage(false); 587 } 588 589 590 THREADED_TEST(UsingExternalAsciiString) { 591 { 592 v8::HandleScope scope; 593 const char* one_byte_string = "test string"; 594 Local<String> string = String::NewExternal( 595 new TestAsciiResource(i::StrDup(one_byte_string))); 596 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 597 // Trigger GCs so that the newly allocated string moves to old gen. 598 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now 599 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now 600 i::Handle<i::String> isymbol = FACTORY->SymbolFromString(istring); 601 CHECK(isymbol->IsSymbol()); 602 } 603 HEAP->CollectAllGarbage(false); 604 HEAP->CollectAllGarbage(false); 605 } 606 607 608 THREADED_TEST(ScavengeExternalString) { 609 TestResource::dispose_count = 0; 610 bool in_new_space = false; 611 { 612 v8::HandleScope scope; 613 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 614 Local<String> string = 615 String::NewExternal(new TestResource(two_byte_string)); 616 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 617 HEAP->CollectGarbage(i::NEW_SPACE); 618 in_new_space = HEAP->InNewSpace(*istring); 619 CHECK(in_new_space || HEAP->old_data_space()->Contains(*istring)); 620 CHECK_EQ(0, TestResource::dispose_count); 621 } 622 HEAP->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); 623 CHECK_EQ(1, TestResource::dispose_count); 624 } 625 626 627 THREADED_TEST(ScavengeExternalAsciiString) { 628 TestAsciiResource::dispose_count = 0; 629 bool in_new_space = false; 630 { 631 v8::HandleScope scope; 632 const char* one_byte_string = "test string"; 633 Local<String> string = String::NewExternal( 634 new TestAsciiResource(i::StrDup(one_byte_string))); 635 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 636 HEAP->CollectGarbage(i::NEW_SPACE); 637 in_new_space = HEAP->InNewSpace(*istring); 638 CHECK(in_new_space || HEAP->old_data_space()->Contains(*istring)); 639 CHECK_EQ(0, TestAsciiResource::dispose_count); 640 } 641 HEAP->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); 642 CHECK_EQ(1, TestAsciiResource::dispose_count); 643 } 644 645 646 class TestAsciiResourceWithDisposeControl: public TestAsciiResource { 647 public: 648 static int dispose_calls; 649 650 TestAsciiResourceWithDisposeControl(const char* data, bool dispose) 651 : TestAsciiResource(data), 652 dispose_(dispose) { } 653 654 void Dispose() { 655 ++dispose_calls; 656 if (dispose_) delete this; 657 } 658 private: 659 bool dispose_; 660 }; 661 662 663 int TestAsciiResourceWithDisposeControl::dispose_calls = 0; 664 665 666 TEST(ExternalStringWithDisposeHandling) { 667 const char* c_source = "1 + 2 * 3"; 668 669 // Use a stack allocated external string resource allocated object. 670 TestAsciiResource::dispose_count = 0; 671 TestAsciiResourceWithDisposeControl::dispose_calls = 0; 672 TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false); 673 { 674 v8::HandleScope scope; 675 LocalContext env; 676 Local<String> source = String::NewExternal(&res_stack); 677 Local<Script> script = Script::Compile(source); 678 Local<Value> value = script->Run(); 679 CHECK(value->IsNumber()); 680 CHECK_EQ(7, value->Int32Value()); 681 HEAP->CollectAllGarbage(false); 682 CHECK_EQ(0, TestAsciiResource::dispose_count); 683 } 684 i::Isolate::Current()->compilation_cache()->Clear(); 685 HEAP->CollectAllGarbage(false); 686 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); 687 CHECK_EQ(0, TestAsciiResource::dispose_count); 688 689 // Use a heap allocated external string resource allocated object. 690 TestAsciiResource::dispose_count = 0; 691 TestAsciiResourceWithDisposeControl::dispose_calls = 0; 692 TestAsciiResource* res_heap = 693 new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true); 694 { 695 v8::HandleScope scope; 696 LocalContext env; 697 Local<String> source = String::NewExternal(res_heap); 698 Local<Script> script = Script::Compile(source); 699 Local<Value> value = script->Run(); 700 CHECK(value->IsNumber()); 701 CHECK_EQ(7, value->Int32Value()); 702 HEAP->CollectAllGarbage(false); 703 CHECK_EQ(0, TestAsciiResource::dispose_count); 704 } 705 i::Isolate::Current()->compilation_cache()->Clear(); 706 HEAP->CollectAllGarbage(false); 707 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); 708 CHECK_EQ(1, TestAsciiResource::dispose_count); 709 } 710 711 712 THREADED_TEST(StringConcat) { 713 { 714 v8::HandleScope scope; 715 LocalContext env; 716 const char* one_byte_string_1 = "function a_times_t"; 717 const char* two_byte_string_1 = "wo_plus_b(a, b) {return "; 718 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + "; 719 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + "; 720 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 721 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 722 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);"; 723 Local<String> left = v8_str(one_byte_string_1); 724 725 uint16_t* two_byte_source = AsciiToTwoByteString(two_byte_string_1); 726 Local<String> right = String::New(two_byte_source); 727 i::DeleteArray(two_byte_source); 728 729 Local<String> source = String::Concat(left, right); 730 right = String::NewExternal( 731 new TestAsciiResource(i::StrDup(one_byte_extern_1))); 732 source = String::Concat(source, right); 733 right = String::NewExternal( 734 new TestResource(AsciiToTwoByteString(two_byte_extern_1))); 735 source = String::Concat(source, right); 736 right = v8_str(one_byte_string_2); 737 source = String::Concat(source, right); 738 739 two_byte_source = AsciiToTwoByteString(two_byte_string_2); 740 right = String::New(two_byte_source); 741 i::DeleteArray(two_byte_source); 742 743 source = String::Concat(source, right); 744 right = String::NewExternal( 745 new TestResource(AsciiToTwoByteString(two_byte_extern_2))); 746 source = String::Concat(source, right); 747 Local<Script> script = Script::Compile(source); 748 Local<Value> value = script->Run(); 749 CHECK(value->IsNumber()); 750 CHECK_EQ(68, value->Int32Value()); 751 } 752 i::Isolate::Current()->compilation_cache()->Clear(); 753 HEAP->CollectAllGarbage(false); 754 HEAP->CollectAllGarbage(false); 755 } 756 757 758 THREADED_TEST(GlobalProperties) { 759 v8::HandleScope scope; 760 LocalContext env; 761 v8::Handle<v8::Object> global = env->Global(); 762 global->Set(v8_str("pi"), v8_num(3.1415926)); 763 Local<Value> pi = global->Get(v8_str("pi")); 764 CHECK_EQ(3.1415926, pi->NumberValue()); 765 } 766 767 768 static v8::Handle<Value> handle_call(const v8::Arguments& args) { 769 ApiTestFuzzer::Fuzz(); 770 return v8_num(102); 771 } 772 773 774 static v8::Handle<Value> construct_call(const v8::Arguments& args) { 775 ApiTestFuzzer::Fuzz(); 776 args.This()->Set(v8_str("x"), v8_num(1)); 777 args.This()->Set(v8_str("y"), v8_num(2)); 778 return args.This(); 779 } 780 781 static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) { 782 ApiTestFuzzer::Fuzz(); 783 return v8_num(239); 784 } 785 786 787 THREADED_TEST(FunctionTemplate) { 788 v8::HandleScope scope; 789 LocalContext env; 790 { 791 Local<v8::FunctionTemplate> fun_templ = 792 v8::FunctionTemplate::New(handle_call); 793 Local<Function> fun = fun_templ->GetFunction(); 794 env->Global()->Set(v8_str("obj"), fun); 795 Local<Script> script = v8_compile("obj()"); 796 CHECK_EQ(102, script->Run()->Int32Value()); 797 } 798 // Use SetCallHandler to initialize a function template, should work like the 799 // previous one. 800 { 801 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 802 fun_templ->SetCallHandler(handle_call); 803 Local<Function> fun = fun_templ->GetFunction(); 804 env->Global()->Set(v8_str("obj"), fun); 805 Local<Script> script = v8_compile("obj()"); 806 CHECK_EQ(102, script->Run()->Int32Value()); 807 } 808 // Test constructor calls. 809 { 810 Local<v8::FunctionTemplate> fun_templ = 811 v8::FunctionTemplate::New(construct_call); 812 fun_templ->SetClassName(v8_str("funky")); 813 fun_templ->InstanceTemplate()->SetAccessor(v8_str("m"), Return239); 814 Local<Function> fun = fun_templ->GetFunction(); 815 env->Global()->Set(v8_str("obj"), fun); 816 Local<Script> script = v8_compile("var s = new obj(); s.x"); 817 CHECK_EQ(1, script->Run()->Int32Value()); 818 819 Local<Value> result = v8_compile("(new obj()).toString()")->Run(); 820 CHECK_EQ(v8_str("[object funky]"), result); 821 822 result = v8_compile("(new obj()).m")->Run(); 823 CHECK_EQ(239, result->Int32Value()); 824 } 825 } 826 827 828 static void* expected_ptr; 829 static v8::Handle<v8::Value> callback(const v8::Arguments& args) { 830 void* ptr = v8::External::Unwrap(args.Data()); 831 CHECK_EQ(expected_ptr, ptr); 832 return v8::Boolean::New(true); 833 } 834 835 836 static void TestExternalPointerWrapping() { 837 v8::HandleScope scope; 838 LocalContext env; 839 840 v8::Handle<v8::Value> data = v8::External::Wrap(expected_ptr); 841 842 v8::Handle<v8::Object> obj = v8::Object::New(); 843 obj->Set(v8_str("func"), 844 v8::FunctionTemplate::New(callback, data)->GetFunction()); 845 env->Global()->Set(v8_str("obj"), obj); 846 847 CHECK(CompileRun( 848 "function foo() {\n" 849 " for (var i = 0; i < 13; i++) obj.func();\n" 850 "}\n" 851 "foo(), true")->BooleanValue()); 852 } 853 854 855 THREADED_TEST(ExternalWrap) { 856 // Check heap allocated object. 857 int* ptr = new int; 858 expected_ptr = ptr; 859 TestExternalPointerWrapping(); 860 delete ptr; 861 862 // Check stack allocated object. 863 int foo; 864 expected_ptr = &foo; 865 TestExternalPointerWrapping(); 866 867 // Check not aligned addresses. 868 const int n = 100; 869 char* s = new char[n]; 870 for (int i = 0; i < n; i++) { 871 expected_ptr = s + i; 872 TestExternalPointerWrapping(); 873 } 874 875 delete[] s; 876 877 // Check several invalid addresses. 878 expected_ptr = reinterpret_cast<void*>(1); 879 TestExternalPointerWrapping(); 880 881 expected_ptr = reinterpret_cast<void*>(0xdeadbeef); 882 TestExternalPointerWrapping(); 883 884 expected_ptr = reinterpret_cast<void*>(0xdeadbeef + 1); 885 TestExternalPointerWrapping(); 886 887 #if defined(V8_HOST_ARCH_X64) 888 // Check a value with a leading 1 bit in x64 Smi encoding. 889 expected_ptr = reinterpret_cast<void*>(0x400000000); 890 TestExternalPointerWrapping(); 891 892 expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef); 893 TestExternalPointerWrapping(); 894 895 expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef + 1); 896 TestExternalPointerWrapping(); 897 #endif 898 } 899 900 901 THREADED_TEST(FindInstanceInPrototypeChain) { 902 v8::HandleScope scope; 903 LocalContext env; 904 905 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New(); 906 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New(); 907 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New(); 908 derived->Inherit(base); 909 910 Local<v8::Function> base_function = base->GetFunction(); 911 Local<v8::Function> derived_function = derived->GetFunction(); 912 Local<v8::Function> other_function = other->GetFunction(); 913 914 Local<v8::Object> base_instance = base_function->NewInstance(); 915 Local<v8::Object> derived_instance = derived_function->NewInstance(); 916 Local<v8::Object> derived_instance2 = derived_function->NewInstance(); 917 Local<v8::Object> other_instance = other_function->NewInstance(); 918 derived_instance2->Set(v8_str("__proto__"), derived_instance); 919 other_instance->Set(v8_str("__proto__"), derived_instance2); 920 921 // base_instance is only an instance of base. 922 CHECK_EQ(base_instance, 923 base_instance->FindInstanceInPrototypeChain(base)); 924 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty()); 925 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 926 927 // derived_instance is an instance of base and derived. 928 CHECK_EQ(derived_instance, 929 derived_instance->FindInstanceInPrototypeChain(base)); 930 CHECK_EQ(derived_instance, 931 derived_instance->FindInstanceInPrototypeChain(derived)); 932 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 933 934 // other_instance is an instance of other and its immediate 935 // prototype derived_instance2 is an instance of base and derived. 936 // Note, derived_instance is an instance of base and derived too, 937 // but it comes after derived_instance2 in the prototype chain of 938 // other_instance. 939 CHECK_EQ(derived_instance2, 940 other_instance->FindInstanceInPrototypeChain(base)); 941 CHECK_EQ(derived_instance2, 942 other_instance->FindInstanceInPrototypeChain(derived)); 943 CHECK_EQ(other_instance, 944 other_instance->FindInstanceInPrototypeChain(other)); 945 } 946 947 948 THREADED_TEST(TinyInteger) { 949 v8::HandleScope scope; 950 LocalContext env; 951 int32_t value = 239; 952 Local<v8::Integer> value_obj = v8::Integer::New(value); 953 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 954 } 955 956 957 THREADED_TEST(BigSmiInteger) { 958 v8::HandleScope scope; 959 LocalContext env; 960 int32_t value = i::Smi::kMaxValue; 961 // We cannot add one to a Smi::kMaxValue without wrapping. 962 if (i::kSmiValueSize < 32) { 963 CHECK(i::Smi::IsValid(value)); 964 CHECK(!i::Smi::IsValid(value + 1)); 965 Local<v8::Integer> value_obj = v8::Integer::New(value); 966 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 967 } 968 } 969 970 971 THREADED_TEST(BigInteger) { 972 v8::HandleScope scope; 973 LocalContext env; 974 // We cannot add one to a Smi::kMaxValue without wrapping. 975 if (i::kSmiValueSize < 32) { 976 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1. 977 // The code will not be run in that case, due to the "if" guard. 978 int32_t value = 979 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1); 980 CHECK(value > i::Smi::kMaxValue); 981 CHECK(!i::Smi::IsValid(value)); 982 Local<v8::Integer> value_obj = v8::Integer::New(value); 983 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 984 } 985 } 986 987 988 THREADED_TEST(TinyUnsignedInteger) { 989 v8::HandleScope scope; 990 LocalContext env; 991 uint32_t value = 239; 992 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 993 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 994 } 995 996 997 THREADED_TEST(BigUnsignedSmiInteger) { 998 v8::HandleScope scope; 999 LocalContext env; 1000 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue); 1001 CHECK(i::Smi::IsValid(value)); 1002 CHECK(!i::Smi::IsValid(value + 1)); 1003 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 1004 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1005 } 1006 1007 1008 THREADED_TEST(BigUnsignedInteger) { 1009 v8::HandleScope scope; 1010 LocalContext env; 1011 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1; 1012 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue)); 1013 CHECK(!i::Smi::IsValid(value)); 1014 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 1015 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1016 } 1017 1018 1019 THREADED_TEST(OutOfSignedRangeUnsignedInteger) { 1020 v8::HandleScope scope; 1021 LocalContext env; 1022 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1; 1023 uint32_t value = INT32_MAX_AS_UINT + 1; 1024 CHECK(value > INT32_MAX_AS_UINT); // No overflow. 1025 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 1026 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1027 } 1028 1029 1030 THREADED_TEST(Number) { 1031 v8::HandleScope scope; 1032 LocalContext env; 1033 double PI = 3.1415926; 1034 Local<v8::Number> pi_obj = v8::Number::New(PI); 1035 CHECK_EQ(PI, pi_obj->NumberValue()); 1036 } 1037 1038 1039 THREADED_TEST(ToNumber) { 1040 v8::HandleScope scope; 1041 LocalContext env; 1042 Local<String> str = v8_str("3.1415926"); 1043 CHECK_EQ(3.1415926, str->NumberValue()); 1044 v8::Handle<v8::Boolean> t = v8::True(); 1045 CHECK_EQ(1.0, t->NumberValue()); 1046 v8::Handle<v8::Boolean> f = v8::False(); 1047 CHECK_EQ(0.0, f->NumberValue()); 1048 } 1049 1050 1051 THREADED_TEST(Date) { 1052 v8::HandleScope scope; 1053 LocalContext env; 1054 double PI = 3.1415926; 1055 Local<Value> date_obj = v8::Date::New(PI); 1056 CHECK_EQ(3.0, date_obj->NumberValue()); 1057 } 1058 1059 1060 THREADED_TEST(Boolean) { 1061 v8::HandleScope scope; 1062 LocalContext env; 1063 v8::Handle<v8::Boolean> t = v8::True(); 1064 CHECK(t->Value()); 1065 v8::Handle<v8::Boolean> f = v8::False(); 1066 CHECK(!f->Value()); 1067 v8::Handle<v8::Primitive> u = v8::Undefined(); 1068 CHECK(!u->BooleanValue()); 1069 v8::Handle<v8::Primitive> n = v8::Null(); 1070 CHECK(!n->BooleanValue()); 1071 v8::Handle<String> str1 = v8_str(""); 1072 CHECK(!str1->BooleanValue()); 1073 v8::Handle<String> str2 = v8_str("x"); 1074 CHECK(str2->BooleanValue()); 1075 CHECK(!v8::Number::New(0)->BooleanValue()); 1076 CHECK(v8::Number::New(-1)->BooleanValue()); 1077 CHECK(v8::Number::New(1)->BooleanValue()); 1078 CHECK(v8::Number::New(42)->BooleanValue()); 1079 CHECK(!v8_compile("NaN")->Run()->BooleanValue()); 1080 } 1081 1082 1083 static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) { 1084 ApiTestFuzzer::Fuzz(); 1085 return v8_num(13.4); 1086 } 1087 1088 1089 static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) { 1090 ApiTestFuzzer::Fuzz(); 1091 return v8_num(876); 1092 } 1093 1094 1095 THREADED_TEST(GlobalPrototype) { 1096 v8::HandleScope scope; 1097 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 1098 func_templ->PrototypeTemplate()->Set( 1099 "dummy", 1100 v8::FunctionTemplate::New(DummyCallHandler)); 1101 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate(); 1102 templ->Set("x", v8_num(200)); 1103 templ->SetAccessor(v8_str("m"), GetM); 1104 LocalContext env(0, templ); 1105 v8::Handle<v8::Object> obj = env->Global(); 1106 v8::Handle<Script> script = v8_compile("dummy()"); 1107 v8::Handle<Value> result = script->Run(); 1108 CHECK_EQ(13.4, result->NumberValue()); 1109 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value()); 1110 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value()); 1111 } 1112 1113 1114 THREADED_TEST(ObjectTemplate) { 1115 v8::HandleScope scope; 1116 Local<ObjectTemplate> templ1 = ObjectTemplate::New(); 1117 templ1->Set("x", v8_num(10)); 1118 templ1->Set("y", v8_num(13)); 1119 LocalContext env; 1120 Local<v8::Object> instance1 = templ1->NewInstance(); 1121 env->Global()->Set(v8_str("p"), instance1); 1122 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue()); 1123 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue()); 1124 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); 1125 fun->PrototypeTemplate()->Set("nirk", v8_num(123)); 1126 Local<ObjectTemplate> templ2 = fun->InstanceTemplate(); 1127 templ2->Set("a", v8_num(12)); 1128 templ2->Set("b", templ1); 1129 Local<v8::Object> instance2 = templ2->NewInstance(); 1130 env->Global()->Set(v8_str("q"), instance2); 1131 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue()); 1132 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue()); 1133 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue()); 1134 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue()); 1135 } 1136 1137 1138 static v8::Handle<Value> GetFlabby(const v8::Arguments& args) { 1139 ApiTestFuzzer::Fuzz(); 1140 return v8_num(17.2); 1141 } 1142 1143 1144 static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) { 1145 ApiTestFuzzer::Fuzz(); 1146 return v8_num(15.2); 1147 } 1148 1149 1150 THREADED_TEST(DescriptorInheritance) { 1151 v8::HandleScope scope; 1152 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New(); 1153 super->PrototypeTemplate()->Set("flabby", 1154 v8::FunctionTemplate::New(GetFlabby)); 1155 super->PrototypeTemplate()->Set("PI", v8_num(3.14)); 1156 1157 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd); 1158 1159 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New(); 1160 base1->Inherit(super); 1161 base1->PrototypeTemplate()->Set("v1", v8_num(20.1)); 1162 1163 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New(); 1164 base2->Inherit(super); 1165 base2->PrototypeTemplate()->Set("v2", v8_num(10.1)); 1166 1167 LocalContext env; 1168 1169 env->Global()->Set(v8_str("s"), super->GetFunction()); 1170 env->Global()->Set(v8_str("base1"), base1->GetFunction()); 1171 env->Global()->Set(v8_str("base2"), base2->GetFunction()); 1172 1173 // Checks right __proto__ chain. 1174 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue()); 1175 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue()); 1176 1177 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue()); 1178 1179 // Instance accessor should not be visible on function object or its prototype 1180 CHECK(CompileRun("s.knurd == undefined")->BooleanValue()); 1181 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue()); 1182 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue()); 1183 1184 env->Global()->Set(v8_str("obj"), 1185 base1->GetFunction()->NewInstance()); 1186 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue()); 1187 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue()); 1188 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue()); 1189 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue()); 1190 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue()); 1191 1192 env->Global()->Set(v8_str("obj2"), 1193 base2->GetFunction()->NewInstance()); 1194 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue()); 1195 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue()); 1196 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue()); 1197 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue()); 1198 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue()); 1199 1200 // base1 and base2 cannot cross reference to each's prototype 1201 CHECK(v8_compile("obj.v2")->Run()->IsUndefined()); 1202 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined()); 1203 } 1204 1205 1206 int echo_named_call_count; 1207 1208 1209 static v8::Handle<Value> EchoNamedProperty(Local<String> name, 1210 const AccessorInfo& info) { 1211 ApiTestFuzzer::Fuzz(); 1212 CHECK_EQ(v8_str("data"), info.Data()); 1213 echo_named_call_count++; 1214 return name; 1215 } 1216 1217 1218 THREADED_TEST(NamedPropertyHandlerGetter) { 1219 echo_named_call_count = 0; 1220 v8::HandleScope scope; 1221 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1222 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty, 1223 0, 0, 0, 0, 1224 v8_str("data")); 1225 LocalContext env; 1226 env->Global()->Set(v8_str("obj"), 1227 templ->GetFunction()->NewInstance()); 1228 CHECK_EQ(echo_named_call_count, 0); 1229 v8_compile("obj.x")->Run(); 1230 CHECK_EQ(echo_named_call_count, 1); 1231 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;"; 1232 v8::Handle<Value> str = CompileRun(code); 1233 String::AsciiValue value(str); 1234 CHECK_EQ(*value, "oddlepoddle"); 1235 // Check default behavior 1236 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10); 1237 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue()); 1238 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue()); 1239 } 1240 1241 1242 int echo_indexed_call_count = 0; 1243 1244 1245 static v8::Handle<Value> EchoIndexedProperty(uint32_t index, 1246 const AccessorInfo& info) { 1247 ApiTestFuzzer::Fuzz(); 1248 CHECK_EQ(v8_num(637), info.Data()); 1249 echo_indexed_call_count++; 1250 return v8_num(index); 1251 } 1252 1253 1254 THREADED_TEST(IndexedPropertyHandlerGetter) { 1255 v8::HandleScope scope; 1256 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1257 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty, 1258 0, 0, 0, 0, 1259 v8_num(637)); 1260 LocalContext env; 1261 env->Global()->Set(v8_str("obj"), 1262 templ->GetFunction()->NewInstance()); 1263 Local<Script> script = v8_compile("obj[900]"); 1264 CHECK_EQ(script->Run()->Int32Value(), 900); 1265 } 1266 1267 1268 v8::Handle<v8::Object> bottom; 1269 1270 static v8::Handle<Value> CheckThisIndexedPropertyHandler( 1271 uint32_t index, 1272 const AccessorInfo& info) { 1273 ApiTestFuzzer::Fuzz(); 1274 CHECK(info.This()->Equals(bottom)); 1275 return v8::Handle<Value>(); 1276 } 1277 1278 static v8::Handle<Value> CheckThisNamedPropertyHandler( 1279 Local<String> name, 1280 const AccessorInfo& info) { 1281 ApiTestFuzzer::Fuzz(); 1282 CHECK(info.This()->Equals(bottom)); 1283 return v8::Handle<Value>(); 1284 } 1285 1286 1287 v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index, 1288 Local<Value> value, 1289 const AccessorInfo& info) { 1290 ApiTestFuzzer::Fuzz(); 1291 CHECK(info.This()->Equals(bottom)); 1292 return v8::Handle<Value>(); 1293 } 1294 1295 1296 v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property, 1297 Local<Value> value, 1298 const AccessorInfo& info) { 1299 ApiTestFuzzer::Fuzz(); 1300 CHECK(info.This()->Equals(bottom)); 1301 return v8::Handle<Value>(); 1302 } 1303 1304 v8::Handle<v8::Integer> CheckThisIndexedPropertyQuery( 1305 uint32_t index, 1306 const AccessorInfo& info) { 1307 ApiTestFuzzer::Fuzz(); 1308 CHECK(info.This()->Equals(bottom)); 1309 return v8::Handle<v8::Integer>(); 1310 } 1311 1312 1313 v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property, 1314 const AccessorInfo& info) { 1315 ApiTestFuzzer::Fuzz(); 1316 CHECK(info.This()->Equals(bottom)); 1317 return v8::Handle<v8::Integer>(); 1318 } 1319 1320 1321 v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter( 1322 uint32_t index, 1323 const AccessorInfo& info) { 1324 ApiTestFuzzer::Fuzz(); 1325 CHECK(info.This()->Equals(bottom)); 1326 return v8::Handle<v8::Boolean>(); 1327 } 1328 1329 1330 v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter( 1331 Local<String> property, 1332 const AccessorInfo& info) { 1333 ApiTestFuzzer::Fuzz(); 1334 CHECK(info.This()->Equals(bottom)); 1335 return v8::Handle<v8::Boolean>(); 1336 } 1337 1338 1339 v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator( 1340 const AccessorInfo& info) { 1341 ApiTestFuzzer::Fuzz(); 1342 CHECK(info.This()->Equals(bottom)); 1343 return v8::Handle<v8::Array>(); 1344 } 1345 1346 1347 v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator( 1348 const AccessorInfo& info) { 1349 ApiTestFuzzer::Fuzz(); 1350 CHECK(info.This()->Equals(bottom)); 1351 return v8::Handle<v8::Array>(); 1352 } 1353 1354 1355 THREADED_TEST(PropertyHandlerInPrototype) { 1356 v8::HandleScope scope; 1357 LocalContext env; 1358 1359 // Set up a prototype chain with three interceptors. 1360 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1361 templ->InstanceTemplate()->SetIndexedPropertyHandler( 1362 CheckThisIndexedPropertyHandler, 1363 CheckThisIndexedPropertySetter, 1364 CheckThisIndexedPropertyQuery, 1365 CheckThisIndexedPropertyDeleter, 1366 CheckThisIndexedPropertyEnumerator); 1367 1368 templ->InstanceTemplate()->SetNamedPropertyHandler( 1369 CheckThisNamedPropertyHandler, 1370 CheckThisNamedPropertySetter, 1371 CheckThisNamedPropertyQuery, 1372 CheckThisNamedPropertyDeleter, 1373 CheckThisNamedPropertyEnumerator); 1374 1375 bottom = templ->GetFunction()->NewInstance(); 1376 Local<v8::Object> top = templ->GetFunction()->NewInstance(); 1377 Local<v8::Object> middle = templ->GetFunction()->NewInstance(); 1378 1379 bottom->Set(v8_str("__proto__"), middle); 1380 middle->Set(v8_str("__proto__"), top); 1381 env->Global()->Set(v8_str("obj"), bottom); 1382 1383 // Indexed and named get. 1384 Script::Compile(v8_str("obj[0]"))->Run(); 1385 Script::Compile(v8_str("obj.x"))->Run(); 1386 1387 // Indexed and named set. 1388 Script::Compile(v8_str("obj[1] = 42"))->Run(); 1389 Script::Compile(v8_str("obj.y = 42"))->Run(); 1390 1391 // Indexed and named query. 1392 Script::Compile(v8_str("0 in obj"))->Run(); 1393 Script::Compile(v8_str("'x' in obj"))->Run(); 1394 1395 // Indexed and named deleter. 1396 Script::Compile(v8_str("delete obj[0]"))->Run(); 1397 Script::Compile(v8_str("delete obj.x"))->Run(); 1398 1399 // Enumerators. 1400 Script::Compile(v8_str("for (var p in obj) ;"))->Run(); 1401 } 1402 1403 1404 static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key, 1405 const AccessorInfo& info) { 1406 ApiTestFuzzer::Fuzz(); 1407 if (v8_str("pre")->Equals(key)) { 1408 return v8_str("PrePropertyHandler: pre"); 1409 } 1410 return v8::Handle<String>(); 1411 } 1412 1413 1414 static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key, 1415 const AccessorInfo&) { 1416 if (v8_str("pre")->Equals(key)) { 1417 return v8::Integer::New(v8::None); 1418 } 1419 1420 return v8::Handle<v8::Integer>(); // do not intercept the call 1421 } 1422 1423 1424 THREADED_TEST(PrePropertyHandler) { 1425 v8::HandleScope scope; 1426 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(); 1427 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet, 1428 0, 1429 PrePropertyHandlerQuery); 1430 LocalContext env(NULL, desc->InstanceTemplate()); 1431 Script::Compile(v8_str( 1432 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run(); 1433 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run(); 1434 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre); 1435 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run(); 1436 CHECK_EQ(v8_str("Object: on"), result_on); 1437 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run(); 1438 CHECK(result_post.IsEmpty()); 1439 } 1440 1441 1442 THREADED_TEST(UndefinedIsNotEnumerable) { 1443 v8::HandleScope scope; 1444 LocalContext env; 1445 v8::Handle<Value> result = Script::Compile(v8_str( 1446 "this.propertyIsEnumerable(undefined)"))->Run(); 1447 CHECK(result->IsFalse()); 1448 } 1449 1450 1451 v8::Handle<Script> call_recursively_script; 1452 static const int kTargetRecursionDepth = 200; // near maximum 1453 1454 1455 static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) { 1456 ApiTestFuzzer::Fuzz(); 1457 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1458 if (depth == kTargetRecursionDepth) return v8::Undefined(); 1459 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1460 return call_recursively_script->Run(); 1461 } 1462 1463 1464 static v8::Handle<Value> CallFunctionRecursivelyCall( 1465 const v8::Arguments& args) { 1466 ApiTestFuzzer::Fuzz(); 1467 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1468 if (depth == kTargetRecursionDepth) { 1469 printf("[depth = %d]\n", depth); 1470 return v8::Undefined(); 1471 } 1472 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1473 v8::Handle<Value> function = 1474 args.This()->Get(v8_str("callFunctionRecursively")); 1475 return function.As<Function>()->Call(args.This(), 0, NULL); 1476 } 1477 1478 1479 THREADED_TEST(DeepCrossLanguageRecursion) { 1480 v8::HandleScope scope; 1481 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 1482 global->Set(v8_str("callScriptRecursively"), 1483 v8::FunctionTemplate::New(CallScriptRecursivelyCall)); 1484 global->Set(v8_str("callFunctionRecursively"), 1485 v8::FunctionTemplate::New(CallFunctionRecursivelyCall)); 1486 LocalContext env(NULL, global); 1487 1488 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1489 call_recursively_script = v8_compile("callScriptRecursively()"); 1490 v8::Handle<Value> result = call_recursively_script->Run(); 1491 call_recursively_script = v8::Handle<Script>(); 1492 1493 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1494 Script::Compile(v8_str("callFunctionRecursively()"))->Run(); 1495 } 1496 1497 1498 static v8::Handle<Value> 1499 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) { 1500 ApiTestFuzzer::Fuzz(); 1501 return v8::ThrowException(key); 1502 } 1503 1504 1505 static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key, 1506 Local<Value>, 1507 const AccessorInfo&) { 1508 v8::ThrowException(key); 1509 return v8::Undefined(); // not the same as v8::Handle<v8::Value>() 1510 } 1511 1512 1513 THREADED_TEST(CallbackExceptionRegression) { 1514 v8::HandleScope scope; 1515 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 1516 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet, 1517 ThrowingPropertyHandlerSet); 1518 LocalContext env; 1519 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 1520 v8::Handle<Value> otto = Script::Compile(v8_str( 1521 "try { with (obj) { otto; } } catch (e) { e; }"))->Run(); 1522 CHECK_EQ(v8_str("otto"), otto); 1523 v8::Handle<Value> netto = Script::Compile(v8_str( 1524 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run(); 1525 CHECK_EQ(v8_str("netto"), netto); 1526 } 1527 1528 1529 THREADED_TEST(FunctionPrototype) { 1530 v8::HandleScope scope; 1531 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New(); 1532 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321)); 1533 LocalContext env; 1534 env->Global()->Set(v8_str("Foo"), Foo->GetFunction()); 1535 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak")); 1536 CHECK_EQ(script->Run()->Int32Value(), 321); 1537 } 1538 1539 1540 THREADED_TEST(InternalFields) { 1541 v8::HandleScope scope; 1542 LocalContext env; 1543 1544 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1545 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1546 instance_templ->SetInternalFieldCount(1); 1547 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1548 CHECK_EQ(1, obj->InternalFieldCount()); 1549 CHECK(obj->GetInternalField(0)->IsUndefined()); 1550 obj->SetInternalField(0, v8_num(17)); 1551 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value()); 1552 } 1553 1554 1555 THREADED_TEST(GlobalObjectInternalFields) { 1556 v8::HandleScope scope; 1557 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 1558 global_template->SetInternalFieldCount(1); 1559 LocalContext env(NULL, global_template); 1560 v8::Handle<v8::Object> global_proxy = env->Global(); 1561 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); 1562 CHECK_EQ(1, global->InternalFieldCount()); 1563 CHECK(global->GetInternalField(0)->IsUndefined()); 1564 global->SetInternalField(0, v8_num(17)); 1565 CHECK_EQ(17, global->GetInternalField(0)->Int32Value()); 1566 } 1567 1568 1569 THREADED_TEST(InternalFieldsNativePointers) { 1570 v8::HandleScope scope; 1571 LocalContext env; 1572 1573 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1574 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1575 instance_templ->SetInternalFieldCount(1); 1576 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1577 CHECK_EQ(1, obj->InternalFieldCount()); 1578 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1579 1580 char* data = new char[100]; 1581 1582 void* aligned = data; 1583 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1)); 1584 void* unaligned = data + 1; 1585 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1)); 1586 1587 // Check reading and writing aligned pointers. 1588 obj->SetPointerInInternalField(0, aligned); 1589 HEAP->CollectAllGarbage(false); 1590 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1591 1592 // Check reading and writing unaligned pointers. 1593 obj->SetPointerInInternalField(0, unaligned); 1594 HEAP->CollectAllGarbage(false); 1595 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1596 1597 delete[] data; 1598 } 1599 1600 1601 THREADED_TEST(InternalFieldsNativePointersAndExternal) { 1602 v8::HandleScope scope; 1603 LocalContext env; 1604 1605 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1606 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1607 instance_templ->SetInternalFieldCount(1); 1608 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1609 CHECK_EQ(1, obj->InternalFieldCount()); 1610 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1611 1612 char* data = new char[100]; 1613 1614 void* aligned = data; 1615 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1)); 1616 void* unaligned = data + 1; 1617 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1)); 1618 1619 obj->SetPointerInInternalField(0, aligned); 1620 HEAP->CollectAllGarbage(false); 1621 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0))); 1622 1623 obj->SetPointerInInternalField(0, unaligned); 1624 HEAP->CollectAllGarbage(false); 1625 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0))); 1626 1627 obj->SetInternalField(0, v8::External::Wrap(aligned)); 1628 HEAP->CollectAllGarbage(false); 1629 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1630 1631 obj->SetInternalField(0, v8::External::Wrap(unaligned)); 1632 HEAP->CollectAllGarbage(false); 1633 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1634 1635 delete[] data; 1636 } 1637 1638 1639 THREADED_TEST(IdentityHash) { 1640 v8::HandleScope scope; 1641 LocalContext env; 1642 1643 // Ensure that the test starts with an fresh heap to test whether the hash 1644 // code is based on the address. 1645 HEAP->CollectAllGarbage(false); 1646 Local<v8::Object> obj = v8::Object::New(); 1647 int hash = obj->GetIdentityHash(); 1648 int hash1 = obj->GetIdentityHash(); 1649 CHECK_EQ(hash, hash1); 1650 int hash2 = v8::Object::New()->GetIdentityHash(); 1651 // Since the identity hash is essentially a random number two consecutive 1652 // objects should not be assigned the same hash code. If the test below fails 1653 // the random number generator should be evaluated. 1654 CHECK_NE(hash, hash2); 1655 HEAP->CollectAllGarbage(false); 1656 int hash3 = v8::Object::New()->GetIdentityHash(); 1657 // Make sure that the identity hash is not based on the initial address of 1658 // the object alone. If the test below fails the random number generator 1659 // should be evaluated. 1660 CHECK_NE(hash, hash3); 1661 int hash4 = obj->GetIdentityHash(); 1662 CHECK_EQ(hash, hash4); 1663 1664 // Check identity hashes behaviour in the presence of JS accessors. 1665 // Put a getter for 'v8::IdentityHash' on the Object's prototype: 1666 { 1667 CompileRun("Object.prototype['v8::IdentityHash'] = 42;\n"); 1668 Local<v8::Object> o1 = v8::Object::New(); 1669 Local<v8::Object> o2 = v8::Object::New(); 1670 CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash()); 1671 } 1672 { 1673 CompileRun( 1674 "function cnst() { return 42; };\n" 1675 "Object.prototype.__defineGetter__('v8::IdentityHash', cnst);\n"); 1676 Local<v8::Object> o1 = v8::Object::New(); 1677 Local<v8::Object> o2 = v8::Object::New(); 1678 CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash()); 1679 } 1680 } 1681 1682 1683 THREADED_TEST(HiddenProperties) { 1684 v8::HandleScope scope; 1685 LocalContext env; 1686 1687 v8::Local<v8::Object> obj = v8::Object::New(); 1688 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 1689 v8::Local<v8::String> empty = v8_str(""); 1690 v8::Local<v8::String> prop_name = v8_str("prop_name"); 1691 1692 HEAP->CollectAllGarbage(false); 1693 1694 // Make sure delete of a non-existent hidden value works 1695 CHECK(obj->DeleteHiddenValue(key)); 1696 1697 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503))); 1698 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value()); 1699 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002))); 1700 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1701 1702 HEAP->CollectAllGarbage(false); 1703 1704 // Make sure we do not find the hidden property. 1705 CHECK(!obj->Has(empty)); 1706 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1707 CHECK(obj->Get(empty)->IsUndefined()); 1708 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1709 CHECK(obj->Set(empty, v8::Integer::New(2003))); 1710 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1711 CHECK_EQ(2003, obj->Get(empty)->Int32Value()); 1712 1713 HEAP->CollectAllGarbage(false); 1714 1715 // Add another property and delete it afterwards to force the object in 1716 // slow case. 1717 CHECK(obj->Set(prop_name, v8::Integer::New(2008))); 1718 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1719 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value()); 1720 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1721 CHECK(obj->Delete(prop_name)); 1722 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1723 1724 HEAP->CollectAllGarbage(false); 1725 1726 CHECK(obj->DeleteHiddenValue(key)); 1727 CHECK(obj->GetHiddenValue(key).IsEmpty()); 1728 } 1729 1730 1731 static bool interceptor_for_hidden_properties_called; 1732 static v8::Handle<Value> InterceptorForHiddenProperties( 1733 Local<String> name, const AccessorInfo& info) { 1734 interceptor_for_hidden_properties_called = true; 1735 return v8::Handle<Value>(); 1736 } 1737 1738 1739 THREADED_TEST(HiddenPropertiesWithInterceptors) { 1740 v8::HandleScope scope; 1741 LocalContext context; 1742 1743 interceptor_for_hidden_properties_called = false; 1744 1745 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 1746 1747 // Associate an interceptor with an object and start setting hidden values. 1748 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 1749 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 1750 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties); 1751 Local<v8::Function> function = fun_templ->GetFunction(); 1752 Local<v8::Object> obj = function->NewInstance(); 1753 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302))); 1754 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value()); 1755 CHECK(!interceptor_for_hidden_properties_called); 1756 } 1757 1758 1759 THREADED_TEST(External) { 1760 v8::HandleScope scope; 1761 int x = 3; 1762 Local<v8::External> ext = v8::External::New(&x); 1763 LocalContext env; 1764 env->Global()->Set(v8_str("ext"), ext); 1765 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run(); 1766 v8::Handle<v8::External> reext = reext_obj.As<v8::External>(); 1767 int* ptr = static_cast<int*>(reext->Value()); 1768 CHECK_EQ(x, 3); 1769 *ptr = 10; 1770 CHECK_EQ(x, 10); 1771 1772 // Make sure unaligned pointers are wrapped properly. 1773 char* data = i::StrDup("0123456789"); 1774 Local<v8::Value> zero = v8::External::Wrap(&data[0]); 1775 Local<v8::Value> one = v8::External::Wrap(&data[1]); 1776 Local<v8::Value> two = v8::External::Wrap(&data[2]); 1777 Local<v8::Value> three = v8::External::Wrap(&data[3]); 1778 1779 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero)); 1780 CHECK_EQ('0', *char_ptr); 1781 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one)); 1782 CHECK_EQ('1', *char_ptr); 1783 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two)); 1784 CHECK_EQ('2', *char_ptr); 1785 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three)); 1786 CHECK_EQ('3', *char_ptr); 1787 i::DeleteArray(data); 1788 } 1789 1790 1791 THREADED_TEST(GlobalHandle) { 1792 v8::Persistent<String> global; 1793 { 1794 v8::HandleScope scope; 1795 Local<String> str = v8_str("str"); 1796 global = v8::Persistent<String>::New(str); 1797 } 1798 CHECK_EQ(global->Length(), 3); 1799 global.Dispose(); 1800 } 1801 1802 1803 static int NumberOfWeakCalls = 0; 1804 static void WeakPointerCallback(Persistent<Value> handle, void* id) { 1805 CHECK_EQ(reinterpret_cast<void*>(1234), id); 1806 NumberOfWeakCalls++; 1807 handle.Dispose(); 1808 } 1809 1810 THREADED_TEST(ApiObjectGroups) { 1811 HandleScope scope; 1812 LocalContext env; 1813 1814 NumberOfWeakCalls = 0; 1815 1816 Persistent<Object> g1s1; 1817 Persistent<Object> g1s2; 1818 Persistent<Object> g1c1; 1819 Persistent<Object> g2s1; 1820 Persistent<Object> g2s2; 1821 Persistent<Object> g2c1; 1822 1823 { 1824 HandleScope scope; 1825 g1s1 = Persistent<Object>::New(Object::New()); 1826 g1s2 = Persistent<Object>::New(Object::New()); 1827 g1c1 = Persistent<Object>::New(Object::New()); 1828 g1s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); 1829 g1s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); 1830 g1c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); 1831 1832 g2s1 = Persistent<Object>::New(Object::New()); 1833 g2s2 = Persistent<Object>::New(Object::New()); 1834 g2c1 = Persistent<Object>::New(Object::New()); 1835 g2s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); 1836 g2s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); 1837 g2c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); 1838 } 1839 1840 Persistent<Object> root = Persistent<Object>::New(g1s1); // make a root. 1841 1842 // Connect group 1 and 2, make a cycle. 1843 CHECK(g1s2->Set(0, g2s2)); 1844 CHECK(g2s1->Set(0, g1s1)); 1845 1846 { 1847 Persistent<Value> g1_objects[] = { g1s1, g1s2 }; 1848 Persistent<Value> g1_children[] = { g1c1 }; 1849 Persistent<Value> g2_objects[] = { g2s1, g2s2 }; 1850 Persistent<Value> g2_children[] = { g2c1 }; 1851 V8::AddObjectGroup(g1_objects, 2); 1852 V8::AddImplicitReferences(g1s1, g1_children, 1); 1853 V8::AddObjectGroup(g2_objects, 2); 1854 V8::AddImplicitReferences(g2s2, g2_children, 1); 1855 } 1856 // Do a full GC 1857 HEAP->CollectGarbage(i::OLD_POINTER_SPACE); 1858 1859 // All object should be alive. 1860 CHECK_EQ(0, NumberOfWeakCalls); 1861 1862 // Weaken the root. 1863 root.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); 1864 // But make children strong roots---all the objects (except for children) 1865 // should be collectable now. 1866 g1c1.ClearWeak(); 1867 g2c1.ClearWeak(); 1868 1869 // Groups are deleted, rebuild groups. 1870 { 1871 Persistent<Value> g1_objects[] = { g1s1, g1s2 }; 1872 Persistent<Value> g1_children[] = { g1c1 }; 1873 Persistent<Value> g2_objects[] = { g2s1, g2s2 }; 1874 Persistent<Value> g2_children[] = { g2c1 }; 1875 V8::AddObjectGroup(g1_objects, 2); 1876 V8::AddImplicitReferences(g1s1, g1_children, 1); 1877 V8::AddObjectGroup(g2_objects, 2); 1878 V8::AddImplicitReferences(g2s2, g2_children, 1); 1879 } 1880 1881 HEAP->CollectGarbage(i::OLD_POINTER_SPACE); 1882 1883 // All objects should be gone. 5 global handles in total. 1884 CHECK_EQ(5, NumberOfWeakCalls); 1885 1886 // And now make children weak again and collect them. 1887 g1c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); 1888 g2c1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); 1889 1890 HEAP->CollectGarbage(i::OLD_POINTER_SPACE); 1891 CHECK_EQ(7, NumberOfWeakCalls); 1892 } 1893 1894 1895 THREADED_TEST(ApiObjectGroupsCycle) { 1896 HandleScope scope; 1897 LocalContext env; 1898 1899 NumberOfWeakCalls = 0; 1900 1901 Persistent<Object> g1s1; 1902 Persistent<Object> g1s2; 1903 Persistent<Object> g2s1; 1904 Persistent<Object> g2s2; 1905 Persistent<Object> g3s1; 1906 Persistent<Object> g3s2; 1907 1908 { 1909 HandleScope scope; 1910 g1s1 = Persistent<Object>::New(Object::New()); 1911 g1s2 = Persistent<Object>::New(Object::New()); 1912 g1s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); 1913 g1s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); 1914 1915 g2s1 = Persistent<Object>::New(Object::New()); 1916 g2s2 = Persistent<Object>::New(Object::New()); 1917 g2s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); 1918 g2s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); 1919 1920 g3s1 = Persistent<Object>::New(Object::New()); 1921 g3s2 = Persistent<Object>::New(Object::New()); 1922 g3s1.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); 1923 g3s2.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); 1924 } 1925 1926 Persistent<Object> root = Persistent<Object>::New(g1s1); // make a root. 1927 1928 // Connect groups. We're building the following cycle: 1929 // G1: { g1s1, g2s1 }, g1s1 implicitly references g2s1, ditto for other 1930 // groups. 1931 { 1932 Persistent<Value> g1_objects[] = { g1s1, g1s2 }; 1933 Persistent<Value> g1_children[] = { g2s1 }; 1934 Persistent<Value> g2_objects[] = { g2s1, g2s2 }; 1935 Persistent<Value> g2_children[] = { g3s1 }; 1936 Persistent<Value> g3_objects[] = { g3s1, g3s2 }; 1937 Persistent<Value> g3_children[] = { g1s1 }; 1938 V8::AddObjectGroup(g1_objects, 2); 1939 V8::AddImplicitReferences(g1s1, g1_children, 1); 1940 V8::AddObjectGroup(g2_objects, 2); 1941 V8::AddImplicitReferences(g2s1, g2_children, 1); 1942 V8::AddObjectGroup(g3_objects, 2); 1943 V8::AddImplicitReferences(g3s1, g3_children, 1); 1944 } 1945 // Do a full GC 1946 HEAP->CollectGarbage(i::OLD_POINTER_SPACE); 1947 1948 // All object should be alive. 1949 CHECK_EQ(0, NumberOfWeakCalls); 1950 1951 // Weaken the root. 1952 root.MakeWeak(reinterpret_cast<void*>(1234), &WeakPointerCallback); 1953 1954 // Groups are deleted, rebuild groups. 1955 { 1956 Persistent<Value> g1_objects[] = { g1s1, g1s2 }; 1957 Persistent<Value> g1_children[] = { g2s1 }; 1958 Persistent<Value> g2_objects[] = { g2s1, g2s2 }; 1959 Persistent<Value> g2_children[] = { g3s1 }; 1960 Persistent<Value> g3_objects[] = { g3s1, g3s2 }; 1961 Persistent<Value> g3_children[] = { g1s1 }; 1962 V8::AddObjectGroup(g1_objects, 2); 1963 V8::AddImplicitReferences(g1s1, g1_children, 1); 1964 V8::AddObjectGroup(g2_objects, 2); 1965 V8::AddImplicitReferences(g2s1, g2_children, 1); 1966 V8::AddObjectGroup(g3_objects, 2); 1967 V8::AddImplicitReferences(g3s1, g3_children, 1); 1968 } 1969 1970 HEAP->CollectGarbage(i::OLD_POINTER_SPACE); 1971 1972 // All objects should be gone. 7 global handles in total. 1973 CHECK_EQ(7, NumberOfWeakCalls); 1974 } 1975 1976 1977 THREADED_TEST(ScriptException) { 1978 v8::HandleScope scope; 1979 LocalContext env; 1980 Local<Script> script = Script::Compile(v8_str("throw 'panama!';")); 1981 v8::TryCatch try_catch; 1982 Local<Value> result = script->Run(); 1983 CHECK(result.IsEmpty()); 1984 CHECK(try_catch.HasCaught()); 1985 String::AsciiValue exception_value(try_catch.Exception()); 1986 CHECK_EQ(*exception_value, "panama!"); 1987 } 1988 1989 1990 bool message_received; 1991 1992 1993 static void check_message(v8::Handle<v8::Message> message, 1994 v8::Handle<Value> data) { 1995 CHECK_EQ(5.76, data->NumberValue()); 1996 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue()); 1997 CHECK_EQ(7.56, message->GetScriptData()->NumberValue()); 1998 message_received = true; 1999 } 2000 2001 2002 THREADED_TEST(MessageHandlerData) { 2003 message_received = false; 2004 v8::HandleScope scope; 2005 CHECK(!message_received); 2006 v8::V8::AddMessageListener(check_message, v8_num(5.76)); 2007 LocalContext context; 2008 v8::ScriptOrigin origin = 2009 v8::ScriptOrigin(v8_str("6.75")); 2010 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"), 2011 &origin); 2012 script->SetData(v8_str("7.56")); 2013 script->Run(); 2014 CHECK(message_received); 2015 // clear out the message listener 2016 v8::V8::RemoveMessageListeners(check_message); 2017 } 2018 2019 2020 THREADED_TEST(GetSetProperty) { 2021 v8::HandleScope scope; 2022 LocalContext context; 2023 context->Global()->Set(v8_str("foo"), v8_num(14)); 2024 context->Global()->Set(v8_str("12"), v8_num(92)); 2025 context->Global()->Set(v8::Integer::New(16), v8_num(32)); 2026 context->Global()->Set(v8_num(13), v8_num(56)); 2027 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run(); 2028 CHECK_EQ(14, foo->Int32Value()); 2029 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run(); 2030 CHECK_EQ(92, twelve->Int32Value()); 2031 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run(); 2032 CHECK_EQ(32, sixteen->Int32Value()); 2033 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run(); 2034 CHECK_EQ(56, thirteen->Int32Value()); 2035 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value()); 2036 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value()); 2037 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value()); 2038 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value()); 2039 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value()); 2040 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value()); 2041 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value()); 2042 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value()); 2043 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value()); 2044 } 2045 2046 2047 THREADED_TEST(PropertyAttributes) { 2048 v8::HandleScope scope; 2049 LocalContext context; 2050 // read-only 2051 Local<String> prop = v8_str("read_only"); 2052 context->Global()->Set(prop, v8_num(7), v8::ReadOnly); 2053 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 2054 Script::Compile(v8_str("read_only = 9"))->Run(); 2055 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 2056 context->Global()->Set(prop, v8_num(10)); 2057 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 2058 // dont-delete 2059 prop = v8_str("dont_delete"); 2060 context->Global()->Set(prop, v8_num(13), v8::DontDelete); 2061 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 2062 Script::Compile(v8_str("delete dont_delete"))->Run(); 2063 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 2064 } 2065 2066 2067 THREADED_TEST(Array) { 2068 v8::HandleScope scope; 2069 LocalContext context; 2070 Local<v8::Array> array = v8::Array::New(); 2071 CHECK_EQ(0, array->Length()); 2072 CHECK(array->Get(0)->IsUndefined()); 2073 CHECK(!array->Has(0)); 2074 CHECK(array->Get(100)->IsUndefined()); 2075 CHECK(!array->Has(100)); 2076 array->Set(2, v8_num(7)); 2077 CHECK_EQ(3, array->Length()); 2078 CHECK(!array->Has(0)); 2079 CHECK(!array->Has(1)); 2080 CHECK(array->Has(2)); 2081 CHECK_EQ(7, array->Get(2)->Int32Value()); 2082 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run(); 2083 Local<v8::Array> arr = obj.As<v8::Array>(); 2084 CHECK_EQ(3, arr->Length()); 2085 CHECK_EQ(1, arr->Get(0)->Int32Value()); 2086 CHECK_EQ(2, arr->Get(1)->Int32Value()); 2087 CHECK_EQ(3, arr->Get(2)->Int32Value()); 2088 array = v8::Array::New(27); 2089 CHECK_EQ(27, array->Length()); 2090 array = v8::Array::New(-27); 2091 CHECK_EQ(0, array->Length()); 2092 } 2093 2094 2095 v8::Handle<Value> HandleF(const v8::Arguments& args) { 2096 v8::HandleScope scope; 2097 ApiTestFuzzer::Fuzz(); 2098 Local<v8::Array> result = v8::Array::New(args.Length()); 2099 for (int i = 0; i < args.Length(); i++) 2100 result->Set(i, args[i]); 2101 return scope.Close(result); 2102 } 2103 2104 2105 THREADED_TEST(Vector) { 2106 v8::HandleScope scope; 2107 Local<ObjectTemplate> global = ObjectTemplate::New(); 2108 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF)); 2109 LocalContext context(0, global); 2110 2111 const char* fun = "f()"; 2112 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>(); 2113 CHECK_EQ(0, a0->Length()); 2114 2115 const char* fun2 = "f(11)"; 2116 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>(); 2117 CHECK_EQ(1, a1->Length()); 2118 CHECK_EQ(11, a1->Get(0)->Int32Value()); 2119 2120 const char* fun3 = "f(12, 13)"; 2121 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>(); 2122 CHECK_EQ(2, a2->Length()); 2123 CHECK_EQ(12, a2->Get(0)->Int32Value()); 2124 CHECK_EQ(13, a2->Get(1)->Int32Value()); 2125 2126 const char* fun4 = "f(14, 15, 16)"; 2127 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>(); 2128 CHECK_EQ(3, a3->Length()); 2129 CHECK_EQ(14, a3->Get(0)->Int32Value()); 2130 CHECK_EQ(15, a3->Get(1)->Int32Value()); 2131 CHECK_EQ(16, a3->Get(2)->Int32Value()); 2132 2133 const char* fun5 = "f(17, 18, 19, 20)"; 2134 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>(); 2135 CHECK_EQ(4, a4->Length()); 2136 CHECK_EQ(17, a4->Get(0)->Int32Value()); 2137 CHECK_EQ(18, a4->Get(1)->Int32Value()); 2138 CHECK_EQ(19, a4->Get(2)->Int32Value()); 2139 CHECK_EQ(20, a4->Get(3)->Int32Value()); 2140 } 2141 2142 2143 THREADED_TEST(FunctionCall) { 2144 v8::HandleScope scope; 2145 LocalContext context; 2146 CompileRun( 2147 "function Foo() {" 2148 " var result = [];" 2149 " for (var i = 0; i < arguments.length; i++) {" 2150 " result.push(arguments[i]);" 2151 " }" 2152 " return result;" 2153 "}"); 2154 Local<Function> Foo = 2155 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 2156 2157 v8::Handle<Value>* args0 = NULL; 2158 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0)); 2159 CHECK_EQ(0, a0->Length()); 2160 2161 v8::Handle<Value> args1[] = { v8_num(1.1) }; 2162 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1)); 2163 CHECK_EQ(1, a1->Length()); 2164 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 2165 2166 v8::Handle<Value> args2[] = { v8_num(2.2), 2167 v8_num(3.3) }; 2168 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2)); 2169 CHECK_EQ(2, a2->Length()); 2170 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 2171 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 2172 2173 v8::Handle<Value> args3[] = { v8_num(4.4), 2174 v8_num(5.5), 2175 v8_num(6.6) }; 2176 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3)); 2177 CHECK_EQ(3, a3->Length()); 2178 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 2179 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 2180 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 2181 2182 v8::Handle<Value> args4[] = { v8_num(7.7), 2183 v8_num(8.8), 2184 v8_num(9.9), 2185 v8_num(10.11) }; 2186 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4)); 2187 CHECK_EQ(4, a4->Length()); 2188 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 2189 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 2190 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 2191 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 2192 } 2193 2194 2195 static const char* js_code_causing_out_of_memory = 2196 "var a = new Array(); while(true) a.push(a);"; 2197 2198 2199 // These tests run for a long time and prevent us from running tests 2200 // that come after them so they cannot run in parallel. 2201 TEST(OutOfMemory) { 2202 // It's not possible to read a snapshot into a heap with different dimensions. 2203 if (i::Snapshot::IsEnabled()) return; 2204 // Set heap limits. 2205 static const int K = 1024; 2206 v8::ResourceConstraints constraints; 2207 constraints.set_max_young_space_size(256 * K); 2208 constraints.set_max_old_space_size(4 * K * K); 2209 v8::SetResourceConstraints(&constraints); 2210 2211 // Execute a script that causes out of memory. 2212 v8::HandleScope scope; 2213 LocalContext context; 2214 v8::V8::IgnoreOutOfMemoryException(); 2215 Local<Script> script = 2216 Script::Compile(String::New(js_code_causing_out_of_memory)); 2217 Local<Value> result = script->Run(); 2218 2219 // Check for out of memory state. 2220 CHECK(result.IsEmpty()); 2221 CHECK(context->HasOutOfMemoryException()); 2222 } 2223 2224 2225 v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) { 2226 ApiTestFuzzer::Fuzz(); 2227 2228 v8::HandleScope scope; 2229 LocalContext context; 2230 Local<Script> script = 2231 Script::Compile(String::New(js_code_causing_out_of_memory)); 2232 Local<Value> result = script->Run(); 2233 2234 // Check for out of memory state. 2235 CHECK(result.IsEmpty()); 2236 CHECK(context->HasOutOfMemoryException()); 2237 2238 return result; 2239 } 2240 2241 2242 TEST(OutOfMemoryNested) { 2243 // It's not possible to read a snapshot into a heap with different dimensions. 2244 if (i::Snapshot::IsEnabled()) return; 2245 // Set heap limits. 2246 static const int K = 1024; 2247 v8::ResourceConstraints constraints; 2248 constraints.set_max_young_space_size(256 * K); 2249 constraints.set_max_old_space_size(4 * K * K); 2250 v8::SetResourceConstraints(&constraints); 2251 2252 v8::HandleScope scope; 2253 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2254 templ->Set(v8_str("ProvokeOutOfMemory"), 2255 v8::FunctionTemplate::New(ProvokeOutOfMemory)); 2256 LocalContext context(0, templ); 2257 v8::V8::IgnoreOutOfMemoryException(); 2258 Local<Value> result = CompileRun( 2259 "var thrown = false;" 2260 "try {" 2261 " ProvokeOutOfMemory();" 2262 "} catch (e) {" 2263 " thrown = true;" 2264 "}"); 2265 // Check for out of memory state. 2266 CHECK(result.IsEmpty()); 2267 CHECK(context->HasOutOfMemoryException()); 2268 } 2269 2270 2271 TEST(HugeConsStringOutOfMemory) { 2272 // It's not possible to read a snapshot into a heap with different dimensions. 2273 if (i::Snapshot::IsEnabled()) return; 2274 // Set heap limits. 2275 static const int K = 1024; 2276 v8::ResourceConstraints constraints; 2277 constraints.set_max_young_space_size(256 * K); 2278 constraints.set_max_old_space_size(2 * K * K); 2279 v8::SetResourceConstraints(&constraints); 2280 2281 // Execute a script that causes out of memory. 2282 v8::V8::IgnoreOutOfMemoryException(); 2283 2284 v8::HandleScope scope; 2285 LocalContext context; 2286 2287 // Build huge string. This should fail with out of memory exception. 2288 Local<Value> result = CompileRun( 2289 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();" 2290 "for (var i = 0; i < 22; i++) { str = str + str; }"); 2291 2292 // Check for out of memory state. 2293 CHECK(result.IsEmpty()); 2294 CHECK(context->HasOutOfMemoryException()); 2295 } 2296 2297 2298 THREADED_TEST(ConstructCall) { 2299 v8::HandleScope scope; 2300 LocalContext context; 2301 CompileRun( 2302 "function Foo() {" 2303 " var result = [];" 2304 " for (var i = 0; i < arguments.length; i++) {" 2305 " result.push(arguments[i]);" 2306 " }" 2307 " return result;" 2308 "}"); 2309 Local<Function> Foo = 2310 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 2311 2312 v8::Handle<Value>* args0 = NULL; 2313 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0)); 2314 CHECK_EQ(0, a0->Length()); 2315 2316 v8::Handle<Value> args1[] = { v8_num(1.1) }; 2317 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1)); 2318 CHECK_EQ(1, a1->Length()); 2319 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 2320 2321 v8::Handle<Value> args2[] = { v8_num(2.2), 2322 v8_num(3.3) }; 2323 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2)); 2324 CHECK_EQ(2, a2->Length()); 2325 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 2326 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 2327 2328 v8::Handle<Value> args3[] = { v8_num(4.4), 2329 v8_num(5.5), 2330 v8_num(6.6) }; 2331 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3)); 2332 CHECK_EQ(3, a3->Length()); 2333 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 2334 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 2335 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 2336 2337 v8::Handle<Value> args4[] = { v8_num(7.7), 2338 v8_num(8.8), 2339 v8_num(9.9), 2340 v8_num(10.11) }; 2341 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4)); 2342 CHECK_EQ(4, a4->Length()); 2343 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 2344 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 2345 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 2346 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 2347 } 2348 2349 2350 static void CheckUncle(v8::TryCatch* try_catch) { 2351 CHECK(try_catch->HasCaught()); 2352 String::AsciiValue str_value(try_catch->Exception()); 2353 CHECK_EQ(*str_value, "uncle?"); 2354 try_catch->Reset(); 2355 } 2356 2357 2358 THREADED_TEST(ConversionNumber) { 2359 v8::HandleScope scope; 2360 LocalContext env; 2361 // Very large number. 2362 CompileRun("var obj = Math.pow(2,32) * 1237;"); 2363 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2364 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value()); 2365 CHECK_EQ(0, obj->ToInt32()->Value()); 2366 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned. 2367 // Large number. 2368 CompileRun("var obj = -1234567890123;"); 2369 obj = env->Global()->Get(v8_str("obj")); 2370 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value()); 2371 CHECK_EQ(-1912276171, obj->ToInt32()->Value()); 2372 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT 2373 // Small positive integer. 2374 CompileRun("var obj = 42;"); 2375 obj = env->Global()->Get(v8_str("obj")); 2376 CHECK_EQ(42.0, obj->ToNumber()->Value()); 2377 CHECK_EQ(42, obj->ToInt32()->Value()); 2378 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 2379 // Negative integer. 2380 CompileRun("var obj = -37;"); 2381 obj = env->Global()->Get(v8_str("obj")); 2382 CHECK_EQ(-37.0, obj->ToNumber()->Value()); 2383 CHECK_EQ(-37, obj->ToInt32()->Value()); 2384 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT 2385 // Positive non-int32 integer. 2386 CompileRun("var obj = 0x81234567;"); 2387 obj = env->Global()->Get(v8_str("obj")); 2388 CHECK_EQ(2166572391.0, obj->ToNumber()->Value()); 2389 CHECK_EQ(-2128394905, obj->ToInt32()->Value()); 2390 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT 2391 // Fraction. 2392 CompileRun("var obj = 42.3;"); 2393 obj = env->Global()->Get(v8_str("obj")); 2394 CHECK_EQ(42.3, obj->ToNumber()->Value()); 2395 CHECK_EQ(42, obj->ToInt32()->Value()); 2396 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 2397 // Large negative fraction. 2398 CompileRun("var obj = -5726623061.75;"); 2399 obj = env->Global()->Get(v8_str("obj")); 2400 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value()); 2401 CHECK_EQ(-1431655765, obj->ToInt32()->Value()); 2402 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT 2403 } 2404 2405 2406 THREADED_TEST(isNumberType) { 2407 v8::HandleScope scope; 2408 LocalContext env; 2409 // Very large number. 2410 CompileRun("var obj = Math.pow(2,32) * 1237;"); 2411 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2412 CHECK(!obj->IsInt32()); 2413 CHECK(!obj->IsUint32()); 2414 // Large negative number. 2415 CompileRun("var obj = -1234567890123;"); 2416 obj = env->Global()->Get(v8_str("obj")); 2417 CHECK(!obj->IsInt32()); 2418 CHECK(!obj->IsUint32()); 2419 // Small positive integer. 2420 CompileRun("var obj = 42;"); 2421 obj = env->Global()->Get(v8_str("obj")); 2422 CHECK(obj->IsInt32()); 2423 CHECK(obj->IsUint32()); 2424 // Negative integer. 2425 CompileRun("var obj = -37;"); 2426 obj = env->Global()->Get(v8_str("obj")); 2427 CHECK(obj->IsInt32()); 2428 CHECK(!obj->IsUint32()); 2429 // Positive non-int32 integer. 2430 CompileRun("var obj = 0x81234567;"); 2431 obj = env->Global()->Get(v8_str("obj")); 2432 CHECK(!obj->IsInt32()); 2433 CHECK(obj->IsUint32()); 2434 // Fraction. 2435 CompileRun("var obj = 42.3;"); 2436 obj = env->Global()->Get(v8_str("obj")); 2437 CHECK(!obj->IsInt32()); 2438 CHECK(!obj->IsUint32()); 2439 // Large negative fraction. 2440 CompileRun("var obj = -5726623061.75;"); 2441 obj = env->Global()->Get(v8_str("obj")); 2442 CHECK(!obj->IsInt32()); 2443 CHECK(!obj->IsUint32()); 2444 } 2445 2446 2447 THREADED_TEST(ConversionException) { 2448 v8::HandleScope scope; 2449 LocalContext env; 2450 CompileRun( 2451 "function TestClass() { };" 2452 "TestClass.prototype.toString = function () { throw 'uncle?'; };" 2453 "var obj = new TestClass();"); 2454 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2455 2456 v8::TryCatch try_catch; 2457 2458 Local<Value> to_string_result = obj->ToString(); 2459 CHECK(to_string_result.IsEmpty()); 2460 CheckUncle(&try_catch); 2461 2462 Local<Value> to_number_result = obj->ToNumber(); 2463 CHECK(to_number_result.IsEmpty()); 2464 CheckUncle(&try_catch); 2465 2466 Local<Value> to_integer_result = obj->ToInteger(); 2467 CHECK(to_integer_result.IsEmpty()); 2468 CheckUncle(&try_catch); 2469 2470 Local<Value> to_uint32_result = obj->ToUint32(); 2471 CHECK(to_uint32_result.IsEmpty()); 2472 CheckUncle(&try_catch); 2473 2474 Local<Value> to_int32_result = obj->ToInt32(); 2475 CHECK(to_int32_result.IsEmpty()); 2476 CheckUncle(&try_catch); 2477 2478 Local<Value> to_object_result = v8::Undefined()->ToObject(); 2479 CHECK(to_object_result.IsEmpty()); 2480 CHECK(try_catch.HasCaught()); 2481 try_catch.Reset(); 2482 2483 int32_t int32_value = obj->Int32Value(); 2484 CHECK_EQ(0, int32_value); 2485 CheckUncle(&try_catch); 2486 2487 uint32_t uint32_value = obj->Uint32Value(); 2488 CHECK_EQ(0, uint32_value); 2489 CheckUncle(&try_catch); 2490 2491 double number_value = obj->NumberValue(); 2492 CHECK_NE(0, IsNaN(number_value)); 2493 CheckUncle(&try_catch); 2494 2495 int64_t integer_value = obj->IntegerValue(); 2496 CHECK_EQ(0.0, static_cast<double>(integer_value)); 2497 CheckUncle(&try_catch); 2498 } 2499 2500 2501 v8::Handle<Value> ThrowFromC(const v8::Arguments& args) { 2502 ApiTestFuzzer::Fuzz(); 2503 return v8::ThrowException(v8_str("konto")); 2504 } 2505 2506 2507 v8::Handle<Value> CCatcher(const v8::Arguments& args) { 2508 if (args.Length() < 1) return v8::Boolean::New(false); 2509 v8::HandleScope scope; 2510 v8::TryCatch try_catch; 2511 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run(); 2512 CHECK(!try_catch.HasCaught() || result.IsEmpty()); 2513 return v8::Boolean::New(try_catch.HasCaught()); 2514 } 2515 2516 2517 THREADED_TEST(APICatch) { 2518 v8::HandleScope scope; 2519 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2520 templ->Set(v8_str("ThrowFromC"), 2521 v8::FunctionTemplate::New(ThrowFromC)); 2522 LocalContext context(0, templ); 2523 CompileRun( 2524 "var thrown = false;" 2525 "try {" 2526 " ThrowFromC();" 2527 "} catch (e) {" 2528 " thrown = true;" 2529 "}"); 2530 Local<Value> thrown = context->Global()->Get(v8_str("thrown")); 2531 CHECK(thrown->BooleanValue()); 2532 } 2533 2534 2535 THREADED_TEST(APIThrowTryCatch) { 2536 v8::HandleScope scope; 2537 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2538 templ->Set(v8_str("ThrowFromC"), 2539 v8::FunctionTemplate::New(ThrowFromC)); 2540 LocalContext context(0, templ); 2541 v8::TryCatch try_catch; 2542 CompileRun("ThrowFromC();"); 2543 CHECK(try_catch.HasCaught()); 2544 } 2545 2546 2547 // Test that a try-finally block doesn't shadow a try-catch block 2548 // when setting up an external handler. 2549 // 2550 // BUG(271): Some of the exception propagation does not work on the 2551 // ARM simulator because the simulator separates the C++ stack and the 2552 // JS stack. This test therefore fails on the simulator. The test is 2553 // not threaded to allow the threading tests to run on the simulator. 2554 TEST(TryCatchInTryFinally) { 2555 v8::HandleScope scope; 2556 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2557 templ->Set(v8_str("CCatcher"), 2558 v8::FunctionTemplate::New(CCatcher)); 2559 LocalContext context(0, templ); 2560 Local<Value> result = CompileRun("try {" 2561 " try {" 2562 " CCatcher('throw 7;');" 2563 " } finally {" 2564 " }" 2565 "} catch (e) {" 2566 "}"); 2567 CHECK(result->IsTrue()); 2568 } 2569 2570 2571 static void check_reference_error_message( 2572 v8::Handle<v8::Message> message, 2573 v8::Handle<v8::Value> data) { 2574 const char* reference_error = "Uncaught ReferenceError: asdf is not defined"; 2575 CHECK(message->Get()->Equals(v8_str(reference_error))); 2576 } 2577 2578 2579 static v8::Handle<Value> Fail(const v8::Arguments& args) { 2580 ApiTestFuzzer::Fuzz(); 2581 CHECK(false); 2582 return v8::Undefined(); 2583 } 2584 2585 2586 // Test that overwritten methods are not invoked on uncaught exception 2587 // formatting. However, they are invoked when performing normal error 2588 // string conversions. 2589 TEST(APIThrowMessageOverwrittenToString) { 2590 v8::HandleScope scope; 2591 v8::V8::AddMessageListener(check_reference_error_message); 2592 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2593 templ->Set(v8_str("fail"), v8::FunctionTemplate::New(Fail)); 2594 LocalContext context(NULL, templ); 2595 CompileRun("asdf;"); 2596 CompileRun("var limit = {};" 2597 "limit.valueOf = fail;" 2598 "Error.stackTraceLimit = limit;"); 2599 CompileRun("asdf"); 2600 CompileRun("Array.prototype.pop = fail;"); 2601 CompileRun("Object.prototype.hasOwnProperty = fail;"); 2602 CompileRun("Object.prototype.toString = function f() { return 'Yikes'; }"); 2603 CompileRun("Number.prototype.toString = function f() { return 'Yikes'; }"); 2604 CompileRun("String.prototype.toString = function f() { return 'Yikes'; }"); 2605 CompileRun("ReferenceError.prototype.toString =" 2606 " function() { return 'Whoops' }"); 2607 CompileRun("asdf;"); 2608 CompileRun("ReferenceError.prototype.constructor.name = void 0;"); 2609 CompileRun("asdf;"); 2610 CompileRun("ReferenceError.prototype.constructor = void 0;"); 2611 CompileRun("asdf;"); 2612 CompileRun("ReferenceError.prototype.__proto__ = new Object();"); 2613 CompileRun("asdf;"); 2614 CompileRun("ReferenceError.prototype = new Object();"); 2615 CompileRun("asdf;"); 2616 v8::Handle<Value> string = CompileRun("try { asdf; } catch(e) { e + ''; }"); 2617 CHECK(string->Equals(v8_str("Whoops"))); 2618 CompileRun("ReferenceError.prototype.constructor = new Object();" 2619 "ReferenceError.prototype.constructor.name = 1;" 2620 "Number.prototype.toString = function() { return 'Whoops'; };" 2621 "ReferenceError.prototype.toString = Object.prototype.toString;"); 2622 CompileRun("asdf;"); 2623 v8::V8::RemoveMessageListeners(check_message); 2624 } 2625 2626 2627 static void receive_message(v8::Handle<v8::Message> message, 2628 v8::Handle<v8::Value> data) { 2629 message->Get(); 2630 message_received = true; 2631 } 2632 2633 2634 TEST(APIThrowMessage) { 2635 message_received = false; 2636 v8::HandleScope scope; 2637 v8::V8::AddMessageListener(receive_message); 2638 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2639 templ->Set(v8_str("ThrowFromC"), 2640 v8::FunctionTemplate::New(ThrowFromC)); 2641 LocalContext context(0, templ); 2642 CompileRun("ThrowFromC();"); 2643 CHECK(message_received); 2644 v8::V8::RemoveMessageListeners(check_message); 2645 } 2646 2647 2648 TEST(APIThrowMessageAndVerboseTryCatch) { 2649 message_received = false; 2650 v8::HandleScope scope; 2651 v8::V8::AddMessageListener(receive_message); 2652 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2653 templ->Set(v8_str("ThrowFromC"), 2654 v8::FunctionTemplate::New(ThrowFromC)); 2655 LocalContext context(0, templ); 2656 v8::TryCatch try_catch; 2657 try_catch.SetVerbose(true); 2658 Local<Value> result = CompileRun("ThrowFromC();"); 2659 CHECK(try_catch.HasCaught()); 2660 CHECK(result.IsEmpty()); 2661 CHECK(message_received); 2662 v8::V8::RemoveMessageListeners(check_message); 2663 } 2664 2665 2666 TEST(APIStackOverflowAndVerboseTryCatch) { 2667 message_received = false; 2668 v8::HandleScope scope; 2669 v8::V8::AddMessageListener(receive_message); 2670 LocalContext context; 2671 v8::TryCatch try_catch; 2672 try_catch.SetVerbose(true); 2673 Local<Value> result = CompileRun("function foo() { foo(); } foo();"); 2674 CHECK(try_catch.HasCaught()); 2675 CHECK(result.IsEmpty()); 2676 CHECK(message_received); 2677 v8::V8::RemoveMessageListeners(receive_message); 2678 } 2679 2680 2681 THREADED_TEST(ExternalScriptException) { 2682 v8::HandleScope scope; 2683 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2684 templ->Set(v8_str("ThrowFromC"), 2685 v8::FunctionTemplate::New(ThrowFromC)); 2686 LocalContext context(0, templ); 2687 2688 v8::TryCatch try_catch; 2689 Local<Script> script 2690 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';")); 2691 Local<Value> result = script->Run(); 2692 CHECK(result.IsEmpty()); 2693 CHECK(try_catch.HasCaught()); 2694 String::AsciiValue exception_value(try_catch.Exception()); 2695 CHECK_EQ("konto", *exception_value); 2696 } 2697 2698 2699 2700 v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) { 2701 ApiTestFuzzer::Fuzz(); 2702 CHECK_EQ(4, args.Length()); 2703 int count = args[0]->Int32Value(); 2704 int cInterval = args[2]->Int32Value(); 2705 if (count == 0) { 2706 return v8::ThrowException(v8_str("FromC")); 2707 } else { 2708 Local<v8::Object> global = Context::GetCurrent()->Global(); 2709 Local<Value> fun = global->Get(v8_str("JSThrowCountDown")); 2710 v8::Handle<Value> argv[] = { v8_num(count - 1), 2711 args[1], 2712 args[2], 2713 args[3] }; 2714 if (count % cInterval == 0) { 2715 v8::TryCatch try_catch; 2716 Local<Value> result = fun.As<Function>()->Call(global, 4, argv); 2717 int expected = args[3]->Int32Value(); 2718 if (try_catch.HasCaught()) { 2719 CHECK_EQ(expected, count); 2720 CHECK(result.IsEmpty()); 2721 CHECK(!i::Isolate::Current()->has_scheduled_exception()); 2722 } else { 2723 CHECK_NE(expected, count); 2724 } 2725 return result; 2726 } else { 2727 return fun.As<Function>()->Call(global, 4, argv); 2728 } 2729 } 2730 } 2731 2732 2733 v8::Handle<Value> JSCheck(const v8::Arguments& args) { 2734 ApiTestFuzzer::Fuzz(); 2735 CHECK_EQ(3, args.Length()); 2736 bool equality = args[0]->BooleanValue(); 2737 int count = args[1]->Int32Value(); 2738 int expected = args[2]->Int32Value(); 2739 if (equality) { 2740 CHECK_EQ(count, expected); 2741 } else { 2742 CHECK_NE(count, expected); 2743 } 2744 return v8::Undefined(); 2745 } 2746 2747 2748 THREADED_TEST(EvalInTryFinally) { 2749 v8::HandleScope scope; 2750 LocalContext context; 2751 v8::TryCatch try_catch; 2752 CompileRun("(function() {" 2753 " try {" 2754 " eval('asldkf (*&^&*^');" 2755 " } finally {" 2756 " return;" 2757 " }" 2758 "})()"); 2759 CHECK(!try_catch.HasCaught()); 2760 } 2761 2762 2763 // This test works by making a stack of alternating JavaScript and C 2764 // activations. These activations set up exception handlers with regular 2765 // intervals, one interval for C activations and another for JavaScript 2766 // activations. When enough activations have been created an exception is 2767 // thrown and we check that the right activation catches the exception and that 2768 // no other activations do. The right activation is always the topmost one with 2769 // a handler, regardless of whether it is in JavaScript or C. 2770 // 2771 // The notation used to describe a test case looks like this: 2772 // 2773 // *JS[4] *C[3] @JS[2] C[1] JS[0] 2774 // 2775 // Each entry is an activation, either JS or C. The index is the count at that 2776 // level. Stars identify activations with exception handlers, the @ identifies 2777 // the exception handler that should catch the exception. 2778 // 2779 // BUG(271): Some of the exception propagation does not work on the 2780 // ARM simulator because the simulator separates the C++ stack and the 2781 // JS stack. This test therefore fails on the simulator. The test is 2782 // not threaded to allow the threading tests to run on the simulator. 2783 TEST(ExceptionOrder) { 2784 v8::HandleScope scope; 2785 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2786 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck)); 2787 templ->Set(v8_str("CThrowCountDown"), 2788 v8::FunctionTemplate::New(CThrowCountDown)); 2789 LocalContext context(0, templ); 2790 CompileRun( 2791 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {" 2792 " if (count == 0) throw 'FromJS';" 2793 " if (count % jsInterval == 0) {" 2794 " try {" 2795 " var value = CThrowCountDown(count - 1," 2796 " jsInterval," 2797 " cInterval," 2798 " expected);" 2799 " check(false, count, expected);" 2800 " return value;" 2801 " } catch (e) {" 2802 " check(true, count, expected);" 2803 " }" 2804 " } else {" 2805 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);" 2806 " }" 2807 "}"); 2808 Local<Function> fun = 2809 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown"))); 2810 2811 const int argc = 4; 2812 // count jsInterval cInterval expected 2813 2814 // *JS[4] *C[3] @JS[2] C[1] JS[0] 2815 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) }; 2816 fun->Call(fun, argc, a0); 2817 2818 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0] 2819 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) }; 2820 fun->Call(fun, argc, a1); 2821 2822 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0] 2823 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) }; 2824 fun->Call(fun, argc, a2); 2825 2826 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0] 2827 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) }; 2828 fun->Call(fun, argc, a3); 2829 2830 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0] 2831 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) }; 2832 fun->Call(fun, argc, a4); 2833 2834 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0] 2835 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) }; 2836 fun->Call(fun, argc, a5); 2837 } 2838 2839 2840 v8::Handle<Value> ThrowValue(const v8::Arguments& args) { 2841 ApiTestFuzzer::Fuzz(); 2842 CHECK_EQ(1, args.Length()); 2843 return v8::ThrowException(args[0]); 2844 } 2845 2846 2847 THREADED_TEST(ThrowValues) { 2848 v8::HandleScope scope; 2849 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2850 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue)); 2851 LocalContext context(0, templ); 2852 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 2853 "function Run(obj) {" 2854 " try {" 2855 " Throw(obj);" 2856 " } catch (e) {" 2857 " return e;" 2858 " }" 2859 " return 'no exception';" 2860 "}" 2861 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];")); 2862 CHECK_EQ(5, result->Length()); 2863 CHECK(result->Get(v8::Integer::New(0))->IsString()); 2864 CHECK(result->Get(v8::Integer::New(1))->IsNumber()); 2865 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value()); 2866 CHECK(result->Get(v8::Integer::New(2))->IsNumber()); 2867 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value()); 2868 CHECK(result->Get(v8::Integer::New(3))->IsNull()); 2869 CHECK(result->Get(v8::Integer::New(4))->IsUndefined()); 2870 } 2871 2872 2873 THREADED_TEST(CatchZero) { 2874 v8::HandleScope scope; 2875 LocalContext context; 2876 v8::TryCatch try_catch; 2877 CHECK(!try_catch.HasCaught()); 2878 Script::Compile(v8_str("throw 10"))->Run(); 2879 CHECK(try_catch.HasCaught()); 2880 CHECK_EQ(10, try_catch.Exception()->Int32Value()); 2881 try_catch.Reset(); 2882 CHECK(!try_catch.HasCaught()); 2883 Script::Compile(v8_str("throw 0"))->Run(); 2884 CHECK(try_catch.HasCaught()); 2885 CHECK_EQ(0, try_catch.Exception()->Int32Value()); 2886 } 2887 2888 2889 THREADED_TEST(CatchExceptionFromWith) { 2890 v8::HandleScope scope; 2891 LocalContext context; 2892 v8::TryCatch try_catch; 2893 CHECK(!try_catch.HasCaught()); 2894 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run(); 2895 CHECK(try_catch.HasCaught()); 2896 } 2897 2898 2899 THREADED_TEST(TryCatchAndFinallyHidingException) { 2900 v8::HandleScope scope; 2901 LocalContext context; 2902 v8::TryCatch try_catch; 2903 CHECK(!try_catch.HasCaught()); 2904 CompileRun("function f(k) { try { this[k]; } finally { return 0; } };"); 2905 CompileRun("f({toString: function() { throw 42; }});"); 2906 CHECK(!try_catch.HasCaught()); 2907 } 2908 2909 2910 v8::Handle<v8::Value> WithTryCatch(const v8::Arguments& args) { 2911 v8::TryCatch try_catch; 2912 return v8::Undefined(); 2913 } 2914 2915 2916 THREADED_TEST(TryCatchAndFinally) { 2917 v8::HandleScope scope; 2918 LocalContext context; 2919 context->Global()->Set( 2920 v8_str("native_with_try_catch"), 2921 v8::FunctionTemplate::New(WithTryCatch)->GetFunction()); 2922 v8::TryCatch try_catch; 2923 CHECK(!try_catch.HasCaught()); 2924 CompileRun( 2925 "try {\n" 2926 " throw new Error('a');\n" 2927 "} finally {\n" 2928 " native_with_try_catch();\n" 2929 "}\n"); 2930 CHECK(try_catch.HasCaught()); 2931 } 2932 2933 2934 THREADED_TEST(Equality) { 2935 v8::HandleScope scope; 2936 LocalContext context; 2937 // Check that equality works at all before relying on CHECK_EQ 2938 CHECK(v8_str("a")->Equals(v8_str("a"))); 2939 CHECK(!v8_str("a")->Equals(v8_str("b"))); 2940 2941 CHECK_EQ(v8_str("a"), v8_str("a")); 2942 CHECK_NE(v8_str("a"), v8_str("b")); 2943 CHECK_EQ(v8_num(1), v8_num(1)); 2944 CHECK_EQ(v8_num(1.00), v8_num(1)); 2945 CHECK_NE(v8_num(1), v8_num(2)); 2946 2947 // Assume String is not symbol. 2948 CHECK(v8_str("a")->StrictEquals(v8_str("a"))); 2949 CHECK(!v8_str("a")->StrictEquals(v8_str("b"))); 2950 CHECK(!v8_str("5")->StrictEquals(v8_num(5))); 2951 CHECK(v8_num(1)->StrictEquals(v8_num(1))); 2952 CHECK(!v8_num(1)->StrictEquals(v8_num(2))); 2953 CHECK(v8_num(0)->StrictEquals(v8_num(-0))); 2954 Local<Value> not_a_number = v8_num(i::OS::nan_value()); 2955 CHECK(!not_a_number->StrictEquals(not_a_number)); 2956 CHECK(v8::False()->StrictEquals(v8::False())); 2957 CHECK(!v8::False()->StrictEquals(v8::Undefined())); 2958 2959 v8::Handle<v8::Object> obj = v8::Object::New(); 2960 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj); 2961 CHECK(alias->StrictEquals(obj)); 2962 alias.Dispose(); 2963 } 2964 2965 2966 THREADED_TEST(MultiRun) { 2967 v8::HandleScope scope; 2968 LocalContext context; 2969 Local<Script> script = Script::Compile(v8_str("x")); 2970 for (int i = 0; i < 10; i++) 2971 script->Run(); 2972 } 2973 2974 2975 static v8::Handle<Value> GetXValue(Local<String> name, 2976 const AccessorInfo& info) { 2977 ApiTestFuzzer::Fuzz(); 2978 CHECK_EQ(info.Data(), v8_str("donut")); 2979 CHECK_EQ(name, v8_str("x")); 2980 return name; 2981 } 2982 2983 2984 THREADED_TEST(SimplePropertyRead) { 2985 v8::HandleScope scope; 2986 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2987 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2988 LocalContext context; 2989 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2990 Local<Script> script = Script::Compile(v8_str("obj.x")); 2991 for (int i = 0; i < 10; i++) { 2992 Local<Value> result = script->Run(); 2993 CHECK_EQ(result, v8_str("x")); 2994 } 2995 } 2996 2997 THREADED_TEST(DefinePropertyOnAPIAccessor) { 2998 v8::HandleScope scope; 2999 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3000 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 3001 LocalContext context; 3002 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3003 3004 // Uses getOwnPropertyDescriptor to check the configurable status 3005 Local<Script> script_desc 3006 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( " 3007 "obj, 'x');" 3008 "prop.configurable;")); 3009 Local<Value> result = script_desc->Run(); 3010 CHECK_EQ(result->BooleanValue(), true); 3011 3012 // Redefine get - but still configurable 3013 Local<Script> script_define 3014 = Script::Compile(v8_str("var desc = { get: function(){return 42; }," 3015 " configurable: true };" 3016 "Object.defineProperty(obj, 'x', desc);" 3017 "obj.x")); 3018 result = script_define->Run(); 3019 CHECK_EQ(result, v8_num(42)); 3020 3021 // Check that the accessor is still configurable 3022 result = script_desc->Run(); 3023 CHECK_EQ(result->BooleanValue(), true); 3024 3025 // Redefine to a non-configurable 3026 script_define 3027 = Script::Compile(v8_str("var desc = { get: function(){return 43; }," 3028 " configurable: false };" 3029 "Object.defineProperty(obj, 'x', desc);" 3030 "obj.x")); 3031 result = script_define->Run(); 3032 CHECK_EQ(result, v8_num(43)); 3033 result = script_desc->Run(); 3034 CHECK_EQ(result->BooleanValue(), false); 3035 3036 // Make sure that it is not possible to redefine again 3037 v8::TryCatch try_catch; 3038 result = script_define->Run(); 3039 CHECK(try_catch.HasCaught()); 3040 String::AsciiValue exception_value(try_catch.Exception()); 3041 CHECK_EQ(*exception_value, 3042 "TypeError: Cannot redefine property: defineProperty"); 3043 } 3044 3045 THREADED_TEST(DefinePropertyOnDefineGetterSetter) { 3046 v8::HandleScope scope; 3047 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3048 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 3049 LocalContext context; 3050 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3051 3052 Local<Script> script_desc = Script::Compile(v8_str("var prop =" 3053 "Object.getOwnPropertyDescriptor( " 3054 "obj, 'x');" 3055 "prop.configurable;")); 3056 Local<Value> result = script_desc->Run(); 3057 CHECK_EQ(result->BooleanValue(), true); 3058 3059 Local<Script> script_define = 3060 Script::Compile(v8_str("var desc = {get: function(){return 42; }," 3061 " configurable: true };" 3062 "Object.defineProperty(obj, 'x', desc);" 3063 "obj.x")); 3064 result = script_define->Run(); 3065 CHECK_EQ(result, v8_num(42)); 3066 3067 3068 result = script_desc->Run(); 3069 CHECK_EQ(result->BooleanValue(), true); 3070 3071 3072 script_define = 3073 Script::Compile(v8_str("var desc = {get: function(){return 43; }," 3074 " configurable: false };" 3075 "Object.defineProperty(obj, 'x', desc);" 3076 "obj.x")); 3077 result = script_define->Run(); 3078 CHECK_EQ(result, v8_num(43)); 3079 result = script_desc->Run(); 3080 3081 CHECK_EQ(result->BooleanValue(), false); 3082 3083 v8::TryCatch try_catch; 3084 result = script_define->Run(); 3085 CHECK(try_catch.HasCaught()); 3086 String::AsciiValue exception_value(try_catch.Exception()); 3087 CHECK_EQ(*exception_value, 3088 "TypeError: Cannot redefine property: defineProperty"); 3089 } 3090 3091 3092 static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context, 3093 char const* name) { 3094 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name))); 3095 } 3096 3097 3098 THREADED_TEST(DefineAPIAccessorOnObject) { 3099 v8::HandleScope scope; 3100 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3101 LocalContext context; 3102 3103 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 3104 CompileRun("var obj2 = {};"); 3105 3106 CHECK(CompileRun("obj1.x")->IsUndefined()); 3107 CHECK(CompileRun("obj2.x")->IsUndefined()); 3108 3109 CHECK(GetGlobalProperty(&context, "obj1")-> 3110 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3111 3112 ExpectString("obj1.x", "x"); 3113 CHECK(CompileRun("obj2.x")->IsUndefined()); 3114 3115 CHECK(GetGlobalProperty(&context, "obj2")-> 3116 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3117 3118 ExpectString("obj1.x", "x"); 3119 ExpectString("obj2.x", "x"); 3120 3121 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 3122 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 3123 3124 CompileRun("Object.defineProperty(obj1, 'x'," 3125 "{ get: function() { return 'y'; }, configurable: true })"); 3126 3127 ExpectString("obj1.x", "y"); 3128 ExpectString("obj2.x", "x"); 3129 3130 CompileRun("Object.defineProperty(obj2, 'x'," 3131 "{ get: function() { return 'y'; }, configurable: true })"); 3132 3133 ExpectString("obj1.x", "y"); 3134 ExpectString("obj2.x", "y"); 3135 3136 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 3137 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 3138 3139 CHECK(GetGlobalProperty(&context, "obj1")-> 3140 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3141 CHECK(GetGlobalProperty(&context, "obj2")-> 3142 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3143 3144 ExpectString("obj1.x", "x"); 3145 ExpectString("obj2.x", "x"); 3146 3147 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 3148 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 3149 3150 // Define getters/setters, but now make them not configurable. 3151 CompileRun("Object.defineProperty(obj1, 'x'," 3152 "{ get: function() { return 'z'; }, configurable: false })"); 3153 CompileRun("Object.defineProperty(obj2, 'x'," 3154 "{ get: function() { return 'z'; }, configurable: false })"); 3155 3156 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 3157 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 3158 3159 ExpectString("obj1.x", "z"); 3160 ExpectString("obj2.x", "z"); 3161 3162 CHECK(!GetGlobalProperty(&context, "obj1")-> 3163 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3164 CHECK(!GetGlobalProperty(&context, "obj2")-> 3165 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3166 3167 ExpectString("obj1.x", "z"); 3168 ExpectString("obj2.x", "z"); 3169 } 3170 3171 3172 THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) { 3173 v8::HandleScope scope; 3174 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3175 LocalContext context; 3176 3177 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 3178 CompileRun("var obj2 = {};"); 3179 3180 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 3181 v8_str("x"), 3182 GetXValue, NULL, 3183 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 3184 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 3185 v8_str("x"), 3186 GetXValue, NULL, 3187 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 3188 3189 ExpectString("obj1.x", "x"); 3190 ExpectString("obj2.x", "x"); 3191 3192 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 3193 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 3194 3195 CHECK(!GetGlobalProperty(&context, "obj1")-> 3196 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3197 CHECK(!GetGlobalProperty(&context, "obj2")-> 3198 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3199 3200 { 3201 v8::TryCatch try_catch; 3202 CompileRun("Object.defineProperty(obj1, 'x'," 3203 "{get: function() { return 'func'; }})"); 3204 CHECK(try_catch.HasCaught()); 3205 String::AsciiValue exception_value(try_catch.Exception()); 3206 CHECK_EQ(*exception_value, 3207 "TypeError: Cannot redefine property: defineProperty"); 3208 } 3209 { 3210 v8::TryCatch try_catch; 3211 CompileRun("Object.defineProperty(obj2, 'x'," 3212 "{get: function() { return 'func'; }})"); 3213 CHECK(try_catch.HasCaught()); 3214 String::AsciiValue exception_value(try_catch.Exception()); 3215 CHECK_EQ(*exception_value, 3216 "TypeError: Cannot redefine property: defineProperty"); 3217 } 3218 } 3219 3220 3221 static v8::Handle<Value> Get239Value(Local<String> name, 3222 const AccessorInfo& info) { 3223 ApiTestFuzzer::Fuzz(); 3224 CHECK_EQ(info.Data(), v8_str("donut")); 3225 CHECK_EQ(name, v8_str("239")); 3226 return name; 3227 } 3228 3229 3230 THREADED_TEST(ElementAPIAccessor) { 3231 v8::HandleScope scope; 3232 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3233 LocalContext context; 3234 3235 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 3236 CompileRun("var obj2 = {};"); 3237 3238 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 3239 v8_str("239"), 3240 Get239Value, NULL, 3241 v8_str("donut"))); 3242 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 3243 v8_str("239"), 3244 Get239Value, NULL, 3245 v8_str("donut"))); 3246 3247 ExpectString("obj1[239]", "239"); 3248 ExpectString("obj2[239]", "239"); 3249 ExpectString("obj1['239']", "239"); 3250 ExpectString("obj2['239']", "239"); 3251 } 3252 3253 3254 v8::Persistent<Value> xValue; 3255 3256 3257 static void SetXValue(Local<String> name, 3258 Local<Value> value, 3259 const AccessorInfo& info) { 3260 CHECK_EQ(value, v8_num(4)); 3261 CHECK_EQ(info.Data(), v8_str("donut")); 3262 CHECK_EQ(name, v8_str("x")); 3263 CHECK(xValue.IsEmpty()); 3264 xValue = v8::Persistent<Value>::New(value); 3265 } 3266 3267 3268 THREADED_TEST(SimplePropertyWrite) { 3269 v8::HandleScope scope; 3270 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3271 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut")); 3272 LocalContext context; 3273 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3274 Local<Script> script = Script::Compile(v8_str("obj.x = 4")); 3275 for (int i = 0; i < 10; i++) { 3276 CHECK(xValue.IsEmpty()); 3277 script->Run(); 3278 CHECK_EQ(v8_num(4), xValue); 3279 xValue.Dispose(); 3280 xValue = v8::Persistent<Value>(); 3281 } 3282 } 3283 3284 3285 static v8::Handle<Value> XPropertyGetter(Local<String> property, 3286 const AccessorInfo& info) { 3287 ApiTestFuzzer::Fuzz(); 3288 CHECK(info.Data()->IsUndefined()); 3289 return property; 3290 } 3291 3292 3293 THREADED_TEST(NamedInterceptorPropertyRead) { 3294 v8::HandleScope scope; 3295 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3296 templ->SetNamedPropertyHandler(XPropertyGetter); 3297 LocalContext context; 3298 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3299 Local<Script> script = Script::Compile(v8_str("obj.x")); 3300 for (int i = 0; i < 10; i++) { 3301 Local<Value> result = script->Run(); 3302 CHECK_EQ(result, v8_str("x")); 3303 } 3304 } 3305 3306 3307 THREADED_TEST(NamedInterceptorDictionaryIC) { 3308 v8::HandleScope scope; 3309 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3310 templ->SetNamedPropertyHandler(XPropertyGetter); 3311 LocalContext context; 3312 // Create an object with a named interceptor. 3313 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance()); 3314 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x")); 3315 for (int i = 0; i < 10; i++) { 3316 Local<Value> result = script->Run(); 3317 CHECK_EQ(result, v8_str("x")); 3318 } 3319 // Create a slow case object and a function accessing a property in 3320 // that slow case object (with dictionary probing in generated 3321 // code). Then force object with a named interceptor into slow-case, 3322 // pass it to the function, and check that the interceptor is called 3323 // instead of accessing the local property. 3324 Local<Value> result = 3325 CompileRun("function get_x(o) { return o.x; };" 3326 "var obj = { x : 42, y : 0 };" 3327 "delete obj.y;" 3328 "for (var i = 0; i < 10; i++) get_x(obj);" 3329 "interceptor_obj.x = 42;" 3330 "interceptor_obj.y = 10;" 3331 "delete interceptor_obj.y;" 3332 "get_x(interceptor_obj)"); 3333 CHECK_EQ(result, v8_str("x")); 3334 } 3335 3336 3337 THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) { 3338 v8::HandleScope scope; 3339 3340 v8::Persistent<Context> context1 = Context::New(); 3341 3342 context1->Enter(); 3343 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3344 templ->SetNamedPropertyHandler(XPropertyGetter); 3345 // Create an object with a named interceptor. 3346 v8::Local<v8::Object> object = templ->NewInstance(); 3347 context1->Global()->Set(v8_str("interceptor_obj"), object); 3348 3349 // Force the object into the slow case. 3350 CompileRun("interceptor_obj.y = 0;" 3351 "delete interceptor_obj.y;"); 3352 context1->Exit(); 3353 3354 { 3355 // Introduce the object into a different context. 3356 // Repeat named loads to exercise ICs. 3357 LocalContext context2; 3358 context2->Global()->Set(v8_str("interceptor_obj"), object); 3359 Local<Value> result = 3360 CompileRun("function get_x(o) { return o.x; }" 3361 "interceptor_obj.x = 42;" 3362 "for (var i=0; i != 10; i++) {" 3363 " get_x(interceptor_obj);" 3364 "}" 3365 "get_x(interceptor_obj)"); 3366 // Check that the interceptor was actually invoked. 3367 CHECK_EQ(result, v8_str("x")); 3368 } 3369 3370 // Return to the original context and force some object to the slow case 3371 // to cause the NormalizedMapCache to verify. 3372 context1->Enter(); 3373 CompileRun("var obj = { x : 0 }; delete obj.x;"); 3374 context1->Exit(); 3375 3376 context1.Dispose(); 3377 } 3378 3379 3380 static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property, 3381 const AccessorInfo& info) { 3382 // Set x on the prototype object and do not handle the get request. 3383 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype(); 3384 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23)); 3385 return v8::Handle<Value>(); 3386 } 3387 3388 3389 // This is a regression test for http://crbug.com/20104. Map 3390 // transitions should not interfere with post interceptor lookup. 3391 THREADED_TEST(NamedInterceptorMapTransitionRead) { 3392 v8::HandleScope scope; 3393 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(); 3394 Local<v8::ObjectTemplate> instance_template 3395 = function_template->InstanceTemplate(); 3396 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter); 3397 LocalContext context; 3398 context->Global()->Set(v8_str("F"), function_template->GetFunction()); 3399 // Create an instance of F and introduce a map transition for x. 3400 CompileRun("var o = new F(); o.x = 23;"); 3401 // Create an instance of F and invoke the getter. The result should be 23. 3402 Local<Value> result = CompileRun("o = new F(); o.x"); 3403 CHECK_EQ(result->Int32Value(), 23); 3404 } 3405 3406 3407 static v8::Handle<Value> IndexedPropertyGetter(uint32_t index, 3408 const AccessorInfo& info) { 3409 ApiTestFuzzer::Fuzz(); 3410 if (index == 37) { 3411 return v8::Handle<Value>(v8_num(625)); 3412 } 3413 return v8::Handle<Value>(); 3414 } 3415 3416 3417 static v8::Handle<Value> IndexedPropertySetter(uint32_t index, 3418 Local<Value> value, 3419 const AccessorInfo& info) { 3420 ApiTestFuzzer::Fuzz(); 3421 if (index == 39) { 3422 return value; 3423 } 3424 return v8::Handle<Value>(); 3425 } 3426 3427 3428 THREADED_TEST(IndexedInterceptorWithIndexedAccessor) { 3429 v8::HandleScope scope; 3430 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3431 templ->SetIndexedPropertyHandler(IndexedPropertyGetter, 3432 IndexedPropertySetter); 3433 LocalContext context; 3434 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3435 Local<Script> getter_script = Script::Compile(v8_str( 3436 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];")); 3437 Local<Script> setter_script = Script::Compile(v8_str( 3438 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});" 3439 "obj[17] = 23;" 3440 "obj.foo;")); 3441 Local<Script> interceptor_setter_script = Script::Compile(v8_str( 3442 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});" 3443 "obj[39] = 47;" 3444 "obj.foo;")); // This setter should not run, due to the interceptor. 3445 Local<Script> interceptor_getter_script = Script::Compile(v8_str( 3446 "obj[37];")); 3447 Local<Value> result = getter_script->Run(); 3448 CHECK_EQ(v8_num(5), result); 3449 result = setter_script->Run(); 3450 CHECK_EQ(v8_num(23), result); 3451 result = interceptor_setter_script->Run(); 3452 CHECK_EQ(v8_num(23), result); 3453 result = interceptor_getter_script->Run(); 3454 CHECK_EQ(v8_num(625), result); 3455 } 3456 3457 3458 static v8::Handle<Value> IdentityIndexedPropertyGetter( 3459 uint32_t index, 3460 const AccessorInfo& info) { 3461 return v8::Integer::NewFromUnsigned(index); 3462 } 3463 3464 3465 THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) { 3466 v8::HandleScope scope; 3467 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3468 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3469 3470 LocalContext context; 3471 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3472 3473 // Check fast object case. 3474 const char* fast_case_code = 3475 "Object.getOwnPropertyDescriptor(obj, 0).value.toString()"; 3476 ExpectString(fast_case_code, "0"); 3477 3478 // Check slow case. 3479 const char* slow_case_code = 3480 "obj.x = 1; delete obj.x;" 3481 "Object.getOwnPropertyDescriptor(obj, 1).value.toString()"; 3482 ExpectString(slow_case_code, "1"); 3483 } 3484 3485 3486 THREADED_TEST(IndexedInterceptorWithNoSetter) { 3487 v8::HandleScope scope; 3488 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3489 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3490 3491 LocalContext context; 3492 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3493 3494 const char* code = 3495 "try {" 3496 " obj[0] = 239;" 3497 " for (var i = 0; i < 100; i++) {" 3498 " var v = obj[0];" 3499 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;" 3500 " }" 3501 " 'PASSED'" 3502 "} catch(e) {" 3503 " e" 3504 "}"; 3505 ExpectString(code, "PASSED"); 3506 } 3507 3508 3509 THREADED_TEST(IndexedInterceptorWithAccessorCheck) { 3510 v8::HandleScope scope; 3511 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3512 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3513 3514 LocalContext context; 3515 Local<v8::Object> obj = templ->NewInstance(); 3516 obj->TurnOnAccessCheck(); 3517 context->Global()->Set(v8_str("obj"), obj); 3518 3519 const char* code = 3520 "try {" 3521 " for (var i = 0; i < 100; i++) {" 3522 " var v = obj[0];" 3523 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;" 3524 " }" 3525 " 'PASSED'" 3526 "} catch(e) {" 3527 " e" 3528 "}"; 3529 ExpectString(code, "PASSED"); 3530 } 3531 3532 3533 THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) { 3534 i::FLAG_allow_natives_syntax = true; 3535 v8::HandleScope scope; 3536 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3537 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3538 3539 LocalContext context; 3540 Local<v8::Object> obj = templ->NewInstance(); 3541 context->Global()->Set(v8_str("obj"), obj); 3542 3543 const char* code = 3544 "try {" 3545 " for (var i = 0; i < 100; i++) {" 3546 " var expected = i;" 3547 " if (i == 5) {" 3548 " %EnableAccessChecks(obj);" 3549 " expected = undefined;" 3550 " }" 3551 " var v = obj[i];" 3552 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3553 " if (i == 5) %DisableAccessChecks(obj);" 3554 " }" 3555 " 'PASSED'" 3556 "} catch(e) {" 3557 " e" 3558 "}"; 3559 ExpectString(code, "PASSED"); 3560 } 3561 3562 3563 THREADED_TEST(IndexedInterceptorWithDifferentIndices) { 3564 v8::HandleScope scope; 3565 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3566 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3567 3568 LocalContext context; 3569 Local<v8::Object> obj = templ->NewInstance(); 3570 context->Global()->Set(v8_str("obj"), obj); 3571 3572 const char* code = 3573 "try {" 3574 " for (var i = 0; i < 100; i++) {" 3575 " var v = obj[i];" 3576 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 3577 " }" 3578 " 'PASSED'" 3579 "} catch(e) {" 3580 " e" 3581 "}"; 3582 ExpectString(code, "PASSED"); 3583 } 3584 3585 3586 THREADED_TEST(IndexedInterceptorWithNegativeIndices) { 3587 v8::HandleScope scope; 3588 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3589 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3590 3591 LocalContext context; 3592 Local<v8::Object> obj = templ->NewInstance(); 3593 context->Global()->Set(v8_str("obj"), obj); 3594 3595 const char* code = 3596 "try {" 3597 " for (var i = 0; i < 100; i++) {" 3598 " var expected = i;" 3599 " var key = i;" 3600 " if (i == 25) {" 3601 " key = -1;" 3602 " expected = undefined;" 3603 " }" 3604 " if (i == 50) {" 3605 " /* probe minimal Smi number on 32-bit platforms */" 3606 " key = -(1 << 30);" 3607 " expected = undefined;" 3608 " }" 3609 " if (i == 75) {" 3610 " /* probe minimal Smi number on 64-bit platforms */" 3611 " key = 1 << 31;" 3612 " expected = undefined;" 3613 " }" 3614 " var v = obj[key];" 3615 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3616 " }" 3617 " 'PASSED'" 3618 "} catch(e) {" 3619 " e" 3620 "}"; 3621 ExpectString(code, "PASSED"); 3622 } 3623 3624 3625 THREADED_TEST(IndexedInterceptorWithNotSmiLookup) { 3626 v8::HandleScope scope; 3627 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3628 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3629 3630 LocalContext context; 3631 Local<v8::Object> obj = templ->NewInstance(); 3632 context->Global()->Set(v8_str("obj"), obj); 3633 3634 const char* code = 3635 "try {" 3636 " for (var i = 0; i < 100; i++) {" 3637 " var expected = i;" 3638 " var key = i;" 3639 " if (i == 50) {" 3640 " key = 'foobar';" 3641 " expected = undefined;" 3642 " }" 3643 " var v = obj[key];" 3644 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3645 " }" 3646 " 'PASSED'" 3647 "} catch(e) {" 3648 " e" 3649 "}"; 3650 ExpectString(code, "PASSED"); 3651 } 3652 3653 3654 THREADED_TEST(IndexedInterceptorGoingMegamorphic) { 3655 v8::HandleScope scope; 3656 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3657 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3658 3659 LocalContext context; 3660 Local<v8::Object> obj = templ->NewInstance(); 3661 context->Global()->Set(v8_str("obj"), obj); 3662 3663 const char* code = 3664 "var original = obj;" 3665 "try {" 3666 " for (var i = 0; i < 100; i++) {" 3667 " var expected = i;" 3668 " if (i == 50) {" 3669 " obj = {50: 'foobar'};" 3670 " expected = 'foobar';" 3671 " }" 3672 " var v = obj[i];" 3673 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3674 " if (i == 50) obj = original;" 3675 " }" 3676 " 'PASSED'" 3677 "} catch(e) {" 3678 " e" 3679 "}"; 3680 ExpectString(code, "PASSED"); 3681 } 3682 3683 3684 THREADED_TEST(IndexedInterceptorReceiverTurningSmi) { 3685 v8::HandleScope scope; 3686 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3687 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3688 3689 LocalContext context; 3690 Local<v8::Object> obj = templ->NewInstance(); 3691 context->Global()->Set(v8_str("obj"), obj); 3692 3693 const char* code = 3694 "var original = obj;" 3695 "try {" 3696 " for (var i = 0; i < 100; i++) {" 3697 " var expected = i;" 3698 " if (i == 5) {" 3699 " obj = 239;" 3700 " expected = undefined;" 3701 " }" 3702 " var v = obj[i];" 3703 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3704 " if (i == 5) obj = original;" 3705 " }" 3706 " 'PASSED'" 3707 "} catch(e) {" 3708 " e" 3709 "}"; 3710 ExpectString(code, "PASSED"); 3711 } 3712 3713 3714 THREADED_TEST(IndexedInterceptorOnProto) { 3715 v8::HandleScope scope; 3716 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3717 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3718 3719 LocalContext context; 3720 Local<v8::Object> obj = templ->NewInstance(); 3721 context->Global()->Set(v8_str("obj"), obj); 3722 3723 const char* code = 3724 "var o = {__proto__: obj};" 3725 "try {" 3726 " for (var i = 0; i < 100; i++) {" 3727 " var v = o[i];" 3728 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 3729 " }" 3730 " 'PASSED'" 3731 "} catch(e) {" 3732 " e" 3733 "}"; 3734 ExpectString(code, "PASSED"); 3735 } 3736 3737 3738 THREADED_TEST(MultiContexts) { 3739 v8::HandleScope scope; 3740 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New(); 3741 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler)); 3742 3743 Local<String> password = v8_str("Password"); 3744 3745 // Create an environment 3746 LocalContext context0(0, templ); 3747 context0->SetSecurityToken(password); 3748 v8::Handle<v8::Object> global0 = context0->Global(); 3749 global0->Set(v8_str("custom"), v8_num(1234)); 3750 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 3751 3752 // Create an independent environment 3753 LocalContext context1(0, templ); 3754 context1->SetSecurityToken(password); 3755 v8::Handle<v8::Object> global1 = context1->Global(); 3756 global1->Set(v8_str("custom"), v8_num(1234)); 3757 CHECK_NE(global0, global1); 3758 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 3759 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value()); 3760 3761 // Now create a new context with the old global 3762 LocalContext context2(0, templ, global1); 3763 context2->SetSecurityToken(password); 3764 v8::Handle<v8::Object> global2 = context2->Global(); 3765 CHECK_EQ(global1, global2); 3766 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value()); 3767 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value()); 3768 } 3769 3770 3771 THREADED_TEST(FunctionPrototypeAcrossContexts) { 3772 // Make sure that functions created by cloning boilerplates cannot 3773 // communicate through their __proto__ field. 3774 3775 v8::HandleScope scope; 3776 3777 LocalContext env0; 3778 v8::Handle<v8::Object> global0 = 3779 env0->Global(); 3780 v8::Handle<v8::Object> object0 = 3781 global0->Get(v8_str("Object")).As<v8::Object>(); 3782 v8::Handle<v8::Object> tostring0 = 3783 object0->Get(v8_str("toString")).As<v8::Object>(); 3784 v8::Handle<v8::Object> proto0 = 3785 tostring0->Get(v8_str("__proto__")).As<v8::Object>(); 3786 proto0->Set(v8_str("custom"), v8_num(1234)); 3787 3788 LocalContext env1; 3789 v8::Handle<v8::Object> global1 = 3790 env1->Global(); 3791 v8::Handle<v8::Object> object1 = 3792 global1->Get(v8_str("Object")).As<v8::Object>(); 3793 v8::Handle<v8::Object> tostring1 = 3794 object1->Get(v8_str("toString")).As<v8::Object>(); 3795 v8::Handle<v8::Object> proto1 = 3796 tostring1->Get(v8_str("__proto__")).As<v8::Object>(); 3797 CHECK(!proto1->Has(v8_str("custom"))); 3798 } 3799 3800 3801 THREADED_TEST(Regress892105) { 3802 // Make sure that object and array literals created by cloning 3803 // boilerplates cannot communicate through their __proto__ 3804 // field. This is rather difficult to check, but we try to add stuff 3805 // to Object.prototype and Array.prototype and create a new 3806 // environment. This should succeed. 3807 3808 v8::HandleScope scope; 3809 3810 Local<String> source = v8_str("Object.prototype.obj = 1234;" 3811 "Array.prototype.arr = 4567;" 3812 "8901"); 3813 3814 LocalContext env0; 3815 Local<Script> script0 = Script::Compile(source); 3816 CHECK_EQ(8901.0, script0->Run()->NumberValue()); 3817 3818 LocalContext env1; 3819 Local<Script> script1 = Script::Compile(source); 3820 CHECK_EQ(8901.0, script1->Run()->NumberValue()); 3821 } 3822 3823 3824 THREADED_TEST(UndetectableObject) { 3825 v8::HandleScope scope; 3826 LocalContext env; 3827 3828 Local<v8::FunctionTemplate> desc = 3829 v8::FunctionTemplate::New(0, v8::Handle<Value>()); 3830 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 3831 3832 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 3833 env->Global()->Set(v8_str("undetectable"), obj); 3834 3835 ExpectString("undetectable.toString()", "[object Object]"); 3836 ExpectString("typeof undetectable", "undefined"); 3837 ExpectString("typeof(undetectable)", "undefined"); 3838 ExpectBoolean("typeof undetectable == 'undefined'", true); 3839 ExpectBoolean("typeof undetectable == 'object'", false); 3840 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 3841 ExpectBoolean("!undetectable", true); 3842 3843 ExpectObject("true&&undetectable", obj); 3844 ExpectBoolean("false&&undetectable", false); 3845 ExpectBoolean("true||undetectable", true); 3846 ExpectObject("false||undetectable", obj); 3847 3848 ExpectObject("undetectable&&true", obj); 3849 ExpectObject("undetectable&&false", obj); 3850 ExpectBoolean("undetectable||true", true); 3851 ExpectBoolean("undetectable||false", false); 3852 3853 ExpectBoolean("undetectable==null", true); 3854 ExpectBoolean("null==undetectable", true); 3855 ExpectBoolean("undetectable==undefined", true); 3856 ExpectBoolean("undefined==undetectable", true); 3857 ExpectBoolean("undetectable==undetectable", true); 3858 3859 3860 ExpectBoolean("undetectable===null", false); 3861 ExpectBoolean("null===undetectable", false); 3862 ExpectBoolean("undetectable===undefined", false); 3863 ExpectBoolean("undefined===undetectable", false); 3864 ExpectBoolean("undetectable===undetectable", true); 3865 } 3866 3867 3868 3869 THREADED_TEST(ExtensibleOnUndetectable) { 3870 v8::HandleScope scope; 3871 LocalContext env; 3872 3873 Local<v8::FunctionTemplate> desc = 3874 v8::FunctionTemplate::New(0, v8::Handle<Value>()); 3875 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 3876 3877 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 3878 env->Global()->Set(v8_str("undetectable"), obj); 3879 3880 Local<String> source = v8_str("undetectable.x = 42;" 3881 "undetectable.x"); 3882 3883 Local<Script> script = Script::Compile(source); 3884 3885 CHECK_EQ(v8::Integer::New(42), script->Run()); 3886 3887 ExpectBoolean("Object.isExtensible(undetectable)", true); 3888 3889 source = v8_str("Object.preventExtensions(undetectable);"); 3890 script = Script::Compile(source); 3891 script->Run(); 3892 ExpectBoolean("Object.isExtensible(undetectable)", false); 3893 3894 source = v8_str("undetectable.y = 2000;"); 3895 script = Script::Compile(source); 3896 Local<Value> result = script->Run(); 3897 ExpectBoolean("undetectable.y == undefined", true); 3898 } 3899 3900 3901 3902 THREADED_TEST(UndetectableString) { 3903 v8::HandleScope scope; 3904 LocalContext env; 3905 3906 Local<String> obj = String::NewUndetectable("foo"); 3907 env->Global()->Set(v8_str("undetectable"), obj); 3908 3909 ExpectString("undetectable", "foo"); 3910 ExpectString("typeof undetectable", "undefined"); 3911 ExpectString("typeof(undetectable)", "undefined"); 3912 ExpectBoolean("typeof undetectable == 'undefined'", true); 3913 ExpectBoolean("typeof undetectable == 'string'", false); 3914 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 3915 ExpectBoolean("!undetectable", true); 3916 3917 ExpectObject("true&&undetectable", obj); 3918 ExpectBoolean("false&&undetectable", false); 3919 ExpectBoolean("true||undetectable", true); 3920 ExpectObject("false||undetectable", obj); 3921 3922 ExpectObject("undetectable&&true", obj); 3923 ExpectObject("undetectable&&false", obj); 3924 ExpectBoolean("undetectable||true", true); 3925 ExpectBoolean("undetectable||false", false); 3926 3927 ExpectBoolean("undetectable==null", true); 3928 ExpectBoolean("null==undetectable", true); 3929 ExpectBoolean("undetectable==undefined", true); 3930 ExpectBoolean("undefined==undetectable", true); 3931 ExpectBoolean("undetectable==undetectable", true); 3932 3933 3934 ExpectBoolean("undetectable===null", false); 3935 ExpectBoolean("null===undetectable", false); 3936 ExpectBoolean("undetectable===undefined", false); 3937 ExpectBoolean("undefined===undetectable", false); 3938 ExpectBoolean("undetectable===undetectable", true); 3939 } 3940 3941 3942 template <typename T> static void USE(T) { } 3943 3944 3945 // This test is not intended to be run, just type checked. 3946 static void PersistentHandles() { 3947 USE(PersistentHandles); 3948 Local<String> str = v8_str("foo"); 3949 v8::Persistent<String> p_str = v8::Persistent<String>::New(str); 3950 USE(p_str); 3951 Local<Script> scr = Script::Compile(v8_str("")); 3952 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr); 3953 USE(p_scr); 3954 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3955 v8::Persistent<ObjectTemplate> p_templ = 3956 v8::Persistent<ObjectTemplate>::New(templ); 3957 USE(p_templ); 3958 } 3959 3960 3961 static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) { 3962 ApiTestFuzzer::Fuzz(); 3963 return v8::Undefined(); 3964 } 3965 3966 3967 THREADED_TEST(GlobalObjectTemplate) { 3968 v8::HandleScope handle_scope; 3969 Local<ObjectTemplate> global_template = ObjectTemplate::New(); 3970 global_template->Set(v8_str("JSNI_Log"), 3971 v8::FunctionTemplate::New(HandleLogDelegator)); 3972 v8::Persistent<Context> context = Context::New(0, global_template); 3973 Context::Scope context_scope(context); 3974 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run(); 3975 context.Dispose(); 3976 } 3977 3978 3979 static const char* kSimpleExtensionSource = 3980 "function Foo() {" 3981 " return 4;" 3982 "}"; 3983 3984 3985 THREADED_TEST(SimpleExtensions) { 3986 v8::HandleScope handle_scope; 3987 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource)); 3988 const char* extension_names[] = { "simpletest" }; 3989 v8::ExtensionConfiguration extensions(1, extension_names); 3990 v8::Handle<Context> context = Context::New(&extensions); 3991 Context::Scope lock(context); 3992 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 3993 CHECK_EQ(result, v8::Integer::New(4)); 3994 } 3995 3996 3997 static const char* kEvalExtensionSource1 = 3998 "function UseEval1() {" 3999 " var x = 42;" 4000 " return eval('x');" 4001 "}"; 4002 4003 4004 static const char* kEvalExtensionSource2 = 4005 "(function() {" 4006 " var x = 42;" 4007 " function e() {" 4008 " return eval('x');" 4009 " }" 4010 " this.UseEval2 = e;" 4011 "})()"; 4012 4013 4014 THREADED_TEST(UseEvalFromExtension) { 4015 v8::HandleScope handle_scope; 4016 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1)); 4017 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2)); 4018 const char* extension_names[] = { "evaltest1", "evaltest2" }; 4019 v8::ExtensionConfiguration extensions(2, extension_names); 4020 v8::Handle<Context> context = Context::New(&extensions); 4021 Context::Scope lock(context); 4022 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run(); 4023 CHECK_EQ(result, v8::Integer::New(42)); 4024 result = Script::Compile(v8_str("UseEval2()"))->Run(); 4025 CHECK_EQ(result, v8::Integer::New(42)); 4026 } 4027 4028 4029 static const char* kWithExtensionSource1 = 4030 "function UseWith1() {" 4031 " var x = 42;" 4032 " with({x:87}) { return x; }" 4033 "}"; 4034 4035 4036 4037 static const char* kWithExtensionSource2 = 4038 "(function() {" 4039 " var x = 42;" 4040 " function e() {" 4041 " with ({x:87}) { return x; }" 4042 " }" 4043 " this.UseWith2 = e;" 4044 "})()"; 4045 4046 4047 THREADED_TEST(UseWithFromExtension) { 4048 v8::HandleScope handle_scope; 4049 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1)); 4050 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2)); 4051 const char* extension_names[] = { "withtest1", "withtest2" }; 4052 v8::ExtensionConfiguration extensions(2, extension_names); 4053 v8::Handle<Context> context = Context::New(&extensions); 4054 Context::Scope lock(context); 4055 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run(); 4056 CHECK_EQ(result, v8::Integer::New(87)); 4057 result = Script::Compile(v8_str("UseWith2()"))->Run(); 4058 CHECK_EQ(result, v8::Integer::New(87)); 4059 } 4060 4061 4062 THREADED_TEST(AutoExtensions) { 4063 v8::HandleScope handle_scope; 4064 Extension* extension = new Extension("autotest", kSimpleExtensionSource); 4065 extension->set_auto_enable(true); 4066 v8::RegisterExtension(extension); 4067 v8::Handle<Context> context = Context::New(); 4068 Context::Scope lock(context); 4069 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 4070 CHECK_EQ(result, v8::Integer::New(4)); 4071 } 4072 4073 4074 static const char* kSyntaxErrorInExtensionSource = 4075 "["; 4076 4077 4078 // Test that a syntax error in an extension does not cause a fatal 4079 // error but results in an empty context. 4080 THREADED_TEST(SyntaxErrorExtensions) { 4081 v8::HandleScope handle_scope; 4082 v8::RegisterExtension(new Extension("syntaxerror", 4083 kSyntaxErrorInExtensionSource)); 4084 const char* extension_names[] = { "syntaxerror" }; 4085 v8::ExtensionConfiguration extensions(1, extension_names); 4086 v8::Handle<Context> context = Context::New(&extensions); 4087 CHECK(context.IsEmpty()); 4088 } 4089 4090 4091 static const char* kExceptionInExtensionSource = 4092 "throw 42"; 4093 4094 4095 // Test that an exception when installing an extension does not cause 4096 // a fatal error but results in an empty context. 4097 THREADED_TEST(ExceptionExtensions) { 4098 v8::HandleScope handle_scope; 4099 v8::RegisterExtension(new Extension("exception", 4100 kExceptionInExtensionSource)); 4101 const char* extension_names[] = { "exception" }; 4102 v8::ExtensionConfiguration extensions(1, extension_names); 4103 v8::Handle<Context> context = Context::New(&extensions); 4104 CHECK(context.IsEmpty()); 4105 } 4106 4107 4108 static const char* kNativeCallInExtensionSource = 4109 "function call_runtime_last_index_of(x) {" 4110 " return %StringLastIndexOf(x, 'bob', 10);" 4111 "}"; 4112 4113 4114 static const char* kNativeCallTest = 4115 "call_runtime_last_index_of('bobbobboellebobboellebobbob');"; 4116 4117 // Test that a native runtime calls are supported in extensions. 4118 THREADED_TEST(NativeCallInExtensions) { 4119 v8::HandleScope handle_scope; 4120 v8::RegisterExtension(new Extension("nativecall", 4121 kNativeCallInExtensionSource)); 4122 const char* extension_names[] = { "nativecall" }; 4123 v8::ExtensionConfiguration extensions(1, extension_names); 4124 v8::Handle<Context> context = Context::New(&extensions); 4125 Context::Scope lock(context); 4126 v8::Handle<Value> result = Script::Compile(v8_str(kNativeCallTest))->Run(); 4127 CHECK_EQ(result, v8::Integer::New(3)); 4128 } 4129 4130 4131 static void CheckDependencies(const char* name, const char* expected) { 4132 v8::HandleScope handle_scope; 4133 v8::ExtensionConfiguration config(1, &name); 4134 LocalContext context(&config); 4135 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded"))); 4136 } 4137 4138 4139 /* 4140 * Configuration: 4141 * 4142 * /-- B <--\ 4143 * A <- -- D <-- E 4144 * \-- C <--/ 4145 */ 4146 THREADED_TEST(ExtensionDependency) { 4147 static const char* kEDeps[] = { "D" }; 4148 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps)); 4149 static const char* kDDeps[] = { "B", "C" }; 4150 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps)); 4151 static const char* kBCDeps[] = { "A" }; 4152 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps)); 4153 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps)); 4154 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';")); 4155 CheckDependencies("A", "undefinedA"); 4156 CheckDependencies("B", "undefinedAB"); 4157 CheckDependencies("C", "undefinedAC"); 4158 CheckDependencies("D", "undefinedABCD"); 4159 CheckDependencies("E", "undefinedABCDE"); 4160 v8::HandleScope handle_scope; 4161 static const char* exts[2] = { "C", "E" }; 4162 v8::ExtensionConfiguration config(2, exts); 4163 LocalContext context(&config); 4164 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded"))); 4165 } 4166 4167 4168 static const char* kExtensionTestScript = 4169 "native function A();" 4170 "native function B();" 4171 "native function C();" 4172 "function Foo(i) {" 4173 " if (i == 0) return A();" 4174 " if (i == 1) return B();" 4175 " if (i == 2) return C();" 4176 "}"; 4177 4178 4179 static v8::Handle<Value> CallFun(const v8::Arguments& args) { 4180 ApiTestFuzzer::Fuzz(); 4181 if (args.IsConstructCall()) { 4182 args.This()->Set(v8_str("data"), args.Data()); 4183 return v8::Null(); 4184 } 4185 return args.Data(); 4186 } 4187 4188 4189 class FunctionExtension : public Extension { 4190 public: 4191 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { } 4192 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( 4193 v8::Handle<String> name); 4194 }; 4195 4196 4197 static int lookup_count = 0; 4198 v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction( 4199 v8::Handle<String> name) { 4200 lookup_count++; 4201 if (name->Equals(v8_str("A"))) { 4202 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8)); 4203 } else if (name->Equals(v8_str("B"))) { 4204 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7)); 4205 } else if (name->Equals(v8_str("C"))) { 4206 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6)); 4207 } else { 4208 return v8::Handle<v8::FunctionTemplate>(); 4209 } 4210 } 4211 4212 4213 THREADED_TEST(FunctionLookup) { 4214 v8::RegisterExtension(new FunctionExtension()); 4215 v8::HandleScope handle_scope; 4216 static const char* exts[1] = { "functiontest" }; 4217 v8::ExtensionConfiguration config(1, exts); 4218 LocalContext context(&config); 4219 CHECK_EQ(3, lookup_count); 4220 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run()); 4221 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run()); 4222 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run()); 4223 } 4224 4225 4226 THREADED_TEST(NativeFunctionConstructCall) { 4227 v8::RegisterExtension(new FunctionExtension()); 4228 v8::HandleScope handle_scope; 4229 static const char* exts[1] = { "functiontest" }; 4230 v8::ExtensionConfiguration config(1, exts); 4231 LocalContext context(&config); 4232 for (int i = 0; i < 10; i++) { 4233 // Run a few times to ensure that allocation of objects doesn't 4234 // change behavior of a constructor function. 4235 CHECK_EQ(v8::Integer::New(8), 4236 Script::Compile(v8_str("(new A()).data"))->Run()); 4237 CHECK_EQ(v8::Integer::New(7), 4238 Script::Compile(v8_str("(new B()).data"))->Run()); 4239 CHECK_EQ(v8::Integer::New(6), 4240 Script::Compile(v8_str("(new C()).data"))->Run()); 4241 } 4242 } 4243 4244 4245 static const char* last_location; 4246 static const char* last_message; 4247 void StoringErrorCallback(const char* location, const char* message) { 4248 if (last_location == NULL) { 4249 last_location = location; 4250 last_message = message; 4251 } 4252 } 4253 4254 4255 // ErrorReporting creates a circular extensions configuration and 4256 // tests that the fatal error handler gets called. This renders V8 4257 // unusable and therefore this test cannot be run in parallel. 4258 TEST(ErrorReporting) { 4259 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 4260 static const char* aDeps[] = { "B" }; 4261 v8::RegisterExtension(new Extension("A", "", 1, aDeps)); 4262 static const char* bDeps[] = { "A" }; 4263 v8::RegisterExtension(new Extension("B", "", 1, bDeps)); 4264 last_location = NULL; 4265 v8::ExtensionConfiguration config(1, bDeps); 4266 v8::Handle<Context> context = Context::New(&config); 4267 CHECK(context.IsEmpty()); 4268 CHECK_NE(last_location, NULL); 4269 } 4270 4271 4272 static const char* js_code_causing_huge_string_flattening = 4273 "var str = 'X';" 4274 "for (var i = 0; i < 30; i++) {" 4275 " str = str + str;" 4276 "}" 4277 "str.match(/X/);"; 4278 4279 4280 void OOMCallback(const char* location, const char* message) { 4281 exit(0); 4282 } 4283 4284 4285 TEST(RegexpOutOfMemory) { 4286 // Execute a script that causes out of memory when flattening a string. 4287 v8::HandleScope scope; 4288 v8::V8::SetFatalErrorHandler(OOMCallback); 4289 LocalContext context; 4290 Local<Script> script = 4291 Script::Compile(String::New(js_code_causing_huge_string_flattening)); 4292 last_location = NULL; 4293 Local<Value> result = script->Run(); 4294 4295 CHECK(false); // Should not return. 4296 } 4297 4298 4299 static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message, 4300 v8::Handle<Value> data) { 4301 CHECK_EQ(v8::Undefined(), data); 4302 CHECK(message->GetScriptResourceName()->IsUndefined()); 4303 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName()); 4304 message->GetLineNumber(); 4305 message->GetSourceLine(); 4306 } 4307 4308 4309 THREADED_TEST(ErrorWithMissingScriptInfo) { 4310 v8::HandleScope scope; 4311 LocalContext context; 4312 v8::V8::AddMessageListener(MissingScriptInfoMessageListener); 4313 Script::Compile(v8_str("throw Error()"))->Run(); 4314 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener); 4315 } 4316 4317 4318 int global_index = 0; 4319 4320 class Snorkel { 4321 public: 4322 Snorkel() { index_ = global_index++; } 4323 int index_; 4324 }; 4325 4326 class Whammy { 4327 public: 4328 Whammy() { 4329 cursor_ = 0; 4330 } 4331 ~Whammy() { 4332 script_.Dispose(); 4333 } 4334 v8::Handle<Script> getScript() { 4335 if (script_.IsEmpty()) 4336 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo")); 4337 return Local<Script>(*script_); 4338 } 4339 4340 public: 4341 static const int kObjectCount = 256; 4342 int cursor_; 4343 v8::Persistent<v8::Object> objects_[kObjectCount]; 4344 v8::Persistent<Script> script_; 4345 }; 4346 4347 static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) { 4348 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data); 4349 delete snorkel; 4350 obj.ClearWeak(); 4351 } 4352 4353 v8::Handle<Value> WhammyPropertyGetter(Local<String> name, 4354 const AccessorInfo& info) { 4355 Whammy* whammy = 4356 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); 4357 4358 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_]; 4359 4360 v8::Handle<v8::Object> obj = v8::Object::New(); 4361 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj); 4362 if (!prev.IsEmpty()) { 4363 prev->Set(v8_str("next"), obj); 4364 prev.MakeWeak(new Snorkel(), &HandleWeakReference); 4365 whammy->objects_[whammy->cursor_].Clear(); 4366 } 4367 whammy->objects_[whammy->cursor_] = global; 4368 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount; 4369 return whammy->getScript()->Run(); 4370 } 4371 4372 THREADED_TEST(WeakReference) { 4373 v8::HandleScope handle_scope; 4374 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New(); 4375 Whammy* whammy = new Whammy(); 4376 templ->SetNamedPropertyHandler(WhammyPropertyGetter, 4377 0, 0, 0, 0, 4378 v8::External::New(whammy)); 4379 const char* extension_list[] = { "v8/gc" }; 4380 v8::ExtensionConfiguration extensions(1, extension_list); 4381 v8::Persistent<Context> context = Context::New(&extensions); 4382 Context::Scope context_scope(context); 4383 4384 v8::Handle<v8::Object> interceptor = templ->NewInstance(); 4385 context->Global()->Set(v8_str("whammy"), interceptor); 4386 const char* code = 4387 "var last;" 4388 "for (var i = 0; i < 10000; i++) {" 4389 " var obj = whammy.length;" 4390 " if (last) last.next = obj;" 4391 " last = obj;" 4392 "}" 4393 "gc();" 4394 "4"; 4395 v8::Handle<Value> result = CompileRun(code); 4396 CHECK_EQ(4.0, result->NumberValue()); 4397 delete whammy; 4398 context.Dispose(); 4399 } 4400 4401 4402 static bool in_scavenge = false; 4403 static int last = -1; 4404 4405 static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) { 4406 CHECK_EQ(-1, last); 4407 last = 0; 4408 obj.Dispose(); 4409 obj.Clear(); 4410 in_scavenge = true; 4411 HEAP->PerformScavenge(); 4412 in_scavenge = false; 4413 *(reinterpret_cast<bool*>(data)) = true; 4414 } 4415 4416 static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj, 4417 void* data) { 4418 CHECK_EQ(0, last); 4419 last = 1; 4420 *(reinterpret_cast<bool*>(data)) = in_scavenge; 4421 obj.Dispose(); 4422 obj.Clear(); 4423 } 4424 4425 THREADED_TEST(NoWeakRefCallbacksInScavenge) { 4426 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks. 4427 // Calling callbacks from scavenges is unsafe as objects held by those 4428 // handlers might have become strongly reachable, but scavenge doesn't 4429 // check that. 4430 v8::Persistent<Context> context = Context::New(); 4431 Context::Scope context_scope(context); 4432 4433 v8::Persistent<v8::Object> object_a; 4434 v8::Persistent<v8::Object> object_b; 4435 4436 { 4437 v8::HandleScope handle_scope; 4438 object_b = v8::Persistent<v8::Object>::New(v8::Object::New()); 4439 object_a = v8::Persistent<v8::Object>::New(v8::Object::New()); 4440 } 4441 4442 bool object_a_disposed = false; 4443 object_a.MakeWeak(&object_a_disposed, &ForceScavenge); 4444 bool released_in_scavenge = false; 4445 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge); 4446 4447 while (!object_a_disposed) { 4448 HEAP->CollectAllGarbage(false); 4449 } 4450 CHECK(!released_in_scavenge); 4451 } 4452 4453 4454 v8::Handle<Function> args_fun; 4455 4456 4457 static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) { 4458 ApiTestFuzzer::Fuzz(); 4459 CHECK_EQ(args_fun, args.Callee()); 4460 CHECK_EQ(3, args.Length()); 4461 CHECK_EQ(v8::Integer::New(1), args[0]); 4462 CHECK_EQ(v8::Integer::New(2), args[1]); 4463 CHECK_EQ(v8::Integer::New(3), args[2]); 4464 CHECK_EQ(v8::Undefined(), args[3]); 4465 v8::HandleScope scope; 4466 HEAP->CollectAllGarbage(false); 4467 return v8::Undefined(); 4468 } 4469 4470 4471 THREADED_TEST(Arguments) { 4472 v8::HandleScope scope; 4473 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 4474 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback)); 4475 LocalContext context(NULL, global); 4476 args_fun = context->Global()->Get(v8_str("f")).As<Function>(); 4477 v8_compile("f(1, 2, 3)")->Run(); 4478 } 4479 4480 4481 static v8::Handle<Value> NoBlockGetterX(Local<String> name, 4482 const AccessorInfo&) { 4483 return v8::Handle<Value>(); 4484 } 4485 4486 4487 static v8::Handle<Value> NoBlockGetterI(uint32_t index, 4488 const AccessorInfo&) { 4489 return v8::Handle<Value>(); 4490 } 4491 4492 4493 static v8::Handle<v8::Boolean> PDeleter(Local<String> name, 4494 const AccessorInfo&) { 4495 if (!name->Equals(v8_str("foo"))) { 4496 return v8::Handle<v8::Boolean>(); // not intercepted 4497 } 4498 4499 return v8::False(); // intercepted, and don't delete the property 4500 } 4501 4502 4503 static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) { 4504 if (index != 2) { 4505 return v8::Handle<v8::Boolean>(); // not intercepted 4506 } 4507 4508 return v8::False(); // intercepted, and don't delete the property 4509 } 4510 4511 4512 THREADED_TEST(Deleter) { 4513 v8::HandleScope scope; 4514 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4515 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL); 4516 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL); 4517 LocalContext context; 4518 context->Global()->Set(v8_str("k"), obj->NewInstance()); 4519 CompileRun( 4520 "k.foo = 'foo';" 4521 "k.bar = 'bar';" 4522 "k[2] = 2;" 4523 "k[4] = 4;"); 4524 CHECK(v8_compile("delete k.foo")->Run()->IsFalse()); 4525 CHECK(v8_compile("delete k.bar")->Run()->IsTrue()); 4526 4527 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo")); 4528 CHECK(v8_compile("k.bar")->Run()->IsUndefined()); 4529 4530 CHECK(v8_compile("delete k[2]")->Run()->IsFalse()); 4531 CHECK(v8_compile("delete k[4]")->Run()->IsTrue()); 4532 4533 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2)); 4534 CHECK(v8_compile("k[4]")->Run()->IsUndefined()); 4535 } 4536 4537 4538 static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) { 4539 ApiTestFuzzer::Fuzz(); 4540 if (name->Equals(v8_str("foo")) || 4541 name->Equals(v8_str("bar")) || 4542 name->Equals(v8_str("baz"))) { 4543 return v8::Undefined(); 4544 } 4545 return v8::Handle<Value>(); 4546 } 4547 4548 4549 static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) { 4550 ApiTestFuzzer::Fuzz(); 4551 if (index == 0 || index == 1) return v8::Undefined(); 4552 return v8::Handle<Value>(); 4553 } 4554 4555 4556 static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) { 4557 ApiTestFuzzer::Fuzz(); 4558 v8::Handle<v8::Array> result = v8::Array::New(3); 4559 result->Set(v8::Integer::New(0), v8_str("foo")); 4560 result->Set(v8::Integer::New(1), v8_str("bar")); 4561 result->Set(v8::Integer::New(2), v8_str("baz")); 4562 return result; 4563 } 4564 4565 4566 static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) { 4567 ApiTestFuzzer::Fuzz(); 4568 v8::Handle<v8::Array> result = v8::Array::New(2); 4569 result->Set(v8::Integer::New(0), v8_str("0")); 4570 result->Set(v8::Integer::New(1), v8_str("1")); 4571 return result; 4572 } 4573 4574 4575 THREADED_TEST(Enumerators) { 4576 v8::HandleScope scope; 4577 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4578 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum); 4579 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum); 4580 LocalContext context; 4581 context->Global()->Set(v8_str("k"), obj->NewInstance()); 4582 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 4583 "k[10] = 0;" 4584 "k.a = 0;" 4585 "k[5] = 0;" 4586 "k.b = 0;" 4587 "k[4294967295] = 0;" 4588 "k.c = 0;" 4589 "k[4294967296] = 0;" 4590 "k.d = 0;" 4591 "k[140000] = 0;" 4592 "k.e = 0;" 4593 "k[30000000000] = 0;" 4594 "k.f = 0;" 4595 "var result = [];" 4596 "for (var prop in k) {" 4597 " result.push(prop);" 4598 "}" 4599 "result")); 4600 // Check that we get all the property names returned including the 4601 // ones from the enumerators in the right order: indexed properties 4602 // in numerical order, indexed interceptor properties, named 4603 // properties in insertion order, named interceptor properties. 4604 // This order is not mandated by the spec, so this test is just 4605 // documenting our behavior. 4606 CHECK_EQ(17, result->Length()); 4607 // Indexed properties in numerical order. 4608 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0))); 4609 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1))); 4610 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2))); 4611 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3))); 4612 // Indexed interceptor properties in the order they are returned 4613 // from the enumerator interceptor. 4614 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4))); 4615 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5))); 4616 // Named properties in insertion order. 4617 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6))); 4618 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7))); 4619 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8))); 4620 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9))); 4621 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10))); 4622 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11))); 4623 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12))); 4624 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13))); 4625 // Named interceptor properties. 4626 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14))); 4627 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15))); 4628 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16))); 4629 } 4630 4631 4632 int p_getter_count; 4633 int p_getter_count2; 4634 4635 4636 static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) { 4637 ApiTestFuzzer::Fuzz(); 4638 p_getter_count++; 4639 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 4640 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 4641 if (name->Equals(v8_str("p1"))) { 4642 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 4643 } else if (name->Equals(v8_str("p2"))) { 4644 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 4645 } else if (name->Equals(v8_str("p3"))) { 4646 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 4647 } else if (name->Equals(v8_str("p4"))) { 4648 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 4649 } 4650 return v8::Undefined(); 4651 } 4652 4653 4654 static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) { 4655 ApiTestFuzzer::Fuzz(); 4656 LocalContext context; 4657 context->Global()->Set(v8_str("o1"), obj->NewInstance()); 4658 CompileRun( 4659 "o1.__proto__ = { };" 4660 "var o2 = { __proto__: o1 };" 4661 "var o3 = { __proto__: o2 };" 4662 "var o4 = { __proto__: o3 };" 4663 "for (var i = 0; i < 10; i++) o4.p4;" 4664 "for (var i = 0; i < 10; i++) o3.p3;" 4665 "for (var i = 0; i < 10; i++) o2.p2;" 4666 "for (var i = 0; i < 10; i++) o1.p1;"); 4667 } 4668 4669 4670 static v8::Handle<Value> PGetter2(Local<String> name, 4671 const AccessorInfo& info) { 4672 ApiTestFuzzer::Fuzz(); 4673 p_getter_count2++; 4674 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 4675 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 4676 if (name->Equals(v8_str("p1"))) { 4677 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 4678 } else if (name->Equals(v8_str("p2"))) { 4679 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 4680 } else if (name->Equals(v8_str("p3"))) { 4681 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 4682 } else if (name->Equals(v8_str("p4"))) { 4683 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 4684 } 4685 return v8::Undefined(); 4686 } 4687 4688 4689 THREADED_TEST(GetterHolders) { 4690 v8::HandleScope scope; 4691 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4692 obj->SetAccessor(v8_str("p1"), PGetter); 4693 obj->SetAccessor(v8_str("p2"), PGetter); 4694 obj->SetAccessor(v8_str("p3"), PGetter); 4695 obj->SetAccessor(v8_str("p4"), PGetter); 4696 p_getter_count = 0; 4697 RunHolderTest(obj); 4698 CHECK_EQ(40, p_getter_count); 4699 } 4700 4701 4702 THREADED_TEST(PreInterceptorHolders) { 4703 v8::HandleScope scope; 4704 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4705 obj->SetNamedPropertyHandler(PGetter2); 4706 p_getter_count2 = 0; 4707 RunHolderTest(obj); 4708 CHECK_EQ(40, p_getter_count2); 4709 } 4710 4711 4712 THREADED_TEST(ObjectInstantiation) { 4713 v8::HandleScope scope; 4714 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 4715 templ->SetAccessor(v8_str("t"), PGetter2); 4716 LocalContext context; 4717 context->Global()->Set(v8_str("o"), templ->NewInstance()); 4718 for (int i = 0; i < 100; i++) { 4719 v8::HandleScope inner_scope; 4720 v8::Handle<v8::Object> obj = templ->NewInstance(); 4721 CHECK_NE(obj, context->Global()->Get(v8_str("o"))); 4722 context->Global()->Set(v8_str("o2"), obj); 4723 v8::Handle<Value> value = 4724 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run(); 4725 CHECK_EQ(v8::True(), value); 4726 context->Global()->Set(v8_str("o"), obj); 4727 } 4728 } 4729 4730 4731 static int StrCmp16(uint16_t* a, uint16_t* b) { 4732 while (true) { 4733 if (*a == 0 && *b == 0) return 0; 4734 if (*a != *b) return 0 + *a - *b; 4735 a++; 4736 b++; 4737 } 4738 } 4739 4740 4741 static int StrNCmp16(uint16_t* a, uint16_t* b, int n) { 4742 while (true) { 4743 if (n-- == 0) return 0; 4744 if (*a == 0 && *b == 0) return 0; 4745 if (*a != *b) return 0 + *a - *b; 4746 a++; 4747 b++; 4748 } 4749 } 4750 4751 4752 THREADED_TEST(StringWrite) { 4753 v8::HandleScope scope; 4754 v8::Handle<String> str = v8_str("abcde"); 4755 // abc<Icelandic eth><Unicode snowman>. 4756 v8::Handle<String> str2 = v8_str("abc\303\260\342\230\203"); 4757 4758 CHECK_EQ(5, str2->Length()); 4759 4760 char buf[100]; 4761 char utf8buf[100]; 4762 uint16_t wbuf[100]; 4763 int len; 4764 int charlen; 4765 4766 memset(utf8buf, 0x1, sizeof(utf8buf)); 4767 len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen); 4768 CHECK_EQ(9, len); 4769 CHECK_EQ(5, charlen); 4770 CHECK_EQ(0, strcmp(utf8buf, "abc\303\260\342\230\203")); 4771 4772 memset(utf8buf, 0x1, sizeof(utf8buf)); 4773 len = str2->WriteUtf8(utf8buf, 8, &charlen); 4774 CHECK_EQ(8, len); 4775 CHECK_EQ(5, charlen); 4776 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\342\230\203\1", 9)); 4777 4778 memset(utf8buf, 0x1, sizeof(utf8buf)); 4779 len = str2->WriteUtf8(utf8buf, 7, &charlen); 4780 CHECK_EQ(5, len); 4781 CHECK_EQ(4, charlen); 4782 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); 4783 4784 memset(utf8buf, 0x1, sizeof(utf8buf)); 4785 len = str2->WriteUtf8(utf8buf, 6, &charlen); 4786 CHECK_EQ(5, len); 4787 CHECK_EQ(4, charlen); 4788 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); 4789 4790 memset(utf8buf, 0x1, sizeof(utf8buf)); 4791 len = str2->WriteUtf8(utf8buf, 5, &charlen); 4792 CHECK_EQ(5, len); 4793 CHECK_EQ(4, charlen); 4794 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); 4795 4796 memset(utf8buf, 0x1, sizeof(utf8buf)); 4797 len = str2->WriteUtf8(utf8buf, 4, &charlen); 4798 CHECK_EQ(3, len); 4799 CHECK_EQ(3, charlen); 4800 CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4)); 4801 4802 memset(utf8buf, 0x1, sizeof(utf8buf)); 4803 len = str2->WriteUtf8(utf8buf, 3, &charlen); 4804 CHECK_EQ(3, len); 4805 CHECK_EQ(3, charlen); 4806 CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4)); 4807 4808 memset(utf8buf, 0x1, sizeof(utf8buf)); 4809 len = str2->WriteUtf8(utf8buf, 2, &charlen); 4810 CHECK_EQ(2, len); 4811 CHECK_EQ(2, charlen); 4812 CHECK_EQ(0, strncmp(utf8buf, "ab\1", 3)); 4813 4814 memset(buf, 0x1, sizeof(buf)); 4815 memset(wbuf, 0x1, sizeof(wbuf)); 4816 len = str->WriteAscii(buf); 4817 CHECK_EQ(5, len); 4818 len = str->Write(wbuf); 4819 CHECK_EQ(5, len); 4820 CHECK_EQ(0, strcmp("abcde", buf)); 4821 uint16_t answer1[] = {'a', 'b', 'c', 'd', 'e', '\0'}; 4822 CHECK_EQ(0, StrCmp16(answer1, wbuf)); 4823 4824 memset(buf, 0x1, sizeof(buf)); 4825 memset(wbuf, 0x1, sizeof(wbuf)); 4826 len = str->WriteAscii(buf, 0, 4); 4827 CHECK_EQ(4, len); 4828 len = str->Write(wbuf, 0, 4); 4829 CHECK_EQ(4, len); 4830 CHECK_EQ(0, strncmp("abcd\1", buf, 5)); 4831 uint16_t answer2[] = {'a', 'b', 'c', 'd', 0x101}; 4832 CHECK_EQ(0, StrNCmp16(answer2, wbuf, 5)); 4833 4834 memset(buf, 0x1, sizeof(buf)); 4835 memset(wbuf, 0x1, sizeof(wbuf)); 4836 len = str->WriteAscii(buf, 0, 5); 4837 CHECK_EQ(5, len); 4838 len = str->Write(wbuf, 0, 5); 4839 CHECK_EQ(5, len); 4840 CHECK_EQ(0, strncmp("abcde\1", buf, 6)); 4841 uint16_t answer3[] = {'a', 'b', 'c', 'd', 'e', 0x101}; 4842 CHECK_EQ(0, StrNCmp16(answer3, wbuf, 6)); 4843 4844 memset(buf, 0x1, sizeof(buf)); 4845 memset(wbuf, 0x1, sizeof(wbuf)); 4846 len = str->WriteAscii(buf, 0, 6); 4847 CHECK_EQ(5, len); 4848 len = str->Write(wbuf, 0, 6); 4849 CHECK_EQ(5, len); 4850 CHECK_EQ(0, strcmp("abcde", buf)); 4851 uint16_t answer4[] = {'a', 'b', 'c', 'd', 'e', '\0'}; 4852 CHECK_EQ(0, StrCmp16(answer4, wbuf)); 4853 4854 memset(buf, 0x1, sizeof(buf)); 4855 memset(wbuf, 0x1, sizeof(wbuf)); 4856 len = str->WriteAscii(buf, 4, -1); 4857 CHECK_EQ(1, len); 4858 len = str->Write(wbuf, 4, -1); 4859 CHECK_EQ(1, len); 4860 CHECK_EQ(0, strcmp("e", buf)); 4861 uint16_t answer5[] = {'e', '\0'}; 4862 CHECK_EQ(0, StrCmp16(answer5, wbuf)); 4863 4864 memset(buf, 0x1, sizeof(buf)); 4865 memset(wbuf, 0x1, sizeof(wbuf)); 4866 len = str->WriteAscii(buf, 4, 6); 4867 CHECK_EQ(1, len); 4868 len = str->Write(wbuf, 4, 6); 4869 CHECK_EQ(1, len); 4870 CHECK_EQ(0, strcmp("e", buf)); 4871 CHECK_EQ(0, StrCmp16(answer5, wbuf)); 4872 4873 memset(buf, 0x1, sizeof(buf)); 4874 memset(wbuf, 0x1, sizeof(wbuf)); 4875 len = str->WriteAscii(buf, 4, 1); 4876 CHECK_EQ(1, len); 4877 len = str->Write(wbuf, 4, 1); 4878 CHECK_EQ(1, len); 4879 CHECK_EQ(0, strncmp("e\1", buf, 2)); 4880 uint16_t answer6[] = {'e', 0x101}; 4881 CHECK_EQ(0, StrNCmp16(answer6, wbuf, 2)); 4882 4883 memset(buf, 0x1, sizeof(buf)); 4884 memset(wbuf, 0x1, sizeof(wbuf)); 4885 len = str->WriteAscii(buf, 3, 1); 4886 CHECK_EQ(1, len); 4887 len = str->Write(wbuf, 3, 1); 4888 CHECK_EQ(1, len); 4889 CHECK_EQ(0, strncmp("d\1", buf, 2)); 4890 uint16_t answer7[] = {'d', 0x101}; 4891 CHECK_EQ(0, StrNCmp16(answer7, wbuf, 2)); 4892 } 4893 4894 4895 THREADED_TEST(ToArrayIndex) { 4896 v8::HandleScope scope; 4897 LocalContext context; 4898 4899 v8::Handle<String> str = v8_str("42"); 4900 v8::Handle<v8::Uint32> index = str->ToArrayIndex(); 4901 CHECK(!index.IsEmpty()); 4902 CHECK_EQ(42.0, index->Uint32Value()); 4903 str = v8_str("42asdf"); 4904 index = str->ToArrayIndex(); 4905 CHECK(index.IsEmpty()); 4906 str = v8_str("-42"); 4907 index = str->ToArrayIndex(); 4908 CHECK(index.IsEmpty()); 4909 str = v8_str("4294967295"); 4910 index = str->ToArrayIndex(); 4911 CHECK(!index.IsEmpty()); 4912 CHECK_EQ(4294967295.0, index->Uint32Value()); 4913 v8::Handle<v8::Number> num = v8::Number::New(1); 4914 index = num->ToArrayIndex(); 4915 CHECK(!index.IsEmpty()); 4916 CHECK_EQ(1.0, index->Uint32Value()); 4917 num = v8::Number::New(-1); 4918 index = num->ToArrayIndex(); 4919 CHECK(index.IsEmpty()); 4920 v8::Handle<v8::Object> obj = v8::Object::New(); 4921 index = obj->ToArrayIndex(); 4922 CHECK(index.IsEmpty()); 4923 } 4924 4925 4926 THREADED_TEST(ErrorConstruction) { 4927 v8::HandleScope scope; 4928 LocalContext context; 4929 4930 v8::Handle<String> foo = v8_str("foo"); 4931 v8::Handle<String> message = v8_str("message"); 4932 v8::Handle<Value> range_error = v8::Exception::RangeError(foo); 4933 CHECK(range_error->IsObject()); 4934 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>(); 4935 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo)); 4936 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo); 4937 CHECK(reference_error->IsObject()); 4938 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo)); 4939 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo); 4940 CHECK(syntax_error->IsObject()); 4941 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo)); 4942 v8::Handle<Value> type_error = v8::Exception::TypeError(foo); 4943 CHECK(type_error->IsObject()); 4944 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo)); 4945 v8::Handle<Value> error = v8::Exception::Error(foo); 4946 CHECK(error->IsObject()); 4947 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo)); 4948 } 4949 4950 4951 static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) { 4952 ApiTestFuzzer::Fuzz(); 4953 return v8_num(10); 4954 } 4955 4956 4957 static void YSetter(Local<String> name, 4958 Local<Value> value, 4959 const AccessorInfo& info) { 4960 if (info.This()->Has(name)) { 4961 info.This()->Delete(name); 4962 } 4963 info.This()->Set(name, value); 4964 } 4965 4966 4967 THREADED_TEST(DeleteAccessor) { 4968 v8::HandleScope scope; 4969 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4970 obj->SetAccessor(v8_str("y"), YGetter, YSetter); 4971 LocalContext context; 4972 v8::Handle<v8::Object> holder = obj->NewInstance(); 4973 context->Global()->Set(v8_str("holder"), holder); 4974 v8::Handle<Value> result = CompileRun( 4975 "holder.y = 11; holder.y = 12; holder.y"); 4976 CHECK_EQ(12, result->Uint32Value()); 4977 } 4978 4979 4980 THREADED_TEST(TypeSwitch) { 4981 v8::HandleScope scope; 4982 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New(); 4983 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New(); 4984 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New(); 4985 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 }; 4986 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs); 4987 LocalContext context; 4988 v8::Handle<v8::Object> obj0 = v8::Object::New(); 4989 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance(); 4990 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance(); 4991 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance(); 4992 for (int i = 0; i < 10; i++) { 4993 CHECK_EQ(0, type_switch->match(obj0)); 4994 CHECK_EQ(1, type_switch->match(obj1)); 4995 CHECK_EQ(2, type_switch->match(obj2)); 4996 CHECK_EQ(3, type_switch->match(obj3)); 4997 CHECK_EQ(3, type_switch->match(obj3)); 4998 CHECK_EQ(2, type_switch->match(obj2)); 4999 CHECK_EQ(1, type_switch->match(obj1)); 5000 CHECK_EQ(0, type_switch->match(obj0)); 5001 } 5002 } 5003 5004 5005 // For use within the TestSecurityHandler() test. 5006 static bool g_security_callback_result = false; 5007 static bool NamedSecurityTestCallback(Local<v8::Object> global, 5008 Local<Value> name, 5009 v8::AccessType type, 5010 Local<Value> data) { 5011 // Always allow read access. 5012 if (type == v8::ACCESS_GET) 5013 return true; 5014 5015 // Sometimes allow other access. 5016 return g_security_callback_result; 5017 } 5018 5019 5020 static bool IndexedSecurityTestCallback(Local<v8::Object> global, 5021 uint32_t key, 5022 v8::AccessType type, 5023 Local<Value> data) { 5024 // Always allow read access. 5025 if (type == v8::ACCESS_GET) 5026 return true; 5027 5028 // Sometimes allow other access. 5029 return g_security_callback_result; 5030 } 5031 5032 5033 static int trouble_nesting = 0; 5034 static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) { 5035 ApiTestFuzzer::Fuzz(); 5036 trouble_nesting++; 5037 5038 // Call a JS function that throws an uncaught exception. 5039 Local<v8::Object> arg_this = Context::GetCurrent()->Global(); 5040 Local<Value> trouble_callee = (trouble_nesting == 3) ? 5041 arg_this->Get(v8_str("trouble_callee")) : 5042 arg_this->Get(v8_str("trouble_caller")); 5043 CHECK(trouble_callee->IsFunction()); 5044 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL); 5045 } 5046 5047 5048 static int report_count = 0; 5049 static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>, 5050 v8::Handle<Value>) { 5051 report_count++; 5052 } 5053 5054 5055 // Counts uncaught exceptions, but other tests running in parallel 5056 // also have uncaught exceptions. 5057 TEST(ApiUncaughtException) { 5058 report_count = 0; 5059 v8::HandleScope scope; 5060 LocalContext env; 5061 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener); 5062 5063 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 5064 v8::Local<v8::Object> global = env->Global(); 5065 global->Set(v8_str("trouble"), fun->GetFunction()); 5066 5067 Script::Compile(v8_str("function trouble_callee() {" 5068 " var x = null;" 5069 " return x.foo;" 5070 "};" 5071 "function trouble_caller() {" 5072 " trouble();" 5073 "};"))->Run(); 5074 Local<Value> trouble = global->Get(v8_str("trouble")); 5075 CHECK(trouble->IsFunction()); 5076 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee")); 5077 CHECK(trouble_callee->IsFunction()); 5078 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller")); 5079 CHECK(trouble_caller->IsFunction()); 5080 Function::Cast(*trouble_caller)->Call(global, 0, NULL); 5081 CHECK_EQ(1, report_count); 5082 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener); 5083 } 5084 5085 static const char* script_resource_name = "ExceptionInNativeScript.js"; 5086 static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message, 5087 v8::Handle<Value>) { 5088 v8::Handle<v8::Value> name_val = message->GetScriptResourceName(); 5089 CHECK(!name_val.IsEmpty() && name_val->IsString()); 5090 v8::String::AsciiValue name(message->GetScriptResourceName()); 5091 CHECK_EQ(script_resource_name, *name); 5092 CHECK_EQ(3, message->GetLineNumber()); 5093 v8::String::AsciiValue source_line(message->GetSourceLine()); 5094 CHECK_EQ(" new o.foo();", *source_line); 5095 } 5096 5097 TEST(ExceptionInNativeScript) { 5098 v8::HandleScope scope; 5099 LocalContext env; 5100 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener); 5101 5102 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 5103 v8::Local<v8::Object> global = env->Global(); 5104 global->Set(v8_str("trouble"), fun->GetFunction()); 5105 5106 Script::Compile(v8_str("function trouble() {\n" 5107 " var o = {};\n" 5108 " new o.foo();\n" 5109 "};"), v8::String::New(script_resource_name))->Run(); 5110 Local<Value> trouble = global->Get(v8_str("trouble")); 5111 CHECK(trouble->IsFunction()); 5112 Function::Cast(*trouble)->Call(global, 0, NULL); 5113 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener); 5114 } 5115 5116 5117 TEST(CompilationErrorUsingTryCatchHandler) { 5118 v8::HandleScope scope; 5119 LocalContext env; 5120 v8::TryCatch try_catch; 5121 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile.")); 5122 CHECK_NE(NULL, *try_catch.Exception()); 5123 CHECK(try_catch.HasCaught()); 5124 } 5125 5126 5127 TEST(TryCatchFinallyUsingTryCatchHandler) { 5128 v8::HandleScope scope; 5129 LocalContext env; 5130 v8::TryCatch try_catch; 5131 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run(); 5132 CHECK(!try_catch.HasCaught()); 5133 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run(); 5134 CHECK(try_catch.HasCaught()); 5135 try_catch.Reset(); 5136 Script::Compile(v8_str("(function() {" 5137 "try { throw ''; } finally { return; }" 5138 "})()"))->Run(); 5139 CHECK(!try_catch.HasCaught()); 5140 Script::Compile(v8_str("(function()" 5141 " { try { throw ''; } finally { throw 0; }" 5142 "})()"))->Run(); 5143 CHECK(try_catch.HasCaught()); 5144 } 5145 5146 5147 // SecurityHandler can't be run twice 5148 TEST(SecurityHandler) { 5149 v8::HandleScope scope0; 5150 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 5151 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback, 5152 IndexedSecurityTestCallback); 5153 // Create an environment 5154 v8::Persistent<Context> context0 = 5155 Context::New(NULL, global_template); 5156 context0->Enter(); 5157 5158 v8::Handle<v8::Object> global0 = context0->Global(); 5159 v8::Handle<Script> script0 = v8_compile("foo = 111"); 5160 script0->Run(); 5161 global0->Set(v8_str("0"), v8_num(999)); 5162 v8::Handle<Value> foo0 = global0->Get(v8_str("foo")); 5163 CHECK_EQ(111, foo0->Int32Value()); 5164 v8::Handle<Value> z0 = global0->Get(v8_str("0")); 5165 CHECK_EQ(999, z0->Int32Value()); 5166 5167 // Create another environment, should fail security checks. 5168 v8::HandleScope scope1; 5169 5170 v8::Persistent<Context> context1 = 5171 Context::New(NULL, global_template); 5172 context1->Enter(); 5173 5174 v8::Handle<v8::Object> global1 = context1->Global(); 5175 global1->Set(v8_str("othercontext"), global0); 5176 // This set will fail the security check. 5177 v8::Handle<Script> script1 = 5178 v8_compile("othercontext.foo = 222; othercontext[0] = 888;"); 5179 script1->Run(); 5180 // This read will pass the security check. 5181 v8::Handle<Value> foo1 = global0->Get(v8_str("foo")); 5182 CHECK_EQ(111, foo1->Int32Value()); 5183 // This read will pass the security check. 5184 v8::Handle<Value> z1 = global0->Get(v8_str("0")); 5185 CHECK_EQ(999, z1->Int32Value()); 5186 5187 // Create another environment, should pass security checks. 5188 { g_security_callback_result = true; // allow security handler to pass. 5189 v8::HandleScope scope2; 5190 LocalContext context2; 5191 v8::Handle<v8::Object> global2 = context2->Global(); 5192 global2->Set(v8_str("othercontext"), global0); 5193 v8::Handle<Script> script2 = 5194 v8_compile("othercontext.foo = 333; othercontext[0] = 888;"); 5195 script2->Run(); 5196 v8::Handle<Value> foo2 = global0->Get(v8_str("foo")); 5197 CHECK_EQ(333, foo2->Int32Value()); 5198 v8::Handle<Value> z2 = global0->Get(v8_str("0")); 5199 CHECK_EQ(888, z2->Int32Value()); 5200 } 5201 5202 context1->Exit(); 5203 context1.Dispose(); 5204 5205 context0->Exit(); 5206 context0.Dispose(); 5207 } 5208 5209 5210 THREADED_TEST(SecurityChecks) { 5211 v8::HandleScope handle_scope; 5212 LocalContext env1; 5213 v8::Persistent<Context> env2 = Context::New(); 5214 5215 Local<Value> foo = v8_str("foo"); 5216 Local<Value> bar = v8_str("bar"); 5217 5218 // Set to the same domain. 5219 env1->SetSecurityToken(foo); 5220 5221 // Create a function in env1. 5222 Script::Compile(v8_str("spy=function(){return spy;}"))->Run(); 5223 Local<Value> spy = env1->Global()->Get(v8_str("spy")); 5224 CHECK(spy->IsFunction()); 5225 5226 // Create another function accessing global objects. 5227 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run(); 5228 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2")); 5229 CHECK(spy2->IsFunction()); 5230 5231 // Switch to env2 in the same domain and invoke spy on env2. 5232 { 5233 env2->SetSecurityToken(foo); 5234 // Enter env2 5235 Context::Scope scope_env2(env2); 5236 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL); 5237 CHECK(result->IsFunction()); 5238 } 5239 5240 { 5241 env2->SetSecurityToken(bar); 5242 Context::Scope scope_env2(env2); 5243 5244 // Call cross_domain_call, it should throw an exception 5245 v8::TryCatch try_catch; 5246 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL); 5247 CHECK(try_catch.HasCaught()); 5248 } 5249 5250 env2.Dispose(); 5251 } 5252 5253 5254 // Regression test case for issue 1183439. 5255 THREADED_TEST(SecurityChecksForPrototypeChain) { 5256 v8::HandleScope scope; 5257 LocalContext current; 5258 v8::Persistent<Context> other = Context::New(); 5259 5260 // Change context to be able to get to the Object function in the 5261 // other context without hitting the security checks. 5262 v8::Local<Value> other_object; 5263 { Context::Scope scope(other); 5264 other_object = other->Global()->Get(v8_str("Object")); 5265 other->Global()->Set(v8_num(42), v8_num(87)); 5266 } 5267 5268 current->Global()->Set(v8_str("other"), other->Global()); 5269 CHECK(v8_compile("other")->Run()->Equals(other->Global())); 5270 5271 // Make sure the security check fails here and we get an undefined 5272 // result instead of getting the Object function. Repeat in a loop 5273 // to make sure to exercise the IC code. 5274 v8::Local<Script> access_other0 = v8_compile("other.Object"); 5275 v8::Local<Script> access_other1 = v8_compile("other[42]"); 5276 for (int i = 0; i < 5; i++) { 5277 CHECK(!access_other0->Run()->Equals(other_object)); 5278 CHECK(access_other0->Run()->IsUndefined()); 5279 CHECK(!access_other1->Run()->Equals(v8_num(87))); 5280 CHECK(access_other1->Run()->IsUndefined()); 5281 } 5282 5283 // Create an object that has 'other' in its prototype chain and make 5284 // sure we cannot access the Object function indirectly through 5285 // that. Repeat in a loop to make sure to exercise the IC code. 5286 v8_compile("function F() { };" 5287 "F.prototype = other;" 5288 "var f = new F();")->Run(); 5289 v8::Local<Script> access_f0 = v8_compile("f.Object"); 5290 v8::Local<Script> access_f1 = v8_compile("f[42]"); 5291 for (int j = 0; j < 5; j++) { 5292 CHECK(!access_f0->Run()->Equals(other_object)); 5293 CHECK(access_f0->Run()->IsUndefined()); 5294 CHECK(!access_f1->Run()->Equals(v8_num(87))); 5295 CHECK(access_f1->Run()->IsUndefined()); 5296 } 5297 5298 // Now it gets hairy: Set the prototype for the other global object 5299 // to be the current global object. The prototype chain for 'f' now 5300 // goes through 'other' but ends up in the current global object. 5301 { Context::Scope scope(other); 5302 other->Global()->Set(v8_str("__proto__"), current->Global()); 5303 } 5304 // Set a named and an index property on the current global 5305 // object. To force the lookup to go through the other global object, 5306 // the properties must not exist in the other global object. 5307 current->Global()->Set(v8_str("foo"), v8_num(100)); 5308 current->Global()->Set(v8_num(99), v8_num(101)); 5309 // Try to read the properties from f and make sure that the access 5310 // gets stopped by the security checks on the other global object. 5311 Local<Script> access_f2 = v8_compile("f.foo"); 5312 Local<Script> access_f3 = v8_compile("f[99]"); 5313 for (int k = 0; k < 5; k++) { 5314 CHECK(!access_f2->Run()->Equals(v8_num(100))); 5315 CHECK(access_f2->Run()->IsUndefined()); 5316 CHECK(!access_f3->Run()->Equals(v8_num(101))); 5317 CHECK(access_f3->Run()->IsUndefined()); 5318 } 5319 other.Dispose(); 5320 } 5321 5322 5323 THREADED_TEST(CrossDomainDelete) { 5324 v8::HandleScope handle_scope; 5325 LocalContext env1; 5326 v8::Persistent<Context> env2 = Context::New(); 5327 5328 Local<Value> foo = v8_str("foo"); 5329 Local<Value> bar = v8_str("bar"); 5330 5331 // Set to the same domain. 5332 env1->SetSecurityToken(foo); 5333 env2->SetSecurityToken(foo); 5334 5335 env1->Global()->Set(v8_str("prop"), v8_num(3)); 5336 env2->Global()->Set(v8_str("env1"), env1->Global()); 5337 5338 // Change env2 to a different domain and delete env1.prop. 5339 env2->SetSecurityToken(bar); 5340 { 5341 Context::Scope scope_env2(env2); 5342 Local<Value> result = 5343 Script::Compile(v8_str("delete env1.prop"))->Run(); 5344 CHECK(result->IsFalse()); 5345 } 5346 5347 // Check that env1.prop still exists. 5348 Local<Value> v = env1->Global()->Get(v8_str("prop")); 5349 CHECK(v->IsNumber()); 5350 CHECK_EQ(3, v->Int32Value()); 5351 5352 env2.Dispose(); 5353 } 5354 5355 5356 THREADED_TEST(CrossDomainIsPropertyEnumerable) { 5357 v8::HandleScope handle_scope; 5358 LocalContext env1; 5359 v8::Persistent<Context> env2 = Context::New(); 5360 5361 Local<Value> foo = v8_str("foo"); 5362 Local<Value> bar = v8_str("bar"); 5363 5364 // Set to the same domain. 5365 env1->SetSecurityToken(foo); 5366 env2->SetSecurityToken(foo); 5367 5368 env1->Global()->Set(v8_str("prop"), v8_num(3)); 5369 env2->Global()->Set(v8_str("env1"), env1->Global()); 5370 5371 // env1.prop is enumerable in env2. 5372 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')"); 5373 { 5374 Context::Scope scope_env2(env2); 5375 Local<Value> result = Script::Compile(test)->Run(); 5376 CHECK(result->IsTrue()); 5377 } 5378 5379 // Change env2 to a different domain and test again. 5380 env2->SetSecurityToken(bar); 5381 { 5382 Context::Scope scope_env2(env2); 5383 Local<Value> result = Script::Compile(test)->Run(); 5384 CHECK(result->IsFalse()); 5385 } 5386 5387 env2.Dispose(); 5388 } 5389 5390 5391 THREADED_TEST(CrossDomainForIn) { 5392 v8::HandleScope handle_scope; 5393 LocalContext env1; 5394 v8::Persistent<Context> env2 = Context::New(); 5395 5396 Local<Value> foo = v8_str("foo"); 5397 Local<Value> bar = v8_str("bar"); 5398 5399 // Set to the same domain. 5400 env1->SetSecurityToken(foo); 5401 env2->SetSecurityToken(foo); 5402 5403 env1->Global()->Set(v8_str("prop"), v8_num(3)); 5404 env2->Global()->Set(v8_str("env1"), env1->Global()); 5405 5406 // Change env2 to a different domain and set env1's global object 5407 // as the __proto__ of an object in env2 and enumerate properties 5408 // in for-in. It shouldn't enumerate properties on env1's global 5409 // object. 5410 env2->SetSecurityToken(bar); 5411 { 5412 Context::Scope scope_env2(env2); 5413 Local<Value> result = 5414 CompileRun("(function(){var obj = {'__proto__':env1};" 5415 "for (var p in obj)" 5416 " if (p == 'prop') return false;" 5417 "return true;})()"); 5418 CHECK(result->IsTrue()); 5419 } 5420 env2.Dispose(); 5421 } 5422 5423 5424 TEST(ContextDetachGlobal) { 5425 v8::HandleScope handle_scope; 5426 LocalContext env1; 5427 v8::Persistent<Context> env2 = Context::New(); 5428 5429 Local<v8::Object> global1 = env1->Global(); 5430 5431 Local<Value> foo = v8_str("foo"); 5432 5433 // Set to the same domain. 5434 env1->SetSecurityToken(foo); 5435 env2->SetSecurityToken(foo); 5436 5437 // Enter env2 5438 env2->Enter(); 5439 5440 // Create a function in env2 and add a reference to it in env1. 5441 Local<v8::Object> global2 = env2->Global(); 5442 global2->Set(v8_str("prop"), v8::Integer::New(1)); 5443 CompileRun("function getProp() {return prop;}"); 5444 5445 env1->Global()->Set(v8_str("getProp"), 5446 global2->Get(v8_str("getProp"))); 5447 5448 // Detach env2's global, and reuse the global object of env2 5449 env2->Exit(); 5450 env2->DetachGlobal(); 5451 // env2 has a new global object. 5452 CHECK(!env2->Global()->Equals(global2)); 5453 5454 v8::Persistent<Context> env3 = 5455 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 5456 env3->SetSecurityToken(v8_str("bar")); 5457 env3->Enter(); 5458 5459 Local<v8::Object> global3 = env3->Global(); 5460 CHECK_EQ(global2, global3); 5461 CHECK(global3->Get(v8_str("prop"))->IsUndefined()); 5462 CHECK(global3->Get(v8_str("getProp"))->IsUndefined()); 5463 global3->Set(v8_str("prop"), v8::Integer::New(-1)); 5464 global3->Set(v8_str("prop2"), v8::Integer::New(2)); 5465 env3->Exit(); 5466 5467 // Call getProp in env1, and it should return the value 1 5468 { 5469 Local<Value> get_prop = global1->Get(v8_str("getProp")); 5470 CHECK(get_prop->IsFunction()); 5471 v8::TryCatch try_catch; 5472 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL); 5473 CHECK(!try_catch.HasCaught()); 5474 CHECK_EQ(1, r->Int32Value()); 5475 } 5476 5477 // Check that env3 is not accessible from env1 5478 { 5479 Local<Value> r = global3->Get(v8_str("prop2")); 5480 CHECK(r->IsUndefined()); 5481 } 5482 5483 env2.Dispose(); 5484 env3.Dispose(); 5485 } 5486 5487 5488 TEST(DetachAndReattachGlobal) { 5489 v8::HandleScope scope; 5490 LocalContext env1; 5491 5492 // Create second environment. 5493 v8::Persistent<Context> env2 = Context::New(); 5494 5495 Local<Value> foo = v8_str("foo"); 5496 5497 // Set same security token for env1 and env2. 5498 env1->SetSecurityToken(foo); 5499 env2->SetSecurityToken(foo); 5500 5501 // Create a property on the global object in env2. 5502 { 5503 v8::Context::Scope scope(env2); 5504 env2->Global()->Set(v8_str("p"), v8::Integer::New(42)); 5505 } 5506 5507 // Create a reference to env2 global from env1 global. 5508 env1->Global()->Set(v8_str("other"), env2->Global()); 5509 5510 // Check that we have access to other.p in env2 from env1. 5511 Local<Value> result = CompileRun("other.p"); 5512 CHECK(result->IsInt32()); 5513 CHECK_EQ(42, result->Int32Value()); 5514 5515 // Hold on to global from env2 and detach global from env2. 5516 Local<v8::Object> global2 = env2->Global(); 5517 env2->DetachGlobal(); 5518 5519 // Check that the global has been detached. No other.p property can 5520 // be found. 5521 result = CompileRun("other.p"); 5522 CHECK(result->IsUndefined()); 5523 5524 // Reuse global2 for env3. 5525 v8::Persistent<Context> env3 = 5526 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 5527 CHECK_EQ(global2, env3->Global()); 5528 5529 // Start by using the same security token for env3 as for env1 and env2. 5530 env3->SetSecurityToken(foo); 5531 5532 // Create a property on the global object in env3. 5533 { 5534 v8::Context::Scope scope(env3); 5535 env3->Global()->Set(v8_str("p"), v8::Integer::New(24)); 5536 } 5537 5538 // Check that other.p is now the property in env3 and that we have access. 5539 result = CompileRun("other.p"); 5540 CHECK(result->IsInt32()); 5541 CHECK_EQ(24, result->Int32Value()); 5542 5543 // Change security token for env3 to something different from env1 and env2. 5544 env3->SetSecurityToken(v8_str("bar")); 5545 5546 // Check that we do not have access to other.p in env1. |other| is now 5547 // the global object for env3 which has a different security token, 5548 // so access should be blocked. 5549 result = CompileRun("other.p"); 5550 CHECK(result->IsUndefined()); 5551 5552 // Detach the global for env3 and reattach it to env2. 5553 env3->DetachGlobal(); 5554 env2->ReattachGlobal(global2); 5555 5556 // Check that we have access to other.p again in env1. |other| is now 5557 // the global object for env2 which has the same security token as env1. 5558 result = CompileRun("other.p"); 5559 CHECK(result->IsInt32()); 5560 CHECK_EQ(42, result->Int32Value()); 5561 5562 env2.Dispose(); 5563 env3.Dispose(); 5564 } 5565 5566 5567 static bool allowed_access_type[v8::ACCESS_KEYS + 1] = { false }; 5568 static bool NamedAccessBlocker(Local<v8::Object> global, 5569 Local<Value> name, 5570 v8::AccessType type, 5571 Local<Value> data) { 5572 return Context::GetCurrent()->Global()->Equals(global) || 5573 allowed_access_type[type]; 5574 } 5575 5576 5577 static bool IndexedAccessBlocker(Local<v8::Object> global, 5578 uint32_t key, 5579 v8::AccessType type, 5580 Local<Value> data) { 5581 return Context::GetCurrent()->Global()->Equals(global) || 5582 allowed_access_type[type]; 5583 } 5584 5585 5586 static int g_echo_value = -1; 5587 static v8::Handle<Value> EchoGetter(Local<String> name, 5588 const AccessorInfo& info) { 5589 return v8_num(g_echo_value); 5590 } 5591 5592 5593 static void EchoSetter(Local<String> name, 5594 Local<Value> value, 5595 const AccessorInfo&) { 5596 if (value->IsNumber()) 5597 g_echo_value = value->Int32Value(); 5598 } 5599 5600 5601 static v8::Handle<Value> UnreachableGetter(Local<String> name, 5602 const AccessorInfo& info) { 5603 CHECK(false); // This function should not be called.. 5604 return v8::Undefined(); 5605 } 5606 5607 5608 static void UnreachableSetter(Local<String>, Local<Value>, 5609 const AccessorInfo&) { 5610 CHECK(false); // This function should nto be called. 5611 } 5612 5613 5614 TEST(AccessControl) { 5615 v8::HandleScope handle_scope; 5616 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 5617 5618 global_template->SetAccessCheckCallbacks(NamedAccessBlocker, 5619 IndexedAccessBlocker); 5620 5621 // Add an accessor accessible by cross-domain JS code. 5622 global_template->SetAccessor( 5623 v8_str("accessible_prop"), 5624 EchoGetter, EchoSetter, 5625 v8::Handle<Value>(), 5626 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); 5627 5628 // Add an accessor that is not accessible by cross-domain JS code. 5629 global_template->SetAccessor(v8_str("blocked_prop"), 5630 UnreachableGetter, UnreachableSetter, 5631 v8::Handle<Value>(), 5632 v8::DEFAULT); 5633 5634 // Create an environment 5635 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 5636 context0->Enter(); 5637 5638 v8::Handle<v8::Object> global0 = context0->Global(); 5639 5640 // Define a property with JS getter and setter. 5641 CompileRun( 5642 "function getter() { return 'getter'; };\n" 5643 "function setter() { return 'setter'; }\n" 5644 "Object.defineProperty(this, 'js_accessor_p', {get:getter, set:setter})"); 5645 5646 Local<Value> getter = global0->Get(v8_str("getter")); 5647 Local<Value> setter = global0->Get(v8_str("setter")); 5648 5649 // And define normal element. 5650 global0->Set(239, v8_str("239")); 5651 5652 // Define an element with JS getter and setter. 5653 CompileRun( 5654 "function el_getter() { return 'el_getter'; };\n" 5655 "function el_setter() { return 'el_setter'; };\n" 5656 "Object.defineProperty(this, '42', {get: el_getter, set: el_setter});"); 5657 5658 Local<Value> el_getter = global0->Get(v8_str("el_getter")); 5659 Local<Value> el_setter = global0->Get(v8_str("el_setter")); 5660 5661 v8::HandleScope scope1; 5662 5663 v8::Persistent<Context> context1 = Context::New(); 5664 context1->Enter(); 5665 5666 v8::Handle<v8::Object> global1 = context1->Global(); 5667 global1->Set(v8_str("other"), global0); 5668 5669 // Access blocked property. 5670 CompileRun("other.blocked_prop = 1"); 5671 5672 ExpectUndefined("other.blocked_prop"); 5673 ExpectUndefined( 5674 "Object.getOwnPropertyDescriptor(other, 'blocked_prop')"); 5675 ExpectFalse("propertyIsEnumerable.call(other, 'blocked_prop')"); 5676 5677 // Enable ACCESS_HAS 5678 allowed_access_type[v8::ACCESS_HAS] = true; 5679 ExpectUndefined("other.blocked_prop"); 5680 // ... and now we can get the descriptor... 5681 ExpectUndefined( 5682 "Object.getOwnPropertyDescriptor(other, 'blocked_prop').value"); 5683 // ... and enumerate the property. 5684 ExpectTrue("propertyIsEnumerable.call(other, 'blocked_prop')"); 5685 allowed_access_type[v8::ACCESS_HAS] = false; 5686 5687 // Access blocked element. 5688 CompileRun("other[239] = 1"); 5689 5690 ExpectUndefined("other[239]"); 5691 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '239')"); 5692 ExpectFalse("propertyIsEnumerable.call(other, '239')"); 5693 5694 // Enable ACCESS_HAS 5695 allowed_access_type[v8::ACCESS_HAS] = true; 5696 ExpectUndefined("other[239]"); 5697 // ... and now we can get the descriptor... 5698 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '239').value"); 5699 // ... and enumerate the property. 5700 ExpectTrue("propertyIsEnumerable.call(other, '239')"); 5701 allowed_access_type[v8::ACCESS_HAS] = false; 5702 5703 // Access a property with JS accessor. 5704 CompileRun("other.js_accessor_p = 2"); 5705 5706 ExpectUndefined("other.js_accessor_p"); 5707 ExpectUndefined( 5708 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p')"); 5709 5710 // Enable ACCESS_HAS. 5711 allowed_access_type[v8::ACCESS_HAS] = true; 5712 ExpectUndefined("other.js_accessor_p"); 5713 ExpectUndefined( 5714 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get"); 5715 ExpectUndefined( 5716 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set"); 5717 ExpectUndefined( 5718 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value"); 5719 allowed_access_type[v8::ACCESS_HAS] = false; 5720 5721 // Enable both ACCESS_HAS and ACCESS_GET. 5722 allowed_access_type[v8::ACCESS_HAS] = true; 5723 allowed_access_type[v8::ACCESS_GET] = true; 5724 5725 ExpectString("other.js_accessor_p", "getter"); 5726 ExpectObject( 5727 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter); 5728 ExpectUndefined( 5729 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set"); 5730 ExpectUndefined( 5731 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value"); 5732 5733 allowed_access_type[v8::ACCESS_GET] = false; 5734 allowed_access_type[v8::ACCESS_HAS] = false; 5735 5736 // Enable both ACCESS_HAS and ACCESS_SET. 5737 allowed_access_type[v8::ACCESS_HAS] = true; 5738 allowed_access_type[v8::ACCESS_SET] = true; 5739 5740 ExpectUndefined("other.js_accessor_p"); 5741 ExpectUndefined( 5742 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get"); 5743 ExpectObject( 5744 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter); 5745 ExpectUndefined( 5746 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value"); 5747 5748 allowed_access_type[v8::ACCESS_SET] = false; 5749 allowed_access_type[v8::ACCESS_HAS] = false; 5750 5751 // Enable both ACCESS_HAS, ACCESS_GET and ACCESS_SET. 5752 allowed_access_type[v8::ACCESS_HAS] = true; 5753 allowed_access_type[v8::ACCESS_GET] = true; 5754 allowed_access_type[v8::ACCESS_SET] = true; 5755 5756 ExpectString("other.js_accessor_p", "getter"); 5757 ExpectObject( 5758 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter); 5759 ExpectObject( 5760 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter); 5761 ExpectUndefined( 5762 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value"); 5763 5764 allowed_access_type[v8::ACCESS_SET] = false; 5765 allowed_access_type[v8::ACCESS_GET] = false; 5766 allowed_access_type[v8::ACCESS_HAS] = false; 5767 5768 // Access an element with JS accessor. 5769 CompileRun("other[42] = 2"); 5770 5771 ExpectUndefined("other[42]"); 5772 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42')"); 5773 5774 // Enable ACCESS_HAS. 5775 allowed_access_type[v8::ACCESS_HAS] = true; 5776 ExpectUndefined("other[42]"); 5777 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').get"); 5778 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').set"); 5779 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value"); 5780 allowed_access_type[v8::ACCESS_HAS] = false; 5781 5782 // Enable both ACCESS_HAS and ACCESS_GET. 5783 allowed_access_type[v8::ACCESS_HAS] = true; 5784 allowed_access_type[v8::ACCESS_GET] = true; 5785 5786 ExpectString("other[42]", "el_getter"); 5787 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter); 5788 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').set"); 5789 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value"); 5790 5791 allowed_access_type[v8::ACCESS_GET] = false; 5792 allowed_access_type[v8::ACCESS_HAS] = false; 5793 5794 // Enable both ACCESS_HAS and ACCESS_SET. 5795 allowed_access_type[v8::ACCESS_HAS] = true; 5796 allowed_access_type[v8::ACCESS_SET] = true; 5797 5798 ExpectUndefined("other[42]"); 5799 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').get"); 5800 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter); 5801 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value"); 5802 5803 allowed_access_type[v8::ACCESS_SET] = false; 5804 allowed_access_type[v8::ACCESS_HAS] = false; 5805 5806 // Enable both ACCESS_HAS, ACCESS_GET and ACCESS_SET. 5807 allowed_access_type[v8::ACCESS_HAS] = true; 5808 allowed_access_type[v8::ACCESS_GET] = true; 5809 allowed_access_type[v8::ACCESS_SET] = true; 5810 5811 ExpectString("other[42]", "el_getter"); 5812 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter); 5813 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter); 5814 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value"); 5815 5816 allowed_access_type[v8::ACCESS_SET] = false; 5817 allowed_access_type[v8::ACCESS_GET] = false; 5818 allowed_access_type[v8::ACCESS_HAS] = false; 5819 5820 v8::Handle<Value> value; 5821 5822 // Access accessible property 5823 value = CompileRun("other.accessible_prop = 3"); 5824 CHECK(value->IsNumber()); 5825 CHECK_EQ(3, value->Int32Value()); 5826 CHECK_EQ(3, g_echo_value); 5827 5828 value = CompileRun("other.accessible_prop"); 5829 CHECK(value->IsNumber()); 5830 CHECK_EQ(3, value->Int32Value()); 5831 5832 value = CompileRun( 5833 "Object.getOwnPropertyDescriptor(other, 'accessible_prop').value"); 5834 CHECK(value->IsNumber()); 5835 CHECK_EQ(3, value->Int32Value()); 5836 5837 value = CompileRun("propertyIsEnumerable.call(other, 'accessible_prop')"); 5838 CHECK(value->IsTrue()); 5839 5840 // Enumeration doesn't enumerate accessors from inaccessible objects in 5841 // the prototype chain even if the accessors are in themselves accessible. 5842 value = 5843 CompileRun("(function(){var obj = {'__proto__':other};" 5844 "for (var p in obj)" 5845 " if (p == 'accessible_prop' || p == 'blocked_prop') {" 5846 " return false;" 5847 " }" 5848 "return true;})()"); 5849 CHECK(value->IsTrue()); 5850 5851 context1->Exit(); 5852 context0->Exit(); 5853 context1.Dispose(); 5854 context0.Dispose(); 5855 } 5856 5857 5858 TEST(AccessControlES5) { 5859 v8::HandleScope handle_scope; 5860 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 5861 5862 global_template->SetAccessCheckCallbacks(NamedAccessBlocker, 5863 IndexedAccessBlocker); 5864 5865 // Add accessible accessor. 5866 global_template->SetAccessor( 5867 v8_str("accessible_prop"), 5868 EchoGetter, EchoSetter, 5869 v8::Handle<Value>(), 5870 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); 5871 5872 5873 // Add an accessor that is not accessible by cross-domain JS code. 5874 global_template->SetAccessor(v8_str("blocked_prop"), 5875 UnreachableGetter, UnreachableSetter, 5876 v8::Handle<Value>(), 5877 v8::DEFAULT); 5878 5879 // Create an environment 5880 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 5881 context0->Enter(); 5882 5883 v8::Handle<v8::Object> global0 = context0->Global(); 5884 5885 v8::Persistent<Context> context1 = Context::New(); 5886 context1->Enter(); 5887 v8::Handle<v8::Object> global1 = context1->Global(); 5888 global1->Set(v8_str("other"), global0); 5889 5890 // Regression test for issue 1154. 5891 ExpectTrue("Object.keys(other).indexOf('blocked_prop') == -1"); 5892 5893 ExpectUndefined("other.blocked_prop"); 5894 5895 // Regression test for issue 1027. 5896 CompileRun("Object.defineProperty(\n" 5897 " other, 'blocked_prop', {configurable: false})"); 5898 ExpectUndefined("other.blocked_prop"); 5899 ExpectUndefined( 5900 "Object.getOwnPropertyDescriptor(other, 'blocked_prop')"); 5901 5902 // Regression test for issue 1171. 5903 ExpectTrue("Object.isExtensible(other)"); 5904 CompileRun("Object.preventExtensions(other)"); 5905 ExpectTrue("Object.isExtensible(other)"); 5906 5907 // Object.seal and Object.freeze. 5908 CompileRun("Object.freeze(other)"); 5909 ExpectTrue("Object.isExtensible(other)"); 5910 5911 CompileRun("Object.seal(other)"); 5912 ExpectTrue("Object.isExtensible(other)"); 5913 5914 // Regression test for issue 1250. 5915 // Make sure that we can set the accessible accessors value using normal 5916 // assignment. 5917 CompileRun("other.accessible_prop = 42"); 5918 CHECK_EQ(42, g_echo_value); 5919 5920 v8::Handle<Value> value; 5921 // We follow Safari in ignoring assignments to host object accessors. 5922 CompileRun("Object.defineProperty(other, 'accessible_prop', {value: -1})"); 5923 value = CompileRun("other.accessible_prop == 42"); 5924 CHECK(value->IsTrue()); 5925 } 5926 5927 5928 static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global, 5929 Local<Value> name, 5930 v8::AccessType type, 5931 Local<Value> data) { 5932 return false; 5933 } 5934 5935 5936 static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global, 5937 uint32_t key, 5938 v8::AccessType type, 5939 Local<Value> data) { 5940 return false; 5941 } 5942 5943 5944 THREADED_TEST(AccessControlGetOwnPropertyNames) { 5945 v8::HandleScope handle_scope; 5946 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(); 5947 5948 obj_template->Set(v8_str("x"), v8::Integer::New(42)); 5949 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker, 5950 GetOwnPropertyNamesIndexedBlocker); 5951 5952 // Create an environment 5953 v8::Persistent<Context> context0 = Context::New(NULL, obj_template); 5954 context0->Enter(); 5955 5956 v8::Handle<v8::Object> global0 = context0->Global(); 5957 5958 v8::HandleScope scope1; 5959 5960 v8::Persistent<Context> context1 = Context::New(); 5961 context1->Enter(); 5962 5963 v8::Handle<v8::Object> global1 = context1->Global(); 5964 global1->Set(v8_str("other"), global0); 5965 global1->Set(v8_str("object"), obj_template->NewInstance()); 5966 5967 v8::Handle<Value> value; 5968 5969 // Attempt to get the property names of the other global object and 5970 // of an object that requires access checks. Accessing the other 5971 // global object should be blocked by access checks on the global 5972 // proxy object. Accessing the object that requires access checks 5973 // is blocked by the access checks on the object itself. 5974 value = CompileRun("Object.getOwnPropertyNames(other).length == 0"); 5975 CHECK(value->IsTrue()); 5976 5977 value = CompileRun("Object.getOwnPropertyNames(object).length == 0"); 5978 CHECK(value->IsTrue()); 5979 5980 context1->Exit(); 5981 context0->Exit(); 5982 context1.Dispose(); 5983 context0.Dispose(); 5984 } 5985 5986 5987 static v8::Handle<v8::Array> NamedPropertyEnumerator(const AccessorInfo& info) { 5988 v8::Handle<v8::Array> result = v8::Array::New(1); 5989 result->Set(0, v8_str("x")); 5990 return result; 5991 } 5992 5993 5994 THREADED_TEST(GetOwnPropertyNamesWithInterceptor) { 5995 v8::HandleScope handle_scope; 5996 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(); 5997 5998 obj_template->Set(v8_str("x"), v8::Integer::New(42)); 5999 obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL, 6000 NamedPropertyEnumerator); 6001 6002 LocalContext context; 6003 v8::Handle<v8::Object> global = context->Global(); 6004 global->Set(v8_str("object"), obj_template->NewInstance()); 6005 6006 v8::Handle<Value> value = 6007 CompileRun("Object.getOwnPropertyNames(object).join(',')"); 6008 CHECK_EQ(v8_str("x"), value); 6009 } 6010 6011 6012 static v8::Handle<Value> ConstTenGetter(Local<String> name, 6013 const AccessorInfo& info) { 6014 return v8_num(10); 6015 } 6016 6017 6018 THREADED_TEST(CrossDomainAccessors) { 6019 v8::HandleScope handle_scope; 6020 6021 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(); 6022 6023 v8::Handle<v8::ObjectTemplate> global_template = 6024 func_template->InstanceTemplate(); 6025 6026 v8::Handle<v8::ObjectTemplate> proto_template = 6027 func_template->PrototypeTemplate(); 6028 6029 // Add an accessor to proto that's accessible by cross-domain JS code. 6030 proto_template->SetAccessor(v8_str("accessible"), 6031 ConstTenGetter, 0, 6032 v8::Handle<Value>(), 6033 v8::ALL_CAN_READ); 6034 6035 // Add an accessor that is not accessible by cross-domain JS code. 6036 global_template->SetAccessor(v8_str("unreachable"), 6037 UnreachableGetter, 0, 6038 v8::Handle<Value>(), 6039 v8::DEFAULT); 6040 6041 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 6042 context0->Enter(); 6043 6044 Local<v8::Object> global = context0->Global(); 6045 // Add a normal property that shadows 'accessible' 6046 global->Set(v8_str("accessible"), v8_num(11)); 6047 6048 // Enter a new context. 6049 v8::HandleScope scope1; 6050 v8::Persistent<Context> context1 = Context::New(); 6051 context1->Enter(); 6052 6053 v8::Handle<v8::Object> global1 = context1->Global(); 6054 global1->Set(v8_str("other"), global); 6055 6056 // Should return 10, instead of 11 6057 v8::Handle<Value> value = v8_compile("other.accessible")->Run(); 6058 CHECK(value->IsNumber()); 6059 CHECK_EQ(10, value->Int32Value()); 6060 6061 value = v8_compile("other.unreachable")->Run(); 6062 CHECK(value->IsUndefined()); 6063 6064 context1->Exit(); 6065 context0->Exit(); 6066 context1.Dispose(); 6067 context0.Dispose(); 6068 } 6069 6070 6071 static int named_access_count = 0; 6072 static int indexed_access_count = 0; 6073 6074 static bool NamedAccessCounter(Local<v8::Object> global, 6075 Local<Value> name, 6076 v8::AccessType type, 6077 Local<Value> data) { 6078 named_access_count++; 6079 return true; 6080 } 6081 6082 6083 static bool IndexedAccessCounter(Local<v8::Object> global, 6084 uint32_t key, 6085 v8::AccessType type, 6086 Local<Value> data) { 6087 indexed_access_count++; 6088 return true; 6089 } 6090 6091 6092 // This one is too easily disturbed by other tests. 6093 TEST(AccessControlIC) { 6094 named_access_count = 0; 6095 indexed_access_count = 0; 6096 6097 v8::HandleScope handle_scope; 6098 6099 // Create an environment. 6100 v8::Persistent<Context> context0 = Context::New(); 6101 context0->Enter(); 6102 6103 // Create an object that requires access-check functions to be 6104 // called for cross-domain access. 6105 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 6106 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 6107 IndexedAccessCounter); 6108 Local<v8::Object> object = object_template->NewInstance(); 6109 6110 v8::HandleScope scope1; 6111 6112 // Create another environment. 6113 v8::Persistent<Context> context1 = Context::New(); 6114 context1->Enter(); 6115 6116 // Make easy access to the object from the other environment. 6117 v8::Handle<v8::Object> global1 = context1->Global(); 6118 global1->Set(v8_str("obj"), object); 6119 6120 v8::Handle<Value> value; 6121 6122 // Check that the named access-control function is called every time. 6123 CompileRun("function testProp(obj) {" 6124 " for (var i = 0; i < 10; i++) obj.prop = 1;" 6125 " for (var j = 0; j < 10; j++) obj.prop;" 6126 " return obj.prop" 6127 "}"); 6128 value = CompileRun("testProp(obj)"); 6129 CHECK(value->IsNumber()); 6130 CHECK_EQ(1, value->Int32Value()); 6131 CHECK_EQ(21, named_access_count); 6132 6133 // Check that the named access-control function is called every time. 6134 CompileRun("var p = 'prop';" 6135 "function testKeyed(obj) {" 6136 " for (var i = 0; i < 10; i++) obj[p] = 1;" 6137 " for (var j = 0; j < 10; j++) obj[p];" 6138 " return obj[p];" 6139 "}"); 6140 // Use obj which requires access checks. No inline caching is used 6141 // in that case. 6142 value = CompileRun("testKeyed(obj)"); 6143 CHECK(value->IsNumber()); 6144 CHECK_EQ(1, value->Int32Value()); 6145 CHECK_EQ(42, named_access_count); 6146 // Force the inline caches into generic state and try again. 6147 CompileRun("testKeyed({ a: 0 })"); 6148 CompileRun("testKeyed({ b: 0 })"); 6149 value = CompileRun("testKeyed(obj)"); 6150 CHECK(value->IsNumber()); 6151 CHECK_EQ(1, value->Int32Value()); 6152 CHECK_EQ(63, named_access_count); 6153 6154 // Check that the indexed access-control function is called every time. 6155 CompileRun("function testIndexed(obj) {" 6156 " for (var i = 0; i < 10; i++) obj[0] = 1;" 6157 " for (var j = 0; j < 10; j++) obj[0];" 6158 " return obj[0]" 6159 "}"); 6160 value = CompileRun("testIndexed(obj)"); 6161 CHECK(value->IsNumber()); 6162 CHECK_EQ(1, value->Int32Value()); 6163 CHECK_EQ(21, indexed_access_count); 6164 // Force the inline caches into generic state. 6165 CompileRun("testIndexed(new Array(1))"); 6166 // Test that the indexed access check is called. 6167 value = CompileRun("testIndexed(obj)"); 6168 CHECK(value->IsNumber()); 6169 CHECK_EQ(1, value->Int32Value()); 6170 CHECK_EQ(42, indexed_access_count); 6171 6172 // Check that the named access check is called when invoking 6173 // functions on an object that requires access checks. 6174 CompileRun("obj.f = function() {}"); 6175 CompileRun("function testCallNormal(obj) {" 6176 " for (var i = 0; i < 10; i++) obj.f();" 6177 "}"); 6178 CompileRun("testCallNormal(obj)"); 6179 CHECK_EQ(74, named_access_count); 6180 6181 // Force obj into slow case. 6182 value = CompileRun("delete obj.prop"); 6183 CHECK(value->BooleanValue()); 6184 // Force inline caches into dictionary probing mode. 6185 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);"); 6186 // Test that the named access check is called. 6187 value = CompileRun("testProp(obj);"); 6188 CHECK(value->IsNumber()); 6189 CHECK_EQ(1, value->Int32Value()); 6190 CHECK_EQ(96, named_access_count); 6191 6192 // Force the call inline cache into dictionary probing mode. 6193 CompileRun("o.f = function() {}; testCallNormal(o)"); 6194 // Test that the named access check is still called for each 6195 // invocation of the function. 6196 value = CompileRun("testCallNormal(obj)"); 6197 CHECK_EQ(106, named_access_count); 6198 6199 context1->Exit(); 6200 context0->Exit(); 6201 context1.Dispose(); 6202 context0.Dispose(); 6203 } 6204 6205 6206 static bool NamedAccessFlatten(Local<v8::Object> global, 6207 Local<Value> name, 6208 v8::AccessType type, 6209 Local<Value> data) { 6210 char buf[100]; 6211 int len; 6212 6213 CHECK(name->IsString()); 6214 6215 memset(buf, 0x1, sizeof(buf)); 6216 len = name.As<String>()->WriteAscii(buf); 6217 CHECK_EQ(4, len); 6218 6219 uint16_t buf2[100]; 6220 6221 memset(buf, 0x1, sizeof(buf)); 6222 len = name.As<String>()->Write(buf2); 6223 CHECK_EQ(4, len); 6224 6225 return true; 6226 } 6227 6228 6229 static bool IndexedAccessFlatten(Local<v8::Object> global, 6230 uint32_t key, 6231 v8::AccessType type, 6232 Local<Value> data) { 6233 return true; 6234 } 6235 6236 6237 // Regression test. In access checks, operations that may cause 6238 // garbage collection are not allowed. It used to be the case that 6239 // using the Write operation on a string could cause a garbage 6240 // collection due to flattening of the string. This is no longer the 6241 // case. 6242 THREADED_TEST(AccessControlFlatten) { 6243 named_access_count = 0; 6244 indexed_access_count = 0; 6245 6246 v8::HandleScope handle_scope; 6247 6248 // Create an environment. 6249 v8::Persistent<Context> context0 = Context::New(); 6250 context0->Enter(); 6251 6252 // Create an object that requires access-check functions to be 6253 // called for cross-domain access. 6254 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 6255 object_template->SetAccessCheckCallbacks(NamedAccessFlatten, 6256 IndexedAccessFlatten); 6257 Local<v8::Object> object = object_template->NewInstance(); 6258 6259 v8::HandleScope scope1; 6260 6261 // Create another environment. 6262 v8::Persistent<Context> context1 = Context::New(); 6263 context1->Enter(); 6264 6265 // Make easy access to the object from the other environment. 6266 v8::Handle<v8::Object> global1 = context1->Global(); 6267 global1->Set(v8_str("obj"), object); 6268 6269 v8::Handle<Value> value; 6270 6271 value = v8_compile("var p = 'as' + 'df';")->Run(); 6272 value = v8_compile("obj[p];")->Run(); 6273 6274 context1->Exit(); 6275 context0->Exit(); 6276 context1.Dispose(); 6277 context0.Dispose(); 6278 } 6279 6280 6281 static v8::Handle<Value> AccessControlNamedGetter( 6282 Local<String>, const AccessorInfo&) { 6283 return v8::Integer::New(42); 6284 } 6285 6286 6287 static v8::Handle<Value> AccessControlNamedSetter( 6288 Local<String>, Local<Value> value, const AccessorInfo&) { 6289 return value; 6290 } 6291 6292 6293 static v8::Handle<Value> AccessControlIndexedGetter( 6294 uint32_t index, 6295 const AccessorInfo& info) { 6296 return v8_num(42); 6297 } 6298 6299 6300 static v8::Handle<Value> AccessControlIndexedSetter( 6301 uint32_t, Local<Value> value, const AccessorInfo&) { 6302 return value; 6303 } 6304 6305 6306 THREADED_TEST(AccessControlInterceptorIC) { 6307 named_access_count = 0; 6308 indexed_access_count = 0; 6309 6310 v8::HandleScope handle_scope; 6311 6312 // Create an environment. 6313 v8::Persistent<Context> context0 = Context::New(); 6314 context0->Enter(); 6315 6316 // Create an object that requires access-check functions to be 6317 // called for cross-domain access. The object also has interceptors 6318 // interceptor. 6319 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 6320 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 6321 IndexedAccessCounter); 6322 object_template->SetNamedPropertyHandler(AccessControlNamedGetter, 6323 AccessControlNamedSetter); 6324 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter, 6325 AccessControlIndexedSetter); 6326 Local<v8::Object> object = object_template->NewInstance(); 6327 6328 v8::HandleScope scope1; 6329 6330 // Create another environment. 6331 v8::Persistent<Context> context1 = Context::New(); 6332 context1->Enter(); 6333 6334 // Make easy access to the object from the other environment. 6335 v8::Handle<v8::Object> global1 = context1->Global(); 6336 global1->Set(v8_str("obj"), object); 6337 6338 v8::Handle<Value> value; 6339 6340 // Check that the named access-control function is called every time 6341 // eventhough there is an interceptor on the object. 6342 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run(); 6343 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;" 6344 "obj.x")->Run(); 6345 CHECK(value->IsNumber()); 6346 CHECK_EQ(42, value->Int32Value()); 6347 CHECK_EQ(21, named_access_count); 6348 6349 value = v8_compile("var p = 'x';")->Run(); 6350 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run(); 6351 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];" 6352 "obj[p]")->Run(); 6353 CHECK(value->IsNumber()); 6354 CHECK_EQ(42, value->Int32Value()); 6355 CHECK_EQ(42, named_access_count); 6356 6357 // Check that the indexed access-control function is called every 6358 // time eventhough there is an interceptor on the object. 6359 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run(); 6360 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];" 6361 "obj[0]")->Run(); 6362 CHECK(value->IsNumber()); 6363 CHECK_EQ(42, value->Int32Value()); 6364 CHECK_EQ(21, indexed_access_count); 6365 6366 context1->Exit(); 6367 context0->Exit(); 6368 context1.Dispose(); 6369 context0.Dispose(); 6370 } 6371 6372 6373 THREADED_TEST(Version) { 6374 v8::V8::GetVersion(); 6375 } 6376 6377 6378 static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) { 6379 ApiTestFuzzer::Fuzz(); 6380 return v8_num(12); 6381 } 6382 6383 6384 THREADED_TEST(InstanceProperties) { 6385 v8::HandleScope handle_scope; 6386 LocalContext context; 6387 6388 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 6389 Local<ObjectTemplate> instance = t->InstanceTemplate(); 6390 6391 instance->Set(v8_str("x"), v8_num(42)); 6392 instance->Set(v8_str("f"), 6393 v8::FunctionTemplate::New(InstanceFunctionCallback)); 6394 6395 Local<Value> o = t->GetFunction()->NewInstance(); 6396 6397 context->Global()->Set(v8_str("i"), o); 6398 Local<Value> value = Script::Compile(v8_str("i.x"))->Run(); 6399 CHECK_EQ(42, value->Int32Value()); 6400 6401 value = Script::Compile(v8_str("i.f()"))->Run(); 6402 CHECK_EQ(12, value->Int32Value()); 6403 } 6404 6405 6406 static v8::Handle<Value> 6407 GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) { 6408 ApiTestFuzzer::Fuzz(); 6409 return v8::Handle<Value>(); 6410 } 6411 6412 6413 THREADED_TEST(GlobalObjectInstanceProperties) { 6414 v8::HandleScope handle_scope; 6415 6416 Local<Value> global_object; 6417 6418 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 6419 t->InstanceTemplate()->SetNamedPropertyHandler( 6420 GlobalObjectInstancePropertiesGet); 6421 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 6422 instance_template->Set(v8_str("x"), v8_num(42)); 6423 instance_template->Set(v8_str("f"), 6424 v8::FunctionTemplate::New(InstanceFunctionCallback)); 6425 6426 // The script to check how Crankshaft compiles missing global function 6427 // invocations. function g is not defined and should throw on call. 6428 const char* script = 6429 "function wrapper(call) {" 6430 " var x = 0, y = 1;" 6431 " for (var i = 0; i < 1000; i++) {" 6432 " x += i * 100;" 6433 " y += i * 100;" 6434 " }" 6435 " if (call) g();" 6436 "}" 6437 "for (var i = 0; i < 17; i++) wrapper(false);" 6438 "var thrown = 0;" 6439 "try { wrapper(true); } catch (e) { thrown = 1; };" 6440 "thrown"; 6441 6442 { 6443 LocalContext env(NULL, instance_template); 6444 // Hold on to the global object so it can be used again in another 6445 // environment initialization. 6446 global_object = env->Global(); 6447 6448 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 6449 CHECK_EQ(42, value->Int32Value()); 6450 value = Script::Compile(v8_str("f()"))->Run(); 6451 CHECK_EQ(12, value->Int32Value()); 6452 value = Script::Compile(v8_str(script))->Run(); 6453 CHECK_EQ(1, value->Int32Value()); 6454 } 6455 6456 { 6457 // Create new environment reusing the global object. 6458 LocalContext env(NULL, instance_template, global_object); 6459 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 6460 CHECK_EQ(42, value->Int32Value()); 6461 value = Script::Compile(v8_str("f()"))->Run(); 6462 CHECK_EQ(12, value->Int32Value()); 6463 value = Script::Compile(v8_str(script))->Run(); 6464 CHECK_EQ(1, value->Int32Value()); 6465 } 6466 } 6467 6468 6469 THREADED_TEST(CallKnownGlobalReceiver) { 6470 v8::HandleScope handle_scope; 6471 6472 Local<Value> global_object; 6473 6474 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 6475 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 6476 6477 // The script to check that we leave global object not 6478 // global object proxy on stack when we deoptimize from inside 6479 // arguments evaluation. 6480 // To provoke error we need to both force deoptimization 6481 // from arguments evaluation and to force CallIC to take 6482 // CallIC_Miss code path that can't cope with global proxy. 6483 const char* script = 6484 "function bar(x, y) { try { } finally { } }" 6485 "function baz(x) { try { } finally { } }" 6486 "function bom(x) { try { } finally { } }" 6487 "function foo(x) { bar([x], bom(2)); }" 6488 "for (var i = 0; i < 10000; i++) foo(1);" 6489 "foo"; 6490 6491 Local<Value> foo; 6492 { 6493 LocalContext env(NULL, instance_template); 6494 // Hold on to the global object so it can be used again in another 6495 // environment initialization. 6496 global_object = env->Global(); 6497 foo = Script::Compile(v8_str(script))->Run(); 6498 } 6499 6500 { 6501 // Create new environment reusing the global object. 6502 LocalContext env(NULL, instance_template, global_object); 6503 env->Global()->Set(v8_str("foo"), foo); 6504 Local<Value> value = Script::Compile(v8_str("foo()"))->Run(); 6505 } 6506 } 6507 6508 6509 static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) { 6510 ApiTestFuzzer::Fuzz(); 6511 return v8_num(42); 6512 } 6513 6514 6515 static int shadow_y; 6516 static int shadow_y_setter_call_count; 6517 static int shadow_y_getter_call_count; 6518 6519 6520 static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) { 6521 shadow_y_setter_call_count++; 6522 shadow_y = 42; 6523 } 6524 6525 6526 static v8::Handle<Value> ShadowYGetter(Local<String> name, 6527 const AccessorInfo& info) { 6528 ApiTestFuzzer::Fuzz(); 6529 shadow_y_getter_call_count++; 6530 return v8_num(shadow_y); 6531 } 6532 6533 6534 static v8::Handle<Value> ShadowIndexedGet(uint32_t index, 6535 const AccessorInfo& info) { 6536 return v8::Handle<Value>(); 6537 } 6538 6539 6540 static v8::Handle<Value> ShadowNamedGet(Local<String> key, 6541 const AccessorInfo&) { 6542 return v8::Handle<Value>(); 6543 } 6544 6545 6546 THREADED_TEST(ShadowObject) { 6547 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0; 6548 v8::HandleScope handle_scope; 6549 6550 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New(); 6551 LocalContext context(NULL, global_template); 6552 6553 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 6554 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet); 6555 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet); 6556 Local<ObjectTemplate> proto = t->PrototypeTemplate(); 6557 Local<ObjectTemplate> instance = t->InstanceTemplate(); 6558 6559 // Only allow calls of f on instances of t. 6560 Local<v8::Signature> signature = v8::Signature::New(t); 6561 proto->Set(v8_str("f"), 6562 v8::FunctionTemplate::New(ShadowFunctionCallback, 6563 Local<Value>(), 6564 signature)); 6565 proto->Set(v8_str("x"), v8_num(12)); 6566 6567 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter); 6568 6569 Local<Value> o = t->GetFunction()->NewInstance(); 6570 context->Global()->Set(v8_str("__proto__"), o); 6571 6572 Local<Value> value = 6573 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run(); 6574 CHECK(value->IsBoolean()); 6575 CHECK(!value->BooleanValue()); 6576 6577 value = Script::Compile(v8_str("x"))->Run(); 6578 CHECK_EQ(12, value->Int32Value()); 6579 6580 value = Script::Compile(v8_str("f()"))->Run(); 6581 CHECK_EQ(42, value->Int32Value()); 6582 6583 Script::Compile(v8_str("y = 42"))->Run(); 6584 CHECK_EQ(1, shadow_y_setter_call_count); 6585 value = Script::Compile(v8_str("y"))->Run(); 6586 CHECK_EQ(1, shadow_y_getter_call_count); 6587 CHECK_EQ(42, value->Int32Value()); 6588 } 6589 6590 6591 THREADED_TEST(HiddenPrototype) { 6592 v8::HandleScope handle_scope; 6593 LocalContext context; 6594 6595 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 6596 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 6597 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 6598 t1->SetHiddenPrototype(true); 6599 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 6600 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 6601 t2->SetHiddenPrototype(true); 6602 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 6603 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 6604 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 6605 6606 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 6607 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 6608 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 6609 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 6610 6611 // Setting the prototype on an object skips hidden prototypes. 6612 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 6613 o0->Set(v8_str("__proto__"), o1); 6614 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 6615 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 6616 o0->Set(v8_str("__proto__"), o2); 6617 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 6618 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 6619 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 6620 o0->Set(v8_str("__proto__"), o3); 6621 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 6622 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 6623 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 6624 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 6625 6626 // Getting the prototype of o0 should get the first visible one 6627 // which is o3. Therefore, z should not be defined on the prototype 6628 // object. 6629 Local<Value> proto = o0->Get(v8_str("__proto__")); 6630 CHECK(proto->IsObject()); 6631 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined()); 6632 } 6633 6634 6635 THREADED_TEST(SetPrototype) { 6636 v8::HandleScope handle_scope; 6637 LocalContext context; 6638 6639 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 6640 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 6641 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 6642 t1->SetHiddenPrototype(true); 6643 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 6644 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 6645 t2->SetHiddenPrototype(true); 6646 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 6647 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 6648 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 6649 6650 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 6651 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 6652 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 6653 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 6654 6655 // Setting the prototype on an object does not skip hidden prototypes. 6656 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 6657 CHECK(o0->SetPrototype(o1)); 6658 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 6659 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 6660 CHECK(o1->SetPrototype(o2)); 6661 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 6662 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 6663 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 6664 CHECK(o2->SetPrototype(o3)); 6665 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 6666 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 6667 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 6668 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 6669 6670 // Getting the prototype of o0 should get the first visible one 6671 // which is o3. Therefore, z should not be defined on the prototype 6672 // object. 6673 Local<Value> proto = o0->Get(v8_str("__proto__")); 6674 CHECK(proto->IsObject()); 6675 CHECK_EQ(proto.As<v8::Object>(), o3); 6676 6677 // However, Object::GetPrototype ignores hidden prototype. 6678 Local<Value> proto0 = o0->GetPrototype(); 6679 CHECK(proto0->IsObject()); 6680 CHECK_EQ(proto0.As<v8::Object>(), o1); 6681 6682 Local<Value> proto1 = o1->GetPrototype(); 6683 CHECK(proto1->IsObject()); 6684 CHECK_EQ(proto1.As<v8::Object>(), o2); 6685 6686 Local<Value> proto2 = o2->GetPrototype(); 6687 CHECK(proto2->IsObject()); 6688 CHECK_EQ(proto2.As<v8::Object>(), o3); 6689 } 6690 6691 6692 THREADED_TEST(SetPrototypeThrows) { 6693 v8::HandleScope handle_scope; 6694 LocalContext context; 6695 6696 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 6697 6698 Local<v8::Object> o0 = t->GetFunction()->NewInstance(); 6699 Local<v8::Object> o1 = t->GetFunction()->NewInstance(); 6700 6701 CHECK(o0->SetPrototype(o1)); 6702 // If setting the prototype leads to the cycle, SetPrototype should 6703 // return false and keep VM in sane state. 6704 v8::TryCatch try_catch; 6705 CHECK(!o1->SetPrototype(o0)); 6706 CHECK(!try_catch.HasCaught()); 6707 ASSERT(!i::Isolate::Current()->has_pending_exception()); 6708 6709 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value()); 6710 } 6711 6712 6713 THREADED_TEST(GetterSetterExceptions) { 6714 v8::HandleScope handle_scope; 6715 LocalContext context; 6716 CompileRun( 6717 "function Foo() { };" 6718 "function Throw() { throw 5; };" 6719 "var x = { };" 6720 "x.__defineSetter__('set', Throw);" 6721 "x.__defineGetter__('get', Throw);"); 6722 Local<v8::Object> x = 6723 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x"))); 6724 v8::TryCatch try_catch; 6725 x->Set(v8_str("set"), v8::Integer::New(8)); 6726 x->Get(v8_str("get")); 6727 x->Set(v8_str("set"), v8::Integer::New(8)); 6728 x->Get(v8_str("get")); 6729 x->Set(v8_str("set"), v8::Integer::New(8)); 6730 x->Get(v8_str("get")); 6731 x->Set(v8_str("set"), v8::Integer::New(8)); 6732 x->Get(v8_str("get")); 6733 } 6734 6735 6736 THREADED_TEST(Constructor) { 6737 v8::HandleScope handle_scope; 6738 LocalContext context; 6739 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 6740 templ->SetClassName(v8_str("Fun")); 6741 Local<Function> cons = templ->GetFunction(); 6742 context->Global()->Set(v8_str("Fun"), cons); 6743 Local<v8::Object> inst = cons->NewInstance(); 6744 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst); 6745 Local<Value> value = CompileRun("(new Fun()).constructor === Fun"); 6746 CHECK(value->BooleanValue()); 6747 } 6748 6749 THREADED_TEST(FunctionDescriptorException) { 6750 v8::HandleScope handle_scope; 6751 LocalContext context; 6752 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 6753 templ->SetClassName(v8_str("Fun")); 6754 Local<Function> cons = templ->GetFunction(); 6755 context->Global()->Set(v8_str("Fun"), cons); 6756 Local<Value> value = CompileRun( 6757 "function test() {" 6758 " try {" 6759 " (new Fun()).blah()" 6760 " } catch (e) {" 6761 " var str = String(e);" 6762 " if (str.indexOf('TypeError') == -1) return 1;" 6763 " if (str.indexOf('[object Fun]') != -1) return 2;" 6764 " if (str.indexOf('#<Fun>') == -1) return 3;" 6765 " return 0;" 6766 " }" 6767 " return 4;" 6768 "}" 6769 "test();"); 6770 CHECK_EQ(0, value->Int32Value()); 6771 } 6772 6773 6774 THREADED_TEST(EvalAliasedDynamic) { 6775 v8::HandleScope scope; 6776 LocalContext current; 6777 6778 // Tests where aliased eval can only be resolved dynamically. 6779 Local<Script> script = 6780 Script::Compile(v8_str("function f(x) { " 6781 " var foo = 2;" 6782 " with (x) { return eval('foo'); }" 6783 "}" 6784 "foo = 0;" 6785 "result1 = f(new Object());" 6786 "result2 = f(this);" 6787 "var x = new Object();" 6788 "x.eval = function(x) { return 1; };" 6789 "result3 = f(x);")); 6790 script->Run(); 6791 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value()); 6792 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value()); 6793 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value()); 6794 6795 v8::TryCatch try_catch; 6796 script = 6797 Script::Compile(v8_str("function f(x) { " 6798 " var bar = 2;" 6799 " with (x) { return eval('bar'); }" 6800 "}" 6801 "f(this)")); 6802 script->Run(); 6803 CHECK(try_catch.HasCaught()); 6804 try_catch.Reset(); 6805 } 6806 6807 6808 THREADED_TEST(CrossEval) { 6809 v8::HandleScope scope; 6810 LocalContext other; 6811 LocalContext current; 6812 6813 Local<String> token = v8_str("<security token>"); 6814 other->SetSecurityToken(token); 6815 current->SetSecurityToken(token); 6816 6817 // Setup reference from current to other. 6818 current->Global()->Set(v8_str("other"), other->Global()); 6819 6820 // Check that new variables are introduced in other context. 6821 Local<Script> script = 6822 Script::Compile(v8_str("other.eval('var foo = 1234')")); 6823 script->Run(); 6824 Local<Value> foo = other->Global()->Get(v8_str("foo")); 6825 CHECK_EQ(1234, foo->Int32Value()); 6826 CHECK(!current->Global()->Has(v8_str("foo"))); 6827 6828 // Check that writing to non-existing properties introduces them in 6829 // the other context. 6830 script = 6831 Script::Compile(v8_str("other.eval('na = 1234')")); 6832 script->Run(); 6833 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value()); 6834 CHECK(!current->Global()->Has(v8_str("na"))); 6835 6836 // Check that global variables in current context are not visible in other 6837 // context. 6838 v8::TryCatch try_catch; 6839 script = 6840 Script::Compile(v8_str("var bar = 42; other.eval('bar');")); 6841 Local<Value> result = script->Run(); 6842 CHECK(try_catch.HasCaught()); 6843 try_catch.Reset(); 6844 6845 // Check that local variables in current context are not visible in other 6846 // context. 6847 script = 6848 Script::Compile(v8_str("(function() { " 6849 " var baz = 87;" 6850 " return other.eval('baz');" 6851 "})();")); 6852 result = script->Run(); 6853 CHECK(try_catch.HasCaught()); 6854 try_catch.Reset(); 6855 6856 // Check that global variables in the other environment are visible 6857 // when evaluting code. 6858 other->Global()->Set(v8_str("bis"), v8_num(1234)); 6859 script = Script::Compile(v8_str("other.eval('bis')")); 6860 CHECK_EQ(1234, script->Run()->Int32Value()); 6861 CHECK(!try_catch.HasCaught()); 6862 6863 // Check that the 'this' pointer points to the global object evaluating 6864 // code. 6865 other->Global()->Set(v8_str("t"), other->Global()); 6866 script = Script::Compile(v8_str("other.eval('this == t')")); 6867 result = script->Run(); 6868 CHECK(result->IsTrue()); 6869 CHECK(!try_catch.HasCaught()); 6870 6871 // Check that variables introduced in with-statement are not visible in 6872 // other context. 6873 script = 6874 Script::Compile(v8_str("with({x:2}){other.eval('x')}")); 6875 result = script->Run(); 6876 CHECK(try_catch.HasCaught()); 6877 try_catch.Reset(); 6878 6879 // Check that you cannot use 'eval.call' with another object than the 6880 // current global object. 6881 script = 6882 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')")); 6883 result = script->Run(); 6884 CHECK(try_catch.HasCaught()); 6885 } 6886 6887 6888 // Test that calling eval in a context which has been detached from 6889 // its global throws an exception. This behavior is consistent with 6890 // other JavaScript implementations. 6891 THREADED_TEST(EvalInDetachedGlobal) { 6892 v8::HandleScope scope; 6893 6894 v8::Persistent<Context> context0 = Context::New(); 6895 v8::Persistent<Context> context1 = Context::New(); 6896 6897 // Setup function in context0 that uses eval from context0. 6898 context0->Enter(); 6899 v8::Handle<v8::Value> fun = 6900 CompileRun("var x = 42;" 6901 "(function() {" 6902 " var e = eval;" 6903 " return function(s) { return e(s); }" 6904 "})()"); 6905 context0->Exit(); 6906 6907 // Put the function into context1 and call it before and after 6908 // detaching the global. Before detaching, the call succeeds and 6909 // after detaching and exception is thrown. 6910 context1->Enter(); 6911 context1->Global()->Set(v8_str("fun"), fun); 6912 v8::Handle<v8::Value> x_value = CompileRun("fun('x')"); 6913 CHECK_EQ(42, x_value->Int32Value()); 6914 context0->DetachGlobal(); 6915 v8::TryCatch catcher; 6916 x_value = CompileRun("fun('x')"); 6917 CHECK(x_value.IsEmpty()); 6918 CHECK(catcher.HasCaught()); 6919 context1->Exit(); 6920 6921 context1.Dispose(); 6922 context0.Dispose(); 6923 } 6924 6925 6926 THREADED_TEST(CrossLazyLoad) { 6927 v8::HandleScope scope; 6928 LocalContext other; 6929 LocalContext current; 6930 6931 Local<String> token = v8_str("<security token>"); 6932 other->SetSecurityToken(token); 6933 current->SetSecurityToken(token); 6934 6935 // Setup reference from current to other. 6936 current->Global()->Set(v8_str("other"), other->Global()); 6937 6938 // Trigger lazy loading in other context. 6939 Local<Script> script = 6940 Script::Compile(v8_str("other.eval('new Date(42)')")); 6941 Local<Value> value = script->Run(); 6942 CHECK_EQ(42.0, value->NumberValue()); 6943 } 6944 6945 6946 static v8::Handle<Value> call_as_function(const v8::Arguments& args) { 6947 ApiTestFuzzer::Fuzz(); 6948 if (args.IsConstructCall()) { 6949 if (args[0]->IsInt32()) { 6950 return v8_num(-args[0]->Int32Value()); 6951 } 6952 } 6953 6954 return args[0]; 6955 } 6956 6957 6958 // Test that a call handler can be set for objects which will allow 6959 // non-function objects created through the API to be called as 6960 // functions. 6961 THREADED_TEST(CallAsFunction) { 6962 v8::HandleScope scope; 6963 LocalContext context; 6964 6965 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 6966 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 6967 instance_template->SetCallAsFunctionHandler(call_as_function); 6968 Local<v8::Object> instance = t->GetFunction()->NewInstance(); 6969 context->Global()->Set(v8_str("obj"), instance); 6970 v8::TryCatch try_catch; 6971 Local<Value> value; 6972 CHECK(!try_catch.HasCaught()); 6973 6974 value = CompileRun("obj(42)"); 6975 CHECK(!try_catch.HasCaught()); 6976 CHECK_EQ(42, value->Int32Value()); 6977 6978 value = CompileRun("(function(o){return o(49)})(obj)"); 6979 CHECK(!try_catch.HasCaught()); 6980 CHECK_EQ(49, value->Int32Value()); 6981 6982 // test special case of call as function 6983 value = CompileRun("[obj]['0'](45)"); 6984 CHECK(!try_catch.HasCaught()); 6985 CHECK_EQ(45, value->Int32Value()); 6986 6987 value = CompileRun("obj.call = Function.prototype.call;" 6988 "obj.call(null, 87)"); 6989 CHECK(!try_catch.HasCaught()); 6990 CHECK_EQ(87, value->Int32Value()); 6991 6992 // Regression tests for bug #1116356: Calling call through call/apply 6993 // must work for non-function receivers. 6994 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])"; 6995 value = CompileRun(apply_99); 6996 CHECK(!try_catch.HasCaught()); 6997 CHECK_EQ(99, value->Int32Value()); 6998 6999 const char* call_17 = "Function.prototype.call.call(obj, this, 17)"; 7000 value = CompileRun(call_17); 7001 CHECK(!try_catch.HasCaught()); 7002 CHECK_EQ(17, value->Int32Value()); 7003 7004 // Check that the call-as-function handler can be called through 7005 // new. 7006 value = CompileRun("new obj(43)"); 7007 CHECK(!try_catch.HasCaught()); 7008 CHECK_EQ(-43, value->Int32Value()); 7009 } 7010 7011 7012 static int CountHandles() { 7013 return v8::HandleScope::NumberOfHandles(); 7014 } 7015 7016 7017 static int Recurse(int depth, int iterations) { 7018 v8::HandleScope scope; 7019 if (depth == 0) return CountHandles(); 7020 for (int i = 0; i < iterations; i++) { 7021 Local<v8::Number> n = v8::Integer::New(42); 7022 } 7023 return Recurse(depth - 1, iterations); 7024 } 7025 7026 7027 THREADED_TEST(HandleIteration) { 7028 static const int kIterations = 500; 7029 static const int kNesting = 200; 7030 CHECK_EQ(0, CountHandles()); 7031 { 7032 v8::HandleScope scope1; 7033 CHECK_EQ(0, CountHandles()); 7034 for (int i = 0; i < kIterations; i++) { 7035 Local<v8::Number> n = v8::Integer::New(42); 7036 CHECK_EQ(i + 1, CountHandles()); 7037 } 7038 7039 CHECK_EQ(kIterations, CountHandles()); 7040 { 7041 v8::HandleScope scope2; 7042 for (int j = 0; j < kIterations; j++) { 7043 Local<v8::Number> n = v8::Integer::New(42); 7044 CHECK_EQ(j + 1 + kIterations, CountHandles()); 7045 } 7046 } 7047 CHECK_EQ(kIterations, CountHandles()); 7048 } 7049 CHECK_EQ(0, CountHandles()); 7050 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations)); 7051 } 7052 7053 7054 static v8::Handle<Value> InterceptorHasOwnPropertyGetter( 7055 Local<String> name, 7056 const AccessorInfo& info) { 7057 ApiTestFuzzer::Fuzz(); 7058 return v8::Handle<Value>(); 7059 } 7060 7061 7062 THREADED_TEST(InterceptorHasOwnProperty) { 7063 v8::HandleScope scope; 7064 LocalContext context; 7065 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7066 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 7067 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter); 7068 Local<Function> function = fun_templ->GetFunction(); 7069 context->Global()->Set(v8_str("constructor"), function); 7070 v8::Handle<Value> value = CompileRun( 7071 "var o = new constructor();" 7072 "o.hasOwnProperty('ostehaps');"); 7073 CHECK_EQ(false, value->BooleanValue()); 7074 value = CompileRun( 7075 "o.ostehaps = 42;" 7076 "o.hasOwnProperty('ostehaps');"); 7077 CHECK_EQ(true, value->BooleanValue()); 7078 value = CompileRun( 7079 "var p = new constructor();" 7080 "p.hasOwnProperty('ostehaps');"); 7081 CHECK_EQ(false, value->BooleanValue()); 7082 } 7083 7084 7085 static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC( 7086 Local<String> name, 7087 const AccessorInfo& info) { 7088 ApiTestFuzzer::Fuzz(); 7089 HEAP->CollectAllGarbage(false); 7090 return v8::Handle<Value>(); 7091 } 7092 7093 7094 THREADED_TEST(InterceptorHasOwnPropertyCausingGC) { 7095 v8::HandleScope scope; 7096 LocalContext context; 7097 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7098 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 7099 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC); 7100 Local<Function> function = fun_templ->GetFunction(); 7101 context->Global()->Set(v8_str("constructor"), function); 7102 // Let's first make some stuff so we can be sure to get a good GC. 7103 CompileRun( 7104 "function makestr(size) {" 7105 " switch (size) {" 7106 " case 1: return 'f';" 7107 " case 2: return 'fo';" 7108 " case 3: return 'foo';" 7109 " }" 7110 " return makestr(size >> 1) + makestr((size + 1) >> 1);" 7111 "}" 7112 "var x = makestr(12345);" 7113 "x = makestr(31415);" 7114 "x = makestr(23456);"); 7115 v8::Handle<Value> value = CompileRun( 7116 "var o = new constructor();" 7117 "o.__proto__ = new String(x);" 7118 "o.hasOwnProperty('ostehaps');"); 7119 CHECK_EQ(false, value->BooleanValue()); 7120 } 7121 7122 7123 typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property, 7124 const AccessorInfo& info); 7125 7126 7127 static void CheckInterceptorLoadIC(NamedPropertyGetter getter, 7128 const char* source, 7129 int expected) { 7130 v8::HandleScope scope; 7131 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7132 templ->SetNamedPropertyHandler(getter, 0, 0, 0, 0, v8_str("data")); 7133 LocalContext context; 7134 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7135 v8::Handle<Value> value = CompileRun(source); 7136 CHECK_EQ(expected, value->Int32Value()); 7137 } 7138 7139 7140 static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name, 7141 const AccessorInfo& info) { 7142 ApiTestFuzzer::Fuzz(); 7143 CHECK_EQ(v8_str("data"), info.Data()); 7144 CHECK_EQ(v8_str("x"), name); 7145 return v8::Integer::New(42); 7146 } 7147 7148 7149 // This test should hit the load IC for the interceptor case. 7150 THREADED_TEST(InterceptorLoadIC) { 7151 CheckInterceptorLoadIC(InterceptorLoadICGetter, 7152 "var result = 0;" 7153 "for (var i = 0; i < 1000; i++) {" 7154 " result = o.x;" 7155 "}", 7156 42); 7157 } 7158 7159 7160 // Below go several tests which verify that JITing for various 7161 // configurations of interceptor and explicit fields works fine 7162 // (those cases are special cased to get better performance). 7163 7164 static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name, 7165 const AccessorInfo& info) { 7166 ApiTestFuzzer::Fuzz(); 7167 return v8_str("x")->Equals(name) 7168 ? v8::Integer::New(42) : v8::Handle<v8::Value>(); 7169 } 7170 7171 7172 THREADED_TEST(InterceptorLoadICWithFieldOnHolder) { 7173 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 7174 "var result = 0;" 7175 "o.y = 239;" 7176 "for (var i = 0; i < 1000; i++) {" 7177 " result = o.y;" 7178 "}", 7179 239); 7180 } 7181 7182 7183 THREADED_TEST(InterceptorLoadICWithSubstitutedProto) { 7184 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 7185 "var result = 0;" 7186 "o.__proto__ = { 'y': 239 };" 7187 "for (var i = 0; i < 1000; i++) {" 7188 " result = o.y + o.x;" 7189 "}", 7190 239 + 42); 7191 } 7192 7193 7194 THREADED_TEST(InterceptorLoadICWithPropertyOnProto) { 7195 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 7196 "var result = 0;" 7197 "o.__proto__.y = 239;" 7198 "for (var i = 0; i < 1000; i++) {" 7199 " result = o.y + o.x;" 7200 "}", 7201 239 + 42); 7202 } 7203 7204 7205 THREADED_TEST(InterceptorLoadICUndefined) { 7206 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 7207 "var result = 0;" 7208 "for (var i = 0; i < 1000; i++) {" 7209 " result = (o.y == undefined) ? 239 : 42;" 7210 "}", 7211 239); 7212 } 7213 7214 7215 THREADED_TEST(InterceptorLoadICWithOverride) { 7216 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 7217 "fst = new Object(); fst.__proto__ = o;" 7218 "snd = new Object(); snd.__proto__ = fst;" 7219 "var result1 = 0;" 7220 "for (var i = 0; i < 1000; i++) {" 7221 " result1 = snd.x;" 7222 "}" 7223 "fst.x = 239;" 7224 "var result = 0;" 7225 "for (var i = 0; i < 1000; i++) {" 7226 " result = snd.x;" 7227 "}" 7228 "result + result1", 7229 239 + 42); 7230 } 7231 7232 7233 // Test the case when we stored field into 7234 // a stub, but interceptor produced value on its own. 7235 THREADED_TEST(InterceptorLoadICFieldNotNeeded) { 7236 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 7237 "proto = new Object();" 7238 "o.__proto__ = proto;" 7239 "proto.x = 239;" 7240 "for (var i = 0; i < 1000; i++) {" 7241 " o.x;" 7242 // Now it should be ICed and keep a reference to x defined on proto 7243 "}" 7244 "var result = 0;" 7245 "for (var i = 0; i < 1000; i++) {" 7246 " result += o.x;" 7247 "}" 7248 "result;", 7249 42 * 1000); 7250 } 7251 7252 7253 // Test the case when we stored field into 7254 // a stub, but it got invalidated later on. 7255 THREADED_TEST(InterceptorLoadICInvalidatedField) { 7256 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 7257 "proto1 = new Object();" 7258 "proto2 = new Object();" 7259 "o.__proto__ = proto1;" 7260 "proto1.__proto__ = proto2;" 7261 "proto2.y = 239;" 7262 "for (var i = 0; i < 1000; i++) {" 7263 " o.y;" 7264 // Now it should be ICed and keep a reference to y defined on proto2 7265 "}" 7266 "proto1.y = 42;" 7267 "var result = 0;" 7268 "for (var i = 0; i < 1000; i++) {" 7269 " result += o.y;" 7270 "}" 7271 "result;", 7272 42 * 1000); 7273 } 7274 7275 7276 static int interceptor_load_not_handled_calls = 0; 7277 static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name, 7278 const AccessorInfo& info) { 7279 ++interceptor_load_not_handled_calls; 7280 return v8::Handle<v8::Value>(); 7281 } 7282 7283 7284 // Test how post-interceptor lookups are done in the non-cacheable 7285 // case: the interceptor should not be invoked during this lookup. 7286 THREADED_TEST(InterceptorLoadICPostInterceptor) { 7287 interceptor_load_not_handled_calls = 0; 7288 CheckInterceptorLoadIC(InterceptorLoadNotHandled, 7289 "receiver = new Object();" 7290 "receiver.__proto__ = o;" 7291 "proto = new Object();" 7292 "/* Make proto a slow-case object. */" 7293 "for (var i = 0; i < 1000; i++) {" 7294 " proto[\"xxxxxxxx\" + i] = [];" 7295 "}" 7296 "proto.x = 17;" 7297 "o.__proto__ = proto;" 7298 "var result = 0;" 7299 "for (var i = 0; i < 1000; i++) {" 7300 " result += receiver.x;" 7301 "}" 7302 "result;", 7303 17 * 1000); 7304 CHECK_EQ(1000, interceptor_load_not_handled_calls); 7305 } 7306 7307 7308 // Test the case when we stored field into 7309 // a stub, but it got invalidated later on due to override on 7310 // global object which is between interceptor and fields' holders. 7311 THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) { 7312 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 7313 "o.__proto__ = this;" // set a global to be a proto of o. 7314 "this.__proto__.y = 239;" 7315 "for (var i = 0; i < 10; i++) {" 7316 " if (o.y != 239) throw 'oops: ' + o.y;" 7317 // Now it should be ICed and keep a reference to y defined on field_holder. 7318 "}" 7319 "this.y = 42;" // Assign on a global. 7320 "var result = 0;" 7321 "for (var i = 0; i < 10; i++) {" 7322 " result += o.y;" 7323 "}" 7324 "result;", 7325 42 * 10); 7326 } 7327 7328 7329 static void SetOnThis(Local<String> name, 7330 Local<Value> value, 7331 const AccessorInfo& info) { 7332 info.This()->ForceSet(name, value); 7333 } 7334 7335 7336 THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) { 7337 v8::HandleScope scope; 7338 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7339 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 7340 templ->SetAccessor(v8_str("y"), Return239); 7341 LocalContext context; 7342 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7343 7344 // Check the case when receiver and interceptor's holder 7345 // are the same objects. 7346 v8::Handle<Value> value = CompileRun( 7347 "var result = 0;" 7348 "for (var i = 0; i < 7; i++) {" 7349 " result = o.y;" 7350 "}"); 7351 CHECK_EQ(239, value->Int32Value()); 7352 7353 // Check the case when interceptor's holder is in proto chain 7354 // of receiver. 7355 value = CompileRun( 7356 "r = { __proto__: o };" 7357 "var result = 0;" 7358 "for (var i = 0; i < 7; i++) {" 7359 " result = r.y;" 7360 "}"); 7361 CHECK_EQ(239, value->Int32Value()); 7362 } 7363 7364 7365 THREADED_TEST(InterceptorLoadICWithCallbackOnProto) { 7366 v8::HandleScope scope; 7367 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7368 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 7369 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 7370 templ_p->SetAccessor(v8_str("y"), Return239); 7371 7372 LocalContext context; 7373 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 7374 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 7375 7376 // Check the case when receiver and interceptor's holder 7377 // are the same objects. 7378 v8::Handle<Value> value = CompileRun( 7379 "o.__proto__ = p;" 7380 "var result = 0;" 7381 "for (var i = 0; i < 7; i++) {" 7382 " result = o.x + o.y;" 7383 "}"); 7384 CHECK_EQ(239 + 42, value->Int32Value()); 7385 7386 // Check the case when interceptor's holder is in proto chain 7387 // of receiver. 7388 value = CompileRun( 7389 "r = { __proto__: o };" 7390 "var result = 0;" 7391 "for (var i = 0; i < 7; i++) {" 7392 " result = r.x + r.y;" 7393 "}"); 7394 CHECK_EQ(239 + 42, value->Int32Value()); 7395 } 7396 7397 7398 THREADED_TEST(InterceptorLoadICForCallbackWithOverride) { 7399 v8::HandleScope scope; 7400 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7401 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 7402 templ->SetAccessor(v8_str("y"), Return239); 7403 7404 LocalContext context; 7405 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7406 7407 v8::Handle<Value> value = CompileRun( 7408 "fst = new Object(); fst.__proto__ = o;" 7409 "snd = new Object(); snd.__proto__ = fst;" 7410 "var result1 = 0;" 7411 "for (var i = 0; i < 7; i++) {" 7412 " result1 = snd.x;" 7413 "}" 7414 "fst.x = 239;" 7415 "var result = 0;" 7416 "for (var i = 0; i < 7; i++) {" 7417 " result = snd.x;" 7418 "}" 7419 "result + result1"); 7420 CHECK_EQ(239 + 42, value->Int32Value()); 7421 } 7422 7423 7424 // Test the case when we stored callback into 7425 // a stub, but interceptor produced value on its own. 7426 THREADED_TEST(InterceptorLoadICCallbackNotNeeded) { 7427 v8::HandleScope scope; 7428 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7429 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 7430 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 7431 templ_p->SetAccessor(v8_str("y"), Return239); 7432 7433 LocalContext context; 7434 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 7435 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 7436 7437 v8::Handle<Value> value = CompileRun( 7438 "o.__proto__ = p;" 7439 "for (var i = 0; i < 7; i++) {" 7440 " o.x;" 7441 // Now it should be ICed and keep a reference to x defined on p 7442 "}" 7443 "var result = 0;" 7444 "for (var i = 0; i < 7; i++) {" 7445 " result += o.x;" 7446 "}" 7447 "result"); 7448 CHECK_EQ(42 * 7, value->Int32Value()); 7449 } 7450 7451 7452 // Test the case when we stored callback into 7453 // a stub, but it got invalidated later on. 7454 THREADED_TEST(InterceptorLoadICInvalidatedCallback) { 7455 v8::HandleScope scope; 7456 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7457 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 7458 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 7459 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 7460 7461 LocalContext context; 7462 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 7463 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 7464 7465 v8::Handle<Value> value = CompileRun( 7466 "inbetween = new Object();" 7467 "o.__proto__ = inbetween;" 7468 "inbetween.__proto__ = p;" 7469 "for (var i = 0; i < 10; i++) {" 7470 " o.y;" 7471 // Now it should be ICed and keep a reference to y defined on p 7472 "}" 7473 "inbetween.y = 42;" 7474 "var result = 0;" 7475 "for (var i = 0; i < 10; i++) {" 7476 " result += o.y;" 7477 "}" 7478 "result"); 7479 CHECK_EQ(42 * 10, value->Int32Value()); 7480 } 7481 7482 7483 // Test the case when we stored callback into 7484 // a stub, but it got invalidated later on due to override on 7485 // global object which is between interceptor and callbacks' holders. 7486 THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) { 7487 v8::HandleScope scope; 7488 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7489 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 7490 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 7491 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 7492 7493 LocalContext context; 7494 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 7495 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 7496 7497 v8::Handle<Value> value = CompileRun( 7498 "o.__proto__ = this;" 7499 "this.__proto__ = p;" 7500 "for (var i = 0; i < 10; i++) {" 7501 " if (o.y != 239) throw 'oops: ' + o.y;" 7502 // Now it should be ICed and keep a reference to y defined on p 7503 "}" 7504 "this.y = 42;" 7505 "var result = 0;" 7506 "for (var i = 0; i < 10; i++) {" 7507 " result += o.y;" 7508 "}" 7509 "result"); 7510 CHECK_EQ(42 * 10, value->Int32Value()); 7511 } 7512 7513 7514 static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name, 7515 const AccessorInfo& info) { 7516 ApiTestFuzzer::Fuzz(); 7517 CHECK(v8_str("x")->Equals(name)); 7518 return v8::Integer::New(0); 7519 } 7520 7521 7522 THREADED_TEST(InterceptorReturningZero) { 7523 CheckInterceptorLoadIC(InterceptorLoadICGetter0, 7524 "o.x == undefined ? 1 : 0", 7525 0); 7526 } 7527 7528 7529 static v8::Handle<Value> InterceptorStoreICSetter( 7530 Local<String> key, Local<Value> value, const AccessorInfo&) { 7531 CHECK(v8_str("x")->Equals(key)); 7532 CHECK_EQ(42, value->Int32Value()); 7533 return value; 7534 } 7535 7536 7537 // This test should hit the store IC for the interceptor case. 7538 THREADED_TEST(InterceptorStoreIC) { 7539 v8::HandleScope scope; 7540 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7541 templ->SetNamedPropertyHandler(InterceptorLoadICGetter, 7542 InterceptorStoreICSetter, 7543 0, 0, 0, v8_str("data")); 7544 LocalContext context; 7545 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7546 v8::Handle<Value> value = CompileRun( 7547 "for (var i = 0; i < 1000; i++) {" 7548 " o.x = 42;" 7549 "}"); 7550 } 7551 7552 7553 THREADED_TEST(InterceptorStoreICWithNoSetter) { 7554 v8::HandleScope scope; 7555 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7556 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 7557 LocalContext context; 7558 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7559 v8::Handle<Value> value = CompileRun( 7560 "for (var i = 0; i < 1000; i++) {" 7561 " o.y = 239;" 7562 "}" 7563 "42 + o.y"); 7564 CHECK_EQ(239 + 42, value->Int32Value()); 7565 } 7566 7567 7568 7569 7570 v8::Handle<Value> call_ic_function; 7571 v8::Handle<Value> call_ic_function2; 7572 v8::Handle<Value> call_ic_function3; 7573 7574 static v8::Handle<Value> InterceptorCallICGetter(Local<String> name, 7575 const AccessorInfo& info) { 7576 ApiTestFuzzer::Fuzz(); 7577 CHECK(v8_str("x")->Equals(name)); 7578 return call_ic_function; 7579 } 7580 7581 7582 // This test should hit the call IC for the interceptor case. 7583 THREADED_TEST(InterceptorCallIC) { 7584 v8::HandleScope scope; 7585 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7586 templ->SetNamedPropertyHandler(InterceptorCallICGetter); 7587 LocalContext context; 7588 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7589 call_ic_function = 7590 v8_compile("function f(x) { return x + 1; }; f")->Run(); 7591 v8::Handle<Value> value = CompileRun( 7592 "var result = 0;" 7593 "for (var i = 0; i < 1000; i++) {" 7594 " result = o.x(41);" 7595 "}"); 7596 CHECK_EQ(42, value->Int32Value()); 7597 } 7598 7599 7600 // This test checks that if interceptor doesn't provide 7601 // a value, we can fetch regular value. 7602 THREADED_TEST(InterceptorCallICSeesOthers) { 7603 v8::HandleScope scope; 7604 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7605 templ->SetNamedPropertyHandler(NoBlockGetterX); 7606 LocalContext context; 7607 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7608 v8::Handle<Value> value = CompileRun( 7609 "o.x = function f(x) { return x + 1; };" 7610 "var result = 0;" 7611 "for (var i = 0; i < 7; i++) {" 7612 " result = o.x(41);" 7613 "}"); 7614 CHECK_EQ(42, value->Int32Value()); 7615 } 7616 7617 7618 static v8::Handle<Value> call_ic_function4; 7619 static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name, 7620 const AccessorInfo& info) { 7621 ApiTestFuzzer::Fuzz(); 7622 CHECK(v8_str("x")->Equals(name)); 7623 return call_ic_function4; 7624 } 7625 7626 7627 // This test checks that if interceptor provides a function, 7628 // even if we cached shadowed variant, interceptor's function 7629 // is invoked 7630 THREADED_TEST(InterceptorCallICCacheableNotNeeded) { 7631 v8::HandleScope scope; 7632 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7633 templ->SetNamedPropertyHandler(InterceptorCallICGetter4); 7634 LocalContext context; 7635 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7636 call_ic_function4 = 7637 v8_compile("function f(x) { return x - 1; }; f")->Run(); 7638 v8::Handle<Value> value = CompileRun( 7639 "o.__proto__.x = function(x) { return x + 1; };" 7640 "var result = 0;" 7641 "for (var i = 0; i < 1000; i++) {" 7642 " result = o.x(42);" 7643 "}"); 7644 CHECK_EQ(41, value->Int32Value()); 7645 } 7646 7647 7648 // Test the case when we stored cacheable lookup into 7649 // a stub, but it got invalidated later on 7650 THREADED_TEST(InterceptorCallICInvalidatedCacheable) { 7651 v8::HandleScope scope; 7652 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7653 templ->SetNamedPropertyHandler(NoBlockGetterX); 7654 LocalContext context; 7655 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7656 v8::Handle<Value> value = CompileRun( 7657 "proto1 = new Object();" 7658 "proto2 = new Object();" 7659 "o.__proto__ = proto1;" 7660 "proto1.__proto__ = proto2;" 7661 "proto2.y = function(x) { return x + 1; };" 7662 // Invoke it many times to compile a stub 7663 "for (var i = 0; i < 7; i++) {" 7664 " o.y(42);" 7665 "}" 7666 "proto1.y = function(x) { return x - 1; };" 7667 "var result = 0;" 7668 "for (var i = 0; i < 7; i++) {" 7669 " result += o.y(42);" 7670 "}"); 7671 CHECK_EQ(41 * 7, value->Int32Value()); 7672 } 7673 7674 7675 static v8::Handle<Value> call_ic_function5; 7676 static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name, 7677 const AccessorInfo& info) { 7678 ApiTestFuzzer::Fuzz(); 7679 if (v8_str("x")->Equals(name)) 7680 return call_ic_function5; 7681 else 7682 return Local<Value>(); 7683 } 7684 7685 7686 // This test checks that if interceptor doesn't provide a function, 7687 // cached constant function is used 7688 THREADED_TEST(InterceptorCallICConstantFunctionUsed) { 7689 v8::HandleScope scope; 7690 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7691 templ->SetNamedPropertyHandler(NoBlockGetterX); 7692 LocalContext context; 7693 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7694 v8::Handle<Value> value = CompileRun( 7695 "function inc(x) { return x + 1; };" 7696 "inc(1);" 7697 "o.x = inc;" 7698 "var result = 0;" 7699 "for (var i = 0; i < 1000; i++) {" 7700 " result = o.x(42);" 7701 "}"); 7702 CHECK_EQ(43, value->Int32Value()); 7703 } 7704 7705 7706 // This test checks that if interceptor provides a function, 7707 // even if we cached constant function, interceptor's function 7708 // is invoked 7709 THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) { 7710 v8::HandleScope scope; 7711 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7712 templ->SetNamedPropertyHandler(InterceptorCallICGetter5); 7713 LocalContext context; 7714 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7715 call_ic_function5 = 7716 v8_compile("function f(x) { return x - 1; }; f")->Run(); 7717 v8::Handle<Value> value = CompileRun( 7718 "function inc(x) { return x + 1; };" 7719 "inc(1);" 7720 "o.x = inc;" 7721 "var result = 0;" 7722 "for (var i = 0; i < 1000; i++) {" 7723 " result = o.x(42);" 7724 "}"); 7725 CHECK_EQ(41, value->Int32Value()); 7726 } 7727 7728 7729 // Test the case when we stored constant function into 7730 // a stub, but it got invalidated later on 7731 THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) { 7732 v8::HandleScope scope; 7733 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7734 templ->SetNamedPropertyHandler(NoBlockGetterX); 7735 LocalContext context; 7736 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7737 v8::Handle<Value> value = CompileRun( 7738 "function inc(x) { return x + 1; };" 7739 "inc(1);" 7740 "proto1 = new Object();" 7741 "proto2 = new Object();" 7742 "o.__proto__ = proto1;" 7743 "proto1.__proto__ = proto2;" 7744 "proto2.y = inc;" 7745 // Invoke it many times to compile a stub 7746 "for (var i = 0; i < 7; i++) {" 7747 " o.y(42);" 7748 "}" 7749 "proto1.y = function(x) { return x - 1; };" 7750 "var result = 0;" 7751 "for (var i = 0; i < 7; i++) {" 7752 " result += o.y(42);" 7753 "}"); 7754 CHECK_EQ(41 * 7, value->Int32Value()); 7755 } 7756 7757 7758 // Test the case when we stored constant function into 7759 // a stub, but it got invalidated later on due to override on 7760 // global object which is between interceptor and constant function' holders. 7761 THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) { 7762 v8::HandleScope scope; 7763 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7764 templ->SetNamedPropertyHandler(NoBlockGetterX); 7765 LocalContext context; 7766 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7767 v8::Handle<Value> value = CompileRun( 7768 "function inc(x) { return x + 1; };" 7769 "inc(1);" 7770 "o.__proto__ = this;" 7771 "this.__proto__.y = inc;" 7772 // Invoke it many times to compile a stub 7773 "for (var i = 0; i < 7; i++) {" 7774 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);" 7775 "}" 7776 "this.y = function(x) { return x - 1; };" 7777 "var result = 0;" 7778 "for (var i = 0; i < 7; i++) {" 7779 " result += o.y(42);" 7780 "}"); 7781 CHECK_EQ(41 * 7, value->Int32Value()); 7782 } 7783 7784 7785 // Test the case when actual function to call sits on global object. 7786 THREADED_TEST(InterceptorCallICCachedFromGlobal) { 7787 v8::HandleScope scope; 7788 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7789 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 7790 7791 LocalContext context; 7792 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 7793 7794 v8::Handle<Value> value = CompileRun( 7795 "try {" 7796 " o.__proto__ = this;" 7797 " for (var i = 0; i < 10; i++) {" 7798 " var v = o.parseFloat('239');" 7799 " if (v != 239) throw v;" 7800 // Now it should be ICed and keep a reference to parseFloat. 7801 " }" 7802 " var result = 0;" 7803 " for (var i = 0; i < 10; i++) {" 7804 " result += o.parseFloat('239');" 7805 " }" 7806 " result" 7807 "} catch(e) {" 7808 " e" 7809 "};"); 7810 CHECK_EQ(239 * 10, value->Int32Value()); 7811 } 7812 7813 static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name, 7814 const AccessorInfo& info) { 7815 ApiTestFuzzer::Fuzz(); 7816 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data())); 7817 ++(*call_count); 7818 if ((*call_count) % 20 == 0) { 7819 HEAP->CollectAllGarbage(true); 7820 } 7821 return v8::Handle<Value>(); 7822 } 7823 7824 static v8::Handle<Value> FastApiCallback_TrivialSignature( 7825 const v8::Arguments& args) { 7826 ApiTestFuzzer::Fuzz(); 7827 CHECK_EQ(args.This(), args.Holder()); 7828 CHECK(args.Data()->Equals(v8_str("method_data"))); 7829 return v8::Integer::New(args[0]->Int32Value() + 1); 7830 } 7831 7832 static v8::Handle<Value> FastApiCallback_SimpleSignature( 7833 const v8::Arguments& args) { 7834 ApiTestFuzzer::Fuzz(); 7835 CHECK_EQ(args.This()->GetPrototype(), args.Holder()); 7836 CHECK(args.Data()->Equals(v8_str("method_data"))); 7837 // Note, we're using HasRealNamedProperty instead of Has to avoid 7838 // invoking the interceptor again. 7839 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo"))); 7840 return v8::Integer::New(args[0]->Int32Value() + 1); 7841 } 7842 7843 // Helper to maximize the odds of object moving. 7844 static void GenerateSomeGarbage() { 7845 CompileRun( 7846 "var garbage;" 7847 "for (var i = 0; i < 1000; i++) {" 7848 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];" 7849 "}" 7850 "garbage = undefined;"); 7851 } 7852 7853 7854 v8::Handle<v8::Value> DirectApiCallback(const v8::Arguments& args) { 7855 static int count = 0; 7856 if (count++ % 3 == 0) { 7857 HEAP-> CollectAllGarbage(true); // This should move the stub 7858 GenerateSomeGarbage(); // This should ensure the old stub memory is flushed 7859 } 7860 return v8::Handle<v8::Value>(); 7861 } 7862 7863 7864 THREADED_TEST(CallICFastApi_DirectCall_GCMoveStub) { 7865 v8::HandleScope scope; 7866 LocalContext context; 7867 v8::Handle<v8::ObjectTemplate> nativeobject_templ = v8::ObjectTemplate::New(); 7868 nativeobject_templ->Set("callback", 7869 v8::FunctionTemplate::New(DirectApiCallback)); 7870 v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance(); 7871 context->Global()->Set(v8_str("nativeobject"), nativeobject_obj); 7872 // call the api function multiple times to ensure direct call stub creation. 7873 CompileRun( 7874 "function f() {" 7875 " for (var i = 1; i <= 30; i++) {" 7876 " nativeobject.callback();" 7877 " }" 7878 "}" 7879 "f();"); 7880 } 7881 7882 7883 v8::Handle<v8::Value> ThrowingDirectApiCallback(const v8::Arguments& args) { 7884 return v8::ThrowException(v8_str("g")); 7885 } 7886 7887 7888 THREADED_TEST(CallICFastApi_DirectCall_Throw) { 7889 v8::HandleScope scope; 7890 LocalContext context; 7891 v8::Handle<v8::ObjectTemplate> nativeobject_templ = v8::ObjectTemplate::New(); 7892 nativeobject_templ->Set("callback", 7893 v8::FunctionTemplate::New(ThrowingDirectApiCallback)); 7894 v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance(); 7895 context->Global()->Set(v8_str("nativeobject"), nativeobject_obj); 7896 // call the api function multiple times to ensure direct call stub creation. 7897 v8::Handle<Value> result = CompileRun( 7898 "var result = '';" 7899 "function f() {" 7900 " for (var i = 1; i <= 5; i++) {" 7901 " try { nativeobject.callback(); } catch (e) { result += e; }" 7902 " }" 7903 "}" 7904 "f(); result;"); 7905 CHECK_EQ(v8_str("ggggg"), result); 7906 } 7907 7908 7909 v8::Handle<v8::Value> DirectGetterCallback(Local<String> name, 7910 const v8::AccessorInfo& info) { 7911 if (++p_getter_count % 3 == 0) { 7912 HEAP->CollectAllGarbage(true); 7913 GenerateSomeGarbage(); 7914 } 7915 return v8::Handle<v8::Value>(); 7916 } 7917 7918 7919 THREADED_TEST(LoadICFastApi_DirectCall_GCMoveStub) { 7920 v8::HandleScope scope; 7921 LocalContext context; 7922 v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(); 7923 obj->SetAccessor(v8_str("p1"), DirectGetterCallback); 7924 context->Global()->Set(v8_str("o1"), obj->NewInstance()); 7925 p_getter_count = 0; 7926 CompileRun( 7927 "function f() {" 7928 " for (var i = 0; i < 30; i++) o1.p1;" 7929 "}" 7930 "f();"); 7931 CHECK_EQ(30, p_getter_count); 7932 } 7933 7934 7935 v8::Handle<v8::Value> ThrowingDirectGetterCallback( 7936 Local<String> name, const v8::AccessorInfo& info) { 7937 return v8::ThrowException(v8_str("g")); 7938 } 7939 7940 7941 THREADED_TEST(LoadICFastApi_DirectCall_Throw) { 7942 v8::HandleScope scope; 7943 LocalContext context; 7944 v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(); 7945 obj->SetAccessor(v8_str("p1"), ThrowingDirectGetterCallback); 7946 context->Global()->Set(v8_str("o1"), obj->NewInstance()); 7947 v8::Handle<Value> result = CompileRun( 7948 "var result = '';" 7949 "for (var i = 0; i < 5; i++) {" 7950 " try { o1.p1; } catch (e) { result += e; }" 7951 "}" 7952 "result;"); 7953 CHECK_EQ(v8_str("ggggg"), result); 7954 } 7955 7956 7957 THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) { 7958 int interceptor_call_count = 0; 7959 v8::HandleScope scope; 7960 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7961 v8::Handle<v8::FunctionTemplate> method_templ = 7962 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 7963 v8_str("method_data"), 7964 v8::Handle<v8::Signature>()); 7965 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7966 proto_templ->Set(v8_str("method"), method_templ); 7967 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7968 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 7969 NULL, NULL, NULL, NULL, 7970 v8::External::Wrap(&interceptor_call_count)); 7971 LocalContext context; 7972 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7973 GenerateSomeGarbage(); 7974 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7975 v8::Handle<Value> value = CompileRun( 7976 "var result = 0;" 7977 "for (var i = 0; i < 100; i++) {" 7978 " result = o.method(41);" 7979 "}"); 7980 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 7981 CHECK_EQ(100, interceptor_call_count); 7982 } 7983 7984 THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) { 7985 int interceptor_call_count = 0; 7986 v8::HandleScope scope; 7987 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7988 v8::Handle<v8::FunctionTemplate> method_templ = 7989 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7990 v8_str("method_data"), 7991 v8::Signature::New(fun_templ)); 7992 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7993 proto_templ->Set(v8_str("method"), method_templ); 7994 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7995 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 7996 NULL, NULL, NULL, NULL, 7997 v8::External::Wrap(&interceptor_call_count)); 7998 LocalContext context; 7999 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 8000 GenerateSomeGarbage(); 8001 context->Global()->Set(v8_str("o"), fun->NewInstance()); 8002 v8::Handle<Value> value = CompileRun( 8003 "o.foo = 17;" 8004 "var receiver = {};" 8005 "receiver.__proto__ = o;" 8006 "var result = 0;" 8007 "for (var i = 0; i < 100; i++) {" 8008 " result = receiver.method(41);" 8009 "}"); 8010 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 8011 CHECK_EQ(100, interceptor_call_count); 8012 } 8013 8014 THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) { 8015 int interceptor_call_count = 0; 8016 v8::HandleScope scope; 8017 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 8018 v8::Handle<v8::FunctionTemplate> method_templ = 8019 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 8020 v8_str("method_data"), 8021 v8::Signature::New(fun_templ)); 8022 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 8023 proto_templ->Set(v8_str("method"), method_templ); 8024 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 8025 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 8026 NULL, NULL, NULL, NULL, 8027 v8::External::Wrap(&interceptor_call_count)); 8028 LocalContext context; 8029 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 8030 GenerateSomeGarbage(); 8031 context->Global()->Set(v8_str("o"), fun->NewInstance()); 8032 v8::Handle<Value> value = CompileRun( 8033 "o.foo = 17;" 8034 "var receiver = {};" 8035 "receiver.__proto__ = o;" 8036 "var result = 0;" 8037 "var saved_result = 0;" 8038 "for (var i = 0; i < 100; i++) {" 8039 " result = receiver.method(41);" 8040 " if (i == 50) {" 8041 " saved_result = result;" 8042 " receiver = {method: function(x) { return x - 1 }};" 8043 " }" 8044 "}"); 8045 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 8046 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 8047 CHECK_GE(interceptor_call_count, 50); 8048 } 8049 8050 THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) { 8051 int interceptor_call_count = 0; 8052 v8::HandleScope scope; 8053 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 8054 v8::Handle<v8::FunctionTemplate> method_templ = 8055 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 8056 v8_str("method_data"), 8057 v8::Signature::New(fun_templ)); 8058 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 8059 proto_templ->Set(v8_str("method"), method_templ); 8060 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 8061 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 8062 NULL, NULL, NULL, NULL, 8063 v8::External::Wrap(&interceptor_call_count)); 8064 LocalContext context; 8065 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 8066 GenerateSomeGarbage(); 8067 context->Global()->Set(v8_str("o"), fun->NewInstance()); 8068 v8::Handle<Value> value = CompileRun( 8069 "o.foo = 17;" 8070 "var receiver = {};" 8071 "receiver.__proto__ = o;" 8072 "var result = 0;" 8073 "var saved_result = 0;" 8074 "for (var i = 0; i < 100; i++) {" 8075 " result = receiver.method(41);" 8076 " if (i == 50) {" 8077 " saved_result = result;" 8078 " o.method = function(x) { return x - 1 };" 8079 " }" 8080 "}"); 8081 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 8082 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 8083 CHECK_GE(interceptor_call_count, 50); 8084 } 8085 8086 THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) { 8087 int interceptor_call_count = 0; 8088 v8::HandleScope scope; 8089 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 8090 v8::Handle<v8::FunctionTemplate> method_templ = 8091 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 8092 v8_str("method_data"), 8093 v8::Signature::New(fun_templ)); 8094 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 8095 proto_templ->Set(v8_str("method"), method_templ); 8096 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 8097 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 8098 NULL, NULL, NULL, NULL, 8099 v8::External::Wrap(&interceptor_call_count)); 8100 LocalContext context; 8101 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 8102 GenerateSomeGarbage(); 8103 context->Global()->Set(v8_str("o"), fun->NewInstance()); 8104 v8::TryCatch try_catch; 8105 v8::Handle<Value> value = CompileRun( 8106 "o.foo = 17;" 8107 "var receiver = {};" 8108 "receiver.__proto__ = o;" 8109 "var result = 0;" 8110 "var saved_result = 0;" 8111 "for (var i = 0; i < 100; i++) {" 8112 " result = receiver.method(41);" 8113 " if (i == 50) {" 8114 " saved_result = result;" 8115 " receiver = 333;" 8116 " }" 8117 "}"); 8118 CHECK(try_catch.HasCaught()); 8119 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), 8120 try_catch.Exception()->ToString()); 8121 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 8122 CHECK_GE(interceptor_call_count, 50); 8123 } 8124 8125 THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) { 8126 int interceptor_call_count = 0; 8127 v8::HandleScope scope; 8128 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 8129 v8::Handle<v8::FunctionTemplate> method_templ = 8130 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 8131 v8_str("method_data"), 8132 v8::Signature::New(fun_templ)); 8133 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 8134 proto_templ->Set(v8_str("method"), method_templ); 8135 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 8136 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 8137 NULL, NULL, NULL, NULL, 8138 v8::External::Wrap(&interceptor_call_count)); 8139 LocalContext context; 8140 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 8141 GenerateSomeGarbage(); 8142 context->Global()->Set(v8_str("o"), fun->NewInstance()); 8143 v8::TryCatch try_catch; 8144 v8::Handle<Value> value = CompileRun( 8145 "o.foo = 17;" 8146 "var receiver = {};" 8147 "receiver.__proto__ = o;" 8148 "var result = 0;" 8149 "var saved_result = 0;" 8150 "for (var i = 0; i < 100; i++) {" 8151 " result = receiver.method(41);" 8152 " if (i == 50) {" 8153 " saved_result = result;" 8154 " receiver = {method: receiver.method};" 8155 " }" 8156 "}"); 8157 CHECK(try_catch.HasCaught()); 8158 CHECK_EQ(v8_str("TypeError: Illegal invocation"), 8159 try_catch.Exception()->ToString()); 8160 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 8161 CHECK_GE(interceptor_call_count, 50); 8162 } 8163 8164 THREADED_TEST(CallICFastApi_TrivialSignature) { 8165 v8::HandleScope scope; 8166 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 8167 v8::Handle<v8::FunctionTemplate> method_templ = 8168 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 8169 v8_str("method_data"), 8170 v8::Handle<v8::Signature>()); 8171 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 8172 proto_templ->Set(v8_str("method"), method_templ); 8173 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 8174 LocalContext context; 8175 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 8176 GenerateSomeGarbage(); 8177 context->Global()->Set(v8_str("o"), fun->NewInstance()); 8178 v8::Handle<Value> value = CompileRun( 8179 "var result = 0;" 8180 "for (var i = 0; i < 100; i++) {" 8181 " result = o.method(41);" 8182 "}"); 8183 8184 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 8185 } 8186 8187 THREADED_TEST(CallICFastApi_SimpleSignature) { 8188 v8::HandleScope scope; 8189 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 8190 v8::Handle<v8::FunctionTemplate> method_templ = 8191 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 8192 v8_str("method_data"), 8193 v8::Signature::New(fun_templ)); 8194 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 8195 proto_templ->Set(v8_str("method"), method_templ); 8196 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 8197 LocalContext context; 8198 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 8199 GenerateSomeGarbage(); 8200 context->Global()->Set(v8_str("o"), fun->NewInstance()); 8201 v8::Handle<Value> value = CompileRun( 8202 "o.foo = 17;" 8203 "var receiver = {};" 8204 "receiver.__proto__ = o;" 8205 "var result = 0;" 8206 "for (var i = 0; i < 100; i++) {" 8207 " result = receiver.method(41);" 8208 "}"); 8209 8210 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 8211 } 8212 8213 THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) { 8214 v8::HandleScope scope; 8215 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 8216 v8::Handle<v8::FunctionTemplate> method_templ = 8217 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 8218 v8_str("method_data"), 8219 v8::Signature::New(fun_templ)); 8220 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 8221 proto_templ->Set(v8_str("method"), method_templ); 8222 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 8223 LocalContext context; 8224 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 8225 GenerateSomeGarbage(); 8226 context->Global()->Set(v8_str("o"), fun->NewInstance()); 8227 v8::Handle<Value> value = CompileRun( 8228 "o.foo = 17;" 8229 "var receiver = {};" 8230 "receiver.__proto__ = o;" 8231 "var result = 0;" 8232 "var saved_result = 0;" 8233 "for (var i = 0; i < 100; i++) {" 8234 " result = receiver.method(41);" 8235 " if (i == 50) {" 8236 " saved_result = result;" 8237 " receiver = {method: function(x) { return x - 1 }};" 8238 " }" 8239 "}"); 8240 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 8241 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 8242 } 8243 8244 THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) { 8245 v8::HandleScope scope; 8246 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 8247 v8::Handle<v8::FunctionTemplate> method_templ = 8248 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 8249 v8_str("method_data"), 8250 v8::Signature::New(fun_templ)); 8251 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 8252 proto_templ->Set(v8_str("method"), method_templ); 8253 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 8254 LocalContext context; 8255 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 8256 GenerateSomeGarbage(); 8257 context->Global()->Set(v8_str("o"), fun->NewInstance()); 8258 v8::TryCatch try_catch; 8259 v8::Handle<Value> value = CompileRun( 8260 "o.foo = 17;" 8261 "var receiver = {};" 8262 "receiver.__proto__ = o;" 8263 "var result = 0;" 8264 "var saved_result = 0;" 8265 "for (var i = 0; i < 100; i++) {" 8266 " result = receiver.method(41);" 8267 " if (i == 50) {" 8268 " saved_result = result;" 8269 " receiver = 333;" 8270 " }" 8271 "}"); 8272 CHECK(try_catch.HasCaught()); 8273 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), 8274 try_catch.Exception()->ToString()); 8275 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 8276 } 8277 8278 8279 v8::Handle<Value> keyed_call_ic_function; 8280 8281 static v8::Handle<Value> InterceptorKeyedCallICGetter( 8282 Local<String> name, const AccessorInfo& info) { 8283 ApiTestFuzzer::Fuzz(); 8284 if (v8_str("x")->Equals(name)) { 8285 return keyed_call_ic_function; 8286 } 8287 return v8::Handle<Value>(); 8288 } 8289 8290 8291 // Test the case when we stored cacheable lookup into 8292 // a stub, but the function name changed (to another cacheable function). 8293 THREADED_TEST(InterceptorKeyedCallICKeyChange1) { 8294 v8::HandleScope scope; 8295 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8296 templ->SetNamedPropertyHandler(NoBlockGetterX); 8297 LocalContext context; 8298 context->Global()->Set(v8_str("o"), templ->NewInstance()); 8299 v8::Handle<Value> value = CompileRun( 8300 "proto = new Object();" 8301 "proto.y = function(x) { return x + 1; };" 8302 "proto.z = function(x) { return x - 1; };" 8303 "o.__proto__ = proto;" 8304 "var result = 0;" 8305 "var method = 'y';" 8306 "for (var i = 0; i < 10; i++) {" 8307 " if (i == 5) { method = 'z'; };" 8308 " result += o[method](41);" 8309 "}"); 8310 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 8311 } 8312 8313 8314 // Test the case when we stored cacheable lookup into 8315 // a stub, but the function name changed (and the new function is present 8316 // both before and after the interceptor in the prototype chain). 8317 THREADED_TEST(InterceptorKeyedCallICKeyChange2) { 8318 v8::HandleScope scope; 8319 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8320 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter); 8321 LocalContext context; 8322 context->Global()->Set(v8_str("proto1"), templ->NewInstance()); 8323 keyed_call_ic_function = 8324 v8_compile("function f(x) { return x - 1; }; f")->Run(); 8325 v8::Handle<Value> value = CompileRun( 8326 "o = new Object();" 8327 "proto2 = new Object();" 8328 "o.y = function(x) { return x + 1; };" 8329 "proto2.y = function(x) { return x + 2; };" 8330 "o.__proto__ = proto1;" 8331 "proto1.__proto__ = proto2;" 8332 "var result = 0;" 8333 "var method = 'x';" 8334 "for (var i = 0; i < 10; i++) {" 8335 " if (i == 5) { method = 'y'; };" 8336 " result += o[method](41);" 8337 "}"); 8338 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 8339 } 8340 8341 8342 // Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit 8343 // on the global object. 8344 THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) { 8345 v8::HandleScope scope; 8346 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8347 templ->SetNamedPropertyHandler(NoBlockGetterX); 8348 LocalContext context; 8349 context->Global()->Set(v8_str("o"), templ->NewInstance()); 8350 v8::Handle<Value> value = CompileRun( 8351 "function inc(x) { return x + 1; };" 8352 "inc(1);" 8353 "function dec(x) { return x - 1; };" 8354 "dec(1);" 8355 "o.__proto__ = this;" 8356 "this.__proto__.x = inc;" 8357 "this.__proto__.y = dec;" 8358 "var result = 0;" 8359 "var method = 'x';" 8360 "for (var i = 0; i < 10; i++) {" 8361 " if (i == 5) { method = 'y'; };" 8362 " result += o[method](41);" 8363 "}"); 8364 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 8365 } 8366 8367 8368 // Test the case when actual function to call sits on global object. 8369 THREADED_TEST(InterceptorKeyedCallICFromGlobal) { 8370 v8::HandleScope scope; 8371 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 8372 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 8373 LocalContext context; 8374 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 8375 8376 v8::Handle<Value> value = CompileRun( 8377 "function len(x) { return x.length; };" 8378 "o.__proto__ = this;" 8379 "var m = 'parseFloat';" 8380 "var result = 0;" 8381 "for (var i = 0; i < 10; i++) {" 8382 " if (i == 5) {" 8383 " m = 'len';" 8384 " saved_result = result;" 8385 " };" 8386 " result = o[m]('239');" 8387 "}"); 8388 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value()); 8389 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 8390 } 8391 8392 // Test the map transition before the interceptor. 8393 THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) { 8394 v8::HandleScope scope; 8395 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 8396 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 8397 LocalContext context; 8398 context->Global()->Set(v8_str("proto"), templ_o->NewInstance()); 8399 8400 v8::Handle<Value> value = CompileRun( 8401 "var o = new Object();" 8402 "o.__proto__ = proto;" 8403 "o.method = function(x) { return x + 1; };" 8404 "var m = 'method';" 8405 "var result = 0;" 8406 "for (var i = 0; i < 10; i++) {" 8407 " if (i == 5) { o.method = function(x) { return x - 1; }; };" 8408 " result += o[m](41);" 8409 "}"); 8410 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 8411 } 8412 8413 8414 // Test the map transition after the interceptor. 8415 THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) { 8416 v8::HandleScope scope; 8417 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 8418 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 8419 LocalContext context; 8420 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 8421 8422 v8::Handle<Value> value = CompileRun( 8423 "var proto = new Object();" 8424 "o.__proto__ = proto;" 8425 "proto.method = function(x) { return x + 1; };" 8426 "var m = 'method';" 8427 "var result = 0;" 8428 "for (var i = 0; i < 10; i++) {" 8429 " if (i == 5) { proto.method = function(x) { return x - 1; }; };" 8430 " result += o[m](41);" 8431 "}"); 8432 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 8433 } 8434 8435 8436 static int interceptor_call_count = 0; 8437 8438 static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name, 8439 const AccessorInfo& info) { 8440 ApiTestFuzzer::Fuzz(); 8441 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) { 8442 return call_ic_function2; 8443 } 8444 return v8::Handle<Value>(); 8445 } 8446 8447 8448 // This test should hit load and call ICs for the interceptor case. 8449 // Once in a while, the interceptor will reply that a property was not 8450 // found in which case we should get a reference error. 8451 THREADED_TEST(InterceptorICReferenceErrors) { 8452 v8::HandleScope scope; 8453 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8454 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter); 8455 LocalContext context(0, templ, v8::Handle<Value>()); 8456 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run(); 8457 v8::Handle<Value> value = CompileRun( 8458 "function f() {" 8459 " for (var i = 0; i < 1000; i++) {" 8460 " try { x; } catch(e) { return true; }" 8461 " }" 8462 " return false;" 8463 "};" 8464 "f();"); 8465 CHECK_EQ(true, value->BooleanValue()); 8466 interceptor_call_count = 0; 8467 value = CompileRun( 8468 "function g() {" 8469 " for (var i = 0; i < 1000; i++) {" 8470 " try { x(42); } catch(e) { return true; }" 8471 " }" 8472 " return false;" 8473 "};" 8474 "g();"); 8475 CHECK_EQ(true, value->BooleanValue()); 8476 } 8477 8478 8479 static int interceptor_ic_exception_get_count = 0; 8480 8481 static v8::Handle<Value> InterceptorICExceptionGetter( 8482 Local<String> name, 8483 const AccessorInfo& info) { 8484 ApiTestFuzzer::Fuzz(); 8485 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) { 8486 return call_ic_function3; 8487 } 8488 if (interceptor_ic_exception_get_count == 20) { 8489 return v8::ThrowException(v8_num(42)); 8490 } 8491 // Do not handle get for properties other than x. 8492 return v8::Handle<Value>(); 8493 } 8494 8495 // Test interceptor load/call IC where the interceptor throws an 8496 // exception once in a while. 8497 THREADED_TEST(InterceptorICGetterExceptions) { 8498 interceptor_ic_exception_get_count = 0; 8499 v8::HandleScope scope; 8500 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8501 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter); 8502 LocalContext context(0, templ, v8::Handle<Value>()); 8503 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run(); 8504 v8::Handle<Value> value = CompileRun( 8505 "function f() {" 8506 " for (var i = 0; i < 100; i++) {" 8507 " try { x; } catch(e) { return true; }" 8508 " }" 8509 " return false;" 8510 "};" 8511 "f();"); 8512 CHECK_EQ(true, value->BooleanValue()); 8513 interceptor_ic_exception_get_count = 0; 8514 value = CompileRun( 8515 "function f() {" 8516 " for (var i = 0; i < 100; i++) {" 8517 " try { x(42); } catch(e) { return true; }" 8518 " }" 8519 " return false;" 8520 "};" 8521 "f();"); 8522 CHECK_EQ(true, value->BooleanValue()); 8523 } 8524 8525 8526 static int interceptor_ic_exception_set_count = 0; 8527 8528 static v8::Handle<Value> InterceptorICExceptionSetter( 8529 Local<String> key, Local<Value> value, const AccessorInfo&) { 8530 ApiTestFuzzer::Fuzz(); 8531 if (++interceptor_ic_exception_set_count > 20) { 8532 return v8::ThrowException(v8_num(42)); 8533 } 8534 // Do not actually handle setting. 8535 return v8::Handle<Value>(); 8536 } 8537 8538 // Test interceptor store IC where the interceptor throws an exception 8539 // once in a while. 8540 THREADED_TEST(InterceptorICSetterExceptions) { 8541 interceptor_ic_exception_set_count = 0; 8542 v8::HandleScope scope; 8543 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8544 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter); 8545 LocalContext context(0, templ, v8::Handle<Value>()); 8546 v8::Handle<Value> value = CompileRun( 8547 "function f() {" 8548 " for (var i = 0; i < 100; i++) {" 8549 " try { x = 42; } catch(e) { return true; }" 8550 " }" 8551 " return false;" 8552 "};" 8553 "f();"); 8554 CHECK_EQ(true, value->BooleanValue()); 8555 } 8556 8557 8558 // Test that we ignore null interceptors. 8559 THREADED_TEST(NullNamedInterceptor) { 8560 v8::HandleScope scope; 8561 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8562 templ->SetNamedPropertyHandler(0); 8563 LocalContext context; 8564 templ->Set("x", v8_num(42)); 8565 v8::Handle<v8::Object> obj = templ->NewInstance(); 8566 context->Global()->Set(v8_str("obj"), obj); 8567 v8::Handle<Value> value = CompileRun("obj.x"); 8568 CHECK(value->IsInt32()); 8569 CHECK_EQ(42, value->Int32Value()); 8570 } 8571 8572 8573 // Test that we ignore null interceptors. 8574 THREADED_TEST(NullIndexedInterceptor) { 8575 v8::HandleScope scope; 8576 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8577 templ->SetIndexedPropertyHandler(0); 8578 LocalContext context; 8579 templ->Set("42", v8_num(42)); 8580 v8::Handle<v8::Object> obj = templ->NewInstance(); 8581 context->Global()->Set(v8_str("obj"), obj); 8582 v8::Handle<Value> value = CompileRun("obj[42]"); 8583 CHECK(value->IsInt32()); 8584 CHECK_EQ(42, value->Int32Value()); 8585 } 8586 8587 8588 THREADED_TEST(NamedPropertyHandlerGetterAttributes) { 8589 v8::HandleScope scope; 8590 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 8591 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter); 8592 LocalContext env; 8593 env->Global()->Set(v8_str("obj"), 8594 templ->GetFunction()->NewInstance()); 8595 ExpectTrue("obj.x === 42"); 8596 ExpectTrue("!obj.propertyIsEnumerable('x')"); 8597 } 8598 8599 8600 static Handle<Value> ThrowingGetter(Local<String> name, 8601 const AccessorInfo& info) { 8602 ApiTestFuzzer::Fuzz(); 8603 ThrowException(Handle<Value>()); 8604 return Undefined(); 8605 } 8606 8607 8608 THREADED_TEST(VariousGetPropertiesAndThrowingCallbacks) { 8609 HandleScope scope; 8610 LocalContext context; 8611 8612 Local<FunctionTemplate> templ = FunctionTemplate::New(); 8613 Local<ObjectTemplate> instance_templ = templ->InstanceTemplate(); 8614 instance_templ->SetAccessor(v8_str("f"), ThrowingGetter); 8615 8616 Local<Object> instance = templ->GetFunction()->NewInstance(); 8617 8618 Local<Object> another = Object::New(); 8619 another->SetPrototype(instance); 8620 8621 Local<Object> with_js_getter = CompileRun( 8622 "o = {};\n" 8623 "o.__defineGetter__('f', function() { throw undefined; });\n" 8624 "o\n").As<Object>(); 8625 CHECK(!with_js_getter.IsEmpty()); 8626 8627 TryCatch try_catch; 8628 8629 Local<Value> result = instance->GetRealNamedProperty(v8_str("f")); 8630 CHECK(try_catch.HasCaught()); 8631 try_catch.Reset(); 8632 CHECK(result.IsEmpty()); 8633 8634 result = another->GetRealNamedProperty(v8_str("f")); 8635 CHECK(try_catch.HasCaught()); 8636 try_catch.Reset(); 8637 CHECK(result.IsEmpty()); 8638 8639 result = another->GetRealNamedPropertyInPrototypeChain(v8_str("f")); 8640 CHECK(try_catch.HasCaught()); 8641 try_catch.Reset(); 8642 CHECK(result.IsEmpty()); 8643 8644 result = another->Get(v8_str("f")); 8645 CHECK(try_catch.HasCaught()); 8646 try_catch.Reset(); 8647 CHECK(result.IsEmpty()); 8648 8649 result = with_js_getter->GetRealNamedProperty(v8_str("f")); 8650 CHECK(try_catch.HasCaught()); 8651 try_catch.Reset(); 8652 CHECK(result.IsEmpty()); 8653 8654 result = with_js_getter->Get(v8_str("f")); 8655 CHECK(try_catch.HasCaught()); 8656 try_catch.Reset(); 8657 CHECK(result.IsEmpty()); 8658 } 8659 8660 8661 static Handle<Value> ThrowingCallbackWithTryCatch(const Arguments& args) { 8662 TryCatch try_catch; 8663 // Verboseness is important: it triggers message delivery which can call into 8664 // external code. 8665 try_catch.SetVerbose(true); 8666 CompileRun("throw 'from JS';"); 8667 CHECK(try_catch.HasCaught()); 8668 CHECK(!i::Isolate::Current()->has_pending_exception()); 8669 CHECK(!i::Isolate::Current()->has_scheduled_exception()); 8670 return Undefined(); 8671 } 8672 8673 8674 static int call_depth; 8675 8676 8677 static void WithTryCatch(Handle<Message> message, Handle<Value> data) { 8678 TryCatch try_catch; 8679 } 8680 8681 8682 static void ThrowFromJS(Handle<Message> message, Handle<Value> data) { 8683 if (--call_depth) CompileRun("throw 'ThrowInJS';"); 8684 } 8685 8686 8687 static void ThrowViaApi(Handle<Message> message, Handle<Value> data) { 8688 if (--call_depth) ThrowException(v8_str("ThrowViaApi")); 8689 } 8690 8691 8692 static void WebKitLike(Handle<Message> message, Handle<Value> data) { 8693 Handle<String> errorMessageString = message->Get(); 8694 CHECK(!errorMessageString.IsEmpty()); 8695 message->GetStackTrace(); 8696 message->GetScriptResourceName(); 8697 } 8698 8699 THREADED_TEST(ExceptionsDoNotPropagatePastTryCatch) { 8700 HandleScope scope; 8701 LocalContext context; 8702 8703 Local<Function> func = 8704 FunctionTemplate::New(ThrowingCallbackWithTryCatch)->GetFunction(); 8705 context->Global()->Set(v8_str("func"), func); 8706 8707 MessageCallback callbacks[] = 8708 { NULL, WebKitLike, ThrowViaApi, ThrowFromJS, WithTryCatch }; 8709 for (unsigned i = 0; i < sizeof(callbacks)/sizeof(callbacks[0]); i++) { 8710 MessageCallback callback = callbacks[i]; 8711 if (callback != NULL) { 8712 V8::AddMessageListener(callback); 8713 } 8714 call_depth = 5; 8715 ExpectFalse( 8716 "var thrown = false;\n" 8717 "try { func(); } catch(e) { thrown = true; }\n" 8718 "thrown\n"); 8719 if (callback != NULL) { 8720 V8::RemoveMessageListeners(callback); 8721 } 8722 } 8723 } 8724 8725 8726 static v8::Handle<Value> ParentGetter(Local<String> name, 8727 const AccessorInfo& info) { 8728 ApiTestFuzzer::Fuzz(); 8729 return v8_num(1); 8730 } 8731 8732 8733 static v8::Handle<Value> ChildGetter(Local<String> name, 8734 const AccessorInfo& info) { 8735 ApiTestFuzzer::Fuzz(); 8736 return v8_num(42); 8737 } 8738 8739 8740 THREADED_TEST(Overriding) { 8741 v8::HandleScope scope; 8742 LocalContext context; 8743 8744 // Parent template. 8745 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New(); 8746 Local<ObjectTemplate> parent_instance_templ = 8747 parent_templ->InstanceTemplate(); 8748 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter); 8749 8750 // Template that inherits from the parent template. 8751 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New(); 8752 Local<ObjectTemplate> child_instance_templ = 8753 child_templ->InstanceTemplate(); 8754 child_templ->Inherit(parent_templ); 8755 // Override 'f'. The child version of 'f' should get called for child 8756 // instances. 8757 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter); 8758 // Add 'g' twice. The 'g' added last should get called for instances. 8759 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter); 8760 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter); 8761 8762 // Add 'h' as an accessor to the proto template with ReadOnly attributes 8763 // so 'h' can be shadowed on the instance object. 8764 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate(); 8765 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0, 8766 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 8767 8768 // Add 'i' as an accessor to the instance template with ReadOnly attributes 8769 // but the attribute does not have effect because it is duplicated with 8770 // NULL setter. 8771 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0, 8772 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 8773 8774 8775 8776 // Instantiate the child template. 8777 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance(); 8778 8779 // Check that the child function overrides the parent one. 8780 context->Global()->Set(v8_str("o"), instance); 8781 Local<Value> value = v8_compile("o.f")->Run(); 8782 // Check that the 'g' that was added last is hit. 8783 CHECK_EQ(42, value->Int32Value()); 8784 value = v8_compile("o.g")->Run(); 8785 CHECK_EQ(42, value->Int32Value()); 8786 8787 // Check 'h' can be shadowed. 8788 value = v8_compile("o.h = 3; o.h")->Run(); 8789 CHECK_EQ(3, value->Int32Value()); 8790 8791 // Check 'i' is cannot be shadowed or changed. 8792 value = v8_compile("o.i = 3; o.i")->Run(); 8793 CHECK_EQ(42, value->Int32Value()); 8794 } 8795 8796 8797 static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) { 8798 ApiTestFuzzer::Fuzz(); 8799 if (args.IsConstructCall()) { 8800 return v8::Boolean::New(true); 8801 } 8802 return v8::Boolean::New(false); 8803 } 8804 8805 8806 THREADED_TEST(IsConstructCall) { 8807 v8::HandleScope scope; 8808 8809 // Function template with call handler. 8810 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 8811 templ->SetCallHandler(IsConstructHandler); 8812 8813 LocalContext context; 8814 8815 context->Global()->Set(v8_str("f"), templ->GetFunction()); 8816 Local<Value> value = v8_compile("f()")->Run(); 8817 CHECK(!value->BooleanValue()); 8818 value = v8_compile("new f()")->Run(); 8819 CHECK(value->BooleanValue()); 8820 } 8821 8822 8823 THREADED_TEST(ObjectProtoToString) { 8824 v8::HandleScope scope; 8825 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 8826 templ->SetClassName(v8_str("MyClass")); 8827 8828 LocalContext context; 8829 8830 Local<String> customized_tostring = v8_str("customized toString"); 8831 8832 // Replace Object.prototype.toString 8833 v8_compile("Object.prototype.toString = function() {" 8834 " return 'customized toString';" 8835 "}")->Run(); 8836 8837 // Normal ToString call should call replaced Object.prototype.toString 8838 Local<v8::Object> instance = templ->GetFunction()->NewInstance(); 8839 Local<String> value = instance->ToString(); 8840 CHECK(value->IsString() && value->Equals(customized_tostring)); 8841 8842 // ObjectProtoToString should not call replace toString function. 8843 value = instance->ObjectProtoToString(); 8844 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]"))); 8845 8846 // Check global 8847 value = context->Global()->ObjectProtoToString(); 8848 CHECK(value->IsString() && value->Equals(v8_str("[object global]"))); 8849 8850 // Check ordinary object 8851 Local<Value> object = v8_compile("new Object()")->Run(); 8852 value = object.As<v8::Object>()->ObjectProtoToString(); 8853 CHECK(value->IsString() && value->Equals(v8_str("[object Object]"))); 8854 } 8855 8856 8857 THREADED_TEST(ObjectGetConstructorName) { 8858 v8::HandleScope scope; 8859 LocalContext context; 8860 v8_compile("function Parent() {};" 8861 "function Child() {};" 8862 "Child.prototype = new Parent();" 8863 "var outer = { inner: function() { } };" 8864 "var p = new Parent();" 8865 "var c = new Child();" 8866 "var x = new outer.inner();")->Run(); 8867 8868 Local<v8::Value> p = context->Global()->Get(v8_str("p")); 8869 CHECK(p->IsObject() && p->ToObject()->GetConstructorName()->Equals( 8870 v8_str("Parent"))); 8871 8872 Local<v8::Value> c = context->Global()->Get(v8_str("c")); 8873 CHECK(c->IsObject() && c->ToObject()->GetConstructorName()->Equals( 8874 v8_str("Child"))); 8875 8876 Local<v8::Value> x = context->Global()->Get(v8_str("x")); 8877 CHECK(x->IsObject() && x->ToObject()->GetConstructorName()->Equals( 8878 v8_str("outer.inner"))); 8879 } 8880 8881 8882 bool ApiTestFuzzer::fuzzing_ = false; 8883 i::Semaphore* ApiTestFuzzer::all_tests_done_= 8884 i::OS::CreateSemaphore(0); 8885 int ApiTestFuzzer::active_tests_; 8886 int ApiTestFuzzer::tests_being_run_; 8887 int ApiTestFuzzer::current_; 8888 8889 8890 // We are in a callback and want to switch to another thread (if we 8891 // are currently running the thread fuzzing test). 8892 void ApiTestFuzzer::Fuzz() { 8893 if (!fuzzing_) return; 8894 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_; 8895 test->ContextSwitch(); 8896 } 8897 8898 8899 // Let the next thread go. Since it is also waiting on the V8 lock it may 8900 // not start immediately. 8901 bool ApiTestFuzzer::NextThread() { 8902 int test_position = GetNextTestNumber(); 8903 const char* test_name = RegisterThreadedTest::nth(current_)->name(); 8904 if (test_position == current_) { 8905 if (kLogThreading) 8906 printf("Stay with %s\n", test_name); 8907 return false; 8908 } 8909 if (kLogThreading) { 8910 printf("Switch from %s to %s\n", 8911 test_name, 8912 RegisterThreadedTest::nth(test_position)->name()); 8913 } 8914 current_ = test_position; 8915 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal(); 8916 return true; 8917 } 8918 8919 8920 void ApiTestFuzzer::Run() { 8921 // When it is our turn... 8922 gate_->Wait(); 8923 { 8924 // ... get the V8 lock and start running the test. 8925 v8::Locker locker; 8926 CallTest(); 8927 } 8928 // This test finished. 8929 active_ = false; 8930 active_tests_--; 8931 // If it was the last then signal that fact. 8932 if (active_tests_ == 0) { 8933 all_tests_done_->Signal(); 8934 } else { 8935 // Otherwise select a new test and start that. 8936 NextThread(); 8937 } 8938 } 8939 8940 8941 static unsigned linear_congruential_generator; 8942 8943 8944 void ApiTestFuzzer::Setup(PartOfTest part) { 8945 linear_congruential_generator = i::FLAG_testing_prng_seed; 8946 fuzzing_ = true; 8947 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1); 8948 int end = (part == FIRST_PART) 8949 ? (RegisterThreadedTest::count() >> 1) 8950 : RegisterThreadedTest::count(); 8951 active_tests_ = tests_being_run_ = end - start; 8952 for (int i = 0; i < tests_being_run_; i++) { 8953 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer( 8954 i::Isolate::Current(), i + start); 8955 } 8956 for (int i = 0; i < active_tests_; i++) { 8957 RegisterThreadedTest::nth(i)->fuzzer_->Start(); 8958 } 8959 } 8960 8961 8962 static void CallTestNumber(int test_number) { 8963 (RegisterThreadedTest::nth(test_number)->callback())(); 8964 } 8965 8966 8967 void ApiTestFuzzer::RunAllTests() { 8968 // Set off the first test. 8969 current_ = -1; 8970 NextThread(); 8971 // Wait till they are all done. 8972 all_tests_done_->Wait(); 8973 } 8974 8975 8976 int ApiTestFuzzer::GetNextTestNumber() { 8977 int next_test; 8978 do { 8979 next_test = (linear_congruential_generator >> 16) % tests_being_run_; 8980 linear_congruential_generator *= 1664525u; 8981 linear_congruential_generator += 1013904223u; 8982 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_); 8983 return next_test; 8984 } 8985 8986 8987 void ApiTestFuzzer::ContextSwitch() { 8988 // If the new thread is the same as the current thread there is nothing to do. 8989 if (NextThread()) { 8990 // Now it can start. 8991 v8::Unlocker unlocker; 8992 // Wait till someone starts us again. 8993 gate_->Wait(); 8994 // And we're off. 8995 } 8996 } 8997 8998 8999 void ApiTestFuzzer::TearDown() { 9000 fuzzing_ = false; 9001 for (int i = 0; i < RegisterThreadedTest::count(); i++) { 9002 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_; 9003 if (fuzzer != NULL) fuzzer->Join(); 9004 } 9005 } 9006 9007 9008 // Lets not be needlessly self-referential. 9009 TEST(Threading) { 9010 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART); 9011 ApiTestFuzzer::RunAllTests(); 9012 ApiTestFuzzer::TearDown(); 9013 } 9014 9015 TEST(Threading2) { 9016 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART); 9017 ApiTestFuzzer::RunAllTests(); 9018 ApiTestFuzzer::TearDown(); 9019 } 9020 9021 9022 void ApiTestFuzzer::CallTest() { 9023 if (kLogThreading) 9024 printf("Start test %d\n", test_number_); 9025 CallTestNumber(test_number_); 9026 if (kLogThreading) 9027 printf("End test %d\n", test_number_); 9028 } 9029 9030 9031 static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) { 9032 CHECK(v8::Locker::IsLocked()); 9033 ApiTestFuzzer::Fuzz(); 9034 v8::Unlocker unlocker; 9035 const char* code = "throw 7;"; 9036 { 9037 v8::Locker nested_locker; 9038 v8::HandleScope scope; 9039 v8::Handle<Value> exception; 9040 { v8::TryCatch try_catch; 9041 v8::Handle<Value> value = CompileRun(code); 9042 CHECK(value.IsEmpty()); 9043 CHECK(try_catch.HasCaught()); 9044 // Make sure to wrap the exception in a new handle because 9045 // the handle returned from the TryCatch is destroyed 9046 // when the TryCatch is destroyed. 9047 exception = Local<Value>::New(try_catch.Exception()); 9048 } 9049 return v8::ThrowException(exception); 9050 } 9051 } 9052 9053 9054 static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) { 9055 CHECK(v8::Locker::IsLocked()); 9056 ApiTestFuzzer::Fuzz(); 9057 v8::Unlocker unlocker; 9058 const char* code = "throw 7;"; 9059 { 9060 v8::Locker nested_locker; 9061 v8::HandleScope scope; 9062 v8::Handle<Value> value = CompileRun(code); 9063 CHECK(value.IsEmpty()); 9064 return v8_str("foo"); 9065 } 9066 } 9067 9068 9069 // These are locking tests that don't need to be run again 9070 // as part of the locking aggregation tests. 9071 TEST(NestedLockers) { 9072 v8::Locker locker; 9073 CHECK(v8::Locker::IsLocked()); 9074 v8::HandleScope scope; 9075 LocalContext env; 9076 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS); 9077 Local<Function> fun = fun_templ->GetFunction(); 9078 env->Global()->Set(v8_str("throw_in_js"), fun); 9079 Local<Script> script = v8_compile("(function () {" 9080 " try {" 9081 " throw_in_js();" 9082 " return 42;" 9083 " } catch (e) {" 9084 " return e * 13;" 9085 " }" 9086 "})();"); 9087 CHECK_EQ(91, script->Run()->Int32Value()); 9088 } 9089 9090 9091 // These are locking tests that don't need to be run again 9092 // as part of the locking aggregation tests. 9093 TEST(NestedLockersNoTryCatch) { 9094 v8::Locker locker; 9095 v8::HandleScope scope; 9096 LocalContext env; 9097 Local<v8::FunctionTemplate> fun_templ = 9098 v8::FunctionTemplate::New(ThrowInJSNoCatch); 9099 Local<Function> fun = fun_templ->GetFunction(); 9100 env->Global()->Set(v8_str("throw_in_js"), fun); 9101 Local<Script> script = v8_compile("(function () {" 9102 " try {" 9103 " throw_in_js();" 9104 " return 42;" 9105 " } catch (e) {" 9106 " return e * 13;" 9107 " }" 9108 "})();"); 9109 CHECK_EQ(91, script->Run()->Int32Value()); 9110 } 9111 9112 9113 THREADED_TEST(RecursiveLocking) { 9114 v8::Locker locker; 9115 { 9116 v8::Locker locker2; 9117 CHECK(v8::Locker::IsLocked()); 9118 } 9119 } 9120 9121 9122 static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) { 9123 ApiTestFuzzer::Fuzz(); 9124 v8::Unlocker unlocker; 9125 return v8::Undefined(); 9126 } 9127 9128 9129 THREADED_TEST(LockUnlockLock) { 9130 { 9131 v8::Locker locker; 9132 v8::HandleScope scope; 9133 LocalContext env; 9134 Local<v8::FunctionTemplate> fun_templ = 9135 v8::FunctionTemplate::New(UnlockForAMoment); 9136 Local<Function> fun = fun_templ->GetFunction(); 9137 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 9138 Local<Script> script = v8_compile("(function () {" 9139 " unlock_for_a_moment();" 9140 " return 42;" 9141 "})();"); 9142 CHECK_EQ(42, script->Run()->Int32Value()); 9143 } 9144 { 9145 v8::Locker locker; 9146 v8::HandleScope scope; 9147 LocalContext env; 9148 Local<v8::FunctionTemplate> fun_templ = 9149 v8::FunctionTemplate::New(UnlockForAMoment); 9150 Local<Function> fun = fun_templ->GetFunction(); 9151 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 9152 Local<Script> script = v8_compile("(function () {" 9153 " unlock_for_a_moment();" 9154 " return 42;" 9155 "})();"); 9156 CHECK_EQ(42, script->Run()->Int32Value()); 9157 } 9158 } 9159 9160 9161 static int GetGlobalObjectsCount() { 9162 int count = 0; 9163 i::HeapIterator it; 9164 for (i::HeapObject* object = it.next(); object != NULL; object = it.next()) 9165 if (object->IsJSGlobalObject()) count++; 9166 return count; 9167 } 9168 9169 9170 static void CheckSurvivingGlobalObjectsCount(int expected) { 9171 // We need to collect all garbage twice to be sure that everything 9172 // has been collected. This is because inline caches are cleared in 9173 // the first garbage collection but some of the maps have already 9174 // been marked at that point. Therefore some of the maps are not 9175 // collected until the second garbage collection. 9176 HEAP->CollectAllGarbage(false); 9177 HEAP->CollectAllGarbage(false); 9178 int count = GetGlobalObjectsCount(); 9179 #ifdef DEBUG 9180 if (count != expected) HEAP->TracePathToGlobal(); 9181 #endif 9182 CHECK_EQ(expected, count); 9183 } 9184 9185 9186 TEST(DontLeakGlobalObjects) { 9187 // Regression test for issues 1139850 and 1174891. 9188 9189 v8::V8::Initialize(); 9190 9191 for (int i = 0; i < 5; i++) { 9192 { v8::HandleScope scope; 9193 LocalContext context; 9194 } 9195 CheckSurvivingGlobalObjectsCount(0); 9196 9197 { v8::HandleScope scope; 9198 LocalContext context; 9199 v8_compile("Date")->Run(); 9200 } 9201 CheckSurvivingGlobalObjectsCount(0); 9202 9203 { v8::HandleScope scope; 9204 LocalContext context; 9205 v8_compile("/aaa/")->Run(); 9206 } 9207 CheckSurvivingGlobalObjectsCount(0); 9208 9209 { v8::HandleScope scope; 9210 const char* extension_list[] = { "v8/gc" }; 9211 v8::ExtensionConfiguration extensions(1, extension_list); 9212 LocalContext context(&extensions); 9213 v8_compile("gc();")->Run(); 9214 } 9215 CheckSurvivingGlobalObjectsCount(0); 9216 } 9217 } 9218 9219 9220 v8::Persistent<v8::Object> some_object; 9221 v8::Persistent<v8::Object> bad_handle; 9222 9223 void NewPersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) { 9224 v8::HandleScope scope; 9225 bad_handle = v8::Persistent<v8::Object>::New(some_object); 9226 handle.Dispose(); 9227 } 9228 9229 9230 THREADED_TEST(NewPersistentHandleFromWeakCallback) { 9231 LocalContext context; 9232 9233 v8::Persistent<v8::Object> handle1, handle2; 9234 { 9235 v8::HandleScope scope; 9236 some_object = v8::Persistent<v8::Object>::New(v8::Object::New()); 9237 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 9238 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 9239 } 9240 // Note: order is implementation dependent alas: currently 9241 // global handle nodes are processed by PostGarbageCollectionProcessing 9242 // in reverse allocation order, so if second allocated handle is deleted, 9243 // weak callback of the first handle would be able to 'reallocate' it. 9244 handle1.MakeWeak(NULL, NewPersistentHandleCallback); 9245 handle2.Dispose(); 9246 HEAP->CollectAllGarbage(false); 9247 } 9248 9249 9250 v8::Persistent<v8::Object> to_be_disposed; 9251 9252 void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) { 9253 to_be_disposed.Dispose(); 9254 HEAP->CollectAllGarbage(false); 9255 handle.Dispose(); 9256 } 9257 9258 9259 THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) { 9260 LocalContext context; 9261 9262 v8::Persistent<v8::Object> handle1, handle2; 9263 { 9264 v8::HandleScope scope; 9265 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 9266 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 9267 } 9268 handle1.MakeWeak(NULL, DisposeAndForceGcCallback); 9269 to_be_disposed = handle2; 9270 HEAP->CollectAllGarbage(false); 9271 } 9272 9273 void DisposingCallback(v8::Persistent<v8::Value> handle, void*) { 9274 handle.Dispose(); 9275 } 9276 9277 void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) { 9278 v8::HandleScope scope; 9279 v8::Persistent<v8::Object>::New(v8::Object::New()); 9280 handle.Dispose(); 9281 } 9282 9283 9284 THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) { 9285 LocalContext context; 9286 9287 v8::Persistent<v8::Object> handle1, handle2, handle3; 9288 { 9289 v8::HandleScope scope; 9290 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New()); 9291 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 9292 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 9293 } 9294 handle2.MakeWeak(NULL, DisposingCallback); 9295 handle3.MakeWeak(NULL, HandleCreatingCallback); 9296 HEAP->CollectAllGarbage(false); 9297 } 9298 9299 9300 THREADED_TEST(CheckForCrossContextObjectLiterals) { 9301 v8::V8::Initialize(); 9302 9303 const int nof = 2; 9304 const char* sources[nof] = { 9305 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }", 9306 "Object()" 9307 }; 9308 9309 for (int i = 0; i < nof; i++) { 9310 const char* source = sources[i]; 9311 { v8::HandleScope scope; 9312 LocalContext context; 9313 CompileRun(source); 9314 } 9315 { v8::HandleScope scope; 9316 LocalContext context; 9317 CompileRun(source); 9318 } 9319 } 9320 } 9321 9322 9323 static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) { 9324 v8::HandleScope inner; 9325 env->Enter(); 9326 v8::Handle<Value> three = v8_num(3); 9327 v8::Handle<Value> value = inner.Close(three); 9328 env->Exit(); 9329 return value; 9330 } 9331 9332 9333 THREADED_TEST(NestedHandleScopeAndContexts) { 9334 v8::HandleScope outer; 9335 v8::Persistent<Context> env = Context::New(); 9336 env->Enter(); 9337 v8::Handle<Value> value = NestedScope(env); 9338 v8::Handle<String> str = value->ToString(); 9339 env->Exit(); 9340 env.Dispose(); 9341 } 9342 9343 9344 THREADED_TEST(ExternalAllocatedMemory) { 9345 v8::HandleScope outer; 9346 v8::Persistent<Context> env = Context::New(); 9347 const int kSize = 1024*1024; 9348 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize); 9349 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0); 9350 } 9351 9352 9353 THREADED_TEST(DisposeEnteredContext) { 9354 v8::HandleScope scope; 9355 LocalContext outer; 9356 { v8::Persistent<v8::Context> inner = v8::Context::New(); 9357 inner->Enter(); 9358 inner.Dispose(); 9359 inner.Clear(); 9360 inner->Exit(); 9361 } 9362 } 9363 9364 9365 // Regression test for issue 54, object templates with internal fields 9366 // but no accessors or interceptors did not get their internal field 9367 // count set on instances. 9368 THREADED_TEST(Regress54) { 9369 v8::HandleScope outer; 9370 LocalContext context; 9371 static v8::Persistent<v8::ObjectTemplate> templ; 9372 if (templ.IsEmpty()) { 9373 v8::HandleScope inner; 9374 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New(); 9375 local->SetInternalFieldCount(1); 9376 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local)); 9377 } 9378 v8::Handle<v8::Object> result = templ->NewInstance(); 9379 CHECK_EQ(1, result->InternalFieldCount()); 9380 } 9381 9382 9383 // If part of the threaded tests, this test makes ThreadingTest fail 9384 // on mac. 9385 TEST(CatchStackOverflow) { 9386 v8::HandleScope scope; 9387 LocalContext context; 9388 v8::TryCatch try_catch; 9389 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New( 9390 "function f() {" 9391 " return f();" 9392 "}" 9393 "" 9394 "f();")); 9395 v8::Handle<v8::Value> result = script->Run(); 9396 CHECK(result.IsEmpty()); 9397 } 9398 9399 9400 static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script, 9401 const char* resource_name, 9402 int line_offset) { 9403 v8::HandleScope scope; 9404 v8::TryCatch try_catch; 9405 v8::Handle<v8::Value> result = script->Run(); 9406 CHECK(result.IsEmpty()); 9407 CHECK(try_catch.HasCaught()); 9408 v8::Handle<v8::Message> message = try_catch.Message(); 9409 CHECK(!message.IsEmpty()); 9410 CHECK_EQ(10 + line_offset, message->GetLineNumber()); 9411 CHECK_EQ(91, message->GetStartPosition()); 9412 CHECK_EQ(92, message->GetEndPosition()); 9413 CHECK_EQ(2, message->GetStartColumn()); 9414 CHECK_EQ(3, message->GetEndColumn()); 9415 v8::String::AsciiValue line(message->GetSourceLine()); 9416 CHECK_EQ(" throw 'nirk';", *line); 9417 v8::String::AsciiValue name(message->GetScriptResourceName()); 9418 CHECK_EQ(resource_name, *name); 9419 } 9420 9421 9422 THREADED_TEST(TryCatchSourceInfo) { 9423 v8::HandleScope scope; 9424 LocalContext context; 9425 v8::Handle<v8::String> source = v8::String::New( 9426 "function Foo() {\n" 9427 " return Bar();\n" 9428 "}\n" 9429 "\n" 9430 "function Bar() {\n" 9431 " return Baz();\n" 9432 "}\n" 9433 "\n" 9434 "function Baz() {\n" 9435 " throw 'nirk';\n" 9436 "}\n" 9437 "\n" 9438 "Foo();\n"); 9439 9440 const char* resource_name; 9441 v8::Handle<v8::Script> script; 9442 resource_name = "test.js"; 9443 script = v8::Script::Compile(source, v8::String::New(resource_name)); 9444 CheckTryCatchSourceInfo(script, resource_name, 0); 9445 9446 resource_name = "test1.js"; 9447 v8::ScriptOrigin origin1(v8::String::New(resource_name)); 9448 script = v8::Script::Compile(source, &origin1); 9449 CheckTryCatchSourceInfo(script, resource_name, 0); 9450 9451 resource_name = "test2.js"; 9452 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7)); 9453 script = v8::Script::Compile(source, &origin2); 9454 CheckTryCatchSourceInfo(script, resource_name, 7); 9455 } 9456 9457 9458 THREADED_TEST(CompilationCache) { 9459 v8::HandleScope scope; 9460 LocalContext context; 9461 v8::Handle<v8::String> source0 = v8::String::New("1234"); 9462 v8::Handle<v8::String> source1 = v8::String::New("1234"); 9463 v8::Handle<v8::Script> script0 = 9464 v8::Script::Compile(source0, v8::String::New("test.js")); 9465 v8::Handle<v8::Script> script1 = 9466 v8::Script::Compile(source1, v8::String::New("test.js")); 9467 v8::Handle<v8::Script> script2 = 9468 v8::Script::Compile(source0); // different origin 9469 CHECK_EQ(1234, script0->Run()->Int32Value()); 9470 CHECK_EQ(1234, script1->Run()->Int32Value()); 9471 CHECK_EQ(1234, script2->Run()->Int32Value()); 9472 } 9473 9474 9475 static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) { 9476 ApiTestFuzzer::Fuzz(); 9477 return v8_num(42); 9478 } 9479 9480 9481 THREADED_TEST(CallbackFunctionName) { 9482 v8::HandleScope scope; 9483 LocalContext context; 9484 Local<ObjectTemplate> t = ObjectTemplate::New(); 9485 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback)); 9486 context->Global()->Set(v8_str("obj"), t->NewInstance()); 9487 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name"); 9488 CHECK(value->IsString()); 9489 v8::String::AsciiValue name(value); 9490 CHECK_EQ("asdf", *name); 9491 } 9492 9493 9494 THREADED_TEST(DateAccess) { 9495 v8::HandleScope scope; 9496 LocalContext context; 9497 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0); 9498 CHECK(date->IsDate()); 9499 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue()); 9500 } 9501 9502 9503 void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) { 9504 v8::Handle<v8::Object> obj = val.As<v8::Object>(); 9505 v8::Handle<v8::Array> props = obj->GetPropertyNames(); 9506 CHECK_EQ(elmc, props->Length()); 9507 for (int i = 0; i < elmc; i++) { 9508 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i))); 9509 CHECK_EQ(elmv[i], *elm); 9510 } 9511 } 9512 9513 9514 THREADED_TEST(PropertyEnumeration) { 9515 v8::HandleScope scope; 9516 LocalContext context; 9517 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New( 9518 "var result = [];" 9519 "result[0] = {};" 9520 "result[1] = {a: 1, b: 2};" 9521 "result[2] = [1, 2, 3];" 9522 "var proto = {x: 1, y: 2, z: 3};" 9523 "var x = { __proto__: proto, w: 0, z: 1 };" 9524 "result[3] = x;" 9525 "result;"))->Run(); 9526 v8::Handle<v8::Array> elms = obj.As<v8::Array>(); 9527 CHECK_EQ(4, elms->Length()); 9528 int elmc0 = 0; 9529 const char** elmv0 = NULL; 9530 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0); 9531 int elmc1 = 2; 9532 const char* elmv1[] = {"a", "b"}; 9533 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1); 9534 int elmc2 = 3; 9535 const char* elmv2[] = {"0", "1", "2"}; 9536 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2); 9537 int elmc3 = 4; 9538 const char* elmv3[] = {"w", "z", "x", "y"}; 9539 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3); 9540 } 9541 9542 THREADED_TEST(PropertyEnumeration2) { 9543 v8::HandleScope scope; 9544 LocalContext context; 9545 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New( 9546 "var result = [];" 9547 "result[0] = {};" 9548 "result[1] = {a: 1, b: 2};" 9549 "result[2] = [1, 2, 3];" 9550 "var proto = {x: 1, y: 2, z: 3};" 9551 "var x = { __proto__: proto, w: 0, z: 1 };" 9552 "result[3] = x;" 9553 "result;"))->Run(); 9554 v8::Handle<v8::Array> elms = obj.As<v8::Array>(); 9555 CHECK_EQ(4, elms->Length()); 9556 int elmc0 = 0; 9557 const char** elmv0 = NULL; 9558 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0); 9559 9560 v8::Handle<v8::Value> val = elms->Get(v8::Integer::New(0)); 9561 v8::Handle<v8::Array> props = val.As<v8::Object>()->GetPropertyNames(); 9562 CHECK_EQ(0, props->Length()); 9563 for (uint32_t i = 0; i < props->Length(); i++) { 9564 printf("p[%d]\n", i); 9565 } 9566 } 9567 9568 static bool NamedSetAccessBlocker(Local<v8::Object> obj, 9569 Local<Value> name, 9570 v8::AccessType type, 9571 Local<Value> data) { 9572 return type != v8::ACCESS_SET; 9573 } 9574 9575 9576 static bool IndexedSetAccessBlocker(Local<v8::Object> obj, 9577 uint32_t key, 9578 v8::AccessType type, 9579 Local<Value> data) { 9580 return type != v8::ACCESS_SET; 9581 } 9582 9583 9584 THREADED_TEST(DisableAccessChecksWhileConfiguring) { 9585 v8::HandleScope scope; 9586 LocalContext context; 9587 Local<ObjectTemplate> templ = ObjectTemplate::New(); 9588 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker, 9589 IndexedSetAccessBlocker); 9590 templ->Set(v8_str("x"), v8::True()); 9591 Local<v8::Object> instance = templ->NewInstance(); 9592 context->Global()->Set(v8_str("obj"), instance); 9593 Local<Value> value = CompileRun("obj.x"); 9594 CHECK(value->BooleanValue()); 9595 } 9596 9597 9598 static bool NamedGetAccessBlocker(Local<v8::Object> obj, 9599 Local<Value> name, 9600 v8::AccessType type, 9601 Local<Value> data) { 9602 return false; 9603 } 9604 9605 9606 static bool IndexedGetAccessBlocker(Local<v8::Object> obj, 9607 uint32_t key, 9608 v8::AccessType type, 9609 Local<Value> data) { 9610 return false; 9611 } 9612 9613 9614 9615 THREADED_TEST(AccessChecksReenabledCorrectly) { 9616 v8::HandleScope scope; 9617 LocalContext context; 9618 Local<ObjectTemplate> templ = ObjectTemplate::New(); 9619 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker, 9620 IndexedGetAccessBlocker); 9621 templ->Set(v8_str("a"), v8_str("a")); 9622 // Add more than 8 (see kMaxFastProperties) properties 9623 // so that the constructor will force copying map. 9624 // Cannot sprintf, gcc complains unsafety. 9625 char buf[4]; 9626 for (char i = '0'; i <= '9' ; i++) { 9627 buf[0] = i; 9628 for (char j = '0'; j <= '9'; j++) { 9629 buf[1] = j; 9630 for (char k = '0'; k <= '9'; k++) { 9631 buf[2] = k; 9632 buf[3] = 0; 9633 templ->Set(v8_str(buf), v8::Number::New(k)); 9634 } 9635 } 9636 } 9637 9638 Local<v8::Object> instance_1 = templ->NewInstance(); 9639 context->Global()->Set(v8_str("obj_1"), instance_1); 9640 9641 Local<Value> value_1 = CompileRun("obj_1.a"); 9642 CHECK(value_1->IsUndefined()); 9643 9644 Local<v8::Object> instance_2 = templ->NewInstance(); 9645 context->Global()->Set(v8_str("obj_2"), instance_2); 9646 9647 Local<Value> value_2 = CompileRun("obj_2.a"); 9648 CHECK(value_2->IsUndefined()); 9649 } 9650 9651 9652 // This tests that access check information remains on the global 9653 // object template when creating contexts. 9654 THREADED_TEST(AccessControlRepeatedContextCreation) { 9655 v8::HandleScope handle_scope; 9656 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 9657 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker, 9658 IndexedSetAccessBlocker); 9659 i::Handle<i::ObjectTemplateInfo> internal_template = 9660 v8::Utils::OpenHandle(*global_template); 9661 CHECK(!internal_template->constructor()->IsUndefined()); 9662 i::Handle<i::FunctionTemplateInfo> constructor( 9663 i::FunctionTemplateInfo::cast(internal_template->constructor())); 9664 CHECK(!constructor->access_check_info()->IsUndefined()); 9665 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 9666 CHECK(!constructor->access_check_info()->IsUndefined()); 9667 } 9668 9669 9670 THREADED_TEST(TurnOnAccessCheck) { 9671 v8::HandleScope handle_scope; 9672 9673 // Create an environment with access check to the global object disabled by 9674 // default. 9675 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 9676 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker, 9677 IndexedGetAccessBlocker, 9678 v8::Handle<v8::Value>(), 9679 false); 9680 v8::Persistent<Context> context = Context::New(NULL, global_template); 9681 Context::Scope context_scope(context); 9682 9683 // Set up a property and a number of functions. 9684 context->Global()->Set(v8_str("a"), v8_num(1)); 9685 CompileRun("function f1() {return a;}" 9686 "function f2() {return a;}" 9687 "function g1() {return h();}" 9688 "function g2() {return h();}" 9689 "function h() {return 1;}"); 9690 Local<Function> f1 = 9691 Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); 9692 Local<Function> f2 = 9693 Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); 9694 Local<Function> g1 = 9695 Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); 9696 Local<Function> g2 = 9697 Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); 9698 Local<Function> h = 9699 Local<Function>::Cast(context->Global()->Get(v8_str("h"))); 9700 9701 // Get the global object. 9702 v8::Handle<v8::Object> global = context->Global(); 9703 9704 // Call f1 one time and f2 a number of times. This will ensure that f1 still 9705 // uses the runtime system to retreive property a whereas f2 uses global load 9706 // inline cache. 9707 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1))); 9708 for (int i = 0; i < 4; i++) { 9709 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1))); 9710 } 9711 9712 // Same for g1 and g2. 9713 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1))); 9714 for (int i = 0; i < 4; i++) { 9715 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1))); 9716 } 9717 9718 // Detach the global and turn on access check. 9719 context->DetachGlobal(); 9720 context->Global()->TurnOnAccessCheck(); 9721 9722 // Failing access check to property get results in undefined. 9723 CHECK(f1->Call(global, 0, NULL)->IsUndefined()); 9724 CHECK(f2->Call(global, 0, NULL)->IsUndefined()); 9725 9726 // Failing access check to function call results in exception. 9727 CHECK(g1->Call(global, 0, NULL).IsEmpty()); 9728 CHECK(g2->Call(global, 0, NULL).IsEmpty()); 9729 9730 // No failing access check when just returning a constant. 9731 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1))); 9732 } 9733 9734 9735 v8::Handle<v8::String> a; 9736 v8::Handle<v8::String> h; 9737 9738 static bool NamedGetAccessBlockAandH(Local<v8::Object> obj, 9739 Local<Value> name, 9740 v8::AccessType type, 9741 Local<Value> data) { 9742 return !(name->Equals(a) || name->Equals(h)); 9743 } 9744 9745 9746 THREADED_TEST(TurnOnAccessCheckAndRecompile) { 9747 v8::HandleScope handle_scope; 9748 9749 // Create an environment with access check to the global object disabled by 9750 // default. When the registered access checker will block access to properties 9751 // a and h 9752 a = v8_str("a"); 9753 h = v8_str("h"); 9754 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 9755 global_template->SetAccessCheckCallbacks(NamedGetAccessBlockAandH, 9756 IndexedGetAccessBlocker, 9757 v8::Handle<v8::Value>(), 9758 false); 9759 v8::Persistent<Context> context = Context::New(NULL, global_template); 9760 Context::Scope context_scope(context); 9761 9762 // Set up a property and a number of functions. 9763 context->Global()->Set(v8_str("a"), v8_num(1)); 9764 static const char* source = "function f1() {return a;}" 9765 "function f2() {return a;}" 9766 "function g1() {return h();}" 9767 "function g2() {return h();}" 9768 "function h() {return 1;}"; 9769 9770 CompileRun(source); 9771 Local<Function> f1; 9772 Local<Function> f2; 9773 Local<Function> g1; 9774 Local<Function> g2; 9775 Local<Function> h; 9776 f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); 9777 f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); 9778 g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); 9779 g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); 9780 h = Local<Function>::Cast(context->Global()->Get(v8_str("h"))); 9781 9782 // Get the global object. 9783 v8::Handle<v8::Object> global = context->Global(); 9784 9785 // Call f1 one time and f2 a number of times. This will ensure that f1 still 9786 // uses the runtime system to retreive property a whereas f2 uses global load 9787 // inline cache. 9788 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1))); 9789 for (int i = 0; i < 4; i++) { 9790 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1))); 9791 } 9792 9793 // Same for g1 and g2. 9794 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1))); 9795 for (int i = 0; i < 4; i++) { 9796 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1))); 9797 } 9798 9799 // Detach the global and turn on access check now blocking access to property 9800 // a and function h. 9801 context->DetachGlobal(); 9802 context->Global()->TurnOnAccessCheck(); 9803 9804 // Failing access check to property get results in undefined. 9805 CHECK(f1->Call(global, 0, NULL)->IsUndefined()); 9806 CHECK(f2->Call(global, 0, NULL)->IsUndefined()); 9807 9808 // Failing access check to function call results in exception. 9809 CHECK(g1->Call(global, 0, NULL).IsEmpty()); 9810 CHECK(g2->Call(global, 0, NULL).IsEmpty()); 9811 9812 // No failing access check when just returning a constant. 9813 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1))); 9814 9815 // Now compile the source again. And get the newly compiled functions, except 9816 // for h for which access is blocked. 9817 CompileRun(source); 9818 f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); 9819 f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); 9820 g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); 9821 g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); 9822 CHECK(context->Global()->Get(v8_str("h"))->IsUndefined()); 9823 9824 // Failing access check to property get results in undefined. 9825 CHECK(f1->Call(global, 0, NULL)->IsUndefined()); 9826 CHECK(f2->Call(global, 0, NULL)->IsUndefined()); 9827 9828 // Failing access check to function call results in exception. 9829 CHECK(g1->Call(global, 0, NULL).IsEmpty()); 9830 CHECK(g2->Call(global, 0, NULL).IsEmpty()); 9831 } 9832 9833 9834 // This test verifies that pre-compilation (aka preparsing) can be called 9835 // without initializing the whole VM. Thus we cannot run this test in a 9836 // multi-threaded setup. 9837 TEST(PreCompile) { 9838 // TODO(155): This test would break without the initialization of V8. This is 9839 // a workaround for now to make this test not fail. 9840 v8::V8::Initialize(); 9841 const char* script = "function foo(a) { return a+1; }"; 9842 v8::ScriptData* sd = 9843 v8::ScriptData::PreCompile(script, i::StrLength(script)); 9844 CHECK_NE(sd->Length(), 0); 9845 CHECK_NE(sd->Data(), NULL); 9846 CHECK(!sd->HasError()); 9847 delete sd; 9848 } 9849 9850 9851 TEST(PreCompileWithError) { 9852 v8::V8::Initialize(); 9853 const char* script = "function foo(a) { return 1 * * 2; }"; 9854 v8::ScriptData* sd = 9855 v8::ScriptData::PreCompile(script, i::StrLength(script)); 9856 CHECK(sd->HasError()); 9857 delete sd; 9858 } 9859 9860 9861 TEST(Regress31661) { 9862 v8::V8::Initialize(); 9863 const char* script = " The Definintive Guide"; 9864 v8::ScriptData* sd = 9865 v8::ScriptData::PreCompile(script, i::StrLength(script)); 9866 CHECK(sd->HasError()); 9867 delete sd; 9868 } 9869 9870 9871 // Tests that ScriptData can be serialized and deserialized. 9872 TEST(PreCompileSerialization) { 9873 v8::V8::Initialize(); 9874 const char* script = "function foo(a) { return a+1; }"; 9875 v8::ScriptData* sd = 9876 v8::ScriptData::PreCompile(script, i::StrLength(script)); 9877 9878 // Serialize. 9879 int serialized_data_length = sd->Length(); 9880 char* serialized_data = i::NewArray<char>(serialized_data_length); 9881 memcpy(serialized_data, sd->Data(), serialized_data_length); 9882 9883 // Deserialize. 9884 v8::ScriptData* deserialized_sd = 9885 v8::ScriptData::New(serialized_data, serialized_data_length); 9886 9887 // Verify that the original is the same as the deserialized. 9888 CHECK_EQ(sd->Length(), deserialized_sd->Length()); 9889 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length())); 9890 CHECK_EQ(sd->HasError(), deserialized_sd->HasError()); 9891 9892 delete sd; 9893 delete deserialized_sd; 9894 } 9895 9896 9897 // Attempts to deserialize bad data. 9898 TEST(PreCompileDeserializationError) { 9899 v8::V8::Initialize(); 9900 const char* data = "DONT CARE"; 9901 int invalid_size = 3; 9902 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size); 9903 9904 CHECK_EQ(0, sd->Length()); 9905 9906 delete sd; 9907 } 9908 9909 9910 // Attempts to deserialize bad data. 9911 TEST(PreCompileInvalidPreparseDataError) { 9912 v8::V8::Initialize(); 9913 v8::HandleScope scope; 9914 LocalContext context; 9915 9916 const char* script = "function foo(){ return 5;}\n" 9917 "function bar(){ return 6 + 7;} foo();"; 9918 v8::ScriptData* sd = 9919 v8::ScriptData::PreCompile(script, i::StrLength(script)); 9920 CHECK(!sd->HasError()); 9921 // ScriptDataImpl private implementation details 9922 const int kHeaderSize = i::PreparseDataConstants::kHeaderSize; 9923 const int kFunctionEntrySize = i::FunctionEntry::kSize; 9924 const int kFunctionEntryStartOffset = 0; 9925 const int kFunctionEntryEndOffset = 1; 9926 unsigned* sd_data = 9927 reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data())); 9928 9929 // Overwrite function bar's end position with 0. 9930 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0; 9931 v8::TryCatch try_catch; 9932 9933 Local<String> source = String::New(script); 9934 Local<Script> compiled_script = Script::New(source, NULL, sd); 9935 CHECK(try_catch.HasCaught()); 9936 String::AsciiValue exception_value(try_catch.Message()->Get()); 9937 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar", 9938 *exception_value); 9939 9940 try_catch.Reset(); 9941 // Overwrite function bar's start position with 200. The function entry 9942 // will not be found when searching for it by position. 9943 sd = v8::ScriptData::PreCompile(script, i::StrLength(script)); 9944 sd_data = reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data())); 9945 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] = 9946 200; 9947 compiled_script = Script::New(source, NULL, sd); 9948 CHECK(try_catch.HasCaught()); 9949 String::AsciiValue second_exception_value(try_catch.Message()->Get()); 9950 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar", 9951 *second_exception_value); 9952 9953 delete sd; 9954 } 9955 9956 9957 // Verifies that the Handle<String> and const char* versions of the API produce 9958 // the same results (at least for one trivial case). 9959 TEST(PreCompileAPIVariationsAreSame) { 9960 v8::V8::Initialize(); 9961 v8::HandleScope scope; 9962 9963 const char* cstring = "function foo(a) { return a+1; }"; 9964 9965 v8::ScriptData* sd_from_cstring = 9966 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring)); 9967 9968 TestAsciiResource* resource = new TestAsciiResource(cstring); 9969 v8::ScriptData* sd_from_external_string = v8::ScriptData::PreCompile( 9970 v8::String::NewExternal(resource)); 9971 9972 v8::ScriptData* sd_from_string = v8::ScriptData::PreCompile( 9973 v8::String::New(cstring)); 9974 9975 CHECK_EQ(sd_from_cstring->Length(), sd_from_external_string->Length()); 9976 CHECK_EQ(0, memcmp(sd_from_cstring->Data(), 9977 sd_from_external_string->Data(), 9978 sd_from_cstring->Length())); 9979 9980 CHECK_EQ(sd_from_cstring->Length(), sd_from_string->Length()); 9981 CHECK_EQ(0, memcmp(sd_from_cstring->Data(), 9982 sd_from_string->Data(), 9983 sd_from_cstring->Length())); 9984 9985 9986 delete sd_from_cstring; 9987 delete sd_from_external_string; 9988 delete sd_from_string; 9989 } 9990 9991 9992 // This tests that we do not allow dictionary load/call inline caches 9993 // to use functions that have not yet been compiled. The potential 9994 // problem of loading a function that has not yet been compiled can 9995 // arise because we share code between contexts via the compilation 9996 // cache. 9997 THREADED_TEST(DictionaryICLoadedFunction) { 9998 v8::HandleScope scope; 9999 // Test LoadIC. 10000 for (int i = 0; i < 2; i++) { 10001 LocalContext context; 10002 context->Global()->Set(v8_str("tmp"), v8::True()); 10003 context->Global()->Delete(v8_str("tmp")); 10004 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');"); 10005 } 10006 // Test CallIC. 10007 for (int i = 0; i < 2; i++) { 10008 LocalContext context; 10009 context->Global()->Set(v8_str("tmp"), v8::True()); 10010 context->Global()->Delete(v8_str("tmp")); 10011 CompileRun("for (var j = 0; j < 10; j++) RegExp('')"); 10012 } 10013 } 10014 10015 10016 // Test that cross-context new calls use the context of the callee to 10017 // create the new JavaScript object. 10018 THREADED_TEST(CrossContextNew) { 10019 v8::HandleScope scope; 10020 v8::Persistent<Context> context0 = Context::New(); 10021 v8::Persistent<Context> context1 = Context::New(); 10022 10023 // Allow cross-domain access. 10024 Local<String> token = v8_str("<security token>"); 10025 context0->SetSecurityToken(token); 10026 context1->SetSecurityToken(token); 10027 10028 // Set an 'x' property on the Object prototype and define a 10029 // constructor function in context0. 10030 context0->Enter(); 10031 CompileRun("Object.prototype.x = 42; function C() {};"); 10032 context0->Exit(); 10033 10034 // Call the constructor function from context0 and check that the 10035 // result has the 'x' property. 10036 context1->Enter(); 10037 context1->Global()->Set(v8_str("other"), context0->Global()); 10038 Local<Value> value = CompileRun("var instance = new other.C(); instance.x"); 10039 CHECK(value->IsInt32()); 10040 CHECK_EQ(42, value->Int32Value()); 10041 context1->Exit(); 10042 10043 // Dispose the contexts to allow them to be garbage collected. 10044 context0.Dispose(); 10045 context1.Dispose(); 10046 } 10047 10048 10049 class RegExpInterruptTest { 10050 public: 10051 RegExpInterruptTest() : block_(NULL) {} 10052 ~RegExpInterruptTest() { delete block_; } 10053 void RunTest() { 10054 block_ = i::OS::CreateSemaphore(0); 10055 gc_count_ = 0; 10056 gc_during_regexp_ = 0; 10057 regexp_success_ = false; 10058 gc_success_ = false; 10059 GCThread gc_thread(i::Isolate::Current(), this); 10060 gc_thread.Start(); 10061 v8::Locker::StartPreemption(1); 10062 10063 LongRunningRegExp(); 10064 { 10065 v8::Unlocker unlock; 10066 gc_thread.Join(); 10067 } 10068 v8::Locker::StopPreemption(); 10069 CHECK(regexp_success_); 10070 CHECK(gc_success_); 10071 } 10072 private: 10073 // Number of garbage collections required. 10074 static const int kRequiredGCs = 5; 10075 10076 class GCThread : public i::Thread { 10077 public: 10078 explicit GCThread(i::Isolate* isolate, RegExpInterruptTest* test) 10079 : Thread(isolate, "GCThread"), test_(test) {} 10080 virtual void Run() { 10081 test_->CollectGarbage(); 10082 } 10083 private: 10084 RegExpInterruptTest* test_; 10085 }; 10086 10087 void CollectGarbage() { 10088 block_->Wait(); 10089 while (gc_during_regexp_ < kRequiredGCs) { 10090 { 10091 v8::Locker lock; 10092 // TODO(lrn): Perhaps create some garbage before collecting. 10093 HEAP->CollectAllGarbage(false); 10094 gc_count_++; 10095 } 10096 i::OS::Sleep(1); 10097 } 10098 gc_success_ = true; 10099 } 10100 10101 void LongRunningRegExp() { 10102 block_->Signal(); // Enable garbage collection thread on next preemption. 10103 int rounds = 0; 10104 while (gc_during_regexp_ < kRequiredGCs) { 10105 int gc_before = gc_count_; 10106 { 10107 // Match 15-30 "a"'s against 14 and a "b". 10108 const char* c_source = 10109 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 10110 ".exec('aaaaaaaaaaaaaaab') === null"; 10111 Local<String> source = String::New(c_source); 10112 Local<Script> script = Script::Compile(source); 10113 Local<Value> result = script->Run(); 10114 if (!result->BooleanValue()) { 10115 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit. 10116 return; 10117 } 10118 } 10119 { 10120 // Match 15-30 "a"'s against 15 and a "b". 10121 const char* c_source = 10122 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 10123 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'"; 10124 Local<String> source = String::New(c_source); 10125 Local<Script> script = Script::Compile(source); 10126 Local<Value> result = script->Run(); 10127 if (!result->BooleanValue()) { 10128 gc_during_regexp_ = kRequiredGCs; 10129 return; 10130 } 10131 } 10132 int gc_after = gc_count_; 10133 gc_during_regexp_ += gc_after - gc_before; 10134 rounds++; 10135 i::OS::Sleep(1); 10136 } 10137 regexp_success_ = true; 10138 } 10139 10140 i::Semaphore* block_; 10141 int gc_count_; 10142 int gc_during_regexp_; 10143 bool regexp_success_; 10144 bool gc_success_; 10145 }; 10146 10147 10148 // Test that a regular expression execution can be interrupted and 10149 // survive a garbage collection. 10150 TEST(RegExpInterruption) { 10151 v8::Locker lock; 10152 v8::V8::Initialize(); 10153 v8::HandleScope scope; 10154 Local<Context> local_env; 10155 { 10156 LocalContext env; 10157 local_env = env.local(); 10158 } 10159 10160 // Local context should still be live. 10161 CHECK(!local_env.IsEmpty()); 10162 local_env->Enter(); 10163 10164 // Should complete without problems. 10165 RegExpInterruptTest().RunTest(); 10166 10167 local_env->Exit(); 10168 } 10169 10170 10171 class ApplyInterruptTest { 10172 public: 10173 ApplyInterruptTest() : block_(NULL) {} 10174 ~ApplyInterruptTest() { delete block_; } 10175 void RunTest() { 10176 block_ = i::OS::CreateSemaphore(0); 10177 gc_count_ = 0; 10178 gc_during_apply_ = 0; 10179 apply_success_ = false; 10180 gc_success_ = false; 10181 GCThread gc_thread(i::Isolate::Current(), this); 10182 gc_thread.Start(); 10183 v8::Locker::StartPreemption(1); 10184 10185 LongRunningApply(); 10186 { 10187 v8::Unlocker unlock; 10188 gc_thread.Join(); 10189 } 10190 v8::Locker::StopPreemption(); 10191 CHECK(apply_success_); 10192 CHECK(gc_success_); 10193 } 10194 private: 10195 // Number of garbage collections required. 10196 static const int kRequiredGCs = 2; 10197 10198 class GCThread : public i::Thread { 10199 public: 10200 explicit GCThread(i::Isolate* isolate, ApplyInterruptTest* test) 10201 : Thread(isolate, "GCThread"), test_(test) {} 10202 virtual void Run() { 10203 test_->CollectGarbage(); 10204 } 10205 private: 10206 ApplyInterruptTest* test_; 10207 }; 10208 10209 void CollectGarbage() { 10210 block_->Wait(); 10211 while (gc_during_apply_ < kRequiredGCs) { 10212 { 10213 v8::Locker lock; 10214 HEAP->CollectAllGarbage(false); 10215 gc_count_++; 10216 } 10217 i::OS::Sleep(1); 10218 } 10219 gc_success_ = true; 10220 } 10221 10222 void LongRunningApply() { 10223 block_->Signal(); 10224 int rounds = 0; 10225 while (gc_during_apply_ < kRequiredGCs) { 10226 int gc_before = gc_count_; 10227 { 10228 const char* c_source = 10229 "function do_very_little(bar) {" 10230 " this.foo = bar;" 10231 "}" 10232 "for (var i = 0; i < 100000; i++) {" 10233 " do_very_little.apply(this, ['bar']);" 10234 "}"; 10235 Local<String> source = String::New(c_source); 10236 Local<Script> script = Script::Compile(source); 10237 Local<Value> result = script->Run(); 10238 // Check that no exception was thrown. 10239 CHECK(!result.IsEmpty()); 10240 } 10241 int gc_after = gc_count_; 10242 gc_during_apply_ += gc_after - gc_before; 10243 rounds++; 10244 } 10245 apply_success_ = true; 10246 } 10247 10248 i::Semaphore* block_; 10249 int gc_count_; 10250 int gc_during_apply_; 10251 bool apply_success_; 10252 bool gc_success_; 10253 }; 10254 10255 10256 // Test that nothing bad happens if we get a preemption just when we were 10257 // about to do an apply(). 10258 TEST(ApplyInterruption) { 10259 v8::Locker lock; 10260 v8::V8::Initialize(); 10261 v8::HandleScope scope; 10262 Local<Context> local_env; 10263 { 10264 LocalContext env; 10265 local_env = env.local(); 10266 } 10267 10268 // Local context should still be live. 10269 CHECK(!local_env.IsEmpty()); 10270 local_env->Enter(); 10271 10272 // Should complete without problems. 10273 ApplyInterruptTest().RunTest(); 10274 10275 local_env->Exit(); 10276 } 10277 10278 10279 // Verify that we can clone an object 10280 TEST(ObjectClone) { 10281 v8::HandleScope scope; 10282 LocalContext env; 10283 10284 const char* sample = 10285 "var rv = {};" \ 10286 "rv.alpha = 'hello';" \ 10287 "rv.beta = 123;" \ 10288 "rv;"; 10289 10290 // Create an object, verify basics. 10291 Local<Value> val = CompileRun(sample); 10292 CHECK(val->IsObject()); 10293 Local<v8::Object> obj = val.As<v8::Object>(); 10294 obj->Set(v8_str("gamma"), v8_str("cloneme")); 10295 10296 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha"))); 10297 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 10298 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma"))); 10299 10300 // Clone it. 10301 Local<v8::Object> clone = obj->Clone(); 10302 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha"))); 10303 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta"))); 10304 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma"))); 10305 10306 // Set a property on the clone, verify each object. 10307 clone->Set(v8_str("beta"), v8::Integer::New(456)); 10308 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 10309 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta"))); 10310 } 10311 10312 10313 class AsciiVectorResource : public v8::String::ExternalAsciiStringResource { 10314 public: 10315 explicit AsciiVectorResource(i::Vector<const char> vector) 10316 : data_(vector) {} 10317 virtual ~AsciiVectorResource() {} 10318 virtual size_t length() const { return data_.length(); } 10319 virtual const char* data() const { return data_.start(); } 10320 private: 10321 i::Vector<const char> data_; 10322 }; 10323 10324 10325 class UC16VectorResource : public v8::String::ExternalStringResource { 10326 public: 10327 explicit UC16VectorResource(i::Vector<const i::uc16> vector) 10328 : data_(vector) {} 10329 virtual ~UC16VectorResource() {} 10330 virtual size_t length() const { return data_.length(); } 10331 virtual const i::uc16* data() const { return data_.start(); } 10332 private: 10333 i::Vector<const i::uc16> data_; 10334 }; 10335 10336 10337 static void MorphAString(i::String* string, 10338 AsciiVectorResource* ascii_resource, 10339 UC16VectorResource* uc16_resource) { 10340 CHECK(i::StringShape(string).IsExternal()); 10341 if (string->IsAsciiRepresentation()) { 10342 // Check old map is not symbol or long. 10343 CHECK(string->map() == HEAP->external_ascii_string_map()); 10344 // Morph external string to be TwoByte string. 10345 string->set_map(HEAP->external_string_map()); 10346 i::ExternalTwoByteString* morphed = 10347 i::ExternalTwoByteString::cast(string); 10348 morphed->set_resource(uc16_resource); 10349 } else { 10350 // Check old map is not symbol or long. 10351 CHECK(string->map() == HEAP->external_string_map()); 10352 // Morph external string to be ASCII string. 10353 string->set_map(HEAP->external_ascii_string_map()); 10354 i::ExternalAsciiString* morphed = 10355 i::ExternalAsciiString::cast(string); 10356 morphed->set_resource(ascii_resource); 10357 } 10358 } 10359 10360 10361 // Test that we can still flatten a string if the components it is built up 10362 // from have been turned into 16 bit strings in the mean time. 10363 THREADED_TEST(MorphCompositeStringTest) { 10364 const char* c_string = "Now is the time for all good men" 10365 " to come to the aid of the party"; 10366 uint16_t* two_byte_string = AsciiToTwoByteString(c_string); 10367 { 10368 v8::HandleScope scope; 10369 LocalContext env; 10370 AsciiVectorResource ascii_resource( 10371 i::Vector<const char>(c_string, i::StrLength(c_string))); 10372 UC16VectorResource uc16_resource( 10373 i::Vector<const uint16_t>(two_byte_string, 10374 i::StrLength(c_string))); 10375 10376 Local<String> lhs(v8::Utils::ToLocal( 10377 FACTORY->NewExternalStringFromAscii(&ascii_resource))); 10378 Local<String> rhs(v8::Utils::ToLocal( 10379 FACTORY->NewExternalStringFromAscii(&ascii_resource))); 10380 10381 env->Global()->Set(v8_str("lhs"), lhs); 10382 env->Global()->Set(v8_str("rhs"), rhs); 10383 10384 CompileRun( 10385 "var cons = lhs + rhs;" 10386 "var slice = lhs.substring(1, lhs.length - 1);" 10387 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);"); 10388 10389 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource); 10390 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource); 10391 10392 // Now do some stuff to make sure the strings are flattened, etc. 10393 CompileRun( 10394 "/[^a-z]/.test(cons);" 10395 "/[^a-z]/.test(slice);" 10396 "/[^a-z]/.test(slice_on_cons);"); 10397 const char* expected_cons = 10398 "Now is the time for all good men to come to the aid of the party" 10399 "Now is the time for all good men to come to the aid of the party"; 10400 const char* expected_slice = 10401 "ow is the time for all good men to come to the aid of the part"; 10402 const char* expected_slice_on_cons = 10403 "ow is the time for all good men to come to the aid of the party" 10404 "Now is the time for all good men to come to the aid of the part"; 10405 CHECK_EQ(String::New(expected_cons), 10406 env->Global()->Get(v8_str("cons"))); 10407 CHECK_EQ(String::New(expected_slice), 10408 env->Global()->Get(v8_str("slice"))); 10409 CHECK_EQ(String::New(expected_slice_on_cons), 10410 env->Global()->Get(v8_str("slice_on_cons"))); 10411 } 10412 i::DeleteArray(two_byte_string); 10413 } 10414 10415 10416 TEST(CompileExternalTwoByteSource) { 10417 v8::HandleScope scope; 10418 LocalContext context; 10419 10420 // This is a very short list of sources, which currently is to check for a 10421 // regression caused by r2703. 10422 const char* ascii_sources[] = { 10423 "0.5", 10424 "-0.5", // This mainly testes PushBack in the Scanner. 10425 "--0.5", // This mainly testes PushBack in the Scanner. 10426 NULL 10427 }; 10428 10429 // Compile the sources as external two byte strings. 10430 for (int i = 0; ascii_sources[i] != NULL; i++) { 10431 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]); 10432 UC16VectorResource uc16_resource( 10433 i::Vector<const uint16_t>(two_byte_string, 10434 i::StrLength(ascii_sources[i]))); 10435 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource); 10436 v8::Script::Compile(source); 10437 i::DeleteArray(two_byte_string); 10438 } 10439 } 10440 10441 10442 class RegExpStringModificationTest { 10443 public: 10444 RegExpStringModificationTest() 10445 : block_(i::OS::CreateSemaphore(0)), 10446 morphs_(0), 10447 morphs_during_regexp_(0), 10448 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)), 10449 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {} 10450 ~RegExpStringModificationTest() { delete block_; } 10451 void RunTest() { 10452 regexp_success_ = false; 10453 morph_success_ = false; 10454 10455 // Initialize the contents of two_byte_content_ to be a uc16 representation 10456 // of "aaaaaaaaaaaaaab". 10457 for (int i = 0; i < 14; i++) { 10458 two_byte_content_[i] = 'a'; 10459 } 10460 two_byte_content_[14] = 'b'; 10461 10462 // Create the input string for the regexp - the one we are going to change 10463 // properties of. 10464 input_ = FACTORY->NewExternalStringFromAscii(&ascii_resource_); 10465 10466 // Inject the input as a global variable. 10467 i::Handle<i::String> input_name = 10468 FACTORY->NewStringFromAscii(i::Vector<const char>("input", 5)); 10469 i::Isolate::Current()->global_context()->global()->SetProperty( 10470 *input_name, 10471 *input_, 10472 NONE, 10473 i::kNonStrictMode)->ToObjectChecked(); 10474 10475 MorphThread morph_thread(i::Isolate::Current(), this); 10476 morph_thread.Start(); 10477 v8::Locker::StartPreemption(1); 10478 LongRunningRegExp(); 10479 { 10480 v8::Unlocker unlock; 10481 morph_thread.Join(); 10482 } 10483 v8::Locker::StopPreemption(); 10484 CHECK(regexp_success_); 10485 CHECK(morph_success_); 10486 } 10487 private: 10488 10489 // Number of string modifications required. 10490 static const int kRequiredModifications = 5; 10491 static const int kMaxModifications = 100; 10492 10493 class MorphThread : public i::Thread { 10494 public: 10495 explicit MorphThread(i::Isolate* isolate, 10496 RegExpStringModificationTest* test) 10497 : Thread(isolate, "MorphThread"), test_(test) {} 10498 virtual void Run() { 10499 test_->MorphString(); 10500 } 10501 private: 10502 RegExpStringModificationTest* test_; 10503 }; 10504 10505 void MorphString() { 10506 block_->Wait(); 10507 while (morphs_during_regexp_ < kRequiredModifications && 10508 morphs_ < kMaxModifications) { 10509 { 10510 v8::Locker lock; 10511 // Swap string between ascii and two-byte representation. 10512 i::String* string = *input_; 10513 MorphAString(string, &ascii_resource_, &uc16_resource_); 10514 morphs_++; 10515 } 10516 i::OS::Sleep(1); 10517 } 10518 morph_success_ = true; 10519 } 10520 10521 void LongRunningRegExp() { 10522 block_->Signal(); // Enable morphing thread on next preemption. 10523 while (morphs_during_regexp_ < kRequiredModifications && 10524 morphs_ < kMaxModifications) { 10525 int morphs_before = morphs_; 10526 { 10527 v8::HandleScope scope; 10528 // Match 15-30 "a"'s against 14 and a "b". 10529 const char* c_source = 10530 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 10531 ".exec(input) === null"; 10532 Local<String> source = String::New(c_source); 10533 Local<Script> script = Script::Compile(source); 10534 Local<Value> result = script->Run(); 10535 CHECK(result->IsTrue()); 10536 } 10537 int morphs_after = morphs_; 10538 morphs_during_regexp_ += morphs_after - morphs_before; 10539 } 10540 regexp_success_ = true; 10541 } 10542 10543 i::uc16 two_byte_content_[15]; 10544 i::Semaphore* block_; 10545 int morphs_; 10546 int morphs_during_regexp_; 10547 bool regexp_success_; 10548 bool morph_success_; 10549 i::Handle<i::String> input_; 10550 AsciiVectorResource ascii_resource_; 10551 UC16VectorResource uc16_resource_; 10552 }; 10553 10554 10555 // Test that a regular expression execution can be interrupted and 10556 // the string changed without failing. 10557 TEST(RegExpStringModification) { 10558 v8::Locker lock; 10559 v8::V8::Initialize(); 10560 v8::HandleScope scope; 10561 Local<Context> local_env; 10562 { 10563 LocalContext env; 10564 local_env = env.local(); 10565 } 10566 10567 // Local context should still be live. 10568 CHECK(!local_env.IsEmpty()); 10569 local_env->Enter(); 10570 10571 // Should complete without problems. 10572 RegExpStringModificationTest().RunTest(); 10573 10574 local_env->Exit(); 10575 } 10576 10577 10578 // Test that we can set a property on the global object even if there 10579 // is a read-only property in the prototype chain. 10580 TEST(ReadOnlyPropertyInGlobalProto) { 10581 v8::HandleScope scope; 10582 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 10583 LocalContext context(0, templ); 10584 v8::Handle<v8::Object> global = context->Global(); 10585 v8::Handle<v8::Object> global_proto = 10586 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__"))); 10587 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly); 10588 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly); 10589 // Check without 'eval' or 'with'. 10590 v8::Handle<v8::Value> res = 10591 CompileRun("function f() { x = 42; return x; }; f()"); 10592 // Check with 'eval'. 10593 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()"); 10594 CHECK_EQ(v8::Integer::New(42), res); 10595 // Check with 'with'. 10596 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()"); 10597 CHECK_EQ(v8::Integer::New(42), res); 10598 } 10599 10600 static int force_set_set_count = 0; 10601 static int force_set_get_count = 0; 10602 bool pass_on_get = false; 10603 10604 static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name, 10605 const v8::AccessorInfo& info) { 10606 force_set_get_count++; 10607 if (pass_on_get) { 10608 return v8::Handle<v8::Value>(); 10609 } else { 10610 return v8::Int32::New(3); 10611 } 10612 } 10613 10614 static void ForceSetSetter(v8::Local<v8::String> name, 10615 v8::Local<v8::Value> value, 10616 const v8::AccessorInfo& info) { 10617 force_set_set_count++; 10618 } 10619 10620 static v8::Handle<v8::Value> ForceSetInterceptSetter( 10621 v8::Local<v8::String> name, 10622 v8::Local<v8::Value> value, 10623 const v8::AccessorInfo& info) { 10624 force_set_set_count++; 10625 return v8::Undefined(); 10626 } 10627 10628 TEST(ForceSet) { 10629 force_set_get_count = 0; 10630 force_set_set_count = 0; 10631 pass_on_get = false; 10632 10633 v8::HandleScope scope; 10634 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 10635 v8::Handle<v8::String> access_property = v8::String::New("a"); 10636 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter); 10637 LocalContext context(NULL, templ); 10638 v8::Handle<v8::Object> global = context->Global(); 10639 10640 // Ordinary properties 10641 v8::Handle<v8::String> simple_property = v8::String::New("p"); 10642 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly); 10643 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 10644 // This should fail because the property is read-only 10645 global->Set(simple_property, v8::Int32::New(5)); 10646 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 10647 // This should succeed even though the property is read-only 10648 global->ForceSet(simple_property, v8::Int32::New(6)); 10649 CHECK_EQ(6, global->Get(simple_property)->Int32Value()); 10650 10651 // Accessors 10652 CHECK_EQ(0, force_set_set_count); 10653 CHECK_EQ(0, force_set_get_count); 10654 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 10655 // CHECK_EQ the property shouldn't override it, just call the setter 10656 // which in this case does nothing. 10657 global->Set(access_property, v8::Int32::New(7)); 10658 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 10659 CHECK_EQ(1, force_set_set_count); 10660 CHECK_EQ(2, force_set_get_count); 10661 // Forcing the property to be set should override the accessor without 10662 // calling it 10663 global->ForceSet(access_property, v8::Int32::New(8)); 10664 CHECK_EQ(8, global->Get(access_property)->Int32Value()); 10665 CHECK_EQ(1, force_set_set_count); 10666 CHECK_EQ(2, force_set_get_count); 10667 } 10668 10669 TEST(ForceSetWithInterceptor) { 10670 force_set_get_count = 0; 10671 force_set_set_count = 0; 10672 pass_on_get = false; 10673 10674 v8::HandleScope scope; 10675 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 10676 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter); 10677 LocalContext context(NULL, templ); 10678 v8::Handle<v8::Object> global = context->Global(); 10679 10680 v8::Handle<v8::String> some_property = v8::String::New("a"); 10681 CHECK_EQ(0, force_set_set_count); 10682 CHECK_EQ(0, force_set_get_count); 10683 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 10684 // Setting the property shouldn't override it, just call the setter 10685 // which in this case does nothing. 10686 global->Set(some_property, v8::Int32::New(7)); 10687 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 10688 CHECK_EQ(1, force_set_set_count); 10689 CHECK_EQ(2, force_set_get_count); 10690 // Getting the property when the interceptor returns an empty handle 10691 // should yield undefined, since the property isn't present on the 10692 // object itself yet. 10693 pass_on_get = true; 10694 CHECK(global->Get(some_property)->IsUndefined()); 10695 CHECK_EQ(1, force_set_set_count); 10696 CHECK_EQ(3, force_set_get_count); 10697 // Forcing the property to be set should cause the value to be 10698 // set locally without calling the interceptor. 10699 global->ForceSet(some_property, v8::Int32::New(8)); 10700 CHECK_EQ(8, global->Get(some_property)->Int32Value()); 10701 CHECK_EQ(1, force_set_set_count); 10702 CHECK_EQ(4, force_set_get_count); 10703 // Reenabling the interceptor should cause it to take precedence over 10704 // the property 10705 pass_on_get = false; 10706 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 10707 CHECK_EQ(1, force_set_set_count); 10708 CHECK_EQ(5, force_set_get_count); 10709 // The interceptor should also work for other properties 10710 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value()); 10711 CHECK_EQ(1, force_set_set_count); 10712 CHECK_EQ(6, force_set_get_count); 10713 } 10714 10715 10716 THREADED_TEST(ForceDelete) { 10717 v8::HandleScope scope; 10718 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 10719 LocalContext context(NULL, templ); 10720 v8::Handle<v8::Object> global = context->Global(); 10721 10722 // Ordinary properties 10723 v8::Handle<v8::String> simple_property = v8::String::New("p"); 10724 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete); 10725 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 10726 // This should fail because the property is dont-delete. 10727 CHECK(!global->Delete(simple_property)); 10728 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 10729 // This should succeed even though the property is dont-delete. 10730 CHECK(global->ForceDelete(simple_property)); 10731 CHECK(global->Get(simple_property)->IsUndefined()); 10732 } 10733 10734 10735 static int force_delete_interceptor_count = 0; 10736 static bool pass_on_delete = false; 10737 10738 10739 static v8::Handle<v8::Boolean> ForceDeleteDeleter( 10740 v8::Local<v8::String> name, 10741 const v8::AccessorInfo& info) { 10742 force_delete_interceptor_count++; 10743 if (pass_on_delete) { 10744 return v8::Handle<v8::Boolean>(); 10745 } else { 10746 return v8::True(); 10747 } 10748 } 10749 10750 10751 THREADED_TEST(ForceDeleteWithInterceptor) { 10752 force_delete_interceptor_count = 0; 10753 pass_on_delete = false; 10754 10755 v8::HandleScope scope; 10756 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 10757 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter); 10758 LocalContext context(NULL, templ); 10759 v8::Handle<v8::Object> global = context->Global(); 10760 10761 v8::Handle<v8::String> some_property = v8::String::New("a"); 10762 global->Set(some_property, v8::Integer::New(42), v8::DontDelete); 10763 10764 // Deleting a property should get intercepted and nothing should 10765 // happen. 10766 CHECK_EQ(0, force_delete_interceptor_count); 10767 CHECK(global->Delete(some_property)); 10768 CHECK_EQ(1, force_delete_interceptor_count); 10769 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 10770 // Deleting the property when the interceptor returns an empty 10771 // handle should not delete the property since it is DontDelete. 10772 pass_on_delete = true; 10773 CHECK(!global->Delete(some_property)); 10774 CHECK_EQ(2, force_delete_interceptor_count); 10775 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 10776 // Forcing the property to be deleted should delete the value 10777 // without calling the interceptor. 10778 CHECK(global->ForceDelete(some_property)); 10779 CHECK(global->Get(some_property)->IsUndefined()); 10780 CHECK_EQ(2, force_delete_interceptor_count); 10781 } 10782 10783 10784 // Make sure that forcing a delete invalidates any IC stubs, so we 10785 // don't read the hole value. 10786 THREADED_TEST(ForceDeleteIC) { 10787 v8::HandleScope scope; 10788 LocalContext context; 10789 // Create a DontDelete variable on the global object. 10790 CompileRun("this.__proto__ = { foo: 'horse' };" 10791 "var foo = 'fish';" 10792 "function f() { return foo.length; }"); 10793 // Initialize the IC for foo in f. 10794 CompileRun("for (var i = 0; i < 4; i++) f();"); 10795 // Make sure the value of foo is correct before the deletion. 10796 CHECK_EQ(4, CompileRun("f()")->Int32Value()); 10797 // Force the deletion of foo. 10798 CHECK(context->Global()->ForceDelete(v8_str("foo"))); 10799 // Make sure the value for foo is read from the prototype, and that 10800 // we don't get in trouble with reading the deleted cell value 10801 // sentinel. 10802 CHECK_EQ(5, CompileRun("f()")->Int32Value()); 10803 } 10804 10805 10806 v8::Persistent<Context> calling_context0; 10807 v8::Persistent<Context> calling_context1; 10808 v8::Persistent<Context> calling_context2; 10809 10810 10811 // Check that the call to the callback is initiated in 10812 // calling_context2, the directly calling context is calling_context1 10813 // and the callback itself is in calling_context0. 10814 static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) { 10815 ApiTestFuzzer::Fuzz(); 10816 CHECK(Context::GetCurrent() == calling_context0); 10817 CHECK(Context::GetCalling() == calling_context1); 10818 CHECK(Context::GetEntered() == calling_context2); 10819 return v8::Integer::New(42); 10820 } 10821 10822 10823 THREADED_TEST(GetCallingContext) { 10824 v8::HandleScope scope; 10825 10826 calling_context0 = Context::New(); 10827 calling_context1 = Context::New(); 10828 calling_context2 = Context::New(); 10829 10830 // Allow cross-domain access. 10831 Local<String> token = v8_str("<security token>"); 10832 calling_context0->SetSecurityToken(token); 10833 calling_context1->SetSecurityToken(token); 10834 calling_context2->SetSecurityToken(token); 10835 10836 // Create an object with a C++ callback in context0. 10837 calling_context0->Enter(); 10838 Local<v8::FunctionTemplate> callback_templ = 10839 v8::FunctionTemplate::New(GetCallingContextCallback); 10840 calling_context0->Global()->Set(v8_str("callback"), 10841 callback_templ->GetFunction()); 10842 calling_context0->Exit(); 10843 10844 // Expose context0 in context1 and setup a function that calls the 10845 // callback function. 10846 calling_context1->Enter(); 10847 calling_context1->Global()->Set(v8_str("context0"), 10848 calling_context0->Global()); 10849 CompileRun("function f() { context0.callback() }"); 10850 calling_context1->Exit(); 10851 10852 // Expose context1 in context2 and call the callback function in 10853 // context0 indirectly through f in context1. 10854 calling_context2->Enter(); 10855 calling_context2->Global()->Set(v8_str("context1"), 10856 calling_context1->Global()); 10857 CompileRun("context1.f()"); 10858 calling_context2->Exit(); 10859 10860 // Dispose the contexts to allow them to be garbage collected. 10861 calling_context0.Dispose(); 10862 calling_context1.Dispose(); 10863 calling_context2.Dispose(); 10864 calling_context0.Clear(); 10865 calling_context1.Clear(); 10866 calling_context2.Clear(); 10867 } 10868 10869 10870 // Check that a variable declaration with no explicit initialization 10871 // value does not shadow an existing property in the prototype chain. 10872 // 10873 // This is consistent with Firefox and Safari. 10874 // 10875 // See http://crbug.com/12548. 10876 THREADED_TEST(InitGlobalVarInProtoChain) { 10877 v8::HandleScope scope; 10878 LocalContext context; 10879 // Introduce a variable in the prototype chain. 10880 CompileRun("__proto__.x = 42"); 10881 v8::Handle<v8::Value> result = CompileRun("var x; x"); 10882 CHECK(!result->IsUndefined()); 10883 CHECK_EQ(42, result->Int32Value()); 10884 } 10885 10886 10887 // Regression test for issue 398. 10888 // If a function is added to an object, creating a constant function 10889 // field, and the result is cloned, replacing the constant function on the 10890 // original should not affect the clone. 10891 // See http://code.google.com/p/v8/issues/detail?id=398 10892 THREADED_TEST(ReplaceConstantFunction) { 10893 v8::HandleScope scope; 10894 LocalContext context; 10895 v8::Handle<v8::Object> obj = v8::Object::New(); 10896 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 10897 v8::Handle<v8::String> foo_string = v8::String::New("foo"); 10898 obj->Set(foo_string, func_templ->GetFunction()); 10899 v8::Handle<v8::Object> obj_clone = obj->Clone(); 10900 obj_clone->Set(foo_string, v8::String::New("Hello")); 10901 CHECK(!obj->Get(foo_string)->IsUndefined()); 10902 } 10903 10904 10905 // Regression test for http://crbug.com/16276. 10906 THREADED_TEST(Regress16276) { 10907 v8::HandleScope scope; 10908 LocalContext context; 10909 // Force the IC in f to be a dictionary load IC. 10910 CompileRun("function f(obj) { return obj.x; }\n" 10911 "var obj = { x: { foo: 42 }, y: 87 };\n" 10912 "var x = obj.x;\n" 10913 "delete obj.y;\n" 10914 "for (var i = 0; i < 5; i++) f(obj);"); 10915 // Detach the global object to make 'this' refer directly to the 10916 // global object (not the proxy), and make sure that the dictionary 10917 // load IC doesn't mess up loading directly from the global object. 10918 context->DetachGlobal(); 10919 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value()); 10920 } 10921 10922 10923 THREADED_TEST(PixelArray) { 10924 v8::HandleScope scope; 10925 LocalContext context; 10926 const int kElementCount = 260; 10927 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); 10928 i::Handle<i::ExternalPixelArray> pixels = 10929 i::Handle<i::ExternalPixelArray>::cast( 10930 FACTORY->NewExternalArray(kElementCount, 10931 v8::kExternalPixelArray, 10932 pixel_data)); 10933 HEAP->CollectAllGarbage(false); // Force GC to trigger verification. 10934 for (int i = 0; i < kElementCount; i++) { 10935 pixels->set(i, i % 256); 10936 } 10937 HEAP->CollectAllGarbage(false); // Force GC to trigger verification. 10938 for (int i = 0; i < kElementCount; i++) { 10939 CHECK_EQ(i % 256, pixels->get(i)); 10940 CHECK_EQ(i % 256, pixel_data[i]); 10941 } 10942 10943 v8::Handle<v8::Object> obj = v8::Object::New(); 10944 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 10945 // Set the elements to be the pixels. 10946 // jsobj->set_elements(*pixels); 10947 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount); 10948 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 10949 obj->Set(v8_str("field"), v8::Int32::New(1503)); 10950 context->Global()->Set(v8_str("pixels"), obj); 10951 v8::Handle<v8::Value> result = CompileRun("pixels.field"); 10952 CHECK_EQ(1503, result->Int32Value()); 10953 result = CompileRun("pixels[1]"); 10954 CHECK_EQ(1, result->Int32Value()); 10955 10956 result = CompileRun("var sum = 0;" 10957 "for (var i = 0; i < 8; i++) {" 10958 " sum += pixels[i] = pixels[i] = -i;" 10959 "}" 10960 "sum;"); 10961 CHECK_EQ(-28, result->Int32Value()); 10962 10963 result = CompileRun("var sum = 0;" 10964 "for (var i = 0; i < 8; i++) {" 10965 " sum += pixels[i] = pixels[i] = 0;" 10966 "}" 10967 "sum;"); 10968 CHECK_EQ(0, result->Int32Value()); 10969 10970 result = CompileRun("var sum = 0;" 10971 "for (var i = 0; i < 8; i++) {" 10972 " sum += pixels[i] = pixels[i] = 255;" 10973 "}" 10974 "sum;"); 10975 CHECK_EQ(8 * 255, result->Int32Value()); 10976 10977 result = CompileRun("var sum = 0;" 10978 "for (var i = 0; i < 8; i++) {" 10979 " sum += pixels[i] = pixels[i] = 256 + i;" 10980 "}" 10981 "sum;"); 10982 CHECK_EQ(2076, result->Int32Value()); 10983 10984 result = CompileRun("var sum = 0;" 10985 "for (var i = 0; i < 8; i++) {" 10986 " sum += pixels[i] = pixels[i] = i;" 10987 "}" 10988 "sum;"); 10989 CHECK_EQ(28, result->Int32Value()); 10990 10991 result = CompileRun("var sum = 0;" 10992 "for (var i = 0; i < 8; i++) {" 10993 " sum += pixels[i];" 10994 "}" 10995 "sum;"); 10996 CHECK_EQ(28, result->Int32Value()); 10997 10998 i::Handle<i::Smi> value(i::Smi::FromInt(2)); 10999 i::SetElement(jsobj, 1, value, i::kNonStrictMode); 11000 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 11001 *value.location() = i::Smi::FromInt(256); 11002 i::SetElement(jsobj, 1, value, i::kNonStrictMode); 11003 CHECK_EQ(255, 11004 i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 11005 *value.location() = i::Smi::FromInt(-1); 11006 i::SetElement(jsobj, 1, value, i::kNonStrictMode); 11007 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 11008 11009 result = CompileRun("for (var i = 0; i < 8; i++) {" 11010 " pixels[i] = (i * 65) - 109;" 11011 "}" 11012 "pixels[1] + pixels[6];"); 11013 CHECK_EQ(255, result->Int32Value()); 11014 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value()); 11015 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 11016 CHECK_EQ(21, 11017 i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value()); 11018 CHECK_EQ(86, 11019 i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value()); 11020 CHECK_EQ(151, 11021 i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value()); 11022 CHECK_EQ(216, 11023 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 11024 CHECK_EQ(255, 11025 i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value()); 11026 CHECK_EQ(255, 11027 i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value()); 11028 result = CompileRun("var sum = 0;" 11029 "for (var i = 0; i < 8; i++) {" 11030 " sum += pixels[i];" 11031 "}" 11032 "sum;"); 11033 CHECK_EQ(984, result->Int32Value()); 11034 11035 result = CompileRun("for (var i = 0; i < 8; i++) {" 11036 " pixels[i] = (i * 1.1);" 11037 "}" 11038 "pixels[1] + pixels[6];"); 11039 CHECK_EQ(8, result->Int32Value()); 11040 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value()); 11041 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 11042 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value()); 11043 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value()); 11044 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value()); 11045 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 11046 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value()); 11047 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value()); 11048 11049 result = CompileRun("for (var i = 0; i < 8; i++) {" 11050 " pixels[7] = undefined;" 11051 "}" 11052 "pixels[7];"); 11053 CHECK_EQ(0, result->Int32Value()); 11054 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value()); 11055 11056 result = CompileRun("for (var i = 0; i < 8; i++) {" 11057 " pixels[6] = '2.3';" 11058 "}" 11059 "pixels[6];"); 11060 CHECK_EQ(2, result->Int32Value()); 11061 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value()); 11062 11063 result = CompileRun("for (var i = 0; i < 8; i++) {" 11064 " pixels[5] = NaN;" 11065 "}" 11066 "pixels[5];"); 11067 CHECK_EQ(0, result->Int32Value()); 11068 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 11069 11070 result = CompileRun("for (var i = 0; i < 8; i++) {" 11071 " pixels[8] = Infinity;" 11072 "}" 11073 "pixels[8];"); 11074 CHECK_EQ(255, result->Int32Value()); 11075 CHECK_EQ(255, 11076 i::Smi::cast(jsobj->GetElement(8)->ToObjectChecked())->value()); 11077 11078 result = CompileRun("for (var i = 0; i < 8; i++) {" 11079 " pixels[9] = -Infinity;" 11080 "}" 11081 "pixels[9];"); 11082 CHECK_EQ(0, result->Int32Value()); 11083 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9)->ToObjectChecked())->value()); 11084 11085 result = CompileRun("pixels[3] = 33;" 11086 "delete pixels[3];" 11087 "pixels[3];"); 11088 CHECK_EQ(33, result->Int32Value()); 11089 11090 result = CompileRun("pixels[0] = 10; pixels[1] = 11;" 11091 "pixels[2] = 12; pixels[3] = 13;" 11092 "pixels.__defineGetter__('2'," 11093 "function() { return 120; });" 11094 "pixels[2];"); 11095 CHECK_EQ(12, result->Int32Value()); 11096 11097 result = CompileRun("var js_array = new Array(40);" 11098 "js_array[0] = 77;" 11099 "js_array;"); 11100 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 11101 11102 result = CompileRun("pixels[1] = 23;" 11103 "pixels.__proto__ = [];" 11104 "js_array.__proto__ = pixels;" 11105 "js_array.concat(pixels);"); 11106 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 11107 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 11108 11109 result = CompileRun("pixels[1] = 23;"); 11110 CHECK_EQ(23, result->Int32Value()); 11111 11112 // Test for index greater than 255. Regression test for: 11113 // http://code.google.com/p/chromium/issues/detail?id=26337. 11114 result = CompileRun("pixels[256] = 255;"); 11115 CHECK_EQ(255, result->Int32Value()); 11116 result = CompileRun("var i = 0;" 11117 "for (var j = 0; j < 8; j++) { i = pixels[256]; }" 11118 "i"); 11119 CHECK_EQ(255, result->Int32Value()); 11120 11121 // Make sure that pixel array ICs recognize when a non-pixel array 11122 // is passed to it. 11123 result = CompileRun("function pa_load(p) {" 11124 " var sum = 0;" 11125 " for (var j = 0; j < 256; j++) { sum += p[j]; }" 11126 " return sum;" 11127 "}" 11128 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" 11129 "for (var i = 0; i < 10; ++i) { pa_load(pixels); }" 11130 "just_ints = new Object();" 11131 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" 11132 "for (var i = 0; i < 10; ++i) {" 11133 " result = pa_load(just_ints);" 11134 "}" 11135 "result"); 11136 CHECK_EQ(32640, result->Int32Value()); 11137 11138 // Make sure that pixel array ICs recognize out-of-bound accesses. 11139 result = CompileRun("function pa_load(p, start) {" 11140 " var sum = 0;" 11141 " for (var j = start; j < 256; j++) { sum += p[j]; }" 11142 " return sum;" 11143 "}" 11144 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" 11145 "for (var i = 0; i < 10; ++i) { pa_load(pixels,0); }" 11146 "for (var i = 0; i < 10; ++i) {" 11147 " result = pa_load(pixels,-10);" 11148 "}" 11149 "result"); 11150 CHECK_EQ(0, result->Int32Value()); 11151 11152 // Make sure that generic ICs properly handles a pixel array. 11153 result = CompileRun("function pa_load(p) {" 11154 " var sum = 0;" 11155 " for (var j = 0; j < 256; j++) { sum += p[j]; }" 11156 " return sum;" 11157 "}" 11158 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" 11159 "just_ints = new Object();" 11160 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" 11161 "for (var i = 0; i < 10; ++i) { pa_load(just_ints); }" 11162 "for (var i = 0; i < 10; ++i) {" 11163 " result = pa_load(pixels);" 11164 "}" 11165 "result"); 11166 CHECK_EQ(32640, result->Int32Value()); 11167 11168 // Make sure that generic load ICs recognize out-of-bound accesses in 11169 // pixel arrays. 11170 result = CompileRun("function pa_load(p, start) {" 11171 " var sum = 0;" 11172 " for (var j = start; j < 256; j++) { sum += p[j]; }" 11173 " return sum;" 11174 "}" 11175 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" 11176 "just_ints = new Object();" 11177 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" 11178 "for (var i = 0; i < 10; ++i) { pa_load(just_ints,0); }" 11179 "for (var i = 0; i < 10; ++i) { pa_load(pixels,0); }" 11180 "for (var i = 0; i < 10; ++i) {" 11181 " result = pa_load(pixels,-10);" 11182 "}" 11183 "result"); 11184 CHECK_EQ(0, result->Int32Value()); 11185 11186 // Make sure that generic ICs properly handles other types than pixel 11187 // arrays (that the inlined fast pixel array test leaves the right information 11188 // in the right registers). 11189 result = CompileRun("function pa_load(p) {" 11190 " var sum = 0;" 11191 " for (var j = 0; j < 256; j++) { sum += p[j]; }" 11192 " return sum;" 11193 "}" 11194 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" 11195 "just_ints = new Object();" 11196 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" 11197 "for (var i = 0; i < 10; ++i) { pa_load(just_ints); }" 11198 "for (var i = 0; i < 10; ++i) { pa_load(pixels); }" 11199 "sparse_array = new Object();" 11200 "for (var i = 0; i < 256; ++i) { sparse_array[i] = i; }" 11201 "sparse_array[1000000] = 3;" 11202 "for (var i = 0; i < 10; ++i) {" 11203 " result = pa_load(sparse_array);" 11204 "}" 11205 "result"); 11206 CHECK_EQ(32640, result->Int32Value()); 11207 11208 // Make sure that pixel array store ICs clamp values correctly. 11209 result = CompileRun("function pa_store(p) {" 11210 " for (var j = 0; j < 256; j++) { p[j] = j * 2; }" 11211 "}" 11212 "pa_store(pixels);" 11213 "var sum = 0;" 11214 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }" 11215 "sum"); 11216 CHECK_EQ(48896, result->Int32Value()); 11217 11218 // Make sure that pixel array stores correctly handle accesses outside 11219 // of the pixel array.. 11220 result = CompileRun("function pa_store(p,start) {" 11221 " for (var j = 0; j < 256; j++) {" 11222 " p[j+start] = j * 2;" 11223 " }" 11224 "}" 11225 "pa_store(pixels,0);" 11226 "pa_store(pixels,-128);" 11227 "var sum = 0;" 11228 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }" 11229 "sum"); 11230 CHECK_EQ(65280, result->Int32Value()); 11231 11232 // Make sure that the generic store stub correctly handle accesses outside 11233 // of the pixel array.. 11234 result = CompileRun("function pa_store(p,start) {" 11235 " for (var j = 0; j < 256; j++) {" 11236 " p[j+start] = j * 2;" 11237 " }" 11238 "}" 11239 "pa_store(pixels,0);" 11240 "just_ints = new Object();" 11241 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" 11242 "pa_store(just_ints, 0);" 11243 "pa_store(pixels,-128);" 11244 "var sum = 0;" 11245 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }" 11246 "sum"); 11247 CHECK_EQ(65280, result->Int32Value()); 11248 11249 // Make sure that the generic keyed store stub clamps pixel array values 11250 // correctly. 11251 result = CompileRun("function pa_store(p) {" 11252 " for (var j = 0; j < 256; j++) { p[j] = j * 2; }" 11253 "}" 11254 "pa_store(pixels);" 11255 "just_ints = new Object();" 11256 "pa_store(just_ints);" 11257 "pa_store(pixels);" 11258 "var sum = 0;" 11259 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }" 11260 "sum"); 11261 CHECK_EQ(48896, result->Int32Value()); 11262 11263 // Make sure that pixel array loads are optimized by crankshaft. 11264 result = CompileRun("function pa_load(p) {" 11265 " var sum = 0;" 11266 " for (var i=0; i<256; ++i) {" 11267 " sum += p[i];" 11268 " }" 11269 " return sum; " 11270 "}" 11271 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" 11272 "for (var i = 0; i < 5000; ++i) {" 11273 " result = pa_load(pixels);" 11274 "}" 11275 "result"); 11276 CHECK_EQ(32640, result->Int32Value()); 11277 11278 // Make sure that pixel array stores are optimized by crankshaft. 11279 result = CompileRun("function pa_init(p) {" 11280 "for (var i = 0; i < 256; ++i) { p[i] = i; }" 11281 "}" 11282 "function pa_load(p) {" 11283 " var sum = 0;" 11284 " for (var i=0; i<256; ++i) {" 11285 " sum += p[i];" 11286 " }" 11287 " return sum; " 11288 "}" 11289 "for (var i = 0; i < 5000; ++i) {" 11290 " pa_init(pixels);" 11291 "}" 11292 "result = pa_load(pixels);" 11293 "result"); 11294 CHECK_EQ(32640, result->Int32Value()); 11295 11296 free(pixel_data); 11297 } 11298 11299 11300 THREADED_TEST(PixelArrayInfo) { 11301 v8::HandleScope scope; 11302 LocalContext context; 11303 for (int size = 0; size < 100; size += 10) { 11304 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size)); 11305 v8::Handle<v8::Object> obj = v8::Object::New(); 11306 obj->SetIndexedPropertiesToPixelData(pixel_data, size); 11307 CHECK(obj->HasIndexedPropertiesInPixelData()); 11308 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData()); 11309 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength()); 11310 free(pixel_data); 11311 } 11312 } 11313 11314 11315 static v8::Handle<Value> NotHandledIndexedPropertyGetter( 11316 uint32_t index, 11317 const AccessorInfo& info) { 11318 ApiTestFuzzer::Fuzz(); 11319 return v8::Handle<Value>(); 11320 } 11321 11322 11323 static v8::Handle<Value> NotHandledIndexedPropertySetter( 11324 uint32_t index, 11325 Local<Value> value, 11326 const AccessorInfo& info) { 11327 ApiTestFuzzer::Fuzz(); 11328 return v8::Handle<Value>(); 11329 } 11330 11331 11332 THREADED_TEST(PixelArrayWithInterceptor) { 11333 v8::HandleScope scope; 11334 LocalContext context; 11335 const int kElementCount = 260; 11336 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); 11337 i::Handle<i::ExternalPixelArray> pixels = 11338 i::Handle<i::ExternalPixelArray>::cast( 11339 FACTORY->NewExternalArray(kElementCount, 11340 v8::kExternalPixelArray, 11341 pixel_data)); 11342 for (int i = 0; i < kElementCount; i++) { 11343 pixels->set(i, i % 256); 11344 } 11345 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 11346 templ->SetIndexedPropertyHandler(NotHandledIndexedPropertyGetter, 11347 NotHandledIndexedPropertySetter); 11348 v8::Handle<v8::Object> obj = templ->NewInstance(); 11349 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount); 11350 context->Global()->Set(v8_str("pixels"), obj); 11351 v8::Handle<v8::Value> result = CompileRun("pixels[1]"); 11352 CHECK_EQ(1, result->Int32Value()); 11353 result = CompileRun("var sum = 0;" 11354 "for (var i = 0; i < 8; i++) {" 11355 " sum += pixels[i] = pixels[i] = -i;" 11356 "}" 11357 "sum;"); 11358 CHECK_EQ(-28, result->Int32Value()); 11359 result = CompileRun("pixels.hasOwnProperty('1')"); 11360 CHECK(result->BooleanValue()); 11361 free(pixel_data); 11362 } 11363 11364 11365 static int ExternalArrayElementSize(v8::ExternalArrayType array_type) { 11366 switch (array_type) { 11367 case v8::kExternalByteArray: 11368 case v8::kExternalUnsignedByteArray: 11369 case v8::kExternalPixelArray: 11370 return 1; 11371 break; 11372 case v8::kExternalShortArray: 11373 case v8::kExternalUnsignedShortArray: 11374 return 2; 11375 break; 11376 case v8::kExternalIntArray: 11377 case v8::kExternalUnsignedIntArray: 11378 case v8::kExternalFloatArray: 11379 return 4; 11380 break; 11381 default: 11382 UNREACHABLE(); 11383 return -1; 11384 } 11385 UNREACHABLE(); 11386 return -1; 11387 } 11388 11389 11390 template <class ExternalArrayClass, class ElementType> 11391 static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, 11392 int64_t low, 11393 int64_t high) { 11394 v8::HandleScope scope; 11395 LocalContext context; 11396 const int kElementCount = 40; 11397 int element_size = ExternalArrayElementSize(array_type); 11398 ElementType* array_data = 11399 static_cast<ElementType*>(malloc(kElementCount * element_size)); 11400 i::Handle<ExternalArrayClass> array = 11401 i::Handle<ExternalArrayClass>::cast( 11402 FACTORY->NewExternalArray(kElementCount, array_type, array_data)); 11403 HEAP->CollectAllGarbage(false); // Force GC to trigger verification. 11404 for (int i = 0; i < kElementCount; i++) { 11405 array->set(i, static_cast<ElementType>(i)); 11406 } 11407 HEAP->CollectAllGarbage(false); // Force GC to trigger verification. 11408 for (int i = 0; i < kElementCount; i++) { 11409 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i))); 11410 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i])); 11411 } 11412 11413 v8::Handle<v8::Object> obj = v8::Object::New(); 11414 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 11415 // Set the elements to be the external array. 11416 obj->SetIndexedPropertiesToExternalArrayData(array_data, 11417 array_type, 11418 kElementCount); 11419 CHECK_EQ( 11420 1, static_cast<int>(jsobj->GetElement(1)->ToObjectChecked()->Number())); 11421 obj->Set(v8_str("field"), v8::Int32::New(1503)); 11422 context->Global()->Set(v8_str("ext_array"), obj); 11423 v8::Handle<v8::Value> result = CompileRun("ext_array.field"); 11424 CHECK_EQ(1503, result->Int32Value()); 11425 result = CompileRun("ext_array[1]"); 11426 CHECK_EQ(1, result->Int32Value()); 11427 11428 // Check pass through of assigned smis 11429 result = CompileRun("var sum = 0;" 11430 "for (var i = 0; i < 8; i++) {" 11431 " sum += ext_array[i] = ext_array[i] = -i;" 11432 "}" 11433 "sum;"); 11434 CHECK_EQ(-28, result->Int32Value()); 11435 11436 // Check assigned smis 11437 result = CompileRun("for (var i = 0; i < 8; i++) {" 11438 " ext_array[i] = i;" 11439 "}" 11440 "var sum = 0;" 11441 "for (var i = 0; i < 8; i++) {" 11442 " sum += ext_array[i];" 11443 "}" 11444 "sum;"); 11445 CHECK_EQ(28, result->Int32Value()); 11446 11447 // Check assigned smis in reverse order 11448 result = CompileRun("for (var i = 8; --i >= 0; ) {" 11449 " ext_array[i] = i;" 11450 "}" 11451 "var sum = 0;" 11452 "for (var i = 0; i < 8; i++) {" 11453 " sum += ext_array[i];" 11454 "}" 11455 "sum;"); 11456 CHECK_EQ(28, result->Int32Value()); 11457 11458 // Check pass through of assigned HeapNumbers 11459 result = CompileRun("var sum = 0;" 11460 "for (var i = 0; i < 16; i+=2) {" 11461 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);" 11462 "}" 11463 "sum;"); 11464 CHECK_EQ(-28, result->Int32Value()); 11465 11466 // Check assigned HeapNumbers 11467 result = CompileRun("for (var i = 0; i < 16; i+=2) {" 11468 " ext_array[i] = (i * 0.5);" 11469 "}" 11470 "var sum = 0;" 11471 "for (var i = 0; i < 16; i+=2) {" 11472 " sum += ext_array[i];" 11473 "}" 11474 "sum;"); 11475 CHECK_EQ(28, result->Int32Value()); 11476 11477 // Check assigned HeapNumbers in reverse order 11478 result = CompileRun("for (var i = 14; i >= 0; i-=2) {" 11479 " ext_array[i] = (i * 0.5);" 11480 "}" 11481 "var sum = 0;" 11482 "for (var i = 0; i < 16; i+=2) {" 11483 " sum += ext_array[i];" 11484 "}" 11485 "sum;"); 11486 CHECK_EQ(28, result->Int32Value()); 11487 11488 i::ScopedVector<char> test_buf(1024); 11489 11490 // Check legal boundary conditions. 11491 // The repeated loads and stores ensure the ICs are exercised. 11492 const char* boundary_program = 11493 "var res = 0;" 11494 "for (var i = 0; i < 16; i++) {" 11495 " ext_array[i] = %lld;" 11496 " if (i > 8) {" 11497 " res = ext_array[i];" 11498 " }" 11499 "}" 11500 "res;"; 11501 i::OS::SNPrintF(test_buf, 11502 boundary_program, 11503 low); 11504 result = CompileRun(test_buf.start()); 11505 CHECK_EQ(low, result->IntegerValue()); 11506 11507 i::OS::SNPrintF(test_buf, 11508 boundary_program, 11509 high); 11510 result = CompileRun(test_buf.start()); 11511 CHECK_EQ(high, result->IntegerValue()); 11512 11513 // Check misprediction of type in IC. 11514 result = CompileRun("var tmp_array = ext_array;" 11515 "var sum = 0;" 11516 "for (var i = 0; i < 8; i++) {" 11517 " tmp_array[i] = i;" 11518 " sum += tmp_array[i];" 11519 " if (i == 4) {" 11520 " tmp_array = {};" 11521 " }" 11522 "}" 11523 "sum;"); 11524 HEAP->CollectAllGarbage(false); // Force GC to trigger verification. 11525 CHECK_EQ(28, result->Int32Value()); 11526 11527 // Make sure out-of-range loads do not throw. 11528 i::OS::SNPrintF(test_buf, 11529 "var caught_exception = false;" 11530 "try {" 11531 " ext_array[%d];" 11532 "} catch (e) {" 11533 " caught_exception = true;" 11534 "}" 11535 "caught_exception;", 11536 kElementCount); 11537 result = CompileRun(test_buf.start()); 11538 CHECK_EQ(false, result->BooleanValue()); 11539 11540 // Make sure out-of-range stores do not throw. 11541 i::OS::SNPrintF(test_buf, 11542 "var caught_exception = false;" 11543 "try {" 11544 " ext_array[%d] = 1;" 11545 "} catch (e) {" 11546 " caught_exception = true;" 11547 "}" 11548 "caught_exception;", 11549 kElementCount); 11550 result = CompileRun(test_buf.start()); 11551 CHECK_EQ(false, result->BooleanValue()); 11552 11553 // Check other boundary conditions, values and operations. 11554 result = CompileRun("for (var i = 0; i < 8; i++) {" 11555 " ext_array[7] = undefined;" 11556 "}" 11557 "ext_array[7];"); 11558 CHECK_EQ(0, result->Int32Value()); 11559 CHECK_EQ( 11560 0, static_cast<int>(jsobj->GetElement(7)->ToObjectChecked()->Number())); 11561 11562 result = CompileRun("for (var i = 0; i < 8; i++) {" 11563 " ext_array[6] = '2.3';" 11564 "}" 11565 "ext_array[6];"); 11566 CHECK_EQ(2, result->Int32Value()); 11567 CHECK_EQ( 11568 2, static_cast<int>(jsobj->GetElement(6)->ToObjectChecked()->Number())); 11569 11570 if (array_type != v8::kExternalFloatArray) { 11571 // Though the specification doesn't state it, be explicit about 11572 // converting NaNs and +/-Infinity to zero. 11573 result = CompileRun("for (var i = 0; i < 8; i++) {" 11574 " ext_array[i] = 5;" 11575 "}" 11576 "for (var i = 0; i < 8; i++) {" 11577 " ext_array[i] = NaN;" 11578 "}" 11579 "ext_array[5];"); 11580 CHECK_EQ(0, result->Int32Value()); 11581 CHECK_EQ(0, 11582 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 11583 11584 result = CompileRun("for (var i = 0; i < 8; i++) {" 11585 " ext_array[i] = 5;" 11586 "}" 11587 "for (var i = 0; i < 8; i++) {" 11588 " ext_array[i] = Infinity;" 11589 "}" 11590 "ext_array[5];"); 11591 int expected_value = 11592 (array_type == v8::kExternalPixelArray) ? 255 : 0; 11593 CHECK_EQ(expected_value, result->Int32Value()); 11594 CHECK_EQ(expected_value, 11595 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 11596 11597 result = CompileRun("for (var i = 0; i < 8; i++) {" 11598 " ext_array[i] = 5;" 11599 "}" 11600 "for (var i = 0; i < 8; i++) {" 11601 " ext_array[i] = -Infinity;" 11602 "}" 11603 "ext_array[5];"); 11604 CHECK_EQ(0, result->Int32Value()); 11605 CHECK_EQ(0, 11606 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 11607 11608 // Check truncation behavior of integral arrays. 11609 const char* unsigned_data = 11610 "var source_data = [0.6, 10.6];" 11611 "var expected_results = [0, 10];"; 11612 const char* signed_data = 11613 "var source_data = [0.6, 10.6, -0.6, -10.6];" 11614 "var expected_results = [0, 10, 0, -10];"; 11615 const char* pixel_data = 11616 "var source_data = [0.6, 10.6];" 11617 "var expected_results = [1, 11];"; 11618 bool is_unsigned = 11619 (array_type == v8::kExternalUnsignedByteArray || 11620 array_type == v8::kExternalUnsignedShortArray || 11621 array_type == v8::kExternalUnsignedIntArray); 11622 bool is_pixel_data = array_type == v8::kExternalPixelArray; 11623 11624 i::OS::SNPrintF(test_buf, 11625 "%s" 11626 "var all_passed = true;" 11627 "for (var i = 0; i < source_data.length; i++) {" 11628 " for (var j = 0; j < 8; j++) {" 11629 " ext_array[j] = source_data[i];" 11630 " }" 11631 " all_passed = all_passed &&" 11632 " (ext_array[5] == expected_results[i]);" 11633 "}" 11634 "all_passed;", 11635 (is_unsigned ? 11636 unsigned_data : 11637 (is_pixel_data ? pixel_data : signed_data))); 11638 result = CompileRun(test_buf.start()); 11639 CHECK_EQ(true, result->BooleanValue()); 11640 } 11641 11642 // Test crankshaft external array loads 11643 for (int i = 0; i < kElementCount; i++) { 11644 array->set(i, static_cast<ElementType>(i)); 11645 } 11646 result = CompileRun("function ee_load_test_func(sum) {" 11647 " for (var i = 0; i < 40; ++i)" 11648 " sum += ext_array[i];" 11649 " return sum;" 11650 "}" 11651 "sum=0;" 11652 "for (var i=0;i<10000;++i) {" 11653 " sum=ee_load_test_func(sum);" 11654 "}" 11655 "sum;"); 11656 CHECK_EQ(7800000, result->Int32Value()); 11657 11658 // Test crankshaft external array stores 11659 result = CompileRun("function ee_store_test_func(sum) {" 11660 " for (var i = 0; i < 40; ++i)" 11661 " sum += ext_array[i] = i;" 11662 " return sum;" 11663 "}" 11664 "sum=0;" 11665 "for (var i=0;i<10000;++i) {" 11666 " sum=ee_store_test_func(sum);" 11667 "}" 11668 "sum;"); 11669 CHECK_EQ(7800000, result->Int32Value()); 11670 11671 for (int i = 0; i < kElementCount; i++) { 11672 array->set(i, static_cast<ElementType>(i)); 11673 } 11674 // Test complex assignments 11675 result = CompileRun("function ee_op_test_complex_func(sum) {" 11676 " for (var i = 0; i < 40; ++i) {" 11677 " sum += (ext_array[i] += 1);" 11678 " sum += (ext_array[i] -= 1);" 11679 " } " 11680 " return sum;" 11681 "}" 11682 "sum=0;" 11683 "for (var i=0;i<10000;++i) {" 11684 " sum=ee_op_test_complex_func(sum);" 11685 "}" 11686 "sum;"); 11687 CHECK_EQ(16000000, result->Int32Value()); 11688 11689 // Test count operations 11690 result = CompileRun("function ee_op_test_count_func(sum) {" 11691 " for (var i = 0; i < 40; ++i) {" 11692 " sum += (++ext_array[i]);" 11693 " sum += (--ext_array[i]);" 11694 " } " 11695 " return sum;" 11696 "}" 11697 "sum=0;" 11698 "for (var i=0;i<10000;++i) {" 11699 " sum=ee_op_test_count_func(sum);" 11700 "}" 11701 "sum;"); 11702 CHECK_EQ(16000000, result->Int32Value()); 11703 11704 result = CompileRun("ext_array[3] = 33;" 11705 "delete ext_array[3];" 11706 "ext_array[3];"); 11707 CHECK_EQ(33, result->Int32Value()); 11708 11709 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;" 11710 "ext_array[2] = 12; ext_array[3] = 13;" 11711 "ext_array.__defineGetter__('2'," 11712 "function() { return 120; });" 11713 "ext_array[2];"); 11714 CHECK_EQ(12, result->Int32Value()); 11715 11716 result = CompileRun("var js_array = new Array(40);" 11717 "js_array[0] = 77;" 11718 "js_array;"); 11719 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 11720 11721 result = CompileRun("ext_array[1] = 23;" 11722 "ext_array.__proto__ = [];" 11723 "js_array.__proto__ = ext_array;" 11724 "js_array.concat(ext_array);"); 11725 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 11726 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 11727 11728 result = CompileRun("ext_array[1] = 23;"); 11729 CHECK_EQ(23, result->Int32Value()); 11730 11731 // Test more complex manipulations which cause eax to contain values 11732 // that won't be completely overwritten by loads from the arrays. 11733 // This catches bugs in the instructions used for the KeyedLoadIC 11734 // for byte and word types. 11735 { 11736 const int kXSize = 300; 11737 const int kYSize = 300; 11738 const int kLargeElementCount = kXSize * kYSize * 4; 11739 ElementType* large_array_data = 11740 static_cast<ElementType*>(malloc(kLargeElementCount * element_size)); 11741 i::Handle<ExternalArrayClass> large_array = 11742 i::Handle<ExternalArrayClass>::cast( 11743 FACTORY->NewExternalArray(kLargeElementCount, 11744 array_type, 11745 array_data)); 11746 v8::Handle<v8::Object> large_obj = v8::Object::New(); 11747 // Set the elements to be the external array. 11748 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data, 11749 array_type, 11750 kLargeElementCount); 11751 context->Global()->Set(v8_str("large_array"), large_obj); 11752 // Initialize contents of a few rows. 11753 for (int x = 0; x < 300; x++) { 11754 int row = 0; 11755 int offset = row * 300 * 4; 11756 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 11757 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 11758 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 11759 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 11760 row = 150; 11761 offset = row * 300 * 4; 11762 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 11763 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 11764 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 11765 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 11766 row = 298; 11767 offset = row * 300 * 4; 11768 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 11769 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 11770 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 11771 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 11772 } 11773 // The goal of the code below is to make "offset" large enough 11774 // that the computation of the index (which goes into eax) has 11775 // high bits set which will not be overwritten by a byte or short 11776 // load. 11777 result = CompileRun("var failed = false;" 11778 "var offset = 0;" 11779 "for (var i = 0; i < 300; i++) {" 11780 " if (large_array[4 * i] != 127 ||" 11781 " large_array[4 * i + 1] != 0 ||" 11782 " large_array[4 * i + 2] != 0 ||" 11783 " large_array[4 * i + 3] != 127) {" 11784 " failed = true;" 11785 " }" 11786 "}" 11787 "offset = 150 * 300 * 4;" 11788 "for (var i = 0; i < 300; i++) {" 11789 " if (large_array[offset + 4 * i] != 127 ||" 11790 " large_array[offset + 4 * i + 1] != 0 ||" 11791 " large_array[offset + 4 * i + 2] != 0 ||" 11792 " large_array[offset + 4 * i + 3] != 127) {" 11793 " failed = true;" 11794 " }" 11795 "}" 11796 "offset = 298 * 300 * 4;" 11797 "for (var i = 0; i < 300; i++) {" 11798 " if (large_array[offset + 4 * i] != 127 ||" 11799 " large_array[offset + 4 * i + 1] != 0 ||" 11800 " large_array[offset + 4 * i + 2] != 0 ||" 11801 " large_array[offset + 4 * i + 3] != 127) {" 11802 " failed = true;" 11803 " }" 11804 "}" 11805 "!failed;"); 11806 CHECK_EQ(true, result->BooleanValue()); 11807 free(large_array_data); 11808 } 11809 11810 // The "" property descriptor is overloaded to store information about 11811 // the external array. Ensure that setting and accessing the "" property 11812 // works (it should overwrite the information cached about the external 11813 // array in the DescriptorArray) in various situations. 11814 result = CompileRun("ext_array[''] = 23; ext_array['']"); 11815 CHECK_EQ(23, result->Int32Value()); 11816 11817 // Property "" set after the external array is associated with the object. 11818 { 11819 v8::Handle<v8::Object> obj2 = v8::Object::New(); 11820 obj2->Set(v8_str("ee_test_field"), v8::Int32::New(256)); 11821 obj2->Set(v8_str(""), v8::Int32::New(1503)); 11822 // Set the elements to be the external array. 11823 obj2->SetIndexedPropertiesToExternalArrayData(array_data, 11824 array_type, 11825 kElementCount); 11826 context->Global()->Set(v8_str("ext_array"), obj2); 11827 result = CompileRun("ext_array['']"); 11828 CHECK_EQ(1503, result->Int32Value()); 11829 } 11830 11831 // Property "" set after the external array is associated with the object. 11832 { 11833 v8::Handle<v8::Object> obj2 = v8::Object::New(); 11834 obj2->Set(v8_str("ee_test_field_2"), v8::Int32::New(256)); 11835 // Set the elements to be the external array. 11836 obj2->SetIndexedPropertiesToExternalArrayData(array_data, 11837 array_type, 11838 kElementCount); 11839 obj2->Set(v8_str(""), v8::Int32::New(1503)); 11840 context->Global()->Set(v8_str("ext_array"), obj2); 11841 result = CompileRun("ext_array['']"); 11842 CHECK_EQ(1503, result->Int32Value()); 11843 } 11844 11845 // Should reuse the map from previous test. 11846 { 11847 v8::Handle<v8::Object> obj2 = v8::Object::New(); 11848 obj2->Set(v8_str("ee_test_field_2"), v8::Int32::New(256)); 11849 // Set the elements to be the external array. Should re-use the map 11850 // from previous test. 11851 obj2->SetIndexedPropertiesToExternalArrayData(array_data, 11852 array_type, 11853 kElementCount); 11854 context->Global()->Set(v8_str("ext_array"), obj2); 11855 result = CompileRun("ext_array['']"); 11856 } 11857 11858 // Property "" is a constant function that shouldn't not be interfered with 11859 // when an external array is set. 11860 { 11861 v8::Handle<v8::Object> obj2 = v8::Object::New(); 11862 // Start 11863 obj2->Set(v8_str("ee_test_field3"), v8::Int32::New(256)); 11864 11865 // Add a constant function to an object. 11866 context->Global()->Set(v8_str("ext_array"), obj2); 11867 result = CompileRun("ext_array[''] = function() {return 1503;};" 11868 "ext_array['']();"); 11869 11870 // Add an external array transition to the same map that 11871 // has the constant transition. 11872 v8::Handle<v8::Object> obj3 = v8::Object::New(); 11873 obj3->Set(v8_str("ee_test_field3"), v8::Int32::New(256)); 11874 obj3->SetIndexedPropertiesToExternalArrayData(array_data, 11875 array_type, 11876 kElementCount); 11877 context->Global()->Set(v8_str("ext_array"), obj3); 11878 } 11879 11880 // If a external array transition is in the map, it should get clobbered 11881 // by a constant function. 11882 { 11883 // Add an external array transition. 11884 v8::Handle<v8::Object> obj3 = v8::Object::New(); 11885 obj3->Set(v8_str("ee_test_field4"), v8::Int32::New(256)); 11886 obj3->SetIndexedPropertiesToExternalArrayData(array_data, 11887 array_type, 11888 kElementCount); 11889 11890 // Add a constant function to the same map that just got an external array 11891 // transition. 11892 v8::Handle<v8::Object> obj2 = v8::Object::New(); 11893 obj2->Set(v8_str("ee_test_field4"), v8::Int32::New(256)); 11894 context->Global()->Set(v8_str("ext_array"), obj2); 11895 result = CompileRun("ext_array[''] = function() {return 1503;};" 11896 "ext_array['']();"); 11897 } 11898 11899 free(array_data); 11900 } 11901 11902 11903 THREADED_TEST(ExternalByteArray) { 11904 ExternalArrayTestHelper<i::ExternalByteArray, int8_t>( 11905 v8::kExternalByteArray, 11906 -128, 11907 127); 11908 } 11909 11910 11911 THREADED_TEST(ExternalUnsignedByteArray) { 11912 ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>( 11913 v8::kExternalUnsignedByteArray, 11914 0, 11915 255); 11916 } 11917 11918 11919 THREADED_TEST(ExternalPixelArray) { 11920 ExternalArrayTestHelper<i::ExternalPixelArray, uint8_t>( 11921 v8::kExternalPixelArray, 11922 0, 11923 255); 11924 } 11925 11926 11927 THREADED_TEST(ExternalShortArray) { 11928 ExternalArrayTestHelper<i::ExternalShortArray, int16_t>( 11929 v8::kExternalShortArray, 11930 -32768, 11931 32767); 11932 } 11933 11934 11935 THREADED_TEST(ExternalUnsignedShortArray) { 11936 ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>( 11937 v8::kExternalUnsignedShortArray, 11938 0, 11939 65535); 11940 } 11941 11942 11943 THREADED_TEST(ExternalIntArray) { 11944 ExternalArrayTestHelper<i::ExternalIntArray, int32_t>( 11945 v8::kExternalIntArray, 11946 INT_MIN, // -2147483648 11947 INT_MAX); // 2147483647 11948 } 11949 11950 11951 THREADED_TEST(ExternalUnsignedIntArray) { 11952 ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>( 11953 v8::kExternalUnsignedIntArray, 11954 0, 11955 UINT_MAX); // 4294967295 11956 } 11957 11958 11959 THREADED_TEST(ExternalFloatArray) { 11960 ExternalArrayTestHelper<i::ExternalFloatArray, float>( 11961 v8::kExternalFloatArray, 11962 -500, 11963 500); 11964 } 11965 11966 11967 THREADED_TEST(ExternalArrays) { 11968 TestExternalByteArray(); 11969 TestExternalUnsignedByteArray(); 11970 TestExternalShortArray(); 11971 TestExternalUnsignedShortArray(); 11972 TestExternalIntArray(); 11973 TestExternalUnsignedIntArray(); 11974 TestExternalFloatArray(); 11975 } 11976 11977 11978 void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) { 11979 v8::HandleScope scope; 11980 LocalContext context; 11981 for (int size = 0; size < 100; size += 10) { 11982 int element_size = ExternalArrayElementSize(array_type); 11983 void* external_data = malloc(size * element_size); 11984 v8::Handle<v8::Object> obj = v8::Object::New(); 11985 obj->SetIndexedPropertiesToExternalArrayData( 11986 external_data, array_type, size); 11987 CHECK(obj->HasIndexedPropertiesInExternalArrayData()); 11988 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData()); 11989 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType()); 11990 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength()); 11991 free(external_data); 11992 } 11993 } 11994 11995 11996 THREADED_TEST(ExternalArrayInfo) { 11997 ExternalArrayInfoTestHelper(v8::kExternalByteArray); 11998 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray); 11999 ExternalArrayInfoTestHelper(v8::kExternalShortArray); 12000 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray); 12001 ExternalArrayInfoTestHelper(v8::kExternalIntArray); 12002 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray); 12003 ExternalArrayInfoTestHelper(v8::kExternalFloatArray); 12004 ExternalArrayInfoTestHelper(v8::kExternalPixelArray); 12005 } 12006 12007 12008 THREADED_TEST(ScriptContextDependence) { 12009 v8::HandleScope scope; 12010 LocalContext c1; 12011 const char *source = "foo"; 12012 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source)); 12013 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source)); 12014 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100)); 12015 CHECK_EQ(dep->Run()->Int32Value(), 100); 12016 CHECK_EQ(indep->Run()->Int32Value(), 100); 12017 LocalContext c2; 12018 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101)); 12019 CHECK_EQ(dep->Run()->Int32Value(), 100); 12020 CHECK_EQ(indep->Run()->Int32Value(), 101); 12021 } 12022 12023 12024 THREADED_TEST(StackTrace) { 12025 v8::HandleScope scope; 12026 LocalContext context; 12027 v8::TryCatch try_catch; 12028 const char *source = "function foo() { FAIL.FAIL; }; foo();"; 12029 v8::Handle<v8::String> src = v8::String::New(source); 12030 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test"); 12031 v8::Script::New(src, origin)->Run(); 12032 CHECK(try_catch.HasCaught()); 12033 v8::String::Utf8Value stack(try_catch.StackTrace()); 12034 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL); 12035 } 12036 12037 12038 // Checks that a StackFrame has certain expected values. 12039 void checkStackFrame(const char* expected_script_name, 12040 const char* expected_func_name, int expected_line_number, 12041 int expected_column, bool is_eval, bool is_constructor, 12042 v8::Handle<v8::StackFrame> frame) { 12043 v8::HandleScope scope; 12044 v8::String::Utf8Value func_name(frame->GetFunctionName()); 12045 v8::String::Utf8Value script_name(frame->GetScriptName()); 12046 if (*script_name == NULL) { 12047 // The situation where there is no associated script, like for evals. 12048 CHECK(expected_script_name == NULL); 12049 } else { 12050 CHECK(strstr(*script_name, expected_script_name) != NULL); 12051 } 12052 CHECK(strstr(*func_name, expected_func_name) != NULL); 12053 CHECK_EQ(expected_line_number, frame->GetLineNumber()); 12054 CHECK_EQ(expected_column, frame->GetColumn()); 12055 CHECK_EQ(is_eval, frame->IsEval()); 12056 CHECK_EQ(is_constructor, frame->IsConstructor()); 12057 } 12058 12059 12060 v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) { 12061 v8::HandleScope scope; 12062 const char* origin = "capture-stack-trace-test"; 12063 const int kOverviewTest = 1; 12064 const int kDetailedTest = 2; 12065 12066 ASSERT(args.Length() == 1); 12067 12068 int testGroup = args[0]->Int32Value(); 12069 if (testGroup == kOverviewTest) { 12070 v8::Handle<v8::StackTrace> stackTrace = 12071 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview); 12072 CHECK_EQ(4, stackTrace->GetFrameCount()); 12073 checkStackFrame(origin, "bar", 2, 10, false, false, 12074 stackTrace->GetFrame(0)); 12075 checkStackFrame(origin, "foo", 6, 3, false, false, 12076 stackTrace->GetFrame(1)); 12077 checkStackFrame(NULL, "", 1, 1, false, false, 12078 stackTrace->GetFrame(2)); 12079 // The last frame is an anonymous function that has the initial call. 12080 checkStackFrame(origin, "", 8, 7, false, false, 12081 stackTrace->GetFrame(3)); 12082 12083 CHECK(stackTrace->AsArray()->IsArray()); 12084 } else if (testGroup == kDetailedTest) { 12085 v8::Handle<v8::StackTrace> stackTrace = 12086 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); 12087 CHECK_EQ(4, stackTrace->GetFrameCount()); 12088 checkStackFrame(origin, "bat", 4, 22, false, false, 12089 stackTrace->GetFrame(0)); 12090 checkStackFrame(origin, "baz", 8, 3, false, true, 12091 stackTrace->GetFrame(1)); 12092 #ifdef ENABLE_DEBUGGER_SUPPORT 12093 bool is_eval = true; 12094 #else // ENABLE_DEBUGGER_SUPPORT 12095 bool is_eval = false; 12096 #endif // ENABLE_DEBUGGER_SUPPORT 12097 12098 checkStackFrame(NULL, "", 1, 1, is_eval, false, 12099 stackTrace->GetFrame(2)); 12100 // The last frame is an anonymous function that has the initial call to foo. 12101 checkStackFrame(origin, "", 10, 1, false, false, 12102 stackTrace->GetFrame(3)); 12103 12104 CHECK(stackTrace->AsArray()->IsArray()); 12105 } 12106 return v8::Undefined(); 12107 } 12108 12109 12110 // Tests the C++ StackTrace API. 12111 // TODO(3074796): Reenable this as a THREADED_TEST once it passes. 12112 // THREADED_TEST(CaptureStackTrace) { 12113 TEST(CaptureStackTrace) { 12114 v8::HandleScope scope; 12115 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test"); 12116 Local<ObjectTemplate> templ = ObjectTemplate::New(); 12117 templ->Set(v8_str("AnalyzeStackInNativeCode"), 12118 v8::FunctionTemplate::New(AnalyzeStackInNativeCode)); 12119 LocalContext context(0, templ); 12120 12121 // Test getting OVERVIEW information. Should ignore information that is not 12122 // script name, function name, line number, and column offset. 12123 const char *overview_source = 12124 "function bar() {\n" 12125 " var y; AnalyzeStackInNativeCode(1);\n" 12126 "}\n" 12127 "function foo() {\n" 12128 "\n" 12129 " bar();\n" 12130 "}\n" 12131 "var x;eval('new foo();');"; 12132 v8::Handle<v8::String> overview_src = v8::String::New(overview_source); 12133 v8::Handle<Value> overview_result = 12134 v8::Script::New(overview_src, origin)->Run(); 12135 ASSERT(!overview_result.IsEmpty()); 12136 ASSERT(overview_result->IsObject()); 12137 12138 // Test getting DETAILED information. 12139 const char *detailed_source = 12140 "function bat() {AnalyzeStackInNativeCode(2);\n" 12141 "}\n" 12142 "\n" 12143 "function baz() {\n" 12144 " bat();\n" 12145 "}\n" 12146 "eval('new baz();');"; 12147 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source); 12148 // Make the script using a non-zero line and column offset. 12149 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3); 12150 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5); 12151 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset); 12152 v8::Handle<v8::Script> detailed_script( 12153 v8::Script::New(detailed_src, &detailed_origin)); 12154 v8::Handle<Value> detailed_result = detailed_script->Run(); 12155 ASSERT(!detailed_result.IsEmpty()); 12156 ASSERT(detailed_result->IsObject()); 12157 } 12158 12159 12160 static void StackTraceForUncaughtExceptionListener( 12161 v8::Handle<v8::Message> message, 12162 v8::Handle<Value>) { 12163 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace(); 12164 CHECK_EQ(2, stack_trace->GetFrameCount()); 12165 checkStackFrame("origin", "foo", 2, 3, false, false, 12166 stack_trace->GetFrame(0)); 12167 checkStackFrame("origin", "bar", 5, 3, false, false, 12168 stack_trace->GetFrame(1)); 12169 } 12170 12171 TEST(CaptureStackTraceForUncaughtException) { 12172 report_count = 0; 12173 v8::HandleScope scope; 12174 LocalContext env; 12175 v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener); 12176 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); 12177 12178 Script::Compile(v8_str("function foo() {\n" 12179 " throw 1;\n" 12180 "};\n" 12181 "function bar() {\n" 12182 " foo();\n" 12183 "};"), 12184 v8_str("origin"))->Run(); 12185 v8::Local<v8::Object> global = env->Global(); 12186 Local<Value> trouble = global->Get(v8_str("bar")); 12187 CHECK(trouble->IsFunction()); 12188 Function::Cast(*trouble)->Call(global, 0, NULL); 12189 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); 12190 v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener); 12191 } 12192 12193 12194 TEST(CaptureStackTraceForUncaughtExceptionAndSetters) { 12195 v8::HandleScope scope; 12196 LocalContext env; 12197 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true, 12198 1024, 12199 v8::StackTrace::kDetailed); 12200 12201 CompileRun( 12202 "var setters = ['column', 'lineNumber', 'scriptName',\n" 12203 " 'scriptNameOrSourceURL', 'functionName', 'isEval',\n" 12204 " 'isConstructor'];\n" 12205 "for (var i = 0; i < setters.length; i++) {\n" 12206 " var prop = setters[i];\n" 12207 " Object.prototype.__defineSetter__(prop, function() { throw prop; });\n" 12208 "}\n"); 12209 CompileRun("throw 'exception';"); 12210 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); 12211 } 12212 12213 12214 v8::Handle<Value> AnalyzeStackOfEvalWithSourceURL(const v8::Arguments& args) { 12215 v8::HandleScope scope; 12216 v8::Handle<v8::StackTrace> stackTrace = 12217 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); 12218 CHECK_EQ(5, stackTrace->GetFrameCount()); 12219 v8::Handle<v8::String> url = v8_str("eval_url"); 12220 for (int i = 0; i < 3; i++) { 12221 v8::Handle<v8::String> name = 12222 stackTrace->GetFrame(i)->GetScriptNameOrSourceURL(); 12223 CHECK(!name.IsEmpty()); 12224 CHECK_EQ(url, name); 12225 } 12226 return v8::Undefined(); 12227 } 12228 12229 12230 TEST(SourceURLInStackTrace) { 12231 v8::HandleScope scope; 12232 Local<ObjectTemplate> templ = ObjectTemplate::New(); 12233 templ->Set(v8_str("AnalyzeStackOfEvalWithSourceURL"), 12234 v8::FunctionTemplate::New(AnalyzeStackOfEvalWithSourceURL)); 12235 LocalContext context(0, templ); 12236 12237 const char *source = 12238 "function outer() {\n" 12239 "function bar() {\n" 12240 " AnalyzeStackOfEvalWithSourceURL();\n" 12241 "}\n" 12242 "function foo() {\n" 12243 "\n" 12244 " bar();\n" 12245 "}\n" 12246 "foo();\n" 12247 "}\n" 12248 "eval('(' + outer +')()//@ sourceURL=eval_url');"; 12249 CHECK(CompileRun(source)->IsUndefined()); 12250 } 12251 12252 12253 // Test that idle notification can be handled and eventually returns true. 12254 THREADED_TEST(IdleNotification) { 12255 bool rv = false; 12256 for (int i = 0; i < 100; i++) { 12257 rv = v8::V8::IdleNotification(); 12258 if (rv) 12259 break; 12260 } 12261 CHECK(rv == true); 12262 } 12263 12264 12265 static uint32_t* stack_limit; 12266 12267 static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) { 12268 stack_limit = reinterpret_cast<uint32_t*>( 12269 i::Isolate::Current()->stack_guard()->real_climit()); 12270 return v8::Undefined(); 12271 } 12272 12273 12274 // Uses the address of a local variable to determine the stack top now. 12275 // Given a size, returns an address that is that far from the current 12276 // top of stack. 12277 static uint32_t* ComputeStackLimit(uint32_t size) { 12278 uint32_t* answer = &size - (size / sizeof(size)); 12279 // If the size is very large and the stack is very near the bottom of 12280 // memory then the calculation above may wrap around and give an address 12281 // that is above the (downwards-growing) stack. In that case we return 12282 // a very low address. 12283 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size)); 12284 return answer; 12285 } 12286 12287 12288 TEST(SetResourceConstraints) { 12289 static const int K = 1024; 12290 uint32_t* set_limit = ComputeStackLimit(128 * K); 12291 12292 // Set stack limit. 12293 v8::ResourceConstraints constraints; 12294 constraints.set_stack_limit(set_limit); 12295 CHECK(v8::SetResourceConstraints(&constraints)); 12296 12297 // Execute a script. 12298 v8::HandleScope scope; 12299 LocalContext env; 12300 Local<v8::FunctionTemplate> fun_templ = 12301 v8::FunctionTemplate::New(GetStackLimitCallback); 12302 Local<Function> fun = fun_templ->GetFunction(); 12303 env->Global()->Set(v8_str("get_stack_limit"), fun); 12304 CompileRun("get_stack_limit();"); 12305 12306 CHECK(stack_limit == set_limit); 12307 } 12308 12309 12310 TEST(SetResourceConstraintsInThread) { 12311 uint32_t* set_limit; 12312 { 12313 v8::Locker locker; 12314 static const int K = 1024; 12315 set_limit = ComputeStackLimit(128 * K); 12316 12317 // Set stack limit. 12318 v8::ResourceConstraints constraints; 12319 constraints.set_stack_limit(set_limit); 12320 CHECK(v8::SetResourceConstraints(&constraints)); 12321 12322 // Execute a script. 12323 v8::HandleScope scope; 12324 LocalContext env; 12325 Local<v8::FunctionTemplate> fun_templ = 12326 v8::FunctionTemplate::New(GetStackLimitCallback); 12327 Local<Function> fun = fun_templ->GetFunction(); 12328 env->Global()->Set(v8_str("get_stack_limit"), fun); 12329 CompileRun("get_stack_limit();"); 12330 12331 CHECK(stack_limit == set_limit); 12332 } 12333 { 12334 v8::Locker locker; 12335 CHECK(stack_limit == set_limit); 12336 } 12337 } 12338 12339 12340 THREADED_TEST(GetHeapStatistics) { 12341 v8::HandleScope scope; 12342 LocalContext c1; 12343 v8::HeapStatistics heap_statistics; 12344 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0); 12345 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0); 12346 v8::V8::GetHeapStatistics(&heap_statistics); 12347 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0); 12348 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0); 12349 } 12350 12351 12352 static double DoubleFromBits(uint64_t value) { 12353 double target; 12354 memcpy(&target, &value, sizeof(target)); 12355 return target; 12356 } 12357 12358 12359 static uint64_t DoubleToBits(double value) { 12360 uint64_t target; 12361 memcpy(&target, &value, sizeof(target)); 12362 return target; 12363 } 12364 12365 12366 static double DoubleToDateTime(double input) { 12367 double date_limit = 864e13; 12368 if (IsNaN(input) || input < -date_limit || input > date_limit) { 12369 return i::OS::nan_value(); 12370 } 12371 return (input < 0) ? -(floor(-input)) : floor(input); 12372 } 12373 12374 // We don't have a consistent way to write 64-bit constants syntactically, so we 12375 // split them into two 32-bit constants and combine them programmatically. 12376 static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) { 12377 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits); 12378 } 12379 12380 12381 THREADED_TEST(QuietSignalingNaNs) { 12382 v8::HandleScope scope; 12383 LocalContext context; 12384 v8::TryCatch try_catch; 12385 12386 // Special double values. 12387 double snan = DoubleFromBits(0x7ff00000, 0x00000001); 12388 double qnan = DoubleFromBits(0x7ff80000, 0x00000000); 12389 double infinity = DoubleFromBits(0x7ff00000, 0x00000000); 12390 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu); 12391 double min_normal = DoubleFromBits(0x00100000, 0x00000000); 12392 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu); 12393 double min_denormal = DoubleFromBits(0x00000000, 0x00000001); 12394 12395 // Date values are capped at +/-100000000 days (times 864e5 ms per day) 12396 // on either side of the epoch. 12397 double date_limit = 864e13; 12398 12399 double test_values[] = { 12400 snan, 12401 qnan, 12402 infinity, 12403 max_normal, 12404 date_limit + 1, 12405 date_limit, 12406 min_normal, 12407 max_denormal, 12408 min_denormal, 12409 0, 12410 -0, 12411 -min_denormal, 12412 -max_denormal, 12413 -min_normal, 12414 -date_limit, 12415 -date_limit - 1, 12416 -max_normal, 12417 -infinity, 12418 -qnan, 12419 -snan 12420 }; 12421 int num_test_values = 20; 12422 12423 for (int i = 0; i < num_test_values; i++) { 12424 double test_value = test_values[i]; 12425 12426 // Check that Number::New preserves non-NaNs and quiets SNaNs. 12427 v8::Handle<v8::Value> number = v8::Number::New(test_value); 12428 double stored_number = number->NumberValue(); 12429 if (!IsNaN(test_value)) { 12430 CHECK_EQ(test_value, stored_number); 12431 } else { 12432 uint64_t stored_bits = DoubleToBits(stored_number); 12433 // Check if quiet nan (bits 51..62 all set). 12434 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 12435 } 12436 12437 // Check that Date::New preserves non-NaNs in the date range and 12438 // quiets SNaNs. 12439 v8::Handle<v8::Value> date = v8::Date::New(test_value); 12440 double expected_stored_date = DoubleToDateTime(test_value); 12441 double stored_date = date->NumberValue(); 12442 if (!IsNaN(expected_stored_date)) { 12443 CHECK_EQ(expected_stored_date, stored_date); 12444 } else { 12445 uint64_t stored_bits = DoubleToBits(stored_date); 12446 // Check if quiet nan (bits 51..62 all set). 12447 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 12448 } 12449 } 12450 } 12451 12452 12453 static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) { 12454 v8::HandleScope scope; 12455 v8::TryCatch tc; 12456 v8::Handle<v8::String> str = args[0]->ToString(); 12457 if (tc.HasCaught()) 12458 return tc.ReThrow(); 12459 return v8::Undefined(); 12460 } 12461 12462 12463 // Test that an exception can be propagated down through a spaghetti 12464 // stack using ReThrow. 12465 THREADED_TEST(SpaghettiStackReThrow) { 12466 v8::HandleScope scope; 12467 LocalContext context; 12468 context->Global()->Set( 12469 v8::String::New("s"), 12470 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction()); 12471 v8::TryCatch try_catch; 12472 CompileRun( 12473 "var i = 0;" 12474 "var o = {" 12475 " toString: function () {" 12476 " if (i == 10) {" 12477 " throw 'Hey!';" 12478 " } else {" 12479 " i++;" 12480 " return s(o);" 12481 " }" 12482 " }" 12483 "};" 12484 "s(o);"); 12485 CHECK(try_catch.HasCaught()); 12486 v8::String::Utf8Value value(try_catch.Exception()); 12487 CHECK_EQ(0, strcmp(*value, "Hey!")); 12488 } 12489 12490 12491 TEST(Regress528) { 12492 v8::V8::Initialize(); 12493 12494 v8::HandleScope scope; 12495 v8::Persistent<Context> context; 12496 v8::Persistent<Context> other_context; 12497 int gc_count; 12498 12499 // Create a context used to keep the code from aging in the compilation 12500 // cache. 12501 other_context = Context::New(); 12502 12503 // Context-dependent context data creates reference from the compilation 12504 // cache to the global object. 12505 const char* source_simple = "1"; 12506 context = Context::New(); 12507 { 12508 v8::HandleScope scope; 12509 12510 context->Enter(); 12511 Local<v8::String> obj = v8::String::New(""); 12512 context->SetData(obj); 12513 CompileRun(source_simple); 12514 context->Exit(); 12515 } 12516 context.Dispose(); 12517 for (gc_count = 1; gc_count < 10; gc_count++) { 12518 other_context->Enter(); 12519 CompileRun(source_simple); 12520 other_context->Exit(); 12521 HEAP->CollectAllGarbage(false); 12522 if (GetGlobalObjectsCount() == 1) break; 12523 } 12524 CHECK_GE(2, gc_count); 12525 CHECK_EQ(1, GetGlobalObjectsCount()); 12526 12527 // Eval in a function creates reference from the compilation cache to the 12528 // global object. 12529 const char* source_eval = "function f(){eval('1')}; f()"; 12530 context = Context::New(); 12531 { 12532 v8::HandleScope scope; 12533 12534 context->Enter(); 12535 CompileRun(source_eval); 12536 context->Exit(); 12537 } 12538 context.Dispose(); 12539 for (gc_count = 1; gc_count < 10; gc_count++) { 12540 other_context->Enter(); 12541 CompileRun(source_eval); 12542 other_context->Exit(); 12543 HEAP->CollectAllGarbage(false); 12544 if (GetGlobalObjectsCount() == 1) break; 12545 } 12546 CHECK_GE(2, gc_count); 12547 CHECK_EQ(1, GetGlobalObjectsCount()); 12548 12549 // Looking up the line number for an exception creates reference from the 12550 // compilation cache to the global object. 12551 const char* source_exception = "function f(){throw 1;} f()"; 12552 context = Context::New(); 12553 { 12554 v8::HandleScope scope; 12555 12556 context->Enter(); 12557 v8::TryCatch try_catch; 12558 CompileRun(source_exception); 12559 CHECK(try_catch.HasCaught()); 12560 v8::Handle<v8::Message> message = try_catch.Message(); 12561 CHECK(!message.IsEmpty()); 12562 CHECK_EQ(1, message->GetLineNumber()); 12563 context->Exit(); 12564 } 12565 context.Dispose(); 12566 for (gc_count = 1; gc_count < 10; gc_count++) { 12567 other_context->Enter(); 12568 CompileRun(source_exception); 12569 other_context->Exit(); 12570 HEAP->CollectAllGarbage(false); 12571 if (GetGlobalObjectsCount() == 1) break; 12572 } 12573 CHECK_GE(2, gc_count); 12574 CHECK_EQ(1, GetGlobalObjectsCount()); 12575 12576 other_context.Dispose(); 12577 } 12578 12579 12580 THREADED_TEST(ScriptOrigin) { 12581 v8::HandleScope scope; 12582 LocalContext env; 12583 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 12584 v8::Handle<v8::String> script = v8::String::New( 12585 "function f() {}\n\nfunction g() {}"); 12586 v8::Script::Compile(script, &origin)->Run(); 12587 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 12588 env->Global()->Get(v8::String::New("f"))); 12589 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 12590 env->Global()->Get(v8::String::New("g"))); 12591 12592 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin(); 12593 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName())); 12594 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value()); 12595 12596 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin(); 12597 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName())); 12598 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value()); 12599 } 12600 12601 12602 THREADED_TEST(ScriptLineNumber) { 12603 v8::HandleScope scope; 12604 LocalContext env; 12605 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 12606 v8::Handle<v8::String> script = v8::String::New( 12607 "function f() {}\n\nfunction g() {}"); 12608 v8::Script::Compile(script, &origin)->Run(); 12609 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 12610 env->Global()->Get(v8::String::New("f"))); 12611 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 12612 env->Global()->Get(v8::String::New("g"))); 12613 CHECK_EQ(0, f->GetScriptLineNumber()); 12614 CHECK_EQ(2, g->GetScriptLineNumber()); 12615 } 12616 12617 12618 static v8::Handle<Value> GetterWhichReturns42(Local<String> name, 12619 const AccessorInfo& info) { 12620 return v8_num(42); 12621 } 12622 12623 12624 static void SetterWhichSetsYOnThisTo23(Local<String> name, 12625 Local<Value> value, 12626 const AccessorInfo& info) { 12627 info.This()->Set(v8_str("y"), v8_num(23)); 12628 } 12629 12630 12631 TEST(SetterOnConstructorPrototype) { 12632 v8::HandleScope scope; 12633 Local<ObjectTemplate> templ = ObjectTemplate::New(); 12634 templ->SetAccessor(v8_str("x"), 12635 GetterWhichReturns42, 12636 SetterWhichSetsYOnThisTo23); 12637 LocalContext context; 12638 context->Global()->Set(v8_str("P"), templ->NewInstance()); 12639 CompileRun("function C1() {" 12640 " this.x = 23;" 12641 "};" 12642 "C1.prototype = P;" 12643 "function C2() {" 12644 " this.x = 23" 12645 "};" 12646 "C2.prototype = { };" 12647 "C2.prototype.__proto__ = P;"); 12648 12649 v8::Local<v8::Script> script; 12650 script = v8::Script::Compile(v8_str("new C1();")); 12651 for (int i = 0; i < 10; i++) { 12652 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 12653 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); 12654 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); 12655 } 12656 12657 script = v8::Script::Compile(v8_str("new C2();")); 12658 for (int i = 0; i < 10; i++) { 12659 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 12660 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value()); 12661 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value()); 12662 } 12663 } 12664 12665 12666 static v8::Handle<Value> NamedPropertyGetterWhichReturns42( 12667 Local<String> name, const AccessorInfo& info) { 12668 return v8_num(42); 12669 } 12670 12671 12672 static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23( 12673 Local<String> name, Local<Value> value, const AccessorInfo& info) { 12674 if (name->Equals(v8_str("x"))) { 12675 info.This()->Set(v8_str("y"), v8_num(23)); 12676 } 12677 return v8::Handle<Value>(); 12678 } 12679 12680 12681 THREADED_TEST(InterceptorOnConstructorPrototype) { 12682 v8::HandleScope scope; 12683 Local<ObjectTemplate> templ = ObjectTemplate::New(); 12684 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42, 12685 NamedPropertySetterWhichSetsYOnThisTo23); 12686 LocalContext context; 12687 context->Global()->Set(v8_str("P"), templ->NewInstance()); 12688 CompileRun("function C1() {" 12689 " this.x = 23;" 12690 "};" 12691 "C1.prototype = P;" 12692 "function C2() {" 12693 " this.x = 23" 12694 "};" 12695 "C2.prototype = { };" 12696 "C2.prototype.__proto__ = P;"); 12697 12698 v8::Local<v8::Script> script; 12699 script = v8::Script::Compile(v8_str("new C1();")); 12700 for (int i = 0; i < 10; i++) { 12701 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 12702 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); 12703 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); 12704 } 12705 12706 script = v8::Script::Compile(v8_str("new C2();")); 12707 for (int i = 0; i < 10; i++) { 12708 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 12709 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value()); 12710 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value()); 12711 } 12712 } 12713 12714 12715 TEST(Bug618) { 12716 const char* source = "function C1() {" 12717 " this.x = 23;" 12718 "};" 12719 "C1.prototype = P;"; 12720 12721 v8::HandleScope scope; 12722 LocalContext context; 12723 v8::Local<v8::Script> script; 12724 12725 // Use a simple object as prototype. 12726 v8::Local<v8::Object> prototype = v8::Object::New(); 12727 prototype->Set(v8_str("y"), v8_num(42)); 12728 context->Global()->Set(v8_str("P"), prototype); 12729 12730 // This compile will add the code to the compilation cache. 12731 CompileRun(source); 12732 12733 script = v8::Script::Compile(v8_str("new C1();")); 12734 // Allow enough iterations for the inobject slack tracking logic 12735 // to finalize instance size and install the fast construct stub. 12736 for (int i = 0; i < 256; i++) { 12737 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 12738 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); 12739 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); 12740 } 12741 12742 // Use an API object with accessors as prototype. 12743 Local<ObjectTemplate> templ = ObjectTemplate::New(); 12744 templ->SetAccessor(v8_str("x"), 12745 GetterWhichReturns42, 12746 SetterWhichSetsYOnThisTo23); 12747 context->Global()->Set(v8_str("P"), templ->NewInstance()); 12748 12749 // This compile will get the code from the compilation cache. 12750 CompileRun(source); 12751 12752 script = v8::Script::Compile(v8_str("new C1();")); 12753 for (int i = 0; i < 10; i++) { 12754 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 12755 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); 12756 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); 12757 } 12758 } 12759 12760 int prologue_call_count = 0; 12761 int epilogue_call_count = 0; 12762 int prologue_call_count_second = 0; 12763 int epilogue_call_count_second = 0; 12764 12765 void PrologueCallback(v8::GCType, v8::GCCallbackFlags) { 12766 ++prologue_call_count; 12767 } 12768 12769 void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) { 12770 ++epilogue_call_count; 12771 } 12772 12773 void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { 12774 ++prologue_call_count_second; 12775 } 12776 12777 void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { 12778 ++epilogue_call_count_second; 12779 } 12780 12781 TEST(GCCallbacks) { 12782 LocalContext context; 12783 12784 v8::V8::AddGCPrologueCallback(PrologueCallback); 12785 v8::V8::AddGCEpilogueCallback(EpilogueCallback); 12786 CHECK_EQ(0, prologue_call_count); 12787 CHECK_EQ(0, epilogue_call_count); 12788 HEAP->CollectAllGarbage(false); 12789 CHECK_EQ(1, prologue_call_count); 12790 CHECK_EQ(1, epilogue_call_count); 12791 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond); 12792 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond); 12793 HEAP->CollectAllGarbage(false); 12794 CHECK_EQ(2, prologue_call_count); 12795 CHECK_EQ(2, epilogue_call_count); 12796 CHECK_EQ(1, prologue_call_count_second); 12797 CHECK_EQ(1, epilogue_call_count_second); 12798 v8::V8::RemoveGCPrologueCallback(PrologueCallback); 12799 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback); 12800 HEAP->CollectAllGarbage(false); 12801 CHECK_EQ(2, prologue_call_count); 12802 CHECK_EQ(2, epilogue_call_count); 12803 CHECK_EQ(2, prologue_call_count_second); 12804 CHECK_EQ(2, epilogue_call_count_second); 12805 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond); 12806 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond); 12807 HEAP->CollectAllGarbage(false); 12808 CHECK_EQ(2, prologue_call_count); 12809 CHECK_EQ(2, epilogue_call_count); 12810 CHECK_EQ(2, prologue_call_count_second); 12811 CHECK_EQ(2, epilogue_call_count_second); 12812 } 12813 12814 12815 THREADED_TEST(AddToJSFunctionResultCache) { 12816 i::FLAG_allow_natives_syntax = true; 12817 v8::HandleScope scope; 12818 12819 LocalContext context; 12820 12821 const char* code = 12822 "(function() {" 12823 " var key0 = 'a';" 12824 " var key1 = 'b';" 12825 " var r0 = %_GetFromCache(0, key0);" 12826 " var r1 = %_GetFromCache(0, key1);" 12827 " var r0_ = %_GetFromCache(0, key0);" 12828 " if (r0 !== r0_)" 12829 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;" 12830 " var r1_ = %_GetFromCache(0, key1);" 12831 " if (r1 !== r1_)" 12832 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;" 12833 " return 'PASSED';" 12834 "})()"; 12835 HEAP->ClearJSFunctionResultCaches(); 12836 ExpectString(code, "PASSED"); 12837 } 12838 12839 12840 static const int k0CacheSize = 16; 12841 12842 THREADED_TEST(FillJSFunctionResultCache) { 12843 i::FLAG_allow_natives_syntax = true; 12844 v8::HandleScope scope; 12845 12846 LocalContext context; 12847 12848 const char* code = 12849 "(function() {" 12850 " var k = 'a';" 12851 " var r = %_GetFromCache(0, k);" 12852 " for (var i = 0; i < 16; i++) {" 12853 " %_GetFromCache(0, 'a' + i);" 12854 " };" 12855 " if (r === %_GetFromCache(0, k))" 12856 " return 'FAILED: k0CacheSize is too small';" 12857 " return 'PASSED';" 12858 "})()"; 12859 HEAP->ClearJSFunctionResultCaches(); 12860 ExpectString(code, "PASSED"); 12861 } 12862 12863 12864 THREADED_TEST(RoundRobinGetFromCache) { 12865 i::FLAG_allow_natives_syntax = true; 12866 v8::HandleScope scope; 12867 12868 LocalContext context; 12869 12870 const char* code = 12871 "(function() {" 12872 " var keys = [];" 12873 " for (var i = 0; i < 16; i++) keys.push(i);" 12874 " var values = [];" 12875 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" 12876 " for (var i = 0; i < 16; i++) {" 12877 " var v = %_GetFromCache(0, keys[i]);" 12878 " if (v !== values[i])" 12879 " return 'Wrong value for ' + " 12880 " keys[i] + ': ' + v + ' vs. ' + values[i];" 12881 " };" 12882 " return 'PASSED';" 12883 "})()"; 12884 HEAP->ClearJSFunctionResultCaches(); 12885 ExpectString(code, "PASSED"); 12886 } 12887 12888 12889 THREADED_TEST(ReverseGetFromCache) { 12890 i::FLAG_allow_natives_syntax = true; 12891 v8::HandleScope scope; 12892 12893 LocalContext context; 12894 12895 const char* code = 12896 "(function() {" 12897 " var keys = [];" 12898 " for (var i = 0; i < 16; i++) keys.push(i);" 12899 " var values = [];" 12900 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" 12901 " for (var i = 15; i >= 16; i--) {" 12902 " var v = %_GetFromCache(0, keys[i]);" 12903 " if (v !== values[i])" 12904 " return 'Wrong value for ' + " 12905 " keys[i] + ': ' + v + ' vs. ' + values[i];" 12906 " };" 12907 " return 'PASSED';" 12908 "})()"; 12909 HEAP->ClearJSFunctionResultCaches(); 12910 ExpectString(code, "PASSED"); 12911 } 12912 12913 12914 THREADED_TEST(TestEviction) { 12915 i::FLAG_allow_natives_syntax = true; 12916 v8::HandleScope scope; 12917 12918 LocalContext context; 12919 12920 const char* code = 12921 "(function() {" 12922 " for (var i = 0; i < 2*16; i++) {" 12923 " %_GetFromCache(0, 'a' + i);" 12924 " };" 12925 " return 'PASSED';" 12926 "})()"; 12927 HEAP->ClearJSFunctionResultCaches(); 12928 ExpectString(code, "PASSED"); 12929 } 12930 12931 12932 THREADED_TEST(TwoByteStringInAsciiCons) { 12933 // See Chromium issue 47824. 12934 v8::HandleScope scope; 12935 12936 LocalContext context; 12937 const char* init_code = 12938 "var str1 = 'abelspendabel';" 12939 "var str2 = str1 + str1 + str1;" 12940 "str2;"; 12941 Local<Value> result = CompileRun(init_code); 12942 12943 CHECK(result->IsString()); 12944 i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result)); 12945 int length = string->length(); 12946 CHECK(string->IsAsciiRepresentation()); 12947 12948 FlattenString(string); 12949 i::Handle<i::String> flat_string = FlattenGetString(string); 12950 12951 CHECK(string->IsAsciiRepresentation()); 12952 CHECK(flat_string->IsAsciiRepresentation()); 12953 12954 // Create external resource. 12955 uint16_t* uc16_buffer = new uint16_t[length + 1]; 12956 12957 i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length); 12958 uc16_buffer[length] = 0; 12959 12960 TestResource resource(uc16_buffer); 12961 12962 flat_string->MakeExternal(&resource); 12963 12964 CHECK(flat_string->IsTwoByteRepresentation()); 12965 12966 // At this point, we should have a Cons string which is flat and ASCII, 12967 // with a first half that is a two-byte string (although it only contains 12968 // ASCII characters). This is a valid sequence of steps, and it can happen 12969 // in real pages. 12970 12971 CHECK(string->IsAsciiRepresentation()); 12972 i::ConsString* cons = i::ConsString::cast(*string); 12973 CHECK_EQ(0, cons->second()->length()); 12974 CHECK(cons->first()->IsTwoByteRepresentation()); 12975 12976 // Check that some string operations work. 12977 12978 // Atom RegExp. 12979 Local<Value> reresult = CompileRun("str2.match(/abel/g).length;"); 12980 CHECK_EQ(6, reresult->Int32Value()); 12981 12982 // Nonatom RegExp. 12983 reresult = CompileRun("str2.match(/abe./g).length;"); 12984 CHECK_EQ(6, reresult->Int32Value()); 12985 12986 reresult = CompileRun("str2.search(/bel/g);"); 12987 CHECK_EQ(1, reresult->Int32Value()); 12988 12989 reresult = CompileRun("str2.search(/be./g);"); 12990 CHECK_EQ(1, reresult->Int32Value()); 12991 12992 ExpectTrue("/bel/g.test(str2);"); 12993 12994 ExpectTrue("/be./g.test(str2);"); 12995 12996 reresult = CompileRun("/bel/g.exec(str2);"); 12997 CHECK(!reresult->IsNull()); 12998 12999 reresult = CompileRun("/be./g.exec(str2);"); 13000 CHECK(!reresult->IsNull()); 13001 13002 ExpectString("str2.substring(2, 10);", "elspenda"); 13003 13004 ExpectString("str2.substring(2, 20);", "elspendabelabelspe"); 13005 13006 ExpectString("str2.charAt(2);", "e"); 13007 13008 reresult = CompileRun("str2.charCodeAt(2);"); 13009 CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value()); 13010 } 13011 13012 13013 // Failed access check callback that performs a GC on each invocation. 13014 void FailedAccessCheckCallbackGC(Local<v8::Object> target, 13015 v8::AccessType type, 13016 Local<v8::Value> data) { 13017 HEAP->CollectAllGarbage(true); 13018 } 13019 13020 13021 TEST(GCInFailedAccessCheckCallback) { 13022 // Install a failed access check callback that performs a GC on each 13023 // invocation. Then force the callback to be called from va 13024 13025 v8::V8::Initialize(); 13026 v8::V8::SetFailedAccessCheckCallbackFunction(&FailedAccessCheckCallbackGC); 13027 13028 v8::HandleScope scope; 13029 13030 // Create an ObjectTemplate for global objects and install access 13031 // check callbacks that will block access. 13032 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 13033 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker, 13034 IndexedGetAccessBlocker, 13035 v8::Handle<v8::Value>(), 13036 false); 13037 13038 // Create a context and set an x property on it's global object. 13039 LocalContext context0(NULL, global_template); 13040 context0->Global()->Set(v8_str("x"), v8_num(42)); 13041 v8::Handle<v8::Object> global0 = context0->Global(); 13042 13043 // Create a context with a different security token so that the 13044 // failed access check callback will be called on each access. 13045 LocalContext context1(NULL, global_template); 13046 context1->Global()->Set(v8_str("other"), global0); 13047 13048 // Get property with failed access check. 13049 ExpectUndefined("other.x"); 13050 13051 // Get element with failed access check. 13052 ExpectUndefined("other[0]"); 13053 13054 // Set property with failed access check. 13055 v8::Handle<v8::Value> result = CompileRun("other.x = new Object()"); 13056 CHECK(result->IsObject()); 13057 13058 // Set element with failed access check. 13059 result = CompileRun("other[0] = new Object()"); 13060 CHECK(result->IsObject()); 13061 13062 // Get property attribute with failed access check. 13063 ExpectFalse("\'x\' in other"); 13064 13065 // Get property attribute for element with failed access check. 13066 ExpectFalse("0 in other"); 13067 13068 // Delete property. 13069 ExpectFalse("delete other.x"); 13070 13071 // Delete element. 13072 CHECK_EQ(false, global0->Delete(0)); 13073 13074 // DefineAccessor. 13075 CHECK_EQ(false, 13076 global0->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("x"))); 13077 13078 // Define JavaScript accessor. 13079 ExpectUndefined("Object.prototype.__defineGetter__.call(" 13080 " other, \'x\', function() { return 42; })"); 13081 13082 // LookupAccessor. 13083 ExpectUndefined("Object.prototype.__lookupGetter__.call(" 13084 " other, \'x\')"); 13085 13086 // HasLocalElement. 13087 ExpectFalse("Object.prototype.hasOwnProperty.call(other, \'0\')"); 13088 13089 CHECK_EQ(false, global0->HasRealIndexedProperty(0)); 13090 CHECK_EQ(false, global0->HasRealNamedProperty(v8_str("x"))); 13091 CHECK_EQ(false, global0->HasRealNamedCallbackProperty(v8_str("x"))); 13092 13093 // Reset the failed access check callback so it does not influence 13094 // the other tests. 13095 v8::V8::SetFailedAccessCheckCallbackFunction(NULL); 13096 } 13097 13098 TEST(DefaultIsolateGetCurrent) { 13099 CHECK(v8::Isolate::GetCurrent() != NULL); 13100 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 13101 CHECK(reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate()); 13102 printf("*** %s\n", "DefaultIsolateGetCurrent success"); 13103 } 13104 13105 TEST(IsolateNewDispose) { 13106 v8::Isolate* current_isolate = v8::Isolate::GetCurrent(); 13107 v8::Isolate* isolate = v8::Isolate::New(); 13108 CHECK(isolate != NULL); 13109 CHECK(!reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate()); 13110 CHECK(current_isolate != isolate); 13111 CHECK(current_isolate == v8::Isolate::GetCurrent()); 13112 13113 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 13114 last_location = last_message = NULL; 13115 isolate->Dispose(); 13116 CHECK_EQ(last_location, NULL); 13117 CHECK_EQ(last_message, NULL); 13118 } 13119 13120 TEST(IsolateEnterExitDefault) { 13121 v8::HandleScope scope; 13122 LocalContext context; 13123 v8::Isolate* current_isolate = v8::Isolate::GetCurrent(); 13124 CHECK(current_isolate != NULL); // Default isolate. 13125 ExpectString("'hello'", "hello"); 13126 current_isolate->Enter(); 13127 ExpectString("'still working'", "still working"); 13128 current_isolate->Exit(); 13129 ExpectString("'still working 2'", "still working 2"); 13130 current_isolate->Exit(); 13131 // Default isolate is always, well, 'default current'. 13132 CHECK_EQ(v8::Isolate::GetCurrent(), current_isolate); 13133 // Still working since default isolate is auto-entering any thread 13134 // that has no isolate and attempts to execute V8 APIs. 13135 ExpectString("'still working 3'", "still working 3"); 13136 } 13137 13138 TEST(DisposeDefaultIsolate) { 13139 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 13140 13141 // Run some V8 code to trigger default isolate to become 'current'. 13142 v8::HandleScope scope; 13143 LocalContext context; 13144 ExpectString("'run some V8'", "run some V8"); 13145 13146 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 13147 CHECK(reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate()); 13148 last_location = last_message = NULL; 13149 isolate->Dispose(); 13150 // It is not possible to dispose default isolate via Isolate API. 13151 CHECK_NE(last_location, NULL); 13152 CHECK_NE(last_message, NULL); 13153 } 13154 13155 TEST(RunDefaultAndAnotherIsolate) { 13156 v8::HandleScope scope; 13157 LocalContext context; 13158 13159 // Enter new isolate. 13160 v8::Isolate* isolate = v8::Isolate::New(); 13161 CHECK(isolate); 13162 isolate->Enter(); 13163 { // Need this block because subsequent Exit() will deallocate Heap, 13164 // so we need all scope objects to be deconstructed when it happens. 13165 v8::HandleScope scope_new; 13166 LocalContext context_new; 13167 13168 // Run something in new isolate. 13169 CompileRun("var foo = 153;"); 13170 ExpectTrue("function f() { return foo == 153; }; f()"); 13171 } 13172 isolate->Exit(); 13173 13174 // This runs automatically in default isolate. 13175 // Variables in another isolate should be not available. 13176 ExpectTrue("function f() {" 13177 " try {" 13178 " foo;" 13179 " return false;" 13180 " } catch(e) {" 13181 " return true;" 13182 " }" 13183 "};" 13184 "var bar = 371;" 13185 "f()"); 13186 13187 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 13188 last_location = last_message = NULL; 13189 isolate->Dispose(); 13190 CHECK_EQ(last_location, NULL); 13191 CHECK_EQ(last_message, NULL); 13192 13193 // Check that default isolate still runs. 13194 ExpectTrue("function f() { return bar == 371; }; f()"); 13195 } 13196 13197 TEST(DisposeIsolateWhenInUse) { 13198 v8::Isolate* isolate = v8::Isolate::New(); 13199 CHECK(isolate); 13200 isolate->Enter(); 13201 v8::HandleScope scope; 13202 LocalContext context; 13203 // Run something in this isolate. 13204 ExpectTrue("true"); 13205 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 13206 last_location = last_message = NULL; 13207 // Still entered, should fail. 13208 isolate->Dispose(); 13209 CHECK_NE(last_location, NULL); 13210 CHECK_NE(last_message, NULL); 13211 } 13212 13213 TEST(RunTwoIsolatesOnSingleThread) { 13214 // Run isolate 1. 13215 v8::Isolate* isolate1 = v8::Isolate::New(); 13216 isolate1->Enter(); 13217 v8::Persistent<v8::Context> context1 = v8::Context::New(); 13218 13219 { 13220 v8::Context::Scope cscope(context1); 13221 v8::HandleScope scope; 13222 // Run something in new isolate. 13223 CompileRun("var foo = 'isolate 1';"); 13224 ExpectString("function f() { return foo; }; f()", "isolate 1"); 13225 } 13226 13227 // Run isolate 2. 13228 v8::Isolate* isolate2 = v8::Isolate::New(); 13229 v8::Persistent<v8::Context> context2; 13230 13231 { 13232 v8::Isolate::Scope iscope(isolate2); 13233 context2 = v8::Context::New(); 13234 v8::Context::Scope cscope(context2); 13235 v8::HandleScope scope; 13236 13237 // Run something in new isolate. 13238 CompileRun("var foo = 'isolate 2';"); 13239 ExpectString("function f() { return foo; }; f()", "isolate 2"); 13240 } 13241 13242 { 13243 v8::Context::Scope cscope(context1); 13244 v8::HandleScope scope; 13245 // Now again in isolate 1 13246 ExpectString("function f() { return foo; }; f()", "isolate 1"); 13247 } 13248 13249 isolate1->Exit(); 13250 13251 // Run some stuff in default isolate. 13252 v8::Persistent<v8::Context> context_default = v8::Context::New(); 13253 13254 { 13255 v8::Context::Scope cscope(context_default); 13256 v8::HandleScope scope; 13257 // Variables in other isolates should be not available, verify there 13258 // is an exception. 13259 ExpectTrue("function f() {" 13260 " try {" 13261 " foo;" 13262 " return false;" 13263 " } catch(e) {" 13264 " return true;" 13265 " }" 13266 "};" 13267 "var isDefaultIsolate = true;" 13268 "f()"); 13269 } 13270 13271 isolate1->Enter(); 13272 13273 { 13274 v8::Isolate::Scope iscope(isolate2); 13275 v8::Context::Scope cscope(context2); 13276 v8::HandleScope scope; 13277 ExpectString("function f() { return foo; }; f()", "isolate 2"); 13278 } 13279 13280 { 13281 v8::Context::Scope cscope(context1); 13282 v8::HandleScope scope; 13283 ExpectString("function f() { return foo; }; f()", "isolate 1"); 13284 } 13285 13286 { 13287 v8::Isolate::Scope iscope(isolate2); 13288 context2.Dispose(); 13289 } 13290 13291 context1.Dispose(); 13292 isolate1->Exit(); 13293 13294 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 13295 last_location = last_message = NULL; 13296 13297 isolate1->Dispose(); 13298 CHECK_EQ(last_location, NULL); 13299 CHECK_EQ(last_message, NULL); 13300 13301 isolate2->Dispose(); 13302 CHECK_EQ(last_location, NULL); 13303 CHECK_EQ(last_message, NULL); 13304 13305 // Check that default isolate still runs. 13306 { 13307 v8::Context::Scope cscope(context_default); 13308 v8::HandleScope scope; 13309 ExpectTrue("function f() { return isDefaultIsolate; }; f()"); 13310 } 13311 } 13312 13313 static int CalcFibonacci(v8::Isolate* isolate, int limit) { 13314 v8::Isolate::Scope isolate_scope(isolate); 13315 v8::HandleScope scope; 13316 LocalContext context; 13317 i::ScopedVector<char> code(1024); 13318 i::OS::SNPrintF(code, "function fib(n) {" 13319 " if (n <= 2) return 1;" 13320 " return fib(n-1) + fib(n-2);" 13321 "}" 13322 "fib(%d)", limit); 13323 Local<Value> value = CompileRun(code.start()); 13324 CHECK(value->IsNumber()); 13325 return static_cast<int>(value->NumberValue()); 13326 } 13327 13328 class IsolateThread : public v8::internal::Thread { 13329 public: 13330 explicit IsolateThread(v8::Isolate* isolate, int fib_limit) 13331 : Thread(NULL, "IsolateThread"), 13332 isolate_(isolate), 13333 fib_limit_(fib_limit), 13334 result_(0) { } 13335 13336 void Run() { 13337 result_ = CalcFibonacci(isolate_, fib_limit_); 13338 } 13339 13340 int result() { return result_; } 13341 13342 private: 13343 v8::Isolate* isolate_; 13344 int fib_limit_; 13345 int result_; 13346 }; 13347 13348 TEST(MultipleIsolatesOnIndividualThreads) { 13349 v8::Isolate* isolate1 = v8::Isolate::New(); 13350 v8::Isolate* isolate2 = v8::Isolate::New(); 13351 13352 IsolateThread thread1(isolate1, 21); 13353 IsolateThread thread2(isolate2, 12); 13354 13355 // Compute some fibonacci numbers on 3 threads in 3 isolates. 13356 thread1.Start(); 13357 thread2.Start(); 13358 13359 int result1 = CalcFibonacci(v8::Isolate::GetCurrent(), 21); 13360 int result2 = CalcFibonacci(v8::Isolate::GetCurrent(), 12); 13361 13362 thread1.Join(); 13363 thread2.Join(); 13364 13365 // Compare results. The actual fibonacci numbers for 12 and 21 are taken 13366 // (I'm lazy!) from http://en.wikipedia.org/wiki/Fibonacci_number 13367 CHECK_EQ(result1, 10946); 13368 CHECK_EQ(result2, 144); 13369 CHECK_EQ(result1, thread1.result()); 13370 CHECK_EQ(result2, thread2.result()); 13371 13372 isolate1->Dispose(); 13373 isolate2->Dispose(); 13374 } 13375 13376 13377 class InitDefaultIsolateThread : public v8::internal::Thread { 13378 public: 13379 enum TestCase { 13380 IgnoreOOM, 13381 SetResourceConstraints, 13382 SetFatalHandler, 13383 SetCounterFunction, 13384 SetCreateHistogramFunction, 13385 SetAddHistogramSampleFunction 13386 }; 13387 13388 explicit InitDefaultIsolateThread(TestCase testCase) 13389 : Thread(NULL, "InitDefaultIsolateThread"), 13390 testCase_(testCase), 13391 result_(false) { } 13392 13393 void Run() { 13394 switch (testCase_) { 13395 case IgnoreOOM: 13396 v8::V8::IgnoreOutOfMemoryException(); 13397 break; 13398 13399 case SetResourceConstraints: { 13400 static const int K = 1024; 13401 v8::ResourceConstraints constraints; 13402 constraints.set_max_young_space_size(256 * K); 13403 constraints.set_max_old_space_size(4 * K * K); 13404 v8::SetResourceConstraints(&constraints); 13405 break; 13406 } 13407 13408 case SetFatalHandler: 13409 v8::V8::SetFatalErrorHandler(NULL); 13410 break; 13411 13412 case SetCounterFunction: 13413 v8::V8::SetCounterFunction(NULL); 13414 break; 13415 13416 case SetCreateHistogramFunction: 13417 v8::V8::SetCreateHistogramFunction(NULL); 13418 break; 13419 13420 case SetAddHistogramSampleFunction: 13421 v8::V8::SetAddHistogramSampleFunction(NULL); 13422 break; 13423 } 13424 result_ = true; 13425 } 13426 13427 bool result() { return result_; } 13428 13429 private: 13430 TestCase testCase_; 13431 bool result_; 13432 }; 13433 13434 13435 static void InitializeTestHelper(InitDefaultIsolateThread::TestCase testCase) { 13436 InitDefaultIsolateThread thread(testCase); 13437 thread.Start(); 13438 thread.Join(); 13439 CHECK_EQ(thread.result(), true); 13440 } 13441 13442 TEST(InitializeDefaultIsolateOnSecondaryThread1) { 13443 InitializeTestHelper(InitDefaultIsolateThread::IgnoreOOM); 13444 } 13445 13446 TEST(InitializeDefaultIsolateOnSecondaryThread2) { 13447 InitializeTestHelper(InitDefaultIsolateThread::SetResourceConstraints); 13448 } 13449 13450 TEST(InitializeDefaultIsolateOnSecondaryThread3) { 13451 InitializeTestHelper(InitDefaultIsolateThread::SetFatalHandler); 13452 } 13453 13454 TEST(InitializeDefaultIsolateOnSecondaryThread4) { 13455 InitializeTestHelper(InitDefaultIsolateThread::SetCounterFunction); 13456 } 13457 13458 TEST(InitializeDefaultIsolateOnSecondaryThread5) { 13459 InitializeTestHelper(InitDefaultIsolateThread::SetCreateHistogramFunction); 13460 } 13461 13462 TEST(InitializeDefaultIsolateOnSecondaryThread6) { 13463 InitializeTestHelper(InitDefaultIsolateThread::SetAddHistogramSampleFunction); 13464 } 13465 13466 13467 TEST(StringCheckMultipleContexts) { 13468 const char* code = 13469 "(function() { return \"a\".charAt(0); })()"; 13470 13471 { 13472 // Run the code twice in the first context to initialize the call IC. 13473 v8::HandleScope scope; 13474 LocalContext context1; 13475 ExpectString(code, "a"); 13476 ExpectString(code, "a"); 13477 } 13478 13479 { 13480 // Change the String.prototype in the second context and check 13481 // that the right function gets called. 13482 v8::HandleScope scope; 13483 LocalContext context2; 13484 CompileRun("String.prototype.charAt = function() { return \"not a\"; }"); 13485 ExpectString(code, "not a"); 13486 } 13487 } 13488 13489 13490 TEST(NumberCheckMultipleContexts) { 13491 const char* code = 13492 "(function() { return (42).toString(); })()"; 13493 13494 { 13495 // Run the code twice in the first context to initialize the call IC. 13496 v8::HandleScope scope; 13497 LocalContext context1; 13498 ExpectString(code, "42"); 13499 ExpectString(code, "42"); 13500 } 13501 13502 { 13503 // Change the Number.prototype in the second context and check 13504 // that the right function gets called. 13505 v8::HandleScope scope; 13506 LocalContext context2; 13507 CompileRun("Number.prototype.toString = function() { return \"not 42\"; }"); 13508 ExpectString(code, "not 42"); 13509 } 13510 } 13511 13512 13513 TEST(BooleanCheckMultipleContexts) { 13514 const char* code = 13515 "(function() { return true.toString(); })()"; 13516 13517 { 13518 // Run the code twice in the first context to initialize the call IC. 13519 v8::HandleScope scope; 13520 LocalContext context1; 13521 ExpectString(code, "true"); 13522 ExpectString(code, "true"); 13523 } 13524 13525 { 13526 // Change the Boolean.prototype in the second context and check 13527 // that the right function gets called. 13528 v8::HandleScope scope; 13529 LocalContext context2; 13530 CompileRun("Boolean.prototype.toString = function() { return \"\"; }"); 13531 ExpectString(code, ""); 13532 } 13533 } 13534 13535 13536 TEST(DontDeleteCellLoadIC) { 13537 const char* function_code = 13538 "function readCell() { while (true) { return cell; } }"; 13539 13540 { 13541 // Run the code twice in the first context to initialize the load 13542 // IC for a don't delete cell. 13543 v8::HandleScope scope; 13544 LocalContext context1; 13545 CompileRun("var cell = \"first\";"); 13546 ExpectBoolean("delete cell", false); 13547 CompileRun(function_code); 13548 ExpectString("readCell()", "first"); 13549 ExpectString("readCell()", "first"); 13550 } 13551 13552 { 13553 // Use a deletable cell in the second context. 13554 v8::HandleScope scope; 13555 LocalContext context2; 13556 CompileRun("cell = \"second\";"); 13557 CompileRun(function_code); 13558 ExpectString("readCell()", "second"); 13559 ExpectBoolean("delete cell", true); 13560 ExpectString("(function() {" 13561 " try {" 13562 " return readCell();" 13563 " } catch(e) {" 13564 " return e.toString();" 13565 " }" 13566 "})()", 13567 "ReferenceError: cell is not defined"); 13568 CompileRun("cell = \"new_second\";"); 13569 HEAP->CollectAllGarbage(true); 13570 ExpectString("readCell()", "new_second"); 13571 ExpectString("readCell()", "new_second"); 13572 } 13573 } 13574 13575 13576 TEST(DontDeleteCellLoadICForceDelete) { 13577 const char* function_code = 13578 "function readCell() { while (true) { return cell; } }"; 13579 13580 // Run the code twice to initialize the load IC for a don't delete 13581 // cell. 13582 v8::HandleScope scope; 13583 LocalContext context; 13584 CompileRun("var cell = \"value\";"); 13585 ExpectBoolean("delete cell", false); 13586 CompileRun(function_code); 13587 ExpectString("readCell()", "value"); 13588 ExpectString("readCell()", "value"); 13589 13590 // Delete the cell using the API and check the inlined code works 13591 // correctly. 13592 CHECK(context->Global()->ForceDelete(v8_str("cell"))); 13593 ExpectString("(function() {" 13594 " try {" 13595 " return readCell();" 13596 " } catch(e) {" 13597 " return e.toString();" 13598 " }" 13599 "})()", 13600 "ReferenceError: cell is not defined"); 13601 } 13602 13603 13604 TEST(DontDeleteCellLoadICAPI) { 13605 const char* function_code = 13606 "function readCell() { while (true) { return cell; } }"; 13607 13608 // Run the code twice to initialize the load IC for a don't delete 13609 // cell created using the API. 13610 v8::HandleScope scope; 13611 LocalContext context; 13612 context->Global()->Set(v8_str("cell"), v8_str("value"), v8::DontDelete); 13613 ExpectBoolean("delete cell", false); 13614 CompileRun(function_code); 13615 ExpectString("readCell()", "value"); 13616 ExpectString("readCell()", "value"); 13617 13618 // Delete the cell using the API and check the inlined code works 13619 // correctly. 13620 CHECK(context->Global()->ForceDelete(v8_str("cell"))); 13621 ExpectString("(function() {" 13622 " try {" 13623 " return readCell();" 13624 " } catch(e) {" 13625 " return e.toString();" 13626 " }" 13627 "})()", 13628 "ReferenceError: cell is not defined"); 13629 } 13630 13631 13632 TEST(GlobalLoadICGC) { 13633 const char* function_code = 13634 "function readCell() { while (true) { return cell; } }"; 13635 13636 // Check inline load code for a don't delete cell is cleared during 13637 // GC. 13638 { 13639 v8::HandleScope scope; 13640 LocalContext context; 13641 CompileRun("var cell = \"value\";"); 13642 ExpectBoolean("delete cell", false); 13643 CompileRun(function_code); 13644 ExpectString("readCell()", "value"); 13645 ExpectString("readCell()", "value"); 13646 } 13647 { 13648 v8::HandleScope scope; 13649 LocalContext context2; 13650 // Hold the code object in the second context. 13651 CompileRun(function_code); 13652 CheckSurvivingGlobalObjectsCount(1); 13653 } 13654 13655 // Check inline load code for a deletable cell is cleared during GC. 13656 { 13657 v8::HandleScope scope; 13658 LocalContext context; 13659 CompileRun("cell = \"value\";"); 13660 CompileRun(function_code); 13661 ExpectString("readCell()", "value"); 13662 ExpectString("readCell()", "value"); 13663 } 13664 { 13665 v8::HandleScope scope; 13666 LocalContext context2; 13667 // Hold the code object in the second context. 13668 CompileRun(function_code); 13669 CheckSurvivingGlobalObjectsCount(1); 13670 } 13671 } 13672 13673 13674 TEST(RegExp) { 13675 v8::HandleScope scope; 13676 LocalContext context; 13677 13678 v8::Handle<v8::RegExp> re = v8::RegExp::New(v8_str("foo"), v8::RegExp::kNone); 13679 CHECK(re->IsRegExp()); 13680 CHECK(re->GetSource()->Equals(v8_str("foo"))); 13681 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone); 13682 13683 re = v8::RegExp::New(v8_str("bar"), 13684 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | 13685 v8::RegExp::kGlobal)); 13686 CHECK(re->IsRegExp()); 13687 CHECK(re->GetSource()->Equals(v8_str("bar"))); 13688 CHECK_EQ(static_cast<int>(re->GetFlags()), 13689 v8::RegExp::kIgnoreCase | v8::RegExp::kGlobal); 13690 13691 re = v8::RegExp::New(v8_str("baz"), 13692 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | 13693 v8::RegExp::kMultiline)); 13694 CHECK(re->IsRegExp()); 13695 CHECK(re->GetSource()->Equals(v8_str("baz"))); 13696 CHECK_EQ(static_cast<int>(re->GetFlags()), 13697 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline); 13698 13699 re = CompileRun("/quux/").As<v8::RegExp>(); 13700 CHECK(re->IsRegExp()); 13701 CHECK(re->GetSource()->Equals(v8_str("quux"))); 13702 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone); 13703 13704 re = CompileRun("/quux/gm").As<v8::RegExp>(); 13705 CHECK(re->IsRegExp()); 13706 CHECK(re->GetSource()->Equals(v8_str("quux"))); 13707 CHECK_EQ(static_cast<int>(re->GetFlags()), 13708 v8::RegExp::kGlobal | v8::RegExp::kMultiline); 13709 13710 // Override the RegExp constructor and check the API constructor 13711 // still works. 13712 CompileRun("RegExp = function() {}"); 13713 13714 re = v8::RegExp::New(v8_str("foobar"), v8::RegExp::kNone); 13715 CHECK(re->IsRegExp()); 13716 CHECK(re->GetSource()->Equals(v8_str("foobar"))); 13717 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone); 13718 13719 re = v8::RegExp::New(v8_str("foobarbaz"), 13720 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | 13721 v8::RegExp::kMultiline)); 13722 CHECK(re->IsRegExp()); 13723 CHECK(re->GetSource()->Equals(v8_str("foobarbaz"))); 13724 CHECK_EQ(static_cast<int>(re->GetFlags()), 13725 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline); 13726 13727 context->Global()->Set(v8_str("re"), re); 13728 ExpectTrue("re.test('FoobarbaZ')"); 13729 13730 v8::TryCatch try_catch; 13731 re = v8::RegExp::New(v8_str("foo["), v8::RegExp::kNone); 13732 CHECK(re.IsEmpty()); 13733 CHECK(try_catch.HasCaught()); 13734 context->Global()->Set(v8_str("ex"), try_catch.Exception()); 13735 ExpectTrue("ex instanceof SyntaxError"); 13736 } 13737 13738 13739 THREADED_TEST(Equals) { 13740 v8::HandleScope handleScope; 13741 LocalContext localContext; 13742 13743 v8::Handle<v8::Object> globalProxy = localContext->Global(); 13744 v8::Handle<Value> global = globalProxy->GetPrototype(); 13745 13746 CHECK(global->StrictEquals(global)); 13747 CHECK(!global->StrictEquals(globalProxy)); 13748 CHECK(!globalProxy->StrictEquals(global)); 13749 CHECK(globalProxy->StrictEquals(globalProxy)); 13750 13751 CHECK(global->Equals(global)); 13752 CHECK(!global->Equals(globalProxy)); 13753 CHECK(!globalProxy->Equals(global)); 13754 CHECK(globalProxy->Equals(globalProxy)); 13755 } 13756 13757 13758 static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, 13759 const v8::AccessorInfo& info ) { 13760 return v8_str("42!"); 13761 } 13762 13763 13764 static v8::Handle<v8::Array> Enumerator(const v8::AccessorInfo& info) { 13765 v8::Handle<v8::Array> result = v8::Array::New(); 13766 result->Set(0, v8_str("universalAnswer")); 13767 return result; 13768 } 13769 13770 13771 TEST(NamedEnumeratorAndForIn) { 13772 v8::HandleScope handle_scope; 13773 LocalContext context; 13774 v8::Context::Scope context_scope(context.local()); 13775 13776 v8::Handle<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New(); 13777 tmpl->SetNamedPropertyHandler(Getter, NULL, NULL, NULL, Enumerator); 13778 context->Global()->Set(v8_str("o"), tmpl->NewInstance()); 13779 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 13780 "var result = []; for (var k in o) result.push(k); result")); 13781 CHECK_EQ(1, result->Length()); 13782 CHECK_EQ(v8_str("universalAnswer"), result->Get(0)); 13783 } 13784 13785 13786 TEST(DefinePropertyPostDetach) { 13787 v8::HandleScope scope; 13788 LocalContext context; 13789 v8::Handle<v8::Object> proxy = context->Global(); 13790 v8::Handle<v8::Function> define_property = 13791 CompileRun("(function() {" 13792 " Object.defineProperty(" 13793 " this," 13794 " 1," 13795 " { configurable: true, enumerable: true, value: 3 });" 13796 "})").As<Function>(); 13797 context->DetachGlobal(); 13798 define_property->Call(proxy, 0, NULL); 13799 } 13800 13801 13802 static void InstallContextId(v8::Handle<Context> context, int id) { 13803 Context::Scope scope(context); 13804 CompileRun("Object.prototype").As<Object>()-> 13805 Set(v8_str("context_id"), v8::Integer::New(id)); 13806 } 13807 13808 13809 static void CheckContextId(v8::Handle<Object> object, int expected) { 13810 CHECK_EQ(expected, object->Get(v8_str("context_id"))->Int32Value()); 13811 } 13812 13813 13814 THREADED_TEST(CreationContext) { 13815 HandleScope handle_scope; 13816 Persistent<Context> context1 = Context::New(); 13817 InstallContextId(context1, 1); 13818 Persistent<Context> context2 = Context::New(); 13819 InstallContextId(context2, 2); 13820 Persistent<Context> context3 = Context::New(); 13821 InstallContextId(context3, 3); 13822 13823 Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New(); 13824 13825 Local<Object> object1; 13826 Local<Function> func1; 13827 { 13828 Context::Scope scope(context1); 13829 object1 = Object::New(); 13830 func1 = tmpl->GetFunction(); 13831 } 13832 13833 Local<Object> object2; 13834 Local<Function> func2; 13835 { 13836 Context::Scope scope(context2); 13837 object2 = Object::New(); 13838 func2 = tmpl->GetFunction(); 13839 } 13840 13841 Local<Object> instance1; 13842 Local<Object> instance2; 13843 13844 { 13845 Context::Scope scope(context3); 13846 instance1 = func1->NewInstance(); 13847 instance2 = func2->NewInstance(); 13848 } 13849 13850 CHECK(object1->CreationContext() == context1); 13851 CheckContextId(object1, 1); 13852 CHECK(func1->CreationContext() == context1); 13853 CheckContextId(func1, 1); 13854 CHECK(instance1->CreationContext() == context1); 13855 CheckContextId(instance1, 1); 13856 CHECK(object2->CreationContext() == context2); 13857 CheckContextId(object2, 2); 13858 CHECK(func2->CreationContext() == context2); 13859 CheckContextId(func2, 2); 13860 CHECK(instance2->CreationContext() == context2); 13861 CheckContextId(instance2, 2); 13862 13863 { 13864 Context::Scope scope(context1); 13865 CHECK(object1->CreationContext() == context1); 13866 CheckContextId(object1, 1); 13867 CHECK(func1->CreationContext() == context1); 13868 CheckContextId(func1, 1); 13869 CHECK(instance1->CreationContext() == context1); 13870 CheckContextId(instance1, 1); 13871 CHECK(object2->CreationContext() == context2); 13872 CheckContextId(object2, 2); 13873 CHECK(func2->CreationContext() == context2); 13874 CheckContextId(func2, 2); 13875 CHECK(instance2->CreationContext() == context2); 13876 CheckContextId(instance2, 2); 13877 } 13878 13879 { 13880 Context::Scope scope(context2); 13881 CHECK(object1->CreationContext() == context1); 13882 CheckContextId(object1, 1); 13883 CHECK(func1->CreationContext() == context1); 13884 CheckContextId(func1, 1); 13885 CHECK(instance1->CreationContext() == context1); 13886 CheckContextId(instance1, 1); 13887 CHECK(object2->CreationContext() == context2); 13888 CheckContextId(object2, 2); 13889 CHECK(func2->CreationContext() == context2); 13890 CheckContextId(func2, 2); 13891 CHECK(instance2->CreationContext() == context2); 13892 CheckContextId(instance2, 2); 13893 } 13894 13895 context1.Dispose(); 13896 context2.Dispose(); 13897 context3.Dispose(); 13898 } 13899