1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #include <limits.h> 29 30 #include "v8.h" 31 32 #include "api.h" 33 #include "isolate.h" 34 #include "compilation-cache.h" 35 #include "execution.h" 36 #include "snapshot.h" 37 #include "platform.h" 38 #include "utils.h" 39 #include "cctest.h" 40 #include "parser.h" 41 #include "unicode-inl.h" 42 43 static const bool kLogThreading = false; 44 45 static bool IsNaN(double x) { 46 #ifdef WIN32 47 return _isnan(x); 48 #else 49 return isnan(x); 50 #endif 51 } 52 53 using ::v8::AccessorInfo; 54 using ::v8::Arguments; 55 using ::v8::Context; 56 using ::v8::Extension; 57 using ::v8::Function; 58 using ::v8::FunctionTemplate; 59 using ::v8::Handle; 60 using ::v8::HandleScope; 61 using ::v8::Local; 62 using ::v8::Message; 63 using ::v8::MessageCallback; 64 using ::v8::Object; 65 using ::v8::ObjectTemplate; 66 using ::v8::Persistent; 67 using ::v8::Script; 68 using ::v8::StackTrace; 69 using ::v8::String; 70 using ::v8::TryCatch; 71 using ::v8::Undefined; 72 using ::v8::V8; 73 using ::v8::Value; 74 75 76 static void ExpectString(const char* code, const char* expected) { 77 Local<Value> result = CompileRun(code); 78 CHECK(result->IsString()); 79 String::AsciiValue ascii(result); 80 CHECK_EQ(expected, *ascii); 81 } 82 83 static void ExpectInt32(const char* code, int expected) { 84 Local<Value> result = CompileRun(code); 85 CHECK(result->IsInt32()); 86 CHECK_EQ(expected, result->Int32Value()); 87 } 88 89 static void ExpectBoolean(const char* code, bool expected) { 90 Local<Value> result = CompileRun(code); 91 CHECK(result->IsBoolean()); 92 CHECK_EQ(expected, result->BooleanValue()); 93 } 94 95 96 static void ExpectTrue(const char* code) { 97 ExpectBoolean(code, true); 98 } 99 100 101 static void ExpectFalse(const char* code) { 102 ExpectBoolean(code, false); 103 } 104 105 106 static void ExpectObject(const char* code, Local<Value> expected) { 107 Local<Value> result = CompileRun(code); 108 CHECK(result->Equals(expected)); 109 } 110 111 112 static void ExpectUndefined(const char* code) { 113 Local<Value> result = CompileRun(code); 114 CHECK(result->IsUndefined()); 115 } 116 117 118 static int signature_callback_count; 119 static v8::Handle<Value> IncrementingSignatureCallback( 120 const v8::Arguments& args) { 121 ApiTestFuzzer::Fuzz(); 122 signature_callback_count++; 123 v8::Handle<v8::Array> result = v8::Array::New(args.Length()); 124 for (int i = 0; i < args.Length(); i++) 125 result->Set(v8::Integer::New(i), args[i]); 126 return result; 127 } 128 129 130 static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) { 131 ApiTestFuzzer::Fuzz(); 132 v8::Handle<v8::Array> result = v8::Array::New(args.Length()); 133 for (int i = 0; i < args.Length(); i++) { 134 result->Set(v8::Integer::New(i), args[i]); 135 } 136 return result; 137 } 138 139 140 THREADED_TEST(Handles) { 141 v8::HandleScope scope; 142 Local<Context> local_env; 143 { 144 LocalContext env; 145 local_env = env.local(); 146 } 147 148 // Local context should still be live. 149 CHECK(!local_env.IsEmpty()); 150 local_env->Enter(); 151 152 v8::Handle<v8::Primitive> undef = v8::Undefined(); 153 CHECK(!undef.IsEmpty()); 154 CHECK(undef->IsUndefined()); 155 156 const char* c_source = "1 + 2 + 3"; 157 Local<String> source = String::New(c_source); 158 Local<Script> script = Script::Compile(source); 159 CHECK_EQ(6, script->Run()->Int32Value()); 160 161 local_env->Exit(); 162 } 163 164 165 THREADED_TEST(ReceiverSignature) { 166 v8::HandleScope scope; 167 LocalContext env; 168 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); 169 v8::Handle<v8::Signature> sig = v8::Signature::New(fun); 170 fun->PrototypeTemplate()->Set( 171 v8_str("m"), 172 v8::FunctionTemplate::New(IncrementingSignatureCallback, 173 v8::Handle<Value>(), 174 sig)); 175 env->Global()->Set(v8_str("Fun"), fun->GetFunction()); 176 signature_callback_count = 0; 177 CompileRun( 178 "var o = new Fun();" 179 "o.m();"); 180 CHECK_EQ(1, signature_callback_count); 181 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New(); 182 sub_fun->Inherit(fun); 183 env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction()); 184 CompileRun( 185 "var o = new SubFun();" 186 "o.m();"); 187 CHECK_EQ(2, signature_callback_count); 188 189 v8::TryCatch try_catch; 190 CompileRun( 191 "var o = { };" 192 "o.m = Fun.prototype.m;" 193 "o.m();"); 194 CHECK_EQ(2, signature_callback_count); 195 CHECK(try_catch.HasCaught()); 196 try_catch.Reset(); 197 v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New(); 198 sub_fun->Inherit(fun); 199 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction()); 200 CompileRun( 201 "var o = new UnrelFun();" 202 "o.m = Fun.prototype.m;" 203 "o.m();"); 204 CHECK_EQ(2, signature_callback_count); 205 CHECK(try_catch.HasCaught()); 206 } 207 208 209 THREADED_TEST(ArgumentSignature) { 210 v8::HandleScope scope; 211 LocalContext env; 212 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New(); 213 cons->SetClassName(v8_str("Cons")); 214 v8::Handle<v8::Signature> sig = 215 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons); 216 v8::Handle<v8::FunctionTemplate> fun = 217 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig); 218 env->Global()->Set(v8_str("Cons"), cons->GetFunction()); 219 env->Global()->Set(v8_str("Fun1"), fun->GetFunction()); 220 221 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';"); 222 CHECK(value1->IsTrue()); 223 224 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';"); 225 CHECK(value2->IsTrue()); 226 227 v8::Handle<Value> value3 = CompileRun("Fun1() == '';"); 228 CHECK(value3->IsTrue()); 229 230 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New(); 231 cons1->SetClassName(v8_str("Cons1")); 232 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New(); 233 cons2->SetClassName(v8_str("Cons2")); 234 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New(); 235 cons3->SetClassName(v8_str("Cons3")); 236 237 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 }; 238 v8::Handle<v8::Signature> wsig = 239 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args); 240 v8::Handle<v8::FunctionTemplate> fun2 = 241 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig); 242 243 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction()); 244 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction()); 245 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction()); 246 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction()); 247 v8::Handle<Value> value4 = CompileRun( 248 "Fun2(new Cons1(), new Cons2(), new Cons3()) ==" 249 "'[object Cons1],[object Cons2],[object Cons3]'"); 250 CHECK(value4->IsTrue()); 251 252 v8::Handle<Value> value5 = CompileRun( 253 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'"); 254 CHECK(value5->IsTrue()); 255 256 v8::Handle<Value> value6 = CompileRun( 257 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'"); 258 CHECK(value6->IsTrue()); 259 260 v8::Handle<Value> value7 = CompileRun( 261 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == " 262 "'[object Cons1],[object Cons2],[object Cons3],d';"); 263 CHECK(value7->IsTrue()); 264 265 v8::Handle<Value> value8 = CompileRun( 266 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'"); 267 CHECK(value8->IsTrue()); 268 } 269 270 271 THREADED_TEST(HulIgennem) { 272 v8::HandleScope scope; 273 LocalContext env; 274 v8::Handle<v8::Primitive> undef = v8::Undefined(); 275 Local<String> undef_str = undef->ToString(); 276 char* value = i::NewArray<char>(undef_str->Length() + 1); 277 undef_str->WriteAscii(value); 278 CHECK_EQ(0, strcmp(value, "undefined")); 279 i::DeleteArray(value); 280 } 281 282 283 THREADED_TEST(Access) { 284 v8::HandleScope scope; 285 LocalContext env; 286 Local<v8::Object> obj = v8::Object::New(); 287 Local<Value> foo_before = obj->Get(v8_str("foo")); 288 CHECK(foo_before->IsUndefined()); 289 Local<String> bar_str = v8_str("bar"); 290 obj->Set(v8_str("foo"), bar_str); 291 Local<Value> foo_after = obj->Get(v8_str("foo")); 292 CHECK(!foo_after->IsUndefined()); 293 CHECK(foo_after->IsString()); 294 CHECK_EQ(bar_str, foo_after); 295 } 296 297 298 THREADED_TEST(AccessElement) { 299 v8::HandleScope scope; 300 LocalContext env; 301 Local<v8::Object> obj = v8::Object::New(); 302 Local<Value> before = obj->Get(1); 303 CHECK(before->IsUndefined()); 304 Local<String> bar_str = v8_str("bar"); 305 obj->Set(1, bar_str); 306 Local<Value> after = obj->Get(1); 307 CHECK(!after->IsUndefined()); 308 CHECK(after->IsString()); 309 CHECK_EQ(bar_str, after); 310 311 Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>(); 312 CHECK_EQ(v8_str("a"), value->Get(0)); 313 CHECK_EQ(v8_str("b"), value->Get(1)); 314 } 315 316 317 THREADED_TEST(Script) { 318 v8::HandleScope scope; 319 LocalContext env; 320 const char* c_source = "1 + 2 + 3"; 321 Local<String> source = String::New(c_source); 322 Local<Script> script = Script::Compile(source); 323 CHECK_EQ(6, script->Run()->Int32Value()); 324 } 325 326 327 static uint16_t* AsciiToTwoByteString(const char* source) { 328 int array_length = i::StrLength(source) + 1; 329 uint16_t* converted = i::NewArray<uint16_t>(array_length); 330 for (int i = 0; i < array_length; i++) converted[i] = source[i]; 331 return converted; 332 } 333 334 335 class TestResource: public String::ExternalStringResource { 336 public: 337 explicit TestResource(uint16_t* data, int* counter = NULL) 338 : data_(data), length_(0), counter_(counter) { 339 while (data[length_]) ++length_; 340 } 341 342 ~TestResource() { 343 i::DeleteArray(data_); 344 if (counter_ != NULL) ++*counter_; 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 int* counter_; 358 }; 359 360 361 class TestAsciiResource: public String::ExternalAsciiStringResource { 362 public: 363 explicit TestAsciiResource(const char* data, int* counter = NULL) 364 : data_(data), length_(strlen(data)), counter_(counter) { } 365 366 ~TestAsciiResource() { 367 i::DeleteArray(data_); 368 if (counter_ != NULL) ++*counter_; 369 } 370 371 const char* data() const { 372 return data_; 373 } 374 375 size_t length() const { 376 return length_; 377 } 378 private: 379 const char* data_; 380 size_t length_; 381 int* counter_; 382 }; 383 384 385 THREADED_TEST(ScriptUsingStringResource) { 386 int dispose_count = 0; 387 const char* c_source = "1 + 2 * 3"; 388 uint16_t* two_byte_source = AsciiToTwoByteString(c_source); 389 { 390 v8::HandleScope scope; 391 LocalContext env; 392 TestResource* resource = new TestResource(two_byte_source, &dispose_count); 393 Local<String> source = String::NewExternal(resource); 394 Local<Script> script = Script::Compile(source); 395 Local<Value> value = script->Run(); 396 CHECK(value->IsNumber()); 397 CHECK_EQ(7, value->Int32Value()); 398 CHECK(source->IsExternal()); 399 CHECK_EQ(resource, 400 static_cast<TestResource*>(source->GetExternalStringResource())); 401 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 402 CHECK_EQ(0, dispose_count); 403 } 404 v8::internal::Isolate::Current()->compilation_cache()->Clear(); 405 HEAP->CollectAllAvailableGarbage(); 406 CHECK_EQ(1, dispose_count); 407 } 408 409 410 THREADED_TEST(ScriptUsingAsciiStringResource) { 411 int dispose_count = 0; 412 const char* c_source = "1 + 2 * 3"; 413 { 414 v8::HandleScope scope; 415 LocalContext env; 416 Local<String> source = 417 String::NewExternal(new TestAsciiResource(i::StrDup(c_source), 418 &dispose_count)); 419 Local<Script> script = Script::Compile(source); 420 Local<Value> value = script->Run(); 421 CHECK(value->IsNumber()); 422 CHECK_EQ(7, value->Int32Value()); 423 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 424 CHECK_EQ(0, dispose_count); 425 } 426 i::Isolate::Current()->compilation_cache()->Clear(); 427 HEAP->CollectAllAvailableGarbage(); 428 CHECK_EQ(1, dispose_count); 429 } 430 431 432 THREADED_TEST(ScriptMakingExternalString) { 433 int dispose_count = 0; 434 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3"); 435 { 436 v8::HandleScope scope; 437 LocalContext env; 438 Local<String> source = String::New(two_byte_source); 439 // Trigger GCs so that the newly allocated string moves to old gen. 440 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now 441 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now 442 bool success = source->MakeExternal(new TestResource(two_byte_source, 443 &dispose_count)); 444 CHECK(success); 445 Local<Script> script = Script::Compile(source); 446 Local<Value> value = script->Run(); 447 CHECK(value->IsNumber()); 448 CHECK_EQ(7, value->Int32Value()); 449 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 450 CHECK_EQ(0, dispose_count); 451 } 452 i::Isolate::Current()->compilation_cache()->Clear(); 453 HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 454 CHECK_EQ(1, dispose_count); 455 } 456 457 458 THREADED_TEST(ScriptMakingExternalAsciiString) { 459 int dispose_count = 0; 460 const char* c_source = "1 + 2 * 3"; 461 { 462 v8::HandleScope scope; 463 LocalContext env; 464 Local<String> source = v8_str(c_source); 465 // Trigger GCs so that the newly allocated string moves to old gen. 466 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now 467 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now 468 bool success = source->MakeExternal( 469 new TestAsciiResource(i::StrDup(c_source), &dispose_count)); 470 CHECK(success); 471 Local<Script> script = Script::Compile(source); 472 Local<Value> value = script->Run(); 473 CHECK(value->IsNumber()); 474 CHECK_EQ(7, value->Int32Value()); 475 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 476 CHECK_EQ(0, dispose_count); 477 } 478 i::Isolate::Current()->compilation_cache()->Clear(); 479 HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 480 CHECK_EQ(1, dispose_count); 481 } 482 483 484 TEST(MakingExternalStringConditions) { 485 v8::HandleScope scope; 486 LocalContext env; 487 488 // Free some space in the new space so that we can check freshness. 489 HEAP->CollectGarbage(i::NEW_SPACE); 490 HEAP->CollectGarbage(i::NEW_SPACE); 491 492 uint16_t* two_byte_string = AsciiToTwoByteString("s1"); 493 Local<String> small_string = String::New(two_byte_string); 494 i::DeleteArray(two_byte_string); 495 496 // We should refuse to externalize newly created small string. 497 CHECK(!small_string->CanMakeExternal()); 498 // Trigger GCs so that the newly allocated string moves to old gen. 499 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now 500 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now 501 // Old space strings should be accepted. 502 CHECK(small_string->CanMakeExternal()); 503 504 two_byte_string = AsciiToTwoByteString("small string 2"); 505 small_string = String::New(two_byte_string); 506 i::DeleteArray(two_byte_string); 507 508 // We should refuse externalizing newly created small string. 509 CHECK(!small_string->CanMakeExternal()); 510 for (int i = 0; i < 100; i++) { 511 String::Value value(small_string); 512 } 513 // Frequently used strings should be accepted. 514 CHECK(small_string->CanMakeExternal()); 515 516 const int buf_size = 10 * 1024; 517 char* buf = i::NewArray<char>(buf_size); 518 memset(buf, 'a', buf_size); 519 buf[buf_size - 1] = '\0'; 520 521 two_byte_string = AsciiToTwoByteString(buf); 522 Local<String> large_string = String::New(two_byte_string); 523 i::DeleteArray(buf); 524 i::DeleteArray(two_byte_string); 525 // Large strings should be immediately accepted. 526 CHECK(large_string->CanMakeExternal()); 527 } 528 529 530 TEST(MakingExternalAsciiStringConditions) { 531 v8::HandleScope scope; 532 LocalContext env; 533 534 // Free some space in the new space so that we can check freshness. 535 HEAP->CollectGarbage(i::NEW_SPACE); 536 HEAP->CollectGarbage(i::NEW_SPACE); 537 538 Local<String> small_string = String::New("s1"); 539 // We should refuse to externalize newly created small string. 540 CHECK(!small_string->CanMakeExternal()); 541 // Trigger GCs so that the newly allocated string moves to old gen. 542 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now 543 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now 544 // Old space strings should be accepted. 545 CHECK(small_string->CanMakeExternal()); 546 547 small_string = String::New("small string 2"); 548 // We should refuse externalizing newly created small string. 549 CHECK(!small_string->CanMakeExternal()); 550 for (int i = 0; i < 100; i++) { 551 String::Value value(small_string); 552 } 553 // Frequently used strings should be accepted. 554 CHECK(small_string->CanMakeExternal()); 555 556 const int buf_size = 10 * 1024; 557 char* buf = i::NewArray<char>(buf_size); 558 memset(buf, 'a', buf_size); 559 buf[buf_size - 1] = '\0'; 560 Local<String> large_string = String::New(buf); 561 i::DeleteArray(buf); 562 // Large strings should be immediately accepted. 563 CHECK(large_string->CanMakeExternal()); 564 } 565 566 567 THREADED_TEST(UsingExternalString) { 568 { 569 v8::HandleScope scope; 570 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 571 Local<String> string = 572 String::NewExternal(new TestResource(two_byte_string)); 573 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 574 // Trigger GCs so that the newly allocated string moves to old gen. 575 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now 576 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now 577 i::Handle<i::String> isymbol = FACTORY->SymbolFromString(istring); 578 CHECK(isymbol->IsSymbol()); 579 } 580 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 581 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 582 } 583 584 585 THREADED_TEST(UsingExternalAsciiString) { 586 { 587 v8::HandleScope scope; 588 const char* one_byte_string = "test string"; 589 Local<String> string = String::NewExternal( 590 new TestAsciiResource(i::StrDup(one_byte_string))); 591 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 592 // Trigger GCs so that the newly allocated string moves to old gen. 593 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now 594 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now 595 i::Handle<i::String> isymbol = FACTORY->SymbolFromString(istring); 596 CHECK(isymbol->IsSymbol()); 597 } 598 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 599 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 600 } 601 602 603 THREADED_TEST(ScavengeExternalString) { 604 int dispose_count = 0; 605 bool in_new_space = false; 606 { 607 v8::HandleScope scope; 608 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 609 Local<String> string = 610 String::NewExternal(new TestResource(two_byte_string, 611 &dispose_count)); 612 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 613 HEAP->CollectGarbage(i::NEW_SPACE); 614 in_new_space = HEAP->InNewSpace(*istring); 615 CHECK(in_new_space || HEAP->old_data_space()->Contains(*istring)); 616 CHECK_EQ(0, dispose_count); 617 } 618 HEAP->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); 619 CHECK_EQ(1, dispose_count); 620 } 621 622 623 THREADED_TEST(ScavengeExternalAsciiString) { 624 int dispose_count = 0; 625 bool in_new_space = false; 626 { 627 v8::HandleScope scope; 628 const char* one_byte_string = "test string"; 629 Local<String> string = String::NewExternal( 630 new TestAsciiResource(i::StrDup(one_byte_string), &dispose_count)); 631 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 632 HEAP->CollectGarbage(i::NEW_SPACE); 633 in_new_space = HEAP->InNewSpace(*istring); 634 CHECK(in_new_space || HEAP->old_data_space()->Contains(*istring)); 635 CHECK_EQ(0, dispose_count); 636 } 637 HEAP->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); 638 CHECK_EQ(1, dispose_count); 639 } 640 641 642 class TestAsciiResourceWithDisposeControl: public TestAsciiResource { 643 public: 644 // Only used by non-threaded tests, so it can use static fields. 645 static int dispose_calls; 646 static int dispose_count; 647 648 TestAsciiResourceWithDisposeControl(const char* data, bool dispose) 649 : TestAsciiResource(data, &dispose_count), 650 dispose_(dispose) { } 651 652 void Dispose() { 653 ++dispose_calls; 654 if (dispose_) delete this; 655 } 656 private: 657 bool dispose_; 658 }; 659 660 661 int TestAsciiResourceWithDisposeControl::dispose_count = 0; 662 int TestAsciiResourceWithDisposeControl::dispose_calls = 0; 663 664 665 TEST(ExternalStringWithDisposeHandling) { 666 const char* c_source = "1 + 2 * 3"; 667 668 // Use a stack allocated external string resource allocated object. 669 TestAsciiResourceWithDisposeControl::dispose_count = 0; 670 TestAsciiResourceWithDisposeControl::dispose_calls = 0; 671 TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false); 672 { 673 v8::HandleScope scope; 674 LocalContext env; 675 Local<String> source = String::NewExternal(&res_stack); 676 Local<Script> script = Script::Compile(source); 677 Local<Value> value = script->Run(); 678 CHECK(value->IsNumber()); 679 CHECK_EQ(7, value->Int32Value()); 680 HEAP->CollectAllAvailableGarbage(); 681 CHECK_EQ(0, TestAsciiResourceWithDisposeControl::dispose_count); 682 } 683 i::Isolate::Current()->compilation_cache()->Clear(); 684 HEAP->CollectAllAvailableGarbage(); 685 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); 686 CHECK_EQ(0, TestAsciiResourceWithDisposeControl::dispose_count); 687 688 // Use a heap allocated external string resource allocated object. 689 TestAsciiResourceWithDisposeControl::dispose_count = 0; 690 TestAsciiResourceWithDisposeControl::dispose_calls = 0; 691 TestAsciiResource* res_heap = 692 new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true); 693 { 694 v8::HandleScope scope; 695 LocalContext env; 696 Local<String> source = String::NewExternal(res_heap); 697 Local<Script> script = Script::Compile(source); 698 Local<Value> value = script->Run(); 699 CHECK(value->IsNumber()); 700 CHECK_EQ(7, value->Int32Value()); 701 HEAP->CollectAllAvailableGarbage(); 702 CHECK_EQ(0, TestAsciiResourceWithDisposeControl::dispose_count); 703 } 704 i::Isolate::Current()->compilation_cache()->Clear(); 705 HEAP->CollectAllAvailableGarbage(); 706 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); 707 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_count); 708 } 709 710 711 THREADED_TEST(StringConcat) { 712 { 713 v8::HandleScope scope; 714 LocalContext env; 715 const char* one_byte_string_1 = "function a_times_t"; 716 const char* two_byte_string_1 = "wo_plus_b(a, b) {return "; 717 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + "; 718 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + "; 719 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 720 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 721 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);"; 722 Local<String> left = v8_str(one_byte_string_1); 723 724 uint16_t* two_byte_source = AsciiToTwoByteString(two_byte_string_1); 725 Local<String> right = String::New(two_byte_source); 726 i::DeleteArray(two_byte_source); 727 728 Local<String> source = String::Concat(left, right); 729 right = String::NewExternal( 730 new TestAsciiResource(i::StrDup(one_byte_extern_1))); 731 source = String::Concat(source, right); 732 right = String::NewExternal( 733 new TestResource(AsciiToTwoByteString(two_byte_extern_1))); 734 source = String::Concat(source, right); 735 right = v8_str(one_byte_string_2); 736 source = String::Concat(source, right); 737 738 two_byte_source = AsciiToTwoByteString(two_byte_string_2); 739 right = String::New(two_byte_source); 740 i::DeleteArray(two_byte_source); 741 742 source = String::Concat(source, right); 743 right = String::NewExternal( 744 new TestResource(AsciiToTwoByteString(two_byte_extern_2))); 745 source = String::Concat(source, right); 746 Local<Script> script = Script::Compile(source); 747 Local<Value> value = script->Run(); 748 CHECK(value->IsNumber()); 749 CHECK_EQ(68, value->Int32Value()); 750 } 751 i::Isolate::Current()->compilation_cache()->Clear(); 752 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 753 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 754 } 755 756 757 THREADED_TEST(GlobalProperties) { 758 v8::HandleScope scope; 759 LocalContext env; 760 v8::Handle<v8::Object> global = env->Global(); 761 global->Set(v8_str("pi"), v8_num(3.1415926)); 762 Local<Value> pi = global->Get(v8_str("pi")); 763 CHECK_EQ(3.1415926, pi->NumberValue()); 764 } 765 766 767 static v8::Handle<Value> handle_call(const v8::Arguments& args) { 768 ApiTestFuzzer::Fuzz(); 769 return v8_num(102); 770 } 771 772 773 static v8::Handle<Value> construct_call(const v8::Arguments& args) { 774 ApiTestFuzzer::Fuzz(); 775 args.This()->Set(v8_str("x"), v8_num(1)); 776 args.This()->Set(v8_str("y"), v8_num(2)); 777 return args.This(); 778 } 779 780 static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) { 781 ApiTestFuzzer::Fuzz(); 782 return v8_num(239); 783 } 784 785 786 THREADED_TEST(FunctionTemplate) { 787 v8::HandleScope scope; 788 LocalContext env; 789 { 790 Local<v8::FunctionTemplate> fun_templ = 791 v8::FunctionTemplate::New(handle_call); 792 Local<Function> fun = fun_templ->GetFunction(); 793 env->Global()->Set(v8_str("obj"), fun); 794 Local<Script> script = v8_compile("obj()"); 795 CHECK_EQ(102, script->Run()->Int32Value()); 796 } 797 // Use SetCallHandler to initialize a function template, should work like the 798 // previous one. 799 { 800 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 801 fun_templ->SetCallHandler(handle_call); 802 Local<Function> fun = fun_templ->GetFunction(); 803 env->Global()->Set(v8_str("obj"), fun); 804 Local<Script> script = v8_compile("obj()"); 805 CHECK_EQ(102, script->Run()->Int32Value()); 806 } 807 // Test constructor calls. 808 { 809 Local<v8::FunctionTemplate> fun_templ = 810 v8::FunctionTemplate::New(construct_call); 811 fun_templ->SetClassName(v8_str("funky")); 812 fun_templ->InstanceTemplate()->SetAccessor(v8_str("m"), Return239); 813 Local<Function> fun = fun_templ->GetFunction(); 814 env->Global()->Set(v8_str("obj"), fun); 815 Local<Script> script = v8_compile("var s = new obj(); s.x"); 816 CHECK_EQ(1, script->Run()->Int32Value()); 817 818 Local<Value> result = v8_compile("(new obj()).toString()")->Run(); 819 CHECK_EQ(v8_str("[object funky]"), result); 820 821 result = v8_compile("(new obj()).m")->Run(); 822 CHECK_EQ(239, result->Int32Value()); 823 } 824 } 825 826 827 static void* expected_ptr; 828 static v8::Handle<v8::Value> callback(const v8::Arguments& args) { 829 void* ptr = v8::External::Unwrap(args.Data()); 830 CHECK_EQ(expected_ptr, ptr); 831 return v8::True(); 832 } 833 834 835 static void TestExternalPointerWrapping() { 836 v8::HandleScope scope; 837 LocalContext env; 838 839 v8::Handle<v8::Value> data = v8::External::Wrap(expected_ptr); 840 841 v8::Handle<v8::Object> obj = v8::Object::New(); 842 obj->Set(v8_str("func"), 843 v8::FunctionTemplate::New(callback, data)->GetFunction()); 844 env->Global()->Set(v8_str("obj"), obj); 845 846 CHECK(CompileRun( 847 "function foo() {\n" 848 " for (var i = 0; i < 13; i++) obj.func();\n" 849 "}\n" 850 "foo(), true")->BooleanValue()); 851 } 852 853 854 THREADED_TEST(ExternalWrap) { 855 // Check heap allocated object. 856 int* ptr = new int; 857 expected_ptr = ptr; 858 TestExternalPointerWrapping(); 859 delete ptr; 860 861 // Check stack allocated object. 862 int foo; 863 expected_ptr = &foo; 864 TestExternalPointerWrapping(); 865 866 // Check not aligned addresses. 867 const int n = 100; 868 char* s = new char[n]; 869 for (int i = 0; i < n; i++) { 870 expected_ptr = s + i; 871 TestExternalPointerWrapping(); 872 } 873 874 delete[] s; 875 876 // Check several invalid addresses. 877 expected_ptr = reinterpret_cast<void*>(1); 878 TestExternalPointerWrapping(); 879 880 expected_ptr = reinterpret_cast<void*>(0xdeadbeef); 881 TestExternalPointerWrapping(); 882 883 expected_ptr = reinterpret_cast<void*>(0xdeadbeef + 1); 884 TestExternalPointerWrapping(); 885 886 #if defined(V8_HOST_ARCH_X64) 887 // Check a value with a leading 1 bit in x64 Smi encoding. 888 expected_ptr = reinterpret_cast<void*>(0x400000000); 889 TestExternalPointerWrapping(); 890 891 expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef); 892 TestExternalPointerWrapping(); 893 894 expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef + 1); 895 TestExternalPointerWrapping(); 896 #endif 897 } 898 899 900 THREADED_TEST(FindInstanceInPrototypeChain) { 901 v8::HandleScope scope; 902 LocalContext env; 903 904 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New(); 905 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New(); 906 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New(); 907 derived->Inherit(base); 908 909 Local<v8::Function> base_function = base->GetFunction(); 910 Local<v8::Function> derived_function = derived->GetFunction(); 911 Local<v8::Function> other_function = other->GetFunction(); 912 913 Local<v8::Object> base_instance = base_function->NewInstance(); 914 Local<v8::Object> derived_instance = derived_function->NewInstance(); 915 Local<v8::Object> derived_instance2 = derived_function->NewInstance(); 916 Local<v8::Object> other_instance = other_function->NewInstance(); 917 derived_instance2->Set(v8_str("__proto__"), derived_instance); 918 other_instance->Set(v8_str("__proto__"), derived_instance2); 919 920 // base_instance is only an instance of base. 921 CHECK_EQ(base_instance, 922 base_instance->FindInstanceInPrototypeChain(base)); 923 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty()); 924 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 925 926 // derived_instance is an instance of base and derived. 927 CHECK_EQ(derived_instance, 928 derived_instance->FindInstanceInPrototypeChain(base)); 929 CHECK_EQ(derived_instance, 930 derived_instance->FindInstanceInPrototypeChain(derived)); 931 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 932 933 // other_instance is an instance of other and its immediate 934 // prototype derived_instance2 is an instance of base and derived. 935 // Note, derived_instance is an instance of base and derived too, 936 // but it comes after derived_instance2 in the prototype chain of 937 // other_instance. 938 CHECK_EQ(derived_instance2, 939 other_instance->FindInstanceInPrototypeChain(base)); 940 CHECK_EQ(derived_instance2, 941 other_instance->FindInstanceInPrototypeChain(derived)); 942 CHECK_EQ(other_instance, 943 other_instance->FindInstanceInPrototypeChain(other)); 944 } 945 946 947 THREADED_TEST(TinyInteger) { 948 v8::HandleScope scope; 949 LocalContext env; 950 int32_t value = 239; 951 Local<v8::Integer> value_obj = v8::Integer::New(value); 952 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 953 } 954 955 956 THREADED_TEST(BigSmiInteger) { 957 v8::HandleScope scope; 958 LocalContext env; 959 int32_t value = i::Smi::kMaxValue; 960 // We cannot add one to a Smi::kMaxValue without wrapping. 961 if (i::kSmiValueSize < 32) { 962 CHECK(i::Smi::IsValid(value)); 963 CHECK(!i::Smi::IsValid(value + 1)); 964 Local<v8::Integer> value_obj = v8::Integer::New(value); 965 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 966 } 967 } 968 969 970 THREADED_TEST(BigInteger) { 971 v8::HandleScope scope; 972 LocalContext env; 973 // We cannot add one to a Smi::kMaxValue without wrapping. 974 if (i::kSmiValueSize < 32) { 975 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1. 976 // The code will not be run in that case, due to the "if" guard. 977 int32_t value = 978 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1); 979 CHECK(value > i::Smi::kMaxValue); 980 CHECK(!i::Smi::IsValid(value)); 981 Local<v8::Integer> value_obj = v8::Integer::New(value); 982 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 983 } 984 } 985 986 987 THREADED_TEST(TinyUnsignedInteger) { 988 v8::HandleScope scope; 989 LocalContext env; 990 uint32_t value = 239; 991 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 992 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 993 } 994 995 996 THREADED_TEST(BigUnsignedSmiInteger) { 997 v8::HandleScope scope; 998 LocalContext env; 999 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue); 1000 CHECK(i::Smi::IsValid(value)); 1001 CHECK(!i::Smi::IsValid(value + 1)); 1002 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 1003 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1004 } 1005 1006 1007 THREADED_TEST(BigUnsignedInteger) { 1008 v8::HandleScope scope; 1009 LocalContext env; 1010 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1; 1011 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue)); 1012 CHECK(!i::Smi::IsValid(value)); 1013 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 1014 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1015 } 1016 1017 1018 THREADED_TEST(OutOfSignedRangeUnsignedInteger) { 1019 v8::HandleScope scope; 1020 LocalContext env; 1021 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1; 1022 uint32_t value = INT32_MAX_AS_UINT + 1; 1023 CHECK(value > INT32_MAX_AS_UINT); // No overflow. 1024 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 1025 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1026 } 1027 1028 1029 THREADED_TEST(IsNativeError) { 1030 v8::HandleScope scope; 1031 LocalContext env; 1032 v8::Handle<Value> syntax_error = CompileRun( 1033 "var out = 0; try { eval(\"#\"); } catch(x) { out = x; } out; "); 1034 CHECK(syntax_error->IsNativeError()); 1035 v8::Handle<Value> not_error = CompileRun("{a:42}"); 1036 CHECK(!not_error->IsNativeError()); 1037 v8::Handle<Value> not_object = CompileRun("42"); 1038 CHECK(!not_object->IsNativeError()); 1039 } 1040 1041 1042 THREADED_TEST(StringObject) { 1043 v8::HandleScope scope; 1044 LocalContext env; 1045 v8::Handle<Value> boxed_string = CompileRun("new String(\"test\")"); 1046 CHECK(boxed_string->IsStringObject()); 1047 v8::Handle<Value> unboxed_string = CompileRun("\"test\""); 1048 CHECK(!unboxed_string->IsStringObject()); 1049 v8::Handle<Value> boxed_not_string = CompileRun("new Number(42)"); 1050 CHECK(!boxed_not_string->IsStringObject()); 1051 v8::Handle<Value> not_object = CompileRun("0"); 1052 CHECK(!not_object->IsStringObject()); 1053 v8::Handle<v8::StringObject> as_boxed = boxed_string.As<v8::StringObject>(); 1054 CHECK(!as_boxed.IsEmpty()); 1055 Local<v8::String> the_string = as_boxed->StringValue(); 1056 CHECK(!the_string.IsEmpty()); 1057 ExpectObject("\"test\"", the_string); 1058 v8::Handle<v8::Value> new_boxed_string = v8::StringObject::New(the_string); 1059 CHECK(new_boxed_string->IsStringObject()); 1060 as_boxed = new_boxed_string.As<v8::StringObject>(); 1061 the_string = as_boxed->StringValue(); 1062 CHECK(!the_string.IsEmpty()); 1063 ExpectObject("\"test\"", the_string); 1064 } 1065 1066 1067 THREADED_TEST(NumberObject) { 1068 v8::HandleScope scope; 1069 LocalContext env; 1070 v8::Handle<Value> boxed_number = CompileRun("new Number(42)"); 1071 CHECK(boxed_number->IsNumberObject()); 1072 v8::Handle<Value> unboxed_number = CompileRun("42"); 1073 CHECK(!unboxed_number->IsNumberObject()); 1074 v8::Handle<Value> boxed_not_number = CompileRun("new Boolean(false)"); 1075 CHECK(!boxed_not_number->IsNumberObject()); 1076 v8::Handle<v8::NumberObject> as_boxed = boxed_number.As<v8::NumberObject>(); 1077 CHECK(!as_boxed.IsEmpty()); 1078 double the_number = as_boxed->NumberValue(); 1079 CHECK_EQ(42.0, the_number); 1080 v8::Handle<v8::Value> new_boxed_number = v8::NumberObject::New(43); 1081 CHECK(new_boxed_number->IsNumberObject()); 1082 as_boxed = new_boxed_number.As<v8::NumberObject>(); 1083 the_number = as_boxed->NumberValue(); 1084 CHECK_EQ(43.0, the_number); 1085 } 1086 1087 1088 THREADED_TEST(BooleanObject) { 1089 v8::HandleScope scope; 1090 LocalContext env; 1091 v8::Handle<Value> boxed_boolean = CompileRun("new Boolean(true)"); 1092 CHECK(boxed_boolean->IsBooleanObject()); 1093 v8::Handle<Value> unboxed_boolean = CompileRun("true"); 1094 CHECK(!unboxed_boolean->IsBooleanObject()); 1095 v8::Handle<Value> boxed_not_boolean = CompileRun("new Number(42)"); 1096 CHECK(!boxed_not_boolean->IsBooleanObject()); 1097 v8::Handle<v8::BooleanObject> as_boxed = 1098 boxed_boolean.As<v8::BooleanObject>(); 1099 CHECK(!as_boxed.IsEmpty()); 1100 bool the_boolean = as_boxed->BooleanValue(); 1101 CHECK_EQ(true, the_boolean); 1102 v8::Handle<v8::Value> boxed_true = v8::BooleanObject::New(true); 1103 v8::Handle<v8::Value> boxed_false = v8::BooleanObject::New(false); 1104 CHECK(boxed_true->IsBooleanObject()); 1105 CHECK(boxed_false->IsBooleanObject()); 1106 as_boxed = boxed_true.As<v8::BooleanObject>(); 1107 CHECK_EQ(true, as_boxed->BooleanValue()); 1108 as_boxed = boxed_false.As<v8::BooleanObject>(); 1109 CHECK_EQ(false, as_boxed->BooleanValue()); 1110 } 1111 1112 1113 THREADED_TEST(Number) { 1114 v8::HandleScope scope; 1115 LocalContext env; 1116 double PI = 3.1415926; 1117 Local<v8::Number> pi_obj = v8::Number::New(PI); 1118 CHECK_EQ(PI, pi_obj->NumberValue()); 1119 } 1120 1121 1122 THREADED_TEST(ToNumber) { 1123 v8::HandleScope scope; 1124 LocalContext env; 1125 Local<String> str = v8_str("3.1415926"); 1126 CHECK_EQ(3.1415926, str->NumberValue()); 1127 v8::Handle<v8::Boolean> t = v8::True(); 1128 CHECK_EQ(1.0, t->NumberValue()); 1129 v8::Handle<v8::Boolean> f = v8::False(); 1130 CHECK_EQ(0.0, f->NumberValue()); 1131 } 1132 1133 1134 THREADED_TEST(Date) { 1135 v8::HandleScope scope; 1136 LocalContext env; 1137 double PI = 3.1415926; 1138 Local<Value> date = v8::Date::New(PI); 1139 CHECK_EQ(3.0, date->NumberValue()); 1140 date.As<v8::Date>()->Set(v8_str("property"), v8::Integer::New(42)); 1141 CHECK_EQ(42, date.As<v8::Date>()->Get(v8_str("property"))->Int32Value()); 1142 } 1143 1144 1145 THREADED_TEST(Boolean) { 1146 v8::HandleScope scope; 1147 LocalContext env; 1148 v8::Handle<v8::Boolean> t = v8::True(); 1149 CHECK(t->Value()); 1150 v8::Handle<v8::Boolean> f = v8::False(); 1151 CHECK(!f->Value()); 1152 v8::Handle<v8::Primitive> u = v8::Undefined(); 1153 CHECK(!u->BooleanValue()); 1154 v8::Handle<v8::Primitive> n = v8::Null(); 1155 CHECK(!n->BooleanValue()); 1156 v8::Handle<String> str1 = v8_str(""); 1157 CHECK(!str1->BooleanValue()); 1158 v8::Handle<String> str2 = v8_str("x"); 1159 CHECK(str2->BooleanValue()); 1160 CHECK(!v8::Number::New(0)->BooleanValue()); 1161 CHECK(v8::Number::New(-1)->BooleanValue()); 1162 CHECK(v8::Number::New(1)->BooleanValue()); 1163 CHECK(v8::Number::New(42)->BooleanValue()); 1164 CHECK(!v8_compile("NaN")->Run()->BooleanValue()); 1165 } 1166 1167 1168 static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) { 1169 ApiTestFuzzer::Fuzz(); 1170 return v8_num(13.4); 1171 } 1172 1173 1174 static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) { 1175 ApiTestFuzzer::Fuzz(); 1176 return v8_num(876); 1177 } 1178 1179 1180 THREADED_TEST(GlobalPrototype) { 1181 v8::HandleScope scope; 1182 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 1183 func_templ->PrototypeTemplate()->Set( 1184 "dummy", 1185 v8::FunctionTemplate::New(DummyCallHandler)); 1186 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate(); 1187 templ->Set("x", v8_num(200)); 1188 templ->SetAccessor(v8_str("m"), GetM); 1189 LocalContext env(0, templ); 1190 v8::Handle<Script> script(v8_compile("dummy()")); 1191 v8::Handle<Value> result(script->Run()); 1192 CHECK_EQ(13.4, result->NumberValue()); 1193 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value()); 1194 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value()); 1195 } 1196 1197 1198 THREADED_TEST(ObjectTemplate) { 1199 v8::HandleScope scope; 1200 Local<ObjectTemplate> templ1 = ObjectTemplate::New(); 1201 templ1->Set("x", v8_num(10)); 1202 templ1->Set("y", v8_num(13)); 1203 LocalContext env; 1204 Local<v8::Object> instance1 = templ1->NewInstance(); 1205 env->Global()->Set(v8_str("p"), instance1); 1206 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue()); 1207 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue()); 1208 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); 1209 fun->PrototypeTemplate()->Set("nirk", v8_num(123)); 1210 Local<ObjectTemplate> templ2 = fun->InstanceTemplate(); 1211 templ2->Set("a", v8_num(12)); 1212 templ2->Set("b", templ1); 1213 Local<v8::Object> instance2 = templ2->NewInstance(); 1214 env->Global()->Set(v8_str("q"), instance2); 1215 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue()); 1216 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue()); 1217 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue()); 1218 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue()); 1219 } 1220 1221 1222 static v8::Handle<Value> GetFlabby(const v8::Arguments& args) { 1223 ApiTestFuzzer::Fuzz(); 1224 return v8_num(17.2); 1225 } 1226 1227 1228 static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) { 1229 ApiTestFuzzer::Fuzz(); 1230 return v8_num(15.2); 1231 } 1232 1233 1234 THREADED_TEST(DescriptorInheritance) { 1235 v8::HandleScope scope; 1236 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New(); 1237 super->PrototypeTemplate()->Set("flabby", 1238 v8::FunctionTemplate::New(GetFlabby)); 1239 super->PrototypeTemplate()->Set("PI", v8_num(3.14)); 1240 1241 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd); 1242 1243 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New(); 1244 base1->Inherit(super); 1245 base1->PrototypeTemplate()->Set("v1", v8_num(20.1)); 1246 1247 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New(); 1248 base2->Inherit(super); 1249 base2->PrototypeTemplate()->Set("v2", v8_num(10.1)); 1250 1251 LocalContext env; 1252 1253 env->Global()->Set(v8_str("s"), super->GetFunction()); 1254 env->Global()->Set(v8_str("base1"), base1->GetFunction()); 1255 env->Global()->Set(v8_str("base2"), base2->GetFunction()); 1256 1257 // Checks right __proto__ chain. 1258 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue()); 1259 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue()); 1260 1261 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue()); 1262 1263 // Instance accessor should not be visible on function object or its prototype 1264 CHECK(CompileRun("s.knurd == undefined")->BooleanValue()); 1265 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue()); 1266 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue()); 1267 1268 env->Global()->Set(v8_str("obj"), 1269 base1->GetFunction()->NewInstance()); 1270 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue()); 1271 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue()); 1272 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue()); 1273 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue()); 1274 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue()); 1275 1276 env->Global()->Set(v8_str("obj2"), 1277 base2->GetFunction()->NewInstance()); 1278 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue()); 1279 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue()); 1280 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue()); 1281 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue()); 1282 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue()); 1283 1284 // base1 and base2 cannot cross reference to each's prototype 1285 CHECK(v8_compile("obj.v2")->Run()->IsUndefined()); 1286 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined()); 1287 } 1288 1289 1290 int echo_named_call_count; 1291 1292 1293 static v8::Handle<Value> EchoNamedProperty(Local<String> name, 1294 const AccessorInfo& info) { 1295 ApiTestFuzzer::Fuzz(); 1296 CHECK_EQ(v8_str("data"), info.Data()); 1297 echo_named_call_count++; 1298 return name; 1299 } 1300 1301 // Helper functions for Interceptor/Accessor interaction tests 1302 1303 Handle<Value> SimpleAccessorGetter(Local<String> name, 1304 const AccessorInfo& info) { 1305 Handle<Object> self = info.This(); 1306 return self->Get(String::Concat(v8_str("accessor_"), name)); 1307 } 1308 1309 void SimpleAccessorSetter(Local<String> name, Local<Value> value, 1310 const AccessorInfo& info) { 1311 Handle<Object> self = info.This(); 1312 self->Set(String::Concat(v8_str("accessor_"), name), value); 1313 } 1314 1315 Handle<Value> EmptyInterceptorGetter(Local<String> name, 1316 const AccessorInfo& info) { 1317 return Handle<Value>(); 1318 } 1319 1320 Handle<Value> EmptyInterceptorSetter(Local<String> name, 1321 Local<Value> value, 1322 const AccessorInfo& info) { 1323 return Handle<Value>(); 1324 } 1325 1326 Handle<Value> InterceptorGetter(Local<String> name, 1327 const AccessorInfo& info) { 1328 // Intercept names that start with 'interceptor_'. 1329 String::AsciiValue ascii(name); 1330 char* name_str = *ascii; 1331 char prefix[] = "interceptor_"; 1332 int i; 1333 for (i = 0; name_str[i] && prefix[i]; ++i) { 1334 if (name_str[i] != prefix[i]) return Handle<Value>(); 1335 } 1336 Handle<Object> self = info.This(); 1337 return self->GetHiddenValue(v8_str(name_str + i)); 1338 } 1339 1340 Handle<Value> InterceptorSetter(Local<String> name, 1341 Local<Value> value, 1342 const AccessorInfo& info) { 1343 // Intercept accesses that set certain integer values. 1344 if (value->IsInt32() && value->Int32Value() < 10000) { 1345 Handle<Object> self = info.This(); 1346 self->SetHiddenValue(name, value); 1347 return value; 1348 } 1349 return Handle<Value>(); 1350 } 1351 1352 void AddAccessor(Handle<FunctionTemplate> templ, 1353 Handle<String> name, 1354 v8::AccessorGetter getter, 1355 v8::AccessorSetter setter) { 1356 templ->PrototypeTemplate()->SetAccessor(name, getter, setter); 1357 } 1358 1359 void AddInterceptor(Handle<FunctionTemplate> templ, 1360 v8::NamedPropertyGetter getter, 1361 v8::NamedPropertySetter setter) { 1362 templ->InstanceTemplate()->SetNamedPropertyHandler(getter, setter); 1363 } 1364 1365 THREADED_TEST(EmptyInterceptorDoesNotShadowAccessors) { 1366 v8::HandleScope scope; 1367 Handle<FunctionTemplate> parent = FunctionTemplate::New(); 1368 Handle<FunctionTemplate> child = FunctionTemplate::New(); 1369 child->Inherit(parent); 1370 AddAccessor(parent, v8_str("age"), 1371 SimpleAccessorGetter, SimpleAccessorSetter); 1372 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); 1373 LocalContext env; 1374 env->Global()->Set(v8_str("Child"), child->GetFunction()); 1375 CompileRun("var child = new Child;" 1376 "child.age = 10;"); 1377 ExpectBoolean("child.hasOwnProperty('age')", false); 1378 ExpectInt32("child.age", 10); 1379 ExpectInt32("child.accessor_age", 10); 1380 } 1381 1382 THREADED_TEST(EmptyInterceptorDoesNotShadowJSAccessors) { 1383 v8::HandleScope scope; 1384 Handle<FunctionTemplate> parent = FunctionTemplate::New(); 1385 Handle<FunctionTemplate> child = FunctionTemplate::New(); 1386 child->Inherit(parent); 1387 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); 1388 LocalContext env; 1389 env->Global()->Set(v8_str("Child"), child->GetFunction()); 1390 CompileRun("var child = new Child;" 1391 "var parent = child.__proto__;" 1392 "Object.defineProperty(parent, 'age', " 1393 " {get: function(){ return this.accessor_age; }, " 1394 " set: function(v){ this.accessor_age = v; }, " 1395 " enumerable: true, configurable: true});" 1396 "child.age = 10;"); 1397 ExpectBoolean("child.hasOwnProperty('age')", false); 1398 ExpectInt32("child.age", 10); 1399 ExpectInt32("child.accessor_age", 10); 1400 } 1401 1402 THREADED_TEST(EmptyInterceptorDoesNotAffectJSProperties) { 1403 v8::HandleScope scope; 1404 Handle<FunctionTemplate> parent = FunctionTemplate::New(); 1405 Handle<FunctionTemplate> child = FunctionTemplate::New(); 1406 child->Inherit(parent); 1407 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); 1408 LocalContext env; 1409 env->Global()->Set(v8_str("Child"), child->GetFunction()); 1410 CompileRun("var child = new Child;" 1411 "var parent = child.__proto__;" 1412 "parent.name = 'Alice';"); 1413 ExpectBoolean("child.hasOwnProperty('name')", false); 1414 ExpectString("child.name", "Alice"); 1415 CompileRun("child.name = 'Bob';"); 1416 ExpectString("child.name", "Bob"); 1417 ExpectBoolean("child.hasOwnProperty('name')", true); 1418 ExpectString("parent.name", "Alice"); 1419 } 1420 1421 THREADED_TEST(SwitchFromInterceptorToAccessor) { 1422 v8::HandleScope scope; 1423 Handle<FunctionTemplate> templ = FunctionTemplate::New(); 1424 AddAccessor(templ, v8_str("age"), 1425 SimpleAccessorGetter, SimpleAccessorSetter); 1426 AddInterceptor(templ, InterceptorGetter, InterceptorSetter); 1427 LocalContext env; 1428 env->Global()->Set(v8_str("Obj"), templ->GetFunction()); 1429 CompileRun("var obj = new Obj;" 1430 "function setAge(i){ obj.age = i; };" 1431 "for(var i = 0; i <= 10000; i++) setAge(i);"); 1432 // All i < 10000 go to the interceptor. 1433 ExpectInt32("obj.interceptor_age", 9999); 1434 // The last i goes to the accessor. 1435 ExpectInt32("obj.accessor_age", 10000); 1436 } 1437 1438 THREADED_TEST(SwitchFromAccessorToInterceptor) { 1439 v8::HandleScope scope; 1440 Handle<FunctionTemplate> templ = FunctionTemplate::New(); 1441 AddAccessor(templ, v8_str("age"), 1442 SimpleAccessorGetter, SimpleAccessorSetter); 1443 AddInterceptor(templ, InterceptorGetter, InterceptorSetter); 1444 LocalContext env; 1445 env->Global()->Set(v8_str("Obj"), templ->GetFunction()); 1446 CompileRun("var obj = new Obj;" 1447 "function setAge(i){ obj.age = i; };" 1448 "for(var i = 20000; i >= 9999; i--) setAge(i);"); 1449 // All i >= 10000 go to the accessor. 1450 ExpectInt32("obj.accessor_age", 10000); 1451 // The last i goes to the interceptor. 1452 ExpectInt32("obj.interceptor_age", 9999); 1453 } 1454 1455 THREADED_TEST(SwitchFromInterceptorToAccessorWithInheritance) { 1456 v8::HandleScope scope; 1457 Handle<FunctionTemplate> parent = FunctionTemplate::New(); 1458 Handle<FunctionTemplate> child = FunctionTemplate::New(); 1459 child->Inherit(parent); 1460 AddAccessor(parent, v8_str("age"), 1461 SimpleAccessorGetter, SimpleAccessorSetter); 1462 AddInterceptor(child, InterceptorGetter, InterceptorSetter); 1463 LocalContext env; 1464 env->Global()->Set(v8_str("Child"), child->GetFunction()); 1465 CompileRun("var child = new Child;" 1466 "function setAge(i){ child.age = i; };" 1467 "for(var i = 0; i <= 10000; i++) setAge(i);"); 1468 // All i < 10000 go to the interceptor. 1469 ExpectInt32("child.interceptor_age", 9999); 1470 // The last i goes to the accessor. 1471 ExpectInt32("child.accessor_age", 10000); 1472 } 1473 1474 THREADED_TEST(SwitchFromAccessorToInterceptorWithInheritance) { 1475 v8::HandleScope scope; 1476 Handle<FunctionTemplate> parent = FunctionTemplate::New(); 1477 Handle<FunctionTemplate> child = FunctionTemplate::New(); 1478 child->Inherit(parent); 1479 AddAccessor(parent, v8_str("age"), 1480 SimpleAccessorGetter, SimpleAccessorSetter); 1481 AddInterceptor(child, InterceptorGetter, InterceptorSetter); 1482 LocalContext env; 1483 env->Global()->Set(v8_str("Child"), child->GetFunction()); 1484 CompileRun("var child = new Child;" 1485 "function setAge(i){ child.age = i; };" 1486 "for(var i = 20000; i >= 9999; i--) setAge(i);"); 1487 // All i >= 10000 go to the accessor. 1488 ExpectInt32("child.accessor_age", 10000); 1489 // The last i goes to the interceptor. 1490 ExpectInt32("child.interceptor_age", 9999); 1491 } 1492 1493 THREADED_TEST(SwitchFromInterceptorToJSAccessor) { 1494 v8::HandleScope scope; 1495 Handle<FunctionTemplate> templ = FunctionTemplate::New(); 1496 AddInterceptor(templ, InterceptorGetter, InterceptorSetter); 1497 LocalContext env; 1498 env->Global()->Set(v8_str("Obj"), templ->GetFunction()); 1499 CompileRun("var obj = new Obj;" 1500 "function setter(i) { this.accessor_age = i; };" 1501 "function getter() { return this.accessor_age; };" 1502 "function setAge(i) { obj.age = i; };" 1503 "Object.defineProperty(obj, 'age', { get:getter, set:setter });" 1504 "for(var i = 0; i <= 10000; i++) setAge(i);"); 1505 // All i < 10000 go to the interceptor. 1506 ExpectInt32("obj.interceptor_age", 9999); 1507 // The last i goes to the JavaScript accessor. 1508 ExpectInt32("obj.accessor_age", 10000); 1509 // The installed JavaScript getter is still intact. 1510 // This last part is a regression test for issue 1651 and relies on the fact 1511 // that both interceptor and accessor are being installed on the same object. 1512 ExpectInt32("obj.age", 10000); 1513 ExpectBoolean("obj.hasOwnProperty('age')", true); 1514 ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value"); 1515 } 1516 1517 THREADED_TEST(SwitchFromJSAccessorToInterceptor) { 1518 v8::HandleScope scope; 1519 Handle<FunctionTemplate> templ = FunctionTemplate::New(); 1520 AddInterceptor(templ, InterceptorGetter, InterceptorSetter); 1521 LocalContext env; 1522 env->Global()->Set(v8_str("Obj"), templ->GetFunction()); 1523 CompileRun("var obj = new Obj;" 1524 "function setter(i) { this.accessor_age = i; };" 1525 "function getter() { return this.accessor_age; };" 1526 "function setAge(i) { obj.age = i; };" 1527 "Object.defineProperty(obj, 'age', { get:getter, set:setter });" 1528 "for(var i = 20000; i >= 9999; i--) setAge(i);"); 1529 // All i >= 10000 go to the accessor. 1530 ExpectInt32("obj.accessor_age", 10000); 1531 // The last i goes to the interceptor. 1532 ExpectInt32("obj.interceptor_age", 9999); 1533 // The installed JavaScript getter is still intact. 1534 // This last part is a regression test for issue 1651 and relies on the fact 1535 // that both interceptor and accessor are being installed on the same object. 1536 ExpectInt32("obj.age", 10000); 1537 ExpectBoolean("obj.hasOwnProperty('age')", true); 1538 ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value"); 1539 } 1540 1541 THREADED_TEST(SwitchFromInterceptorToProperty) { 1542 v8::HandleScope scope; 1543 Handle<FunctionTemplate> parent = FunctionTemplate::New(); 1544 Handle<FunctionTemplate> child = FunctionTemplate::New(); 1545 child->Inherit(parent); 1546 AddInterceptor(child, InterceptorGetter, InterceptorSetter); 1547 LocalContext env; 1548 env->Global()->Set(v8_str("Child"), child->GetFunction()); 1549 CompileRun("var child = new Child;" 1550 "function setAge(i){ child.age = i; };" 1551 "for(var i = 0; i <= 10000; i++) setAge(i);"); 1552 // All i < 10000 go to the interceptor. 1553 ExpectInt32("child.interceptor_age", 9999); 1554 // The last i goes to child's own property. 1555 ExpectInt32("child.age", 10000); 1556 } 1557 1558 THREADED_TEST(SwitchFromPropertyToInterceptor) { 1559 v8::HandleScope scope; 1560 Handle<FunctionTemplate> parent = FunctionTemplate::New(); 1561 Handle<FunctionTemplate> child = FunctionTemplate::New(); 1562 child->Inherit(parent); 1563 AddInterceptor(child, InterceptorGetter, InterceptorSetter); 1564 LocalContext env; 1565 env->Global()->Set(v8_str("Child"), child->GetFunction()); 1566 CompileRun("var child = new Child;" 1567 "function setAge(i){ child.age = i; };" 1568 "for(var i = 20000; i >= 9999; i--) setAge(i);"); 1569 // All i >= 10000 go to child's own property. 1570 ExpectInt32("child.age", 10000); 1571 // The last i goes to the interceptor. 1572 ExpectInt32("child.interceptor_age", 9999); 1573 } 1574 1575 THREADED_TEST(NamedPropertyHandlerGetter) { 1576 echo_named_call_count = 0; 1577 v8::HandleScope scope; 1578 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1579 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty, 1580 0, 0, 0, 0, 1581 v8_str("data")); 1582 LocalContext env; 1583 env->Global()->Set(v8_str("obj"), 1584 templ->GetFunction()->NewInstance()); 1585 CHECK_EQ(echo_named_call_count, 0); 1586 v8_compile("obj.x")->Run(); 1587 CHECK_EQ(echo_named_call_count, 1); 1588 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;"; 1589 v8::Handle<Value> str = CompileRun(code); 1590 String::AsciiValue value(str); 1591 CHECK_EQ(*value, "oddlepoddle"); 1592 // Check default behavior 1593 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10); 1594 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue()); 1595 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue()); 1596 } 1597 1598 1599 int echo_indexed_call_count = 0; 1600 1601 1602 static v8::Handle<Value> EchoIndexedProperty(uint32_t index, 1603 const AccessorInfo& info) { 1604 ApiTestFuzzer::Fuzz(); 1605 CHECK_EQ(v8_num(637), info.Data()); 1606 echo_indexed_call_count++; 1607 return v8_num(index); 1608 } 1609 1610 1611 THREADED_TEST(IndexedPropertyHandlerGetter) { 1612 v8::HandleScope scope; 1613 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1614 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty, 1615 0, 0, 0, 0, 1616 v8_num(637)); 1617 LocalContext env; 1618 env->Global()->Set(v8_str("obj"), 1619 templ->GetFunction()->NewInstance()); 1620 Local<Script> script = v8_compile("obj[900]"); 1621 CHECK_EQ(script->Run()->Int32Value(), 900); 1622 } 1623 1624 1625 v8::Handle<v8::Object> bottom; 1626 1627 static v8::Handle<Value> CheckThisIndexedPropertyHandler( 1628 uint32_t index, 1629 const AccessorInfo& info) { 1630 ApiTestFuzzer::Fuzz(); 1631 CHECK(info.This()->Equals(bottom)); 1632 return v8::Handle<Value>(); 1633 } 1634 1635 static v8::Handle<Value> CheckThisNamedPropertyHandler( 1636 Local<String> name, 1637 const AccessorInfo& info) { 1638 ApiTestFuzzer::Fuzz(); 1639 CHECK(info.This()->Equals(bottom)); 1640 return v8::Handle<Value>(); 1641 } 1642 1643 1644 v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index, 1645 Local<Value> value, 1646 const AccessorInfo& info) { 1647 ApiTestFuzzer::Fuzz(); 1648 CHECK(info.This()->Equals(bottom)); 1649 return v8::Handle<Value>(); 1650 } 1651 1652 1653 v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property, 1654 Local<Value> value, 1655 const AccessorInfo& info) { 1656 ApiTestFuzzer::Fuzz(); 1657 CHECK(info.This()->Equals(bottom)); 1658 return v8::Handle<Value>(); 1659 } 1660 1661 v8::Handle<v8::Integer> CheckThisIndexedPropertyQuery( 1662 uint32_t index, 1663 const AccessorInfo& info) { 1664 ApiTestFuzzer::Fuzz(); 1665 CHECK(info.This()->Equals(bottom)); 1666 return v8::Handle<v8::Integer>(); 1667 } 1668 1669 1670 v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property, 1671 const AccessorInfo& info) { 1672 ApiTestFuzzer::Fuzz(); 1673 CHECK(info.This()->Equals(bottom)); 1674 return v8::Handle<v8::Integer>(); 1675 } 1676 1677 1678 v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter( 1679 uint32_t index, 1680 const AccessorInfo& info) { 1681 ApiTestFuzzer::Fuzz(); 1682 CHECK(info.This()->Equals(bottom)); 1683 return v8::Handle<v8::Boolean>(); 1684 } 1685 1686 1687 v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter( 1688 Local<String> property, 1689 const AccessorInfo& info) { 1690 ApiTestFuzzer::Fuzz(); 1691 CHECK(info.This()->Equals(bottom)); 1692 return v8::Handle<v8::Boolean>(); 1693 } 1694 1695 1696 v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator( 1697 const AccessorInfo& info) { 1698 ApiTestFuzzer::Fuzz(); 1699 CHECK(info.This()->Equals(bottom)); 1700 return v8::Handle<v8::Array>(); 1701 } 1702 1703 1704 v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator( 1705 const AccessorInfo& info) { 1706 ApiTestFuzzer::Fuzz(); 1707 CHECK(info.This()->Equals(bottom)); 1708 return v8::Handle<v8::Array>(); 1709 } 1710 1711 1712 THREADED_TEST(PropertyHandlerInPrototype) { 1713 v8::HandleScope scope; 1714 LocalContext env; 1715 1716 // Set up a prototype chain with three interceptors. 1717 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1718 templ->InstanceTemplate()->SetIndexedPropertyHandler( 1719 CheckThisIndexedPropertyHandler, 1720 CheckThisIndexedPropertySetter, 1721 CheckThisIndexedPropertyQuery, 1722 CheckThisIndexedPropertyDeleter, 1723 CheckThisIndexedPropertyEnumerator); 1724 1725 templ->InstanceTemplate()->SetNamedPropertyHandler( 1726 CheckThisNamedPropertyHandler, 1727 CheckThisNamedPropertySetter, 1728 CheckThisNamedPropertyQuery, 1729 CheckThisNamedPropertyDeleter, 1730 CheckThisNamedPropertyEnumerator); 1731 1732 bottom = templ->GetFunction()->NewInstance(); 1733 Local<v8::Object> top = templ->GetFunction()->NewInstance(); 1734 Local<v8::Object> middle = templ->GetFunction()->NewInstance(); 1735 1736 bottom->Set(v8_str("__proto__"), middle); 1737 middle->Set(v8_str("__proto__"), top); 1738 env->Global()->Set(v8_str("obj"), bottom); 1739 1740 // Indexed and named get. 1741 Script::Compile(v8_str("obj[0]"))->Run(); 1742 Script::Compile(v8_str("obj.x"))->Run(); 1743 1744 // Indexed and named set. 1745 Script::Compile(v8_str("obj[1] = 42"))->Run(); 1746 Script::Compile(v8_str("obj.y = 42"))->Run(); 1747 1748 // Indexed and named query. 1749 Script::Compile(v8_str("0 in obj"))->Run(); 1750 Script::Compile(v8_str("'x' in obj"))->Run(); 1751 1752 // Indexed and named deleter. 1753 Script::Compile(v8_str("delete obj[0]"))->Run(); 1754 Script::Compile(v8_str("delete obj.x"))->Run(); 1755 1756 // Enumerators. 1757 Script::Compile(v8_str("for (var p in obj) ;"))->Run(); 1758 } 1759 1760 1761 static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key, 1762 const AccessorInfo& info) { 1763 ApiTestFuzzer::Fuzz(); 1764 if (v8_str("pre")->Equals(key)) { 1765 return v8_str("PrePropertyHandler: pre"); 1766 } 1767 return v8::Handle<String>(); 1768 } 1769 1770 1771 static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key, 1772 const AccessorInfo&) { 1773 if (v8_str("pre")->Equals(key)) { 1774 return v8::Integer::New(v8::None); 1775 } 1776 1777 return v8::Handle<v8::Integer>(); // do not intercept the call 1778 } 1779 1780 1781 THREADED_TEST(PrePropertyHandler) { 1782 v8::HandleScope scope; 1783 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(); 1784 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet, 1785 0, 1786 PrePropertyHandlerQuery); 1787 LocalContext env(NULL, desc->InstanceTemplate()); 1788 Script::Compile(v8_str( 1789 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run(); 1790 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run(); 1791 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre); 1792 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run(); 1793 CHECK_EQ(v8_str("Object: on"), result_on); 1794 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run(); 1795 CHECK(result_post.IsEmpty()); 1796 } 1797 1798 1799 THREADED_TEST(UndefinedIsNotEnumerable) { 1800 v8::HandleScope scope; 1801 LocalContext env; 1802 v8::Handle<Value> result = Script::Compile(v8_str( 1803 "this.propertyIsEnumerable(undefined)"))->Run(); 1804 CHECK(result->IsFalse()); 1805 } 1806 1807 1808 v8::Handle<Script> call_recursively_script; 1809 static const int kTargetRecursionDepth = 200; // near maximum 1810 1811 1812 static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) { 1813 ApiTestFuzzer::Fuzz(); 1814 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1815 if (depth == kTargetRecursionDepth) return v8::Undefined(); 1816 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1817 return call_recursively_script->Run(); 1818 } 1819 1820 1821 static v8::Handle<Value> CallFunctionRecursivelyCall( 1822 const v8::Arguments& args) { 1823 ApiTestFuzzer::Fuzz(); 1824 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1825 if (depth == kTargetRecursionDepth) { 1826 printf("[depth = %d]\n", depth); 1827 return v8::Undefined(); 1828 } 1829 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1830 v8::Handle<Value> function = 1831 args.This()->Get(v8_str("callFunctionRecursively")); 1832 return function.As<Function>()->Call(args.This(), 0, NULL); 1833 } 1834 1835 1836 THREADED_TEST(DeepCrossLanguageRecursion) { 1837 v8::HandleScope scope; 1838 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 1839 global->Set(v8_str("callScriptRecursively"), 1840 v8::FunctionTemplate::New(CallScriptRecursivelyCall)); 1841 global->Set(v8_str("callFunctionRecursively"), 1842 v8::FunctionTemplate::New(CallFunctionRecursivelyCall)); 1843 LocalContext env(NULL, global); 1844 1845 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1846 call_recursively_script = v8_compile("callScriptRecursively()"); 1847 call_recursively_script->Run(); 1848 call_recursively_script = v8::Handle<Script>(); 1849 1850 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1851 Script::Compile(v8_str("callFunctionRecursively()"))->Run(); 1852 } 1853 1854 1855 static v8::Handle<Value> 1856 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) { 1857 ApiTestFuzzer::Fuzz(); 1858 return v8::ThrowException(key); 1859 } 1860 1861 1862 static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key, 1863 Local<Value>, 1864 const AccessorInfo&) { 1865 v8::ThrowException(key); 1866 return v8::Undefined(); // not the same as v8::Handle<v8::Value>() 1867 } 1868 1869 1870 THREADED_TEST(CallbackExceptionRegression) { 1871 v8::HandleScope scope; 1872 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 1873 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet, 1874 ThrowingPropertyHandlerSet); 1875 LocalContext env; 1876 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 1877 v8::Handle<Value> otto = Script::Compile(v8_str( 1878 "try { with (obj) { otto; } } catch (e) { e; }"))->Run(); 1879 CHECK_EQ(v8_str("otto"), otto); 1880 v8::Handle<Value> netto = Script::Compile(v8_str( 1881 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run(); 1882 CHECK_EQ(v8_str("netto"), netto); 1883 } 1884 1885 1886 THREADED_TEST(FunctionPrototype) { 1887 v8::HandleScope scope; 1888 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New(); 1889 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321)); 1890 LocalContext env; 1891 env->Global()->Set(v8_str("Foo"), Foo->GetFunction()); 1892 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak")); 1893 CHECK_EQ(script->Run()->Int32Value(), 321); 1894 } 1895 1896 1897 THREADED_TEST(InternalFields) { 1898 v8::HandleScope scope; 1899 LocalContext env; 1900 1901 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1902 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1903 instance_templ->SetInternalFieldCount(1); 1904 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1905 CHECK_EQ(1, obj->InternalFieldCount()); 1906 CHECK(obj->GetInternalField(0)->IsUndefined()); 1907 obj->SetInternalField(0, v8_num(17)); 1908 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value()); 1909 } 1910 1911 1912 THREADED_TEST(GlobalObjectInternalFields) { 1913 v8::HandleScope scope; 1914 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 1915 global_template->SetInternalFieldCount(1); 1916 LocalContext env(NULL, global_template); 1917 v8::Handle<v8::Object> global_proxy = env->Global(); 1918 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); 1919 CHECK_EQ(1, global->InternalFieldCount()); 1920 CHECK(global->GetInternalField(0)->IsUndefined()); 1921 global->SetInternalField(0, v8_num(17)); 1922 CHECK_EQ(17, global->GetInternalField(0)->Int32Value()); 1923 } 1924 1925 1926 THREADED_TEST(InternalFieldsNativePointers) { 1927 v8::HandleScope scope; 1928 LocalContext env; 1929 1930 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1931 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1932 instance_templ->SetInternalFieldCount(1); 1933 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1934 CHECK_EQ(1, obj->InternalFieldCount()); 1935 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1936 1937 char* data = new char[100]; 1938 1939 void* aligned = data; 1940 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1)); 1941 void* unaligned = data + 1; 1942 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1)); 1943 1944 // Check reading and writing aligned pointers. 1945 obj->SetPointerInInternalField(0, aligned); 1946 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 1947 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1948 1949 // Check reading and writing unaligned pointers. 1950 obj->SetPointerInInternalField(0, unaligned); 1951 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 1952 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1953 1954 delete[] data; 1955 } 1956 1957 1958 THREADED_TEST(InternalFieldsNativePointersAndExternal) { 1959 v8::HandleScope scope; 1960 LocalContext env; 1961 1962 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1963 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1964 instance_templ->SetInternalFieldCount(1); 1965 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1966 CHECK_EQ(1, obj->InternalFieldCount()); 1967 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1968 1969 char* data = new char[100]; 1970 1971 void* aligned = data; 1972 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1)); 1973 void* unaligned = data + 1; 1974 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1)); 1975 1976 obj->SetPointerInInternalField(0, aligned); 1977 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 1978 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0))); 1979 1980 obj->SetPointerInInternalField(0, unaligned); 1981 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 1982 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0))); 1983 1984 obj->SetInternalField(0, v8::External::Wrap(aligned)); 1985 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 1986 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1987 1988 obj->SetInternalField(0, v8::External::Wrap(unaligned)); 1989 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 1990 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1991 1992 delete[] data; 1993 } 1994 1995 1996 THREADED_TEST(IdentityHash) { 1997 v8::HandleScope scope; 1998 LocalContext env; 1999 2000 // Ensure that the test starts with an fresh heap to test whether the hash 2001 // code is based on the address. 2002 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 2003 Local<v8::Object> obj = v8::Object::New(); 2004 int hash = obj->GetIdentityHash(); 2005 int hash1 = obj->GetIdentityHash(); 2006 CHECK_EQ(hash, hash1); 2007 int hash2 = v8::Object::New()->GetIdentityHash(); 2008 // Since the identity hash is essentially a random number two consecutive 2009 // objects should not be assigned the same hash code. If the test below fails 2010 // the random number generator should be evaluated. 2011 CHECK_NE(hash, hash2); 2012 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 2013 int hash3 = v8::Object::New()->GetIdentityHash(); 2014 // Make sure that the identity hash is not based on the initial address of 2015 // the object alone. If the test below fails the random number generator 2016 // should be evaluated. 2017 CHECK_NE(hash, hash3); 2018 int hash4 = obj->GetIdentityHash(); 2019 CHECK_EQ(hash, hash4); 2020 2021 // Check identity hashes behaviour in the presence of JS accessors. 2022 // Put a getter for 'v8::IdentityHash' on the Object's prototype: 2023 { 2024 CompileRun("Object.prototype['v8::IdentityHash'] = 42;\n"); 2025 Local<v8::Object> o1 = v8::Object::New(); 2026 Local<v8::Object> o2 = v8::Object::New(); 2027 CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash()); 2028 } 2029 { 2030 CompileRun( 2031 "function cnst() { return 42; };\n" 2032 "Object.prototype.__defineGetter__('v8::IdentityHash', cnst);\n"); 2033 Local<v8::Object> o1 = v8::Object::New(); 2034 Local<v8::Object> o2 = v8::Object::New(); 2035 CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash()); 2036 } 2037 } 2038 2039 2040 THREADED_TEST(HiddenProperties) { 2041 v8::HandleScope scope; 2042 LocalContext env; 2043 2044 v8::Local<v8::Object> obj = v8::Object::New(); 2045 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 2046 v8::Local<v8::String> empty = v8_str(""); 2047 v8::Local<v8::String> prop_name = v8_str("prop_name"); 2048 2049 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 2050 2051 // Make sure delete of a non-existent hidden value works 2052 CHECK(obj->DeleteHiddenValue(key)); 2053 2054 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503))); 2055 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value()); 2056 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002))); 2057 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 2058 2059 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 2060 2061 // Make sure we do not find the hidden property. 2062 CHECK(!obj->Has(empty)); 2063 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 2064 CHECK(obj->Get(empty)->IsUndefined()); 2065 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 2066 CHECK(obj->Set(empty, v8::Integer::New(2003))); 2067 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 2068 CHECK_EQ(2003, obj->Get(empty)->Int32Value()); 2069 2070 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 2071 2072 // Add another property and delete it afterwards to force the object in 2073 // slow case. 2074 CHECK(obj->Set(prop_name, v8::Integer::New(2008))); 2075 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 2076 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value()); 2077 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 2078 CHECK(obj->Delete(prop_name)); 2079 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 2080 2081 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 2082 2083 CHECK(obj->DeleteHiddenValue(key)); 2084 CHECK(obj->GetHiddenValue(key).IsEmpty()); 2085 } 2086 2087 2088 THREADED_TEST(Regress97784) { 2089 // Regression test for crbug.com/97784 2090 // Messing with the Object.prototype should not have effect on 2091 // hidden properties. 2092 v8::HandleScope scope; 2093 LocalContext env; 2094 2095 v8::Local<v8::Object> obj = v8::Object::New(); 2096 v8::Local<v8::String> key = v8_str("hidden"); 2097 2098 CompileRun( 2099 "set_called = false;" 2100 "Object.defineProperty(" 2101 " Object.prototype," 2102 " 'hidden'," 2103 " {get: function() { return 45; }," 2104 " set: function() { set_called = true; }})"); 2105 2106 CHECK(obj->GetHiddenValue(key).IsEmpty()); 2107 // Make sure that the getter and setter from Object.prototype is not invoked. 2108 // If it did we would have full access to the hidden properties in 2109 // the accessor. 2110 CHECK(obj->SetHiddenValue(key, v8::Integer::New(42))); 2111 ExpectFalse("set_called"); 2112 CHECK_EQ(42, obj->GetHiddenValue(key)->Int32Value()); 2113 } 2114 2115 2116 static bool interceptor_for_hidden_properties_called; 2117 static v8::Handle<Value> InterceptorForHiddenProperties( 2118 Local<String> name, const AccessorInfo& info) { 2119 interceptor_for_hidden_properties_called = true; 2120 return v8::Handle<Value>(); 2121 } 2122 2123 2124 THREADED_TEST(HiddenPropertiesWithInterceptors) { 2125 v8::HandleScope scope; 2126 LocalContext context; 2127 2128 interceptor_for_hidden_properties_called = false; 2129 2130 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 2131 2132 // Associate an interceptor with an object and start setting hidden values. 2133 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 2134 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 2135 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties); 2136 Local<v8::Function> function = fun_templ->GetFunction(); 2137 Local<v8::Object> obj = function->NewInstance(); 2138 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302))); 2139 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value()); 2140 CHECK(!interceptor_for_hidden_properties_called); 2141 } 2142 2143 2144 THREADED_TEST(External) { 2145 v8::HandleScope scope; 2146 int x = 3; 2147 Local<v8::External> ext = v8::External::New(&x); 2148 LocalContext env; 2149 env->Global()->Set(v8_str("ext"), ext); 2150 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run(); 2151 v8::Handle<v8::External> reext = reext_obj.As<v8::External>(); 2152 int* ptr = static_cast<int*>(reext->Value()); 2153 CHECK_EQ(x, 3); 2154 *ptr = 10; 2155 CHECK_EQ(x, 10); 2156 2157 // Make sure unaligned pointers are wrapped properly. 2158 char* data = i::StrDup("0123456789"); 2159 Local<v8::Value> zero = v8::External::Wrap(&data[0]); 2160 Local<v8::Value> one = v8::External::Wrap(&data[1]); 2161 Local<v8::Value> two = v8::External::Wrap(&data[2]); 2162 Local<v8::Value> three = v8::External::Wrap(&data[3]); 2163 2164 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero)); 2165 CHECK_EQ('0', *char_ptr); 2166 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one)); 2167 CHECK_EQ('1', *char_ptr); 2168 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two)); 2169 CHECK_EQ('2', *char_ptr); 2170 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three)); 2171 CHECK_EQ('3', *char_ptr); 2172 i::DeleteArray(data); 2173 } 2174 2175 2176 THREADED_TEST(GlobalHandle) { 2177 v8::Persistent<String> global; 2178 { 2179 v8::HandleScope scope; 2180 Local<String> str = v8_str("str"); 2181 global = v8::Persistent<String>::New(str); 2182 } 2183 CHECK_EQ(global->Length(), 3); 2184 global.Dispose(); 2185 } 2186 2187 2188 class WeakCallCounter { 2189 public: 2190 explicit WeakCallCounter(int id) : id_(id), number_of_weak_calls_(0) { } 2191 int id() { return id_; } 2192 void increment() { number_of_weak_calls_++; } 2193 int NumberOfWeakCalls() { return number_of_weak_calls_; } 2194 private: 2195 int id_; 2196 int number_of_weak_calls_; 2197 }; 2198 2199 2200 static void WeakPointerCallback(Persistent<Value> handle, void* id) { 2201 WeakCallCounter* counter = reinterpret_cast<WeakCallCounter*>(id); 2202 CHECK_EQ(1234, counter->id()); 2203 counter->increment(); 2204 handle.Dispose(); 2205 } 2206 2207 2208 THREADED_TEST(ApiObjectGroups) { 2209 HandleScope scope; 2210 LocalContext env; 2211 2212 Persistent<Object> g1s1; 2213 Persistent<Object> g1s2; 2214 Persistent<Object> g1c1; 2215 Persistent<Object> g2s1; 2216 Persistent<Object> g2s2; 2217 Persistent<Object> g2c1; 2218 2219 WeakCallCounter counter(1234); 2220 2221 { 2222 HandleScope scope; 2223 g1s1 = Persistent<Object>::New(Object::New()); 2224 g1s2 = Persistent<Object>::New(Object::New()); 2225 g1c1 = Persistent<Object>::New(Object::New()); 2226 g1s1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2227 g1s2.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2228 g1c1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2229 2230 g2s1 = Persistent<Object>::New(Object::New()); 2231 g2s2 = Persistent<Object>::New(Object::New()); 2232 g2c1 = Persistent<Object>::New(Object::New()); 2233 g2s1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2234 g2s2.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2235 g2c1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2236 } 2237 2238 Persistent<Object> root = Persistent<Object>::New(g1s1); // make a root. 2239 2240 // Connect group 1 and 2, make a cycle. 2241 CHECK(g1s2->Set(0, g2s2)); 2242 CHECK(g2s1->Set(0, g1s1)); 2243 2244 { 2245 Persistent<Value> g1_objects[] = { g1s1, g1s2 }; 2246 Persistent<Value> g1_children[] = { g1c1 }; 2247 Persistent<Value> g2_objects[] = { g2s1, g2s2 }; 2248 Persistent<Value> g2_children[] = { g2c1 }; 2249 V8::AddObjectGroup(g1_objects, 2); 2250 V8::AddImplicitReferences(g1s1, g1_children, 1); 2251 V8::AddObjectGroup(g2_objects, 2); 2252 V8::AddImplicitReferences(g2s2, g2_children, 1); 2253 } 2254 // Do a single full GC, ensure incremental marking is stopped. 2255 HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 2256 2257 // All object should be alive. 2258 CHECK_EQ(0, counter.NumberOfWeakCalls()); 2259 2260 // Weaken the root. 2261 root.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2262 // But make children strong roots---all the objects (except for children) 2263 // should be collectable now. 2264 g1c1.ClearWeak(); 2265 g2c1.ClearWeak(); 2266 2267 // Groups are deleted, rebuild groups. 2268 { 2269 Persistent<Value> g1_objects[] = { g1s1, g1s2 }; 2270 Persistent<Value> g1_children[] = { g1c1 }; 2271 Persistent<Value> g2_objects[] = { g2s1, g2s2 }; 2272 Persistent<Value> g2_children[] = { g2c1 }; 2273 V8::AddObjectGroup(g1_objects, 2); 2274 V8::AddImplicitReferences(g1s1, g1_children, 1); 2275 V8::AddObjectGroup(g2_objects, 2); 2276 V8::AddImplicitReferences(g2s2, g2_children, 1); 2277 } 2278 2279 HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 2280 2281 // All objects should be gone. 5 global handles in total. 2282 CHECK_EQ(5, counter.NumberOfWeakCalls()); 2283 2284 // And now make children weak again and collect them. 2285 g1c1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2286 g2c1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2287 2288 HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 2289 CHECK_EQ(7, counter.NumberOfWeakCalls()); 2290 } 2291 2292 2293 THREADED_TEST(ApiObjectGroupsCycle) { 2294 HandleScope scope; 2295 LocalContext env; 2296 2297 WeakCallCounter counter(1234); 2298 2299 Persistent<Object> g1s1; 2300 Persistent<Object> g1s2; 2301 Persistent<Object> g2s1; 2302 Persistent<Object> g2s2; 2303 Persistent<Object> g3s1; 2304 Persistent<Object> g3s2; 2305 2306 { 2307 HandleScope scope; 2308 g1s1 = Persistent<Object>::New(Object::New()); 2309 g1s2 = Persistent<Object>::New(Object::New()); 2310 g1s1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2311 g1s2.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2312 2313 g2s1 = Persistent<Object>::New(Object::New()); 2314 g2s2 = Persistent<Object>::New(Object::New()); 2315 g2s1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2316 g2s2.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2317 2318 g3s1 = Persistent<Object>::New(Object::New()); 2319 g3s2 = Persistent<Object>::New(Object::New()); 2320 g3s1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2321 g3s2.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2322 } 2323 2324 Persistent<Object> root = Persistent<Object>::New(g1s1); // make a root. 2325 2326 // Connect groups. We're building the following cycle: 2327 // G1: { g1s1, g2s1 }, g1s1 implicitly references g2s1, ditto for other 2328 // groups. 2329 { 2330 Persistent<Value> g1_objects[] = { g1s1, g1s2 }; 2331 Persistent<Value> g1_children[] = { g2s1 }; 2332 Persistent<Value> g2_objects[] = { g2s1, g2s2 }; 2333 Persistent<Value> g2_children[] = { g3s1 }; 2334 Persistent<Value> g3_objects[] = { g3s1, g3s2 }; 2335 Persistent<Value> g3_children[] = { g1s1 }; 2336 V8::AddObjectGroup(g1_objects, 2); 2337 V8::AddImplicitReferences(g1s1, g1_children, 1); 2338 V8::AddObjectGroup(g2_objects, 2); 2339 V8::AddImplicitReferences(g2s1, g2_children, 1); 2340 V8::AddObjectGroup(g3_objects, 2); 2341 V8::AddImplicitReferences(g3s1, g3_children, 1); 2342 } 2343 // Do a single full GC 2344 HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 2345 2346 // All object should be alive. 2347 CHECK_EQ(0, counter.NumberOfWeakCalls()); 2348 2349 // Weaken the root. 2350 root.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2351 2352 // Groups are deleted, rebuild groups. 2353 { 2354 Persistent<Value> g1_objects[] = { g1s1, g1s2 }; 2355 Persistent<Value> g1_children[] = { g2s1 }; 2356 Persistent<Value> g2_objects[] = { g2s1, g2s2 }; 2357 Persistent<Value> g2_children[] = { g3s1 }; 2358 Persistent<Value> g3_objects[] = { g3s1, g3s2 }; 2359 Persistent<Value> g3_children[] = { g1s1 }; 2360 V8::AddObjectGroup(g1_objects, 2); 2361 V8::AddImplicitReferences(g1s1, g1_children, 1); 2362 V8::AddObjectGroup(g2_objects, 2); 2363 V8::AddImplicitReferences(g2s1, g2_children, 1); 2364 V8::AddObjectGroup(g3_objects, 2); 2365 V8::AddImplicitReferences(g3s1, g3_children, 1); 2366 } 2367 2368 HEAP->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 2369 2370 // All objects should be gone. 7 global handles in total. 2371 CHECK_EQ(7, counter.NumberOfWeakCalls()); 2372 } 2373 2374 2375 THREADED_TEST(ScriptException) { 2376 v8::HandleScope scope; 2377 LocalContext env; 2378 Local<Script> script = Script::Compile(v8_str("throw 'panama!';")); 2379 v8::TryCatch try_catch; 2380 Local<Value> result = script->Run(); 2381 CHECK(result.IsEmpty()); 2382 CHECK(try_catch.HasCaught()); 2383 String::AsciiValue exception_value(try_catch.Exception()); 2384 CHECK_EQ(*exception_value, "panama!"); 2385 } 2386 2387 2388 bool message_received; 2389 2390 2391 static void check_message(v8::Handle<v8::Message> message, 2392 v8::Handle<Value> data) { 2393 CHECK_EQ(5.76, data->NumberValue()); 2394 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue()); 2395 CHECK_EQ(7.56, message->GetScriptData()->NumberValue()); 2396 message_received = true; 2397 } 2398 2399 2400 THREADED_TEST(MessageHandlerData) { 2401 message_received = false; 2402 v8::HandleScope scope; 2403 CHECK(!message_received); 2404 v8::V8::AddMessageListener(check_message, v8_num(5.76)); 2405 LocalContext context; 2406 v8::ScriptOrigin origin = 2407 v8::ScriptOrigin(v8_str("6.75")); 2408 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"), 2409 &origin); 2410 script->SetData(v8_str("7.56")); 2411 script->Run(); 2412 CHECK(message_received); 2413 // clear out the message listener 2414 v8::V8::RemoveMessageListeners(check_message); 2415 } 2416 2417 2418 THREADED_TEST(GetSetProperty) { 2419 v8::HandleScope scope; 2420 LocalContext context; 2421 context->Global()->Set(v8_str("foo"), v8_num(14)); 2422 context->Global()->Set(v8_str("12"), v8_num(92)); 2423 context->Global()->Set(v8::Integer::New(16), v8_num(32)); 2424 context->Global()->Set(v8_num(13), v8_num(56)); 2425 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run(); 2426 CHECK_EQ(14, foo->Int32Value()); 2427 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run(); 2428 CHECK_EQ(92, twelve->Int32Value()); 2429 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run(); 2430 CHECK_EQ(32, sixteen->Int32Value()); 2431 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run(); 2432 CHECK_EQ(56, thirteen->Int32Value()); 2433 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value()); 2434 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value()); 2435 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value()); 2436 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value()); 2437 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value()); 2438 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value()); 2439 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value()); 2440 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value()); 2441 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value()); 2442 } 2443 2444 2445 THREADED_TEST(PropertyAttributes) { 2446 v8::HandleScope scope; 2447 LocalContext context; 2448 // none 2449 Local<String> prop = v8_str("none"); 2450 context->Global()->Set(prop, v8_num(7)); 2451 CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(prop)); 2452 // read-only 2453 prop = v8_str("read_only"); 2454 context->Global()->Set(prop, v8_num(7), v8::ReadOnly); 2455 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 2456 CHECK_EQ(v8::ReadOnly, context->Global()->GetPropertyAttributes(prop)); 2457 Script::Compile(v8_str("read_only = 9"))->Run(); 2458 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 2459 context->Global()->Set(prop, v8_num(10)); 2460 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 2461 // dont-delete 2462 prop = v8_str("dont_delete"); 2463 context->Global()->Set(prop, v8_num(13), v8::DontDelete); 2464 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 2465 Script::Compile(v8_str("delete dont_delete"))->Run(); 2466 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 2467 CHECK_EQ(v8::DontDelete, context->Global()->GetPropertyAttributes(prop)); 2468 // dont-enum 2469 prop = v8_str("dont_enum"); 2470 context->Global()->Set(prop, v8_num(28), v8::DontEnum); 2471 CHECK_EQ(v8::DontEnum, context->Global()->GetPropertyAttributes(prop)); 2472 // absent 2473 prop = v8_str("absent"); 2474 CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(prop)); 2475 Local<Value> fake_prop = v8_num(1); 2476 CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(fake_prop)); 2477 // exception 2478 TryCatch try_catch; 2479 Local<Value> exception = 2480 CompileRun("({ toString: function() { throw 'exception';} })"); 2481 CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(exception)); 2482 CHECK(try_catch.HasCaught()); 2483 String::AsciiValue exception_value(try_catch.Exception()); 2484 CHECK_EQ("exception", *exception_value); 2485 try_catch.Reset(); 2486 } 2487 2488 2489 THREADED_TEST(Array) { 2490 v8::HandleScope scope; 2491 LocalContext context; 2492 Local<v8::Array> array = v8::Array::New(); 2493 CHECK_EQ(0, array->Length()); 2494 CHECK(array->Get(0)->IsUndefined()); 2495 CHECK(!array->Has(0)); 2496 CHECK(array->Get(100)->IsUndefined()); 2497 CHECK(!array->Has(100)); 2498 array->Set(2, v8_num(7)); 2499 CHECK_EQ(3, array->Length()); 2500 CHECK(!array->Has(0)); 2501 CHECK(!array->Has(1)); 2502 CHECK(array->Has(2)); 2503 CHECK_EQ(7, array->Get(2)->Int32Value()); 2504 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run(); 2505 Local<v8::Array> arr = obj.As<v8::Array>(); 2506 CHECK_EQ(3, arr->Length()); 2507 CHECK_EQ(1, arr->Get(0)->Int32Value()); 2508 CHECK_EQ(2, arr->Get(1)->Int32Value()); 2509 CHECK_EQ(3, arr->Get(2)->Int32Value()); 2510 array = v8::Array::New(27); 2511 CHECK_EQ(27, array->Length()); 2512 array = v8::Array::New(-27); 2513 CHECK_EQ(0, array->Length()); 2514 } 2515 2516 2517 v8::Handle<Value> HandleF(const v8::Arguments& args) { 2518 v8::HandleScope scope; 2519 ApiTestFuzzer::Fuzz(); 2520 Local<v8::Array> result = v8::Array::New(args.Length()); 2521 for (int i = 0; i < args.Length(); i++) 2522 result->Set(i, args[i]); 2523 return scope.Close(result); 2524 } 2525 2526 2527 THREADED_TEST(Vector) { 2528 v8::HandleScope scope; 2529 Local<ObjectTemplate> global = ObjectTemplate::New(); 2530 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF)); 2531 LocalContext context(0, global); 2532 2533 const char* fun = "f()"; 2534 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>(); 2535 CHECK_EQ(0, a0->Length()); 2536 2537 const char* fun2 = "f(11)"; 2538 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>(); 2539 CHECK_EQ(1, a1->Length()); 2540 CHECK_EQ(11, a1->Get(0)->Int32Value()); 2541 2542 const char* fun3 = "f(12, 13)"; 2543 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>(); 2544 CHECK_EQ(2, a2->Length()); 2545 CHECK_EQ(12, a2->Get(0)->Int32Value()); 2546 CHECK_EQ(13, a2->Get(1)->Int32Value()); 2547 2548 const char* fun4 = "f(14, 15, 16)"; 2549 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>(); 2550 CHECK_EQ(3, a3->Length()); 2551 CHECK_EQ(14, a3->Get(0)->Int32Value()); 2552 CHECK_EQ(15, a3->Get(1)->Int32Value()); 2553 CHECK_EQ(16, a3->Get(2)->Int32Value()); 2554 2555 const char* fun5 = "f(17, 18, 19, 20)"; 2556 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>(); 2557 CHECK_EQ(4, a4->Length()); 2558 CHECK_EQ(17, a4->Get(0)->Int32Value()); 2559 CHECK_EQ(18, a4->Get(1)->Int32Value()); 2560 CHECK_EQ(19, a4->Get(2)->Int32Value()); 2561 CHECK_EQ(20, a4->Get(3)->Int32Value()); 2562 } 2563 2564 2565 THREADED_TEST(FunctionCall) { 2566 v8::HandleScope scope; 2567 LocalContext context; 2568 CompileRun( 2569 "function Foo() {" 2570 " var result = [];" 2571 " for (var i = 0; i < arguments.length; i++) {" 2572 " result.push(arguments[i]);" 2573 " }" 2574 " return result;" 2575 "}"); 2576 Local<Function> Foo = 2577 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 2578 2579 v8::Handle<Value>* args0 = NULL; 2580 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0)); 2581 CHECK_EQ(0, a0->Length()); 2582 2583 v8::Handle<Value> args1[] = { v8_num(1.1) }; 2584 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1)); 2585 CHECK_EQ(1, a1->Length()); 2586 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 2587 2588 v8::Handle<Value> args2[] = { v8_num(2.2), 2589 v8_num(3.3) }; 2590 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2)); 2591 CHECK_EQ(2, a2->Length()); 2592 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 2593 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 2594 2595 v8::Handle<Value> args3[] = { v8_num(4.4), 2596 v8_num(5.5), 2597 v8_num(6.6) }; 2598 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3)); 2599 CHECK_EQ(3, a3->Length()); 2600 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 2601 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 2602 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 2603 2604 v8::Handle<Value> args4[] = { v8_num(7.7), 2605 v8_num(8.8), 2606 v8_num(9.9), 2607 v8_num(10.11) }; 2608 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4)); 2609 CHECK_EQ(4, a4->Length()); 2610 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 2611 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 2612 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 2613 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 2614 } 2615 2616 2617 static const char* js_code_causing_out_of_memory = 2618 "var a = new Array(); while(true) a.push(a);"; 2619 2620 2621 // These tests run for a long time and prevent us from running tests 2622 // that come after them so they cannot run in parallel. 2623 TEST(OutOfMemory) { 2624 // It's not possible to read a snapshot into a heap with different dimensions. 2625 if (i::Snapshot::IsEnabled()) return; 2626 // Set heap limits. 2627 static const int K = 1024; 2628 v8::ResourceConstraints constraints; 2629 constraints.set_max_young_space_size(256 * K); 2630 constraints.set_max_old_space_size(4 * K * K); 2631 v8::SetResourceConstraints(&constraints); 2632 2633 // Execute a script that causes out of memory. 2634 v8::HandleScope scope; 2635 LocalContext context; 2636 v8::V8::IgnoreOutOfMemoryException(); 2637 Local<Script> script = 2638 Script::Compile(String::New(js_code_causing_out_of_memory)); 2639 Local<Value> result = script->Run(); 2640 2641 // Check for out of memory state. 2642 CHECK(result.IsEmpty()); 2643 CHECK(context->HasOutOfMemoryException()); 2644 } 2645 2646 2647 v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) { 2648 ApiTestFuzzer::Fuzz(); 2649 2650 v8::HandleScope scope; 2651 LocalContext context; 2652 Local<Script> script = 2653 Script::Compile(String::New(js_code_causing_out_of_memory)); 2654 Local<Value> result = script->Run(); 2655 2656 // Check for out of memory state. 2657 CHECK(result.IsEmpty()); 2658 CHECK(context->HasOutOfMemoryException()); 2659 2660 return result; 2661 } 2662 2663 2664 TEST(OutOfMemoryNested) { 2665 // It's not possible to read a snapshot into a heap with different dimensions. 2666 if (i::Snapshot::IsEnabled()) return; 2667 // Set heap limits. 2668 static const int K = 1024; 2669 v8::ResourceConstraints constraints; 2670 constraints.set_max_young_space_size(256 * K); 2671 constraints.set_max_old_space_size(4 * K * K); 2672 v8::SetResourceConstraints(&constraints); 2673 2674 v8::HandleScope scope; 2675 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2676 templ->Set(v8_str("ProvokeOutOfMemory"), 2677 v8::FunctionTemplate::New(ProvokeOutOfMemory)); 2678 LocalContext context(0, templ); 2679 v8::V8::IgnoreOutOfMemoryException(); 2680 Local<Value> result = CompileRun( 2681 "var thrown = false;" 2682 "try {" 2683 " ProvokeOutOfMemory();" 2684 "} catch (e) {" 2685 " thrown = true;" 2686 "}"); 2687 // Check for out of memory state. 2688 CHECK(result.IsEmpty()); 2689 CHECK(context->HasOutOfMemoryException()); 2690 } 2691 2692 2693 TEST(HugeConsStringOutOfMemory) { 2694 // It's not possible to read a snapshot into a heap with different dimensions. 2695 if (i::Snapshot::IsEnabled()) return; 2696 // Set heap limits. 2697 static const int K = 1024; 2698 v8::ResourceConstraints constraints; 2699 constraints.set_max_young_space_size(256 * K); 2700 constraints.set_max_old_space_size(2 * K * K); 2701 v8::SetResourceConstraints(&constraints); 2702 2703 // Execute a script that causes out of memory. 2704 v8::V8::IgnoreOutOfMemoryException(); 2705 2706 v8::HandleScope scope; 2707 LocalContext context; 2708 2709 // Build huge string. This should fail with out of memory exception. 2710 Local<Value> result = CompileRun( 2711 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();" 2712 "for (var i = 0; i < 22; i++) { str = str + str; }"); 2713 2714 // Check for out of memory state. 2715 CHECK(result.IsEmpty()); 2716 CHECK(context->HasOutOfMemoryException()); 2717 } 2718 2719 2720 THREADED_TEST(ConstructCall) { 2721 v8::HandleScope scope; 2722 LocalContext context; 2723 CompileRun( 2724 "function Foo() {" 2725 " var result = [];" 2726 " for (var i = 0; i < arguments.length; i++) {" 2727 " result.push(arguments[i]);" 2728 " }" 2729 " return result;" 2730 "}"); 2731 Local<Function> Foo = 2732 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 2733 2734 v8::Handle<Value>* args0 = NULL; 2735 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0)); 2736 CHECK_EQ(0, a0->Length()); 2737 2738 v8::Handle<Value> args1[] = { v8_num(1.1) }; 2739 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1)); 2740 CHECK_EQ(1, a1->Length()); 2741 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 2742 2743 v8::Handle<Value> args2[] = { v8_num(2.2), 2744 v8_num(3.3) }; 2745 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2)); 2746 CHECK_EQ(2, a2->Length()); 2747 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 2748 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 2749 2750 v8::Handle<Value> args3[] = { v8_num(4.4), 2751 v8_num(5.5), 2752 v8_num(6.6) }; 2753 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3)); 2754 CHECK_EQ(3, a3->Length()); 2755 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 2756 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 2757 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 2758 2759 v8::Handle<Value> args4[] = { v8_num(7.7), 2760 v8_num(8.8), 2761 v8_num(9.9), 2762 v8_num(10.11) }; 2763 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4)); 2764 CHECK_EQ(4, a4->Length()); 2765 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 2766 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 2767 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 2768 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 2769 } 2770 2771 2772 static void CheckUncle(v8::TryCatch* try_catch) { 2773 CHECK(try_catch->HasCaught()); 2774 String::AsciiValue str_value(try_catch->Exception()); 2775 CHECK_EQ(*str_value, "uncle?"); 2776 try_catch->Reset(); 2777 } 2778 2779 2780 THREADED_TEST(ConversionNumber) { 2781 v8::HandleScope scope; 2782 LocalContext env; 2783 // Very large number. 2784 CompileRun("var obj = Math.pow(2,32) * 1237;"); 2785 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2786 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value()); 2787 CHECK_EQ(0, obj->ToInt32()->Value()); 2788 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned. 2789 // Large number. 2790 CompileRun("var obj = -1234567890123;"); 2791 obj = env->Global()->Get(v8_str("obj")); 2792 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value()); 2793 CHECK_EQ(-1912276171, obj->ToInt32()->Value()); 2794 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT 2795 // Small positive integer. 2796 CompileRun("var obj = 42;"); 2797 obj = env->Global()->Get(v8_str("obj")); 2798 CHECK_EQ(42.0, obj->ToNumber()->Value()); 2799 CHECK_EQ(42, obj->ToInt32()->Value()); 2800 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 2801 // Negative integer. 2802 CompileRun("var obj = -37;"); 2803 obj = env->Global()->Get(v8_str("obj")); 2804 CHECK_EQ(-37.0, obj->ToNumber()->Value()); 2805 CHECK_EQ(-37, obj->ToInt32()->Value()); 2806 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT 2807 // Positive non-int32 integer. 2808 CompileRun("var obj = 0x81234567;"); 2809 obj = env->Global()->Get(v8_str("obj")); 2810 CHECK_EQ(2166572391.0, obj->ToNumber()->Value()); 2811 CHECK_EQ(-2128394905, obj->ToInt32()->Value()); 2812 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT 2813 // Fraction. 2814 CompileRun("var obj = 42.3;"); 2815 obj = env->Global()->Get(v8_str("obj")); 2816 CHECK_EQ(42.3, obj->ToNumber()->Value()); 2817 CHECK_EQ(42, obj->ToInt32()->Value()); 2818 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 2819 // Large negative fraction. 2820 CompileRun("var obj = -5726623061.75;"); 2821 obj = env->Global()->Get(v8_str("obj")); 2822 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value()); 2823 CHECK_EQ(-1431655765, obj->ToInt32()->Value()); 2824 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT 2825 } 2826 2827 2828 THREADED_TEST(isNumberType) { 2829 v8::HandleScope scope; 2830 LocalContext env; 2831 // Very large number. 2832 CompileRun("var obj = Math.pow(2,32) * 1237;"); 2833 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2834 CHECK(!obj->IsInt32()); 2835 CHECK(!obj->IsUint32()); 2836 // Large negative number. 2837 CompileRun("var obj = -1234567890123;"); 2838 obj = env->Global()->Get(v8_str("obj")); 2839 CHECK(!obj->IsInt32()); 2840 CHECK(!obj->IsUint32()); 2841 // Small positive integer. 2842 CompileRun("var obj = 42;"); 2843 obj = env->Global()->Get(v8_str("obj")); 2844 CHECK(obj->IsInt32()); 2845 CHECK(obj->IsUint32()); 2846 // Negative integer. 2847 CompileRun("var obj = -37;"); 2848 obj = env->Global()->Get(v8_str("obj")); 2849 CHECK(obj->IsInt32()); 2850 CHECK(!obj->IsUint32()); 2851 // Positive non-int32 integer. 2852 CompileRun("var obj = 0x81234567;"); 2853 obj = env->Global()->Get(v8_str("obj")); 2854 CHECK(!obj->IsInt32()); 2855 CHECK(obj->IsUint32()); 2856 // Fraction. 2857 CompileRun("var obj = 42.3;"); 2858 obj = env->Global()->Get(v8_str("obj")); 2859 CHECK(!obj->IsInt32()); 2860 CHECK(!obj->IsUint32()); 2861 // Large negative fraction. 2862 CompileRun("var obj = -5726623061.75;"); 2863 obj = env->Global()->Get(v8_str("obj")); 2864 CHECK(!obj->IsInt32()); 2865 CHECK(!obj->IsUint32()); 2866 // Positive zero 2867 CompileRun("var obj = 0.0;"); 2868 obj = env->Global()->Get(v8_str("obj")); 2869 CHECK(obj->IsInt32()); 2870 CHECK(obj->IsUint32()); 2871 // Positive zero 2872 CompileRun("var obj = -0.0;"); 2873 obj = env->Global()->Get(v8_str("obj")); 2874 CHECK(!obj->IsInt32()); 2875 CHECK(!obj->IsUint32()); 2876 } 2877 2878 2879 THREADED_TEST(ConversionException) { 2880 v8::HandleScope scope; 2881 LocalContext env; 2882 CompileRun( 2883 "function TestClass() { };" 2884 "TestClass.prototype.toString = function () { throw 'uncle?'; };" 2885 "var obj = new TestClass();"); 2886 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2887 2888 v8::TryCatch try_catch; 2889 2890 Local<Value> to_string_result = obj->ToString(); 2891 CHECK(to_string_result.IsEmpty()); 2892 CheckUncle(&try_catch); 2893 2894 Local<Value> to_number_result = obj->ToNumber(); 2895 CHECK(to_number_result.IsEmpty()); 2896 CheckUncle(&try_catch); 2897 2898 Local<Value> to_integer_result = obj->ToInteger(); 2899 CHECK(to_integer_result.IsEmpty()); 2900 CheckUncle(&try_catch); 2901 2902 Local<Value> to_uint32_result = obj->ToUint32(); 2903 CHECK(to_uint32_result.IsEmpty()); 2904 CheckUncle(&try_catch); 2905 2906 Local<Value> to_int32_result = obj->ToInt32(); 2907 CHECK(to_int32_result.IsEmpty()); 2908 CheckUncle(&try_catch); 2909 2910 Local<Value> to_object_result = v8::Undefined()->ToObject(); 2911 CHECK(to_object_result.IsEmpty()); 2912 CHECK(try_catch.HasCaught()); 2913 try_catch.Reset(); 2914 2915 int32_t int32_value = obj->Int32Value(); 2916 CHECK_EQ(0, int32_value); 2917 CheckUncle(&try_catch); 2918 2919 uint32_t uint32_value = obj->Uint32Value(); 2920 CHECK_EQ(0, uint32_value); 2921 CheckUncle(&try_catch); 2922 2923 double number_value = obj->NumberValue(); 2924 CHECK_NE(0, IsNaN(number_value)); 2925 CheckUncle(&try_catch); 2926 2927 int64_t integer_value = obj->IntegerValue(); 2928 CHECK_EQ(0.0, static_cast<double>(integer_value)); 2929 CheckUncle(&try_catch); 2930 } 2931 2932 2933 v8::Handle<Value> ThrowFromC(const v8::Arguments& args) { 2934 ApiTestFuzzer::Fuzz(); 2935 return v8::ThrowException(v8_str("konto")); 2936 } 2937 2938 2939 v8::Handle<Value> CCatcher(const v8::Arguments& args) { 2940 if (args.Length() < 1) return v8::False(); 2941 v8::HandleScope scope; 2942 v8::TryCatch try_catch; 2943 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run(); 2944 CHECK(!try_catch.HasCaught() || result.IsEmpty()); 2945 return v8::Boolean::New(try_catch.HasCaught()); 2946 } 2947 2948 2949 THREADED_TEST(APICatch) { 2950 v8::HandleScope scope; 2951 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2952 templ->Set(v8_str("ThrowFromC"), 2953 v8::FunctionTemplate::New(ThrowFromC)); 2954 LocalContext context(0, templ); 2955 CompileRun( 2956 "var thrown = false;" 2957 "try {" 2958 " ThrowFromC();" 2959 "} catch (e) {" 2960 " thrown = true;" 2961 "}"); 2962 Local<Value> thrown = context->Global()->Get(v8_str("thrown")); 2963 CHECK(thrown->BooleanValue()); 2964 } 2965 2966 2967 THREADED_TEST(APIThrowTryCatch) { 2968 v8::HandleScope scope; 2969 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2970 templ->Set(v8_str("ThrowFromC"), 2971 v8::FunctionTemplate::New(ThrowFromC)); 2972 LocalContext context(0, templ); 2973 v8::TryCatch try_catch; 2974 CompileRun("ThrowFromC();"); 2975 CHECK(try_catch.HasCaught()); 2976 } 2977 2978 2979 // Test that a try-finally block doesn't shadow a try-catch block 2980 // when setting up an external handler. 2981 // 2982 // BUG(271): Some of the exception propagation does not work on the 2983 // ARM simulator because the simulator separates the C++ stack and the 2984 // JS stack. This test therefore fails on the simulator. The test is 2985 // not threaded to allow the threading tests to run on the simulator. 2986 TEST(TryCatchInTryFinally) { 2987 v8::HandleScope scope; 2988 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2989 templ->Set(v8_str("CCatcher"), 2990 v8::FunctionTemplate::New(CCatcher)); 2991 LocalContext context(0, templ); 2992 Local<Value> result = CompileRun("try {" 2993 " try {" 2994 " CCatcher('throw 7;');" 2995 " } finally {" 2996 " }" 2997 "} catch (e) {" 2998 "}"); 2999 CHECK(result->IsTrue()); 3000 } 3001 3002 3003 static void check_reference_error_message( 3004 v8::Handle<v8::Message> message, 3005 v8::Handle<v8::Value> data) { 3006 const char* reference_error = "Uncaught ReferenceError: asdf is not defined"; 3007 CHECK(message->Get()->Equals(v8_str(reference_error))); 3008 } 3009 3010 3011 static v8::Handle<Value> Fail(const v8::Arguments& args) { 3012 ApiTestFuzzer::Fuzz(); 3013 CHECK(false); 3014 return v8::Undefined(); 3015 } 3016 3017 3018 // Test that overwritten methods are not invoked on uncaught exception 3019 // formatting. However, they are invoked when performing normal error 3020 // string conversions. 3021 TEST(APIThrowMessageOverwrittenToString) { 3022 v8::HandleScope scope; 3023 v8::V8::AddMessageListener(check_reference_error_message); 3024 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3025 templ->Set(v8_str("fail"), v8::FunctionTemplate::New(Fail)); 3026 LocalContext context(NULL, templ); 3027 CompileRun("asdf;"); 3028 CompileRun("var limit = {};" 3029 "limit.valueOf = fail;" 3030 "Error.stackTraceLimit = limit;"); 3031 CompileRun("asdf"); 3032 CompileRun("Array.prototype.pop = fail;"); 3033 CompileRun("Object.prototype.hasOwnProperty = fail;"); 3034 CompileRun("Object.prototype.toString = function f() { return 'Yikes'; }"); 3035 CompileRun("Number.prototype.toString = function f() { return 'Yikes'; }"); 3036 CompileRun("String.prototype.toString = function f() { return 'Yikes'; }"); 3037 CompileRun("ReferenceError.prototype.toString =" 3038 " function() { return 'Whoops' }"); 3039 CompileRun("asdf;"); 3040 CompileRun("ReferenceError.prototype.constructor.name = void 0;"); 3041 CompileRun("asdf;"); 3042 CompileRun("ReferenceError.prototype.constructor = void 0;"); 3043 CompileRun("asdf;"); 3044 CompileRun("ReferenceError.prototype.__proto__ = new Object();"); 3045 CompileRun("asdf;"); 3046 CompileRun("ReferenceError.prototype = new Object();"); 3047 CompileRun("asdf;"); 3048 v8::Handle<Value> string = CompileRun("try { asdf; } catch(e) { e + ''; }"); 3049 CHECK(string->Equals(v8_str("Whoops"))); 3050 CompileRun("ReferenceError.prototype.constructor = new Object();" 3051 "ReferenceError.prototype.constructor.name = 1;" 3052 "Number.prototype.toString = function() { return 'Whoops'; };" 3053 "ReferenceError.prototype.toString = Object.prototype.toString;"); 3054 CompileRun("asdf;"); 3055 v8::V8::RemoveMessageListeners(check_message); 3056 } 3057 3058 3059 static void receive_message(v8::Handle<v8::Message> message, 3060 v8::Handle<v8::Value> data) { 3061 message->Get(); 3062 message_received = true; 3063 } 3064 3065 3066 TEST(APIThrowMessage) { 3067 message_received = false; 3068 v8::HandleScope scope; 3069 v8::V8::AddMessageListener(receive_message); 3070 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3071 templ->Set(v8_str("ThrowFromC"), 3072 v8::FunctionTemplate::New(ThrowFromC)); 3073 LocalContext context(0, templ); 3074 CompileRun("ThrowFromC();"); 3075 CHECK(message_received); 3076 v8::V8::RemoveMessageListeners(check_message); 3077 } 3078 3079 3080 TEST(APIThrowMessageAndVerboseTryCatch) { 3081 message_received = false; 3082 v8::HandleScope scope; 3083 v8::V8::AddMessageListener(receive_message); 3084 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3085 templ->Set(v8_str("ThrowFromC"), 3086 v8::FunctionTemplate::New(ThrowFromC)); 3087 LocalContext context(0, templ); 3088 v8::TryCatch try_catch; 3089 try_catch.SetVerbose(true); 3090 Local<Value> result = CompileRun("ThrowFromC();"); 3091 CHECK(try_catch.HasCaught()); 3092 CHECK(result.IsEmpty()); 3093 CHECK(message_received); 3094 v8::V8::RemoveMessageListeners(check_message); 3095 } 3096 3097 3098 TEST(APIStackOverflowAndVerboseTryCatch) { 3099 message_received = false; 3100 v8::HandleScope scope; 3101 v8::V8::AddMessageListener(receive_message); 3102 LocalContext context; 3103 v8::TryCatch try_catch; 3104 try_catch.SetVerbose(true); 3105 Local<Value> result = CompileRun("function foo() { foo(); } foo();"); 3106 CHECK(try_catch.HasCaught()); 3107 CHECK(result.IsEmpty()); 3108 CHECK(message_received); 3109 v8::V8::RemoveMessageListeners(receive_message); 3110 } 3111 3112 3113 THREADED_TEST(ExternalScriptException) { 3114 v8::HandleScope scope; 3115 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3116 templ->Set(v8_str("ThrowFromC"), 3117 v8::FunctionTemplate::New(ThrowFromC)); 3118 LocalContext context(0, templ); 3119 3120 v8::TryCatch try_catch; 3121 Local<Script> script 3122 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';")); 3123 Local<Value> result = script->Run(); 3124 CHECK(result.IsEmpty()); 3125 CHECK(try_catch.HasCaught()); 3126 String::AsciiValue exception_value(try_catch.Exception()); 3127 CHECK_EQ("konto", *exception_value); 3128 } 3129 3130 3131 3132 v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) { 3133 ApiTestFuzzer::Fuzz(); 3134 CHECK_EQ(4, args.Length()); 3135 int count = args[0]->Int32Value(); 3136 int cInterval = args[2]->Int32Value(); 3137 if (count == 0) { 3138 return v8::ThrowException(v8_str("FromC")); 3139 } else { 3140 Local<v8::Object> global = Context::GetCurrent()->Global(); 3141 Local<Value> fun = global->Get(v8_str("JSThrowCountDown")); 3142 v8::Handle<Value> argv[] = { v8_num(count - 1), 3143 args[1], 3144 args[2], 3145 args[3] }; 3146 if (count % cInterval == 0) { 3147 v8::TryCatch try_catch; 3148 Local<Value> result = fun.As<Function>()->Call(global, 4, argv); 3149 int expected = args[3]->Int32Value(); 3150 if (try_catch.HasCaught()) { 3151 CHECK_EQ(expected, count); 3152 CHECK(result.IsEmpty()); 3153 CHECK(!i::Isolate::Current()->has_scheduled_exception()); 3154 } else { 3155 CHECK_NE(expected, count); 3156 } 3157 return result; 3158 } else { 3159 return fun.As<Function>()->Call(global, 4, argv); 3160 } 3161 } 3162 } 3163 3164 3165 v8::Handle<Value> JSCheck(const v8::Arguments& args) { 3166 ApiTestFuzzer::Fuzz(); 3167 CHECK_EQ(3, args.Length()); 3168 bool equality = args[0]->BooleanValue(); 3169 int count = args[1]->Int32Value(); 3170 int expected = args[2]->Int32Value(); 3171 if (equality) { 3172 CHECK_EQ(count, expected); 3173 } else { 3174 CHECK_NE(count, expected); 3175 } 3176 return v8::Undefined(); 3177 } 3178 3179 3180 THREADED_TEST(EvalInTryFinally) { 3181 v8::HandleScope scope; 3182 LocalContext context; 3183 v8::TryCatch try_catch; 3184 CompileRun("(function() {" 3185 " try {" 3186 " eval('asldkf (*&^&*^');" 3187 " } finally {" 3188 " return;" 3189 " }" 3190 "})()"); 3191 CHECK(!try_catch.HasCaught()); 3192 } 3193 3194 3195 // This test works by making a stack of alternating JavaScript and C 3196 // activations. These activations set up exception handlers with regular 3197 // intervals, one interval for C activations and another for JavaScript 3198 // activations. When enough activations have been created an exception is 3199 // thrown and we check that the right activation catches the exception and that 3200 // no other activations do. The right activation is always the topmost one with 3201 // a handler, regardless of whether it is in JavaScript or C. 3202 // 3203 // The notation used to describe a test case looks like this: 3204 // 3205 // *JS[4] *C[3] @JS[2] C[1] JS[0] 3206 // 3207 // Each entry is an activation, either JS or C. The index is the count at that 3208 // level. Stars identify activations with exception handlers, the @ identifies 3209 // the exception handler that should catch the exception. 3210 // 3211 // BUG(271): Some of the exception propagation does not work on the 3212 // ARM simulator because the simulator separates the C++ stack and the 3213 // JS stack. This test therefore fails on the simulator. The test is 3214 // not threaded to allow the threading tests to run on the simulator. 3215 TEST(ExceptionOrder) { 3216 v8::HandleScope scope; 3217 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3218 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck)); 3219 templ->Set(v8_str("CThrowCountDown"), 3220 v8::FunctionTemplate::New(CThrowCountDown)); 3221 LocalContext context(0, templ); 3222 CompileRun( 3223 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {" 3224 " if (count == 0) throw 'FromJS';" 3225 " if (count % jsInterval == 0) {" 3226 " try {" 3227 " var value = CThrowCountDown(count - 1," 3228 " jsInterval," 3229 " cInterval," 3230 " expected);" 3231 " check(false, count, expected);" 3232 " return value;" 3233 " } catch (e) {" 3234 " check(true, count, expected);" 3235 " }" 3236 " } else {" 3237 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);" 3238 " }" 3239 "}"); 3240 Local<Function> fun = 3241 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown"))); 3242 3243 const int argc = 4; 3244 // count jsInterval cInterval expected 3245 3246 // *JS[4] *C[3] @JS[2] C[1] JS[0] 3247 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) }; 3248 fun->Call(fun, argc, a0); 3249 3250 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0] 3251 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) }; 3252 fun->Call(fun, argc, a1); 3253 3254 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0] 3255 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) }; 3256 fun->Call(fun, argc, a2); 3257 3258 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0] 3259 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) }; 3260 fun->Call(fun, argc, a3); 3261 3262 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0] 3263 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) }; 3264 fun->Call(fun, argc, a4); 3265 3266 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0] 3267 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) }; 3268 fun->Call(fun, argc, a5); 3269 } 3270 3271 3272 v8::Handle<Value> ThrowValue(const v8::Arguments& args) { 3273 ApiTestFuzzer::Fuzz(); 3274 CHECK_EQ(1, args.Length()); 3275 return v8::ThrowException(args[0]); 3276 } 3277 3278 3279 THREADED_TEST(ThrowValues) { 3280 v8::HandleScope scope; 3281 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3282 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue)); 3283 LocalContext context(0, templ); 3284 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 3285 "function Run(obj) {" 3286 " try {" 3287 " Throw(obj);" 3288 " } catch (e) {" 3289 " return e;" 3290 " }" 3291 " return 'no exception';" 3292 "}" 3293 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];")); 3294 CHECK_EQ(5, result->Length()); 3295 CHECK(result->Get(v8::Integer::New(0))->IsString()); 3296 CHECK(result->Get(v8::Integer::New(1))->IsNumber()); 3297 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value()); 3298 CHECK(result->Get(v8::Integer::New(2))->IsNumber()); 3299 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value()); 3300 CHECK(result->Get(v8::Integer::New(3))->IsNull()); 3301 CHECK(result->Get(v8::Integer::New(4))->IsUndefined()); 3302 } 3303 3304 3305 THREADED_TEST(CatchZero) { 3306 v8::HandleScope scope; 3307 LocalContext context; 3308 v8::TryCatch try_catch; 3309 CHECK(!try_catch.HasCaught()); 3310 Script::Compile(v8_str("throw 10"))->Run(); 3311 CHECK(try_catch.HasCaught()); 3312 CHECK_EQ(10, try_catch.Exception()->Int32Value()); 3313 try_catch.Reset(); 3314 CHECK(!try_catch.HasCaught()); 3315 Script::Compile(v8_str("throw 0"))->Run(); 3316 CHECK(try_catch.HasCaught()); 3317 CHECK_EQ(0, try_catch.Exception()->Int32Value()); 3318 } 3319 3320 3321 THREADED_TEST(CatchExceptionFromWith) { 3322 v8::HandleScope scope; 3323 LocalContext context; 3324 v8::TryCatch try_catch; 3325 CHECK(!try_catch.HasCaught()); 3326 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run(); 3327 CHECK(try_catch.HasCaught()); 3328 } 3329 3330 3331 THREADED_TEST(TryCatchAndFinallyHidingException) { 3332 v8::HandleScope scope; 3333 LocalContext context; 3334 v8::TryCatch try_catch; 3335 CHECK(!try_catch.HasCaught()); 3336 CompileRun("function f(k) { try { this[k]; } finally { return 0; } };"); 3337 CompileRun("f({toString: function() { throw 42; }});"); 3338 CHECK(!try_catch.HasCaught()); 3339 } 3340 3341 3342 v8::Handle<v8::Value> WithTryCatch(const v8::Arguments& args) { 3343 v8::TryCatch try_catch; 3344 return v8::Undefined(); 3345 } 3346 3347 3348 THREADED_TEST(TryCatchAndFinally) { 3349 v8::HandleScope scope; 3350 LocalContext context; 3351 context->Global()->Set( 3352 v8_str("native_with_try_catch"), 3353 v8::FunctionTemplate::New(WithTryCatch)->GetFunction()); 3354 v8::TryCatch try_catch; 3355 CHECK(!try_catch.HasCaught()); 3356 CompileRun( 3357 "try {\n" 3358 " throw new Error('a');\n" 3359 "} finally {\n" 3360 " native_with_try_catch();\n" 3361 "}\n"); 3362 CHECK(try_catch.HasCaught()); 3363 } 3364 3365 3366 THREADED_TEST(Equality) { 3367 v8::HandleScope scope; 3368 LocalContext context; 3369 // Check that equality works at all before relying on CHECK_EQ 3370 CHECK(v8_str("a")->Equals(v8_str("a"))); 3371 CHECK(!v8_str("a")->Equals(v8_str("b"))); 3372 3373 CHECK_EQ(v8_str("a"), v8_str("a")); 3374 CHECK_NE(v8_str("a"), v8_str("b")); 3375 CHECK_EQ(v8_num(1), v8_num(1)); 3376 CHECK_EQ(v8_num(1.00), v8_num(1)); 3377 CHECK_NE(v8_num(1), v8_num(2)); 3378 3379 // Assume String is not symbol. 3380 CHECK(v8_str("a")->StrictEquals(v8_str("a"))); 3381 CHECK(!v8_str("a")->StrictEquals(v8_str("b"))); 3382 CHECK(!v8_str("5")->StrictEquals(v8_num(5))); 3383 CHECK(v8_num(1)->StrictEquals(v8_num(1))); 3384 CHECK(!v8_num(1)->StrictEquals(v8_num(2))); 3385 CHECK(v8_num(0)->StrictEquals(v8_num(-0))); 3386 Local<Value> not_a_number = v8_num(i::OS::nan_value()); 3387 CHECK(!not_a_number->StrictEquals(not_a_number)); 3388 CHECK(v8::False()->StrictEquals(v8::False())); 3389 CHECK(!v8::False()->StrictEquals(v8::Undefined())); 3390 3391 v8::Handle<v8::Object> obj = v8::Object::New(); 3392 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj); 3393 CHECK(alias->StrictEquals(obj)); 3394 alias.Dispose(); 3395 } 3396 3397 3398 THREADED_TEST(MultiRun) { 3399 v8::HandleScope scope; 3400 LocalContext context; 3401 Local<Script> script = Script::Compile(v8_str("x")); 3402 for (int i = 0; i < 10; i++) 3403 script->Run(); 3404 } 3405 3406 3407 static v8::Handle<Value> GetXValue(Local<String> name, 3408 const AccessorInfo& info) { 3409 ApiTestFuzzer::Fuzz(); 3410 CHECK_EQ(info.Data(), v8_str("donut")); 3411 CHECK_EQ(name, v8_str("x")); 3412 return name; 3413 } 3414 3415 3416 THREADED_TEST(SimplePropertyRead) { 3417 v8::HandleScope scope; 3418 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3419 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 3420 LocalContext context; 3421 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3422 Local<Script> script = Script::Compile(v8_str("obj.x")); 3423 for (int i = 0; i < 10; i++) { 3424 Local<Value> result = script->Run(); 3425 CHECK_EQ(result, v8_str("x")); 3426 } 3427 } 3428 3429 THREADED_TEST(DefinePropertyOnAPIAccessor) { 3430 v8::HandleScope scope; 3431 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3432 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 3433 LocalContext context; 3434 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3435 3436 // Uses getOwnPropertyDescriptor to check the configurable status 3437 Local<Script> script_desc 3438 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( " 3439 "obj, 'x');" 3440 "prop.configurable;")); 3441 Local<Value> result = script_desc->Run(); 3442 CHECK_EQ(result->BooleanValue(), true); 3443 3444 // Redefine get - but still configurable 3445 Local<Script> script_define 3446 = Script::Compile(v8_str("var desc = { get: function(){return 42; }," 3447 " configurable: true };" 3448 "Object.defineProperty(obj, 'x', desc);" 3449 "obj.x")); 3450 result = script_define->Run(); 3451 CHECK_EQ(result, v8_num(42)); 3452 3453 // Check that the accessor is still configurable 3454 result = script_desc->Run(); 3455 CHECK_EQ(result->BooleanValue(), true); 3456 3457 // Redefine to a non-configurable 3458 script_define 3459 = Script::Compile(v8_str("var desc = { get: function(){return 43; }," 3460 " configurable: false };" 3461 "Object.defineProperty(obj, 'x', desc);" 3462 "obj.x")); 3463 result = script_define->Run(); 3464 CHECK_EQ(result, v8_num(43)); 3465 result = script_desc->Run(); 3466 CHECK_EQ(result->BooleanValue(), false); 3467 3468 // Make sure that it is not possible to redefine again 3469 v8::TryCatch try_catch; 3470 result = script_define->Run(); 3471 CHECK(try_catch.HasCaught()); 3472 String::AsciiValue exception_value(try_catch.Exception()); 3473 CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); 3474 } 3475 3476 THREADED_TEST(DefinePropertyOnDefineGetterSetter) { 3477 v8::HandleScope scope; 3478 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3479 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 3480 LocalContext context; 3481 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3482 3483 Local<Script> script_desc = Script::Compile(v8_str("var prop =" 3484 "Object.getOwnPropertyDescriptor( " 3485 "obj, 'x');" 3486 "prop.configurable;")); 3487 Local<Value> result = script_desc->Run(); 3488 CHECK_EQ(result->BooleanValue(), true); 3489 3490 Local<Script> script_define = 3491 Script::Compile(v8_str("var desc = {get: function(){return 42; }," 3492 " configurable: true };" 3493 "Object.defineProperty(obj, 'x', desc);" 3494 "obj.x")); 3495 result = script_define->Run(); 3496 CHECK_EQ(result, v8_num(42)); 3497 3498 3499 result = script_desc->Run(); 3500 CHECK_EQ(result->BooleanValue(), true); 3501 3502 3503 script_define = 3504 Script::Compile(v8_str("var desc = {get: function(){return 43; }," 3505 " configurable: false };" 3506 "Object.defineProperty(obj, 'x', desc);" 3507 "obj.x")); 3508 result = script_define->Run(); 3509 CHECK_EQ(result, v8_num(43)); 3510 result = script_desc->Run(); 3511 3512 CHECK_EQ(result->BooleanValue(), false); 3513 3514 v8::TryCatch try_catch; 3515 result = script_define->Run(); 3516 CHECK(try_catch.HasCaught()); 3517 String::AsciiValue exception_value(try_catch.Exception()); 3518 CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); 3519 } 3520 3521 3522 static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context, 3523 char const* name) { 3524 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name))); 3525 } 3526 3527 3528 THREADED_TEST(DefineAPIAccessorOnObject) { 3529 v8::HandleScope scope; 3530 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3531 LocalContext context; 3532 3533 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 3534 CompileRun("var obj2 = {};"); 3535 3536 CHECK(CompileRun("obj1.x")->IsUndefined()); 3537 CHECK(CompileRun("obj2.x")->IsUndefined()); 3538 3539 CHECK(GetGlobalProperty(&context, "obj1")-> 3540 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3541 3542 ExpectString("obj1.x", "x"); 3543 CHECK(CompileRun("obj2.x")->IsUndefined()); 3544 3545 CHECK(GetGlobalProperty(&context, "obj2")-> 3546 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3547 3548 ExpectString("obj1.x", "x"); 3549 ExpectString("obj2.x", "x"); 3550 3551 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 3552 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 3553 3554 CompileRun("Object.defineProperty(obj1, 'x'," 3555 "{ get: function() { return 'y'; }, configurable: true })"); 3556 3557 ExpectString("obj1.x", "y"); 3558 ExpectString("obj2.x", "x"); 3559 3560 CompileRun("Object.defineProperty(obj2, 'x'," 3561 "{ get: function() { return 'y'; }, configurable: true })"); 3562 3563 ExpectString("obj1.x", "y"); 3564 ExpectString("obj2.x", "y"); 3565 3566 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 3567 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 3568 3569 CHECK(GetGlobalProperty(&context, "obj1")-> 3570 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3571 CHECK(GetGlobalProperty(&context, "obj2")-> 3572 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3573 3574 ExpectString("obj1.x", "x"); 3575 ExpectString("obj2.x", "x"); 3576 3577 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 3578 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 3579 3580 // Define getters/setters, but now make them not configurable. 3581 CompileRun("Object.defineProperty(obj1, 'x'," 3582 "{ get: function() { return 'z'; }, configurable: false })"); 3583 CompileRun("Object.defineProperty(obj2, 'x'," 3584 "{ get: function() { return 'z'; }, configurable: false })"); 3585 3586 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 3587 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 3588 3589 ExpectString("obj1.x", "z"); 3590 ExpectString("obj2.x", "z"); 3591 3592 CHECK(!GetGlobalProperty(&context, "obj1")-> 3593 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3594 CHECK(!GetGlobalProperty(&context, "obj2")-> 3595 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3596 3597 ExpectString("obj1.x", "z"); 3598 ExpectString("obj2.x", "z"); 3599 } 3600 3601 3602 THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) { 3603 v8::HandleScope scope; 3604 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3605 LocalContext context; 3606 3607 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 3608 CompileRun("var obj2 = {};"); 3609 3610 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 3611 v8_str("x"), 3612 GetXValue, NULL, 3613 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 3614 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 3615 v8_str("x"), 3616 GetXValue, NULL, 3617 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 3618 3619 ExpectString("obj1.x", "x"); 3620 ExpectString("obj2.x", "x"); 3621 3622 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 3623 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 3624 3625 CHECK(!GetGlobalProperty(&context, "obj1")-> 3626 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3627 CHECK(!GetGlobalProperty(&context, "obj2")-> 3628 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3629 3630 { 3631 v8::TryCatch try_catch; 3632 CompileRun("Object.defineProperty(obj1, 'x'," 3633 "{get: function() { return 'func'; }})"); 3634 CHECK(try_catch.HasCaught()); 3635 String::AsciiValue exception_value(try_catch.Exception()); 3636 CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); 3637 } 3638 { 3639 v8::TryCatch try_catch; 3640 CompileRun("Object.defineProperty(obj2, 'x'," 3641 "{get: function() { return 'func'; }})"); 3642 CHECK(try_catch.HasCaught()); 3643 String::AsciiValue exception_value(try_catch.Exception()); 3644 CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); 3645 } 3646 } 3647 3648 3649 static v8::Handle<Value> Get239Value(Local<String> name, 3650 const AccessorInfo& info) { 3651 ApiTestFuzzer::Fuzz(); 3652 CHECK_EQ(info.Data(), v8_str("donut")); 3653 CHECK_EQ(name, v8_str("239")); 3654 return name; 3655 } 3656 3657 3658 THREADED_TEST(ElementAPIAccessor) { 3659 v8::HandleScope scope; 3660 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3661 LocalContext context; 3662 3663 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 3664 CompileRun("var obj2 = {};"); 3665 3666 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 3667 v8_str("239"), 3668 Get239Value, NULL, 3669 v8_str("donut"))); 3670 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 3671 v8_str("239"), 3672 Get239Value, NULL, 3673 v8_str("donut"))); 3674 3675 ExpectString("obj1[239]", "239"); 3676 ExpectString("obj2[239]", "239"); 3677 ExpectString("obj1['239']", "239"); 3678 ExpectString("obj2['239']", "239"); 3679 } 3680 3681 3682 v8::Persistent<Value> xValue; 3683 3684 3685 static void SetXValue(Local<String> name, 3686 Local<Value> value, 3687 const AccessorInfo& info) { 3688 CHECK_EQ(value, v8_num(4)); 3689 CHECK_EQ(info.Data(), v8_str("donut")); 3690 CHECK_EQ(name, v8_str("x")); 3691 CHECK(xValue.IsEmpty()); 3692 xValue = v8::Persistent<Value>::New(value); 3693 } 3694 3695 3696 THREADED_TEST(SimplePropertyWrite) { 3697 v8::HandleScope scope; 3698 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3699 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut")); 3700 LocalContext context; 3701 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3702 Local<Script> script = Script::Compile(v8_str("obj.x = 4")); 3703 for (int i = 0; i < 10; i++) { 3704 CHECK(xValue.IsEmpty()); 3705 script->Run(); 3706 CHECK_EQ(v8_num(4), xValue); 3707 xValue.Dispose(); 3708 xValue = v8::Persistent<Value>(); 3709 } 3710 } 3711 3712 3713 static v8::Handle<Value> XPropertyGetter(Local<String> property, 3714 const AccessorInfo& info) { 3715 ApiTestFuzzer::Fuzz(); 3716 CHECK(info.Data()->IsUndefined()); 3717 return property; 3718 } 3719 3720 3721 THREADED_TEST(NamedInterceptorPropertyRead) { 3722 v8::HandleScope scope; 3723 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3724 templ->SetNamedPropertyHandler(XPropertyGetter); 3725 LocalContext context; 3726 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3727 Local<Script> script = Script::Compile(v8_str("obj.x")); 3728 for (int i = 0; i < 10; i++) { 3729 Local<Value> result = script->Run(); 3730 CHECK_EQ(result, v8_str("x")); 3731 } 3732 } 3733 3734 3735 THREADED_TEST(NamedInterceptorDictionaryIC) { 3736 v8::HandleScope scope; 3737 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3738 templ->SetNamedPropertyHandler(XPropertyGetter); 3739 LocalContext context; 3740 // Create an object with a named interceptor. 3741 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance()); 3742 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x")); 3743 for (int i = 0; i < 10; i++) { 3744 Local<Value> result = script->Run(); 3745 CHECK_EQ(result, v8_str("x")); 3746 } 3747 // Create a slow case object and a function accessing a property in 3748 // that slow case object (with dictionary probing in generated 3749 // code). Then force object with a named interceptor into slow-case, 3750 // pass it to the function, and check that the interceptor is called 3751 // instead of accessing the local property. 3752 Local<Value> result = 3753 CompileRun("function get_x(o) { return o.x; };" 3754 "var obj = { x : 42, y : 0 };" 3755 "delete obj.y;" 3756 "for (var i = 0; i < 10; i++) get_x(obj);" 3757 "interceptor_obj.x = 42;" 3758 "interceptor_obj.y = 10;" 3759 "delete interceptor_obj.y;" 3760 "get_x(interceptor_obj)"); 3761 CHECK_EQ(result, v8_str("x")); 3762 } 3763 3764 3765 THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) { 3766 v8::HandleScope scope; 3767 3768 v8::Persistent<Context> context1 = Context::New(); 3769 3770 context1->Enter(); 3771 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3772 templ->SetNamedPropertyHandler(XPropertyGetter); 3773 // Create an object with a named interceptor. 3774 v8::Local<v8::Object> object = templ->NewInstance(); 3775 context1->Global()->Set(v8_str("interceptor_obj"), object); 3776 3777 // Force the object into the slow case. 3778 CompileRun("interceptor_obj.y = 0;" 3779 "delete interceptor_obj.y;"); 3780 context1->Exit(); 3781 3782 { 3783 // Introduce the object into a different context. 3784 // Repeat named loads to exercise ICs. 3785 LocalContext context2; 3786 context2->Global()->Set(v8_str("interceptor_obj"), object); 3787 Local<Value> result = 3788 CompileRun("function get_x(o) { return o.x; }" 3789 "interceptor_obj.x = 42;" 3790 "for (var i=0; i != 10; i++) {" 3791 " get_x(interceptor_obj);" 3792 "}" 3793 "get_x(interceptor_obj)"); 3794 // Check that the interceptor was actually invoked. 3795 CHECK_EQ(result, v8_str("x")); 3796 } 3797 3798 // Return to the original context and force some object to the slow case 3799 // to cause the NormalizedMapCache to verify. 3800 context1->Enter(); 3801 CompileRun("var obj = { x : 0 }; delete obj.x;"); 3802 context1->Exit(); 3803 3804 context1.Dispose(); 3805 } 3806 3807 3808 static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property, 3809 const AccessorInfo& info) { 3810 // Set x on the prototype object and do not handle the get request. 3811 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype(); 3812 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23)); 3813 return v8::Handle<Value>(); 3814 } 3815 3816 3817 // This is a regression test for http://crbug.com/20104. Map 3818 // transitions should not interfere with post interceptor lookup. 3819 THREADED_TEST(NamedInterceptorMapTransitionRead) { 3820 v8::HandleScope scope; 3821 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(); 3822 Local<v8::ObjectTemplate> instance_template 3823 = function_template->InstanceTemplate(); 3824 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter); 3825 LocalContext context; 3826 context->Global()->Set(v8_str("F"), function_template->GetFunction()); 3827 // Create an instance of F and introduce a map transition for x. 3828 CompileRun("var o = new F(); o.x = 23;"); 3829 // Create an instance of F and invoke the getter. The result should be 23. 3830 Local<Value> result = CompileRun("o = new F(); o.x"); 3831 CHECK_EQ(result->Int32Value(), 23); 3832 } 3833 3834 3835 static v8::Handle<Value> IndexedPropertyGetter(uint32_t index, 3836 const AccessorInfo& info) { 3837 ApiTestFuzzer::Fuzz(); 3838 if (index == 37) { 3839 return v8::Handle<Value>(v8_num(625)); 3840 } 3841 return v8::Handle<Value>(); 3842 } 3843 3844 3845 static v8::Handle<Value> IndexedPropertySetter(uint32_t index, 3846 Local<Value> value, 3847 const AccessorInfo& info) { 3848 ApiTestFuzzer::Fuzz(); 3849 if (index == 39) { 3850 return value; 3851 } 3852 return v8::Handle<Value>(); 3853 } 3854 3855 3856 THREADED_TEST(IndexedInterceptorWithIndexedAccessor) { 3857 v8::HandleScope scope; 3858 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3859 templ->SetIndexedPropertyHandler(IndexedPropertyGetter, 3860 IndexedPropertySetter); 3861 LocalContext context; 3862 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3863 Local<Script> getter_script = Script::Compile(v8_str( 3864 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];")); 3865 Local<Script> setter_script = Script::Compile(v8_str( 3866 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});" 3867 "obj[17] = 23;" 3868 "obj.foo;")); 3869 Local<Script> interceptor_setter_script = Script::Compile(v8_str( 3870 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});" 3871 "obj[39] = 47;" 3872 "obj.foo;")); // This setter should not run, due to the interceptor. 3873 Local<Script> interceptor_getter_script = Script::Compile(v8_str( 3874 "obj[37];")); 3875 Local<Value> result = getter_script->Run(); 3876 CHECK_EQ(v8_num(5), result); 3877 result = setter_script->Run(); 3878 CHECK_EQ(v8_num(23), result); 3879 result = interceptor_setter_script->Run(); 3880 CHECK_EQ(v8_num(23), result); 3881 result = interceptor_getter_script->Run(); 3882 CHECK_EQ(v8_num(625), result); 3883 } 3884 3885 3886 static v8::Handle<Value> UnboxedDoubleIndexedPropertyGetter( 3887 uint32_t index, 3888 const AccessorInfo& info) { 3889 ApiTestFuzzer::Fuzz(); 3890 if (index < 25) { 3891 return v8::Handle<Value>(v8_num(index)); 3892 } 3893 return v8::Handle<Value>(); 3894 } 3895 3896 3897 static v8::Handle<Value> UnboxedDoubleIndexedPropertySetter( 3898 uint32_t index, 3899 Local<Value> value, 3900 const AccessorInfo& info) { 3901 ApiTestFuzzer::Fuzz(); 3902 if (index < 25) { 3903 return v8::Handle<Value>(v8_num(index)); 3904 } 3905 return v8::Handle<Value>(); 3906 } 3907 3908 3909 Handle<v8::Array> UnboxedDoubleIndexedPropertyEnumerator( 3910 const AccessorInfo& info) { 3911 // Force the list of returned keys to be stored in a FastDoubleArray. 3912 Local<Script> indexed_property_names_script = Script::Compile(v8_str( 3913 "keys = new Array(); keys[125000] = 1;" 3914 "for(i = 0; i < 80000; i++) { keys[i] = i; };" 3915 "keys.length = 25; keys;")); 3916 Local<Value> result = indexed_property_names_script->Run(); 3917 return Local<v8::Array>(::v8::Array::Cast(*result)); 3918 } 3919 3920 3921 // Make sure that the the interceptor code in the runtime properly handles 3922 // merging property name lists for double-array-backed arrays. 3923 THREADED_TEST(IndexedInterceptorUnboxedDoubleWithIndexedAccessor) { 3924 v8::HandleScope scope; 3925 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3926 templ->SetIndexedPropertyHandler(UnboxedDoubleIndexedPropertyGetter, 3927 UnboxedDoubleIndexedPropertySetter, 3928 0, 3929 0, 3930 UnboxedDoubleIndexedPropertyEnumerator); 3931 LocalContext context; 3932 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3933 // When obj is created, force it to be Stored in a FastDoubleArray. 3934 Local<Script> create_unboxed_double_script = Script::Compile(v8_str( 3935 "obj[125000] = 1; for(i = 0; i < 80000; i+=2) { obj[i] = i; } " 3936 "key_count = 0; " 3937 "for (x in obj) {key_count++;};" 3938 "obj;")); 3939 Local<Value> result = create_unboxed_double_script->Run(); 3940 CHECK(result->ToObject()->HasRealIndexedProperty(2000)); 3941 Local<Script> key_count_check = Script::Compile(v8_str( 3942 "key_count;")); 3943 result = key_count_check->Run(); 3944 CHECK_EQ(v8_num(40013), result); 3945 } 3946 3947 3948 Handle<v8::Array> NonStrictArgsIndexedPropertyEnumerator( 3949 const AccessorInfo& info) { 3950 // Force the list of returned keys to be stored in a Arguments object. 3951 Local<Script> indexed_property_names_script = Script::Compile(v8_str( 3952 "function f(w,x) {" 3953 " return arguments;" 3954 "}" 3955 "keys = f(0, 1, 2, 3);" 3956 "keys;")); 3957 Local<Value> result = indexed_property_names_script->Run(); 3958 return Local<v8::Array>(static_cast<v8::Array*>(::v8::Object::Cast(*result))); 3959 } 3960 3961 3962 static v8::Handle<Value> NonStrictIndexedPropertyGetter( 3963 uint32_t index, 3964 const AccessorInfo& info) { 3965 ApiTestFuzzer::Fuzz(); 3966 if (index < 4) { 3967 return v8::Handle<Value>(v8_num(index)); 3968 } 3969 return v8::Handle<Value>(); 3970 } 3971 3972 3973 // Make sure that the the interceptor code in the runtime properly handles 3974 // merging property name lists for non-string arguments arrays. 3975 THREADED_TEST(IndexedInterceptorNonStrictArgsWithIndexedAccessor) { 3976 v8::HandleScope scope; 3977 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3978 templ->SetIndexedPropertyHandler(NonStrictIndexedPropertyGetter, 3979 0, 3980 0, 3981 0, 3982 NonStrictArgsIndexedPropertyEnumerator); 3983 LocalContext context; 3984 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3985 Local<Script> create_args_script = 3986 Script::Compile(v8_str( 3987 "var key_count = 0;" 3988 "for (x in obj) {key_count++;} key_count;")); 3989 Local<Value> result = create_args_script->Run(); 3990 CHECK_EQ(v8_num(4), result); 3991 } 3992 3993 3994 static v8::Handle<Value> IdentityIndexedPropertyGetter( 3995 uint32_t index, 3996 const AccessorInfo& info) { 3997 return v8::Integer::NewFromUnsigned(index); 3998 } 3999 4000 4001 THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) { 4002 v8::HandleScope scope; 4003 Local<ObjectTemplate> templ = ObjectTemplate::New(); 4004 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 4005 4006 LocalContext context; 4007 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 4008 4009 // Check fast object case. 4010 const char* fast_case_code = 4011 "Object.getOwnPropertyDescriptor(obj, 0).value.toString()"; 4012 ExpectString(fast_case_code, "0"); 4013 4014 // Check slow case. 4015 const char* slow_case_code = 4016 "obj.x = 1; delete obj.x;" 4017 "Object.getOwnPropertyDescriptor(obj, 1).value.toString()"; 4018 ExpectString(slow_case_code, "1"); 4019 } 4020 4021 4022 THREADED_TEST(IndexedInterceptorWithNoSetter) { 4023 v8::HandleScope scope; 4024 Local<ObjectTemplate> templ = ObjectTemplate::New(); 4025 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 4026 4027 LocalContext context; 4028 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 4029 4030 const char* code = 4031 "try {" 4032 " obj[0] = 239;" 4033 " for (var i = 0; i < 100; i++) {" 4034 " var v = obj[0];" 4035 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;" 4036 " }" 4037 " 'PASSED'" 4038 "} catch(e) {" 4039 " e" 4040 "}"; 4041 ExpectString(code, "PASSED"); 4042 } 4043 4044 4045 THREADED_TEST(IndexedInterceptorWithAccessorCheck) { 4046 v8::HandleScope scope; 4047 Local<ObjectTemplate> templ = ObjectTemplate::New(); 4048 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 4049 4050 LocalContext context; 4051 Local<v8::Object> obj = templ->NewInstance(); 4052 obj->TurnOnAccessCheck(); 4053 context->Global()->Set(v8_str("obj"), obj); 4054 4055 const char* code = 4056 "try {" 4057 " for (var i = 0; i < 100; i++) {" 4058 " var v = obj[0];" 4059 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;" 4060 " }" 4061 " 'PASSED'" 4062 "} catch(e) {" 4063 " e" 4064 "}"; 4065 ExpectString(code, "PASSED"); 4066 } 4067 4068 4069 THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) { 4070 i::FLAG_allow_natives_syntax = true; 4071 v8::HandleScope scope; 4072 Local<ObjectTemplate> templ = ObjectTemplate::New(); 4073 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 4074 4075 LocalContext context; 4076 Local<v8::Object> obj = templ->NewInstance(); 4077 context->Global()->Set(v8_str("obj"), obj); 4078 4079 const char* code = 4080 "try {" 4081 " for (var i = 0; i < 100; i++) {" 4082 " var expected = i;" 4083 " if (i == 5) {" 4084 " %EnableAccessChecks(obj);" 4085 " expected = undefined;" 4086 " }" 4087 " var v = obj[i];" 4088 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 4089 " if (i == 5) %DisableAccessChecks(obj);" 4090 " }" 4091 " 'PASSED'" 4092 "} catch(e) {" 4093 " e" 4094 "}"; 4095 ExpectString(code, "PASSED"); 4096 } 4097 4098 4099 THREADED_TEST(IndexedInterceptorWithDifferentIndices) { 4100 v8::HandleScope scope; 4101 Local<ObjectTemplate> templ = ObjectTemplate::New(); 4102 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 4103 4104 LocalContext context; 4105 Local<v8::Object> obj = templ->NewInstance(); 4106 context->Global()->Set(v8_str("obj"), obj); 4107 4108 const char* code = 4109 "try {" 4110 " for (var i = 0; i < 100; i++) {" 4111 " var v = obj[i];" 4112 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 4113 " }" 4114 " 'PASSED'" 4115 "} catch(e) {" 4116 " e" 4117 "}"; 4118 ExpectString(code, "PASSED"); 4119 } 4120 4121 4122 THREADED_TEST(IndexedInterceptorWithNegativeIndices) { 4123 v8::HandleScope scope; 4124 Local<ObjectTemplate> templ = ObjectTemplate::New(); 4125 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 4126 4127 LocalContext context; 4128 Local<v8::Object> obj = templ->NewInstance(); 4129 context->Global()->Set(v8_str("obj"), obj); 4130 4131 const char* code = 4132 "try {" 4133 " for (var i = 0; i < 100; i++) {" 4134 " var expected = i;" 4135 " var key = i;" 4136 " if (i == 25) {" 4137 " key = -1;" 4138 " expected = undefined;" 4139 " }" 4140 " if (i == 50) {" 4141 " /* probe minimal Smi number on 32-bit platforms */" 4142 " key = -(1 << 30);" 4143 " expected = undefined;" 4144 " }" 4145 " if (i == 75) {" 4146 " /* probe minimal Smi number on 64-bit platforms */" 4147 " key = 1 << 31;" 4148 " expected = undefined;" 4149 " }" 4150 " var v = obj[key];" 4151 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 4152 " }" 4153 " 'PASSED'" 4154 "} catch(e) {" 4155 " e" 4156 "}"; 4157 ExpectString(code, "PASSED"); 4158 } 4159 4160 4161 THREADED_TEST(IndexedInterceptorWithNotSmiLookup) { 4162 v8::HandleScope scope; 4163 Local<ObjectTemplate> templ = ObjectTemplate::New(); 4164 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 4165 4166 LocalContext context; 4167 Local<v8::Object> obj = templ->NewInstance(); 4168 context->Global()->Set(v8_str("obj"), obj); 4169 4170 const char* code = 4171 "try {" 4172 " for (var i = 0; i < 100; i++) {" 4173 " var expected = i;" 4174 " var key = i;" 4175 " if (i == 50) {" 4176 " key = 'foobar';" 4177 " expected = undefined;" 4178 " }" 4179 " var v = obj[key];" 4180 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 4181 " }" 4182 " 'PASSED'" 4183 "} catch(e) {" 4184 " e" 4185 "}"; 4186 ExpectString(code, "PASSED"); 4187 } 4188 4189 4190 THREADED_TEST(IndexedInterceptorGoingMegamorphic) { 4191 v8::HandleScope scope; 4192 Local<ObjectTemplate> templ = ObjectTemplate::New(); 4193 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 4194 4195 LocalContext context; 4196 Local<v8::Object> obj = templ->NewInstance(); 4197 context->Global()->Set(v8_str("obj"), obj); 4198 4199 const char* code = 4200 "var original = obj;" 4201 "try {" 4202 " for (var i = 0; i < 100; i++) {" 4203 " var expected = i;" 4204 " if (i == 50) {" 4205 " obj = {50: 'foobar'};" 4206 " expected = 'foobar';" 4207 " }" 4208 " var v = obj[i];" 4209 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 4210 " if (i == 50) obj = original;" 4211 " }" 4212 " 'PASSED'" 4213 "} catch(e) {" 4214 " e" 4215 "}"; 4216 ExpectString(code, "PASSED"); 4217 } 4218 4219 4220 THREADED_TEST(IndexedInterceptorReceiverTurningSmi) { 4221 v8::HandleScope scope; 4222 Local<ObjectTemplate> templ = ObjectTemplate::New(); 4223 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 4224 4225 LocalContext context; 4226 Local<v8::Object> obj = templ->NewInstance(); 4227 context->Global()->Set(v8_str("obj"), obj); 4228 4229 const char* code = 4230 "var original = obj;" 4231 "try {" 4232 " for (var i = 0; i < 100; i++) {" 4233 " var expected = i;" 4234 " if (i == 5) {" 4235 " obj = 239;" 4236 " expected = undefined;" 4237 " }" 4238 " var v = obj[i];" 4239 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 4240 " if (i == 5) obj = original;" 4241 " }" 4242 " 'PASSED'" 4243 "} catch(e) {" 4244 " e" 4245 "}"; 4246 ExpectString(code, "PASSED"); 4247 } 4248 4249 4250 THREADED_TEST(IndexedInterceptorOnProto) { 4251 v8::HandleScope scope; 4252 Local<ObjectTemplate> templ = ObjectTemplate::New(); 4253 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 4254 4255 LocalContext context; 4256 Local<v8::Object> obj = templ->NewInstance(); 4257 context->Global()->Set(v8_str("obj"), obj); 4258 4259 const char* code = 4260 "var o = {__proto__: obj};" 4261 "try {" 4262 " for (var i = 0; i < 100; i++) {" 4263 " var v = o[i];" 4264 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 4265 " }" 4266 " 'PASSED'" 4267 "} catch(e) {" 4268 " e" 4269 "}"; 4270 ExpectString(code, "PASSED"); 4271 } 4272 4273 4274 THREADED_TEST(MultiContexts) { 4275 v8::HandleScope scope; 4276 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New(); 4277 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler)); 4278 4279 Local<String> password = v8_str("Password"); 4280 4281 // Create an environment 4282 LocalContext context0(0, templ); 4283 context0->SetSecurityToken(password); 4284 v8::Handle<v8::Object> global0 = context0->Global(); 4285 global0->Set(v8_str("custom"), v8_num(1234)); 4286 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 4287 4288 // Create an independent environment 4289 LocalContext context1(0, templ); 4290 context1->SetSecurityToken(password); 4291 v8::Handle<v8::Object> global1 = context1->Global(); 4292 global1->Set(v8_str("custom"), v8_num(1234)); 4293 CHECK_NE(global0, global1); 4294 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 4295 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value()); 4296 4297 // Now create a new context with the old global 4298 LocalContext context2(0, templ, global1); 4299 context2->SetSecurityToken(password); 4300 v8::Handle<v8::Object> global2 = context2->Global(); 4301 CHECK_EQ(global1, global2); 4302 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value()); 4303 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value()); 4304 } 4305 4306 4307 THREADED_TEST(FunctionPrototypeAcrossContexts) { 4308 // Make sure that functions created by cloning boilerplates cannot 4309 // communicate through their __proto__ field. 4310 4311 v8::HandleScope scope; 4312 4313 LocalContext env0; 4314 v8::Handle<v8::Object> global0 = 4315 env0->Global(); 4316 v8::Handle<v8::Object> object0 = 4317 global0->Get(v8_str("Object")).As<v8::Object>(); 4318 v8::Handle<v8::Object> tostring0 = 4319 object0->Get(v8_str("toString")).As<v8::Object>(); 4320 v8::Handle<v8::Object> proto0 = 4321 tostring0->Get(v8_str("__proto__")).As<v8::Object>(); 4322 proto0->Set(v8_str("custom"), v8_num(1234)); 4323 4324 LocalContext env1; 4325 v8::Handle<v8::Object> global1 = 4326 env1->Global(); 4327 v8::Handle<v8::Object> object1 = 4328 global1->Get(v8_str("Object")).As<v8::Object>(); 4329 v8::Handle<v8::Object> tostring1 = 4330 object1->Get(v8_str("toString")).As<v8::Object>(); 4331 v8::Handle<v8::Object> proto1 = 4332 tostring1->Get(v8_str("__proto__")).As<v8::Object>(); 4333 CHECK(!proto1->Has(v8_str("custom"))); 4334 } 4335 4336 4337 THREADED_TEST(Regress892105) { 4338 // Make sure that object and array literals created by cloning 4339 // boilerplates cannot communicate through their __proto__ 4340 // field. This is rather difficult to check, but we try to add stuff 4341 // to Object.prototype and Array.prototype and create a new 4342 // environment. This should succeed. 4343 4344 v8::HandleScope scope; 4345 4346 Local<String> source = v8_str("Object.prototype.obj = 1234;" 4347 "Array.prototype.arr = 4567;" 4348 "8901"); 4349 4350 LocalContext env0; 4351 Local<Script> script0 = Script::Compile(source); 4352 CHECK_EQ(8901.0, script0->Run()->NumberValue()); 4353 4354 LocalContext env1; 4355 Local<Script> script1 = Script::Compile(source); 4356 CHECK_EQ(8901.0, script1->Run()->NumberValue()); 4357 } 4358 4359 4360 THREADED_TEST(UndetectableObject) { 4361 v8::HandleScope scope; 4362 LocalContext env; 4363 4364 Local<v8::FunctionTemplate> desc = 4365 v8::FunctionTemplate::New(0, v8::Handle<Value>()); 4366 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 4367 4368 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 4369 env->Global()->Set(v8_str("undetectable"), obj); 4370 4371 ExpectString("undetectable.toString()", "[object Object]"); 4372 ExpectString("typeof undetectable", "undefined"); 4373 ExpectString("typeof(undetectable)", "undefined"); 4374 ExpectBoolean("typeof undetectable == 'undefined'", true); 4375 ExpectBoolean("typeof undetectable == 'object'", false); 4376 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 4377 ExpectBoolean("!undetectable", true); 4378 4379 ExpectObject("true&&undetectable", obj); 4380 ExpectBoolean("false&&undetectable", false); 4381 ExpectBoolean("true||undetectable", true); 4382 ExpectObject("false||undetectable", obj); 4383 4384 ExpectObject("undetectable&&true", obj); 4385 ExpectObject("undetectable&&false", obj); 4386 ExpectBoolean("undetectable||true", true); 4387 ExpectBoolean("undetectable||false", false); 4388 4389 ExpectBoolean("undetectable==null", true); 4390 ExpectBoolean("null==undetectable", true); 4391 ExpectBoolean("undetectable==undefined", true); 4392 ExpectBoolean("undefined==undetectable", true); 4393 ExpectBoolean("undetectable==undetectable", true); 4394 4395 4396 ExpectBoolean("undetectable===null", false); 4397 ExpectBoolean("null===undetectable", false); 4398 ExpectBoolean("undetectable===undefined", false); 4399 ExpectBoolean("undefined===undetectable", false); 4400 ExpectBoolean("undetectable===undetectable", true); 4401 } 4402 4403 4404 THREADED_TEST(VoidLiteral) { 4405 v8::HandleScope scope; 4406 LocalContext env; 4407 4408 Local<v8::FunctionTemplate> desc = 4409 v8::FunctionTemplate::New(0, v8::Handle<Value>()); 4410 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 4411 4412 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 4413 env->Global()->Set(v8_str("undetectable"), obj); 4414 4415 ExpectBoolean("undefined == void 0", true); 4416 ExpectBoolean("undetectable == void 0", true); 4417 ExpectBoolean("null == void 0", true); 4418 ExpectBoolean("undefined === void 0", true); 4419 ExpectBoolean("undetectable === void 0", false); 4420 ExpectBoolean("null === void 0", false); 4421 4422 ExpectBoolean("void 0 == undefined", true); 4423 ExpectBoolean("void 0 == undetectable", true); 4424 ExpectBoolean("void 0 == null", true); 4425 ExpectBoolean("void 0 === undefined", true); 4426 ExpectBoolean("void 0 === undetectable", false); 4427 ExpectBoolean("void 0 === null", false); 4428 4429 ExpectString("(function() {" 4430 " try {" 4431 " return x === void 0;" 4432 " } catch(e) {" 4433 " return e.toString();" 4434 " }" 4435 "})()", 4436 "ReferenceError: x is not defined"); 4437 ExpectString("(function() {" 4438 " try {" 4439 " return void 0 === x;" 4440 " } catch(e) {" 4441 " return e.toString();" 4442 " }" 4443 "})()", 4444 "ReferenceError: x is not defined"); 4445 } 4446 4447 4448 THREADED_TEST(ExtensibleOnUndetectable) { 4449 v8::HandleScope scope; 4450 LocalContext env; 4451 4452 Local<v8::FunctionTemplate> desc = 4453 v8::FunctionTemplate::New(0, v8::Handle<Value>()); 4454 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 4455 4456 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 4457 env->Global()->Set(v8_str("undetectable"), obj); 4458 4459 Local<String> source = v8_str("undetectable.x = 42;" 4460 "undetectable.x"); 4461 4462 Local<Script> script = Script::Compile(source); 4463 4464 CHECK_EQ(v8::Integer::New(42), script->Run()); 4465 4466 ExpectBoolean("Object.isExtensible(undetectable)", true); 4467 4468 source = v8_str("Object.preventExtensions(undetectable);"); 4469 script = Script::Compile(source); 4470 script->Run(); 4471 ExpectBoolean("Object.isExtensible(undetectable)", false); 4472 4473 source = v8_str("undetectable.y = 2000;"); 4474 script = Script::Compile(source); 4475 script->Run(); 4476 ExpectBoolean("undetectable.y == undefined", true); 4477 } 4478 4479 4480 4481 THREADED_TEST(UndetectableString) { 4482 v8::HandleScope scope; 4483 LocalContext env; 4484 4485 Local<String> obj = String::NewUndetectable("foo"); 4486 env->Global()->Set(v8_str("undetectable"), obj); 4487 4488 ExpectString("undetectable", "foo"); 4489 ExpectString("typeof undetectable", "undefined"); 4490 ExpectString("typeof(undetectable)", "undefined"); 4491 ExpectBoolean("typeof undetectable == 'undefined'", true); 4492 ExpectBoolean("typeof undetectable == 'string'", false); 4493 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 4494 ExpectBoolean("!undetectable", true); 4495 4496 ExpectObject("true&&undetectable", obj); 4497 ExpectBoolean("false&&undetectable", false); 4498 ExpectBoolean("true||undetectable", true); 4499 ExpectObject("false||undetectable", obj); 4500 4501 ExpectObject("undetectable&&true", obj); 4502 ExpectObject("undetectable&&false", obj); 4503 ExpectBoolean("undetectable||true", true); 4504 ExpectBoolean("undetectable||false", false); 4505 4506 ExpectBoolean("undetectable==null", true); 4507 ExpectBoolean("null==undetectable", true); 4508 ExpectBoolean("undetectable==undefined", true); 4509 ExpectBoolean("undefined==undetectable", true); 4510 ExpectBoolean("undetectable==undetectable", true); 4511 4512 4513 ExpectBoolean("undetectable===null", false); 4514 ExpectBoolean("null===undetectable", false); 4515 ExpectBoolean("undetectable===undefined", false); 4516 ExpectBoolean("undefined===undetectable", false); 4517 ExpectBoolean("undetectable===undetectable", true); 4518 } 4519 4520 4521 TEST(UndetectableOptimized) { 4522 i::FLAG_allow_natives_syntax = true; 4523 v8::HandleScope scope; 4524 LocalContext env; 4525 4526 Local<String> obj = String::NewUndetectable("foo"); 4527 env->Global()->Set(v8_str("undetectable"), obj); 4528 env->Global()->Set(v8_str("detectable"), v8_str("bar")); 4529 4530 ExpectString( 4531 "function testBranch() {" 4532 " if (!%_IsUndetectableObject(undetectable)) throw 1;" 4533 " if (%_IsUndetectableObject(detectable)) throw 2;" 4534 "}\n" 4535 "function testBool() {" 4536 " var b1 = !%_IsUndetectableObject(undetectable);" 4537 " var b2 = %_IsUndetectableObject(detectable);" 4538 " if (b1) throw 3;" 4539 " if (b2) throw 4;" 4540 " return b1 == b2;" 4541 "}\n" 4542 "%OptimizeFunctionOnNextCall(testBranch);" 4543 "%OptimizeFunctionOnNextCall(testBool);" 4544 "for (var i = 0; i < 10; i++) {" 4545 " testBranch();" 4546 " testBool();" 4547 "}\n" 4548 "\"PASS\"", 4549 "PASS"); 4550 } 4551 4552 4553 template <typename T> static void USE(T) { } 4554 4555 4556 // This test is not intended to be run, just type checked. 4557 static inline void PersistentHandles() { 4558 USE(PersistentHandles); 4559 Local<String> str = v8_str("foo"); 4560 v8::Persistent<String> p_str = v8::Persistent<String>::New(str); 4561 USE(p_str); 4562 Local<Script> scr = Script::Compile(v8_str("")); 4563 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr); 4564 USE(p_scr); 4565 Local<ObjectTemplate> templ = ObjectTemplate::New(); 4566 v8::Persistent<ObjectTemplate> p_templ = 4567 v8::Persistent<ObjectTemplate>::New(templ); 4568 USE(p_templ); 4569 } 4570 4571 4572 static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) { 4573 ApiTestFuzzer::Fuzz(); 4574 return v8::Undefined(); 4575 } 4576 4577 4578 THREADED_TEST(GlobalObjectTemplate) { 4579 v8::HandleScope handle_scope; 4580 Local<ObjectTemplate> global_template = ObjectTemplate::New(); 4581 global_template->Set(v8_str("JSNI_Log"), 4582 v8::FunctionTemplate::New(HandleLogDelegator)); 4583 v8::Persistent<Context> context = Context::New(0, global_template); 4584 Context::Scope context_scope(context); 4585 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run(); 4586 context.Dispose(); 4587 } 4588 4589 4590 static const char* kSimpleExtensionSource = 4591 "function Foo() {" 4592 " return 4;" 4593 "}"; 4594 4595 4596 THREADED_TEST(SimpleExtensions) { 4597 v8::HandleScope handle_scope; 4598 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource)); 4599 const char* extension_names[] = { "simpletest" }; 4600 v8::ExtensionConfiguration extensions(1, extension_names); 4601 v8::Handle<Context> context = Context::New(&extensions); 4602 Context::Scope lock(context); 4603 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 4604 CHECK_EQ(result, v8::Integer::New(4)); 4605 } 4606 4607 4608 static const char* kEmbeddedExtensionSource = 4609 "function Ret54321(){return 54321;}~~@@$" 4610 "$%% THIS IS A SERIES OF NON-NULL-TERMINATED STRINGS."; 4611 static const int kEmbeddedExtensionSourceValidLen = 34; 4612 4613 4614 THREADED_TEST(ExtensionMissingSourceLength) { 4615 v8::HandleScope handle_scope; 4616 v8::RegisterExtension(new Extension("srclentest_fail", 4617 kEmbeddedExtensionSource)); 4618 const char* extension_names[] = { "srclentest_fail" }; 4619 v8::ExtensionConfiguration extensions(1, extension_names); 4620 v8::Handle<Context> context = Context::New(&extensions); 4621 CHECK_EQ(0, *context); 4622 } 4623 4624 4625 THREADED_TEST(ExtensionWithSourceLength) { 4626 for (int source_len = kEmbeddedExtensionSourceValidLen - 1; 4627 source_len <= kEmbeddedExtensionSourceValidLen + 1; ++source_len) { 4628 v8::HandleScope handle_scope; 4629 i::ScopedVector<char> extension_name(32); 4630 i::OS::SNPrintF(extension_name, "ext #%d", source_len); 4631 v8::RegisterExtension(new Extension(extension_name.start(), 4632 kEmbeddedExtensionSource, 0, 0, 4633 source_len)); 4634 const char* extension_names[1] = { extension_name.start() }; 4635 v8::ExtensionConfiguration extensions(1, extension_names); 4636 v8::Handle<Context> context = Context::New(&extensions); 4637 if (source_len == kEmbeddedExtensionSourceValidLen) { 4638 Context::Scope lock(context); 4639 v8::Handle<Value> result = Script::Compile(v8_str("Ret54321()"))->Run(); 4640 CHECK_EQ(v8::Integer::New(54321), result); 4641 } else { 4642 // Anything but exactly the right length should fail to compile. 4643 CHECK_EQ(0, *context); 4644 } 4645 } 4646 } 4647 4648 4649 static const char* kEvalExtensionSource1 = 4650 "function UseEval1() {" 4651 " var x = 42;" 4652 " return eval('x');" 4653 "}"; 4654 4655 4656 static const char* kEvalExtensionSource2 = 4657 "(function() {" 4658 " var x = 42;" 4659 " function e() {" 4660 " return eval('x');" 4661 " }" 4662 " this.UseEval2 = e;" 4663 "})()"; 4664 4665 4666 THREADED_TEST(UseEvalFromExtension) { 4667 v8::HandleScope handle_scope; 4668 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1)); 4669 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2)); 4670 const char* extension_names[] = { "evaltest1", "evaltest2" }; 4671 v8::ExtensionConfiguration extensions(2, extension_names); 4672 v8::Handle<Context> context = Context::New(&extensions); 4673 Context::Scope lock(context); 4674 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run(); 4675 CHECK_EQ(result, v8::Integer::New(42)); 4676 result = Script::Compile(v8_str("UseEval2()"))->Run(); 4677 CHECK_EQ(result, v8::Integer::New(42)); 4678 } 4679 4680 4681 static const char* kWithExtensionSource1 = 4682 "function UseWith1() {" 4683 " var x = 42;" 4684 " with({x:87}) { return x; }" 4685 "}"; 4686 4687 4688 4689 static const char* kWithExtensionSource2 = 4690 "(function() {" 4691 " var x = 42;" 4692 " function e() {" 4693 " with ({x:87}) { return x; }" 4694 " }" 4695 " this.UseWith2 = e;" 4696 "})()"; 4697 4698 4699 THREADED_TEST(UseWithFromExtension) { 4700 v8::HandleScope handle_scope; 4701 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1)); 4702 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2)); 4703 const char* extension_names[] = { "withtest1", "withtest2" }; 4704 v8::ExtensionConfiguration extensions(2, extension_names); 4705 v8::Handle<Context> context = Context::New(&extensions); 4706 Context::Scope lock(context); 4707 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run(); 4708 CHECK_EQ(result, v8::Integer::New(87)); 4709 result = Script::Compile(v8_str("UseWith2()"))->Run(); 4710 CHECK_EQ(result, v8::Integer::New(87)); 4711 } 4712 4713 4714 THREADED_TEST(AutoExtensions) { 4715 v8::HandleScope handle_scope; 4716 Extension* extension = new Extension("autotest", kSimpleExtensionSource); 4717 extension->set_auto_enable(true); 4718 v8::RegisterExtension(extension); 4719 v8::Handle<Context> context = Context::New(); 4720 Context::Scope lock(context); 4721 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 4722 CHECK_EQ(result, v8::Integer::New(4)); 4723 } 4724 4725 4726 static const char* kSyntaxErrorInExtensionSource = 4727 "["; 4728 4729 4730 // Test that a syntax error in an extension does not cause a fatal 4731 // error but results in an empty context. 4732 THREADED_TEST(SyntaxErrorExtensions) { 4733 v8::HandleScope handle_scope; 4734 v8::RegisterExtension(new Extension("syntaxerror", 4735 kSyntaxErrorInExtensionSource)); 4736 const char* extension_names[] = { "syntaxerror" }; 4737 v8::ExtensionConfiguration extensions(1, extension_names); 4738 v8::Handle<Context> context = Context::New(&extensions); 4739 CHECK(context.IsEmpty()); 4740 } 4741 4742 4743 static const char* kExceptionInExtensionSource = 4744 "throw 42"; 4745 4746 4747 // Test that an exception when installing an extension does not cause 4748 // a fatal error but results in an empty context. 4749 THREADED_TEST(ExceptionExtensions) { 4750 v8::HandleScope handle_scope; 4751 v8::RegisterExtension(new Extension("exception", 4752 kExceptionInExtensionSource)); 4753 const char* extension_names[] = { "exception" }; 4754 v8::ExtensionConfiguration extensions(1, extension_names); 4755 v8::Handle<Context> context = Context::New(&extensions); 4756 CHECK(context.IsEmpty()); 4757 } 4758 4759 4760 static const char* kNativeCallInExtensionSource = 4761 "function call_runtime_last_index_of(x) {" 4762 " return %StringLastIndexOf(x, 'bob', 10);" 4763 "}"; 4764 4765 4766 static const char* kNativeCallTest = 4767 "call_runtime_last_index_of('bobbobboellebobboellebobbob');"; 4768 4769 // Test that a native runtime calls are supported in extensions. 4770 THREADED_TEST(NativeCallInExtensions) { 4771 v8::HandleScope handle_scope; 4772 v8::RegisterExtension(new Extension("nativecall", 4773 kNativeCallInExtensionSource)); 4774 const char* extension_names[] = { "nativecall" }; 4775 v8::ExtensionConfiguration extensions(1, extension_names); 4776 v8::Handle<Context> context = Context::New(&extensions); 4777 Context::Scope lock(context); 4778 v8::Handle<Value> result = Script::Compile(v8_str(kNativeCallTest))->Run(); 4779 CHECK_EQ(result, v8::Integer::New(3)); 4780 } 4781 4782 4783 class NativeFunctionExtension : public Extension { 4784 public: 4785 NativeFunctionExtension(const char* name, 4786 const char* source, 4787 v8::InvocationCallback fun = &Echo) 4788 : Extension(name, source), 4789 function_(fun) { } 4790 4791 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( 4792 v8::Handle<v8::String> name) { 4793 return v8::FunctionTemplate::New(function_); 4794 } 4795 4796 static v8::Handle<v8::Value> Echo(const v8::Arguments& args) { 4797 if (args.Length() >= 1) return (args[0]); 4798 return v8::Undefined(); 4799 } 4800 private: 4801 v8::InvocationCallback function_; 4802 }; 4803 4804 4805 THREADED_TEST(NativeFunctionDeclaration) { 4806 v8::HandleScope handle_scope; 4807 const char* name = "nativedecl"; 4808 v8::RegisterExtension(new NativeFunctionExtension(name, 4809 "native function foo();")); 4810 const char* extension_names[] = { name }; 4811 v8::ExtensionConfiguration extensions(1, extension_names); 4812 v8::Handle<Context> context = Context::New(&extensions); 4813 Context::Scope lock(context); 4814 v8::Handle<Value> result = Script::Compile(v8_str("foo(42);"))->Run(); 4815 CHECK_EQ(result, v8::Integer::New(42)); 4816 } 4817 4818 4819 THREADED_TEST(NativeFunctionDeclarationError) { 4820 v8::HandleScope handle_scope; 4821 const char* name = "nativedeclerr"; 4822 // Syntax error in extension code. 4823 v8::RegisterExtension(new NativeFunctionExtension(name, 4824 "native\nfunction foo();")); 4825 const char* extension_names[] = { name }; 4826 v8::ExtensionConfiguration extensions(1, extension_names); 4827 v8::Handle<Context> context(Context::New(&extensions)); 4828 CHECK(context.IsEmpty()); 4829 } 4830 4831 4832 THREADED_TEST(NativeFunctionDeclarationErrorEscape) { 4833 v8::HandleScope handle_scope; 4834 const char* name = "nativedeclerresc"; 4835 // Syntax error in extension code - escape code in "native" means that 4836 // it's not treated as a keyword. 4837 v8::RegisterExtension(new NativeFunctionExtension( 4838 name, 4839 "nativ\\u0065 function foo();")); 4840 const char* extension_names[] = { name }; 4841 v8::ExtensionConfiguration extensions(1, extension_names); 4842 v8::Handle<Context> context(Context::New(&extensions)); 4843 CHECK(context.IsEmpty()); 4844 } 4845 4846 4847 static void CheckDependencies(const char* name, const char* expected) { 4848 v8::HandleScope handle_scope; 4849 v8::ExtensionConfiguration config(1, &name); 4850 LocalContext context(&config); 4851 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded"))); 4852 } 4853 4854 4855 /* 4856 * Configuration: 4857 * 4858 * /-- B <--\ 4859 * A <- -- D <-- E 4860 * \-- C <--/ 4861 */ 4862 THREADED_TEST(ExtensionDependency) { 4863 static const char* kEDeps[] = { "D" }; 4864 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps)); 4865 static const char* kDDeps[] = { "B", "C" }; 4866 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps)); 4867 static const char* kBCDeps[] = { "A" }; 4868 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps)); 4869 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps)); 4870 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';")); 4871 CheckDependencies("A", "undefinedA"); 4872 CheckDependencies("B", "undefinedAB"); 4873 CheckDependencies("C", "undefinedAC"); 4874 CheckDependencies("D", "undefinedABCD"); 4875 CheckDependencies("E", "undefinedABCDE"); 4876 v8::HandleScope handle_scope; 4877 static const char* exts[2] = { "C", "E" }; 4878 v8::ExtensionConfiguration config(2, exts); 4879 LocalContext context(&config); 4880 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded"))); 4881 } 4882 4883 4884 static const char* kExtensionTestScript = 4885 "native function A();" 4886 "native function B();" 4887 "native function C();" 4888 "function Foo(i) {" 4889 " if (i == 0) return A();" 4890 " if (i == 1) return B();" 4891 " if (i == 2) return C();" 4892 "}"; 4893 4894 4895 static v8::Handle<Value> CallFun(const v8::Arguments& args) { 4896 ApiTestFuzzer::Fuzz(); 4897 if (args.IsConstructCall()) { 4898 args.This()->Set(v8_str("data"), args.Data()); 4899 return v8::Null(); 4900 } 4901 return args.Data(); 4902 } 4903 4904 4905 class FunctionExtension : public Extension { 4906 public: 4907 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { } 4908 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( 4909 v8::Handle<String> name); 4910 }; 4911 4912 4913 static int lookup_count = 0; 4914 v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction( 4915 v8::Handle<String> name) { 4916 lookup_count++; 4917 if (name->Equals(v8_str("A"))) { 4918 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8)); 4919 } else if (name->Equals(v8_str("B"))) { 4920 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7)); 4921 } else if (name->Equals(v8_str("C"))) { 4922 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6)); 4923 } else { 4924 return v8::Handle<v8::FunctionTemplate>(); 4925 } 4926 } 4927 4928 4929 THREADED_TEST(FunctionLookup) { 4930 v8::RegisterExtension(new FunctionExtension()); 4931 v8::HandleScope handle_scope; 4932 static const char* exts[1] = { "functiontest" }; 4933 v8::ExtensionConfiguration config(1, exts); 4934 LocalContext context(&config); 4935 CHECK_EQ(3, lookup_count); 4936 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run()); 4937 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run()); 4938 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run()); 4939 } 4940 4941 4942 THREADED_TEST(NativeFunctionConstructCall) { 4943 v8::RegisterExtension(new FunctionExtension()); 4944 v8::HandleScope handle_scope; 4945 static const char* exts[1] = { "functiontest" }; 4946 v8::ExtensionConfiguration config(1, exts); 4947 LocalContext context(&config); 4948 for (int i = 0; i < 10; i++) { 4949 // Run a few times to ensure that allocation of objects doesn't 4950 // change behavior of a constructor function. 4951 CHECK_EQ(v8::Integer::New(8), 4952 Script::Compile(v8_str("(new A()).data"))->Run()); 4953 CHECK_EQ(v8::Integer::New(7), 4954 Script::Compile(v8_str("(new B()).data"))->Run()); 4955 CHECK_EQ(v8::Integer::New(6), 4956 Script::Compile(v8_str("(new C()).data"))->Run()); 4957 } 4958 } 4959 4960 4961 static const char* last_location; 4962 static const char* last_message; 4963 void StoringErrorCallback(const char* location, const char* message) { 4964 if (last_location == NULL) { 4965 last_location = location; 4966 last_message = message; 4967 } 4968 } 4969 4970 4971 // ErrorReporting creates a circular extensions configuration and 4972 // tests that the fatal error handler gets called. This renders V8 4973 // unusable and therefore this test cannot be run in parallel. 4974 TEST(ErrorReporting) { 4975 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 4976 static const char* aDeps[] = { "B" }; 4977 v8::RegisterExtension(new Extension("A", "", 1, aDeps)); 4978 static const char* bDeps[] = { "A" }; 4979 v8::RegisterExtension(new Extension("B", "", 1, bDeps)); 4980 last_location = NULL; 4981 v8::ExtensionConfiguration config(1, bDeps); 4982 v8::Handle<Context> context = Context::New(&config); 4983 CHECK(context.IsEmpty()); 4984 CHECK_NE(last_location, NULL); 4985 } 4986 4987 4988 static const char* js_code_causing_huge_string_flattening = 4989 "var str = 'X';" 4990 "for (var i = 0; i < 30; i++) {" 4991 " str = str + str;" 4992 "}" 4993 "str.match(/X/);"; 4994 4995 4996 void OOMCallback(const char* location, const char* message) { 4997 exit(0); 4998 } 4999 5000 5001 TEST(RegexpOutOfMemory) { 5002 // Execute a script that causes out of memory when flattening a string. 5003 v8::HandleScope scope; 5004 v8::V8::SetFatalErrorHandler(OOMCallback); 5005 LocalContext context; 5006 Local<Script> script = 5007 Script::Compile(String::New(js_code_causing_huge_string_flattening)); 5008 last_location = NULL; 5009 script->Run(); 5010 5011 CHECK(false); // Should not return. 5012 } 5013 5014 5015 static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message, 5016 v8::Handle<Value> data) { 5017 CHECK_EQ(v8::Undefined(), data); 5018 CHECK(message->GetScriptResourceName()->IsUndefined()); 5019 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName()); 5020 message->GetLineNumber(); 5021 message->GetSourceLine(); 5022 } 5023 5024 5025 THREADED_TEST(ErrorWithMissingScriptInfo) { 5026 v8::HandleScope scope; 5027 LocalContext context; 5028 v8::V8::AddMessageListener(MissingScriptInfoMessageListener); 5029 Script::Compile(v8_str("throw Error()"))->Run(); 5030 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener); 5031 } 5032 5033 5034 int global_index = 0; 5035 5036 class Snorkel { 5037 public: 5038 Snorkel() { index_ = global_index++; } 5039 int index_; 5040 }; 5041 5042 class Whammy { 5043 public: 5044 Whammy() { 5045 cursor_ = 0; 5046 } 5047 ~Whammy() { 5048 script_.Dispose(); 5049 } 5050 v8::Handle<Script> getScript() { 5051 if (script_.IsEmpty()) 5052 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo")); 5053 return Local<Script>(*script_); 5054 } 5055 5056 public: 5057 static const int kObjectCount = 256; 5058 int cursor_; 5059 v8::Persistent<v8::Object> objects_[kObjectCount]; 5060 v8::Persistent<Script> script_; 5061 }; 5062 5063 static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) { 5064 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data); 5065 delete snorkel; 5066 obj.ClearWeak(); 5067 } 5068 5069 v8::Handle<Value> WhammyPropertyGetter(Local<String> name, 5070 const AccessorInfo& info) { 5071 Whammy* whammy = 5072 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); 5073 5074 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_]; 5075 5076 v8::Handle<v8::Object> obj = v8::Object::New(); 5077 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj); 5078 if (!prev.IsEmpty()) { 5079 prev->Set(v8_str("next"), obj); 5080 prev.MakeWeak(new Snorkel(), &HandleWeakReference); 5081 whammy->objects_[whammy->cursor_].Clear(); 5082 } 5083 whammy->objects_[whammy->cursor_] = global; 5084 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount; 5085 return whammy->getScript()->Run(); 5086 } 5087 5088 THREADED_TEST(WeakReference) { 5089 v8::HandleScope handle_scope; 5090 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New(); 5091 Whammy* whammy = new Whammy(); 5092 templ->SetNamedPropertyHandler(WhammyPropertyGetter, 5093 0, 0, 0, 0, 5094 v8::External::New(whammy)); 5095 const char* extension_list[] = { "v8/gc" }; 5096 v8::ExtensionConfiguration extensions(1, extension_list); 5097 v8::Persistent<Context> context = Context::New(&extensions); 5098 Context::Scope context_scope(context); 5099 5100 v8::Handle<v8::Object> interceptor = templ->NewInstance(); 5101 context->Global()->Set(v8_str("whammy"), interceptor); 5102 const char* code = 5103 "var last;" 5104 "for (var i = 0; i < 10000; i++) {" 5105 " var obj = whammy.length;" 5106 " if (last) last.next = obj;" 5107 " last = obj;" 5108 "}" 5109 "gc();" 5110 "4"; 5111 v8::Handle<Value> result = CompileRun(code); 5112 CHECK_EQ(4.0, result->NumberValue()); 5113 delete whammy; 5114 context.Dispose(); 5115 } 5116 5117 5118 static void DisposeAndSetFlag(v8::Persistent<v8::Value> obj, void* data) { 5119 obj.Dispose(); 5120 obj.Clear(); 5121 *(reinterpret_cast<bool*>(data)) = true; 5122 } 5123 5124 5125 THREADED_TEST(IndependentWeakHandle) { 5126 v8::Persistent<Context> context = Context::New(); 5127 Context::Scope context_scope(context); 5128 5129 v8::Persistent<v8::Object> object_a; 5130 5131 { 5132 v8::HandleScope handle_scope; 5133 object_a = v8::Persistent<v8::Object>::New(v8::Object::New()); 5134 } 5135 5136 bool object_a_disposed = false; 5137 object_a.MakeWeak(&object_a_disposed, &DisposeAndSetFlag); 5138 object_a.MarkIndependent(); 5139 HEAP->PerformScavenge(); 5140 CHECK(object_a_disposed); 5141 } 5142 5143 5144 static void InvokeScavenge() { 5145 HEAP->PerformScavenge(); 5146 } 5147 5148 5149 static void InvokeMarkSweep() { 5150 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 5151 } 5152 5153 5154 static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) { 5155 obj.Dispose(); 5156 obj.Clear(); 5157 *(reinterpret_cast<bool*>(data)) = true; 5158 InvokeScavenge(); 5159 } 5160 5161 5162 static void ForceMarkSweep(v8::Persistent<v8::Value> obj, void* data) { 5163 obj.Dispose(); 5164 obj.Clear(); 5165 *(reinterpret_cast<bool*>(data)) = true; 5166 InvokeMarkSweep(); 5167 } 5168 5169 5170 THREADED_TEST(GCFromWeakCallbacks) { 5171 v8::Persistent<Context> context = Context::New(); 5172 Context::Scope context_scope(context); 5173 5174 static const int kNumberOfGCTypes = 2; 5175 v8::WeakReferenceCallback gc_forcing_callback[kNumberOfGCTypes] = 5176 {&ForceScavenge, &ForceMarkSweep}; 5177 5178 typedef void (*GCInvoker)(); 5179 GCInvoker invoke_gc[kNumberOfGCTypes] = {&InvokeScavenge, &InvokeMarkSweep}; 5180 5181 for (int outer_gc = 0; outer_gc < kNumberOfGCTypes; outer_gc++) { 5182 for (int inner_gc = 0; inner_gc < kNumberOfGCTypes; inner_gc++) { 5183 v8::Persistent<v8::Object> object; 5184 { 5185 v8::HandleScope handle_scope; 5186 object = v8::Persistent<v8::Object>::New(v8::Object::New()); 5187 } 5188 bool disposed = false; 5189 object.MakeWeak(&disposed, gc_forcing_callback[inner_gc]); 5190 object.MarkIndependent(); 5191 invoke_gc[outer_gc](); 5192 CHECK(disposed); 5193 } 5194 } 5195 } 5196 5197 5198 static void RevivingCallback(v8::Persistent<v8::Value> obj, void* data) { 5199 obj.ClearWeak(); 5200 *(reinterpret_cast<bool*>(data)) = true; 5201 } 5202 5203 5204 THREADED_TEST(IndependentHandleRevival) { 5205 v8::Persistent<Context> context = Context::New(); 5206 Context::Scope context_scope(context); 5207 5208 v8::Persistent<v8::Object> object; 5209 { 5210 v8::HandleScope handle_scope; 5211 object = v8::Persistent<v8::Object>::New(v8::Object::New()); 5212 object->Set(v8_str("x"), v8::Integer::New(1)); 5213 v8::Local<String> y_str = v8_str("y"); 5214 object->Set(y_str, y_str); 5215 } 5216 bool revived = false; 5217 object.MakeWeak(&revived, &RevivingCallback); 5218 object.MarkIndependent(); 5219 HEAP->PerformScavenge(); 5220 CHECK(revived); 5221 HEAP->CollectAllGarbage(true); 5222 { 5223 v8::HandleScope handle_scope; 5224 v8::Local<String> y_str = v8_str("y"); 5225 CHECK_EQ(v8::Integer::New(1), object->Get(v8_str("x"))); 5226 CHECK(object->Get(y_str)->Equals(y_str)); 5227 } 5228 } 5229 5230 5231 v8::Handle<Function> args_fun; 5232 5233 5234 static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) { 5235 ApiTestFuzzer::Fuzz(); 5236 CHECK_EQ(args_fun, args.Callee()); 5237 CHECK_EQ(3, args.Length()); 5238 CHECK_EQ(v8::Integer::New(1), args[0]); 5239 CHECK_EQ(v8::Integer::New(2), args[1]); 5240 CHECK_EQ(v8::Integer::New(3), args[2]); 5241 CHECK_EQ(v8::Undefined(), args[3]); 5242 v8::HandleScope scope; 5243 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 5244 return v8::Undefined(); 5245 } 5246 5247 5248 THREADED_TEST(Arguments) { 5249 v8::HandleScope scope; 5250 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 5251 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback)); 5252 LocalContext context(NULL, global); 5253 args_fun = context->Global()->Get(v8_str("f")).As<Function>(); 5254 v8_compile("f(1, 2, 3)")->Run(); 5255 } 5256 5257 5258 static v8::Handle<Value> NoBlockGetterX(Local<String> name, 5259 const AccessorInfo&) { 5260 return v8::Handle<Value>(); 5261 } 5262 5263 5264 static v8::Handle<Value> NoBlockGetterI(uint32_t index, 5265 const AccessorInfo&) { 5266 return v8::Handle<Value>(); 5267 } 5268 5269 5270 static v8::Handle<v8::Boolean> PDeleter(Local<String> name, 5271 const AccessorInfo&) { 5272 if (!name->Equals(v8_str("foo"))) { 5273 return v8::Handle<v8::Boolean>(); // not intercepted 5274 } 5275 5276 return v8::False(); // intercepted, and don't delete the property 5277 } 5278 5279 5280 static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) { 5281 if (index != 2) { 5282 return v8::Handle<v8::Boolean>(); // not intercepted 5283 } 5284 5285 return v8::False(); // intercepted, and don't delete the property 5286 } 5287 5288 5289 THREADED_TEST(Deleter) { 5290 v8::HandleScope scope; 5291 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 5292 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL); 5293 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL); 5294 LocalContext context; 5295 context->Global()->Set(v8_str("k"), obj->NewInstance()); 5296 CompileRun( 5297 "k.foo = 'foo';" 5298 "k.bar = 'bar';" 5299 "k[2] = 2;" 5300 "k[4] = 4;"); 5301 CHECK(v8_compile("delete k.foo")->Run()->IsFalse()); 5302 CHECK(v8_compile("delete k.bar")->Run()->IsTrue()); 5303 5304 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo")); 5305 CHECK(v8_compile("k.bar")->Run()->IsUndefined()); 5306 5307 CHECK(v8_compile("delete k[2]")->Run()->IsFalse()); 5308 CHECK(v8_compile("delete k[4]")->Run()->IsTrue()); 5309 5310 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2)); 5311 CHECK(v8_compile("k[4]")->Run()->IsUndefined()); 5312 } 5313 5314 5315 static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) { 5316 ApiTestFuzzer::Fuzz(); 5317 if (name->Equals(v8_str("foo")) || 5318 name->Equals(v8_str("bar")) || 5319 name->Equals(v8_str("baz"))) { 5320 return v8::Undefined(); 5321 } 5322 return v8::Handle<Value>(); 5323 } 5324 5325 5326 static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) { 5327 ApiTestFuzzer::Fuzz(); 5328 if (index == 0 || index == 1) return v8::Undefined(); 5329 return v8::Handle<Value>(); 5330 } 5331 5332 5333 static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) { 5334 ApiTestFuzzer::Fuzz(); 5335 v8::Handle<v8::Array> result = v8::Array::New(3); 5336 result->Set(v8::Integer::New(0), v8_str("foo")); 5337 result->Set(v8::Integer::New(1), v8_str("bar")); 5338 result->Set(v8::Integer::New(2), v8_str("baz")); 5339 return result; 5340 } 5341 5342 5343 static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) { 5344 ApiTestFuzzer::Fuzz(); 5345 v8::Handle<v8::Array> result = v8::Array::New(2); 5346 result->Set(v8::Integer::New(0), v8_str("0")); 5347 result->Set(v8::Integer::New(1), v8_str("1")); 5348 return result; 5349 } 5350 5351 5352 THREADED_TEST(Enumerators) { 5353 v8::HandleScope scope; 5354 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 5355 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum); 5356 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum); 5357 LocalContext context; 5358 context->Global()->Set(v8_str("k"), obj->NewInstance()); 5359 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 5360 "k[10] = 0;" 5361 "k.a = 0;" 5362 "k[5] = 0;" 5363 "k.b = 0;" 5364 "k[4294967295] = 0;" 5365 "k.c = 0;" 5366 "k[4294967296] = 0;" 5367 "k.d = 0;" 5368 "k[140000] = 0;" 5369 "k.e = 0;" 5370 "k[30000000000] = 0;" 5371 "k.f = 0;" 5372 "var result = [];" 5373 "for (var prop in k) {" 5374 " result.push(prop);" 5375 "}" 5376 "result")); 5377 // Check that we get all the property names returned including the 5378 // ones from the enumerators in the right order: indexed properties 5379 // in numerical order, indexed interceptor properties, named 5380 // properties in insertion order, named interceptor properties. 5381 // This order is not mandated by the spec, so this test is just 5382 // documenting our behavior. 5383 CHECK_EQ(17, result->Length()); 5384 // Indexed properties in numerical order. 5385 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0))); 5386 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1))); 5387 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2))); 5388 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3))); 5389 // Indexed interceptor properties in the order they are returned 5390 // from the enumerator interceptor. 5391 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4))); 5392 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5))); 5393 // Named properties in insertion order. 5394 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6))); 5395 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7))); 5396 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8))); 5397 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9))); 5398 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10))); 5399 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11))); 5400 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12))); 5401 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13))); 5402 // Named interceptor properties. 5403 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14))); 5404 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15))); 5405 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16))); 5406 } 5407 5408 5409 int p_getter_count; 5410 int p_getter_count2; 5411 5412 5413 static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) { 5414 ApiTestFuzzer::Fuzz(); 5415 p_getter_count++; 5416 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 5417 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 5418 if (name->Equals(v8_str("p1"))) { 5419 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 5420 } else if (name->Equals(v8_str("p2"))) { 5421 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 5422 } else if (name->Equals(v8_str("p3"))) { 5423 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 5424 } else if (name->Equals(v8_str("p4"))) { 5425 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 5426 } 5427 return v8::Undefined(); 5428 } 5429 5430 5431 static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) { 5432 ApiTestFuzzer::Fuzz(); 5433 LocalContext context; 5434 context->Global()->Set(v8_str("o1"), obj->NewInstance()); 5435 CompileRun( 5436 "o1.__proto__ = { };" 5437 "var o2 = { __proto__: o1 };" 5438 "var o3 = { __proto__: o2 };" 5439 "var o4 = { __proto__: o3 };" 5440 "for (var i = 0; i < 10; i++) o4.p4;" 5441 "for (var i = 0; i < 10; i++) o3.p3;" 5442 "for (var i = 0; i < 10; i++) o2.p2;" 5443 "for (var i = 0; i < 10; i++) o1.p1;"); 5444 } 5445 5446 5447 static v8::Handle<Value> PGetter2(Local<String> name, 5448 const AccessorInfo& info) { 5449 ApiTestFuzzer::Fuzz(); 5450 p_getter_count2++; 5451 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 5452 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 5453 if (name->Equals(v8_str("p1"))) { 5454 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 5455 } else if (name->Equals(v8_str("p2"))) { 5456 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 5457 } else if (name->Equals(v8_str("p3"))) { 5458 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 5459 } else if (name->Equals(v8_str("p4"))) { 5460 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 5461 } 5462 return v8::Undefined(); 5463 } 5464 5465 5466 THREADED_TEST(GetterHolders) { 5467 v8::HandleScope scope; 5468 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 5469 obj->SetAccessor(v8_str("p1"), PGetter); 5470 obj->SetAccessor(v8_str("p2"), PGetter); 5471 obj->SetAccessor(v8_str("p3"), PGetter); 5472 obj->SetAccessor(v8_str("p4"), PGetter); 5473 p_getter_count = 0; 5474 RunHolderTest(obj); 5475 CHECK_EQ(40, p_getter_count); 5476 } 5477 5478 5479 THREADED_TEST(PreInterceptorHolders) { 5480 v8::HandleScope scope; 5481 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 5482 obj->SetNamedPropertyHandler(PGetter2); 5483 p_getter_count2 = 0; 5484 RunHolderTest(obj); 5485 CHECK_EQ(40, p_getter_count2); 5486 } 5487 5488 5489 THREADED_TEST(ObjectInstantiation) { 5490 v8::HandleScope scope; 5491 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5492 templ->SetAccessor(v8_str("t"), PGetter2); 5493 LocalContext context; 5494 context->Global()->Set(v8_str("o"), templ->NewInstance()); 5495 for (int i = 0; i < 100; i++) { 5496 v8::HandleScope inner_scope; 5497 v8::Handle<v8::Object> obj = templ->NewInstance(); 5498 CHECK_NE(obj, context->Global()->Get(v8_str("o"))); 5499 context->Global()->Set(v8_str("o2"), obj); 5500 v8::Handle<Value> value = 5501 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run(); 5502 CHECK_EQ(v8::True(), value); 5503 context->Global()->Set(v8_str("o"), obj); 5504 } 5505 } 5506 5507 5508 static int StrCmp16(uint16_t* a, uint16_t* b) { 5509 while (true) { 5510 if (*a == 0 && *b == 0) return 0; 5511 if (*a != *b) return 0 + *a - *b; 5512 a++; 5513 b++; 5514 } 5515 } 5516 5517 5518 static int StrNCmp16(uint16_t* a, uint16_t* b, int n) { 5519 while (true) { 5520 if (n-- == 0) return 0; 5521 if (*a == 0 && *b == 0) return 0; 5522 if (*a != *b) return 0 + *a - *b; 5523 a++; 5524 b++; 5525 } 5526 } 5527 5528 5529 int GetUtf8Length(Handle<String> str) { 5530 int len = str->Utf8Length(); 5531 if (len < 0) { 5532 i::Handle<i::String> istr(v8::Utils::OpenHandle(*str)); 5533 i::FlattenString(istr); 5534 len = str->Utf8Length(); 5535 } 5536 return len; 5537 } 5538 5539 5540 THREADED_TEST(StringWrite) { 5541 LocalContext context; 5542 v8::HandleScope scope; 5543 v8::Handle<String> str = v8_str("abcde"); 5544 // abc<Icelandic eth><Unicode snowman>. 5545 v8::Handle<String> str2 = v8_str("abc\303\260\342\230\203"); 5546 const int kStride = 4; // Must match stride in for loops in JS below. 5547 CompileRun( 5548 "var left = '';" 5549 "for (var i = 0; i < 0xd800; i += 4) {" 5550 " left = left + String.fromCharCode(i);" 5551 "}"); 5552 CompileRun( 5553 "var right = '';" 5554 "for (var i = 0; i < 0xd800; i += 4) {" 5555 " right = String.fromCharCode(i) + right;" 5556 "}"); 5557 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 5558 Handle<String> left_tree = global->Get(v8_str("left")).As<String>(); 5559 Handle<String> right_tree = global->Get(v8_str("right")).As<String>(); 5560 5561 CHECK_EQ(5, str2->Length()); 5562 CHECK_EQ(0xd800 / kStride, left_tree->Length()); 5563 CHECK_EQ(0xd800 / kStride, right_tree->Length()); 5564 5565 char buf[100]; 5566 char utf8buf[0xd800 * 3]; 5567 uint16_t wbuf[100]; 5568 int len; 5569 int charlen; 5570 5571 memset(utf8buf, 0x1, 1000); 5572 len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen); 5573 CHECK_EQ(9, len); 5574 CHECK_EQ(5, charlen); 5575 CHECK_EQ(0, strcmp(utf8buf, "abc\303\260\342\230\203")); 5576 5577 memset(utf8buf, 0x1, 1000); 5578 len = str2->WriteUtf8(utf8buf, 8, &charlen); 5579 CHECK_EQ(8, len); 5580 CHECK_EQ(5, charlen); 5581 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\342\230\203\1", 9)); 5582 5583 memset(utf8buf, 0x1, 1000); 5584 len = str2->WriteUtf8(utf8buf, 7, &charlen); 5585 CHECK_EQ(5, len); 5586 CHECK_EQ(4, charlen); 5587 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); 5588 5589 memset(utf8buf, 0x1, 1000); 5590 len = str2->WriteUtf8(utf8buf, 6, &charlen); 5591 CHECK_EQ(5, len); 5592 CHECK_EQ(4, charlen); 5593 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); 5594 5595 memset(utf8buf, 0x1, 1000); 5596 len = str2->WriteUtf8(utf8buf, 5, &charlen); 5597 CHECK_EQ(5, len); 5598 CHECK_EQ(4, charlen); 5599 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); 5600 5601 memset(utf8buf, 0x1, 1000); 5602 len = str2->WriteUtf8(utf8buf, 4, &charlen); 5603 CHECK_EQ(3, len); 5604 CHECK_EQ(3, charlen); 5605 CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4)); 5606 5607 memset(utf8buf, 0x1, 1000); 5608 len = str2->WriteUtf8(utf8buf, 3, &charlen); 5609 CHECK_EQ(3, len); 5610 CHECK_EQ(3, charlen); 5611 CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4)); 5612 5613 memset(utf8buf, 0x1, 1000); 5614 len = str2->WriteUtf8(utf8buf, 2, &charlen); 5615 CHECK_EQ(2, len); 5616 CHECK_EQ(2, charlen); 5617 CHECK_EQ(0, strncmp(utf8buf, "ab\1", 3)); 5618 5619 memset(utf8buf, 0x1, sizeof(utf8buf)); 5620 len = GetUtf8Length(left_tree); 5621 int utf8_expected = 5622 (0x80 + (0x800 - 0x80) * 2 + (0xd800 - 0x800) * 3) / kStride; 5623 CHECK_EQ(utf8_expected, len); 5624 len = left_tree->WriteUtf8(utf8buf, utf8_expected, &charlen); 5625 CHECK_EQ(utf8_expected, len); 5626 CHECK_EQ(0xd800 / kStride, charlen); 5627 CHECK_EQ(0xed, static_cast<unsigned char>(utf8buf[utf8_expected - 3])); 5628 CHECK_EQ(0x9f, static_cast<unsigned char>(utf8buf[utf8_expected - 2])); 5629 CHECK_EQ(0xc0 - kStride, 5630 static_cast<unsigned char>(utf8buf[utf8_expected - 1])); 5631 CHECK_EQ(1, utf8buf[utf8_expected]); 5632 5633 memset(utf8buf, 0x1, sizeof(utf8buf)); 5634 len = GetUtf8Length(right_tree); 5635 CHECK_EQ(utf8_expected, len); 5636 len = right_tree->WriteUtf8(utf8buf, utf8_expected, &charlen); 5637 CHECK_EQ(utf8_expected, len); 5638 CHECK_EQ(0xd800 / kStride, charlen); 5639 CHECK_EQ(0xed, static_cast<unsigned char>(utf8buf[0])); 5640 CHECK_EQ(0x9f, static_cast<unsigned char>(utf8buf[1])); 5641 CHECK_EQ(0xc0 - kStride, static_cast<unsigned char>(utf8buf[2])); 5642 CHECK_EQ(1, utf8buf[utf8_expected]); 5643 5644 memset(buf, 0x1, sizeof(buf)); 5645 memset(wbuf, 0x1, sizeof(wbuf)); 5646 len = str->WriteAscii(buf); 5647 CHECK_EQ(5, len); 5648 len = str->Write(wbuf); 5649 CHECK_EQ(5, len); 5650 CHECK_EQ(0, strcmp("abcde", buf)); 5651 uint16_t answer1[] = {'a', 'b', 'c', 'd', 'e', '\0'}; 5652 CHECK_EQ(0, StrCmp16(answer1, wbuf)); 5653 5654 memset(buf, 0x1, sizeof(buf)); 5655 memset(wbuf, 0x1, sizeof(wbuf)); 5656 len = str->WriteAscii(buf, 0, 4); 5657 CHECK_EQ(4, len); 5658 len = str->Write(wbuf, 0, 4); 5659 CHECK_EQ(4, len); 5660 CHECK_EQ(0, strncmp("abcd\1", buf, 5)); 5661 uint16_t answer2[] = {'a', 'b', 'c', 'd', 0x101}; 5662 CHECK_EQ(0, StrNCmp16(answer2, wbuf, 5)); 5663 5664 memset(buf, 0x1, sizeof(buf)); 5665 memset(wbuf, 0x1, sizeof(wbuf)); 5666 len = str->WriteAscii(buf, 0, 5); 5667 CHECK_EQ(5, len); 5668 len = str->Write(wbuf, 0, 5); 5669 CHECK_EQ(5, len); 5670 CHECK_EQ(0, strncmp("abcde\1", buf, 6)); 5671 uint16_t answer3[] = {'a', 'b', 'c', 'd', 'e', 0x101}; 5672 CHECK_EQ(0, StrNCmp16(answer3, wbuf, 6)); 5673 5674 memset(buf, 0x1, sizeof(buf)); 5675 memset(wbuf, 0x1, sizeof(wbuf)); 5676 len = str->WriteAscii(buf, 0, 6); 5677 CHECK_EQ(5, len); 5678 len = str->Write(wbuf, 0, 6); 5679 CHECK_EQ(5, len); 5680 CHECK_EQ(0, strcmp("abcde", buf)); 5681 uint16_t answer4[] = {'a', 'b', 'c', 'd', 'e', '\0'}; 5682 CHECK_EQ(0, StrCmp16(answer4, wbuf)); 5683 5684 memset(buf, 0x1, sizeof(buf)); 5685 memset(wbuf, 0x1, sizeof(wbuf)); 5686 len = str->WriteAscii(buf, 4, -1); 5687 CHECK_EQ(1, len); 5688 len = str->Write(wbuf, 4, -1); 5689 CHECK_EQ(1, len); 5690 CHECK_EQ(0, strcmp("e", buf)); 5691 uint16_t answer5[] = {'e', '\0'}; 5692 CHECK_EQ(0, StrCmp16(answer5, wbuf)); 5693 5694 memset(buf, 0x1, sizeof(buf)); 5695 memset(wbuf, 0x1, sizeof(wbuf)); 5696 len = str->WriteAscii(buf, 4, 6); 5697 CHECK_EQ(1, len); 5698 len = str->Write(wbuf, 4, 6); 5699 CHECK_EQ(1, len); 5700 CHECK_EQ(0, strcmp("e", buf)); 5701 CHECK_EQ(0, StrCmp16(answer5, wbuf)); 5702 5703 memset(buf, 0x1, sizeof(buf)); 5704 memset(wbuf, 0x1, sizeof(wbuf)); 5705 len = str->WriteAscii(buf, 4, 1); 5706 CHECK_EQ(1, len); 5707 len = str->Write(wbuf, 4, 1); 5708 CHECK_EQ(1, len); 5709 CHECK_EQ(0, strncmp("e\1", buf, 2)); 5710 uint16_t answer6[] = {'e', 0x101}; 5711 CHECK_EQ(0, StrNCmp16(answer6, wbuf, 2)); 5712 5713 memset(buf, 0x1, sizeof(buf)); 5714 memset(wbuf, 0x1, sizeof(wbuf)); 5715 len = str->WriteAscii(buf, 3, 1); 5716 CHECK_EQ(1, len); 5717 len = str->Write(wbuf, 3, 1); 5718 CHECK_EQ(1, len); 5719 CHECK_EQ(0, strncmp("d\1", buf, 2)); 5720 uint16_t answer7[] = {'d', 0x101}; 5721 CHECK_EQ(0, StrNCmp16(answer7, wbuf, 2)); 5722 5723 memset(wbuf, 0x1, sizeof(wbuf)); 5724 wbuf[5] = 'X'; 5725 len = str->Write(wbuf, 0, 6, String::NO_NULL_TERMINATION); 5726 CHECK_EQ(5, len); 5727 CHECK_EQ('X', wbuf[5]); 5728 uint16_t answer8a[] = {'a', 'b', 'c', 'd', 'e'}; 5729 uint16_t answer8b[] = {'a', 'b', 'c', 'd', 'e', '\0'}; 5730 CHECK_EQ(0, StrNCmp16(answer8a, wbuf, 5)); 5731 CHECK_NE(0, StrCmp16(answer8b, wbuf)); 5732 wbuf[5] = '\0'; 5733 CHECK_EQ(0, StrCmp16(answer8b, wbuf)); 5734 5735 memset(buf, 0x1, sizeof(buf)); 5736 buf[5] = 'X'; 5737 len = str->WriteAscii(buf, 0, 6, String::NO_NULL_TERMINATION); 5738 CHECK_EQ(5, len); 5739 CHECK_EQ('X', buf[5]); 5740 CHECK_EQ(0, strncmp("abcde", buf, 5)); 5741 CHECK_NE(0, strcmp("abcde", buf)); 5742 buf[5] = '\0'; 5743 CHECK_EQ(0, strcmp("abcde", buf)); 5744 5745 memset(utf8buf, 0x1, sizeof(utf8buf)); 5746 utf8buf[8] = 'X'; 5747 len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen, 5748 String::NO_NULL_TERMINATION); 5749 CHECK_EQ(8, len); 5750 CHECK_EQ('X', utf8buf[8]); 5751 CHECK_EQ(5, charlen); 5752 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\342\230\203", 8)); 5753 CHECK_NE(0, strcmp(utf8buf, "abc\303\260\342\230\203")); 5754 utf8buf[8] = '\0'; 5755 CHECK_EQ(0, strcmp(utf8buf, "abc\303\260\342\230\203")); 5756 } 5757 5758 5759 static void Utf16Helper( 5760 LocalContext& context, 5761 const char* name, 5762 const char* lengths_name, 5763 int len) { 5764 Local<v8::Array> a = 5765 Local<v8::Array>::Cast(context->Global()->Get(v8_str(name))); 5766 Local<v8::Array> alens = 5767 Local<v8::Array>::Cast(context->Global()->Get(v8_str(lengths_name))); 5768 for (int i = 0; i < len; i++) { 5769 Local<v8::String> string = 5770 Local<v8::String>::Cast(a->Get(i)); 5771 Local<v8::Number> expected_len = 5772 Local<v8::Number>::Cast(alens->Get(i)); 5773 CHECK_EQ(expected_len->Value() != string->Length(), 5774 string->MayContainNonAscii()); 5775 int length = GetUtf8Length(string); 5776 CHECK_EQ(static_cast<int>(expected_len->Value()), length); 5777 } 5778 } 5779 5780 5781 static uint16_t StringGet(Handle<String> str, int index) { 5782 i::Handle<i::String> istring = 5783 v8::Utils::OpenHandle(String::Cast(*str)); 5784 return istring->Get(index); 5785 } 5786 5787 5788 static void WriteUtf8Helper( 5789 LocalContext& context, 5790 const char* name, 5791 const char* lengths_name, 5792 int len) { 5793 Local<v8::Array> b = 5794 Local<v8::Array>::Cast(context->Global()->Get(v8_str(name))); 5795 Local<v8::Array> alens = 5796 Local<v8::Array>::Cast(context->Global()->Get(v8_str(lengths_name))); 5797 char buffer[1000]; 5798 char buffer2[1000]; 5799 for (int i = 0; i < len; i++) { 5800 Local<v8::String> string = 5801 Local<v8::String>::Cast(b->Get(i)); 5802 Local<v8::Number> expected_len = 5803 Local<v8::Number>::Cast(alens->Get(i)); 5804 int utf8_length = static_cast<int>(expected_len->Value()); 5805 for (int j = utf8_length + 1; j >= 0; j--) { 5806 memset(reinterpret_cast<void*>(&buffer), 42, sizeof(buffer)); 5807 memset(reinterpret_cast<void*>(&buffer2), 42, sizeof(buffer2)); 5808 int nchars; 5809 int utf8_written = 5810 string->WriteUtf8(buffer, j, &nchars, String::NO_OPTIONS); 5811 int utf8_written2 = 5812 string->WriteUtf8(buffer2, j, &nchars, String::NO_NULL_TERMINATION); 5813 CHECK_GE(utf8_length + 1, utf8_written); 5814 CHECK_GE(utf8_length, utf8_written2); 5815 for (int k = 0; k < utf8_written2; k++) { 5816 CHECK_EQ(buffer[k], buffer2[k]); 5817 } 5818 CHECK(nchars * 3 >= utf8_written - 1); 5819 CHECK(nchars <= utf8_written); 5820 if (j == utf8_length + 1) { 5821 CHECK_EQ(utf8_written2, utf8_length); 5822 CHECK_EQ(utf8_written2 + 1, utf8_written); 5823 } 5824 CHECK_EQ(buffer[utf8_written], 42); 5825 if (j > utf8_length) { 5826 if (utf8_written != 0) CHECK_EQ(buffer[utf8_written - 1], 0); 5827 if (utf8_written > 1) CHECK_NE(buffer[utf8_written - 2], 42); 5828 Handle<String> roundtrip = v8_str(buffer); 5829 CHECK(roundtrip->Equals(string)); 5830 } else { 5831 if (utf8_written != 0) CHECK_NE(buffer[utf8_written - 1], 42); 5832 } 5833 if (utf8_written2 != 0) CHECK_NE(buffer[utf8_written - 1], 42); 5834 if (nchars >= 2) { 5835 uint16_t trail = StringGet(string, nchars - 1); 5836 uint16_t lead = StringGet(string, nchars - 2); 5837 if (((lead & 0xfc00) == 0xd800) && 5838 ((trail & 0xfc00) == 0xdc00)) { 5839 unsigned char u1 = buffer2[utf8_written2 - 4]; 5840 unsigned char u2 = buffer2[utf8_written2 - 3]; 5841 unsigned char u3 = buffer2[utf8_written2 - 2]; 5842 unsigned char u4 = buffer2[utf8_written2 - 1]; 5843 CHECK_EQ((u1 & 0xf8), 0xf0); 5844 CHECK_EQ((u2 & 0xc0), 0x80); 5845 CHECK_EQ((u3 & 0xc0), 0x80); 5846 CHECK_EQ((u4 & 0xc0), 0x80); 5847 uint32_t c = 0x10000 + ((lead & 0x3ff) << 10) + (trail & 0x3ff); 5848 CHECK_EQ((u4 & 0x3f), (c & 0x3f)); 5849 CHECK_EQ((u3 & 0x3f), ((c >> 6) & 0x3f)); 5850 CHECK_EQ((u2 & 0x3f), ((c >> 12) & 0x3f)); 5851 CHECK_EQ((u1 & 0x3), c >> 18); 5852 } 5853 } 5854 } 5855 } 5856 } 5857 5858 5859 THREADED_TEST(Utf16) { 5860 LocalContext context; 5861 v8::HandleScope scope; 5862 CompileRun( 5863 "var pad = '01234567890123456789';" 5864 "var p = [];" 5865 "var plens = [20, 3, 3];" 5866 "p.push('01234567890123456789');" 5867 "var lead = 0xd800;" 5868 "var trail = 0xdc00;" 5869 "p.push(String.fromCharCode(0xd800));" 5870 "p.push(String.fromCharCode(0xdc00));" 5871 "var a = [];" 5872 "var b = [];" 5873 "var c = [];" 5874 "var alens = [];" 5875 "for (var i = 0; i < 3; i++) {" 5876 " p[1] = String.fromCharCode(lead++);" 5877 " for (var j = 0; j < 3; j++) {" 5878 " p[2] = String.fromCharCode(trail++);" 5879 " a.push(p[i] + p[j]);" 5880 " b.push(p[i] + p[j]);" 5881 " c.push(p[i] + p[j]);" 5882 " alens.push(plens[i] + plens[j]);" 5883 " }" 5884 "}" 5885 "alens[5] -= 2;" // Here the surrogate pairs match up. 5886 "var a2 = [];" 5887 "var b2 = [];" 5888 "var c2 = [];" 5889 "var a2lens = [];" 5890 "for (var m = 0; m < 9; m++) {" 5891 " for (var n = 0; n < 9; n++) {" 5892 " a2.push(a[m] + a[n]);" 5893 " b2.push(b[m] + b[n]);" 5894 " var newc = 'x' + c[m] + c[n] + 'y';" 5895 " c2.push(newc.substring(1, newc.length - 1));" 5896 " var utf = alens[m] + alens[n];" // And here. 5897 // The 'n's that start with 0xdc.. are 6-8 5898 // The 'm's that end with 0xd8.. are 1, 4 and 7 5899 " if ((m % 3) == 1 && n >= 6) utf -= 2;" 5900 " a2lens.push(utf);" 5901 " }" 5902 "}"); 5903 Utf16Helper(context, "a", "alens", 9); 5904 Utf16Helper(context, "a2", "a2lens", 81); 5905 WriteUtf8Helper(context, "b", "alens", 9); 5906 WriteUtf8Helper(context, "b2", "a2lens", 81); 5907 WriteUtf8Helper(context, "c2", "a2lens", 81); 5908 } 5909 5910 5911 static bool SameSymbol(Handle<String> s1, Handle<String> s2) { 5912 i::Handle<i::String> is1(v8::Utils::OpenHandle(*s1)); 5913 i::Handle<i::String> is2(v8::Utils::OpenHandle(*s2)); 5914 return *is1 == *is2; 5915 } 5916 5917 5918 static void SameSymbolHelper(const char* a, const char* b) { 5919 Handle<String> symbol1 = v8::String::NewSymbol(a); 5920 Handle<String> symbol2 = v8::String::NewSymbol(b); 5921 CHECK(SameSymbol(symbol1, symbol2)); 5922 } 5923 5924 5925 THREADED_TEST(Utf16Symbol) { 5926 LocalContext context; 5927 v8::HandleScope scope; 5928 5929 Handle<String> symbol1 = v8::String::NewSymbol("abc"); 5930 Handle<String> symbol2 = v8::String::NewSymbol("abc"); 5931 CHECK(SameSymbol(symbol1, symbol2)); 5932 5933 SameSymbolHelper("\360\220\220\205", // 4 byte encoding. 5934 "\355\240\201\355\260\205"); // 2 3-byte surrogates. 5935 SameSymbolHelper("\355\240\201\355\260\206", // 2 3-byte surrogates. 5936 "\360\220\220\206"); // 4 byte encoding. 5937 SameSymbolHelper("x\360\220\220\205", // 4 byte encoding. 5938 "x\355\240\201\355\260\205"); // 2 3-byte surrogates. 5939 SameSymbolHelper("x\355\240\201\355\260\206", // 2 3-byte surrogates. 5940 "x\360\220\220\206"); // 4 byte encoding. 5941 CompileRun( 5942 "var sym0 = 'benedictus';" 5943 "var sym0b = 'S\303\270ren';" 5944 "var sym1 = '\355\240\201\355\260\207';" 5945 "var sym2 = '\360\220\220\210';" 5946 "var sym3 = 'x\355\240\201\355\260\207';" 5947 "var sym4 = 'x\360\220\220\210';" 5948 "if (sym1.length != 2) throw sym1;" 5949 "if (sym1.charCodeAt(1) != 0xdc07) throw sym1.charCodeAt(1);" 5950 "if (sym2.length != 2) throw sym2;" 5951 "if (sym2.charCodeAt(1) != 0xdc08) throw sym2.charCodeAt(2);" 5952 "if (sym3.length != 3) throw sym3;" 5953 "if (sym3.charCodeAt(2) != 0xdc07) throw sym1.charCodeAt(2);" 5954 "if (sym4.length != 3) throw sym4;" 5955 "if (sym4.charCodeAt(2) != 0xdc08) throw sym2.charCodeAt(2);"); 5956 Handle<String> sym0 = v8::String::NewSymbol("benedictus"); 5957 Handle<String> sym0b = v8::String::NewSymbol("S\303\270ren"); 5958 Handle<String> sym1 = v8::String::NewSymbol("\355\240\201\355\260\207"); 5959 Handle<String> sym2 = v8::String::NewSymbol("\360\220\220\210"); 5960 Handle<String> sym3 = v8::String::NewSymbol("x\355\240\201\355\260\207"); 5961 Handle<String> sym4 = v8::String::NewSymbol("x\360\220\220\210"); 5962 v8::Local<v8::Object> global = context->Global(); 5963 Local<Value> s0 = global->Get(v8_str("sym0")); 5964 Local<Value> s0b = global->Get(v8_str("sym0b")); 5965 Local<Value> s1 = global->Get(v8_str("sym1")); 5966 Local<Value> s2 = global->Get(v8_str("sym2")); 5967 Local<Value> s3 = global->Get(v8_str("sym3")); 5968 Local<Value> s4 = global->Get(v8_str("sym4")); 5969 CHECK(SameSymbol(sym0, Handle<String>(String::Cast(*s0)))); 5970 CHECK(SameSymbol(sym0b, Handle<String>(String::Cast(*s0b)))); 5971 CHECK(SameSymbol(sym1, Handle<String>(String::Cast(*s1)))); 5972 CHECK(SameSymbol(sym2, Handle<String>(String::Cast(*s2)))); 5973 CHECK(SameSymbol(sym3, Handle<String>(String::Cast(*s3)))); 5974 CHECK(SameSymbol(sym4, Handle<String>(String::Cast(*s4)))); 5975 } 5976 5977 5978 THREADED_TEST(ToArrayIndex) { 5979 v8::HandleScope scope; 5980 LocalContext context; 5981 5982 v8::Handle<String> str = v8_str("42"); 5983 v8::Handle<v8::Uint32> index = str->ToArrayIndex(); 5984 CHECK(!index.IsEmpty()); 5985 CHECK_EQ(42.0, index->Uint32Value()); 5986 str = v8_str("42asdf"); 5987 index = str->ToArrayIndex(); 5988 CHECK(index.IsEmpty()); 5989 str = v8_str("-42"); 5990 index = str->ToArrayIndex(); 5991 CHECK(index.IsEmpty()); 5992 str = v8_str("4294967295"); 5993 index = str->ToArrayIndex(); 5994 CHECK(!index.IsEmpty()); 5995 CHECK_EQ(4294967295.0, index->Uint32Value()); 5996 v8::Handle<v8::Number> num = v8::Number::New(1); 5997 index = num->ToArrayIndex(); 5998 CHECK(!index.IsEmpty()); 5999 CHECK_EQ(1.0, index->Uint32Value()); 6000 num = v8::Number::New(-1); 6001 index = num->ToArrayIndex(); 6002 CHECK(index.IsEmpty()); 6003 v8::Handle<v8::Object> obj = v8::Object::New(); 6004 index = obj->ToArrayIndex(); 6005 CHECK(index.IsEmpty()); 6006 } 6007 6008 6009 THREADED_TEST(ErrorConstruction) { 6010 v8::HandleScope scope; 6011 LocalContext context; 6012 6013 v8::Handle<String> foo = v8_str("foo"); 6014 v8::Handle<String> message = v8_str("message"); 6015 v8::Handle<Value> range_error = v8::Exception::RangeError(foo); 6016 CHECK(range_error->IsObject()); 6017 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo)); 6018 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo); 6019 CHECK(reference_error->IsObject()); 6020 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo)); 6021 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo); 6022 CHECK(syntax_error->IsObject()); 6023 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo)); 6024 v8::Handle<Value> type_error = v8::Exception::TypeError(foo); 6025 CHECK(type_error->IsObject()); 6026 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo)); 6027 v8::Handle<Value> error = v8::Exception::Error(foo); 6028 CHECK(error->IsObject()); 6029 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo)); 6030 } 6031 6032 6033 static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) { 6034 ApiTestFuzzer::Fuzz(); 6035 return v8_num(10); 6036 } 6037 6038 6039 static void YSetter(Local<String> name, 6040 Local<Value> value, 6041 const AccessorInfo& info) { 6042 if (info.This()->Has(name)) { 6043 info.This()->Delete(name); 6044 } 6045 info.This()->Set(name, value); 6046 } 6047 6048 6049 THREADED_TEST(DeleteAccessor) { 6050 v8::HandleScope scope; 6051 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 6052 obj->SetAccessor(v8_str("y"), YGetter, YSetter); 6053 LocalContext context; 6054 v8::Handle<v8::Object> holder = obj->NewInstance(); 6055 context->Global()->Set(v8_str("holder"), holder); 6056 v8::Handle<Value> result = CompileRun( 6057 "holder.y = 11; holder.y = 12; holder.y"); 6058 CHECK_EQ(12, result->Uint32Value()); 6059 } 6060 6061 6062 THREADED_TEST(TypeSwitch) { 6063 v8::HandleScope scope; 6064 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New(); 6065 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New(); 6066 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New(); 6067 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 }; 6068 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs); 6069 LocalContext context; 6070 v8::Handle<v8::Object> obj0 = v8::Object::New(); 6071 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance(); 6072 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance(); 6073 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance(); 6074 for (int i = 0; i < 10; i++) { 6075 CHECK_EQ(0, type_switch->match(obj0)); 6076 CHECK_EQ(1, type_switch->match(obj1)); 6077 CHECK_EQ(2, type_switch->match(obj2)); 6078 CHECK_EQ(3, type_switch->match(obj3)); 6079 CHECK_EQ(3, type_switch->match(obj3)); 6080 CHECK_EQ(2, type_switch->match(obj2)); 6081 CHECK_EQ(1, type_switch->match(obj1)); 6082 CHECK_EQ(0, type_switch->match(obj0)); 6083 } 6084 } 6085 6086 6087 // For use within the TestSecurityHandler() test. 6088 static bool g_security_callback_result = false; 6089 static bool NamedSecurityTestCallback(Local<v8::Object> global, 6090 Local<Value> name, 6091 v8::AccessType type, 6092 Local<Value> data) { 6093 // Always allow read access. 6094 if (type == v8::ACCESS_GET) 6095 return true; 6096 6097 // Sometimes allow other access. 6098 return g_security_callback_result; 6099 } 6100 6101 6102 static bool IndexedSecurityTestCallback(Local<v8::Object> global, 6103 uint32_t key, 6104 v8::AccessType type, 6105 Local<Value> data) { 6106 // Always allow read access. 6107 if (type == v8::ACCESS_GET) 6108 return true; 6109 6110 // Sometimes allow other access. 6111 return g_security_callback_result; 6112 } 6113 6114 6115 static int trouble_nesting = 0; 6116 static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) { 6117 ApiTestFuzzer::Fuzz(); 6118 trouble_nesting++; 6119 6120 // Call a JS function that throws an uncaught exception. 6121 Local<v8::Object> arg_this = Context::GetCurrent()->Global(); 6122 Local<Value> trouble_callee = (trouble_nesting == 3) ? 6123 arg_this->Get(v8_str("trouble_callee")) : 6124 arg_this->Get(v8_str("trouble_caller")); 6125 CHECK(trouble_callee->IsFunction()); 6126 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL); 6127 } 6128 6129 6130 static int report_count = 0; 6131 static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>, 6132 v8::Handle<Value>) { 6133 report_count++; 6134 } 6135 6136 6137 // Counts uncaught exceptions, but other tests running in parallel 6138 // also have uncaught exceptions. 6139 TEST(ApiUncaughtException) { 6140 report_count = 0; 6141 v8::HandleScope scope; 6142 LocalContext env; 6143 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener); 6144 6145 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 6146 v8::Local<v8::Object> global = env->Global(); 6147 global->Set(v8_str("trouble"), fun->GetFunction()); 6148 6149 Script::Compile(v8_str("function trouble_callee() {" 6150 " var x = null;" 6151 " return x.foo;" 6152 "};" 6153 "function trouble_caller() {" 6154 " trouble();" 6155 "};"))->Run(); 6156 Local<Value> trouble = global->Get(v8_str("trouble")); 6157 CHECK(trouble->IsFunction()); 6158 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee")); 6159 CHECK(trouble_callee->IsFunction()); 6160 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller")); 6161 CHECK(trouble_caller->IsFunction()); 6162 Function::Cast(*trouble_caller)->Call(global, 0, NULL); 6163 CHECK_EQ(1, report_count); 6164 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener); 6165 } 6166 6167 static const char* script_resource_name = "ExceptionInNativeScript.js"; 6168 static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message, 6169 v8::Handle<Value>) { 6170 v8::Handle<v8::Value> name_val = message->GetScriptResourceName(); 6171 CHECK(!name_val.IsEmpty() && name_val->IsString()); 6172 v8::String::AsciiValue name(message->GetScriptResourceName()); 6173 CHECK_EQ(script_resource_name, *name); 6174 CHECK_EQ(3, message->GetLineNumber()); 6175 v8::String::AsciiValue source_line(message->GetSourceLine()); 6176 CHECK_EQ(" new o.foo();", *source_line); 6177 } 6178 6179 TEST(ExceptionInNativeScript) { 6180 v8::HandleScope scope; 6181 LocalContext env; 6182 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener); 6183 6184 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 6185 v8::Local<v8::Object> global = env->Global(); 6186 global->Set(v8_str("trouble"), fun->GetFunction()); 6187 6188 Script::Compile(v8_str("function trouble() {\n" 6189 " var o = {};\n" 6190 " new o.foo();\n" 6191 "};"), v8::String::New(script_resource_name))->Run(); 6192 Local<Value> trouble = global->Get(v8_str("trouble")); 6193 CHECK(trouble->IsFunction()); 6194 Function::Cast(*trouble)->Call(global, 0, NULL); 6195 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener); 6196 } 6197 6198 6199 TEST(CompilationErrorUsingTryCatchHandler) { 6200 v8::HandleScope scope; 6201 LocalContext env; 6202 v8::TryCatch try_catch; 6203 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile.")); 6204 CHECK_NE(NULL, *try_catch.Exception()); 6205 CHECK(try_catch.HasCaught()); 6206 } 6207 6208 6209 TEST(TryCatchFinallyUsingTryCatchHandler) { 6210 v8::HandleScope scope; 6211 LocalContext env; 6212 v8::TryCatch try_catch; 6213 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run(); 6214 CHECK(!try_catch.HasCaught()); 6215 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run(); 6216 CHECK(try_catch.HasCaught()); 6217 try_catch.Reset(); 6218 Script::Compile(v8_str("(function() {" 6219 "try { throw ''; } finally { return; }" 6220 "})()"))->Run(); 6221 CHECK(!try_catch.HasCaught()); 6222 Script::Compile(v8_str("(function()" 6223 " { try { throw ''; } finally { throw 0; }" 6224 "})()"))->Run(); 6225 CHECK(try_catch.HasCaught()); 6226 } 6227 6228 6229 // SecurityHandler can't be run twice 6230 TEST(SecurityHandler) { 6231 v8::HandleScope scope0; 6232 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 6233 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback, 6234 IndexedSecurityTestCallback); 6235 // Create an environment 6236 v8::Persistent<Context> context0 = 6237 Context::New(NULL, global_template); 6238 context0->Enter(); 6239 6240 v8::Handle<v8::Object> global0 = context0->Global(); 6241 v8::Handle<Script> script0 = v8_compile("foo = 111"); 6242 script0->Run(); 6243 global0->Set(v8_str("0"), v8_num(999)); 6244 v8::Handle<Value> foo0 = global0->Get(v8_str("foo")); 6245 CHECK_EQ(111, foo0->Int32Value()); 6246 v8::Handle<Value> z0 = global0->Get(v8_str("0")); 6247 CHECK_EQ(999, z0->Int32Value()); 6248 6249 // Create another environment, should fail security checks. 6250 v8::HandleScope scope1; 6251 6252 v8::Persistent<Context> context1 = 6253 Context::New(NULL, global_template); 6254 context1->Enter(); 6255 6256 v8::Handle<v8::Object> global1 = context1->Global(); 6257 global1->Set(v8_str("othercontext"), global0); 6258 // This set will fail the security check. 6259 v8::Handle<Script> script1 = 6260 v8_compile("othercontext.foo = 222; othercontext[0] = 888;"); 6261 script1->Run(); 6262 // This read will pass the security check. 6263 v8::Handle<Value> foo1 = global0->Get(v8_str("foo")); 6264 CHECK_EQ(111, foo1->Int32Value()); 6265 // This read will pass the security check. 6266 v8::Handle<Value> z1 = global0->Get(v8_str("0")); 6267 CHECK_EQ(999, z1->Int32Value()); 6268 6269 // Create another environment, should pass security checks. 6270 { g_security_callback_result = true; // allow security handler to pass. 6271 v8::HandleScope scope2; 6272 LocalContext context2; 6273 v8::Handle<v8::Object> global2 = context2->Global(); 6274 global2->Set(v8_str("othercontext"), global0); 6275 v8::Handle<Script> script2 = 6276 v8_compile("othercontext.foo = 333; othercontext[0] = 888;"); 6277 script2->Run(); 6278 v8::Handle<Value> foo2 = global0->Get(v8_str("foo")); 6279 CHECK_EQ(333, foo2->Int32Value()); 6280 v8::Handle<Value> z2 = global0->Get(v8_str("0")); 6281 CHECK_EQ(888, z2->Int32Value()); 6282 } 6283 6284 context1->Exit(); 6285 context1.Dispose(); 6286 6287 context0->Exit(); 6288 context0.Dispose(); 6289 } 6290 6291 6292 THREADED_TEST(SecurityChecks) { 6293 v8::HandleScope handle_scope; 6294 LocalContext env1; 6295 v8::Persistent<Context> env2 = Context::New(); 6296 6297 Local<Value> foo = v8_str("foo"); 6298 Local<Value> bar = v8_str("bar"); 6299 6300 // Set to the same domain. 6301 env1->SetSecurityToken(foo); 6302 6303 // Create a function in env1. 6304 Script::Compile(v8_str("spy=function(){return spy;}"))->Run(); 6305 Local<Value> spy = env1->Global()->Get(v8_str("spy")); 6306 CHECK(spy->IsFunction()); 6307 6308 // Create another function accessing global objects. 6309 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run(); 6310 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2")); 6311 CHECK(spy2->IsFunction()); 6312 6313 // Switch to env2 in the same domain and invoke spy on env2. 6314 { 6315 env2->SetSecurityToken(foo); 6316 // Enter env2 6317 Context::Scope scope_env2(env2); 6318 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL); 6319 CHECK(result->IsFunction()); 6320 } 6321 6322 { 6323 env2->SetSecurityToken(bar); 6324 Context::Scope scope_env2(env2); 6325 6326 // Call cross_domain_call, it should throw an exception 6327 v8::TryCatch try_catch; 6328 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL); 6329 CHECK(try_catch.HasCaught()); 6330 } 6331 6332 env2.Dispose(); 6333 } 6334 6335 6336 // Regression test case for issue 1183439. 6337 THREADED_TEST(SecurityChecksForPrototypeChain) { 6338 v8::HandleScope scope; 6339 LocalContext current; 6340 v8::Persistent<Context> other = Context::New(); 6341 6342 // Change context to be able to get to the Object function in the 6343 // other context without hitting the security checks. 6344 v8::Local<Value> other_object; 6345 { Context::Scope scope(other); 6346 other_object = other->Global()->Get(v8_str("Object")); 6347 other->Global()->Set(v8_num(42), v8_num(87)); 6348 } 6349 6350 current->Global()->Set(v8_str("other"), other->Global()); 6351 CHECK(v8_compile("other")->Run()->Equals(other->Global())); 6352 6353 // Make sure the security check fails here and we get an undefined 6354 // result instead of getting the Object function. Repeat in a loop 6355 // to make sure to exercise the IC code. 6356 v8::Local<Script> access_other0 = v8_compile("other.Object"); 6357 v8::Local<Script> access_other1 = v8_compile("other[42]"); 6358 for (int i = 0; i < 5; i++) { 6359 CHECK(!access_other0->Run()->Equals(other_object)); 6360 CHECK(access_other0->Run()->IsUndefined()); 6361 CHECK(!access_other1->Run()->Equals(v8_num(87))); 6362 CHECK(access_other1->Run()->IsUndefined()); 6363 } 6364 6365 // Create an object that has 'other' in its prototype chain and make 6366 // sure we cannot access the Object function indirectly through 6367 // that. Repeat in a loop to make sure to exercise the IC code. 6368 v8_compile("function F() { };" 6369 "F.prototype = other;" 6370 "var f = new F();")->Run(); 6371 v8::Local<Script> access_f0 = v8_compile("f.Object"); 6372 v8::Local<Script> access_f1 = v8_compile("f[42]"); 6373 for (int j = 0; j < 5; j++) { 6374 CHECK(!access_f0->Run()->Equals(other_object)); 6375 CHECK(access_f0->Run()->IsUndefined()); 6376 CHECK(!access_f1->Run()->Equals(v8_num(87))); 6377 CHECK(access_f1->Run()->IsUndefined()); 6378 } 6379 6380 // Now it gets hairy: Set the prototype for the other global object 6381 // to be the current global object. The prototype chain for 'f' now 6382 // goes through 'other' but ends up in the current global object. 6383 { Context::Scope scope(other); 6384 other->Global()->Set(v8_str("__proto__"), current->Global()); 6385 } 6386 // Set a named and an index property on the current global 6387 // object. To force the lookup to go through the other global object, 6388 // the properties must not exist in the other global object. 6389 current->Global()->Set(v8_str("foo"), v8_num(100)); 6390 current->Global()->Set(v8_num(99), v8_num(101)); 6391 // Try to read the properties from f and make sure that the access 6392 // gets stopped by the security checks on the other global object. 6393 Local<Script> access_f2 = v8_compile("f.foo"); 6394 Local<Script> access_f3 = v8_compile("f[99]"); 6395 for (int k = 0; k < 5; k++) { 6396 CHECK(!access_f2->Run()->Equals(v8_num(100))); 6397 CHECK(access_f2->Run()->IsUndefined()); 6398 CHECK(!access_f3->Run()->Equals(v8_num(101))); 6399 CHECK(access_f3->Run()->IsUndefined()); 6400 } 6401 other.Dispose(); 6402 } 6403 6404 6405 THREADED_TEST(CrossDomainDelete) { 6406 v8::HandleScope handle_scope; 6407 LocalContext env1; 6408 v8::Persistent<Context> env2 = Context::New(); 6409 6410 Local<Value> foo = v8_str("foo"); 6411 Local<Value> bar = v8_str("bar"); 6412 6413 // Set to the same domain. 6414 env1->SetSecurityToken(foo); 6415 env2->SetSecurityToken(foo); 6416 6417 env1->Global()->Set(v8_str("prop"), v8_num(3)); 6418 env2->Global()->Set(v8_str("env1"), env1->Global()); 6419 6420 // Change env2 to a different domain and delete env1.prop. 6421 env2->SetSecurityToken(bar); 6422 { 6423 Context::Scope scope_env2(env2); 6424 Local<Value> result = 6425 Script::Compile(v8_str("delete env1.prop"))->Run(); 6426 CHECK(result->IsFalse()); 6427 } 6428 6429 // Check that env1.prop still exists. 6430 Local<Value> v = env1->Global()->Get(v8_str("prop")); 6431 CHECK(v->IsNumber()); 6432 CHECK_EQ(3, v->Int32Value()); 6433 6434 env2.Dispose(); 6435 } 6436 6437 6438 THREADED_TEST(CrossDomainIsPropertyEnumerable) { 6439 v8::HandleScope handle_scope; 6440 LocalContext env1; 6441 v8::Persistent<Context> env2 = Context::New(); 6442 6443 Local<Value> foo = v8_str("foo"); 6444 Local<Value> bar = v8_str("bar"); 6445 6446 // Set to the same domain. 6447 env1->SetSecurityToken(foo); 6448 env2->SetSecurityToken(foo); 6449 6450 env1->Global()->Set(v8_str("prop"), v8_num(3)); 6451 env2->Global()->Set(v8_str("env1"), env1->Global()); 6452 6453 // env1.prop is enumerable in env2. 6454 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')"); 6455 { 6456 Context::Scope scope_env2(env2); 6457 Local<Value> result = Script::Compile(test)->Run(); 6458 CHECK(result->IsTrue()); 6459 } 6460 6461 // Change env2 to a different domain and test again. 6462 env2->SetSecurityToken(bar); 6463 { 6464 Context::Scope scope_env2(env2); 6465 Local<Value> result = Script::Compile(test)->Run(); 6466 CHECK(result->IsFalse()); 6467 } 6468 6469 env2.Dispose(); 6470 } 6471 6472 6473 THREADED_TEST(CrossDomainForIn) { 6474 v8::HandleScope handle_scope; 6475 LocalContext env1; 6476 v8::Persistent<Context> env2 = Context::New(); 6477 6478 Local<Value> foo = v8_str("foo"); 6479 Local<Value> bar = v8_str("bar"); 6480 6481 // Set to the same domain. 6482 env1->SetSecurityToken(foo); 6483 env2->SetSecurityToken(foo); 6484 6485 env1->Global()->Set(v8_str("prop"), v8_num(3)); 6486 env2->Global()->Set(v8_str("env1"), env1->Global()); 6487 6488 // Change env2 to a different domain and set env1's global object 6489 // as the __proto__ of an object in env2 and enumerate properties 6490 // in for-in. It shouldn't enumerate properties on env1's global 6491 // object. 6492 env2->SetSecurityToken(bar); 6493 { 6494 Context::Scope scope_env2(env2); 6495 Local<Value> result = 6496 CompileRun("(function(){var obj = {'__proto__':env1};" 6497 "for (var p in obj)" 6498 " if (p == 'prop') return false;" 6499 "return true;})()"); 6500 CHECK(result->IsTrue()); 6501 } 6502 env2.Dispose(); 6503 } 6504 6505 6506 TEST(ContextDetachGlobal) { 6507 v8::HandleScope handle_scope; 6508 LocalContext env1; 6509 v8::Persistent<Context> env2 = Context::New(); 6510 6511 Local<v8::Object> global1 = env1->Global(); 6512 6513 Local<Value> foo = v8_str("foo"); 6514 6515 // Set to the same domain. 6516 env1->SetSecurityToken(foo); 6517 env2->SetSecurityToken(foo); 6518 6519 // Enter env2 6520 env2->Enter(); 6521 6522 // Create a function in env2 and add a reference to it in env1. 6523 Local<v8::Object> global2 = env2->Global(); 6524 global2->Set(v8_str("prop"), v8::Integer::New(1)); 6525 CompileRun("function getProp() {return prop;}"); 6526 6527 env1->Global()->Set(v8_str("getProp"), 6528 global2->Get(v8_str("getProp"))); 6529 6530 // Detach env2's global, and reuse the global object of env2 6531 env2->Exit(); 6532 env2->DetachGlobal(); 6533 // env2 has a new global object. 6534 CHECK(!env2->Global()->Equals(global2)); 6535 6536 v8::Persistent<Context> env3 = 6537 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 6538 env3->SetSecurityToken(v8_str("bar")); 6539 env3->Enter(); 6540 6541 Local<v8::Object> global3 = env3->Global(); 6542 CHECK_EQ(global2, global3); 6543 CHECK(global3->Get(v8_str("prop"))->IsUndefined()); 6544 CHECK(global3->Get(v8_str("getProp"))->IsUndefined()); 6545 global3->Set(v8_str("prop"), v8::Integer::New(-1)); 6546 global3->Set(v8_str("prop2"), v8::Integer::New(2)); 6547 env3->Exit(); 6548 6549 // Call getProp in env1, and it should return the value 1 6550 { 6551 Local<Value> get_prop = global1->Get(v8_str("getProp")); 6552 CHECK(get_prop->IsFunction()); 6553 v8::TryCatch try_catch; 6554 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL); 6555 CHECK(!try_catch.HasCaught()); 6556 CHECK_EQ(1, r->Int32Value()); 6557 } 6558 6559 // Check that env3 is not accessible from env1 6560 { 6561 Local<Value> r = global3->Get(v8_str("prop2")); 6562 CHECK(r->IsUndefined()); 6563 } 6564 6565 env2.Dispose(); 6566 env3.Dispose(); 6567 } 6568 6569 6570 TEST(DetachAndReattachGlobal) { 6571 v8::HandleScope scope; 6572 LocalContext env1; 6573 6574 // Create second environment. 6575 v8::Persistent<Context> env2 = Context::New(); 6576 6577 Local<Value> foo = v8_str("foo"); 6578 6579 // Set same security token for env1 and env2. 6580 env1->SetSecurityToken(foo); 6581 env2->SetSecurityToken(foo); 6582 6583 // Create a property on the global object in env2. 6584 { 6585 v8::Context::Scope scope(env2); 6586 env2->Global()->Set(v8_str("p"), v8::Integer::New(42)); 6587 } 6588 6589 // Create a reference to env2 global from env1 global. 6590 env1->Global()->Set(v8_str("other"), env2->Global()); 6591 6592 // Check that we have access to other.p in env2 from env1. 6593 Local<Value> result = CompileRun("other.p"); 6594 CHECK(result->IsInt32()); 6595 CHECK_EQ(42, result->Int32Value()); 6596 6597 // Hold on to global from env2 and detach global from env2. 6598 Local<v8::Object> global2 = env2->Global(); 6599 env2->DetachGlobal(); 6600 6601 // Check that the global has been detached. No other.p property can 6602 // be found. 6603 result = CompileRun("other.p"); 6604 CHECK(result->IsUndefined()); 6605 6606 // Reuse global2 for env3. 6607 v8::Persistent<Context> env3 = 6608 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 6609 CHECK_EQ(global2, env3->Global()); 6610 6611 // Start by using the same security token for env3 as for env1 and env2. 6612 env3->SetSecurityToken(foo); 6613 6614 // Create a property on the global object in env3. 6615 { 6616 v8::Context::Scope scope(env3); 6617 env3->Global()->Set(v8_str("p"), v8::Integer::New(24)); 6618 } 6619 6620 // Check that other.p is now the property in env3 and that we have access. 6621 result = CompileRun("other.p"); 6622 CHECK(result->IsInt32()); 6623 CHECK_EQ(24, result->Int32Value()); 6624 6625 // Change security token for env3 to something different from env1 and env2. 6626 env3->SetSecurityToken(v8_str("bar")); 6627 6628 // Check that we do not have access to other.p in env1. |other| is now 6629 // the global object for env3 which has a different security token, 6630 // so access should be blocked. 6631 result = CompileRun("other.p"); 6632 CHECK(result->IsUndefined()); 6633 6634 // Detach the global for env3 and reattach it to env2. 6635 env3->DetachGlobal(); 6636 env2->ReattachGlobal(global2); 6637 6638 // Check that we have access to other.p again in env1. |other| is now 6639 // the global object for env2 which has the same security token as env1. 6640 result = CompileRun("other.p"); 6641 CHECK(result->IsInt32()); 6642 CHECK_EQ(42, result->Int32Value()); 6643 6644 env2.Dispose(); 6645 env3.Dispose(); 6646 } 6647 6648 6649 static bool allowed_access_type[v8::ACCESS_KEYS + 1] = { false }; 6650 static bool NamedAccessBlocker(Local<v8::Object> global, 6651 Local<Value> name, 6652 v8::AccessType type, 6653 Local<Value> data) { 6654 return Context::GetCurrent()->Global()->Equals(global) || 6655 allowed_access_type[type]; 6656 } 6657 6658 6659 static bool IndexedAccessBlocker(Local<v8::Object> global, 6660 uint32_t key, 6661 v8::AccessType type, 6662 Local<Value> data) { 6663 return Context::GetCurrent()->Global()->Equals(global) || 6664 allowed_access_type[type]; 6665 } 6666 6667 6668 static int g_echo_value = -1; 6669 static v8::Handle<Value> EchoGetter(Local<String> name, 6670 const AccessorInfo& info) { 6671 return v8_num(g_echo_value); 6672 } 6673 6674 6675 static void EchoSetter(Local<String> name, 6676 Local<Value> value, 6677 const AccessorInfo&) { 6678 if (value->IsNumber()) 6679 g_echo_value = value->Int32Value(); 6680 } 6681 6682 6683 static v8::Handle<Value> UnreachableGetter(Local<String> name, 6684 const AccessorInfo& info) { 6685 CHECK(false); // This function should not be called.. 6686 return v8::Undefined(); 6687 } 6688 6689 6690 static void UnreachableSetter(Local<String>, Local<Value>, 6691 const AccessorInfo&) { 6692 CHECK(false); // This function should nto be called. 6693 } 6694 6695 6696 TEST(AccessControl) { 6697 v8::HandleScope handle_scope; 6698 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 6699 6700 global_template->SetAccessCheckCallbacks(NamedAccessBlocker, 6701 IndexedAccessBlocker); 6702 6703 // Add an accessor accessible by cross-domain JS code. 6704 global_template->SetAccessor( 6705 v8_str("accessible_prop"), 6706 EchoGetter, EchoSetter, 6707 v8::Handle<Value>(), 6708 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); 6709 6710 // Add an accessor that is not accessible by cross-domain JS code. 6711 global_template->SetAccessor(v8_str("blocked_prop"), 6712 UnreachableGetter, UnreachableSetter, 6713 v8::Handle<Value>(), 6714 v8::DEFAULT); 6715 6716 // Create an environment 6717 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 6718 context0->Enter(); 6719 6720 v8::Handle<v8::Object> global0 = context0->Global(); 6721 6722 // Define a property with JS getter and setter. 6723 CompileRun( 6724 "function getter() { return 'getter'; };\n" 6725 "function setter() { return 'setter'; }\n" 6726 "Object.defineProperty(this, 'js_accessor_p', {get:getter, set:setter})"); 6727 6728 Local<Value> getter = global0->Get(v8_str("getter")); 6729 Local<Value> setter = global0->Get(v8_str("setter")); 6730 6731 // And define normal element. 6732 global0->Set(239, v8_str("239")); 6733 6734 // Define an element with JS getter and setter. 6735 CompileRun( 6736 "function el_getter() { return 'el_getter'; };\n" 6737 "function el_setter() { return 'el_setter'; };\n" 6738 "Object.defineProperty(this, '42', {get: el_getter, set: el_setter});"); 6739 6740 Local<Value> el_getter = global0->Get(v8_str("el_getter")); 6741 Local<Value> el_setter = global0->Get(v8_str("el_setter")); 6742 6743 v8::HandleScope scope1; 6744 6745 v8::Persistent<Context> context1 = Context::New(); 6746 context1->Enter(); 6747 6748 v8::Handle<v8::Object> global1 = context1->Global(); 6749 global1->Set(v8_str("other"), global0); 6750 6751 // Access blocked property. 6752 CompileRun("other.blocked_prop = 1"); 6753 6754 ExpectUndefined("other.blocked_prop"); 6755 ExpectUndefined( 6756 "Object.getOwnPropertyDescriptor(other, 'blocked_prop')"); 6757 ExpectFalse("propertyIsEnumerable.call(other, 'blocked_prop')"); 6758 6759 // Enable ACCESS_HAS 6760 allowed_access_type[v8::ACCESS_HAS] = true; 6761 ExpectUndefined("other.blocked_prop"); 6762 // ... and now we can get the descriptor... 6763 ExpectUndefined( 6764 "Object.getOwnPropertyDescriptor(other, 'blocked_prop').value"); 6765 // ... and enumerate the property. 6766 ExpectTrue("propertyIsEnumerable.call(other, 'blocked_prop')"); 6767 allowed_access_type[v8::ACCESS_HAS] = false; 6768 6769 // Access blocked element. 6770 CompileRun("other[239] = 1"); 6771 6772 ExpectUndefined("other[239]"); 6773 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '239')"); 6774 ExpectFalse("propertyIsEnumerable.call(other, '239')"); 6775 6776 // Enable ACCESS_HAS 6777 allowed_access_type[v8::ACCESS_HAS] = true; 6778 ExpectUndefined("other[239]"); 6779 // ... and now we can get the descriptor... 6780 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '239').value"); 6781 // ... and enumerate the property. 6782 ExpectTrue("propertyIsEnumerable.call(other, '239')"); 6783 allowed_access_type[v8::ACCESS_HAS] = false; 6784 6785 // Access a property with JS accessor. 6786 CompileRun("other.js_accessor_p = 2"); 6787 6788 ExpectUndefined("other.js_accessor_p"); 6789 ExpectUndefined( 6790 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p')"); 6791 6792 // Enable ACCESS_HAS. 6793 allowed_access_type[v8::ACCESS_HAS] = true; 6794 ExpectUndefined("other.js_accessor_p"); 6795 ExpectUndefined( 6796 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get"); 6797 ExpectUndefined( 6798 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set"); 6799 ExpectUndefined( 6800 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value"); 6801 allowed_access_type[v8::ACCESS_HAS] = false; 6802 6803 // Enable both ACCESS_HAS and ACCESS_GET. 6804 allowed_access_type[v8::ACCESS_HAS] = true; 6805 allowed_access_type[v8::ACCESS_GET] = true; 6806 6807 ExpectString("other.js_accessor_p", "getter"); 6808 ExpectObject( 6809 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter); 6810 ExpectUndefined( 6811 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set"); 6812 ExpectUndefined( 6813 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value"); 6814 6815 allowed_access_type[v8::ACCESS_GET] = false; 6816 allowed_access_type[v8::ACCESS_HAS] = false; 6817 6818 // Enable both ACCESS_HAS and ACCESS_SET. 6819 allowed_access_type[v8::ACCESS_HAS] = true; 6820 allowed_access_type[v8::ACCESS_SET] = true; 6821 6822 ExpectUndefined("other.js_accessor_p"); 6823 ExpectUndefined( 6824 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get"); 6825 ExpectObject( 6826 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter); 6827 ExpectUndefined( 6828 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value"); 6829 6830 allowed_access_type[v8::ACCESS_SET] = false; 6831 allowed_access_type[v8::ACCESS_HAS] = false; 6832 6833 // Enable both ACCESS_HAS, ACCESS_GET and ACCESS_SET. 6834 allowed_access_type[v8::ACCESS_HAS] = true; 6835 allowed_access_type[v8::ACCESS_GET] = true; 6836 allowed_access_type[v8::ACCESS_SET] = true; 6837 6838 ExpectString("other.js_accessor_p", "getter"); 6839 ExpectObject( 6840 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter); 6841 ExpectObject( 6842 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter); 6843 ExpectUndefined( 6844 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value"); 6845 6846 allowed_access_type[v8::ACCESS_SET] = false; 6847 allowed_access_type[v8::ACCESS_GET] = false; 6848 allowed_access_type[v8::ACCESS_HAS] = false; 6849 6850 // Access an element with JS accessor. 6851 CompileRun("other[42] = 2"); 6852 6853 ExpectUndefined("other[42]"); 6854 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42')"); 6855 6856 // Enable ACCESS_HAS. 6857 allowed_access_type[v8::ACCESS_HAS] = true; 6858 ExpectUndefined("other[42]"); 6859 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').get"); 6860 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').set"); 6861 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value"); 6862 allowed_access_type[v8::ACCESS_HAS] = false; 6863 6864 // Enable both ACCESS_HAS and ACCESS_GET. 6865 allowed_access_type[v8::ACCESS_HAS] = true; 6866 allowed_access_type[v8::ACCESS_GET] = true; 6867 6868 ExpectString("other[42]", "el_getter"); 6869 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter); 6870 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').set"); 6871 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value"); 6872 6873 allowed_access_type[v8::ACCESS_GET] = false; 6874 allowed_access_type[v8::ACCESS_HAS] = false; 6875 6876 // Enable both ACCESS_HAS and ACCESS_SET. 6877 allowed_access_type[v8::ACCESS_HAS] = true; 6878 allowed_access_type[v8::ACCESS_SET] = true; 6879 6880 ExpectUndefined("other[42]"); 6881 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').get"); 6882 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter); 6883 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value"); 6884 6885 allowed_access_type[v8::ACCESS_SET] = false; 6886 allowed_access_type[v8::ACCESS_HAS] = false; 6887 6888 // Enable both ACCESS_HAS, ACCESS_GET and ACCESS_SET. 6889 allowed_access_type[v8::ACCESS_HAS] = true; 6890 allowed_access_type[v8::ACCESS_GET] = true; 6891 allowed_access_type[v8::ACCESS_SET] = true; 6892 6893 ExpectString("other[42]", "el_getter"); 6894 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter); 6895 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter); 6896 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value"); 6897 6898 allowed_access_type[v8::ACCESS_SET] = false; 6899 allowed_access_type[v8::ACCESS_GET] = false; 6900 allowed_access_type[v8::ACCESS_HAS] = false; 6901 6902 v8::Handle<Value> value; 6903 6904 // Access accessible property 6905 value = CompileRun("other.accessible_prop = 3"); 6906 CHECK(value->IsNumber()); 6907 CHECK_EQ(3, value->Int32Value()); 6908 CHECK_EQ(3, g_echo_value); 6909 6910 value = CompileRun("other.accessible_prop"); 6911 CHECK(value->IsNumber()); 6912 CHECK_EQ(3, value->Int32Value()); 6913 6914 value = CompileRun( 6915 "Object.getOwnPropertyDescriptor(other, 'accessible_prop').value"); 6916 CHECK(value->IsNumber()); 6917 CHECK_EQ(3, value->Int32Value()); 6918 6919 value = CompileRun("propertyIsEnumerable.call(other, 'accessible_prop')"); 6920 CHECK(value->IsTrue()); 6921 6922 // Enumeration doesn't enumerate accessors from inaccessible objects in 6923 // the prototype chain even if the accessors are in themselves accessible. 6924 value = 6925 CompileRun("(function(){var obj = {'__proto__':other};" 6926 "for (var p in obj)" 6927 " if (p == 'accessible_prop' || p == 'blocked_prop') {" 6928 " return false;" 6929 " }" 6930 "return true;})()"); 6931 CHECK(value->IsTrue()); 6932 6933 context1->Exit(); 6934 context0->Exit(); 6935 context1.Dispose(); 6936 context0.Dispose(); 6937 } 6938 6939 6940 TEST(AccessControlES5) { 6941 v8::HandleScope handle_scope; 6942 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 6943 6944 global_template->SetAccessCheckCallbacks(NamedAccessBlocker, 6945 IndexedAccessBlocker); 6946 6947 // Add accessible accessor. 6948 global_template->SetAccessor( 6949 v8_str("accessible_prop"), 6950 EchoGetter, EchoSetter, 6951 v8::Handle<Value>(), 6952 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); 6953 6954 6955 // Add an accessor that is not accessible by cross-domain JS code. 6956 global_template->SetAccessor(v8_str("blocked_prop"), 6957 UnreachableGetter, UnreachableSetter, 6958 v8::Handle<Value>(), 6959 v8::DEFAULT); 6960 6961 // Create an environment 6962 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 6963 context0->Enter(); 6964 6965 v8::Handle<v8::Object> global0 = context0->Global(); 6966 6967 v8::Persistent<Context> context1 = Context::New(); 6968 context1->Enter(); 6969 v8::Handle<v8::Object> global1 = context1->Global(); 6970 global1->Set(v8_str("other"), global0); 6971 6972 // Regression test for issue 1154. 6973 ExpectTrue("Object.keys(other).indexOf('blocked_prop') == -1"); 6974 6975 ExpectUndefined("other.blocked_prop"); 6976 6977 // Regression test for issue 1027. 6978 CompileRun("Object.defineProperty(\n" 6979 " other, 'blocked_prop', {configurable: false})"); 6980 ExpectUndefined("other.blocked_prop"); 6981 ExpectUndefined( 6982 "Object.getOwnPropertyDescriptor(other, 'blocked_prop')"); 6983 6984 // Regression test for issue 1171. 6985 ExpectTrue("Object.isExtensible(other)"); 6986 CompileRun("Object.preventExtensions(other)"); 6987 ExpectTrue("Object.isExtensible(other)"); 6988 6989 // Object.seal and Object.freeze. 6990 CompileRun("Object.freeze(other)"); 6991 ExpectTrue("Object.isExtensible(other)"); 6992 6993 CompileRun("Object.seal(other)"); 6994 ExpectTrue("Object.isExtensible(other)"); 6995 6996 // Regression test for issue 1250. 6997 // Make sure that we can set the accessible accessors value using normal 6998 // assignment. 6999 CompileRun("other.accessible_prop = 42"); 7000 CHECK_EQ(42, g_echo_value); 7001 7002 v8::Handle<Value> value; 7003 // We follow Safari in ignoring assignments to host object accessors. 7004 CompileRun("Object.defineProperty(other, 'accessible_prop', {value: -1})"); 7005 value = CompileRun("other.accessible_prop == 42"); 7006 CHECK(value->IsTrue()); 7007 } 7008 7009 7010 static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global, 7011 Local<Value> name, 7012 v8::AccessType type, 7013 Local<Value> data) { 7014 return false; 7015 } 7016 7017 7018 static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global, 7019 uint32_t key, 7020 v8::AccessType type, 7021 Local<Value> data) { 7022 return false; 7023 } 7024 7025 7026 THREADED_TEST(AccessControlGetOwnPropertyNames) { 7027 v8::HandleScope handle_scope; 7028 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(); 7029 7030 obj_template->Set(v8_str("x"), v8::Integer::New(42)); 7031 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker, 7032 GetOwnPropertyNamesIndexedBlocker); 7033 7034 // Create an environment 7035 v8::Persistent<Context> context0 = Context::New(NULL, obj_template); 7036 context0->Enter(); 7037 7038 v8::Handle<v8::Object> global0 = context0->Global(); 7039 7040 v8::HandleScope scope1; 7041 7042 v8::Persistent<Context> context1 = Context::New(); 7043 context1->Enter(); 7044 7045 v8::Handle<v8::Object> global1 = context1->Global(); 7046 global1->Set(v8_str("other"), global0); 7047 global1->Set(v8_str("object"), obj_template->NewInstance()); 7048 7049 v8::Handle<Value> value; 7050 7051 // Attempt to get the property names of the other global object and 7052 // of an object that requires access checks. Accessing the other 7053 // global object should be blocked by access checks on the global 7054 // proxy object. Accessing the object that requires access checks 7055 // is blocked by the access checks on the object itself. 7056 value = CompileRun("Object.getOwnPropertyNames(other).length == 0"); 7057 CHECK(value->IsTrue()); 7058 7059 value = CompileRun("Object.getOwnPropertyNames(object).length == 0"); 7060 CHECK(value->IsTrue()); 7061 7062 context1->Exit(); 7063 context0->Exit(); 7064 context1.Dispose(); 7065 context0.Dispose(); 7066 } 7067 7068 7069 static v8::Handle<v8::Array> NamedPropertyEnumerator(const AccessorInfo& info) { 7070 v8::Handle<v8::Array> result = v8::Array::New(1); 7071 result->Set(0, v8_str("x")); 7072 return result; 7073 } 7074 7075 7076 THREADED_TEST(GetOwnPropertyNamesWithInterceptor) { 7077 v8::HandleScope handle_scope; 7078 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(); 7079 7080 obj_template->Set(v8_str("x"), v8::Integer::New(42)); 7081 obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL, 7082 NamedPropertyEnumerator); 7083 7084 LocalContext context; 7085 v8::Handle<v8::Object> global = context->Global(); 7086 global->Set(v8_str("object"), obj_template->NewInstance()); 7087 7088 v8::Handle<Value> value = 7089 CompileRun("Object.getOwnPropertyNames(object).join(',')"); 7090 CHECK_EQ(v8_str("x"), value); 7091 } 7092 7093 7094 static v8::Handle<Value> ConstTenGetter(Local<String> name, 7095 const AccessorInfo& info) { 7096 return v8_num(10); 7097 } 7098 7099 7100 THREADED_TEST(CrossDomainAccessors) { 7101 v8::HandleScope handle_scope; 7102 7103 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(); 7104 7105 v8::Handle<v8::ObjectTemplate> global_template = 7106 func_template->InstanceTemplate(); 7107 7108 v8::Handle<v8::ObjectTemplate> proto_template = 7109 func_template->PrototypeTemplate(); 7110 7111 // Add an accessor to proto that's accessible by cross-domain JS code. 7112 proto_template->SetAccessor(v8_str("accessible"), 7113 ConstTenGetter, 0, 7114 v8::Handle<Value>(), 7115 v8::ALL_CAN_READ); 7116 7117 // Add an accessor that is not accessible by cross-domain JS code. 7118 global_template->SetAccessor(v8_str("unreachable"), 7119 UnreachableGetter, 0, 7120 v8::Handle<Value>(), 7121 v8::DEFAULT); 7122 7123 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 7124 context0->Enter(); 7125 7126 Local<v8::Object> global = context0->Global(); 7127 // Add a normal property that shadows 'accessible' 7128 global->Set(v8_str("accessible"), v8_num(11)); 7129 7130 // Enter a new context. 7131 v8::HandleScope scope1; 7132 v8::Persistent<Context> context1 = Context::New(); 7133 context1->Enter(); 7134 7135 v8::Handle<v8::Object> global1 = context1->Global(); 7136 global1->Set(v8_str("other"), global); 7137 7138 // Should return 10, instead of 11 7139 v8::Handle<Value> value = v8_compile("other.accessible")->Run(); 7140 CHECK(value->IsNumber()); 7141 CHECK_EQ(10, value->Int32Value()); 7142 7143 value = v8_compile("other.unreachable")->Run(); 7144 CHECK(value->IsUndefined()); 7145 7146 context1->Exit(); 7147 context0->Exit(); 7148 context1.Dispose(); 7149 context0.Dispose(); 7150 } 7151 7152 7153 static int named_access_count = 0; 7154 static int indexed_access_count = 0; 7155 7156 static bool NamedAccessCounter(Local<v8::Object> global, 7157 Local<Value> name, 7158 v8::AccessType type, 7159 Local<Value> data) { 7160 named_access_count++; 7161 return true; 7162 } 7163 7164 7165 static bool IndexedAccessCounter(Local<v8::Object> global, 7166 uint32_t key, 7167 v8::AccessType type, 7168 Local<Value> data) { 7169 indexed_access_count++; 7170 return true; 7171 } 7172 7173 7174 // This one is too easily disturbed by other tests. 7175 TEST(AccessControlIC) { 7176 named_access_count = 0; 7177 indexed_access_count = 0; 7178 7179 v8::HandleScope handle_scope; 7180 7181 // Create an environment. 7182 v8::Persistent<Context> context0 = Context::New(); 7183 context0->Enter(); 7184 7185 // Create an object that requires access-check functions to be 7186 // called for cross-domain access. 7187 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 7188 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 7189 IndexedAccessCounter); 7190 Local<v8::Object> object = object_template->NewInstance(); 7191 7192 v8::HandleScope scope1; 7193 7194 // Create another environment. 7195 v8::Persistent<Context> context1 = Context::New(); 7196 context1->Enter(); 7197 7198 // Make easy access to the object from the other environment. 7199 v8::Handle<v8::Object> global1 = context1->Global(); 7200 global1->Set(v8_str("obj"), object); 7201 7202 v8::Handle<Value> value; 7203 7204 // Check that the named access-control function is called every time. 7205 CompileRun("function testProp(obj) {" 7206 " for (var i = 0; i < 10; i++) obj.prop = 1;" 7207 " for (var j = 0; j < 10; j++) obj.prop;" 7208 " return obj.prop" 7209 "}"); 7210 value = CompileRun("testProp(obj)"); 7211 CHECK(value->IsNumber()); 7212 CHECK_EQ(1, value->Int32Value()); 7213 CHECK_EQ(21, named_access_count); 7214 7215 // Check that the named access-control function is called every time. 7216 CompileRun("var p = 'prop';" 7217 "function testKeyed(obj) {" 7218 " for (var i = 0; i < 10; i++) obj[p] = 1;" 7219 " for (var j = 0; j < 10; j++) obj[p];" 7220 " return obj[p];" 7221 "}"); 7222 // Use obj which requires access checks. No inline caching is used 7223 // in that case. 7224 value = CompileRun("testKeyed(obj)"); 7225 CHECK(value->IsNumber()); 7226 CHECK_EQ(1, value->Int32Value()); 7227 CHECK_EQ(42, named_access_count); 7228 // Force the inline caches into generic state and try again. 7229 CompileRun("testKeyed({ a: 0 })"); 7230 CompileRun("testKeyed({ b: 0 })"); 7231 value = CompileRun("testKeyed(obj)"); 7232 CHECK(value->IsNumber()); 7233 CHECK_EQ(1, value->Int32Value()); 7234 CHECK_EQ(63, named_access_count); 7235 7236 // Check that the indexed access-control function is called every time. 7237 CompileRun("function testIndexed(obj) {" 7238 " for (var i = 0; i < 10; i++) obj[0] = 1;" 7239 " for (var j = 0; j < 10; j++) obj[0];" 7240 " return obj[0]" 7241 "}"); 7242 value = CompileRun("testIndexed(obj)"); 7243 CHECK(value->IsNumber()); 7244 CHECK_EQ(1, value->Int32Value()); 7245 CHECK_EQ(21, indexed_access_count); 7246 // Force the inline caches into generic state. 7247 CompileRun("testIndexed(new Array(1))"); 7248 // Test that the indexed access check is called. 7249 value = CompileRun("testIndexed(obj)"); 7250 CHECK(value->IsNumber()); 7251 CHECK_EQ(1, value->Int32Value()); 7252 CHECK_EQ(42, indexed_access_count); 7253 7254 // Check that the named access check is called when invoking 7255 // functions on an object that requires access checks. 7256 CompileRun("obj.f = function() {}"); 7257 CompileRun("function testCallNormal(obj) {" 7258 " for (var i = 0; i < 10; i++) obj.f();" 7259 "}"); 7260 CompileRun("testCallNormal(obj)"); 7261 CHECK_EQ(74, named_access_count); 7262 7263 // Force obj into slow case. 7264 value = CompileRun("delete obj.prop"); 7265 CHECK(value->BooleanValue()); 7266 // Force inline caches into dictionary probing mode. 7267 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);"); 7268 // Test that the named access check is called. 7269 value = CompileRun("testProp(obj);"); 7270 CHECK(value->IsNumber()); 7271 CHECK_EQ(1, value->Int32Value()); 7272 CHECK_EQ(96, named_access_count); 7273 7274 // Force the call inline cache into dictionary probing mode. 7275 CompileRun("o.f = function() {}; testCallNormal(o)"); 7276 // Test that the named access check is still called for each 7277 // invocation of the function. 7278 value = CompileRun("testCallNormal(obj)"); 7279 CHECK_EQ(106, named_access_count); 7280 7281 context1->Exit(); 7282 context0->Exit(); 7283 context1.Dispose(); 7284 context0.Dispose(); 7285 } 7286 7287 7288 static bool NamedAccessFlatten(Local<v8::Object> global, 7289 Local<Value> name, 7290 v8::AccessType type, 7291 Local<Value> data) { 7292 char buf[100]; 7293 int len; 7294 7295 CHECK(name->IsString()); 7296 7297 memset(buf, 0x1, sizeof(buf)); 7298 len = name.As<String>()->WriteAscii(buf); 7299 CHECK_EQ(4, len); 7300 7301 uint16_t buf2[100]; 7302 7303 memset(buf, 0x1, sizeof(buf)); 7304 len = name.As<String>()->Write(buf2); 7305 CHECK_EQ(4, len); 7306 7307 return true; 7308 } 7309 7310 7311 static bool IndexedAccessFlatten(Local<v8::Object> global, 7312 uint32_t key, 7313 v8::AccessType type, 7314 Local<Value> data) { 7315 return true; 7316 } 7317 7318 7319 // Regression test. In access checks, operations that may cause 7320 // garbage collection are not allowed. It used to be the case that 7321 // using the Write operation on a string could cause a garbage 7322 // collection due to flattening of the string. This is no longer the 7323 // case. 7324 THREADED_TEST(AccessControlFlatten) { 7325 named_access_count = 0; 7326 indexed_access_count = 0; 7327 7328 v8::HandleScope handle_scope; 7329 7330 // Create an environment. 7331 v8::Persistent<Context> context0 = Context::New(); 7332 context0->Enter(); 7333 7334 // Create an object that requires access-check functions to be 7335 // called for cross-domain access. 7336 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 7337 object_template->SetAccessCheckCallbacks(NamedAccessFlatten, 7338 IndexedAccessFlatten); 7339 Local<v8::Object> object = object_template->NewInstance(); 7340 7341 v8::HandleScope scope1; 7342 7343 // Create another environment. 7344 v8::Persistent<Context> context1 = Context::New(); 7345 context1->Enter(); 7346 7347 // Make easy access to the object from the other environment. 7348 v8::Handle<v8::Object> global1 = context1->Global(); 7349 global1->Set(v8_str("obj"), object); 7350 7351 v8::Handle<Value> value; 7352 7353 value = v8_compile("var p = 'as' + 'df';")->Run(); 7354 value = v8_compile("obj[p];")->Run(); 7355 7356 context1->Exit(); 7357 context0->Exit(); 7358 context1.Dispose(); 7359 context0.Dispose(); 7360 } 7361 7362 7363 static v8::Handle<Value> AccessControlNamedGetter( 7364 Local<String>, const AccessorInfo&) { 7365 return v8::Integer::New(42); 7366 } 7367 7368 7369 static v8::Handle<Value> AccessControlNamedSetter( 7370 Local<String>, Local<Value> value, const AccessorInfo&) { 7371 return value; 7372 } 7373 7374 7375 static v8::Handle<Value> AccessControlIndexedGetter( 7376 uint32_t index, 7377 const AccessorInfo& info) { 7378 return v8_num(42); 7379 } 7380 7381 7382 static v8::Handle<Value> AccessControlIndexedSetter( 7383 uint32_t, Local<Value> value, const AccessorInfo&) { 7384 return value; 7385 } 7386 7387 7388 THREADED_TEST(AccessControlInterceptorIC) { 7389 named_access_count = 0; 7390 indexed_access_count = 0; 7391 7392 v8::HandleScope handle_scope; 7393 7394 // Create an environment. 7395 v8::Persistent<Context> context0 = Context::New(); 7396 context0->Enter(); 7397 7398 // Create an object that requires access-check functions to be 7399 // called for cross-domain access. The object also has interceptors 7400 // interceptor. 7401 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 7402 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 7403 IndexedAccessCounter); 7404 object_template->SetNamedPropertyHandler(AccessControlNamedGetter, 7405 AccessControlNamedSetter); 7406 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter, 7407 AccessControlIndexedSetter); 7408 Local<v8::Object> object = object_template->NewInstance(); 7409 7410 v8::HandleScope scope1; 7411 7412 // Create another environment. 7413 v8::Persistent<Context> context1 = Context::New(); 7414 context1->Enter(); 7415 7416 // Make easy access to the object from the other environment. 7417 v8::Handle<v8::Object> global1 = context1->Global(); 7418 global1->Set(v8_str("obj"), object); 7419 7420 v8::Handle<Value> value; 7421 7422 // Check that the named access-control function is called every time 7423 // eventhough there is an interceptor on the object. 7424 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run(); 7425 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;" 7426 "obj.x")->Run(); 7427 CHECK(value->IsNumber()); 7428 CHECK_EQ(42, value->Int32Value()); 7429 CHECK_EQ(21, named_access_count); 7430 7431 value = v8_compile("var p = 'x';")->Run(); 7432 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run(); 7433 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];" 7434 "obj[p]")->Run(); 7435 CHECK(value->IsNumber()); 7436 CHECK_EQ(42, value->Int32Value()); 7437 CHECK_EQ(42, named_access_count); 7438 7439 // Check that the indexed access-control function is called every 7440 // time eventhough there is an interceptor on the object. 7441 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run(); 7442 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];" 7443 "obj[0]")->Run(); 7444 CHECK(value->IsNumber()); 7445 CHECK_EQ(42, value->Int32Value()); 7446 CHECK_EQ(21, indexed_access_count); 7447 7448 context1->Exit(); 7449 context0->Exit(); 7450 context1.Dispose(); 7451 context0.Dispose(); 7452 } 7453 7454 7455 THREADED_TEST(Version) { 7456 v8::V8::GetVersion(); 7457 } 7458 7459 7460 static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) { 7461 ApiTestFuzzer::Fuzz(); 7462 return v8_num(12); 7463 } 7464 7465 7466 THREADED_TEST(InstanceProperties) { 7467 v8::HandleScope handle_scope; 7468 LocalContext context; 7469 7470 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 7471 Local<ObjectTemplate> instance = t->InstanceTemplate(); 7472 7473 instance->Set(v8_str("x"), v8_num(42)); 7474 instance->Set(v8_str("f"), 7475 v8::FunctionTemplate::New(InstanceFunctionCallback)); 7476 7477 Local<Value> o = t->GetFunction()->NewInstance(); 7478 7479 context->Global()->Set(v8_str("i"), o); 7480 Local<Value> value = Script::Compile(v8_str("i.x"))->Run(); 7481 CHECK_EQ(42, value->Int32Value()); 7482 7483 value = Script::Compile(v8_str("i.f()"))->Run(); 7484 CHECK_EQ(12, value->Int32Value()); 7485 } 7486 7487 7488 static v8::Handle<Value> 7489 GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) { 7490 ApiTestFuzzer::Fuzz(); 7491 return v8::Handle<Value>(); 7492 } 7493 7494 7495 THREADED_TEST(GlobalObjectInstanceProperties) { 7496 v8::HandleScope handle_scope; 7497 7498 Local<Value> global_object; 7499 7500 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 7501 t->InstanceTemplate()->SetNamedPropertyHandler( 7502 GlobalObjectInstancePropertiesGet); 7503 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 7504 instance_template->Set(v8_str("x"), v8_num(42)); 7505 instance_template->Set(v8_str("f"), 7506 v8::FunctionTemplate::New(InstanceFunctionCallback)); 7507 7508 // The script to check how Crankshaft compiles missing global function 7509 // invocations. function g is not defined and should throw on call. 7510 const char* script = 7511 "function wrapper(call) {" 7512 " var x = 0, y = 1;" 7513 " for (var i = 0; i < 1000; i++) {" 7514 " x += i * 100;" 7515 " y += i * 100;" 7516 " }" 7517 " if (call) g();" 7518 "}" 7519 "for (var i = 0; i < 17; i++) wrapper(false);" 7520 "var thrown = 0;" 7521 "try { wrapper(true); } catch (e) { thrown = 1; };" 7522 "thrown"; 7523 7524 { 7525 LocalContext env(NULL, instance_template); 7526 // Hold on to the global object so it can be used again in another 7527 // environment initialization. 7528 global_object = env->Global(); 7529 7530 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 7531 CHECK_EQ(42, value->Int32Value()); 7532 value = Script::Compile(v8_str("f()"))->Run(); 7533 CHECK_EQ(12, value->Int32Value()); 7534 value = Script::Compile(v8_str(script))->Run(); 7535 CHECK_EQ(1, value->Int32Value()); 7536 } 7537 7538 { 7539 // Create new environment reusing the global object. 7540 LocalContext env(NULL, instance_template, global_object); 7541 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 7542 CHECK_EQ(42, value->Int32Value()); 7543 value = Script::Compile(v8_str("f()"))->Run(); 7544 CHECK_EQ(12, value->Int32Value()); 7545 value = Script::Compile(v8_str(script))->Run(); 7546 CHECK_EQ(1, value->Int32Value()); 7547 } 7548 } 7549 7550 7551 THREADED_TEST(CallKnownGlobalReceiver) { 7552 v8::HandleScope handle_scope; 7553 7554 Local<Value> global_object; 7555 7556 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 7557 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 7558 7559 // The script to check that we leave global object not 7560 // global object proxy on stack when we deoptimize from inside 7561 // arguments evaluation. 7562 // To provoke error we need to both force deoptimization 7563 // from arguments evaluation and to force CallIC to take 7564 // CallIC_Miss code path that can't cope with global proxy. 7565 const char* script = 7566 "function bar(x, y) { try { } finally { } }" 7567 "function baz(x) { try { } finally { } }" 7568 "function bom(x) { try { } finally { } }" 7569 "function foo(x) { bar([x], bom(2)); }" 7570 "for (var i = 0; i < 10000; i++) foo(1);" 7571 "foo"; 7572 7573 Local<Value> foo; 7574 { 7575 LocalContext env(NULL, instance_template); 7576 // Hold on to the global object so it can be used again in another 7577 // environment initialization. 7578 global_object = env->Global(); 7579 foo = Script::Compile(v8_str(script))->Run(); 7580 } 7581 7582 { 7583 // Create new environment reusing the global object. 7584 LocalContext env(NULL, instance_template, global_object); 7585 env->Global()->Set(v8_str("foo"), foo); 7586 Script::Compile(v8_str("foo()"))->Run(); 7587 } 7588 } 7589 7590 7591 static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) { 7592 ApiTestFuzzer::Fuzz(); 7593 return v8_num(42); 7594 } 7595 7596 7597 static int shadow_y; 7598 static int shadow_y_setter_call_count; 7599 static int shadow_y_getter_call_count; 7600 7601 7602 static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) { 7603 shadow_y_setter_call_count++; 7604 shadow_y = 42; 7605 } 7606 7607 7608 static v8::Handle<Value> ShadowYGetter(Local<String> name, 7609 const AccessorInfo& info) { 7610 ApiTestFuzzer::Fuzz(); 7611 shadow_y_getter_call_count++; 7612 return v8_num(shadow_y); 7613 } 7614 7615 7616 static v8::Handle<Value> ShadowIndexedGet(uint32_t index, 7617 const AccessorInfo& info) { 7618 return v8::Handle<Value>(); 7619 } 7620 7621 7622 static v8::Handle<Value> ShadowNamedGet(Local<String> key, 7623 const AccessorInfo&) { 7624 return v8::Handle<Value>(); 7625 } 7626 7627 7628 THREADED_TEST(ShadowObject) { 7629 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0; 7630 v8::HandleScope handle_scope; 7631 7632 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New(); 7633 LocalContext context(NULL, global_template); 7634 7635 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 7636 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet); 7637 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet); 7638 Local<ObjectTemplate> proto = t->PrototypeTemplate(); 7639 Local<ObjectTemplate> instance = t->InstanceTemplate(); 7640 7641 // Only allow calls of f on instances of t. 7642 Local<v8::Signature> signature = v8::Signature::New(t); 7643 proto->Set(v8_str("f"), 7644 v8::FunctionTemplate::New(ShadowFunctionCallback, 7645 Local<Value>(), 7646 signature)); 7647 proto->Set(v8_str("x"), v8_num(12)); 7648 7649 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter); 7650 7651 Local<Value> o = t->GetFunction()->NewInstance(); 7652 context->Global()->Set(v8_str("__proto__"), o); 7653 7654 Local<Value> value = 7655 Script::Compile(v8_str("this.propertyIsEnumerable(0)"))->Run(); 7656 CHECK(value->IsBoolean()); 7657 CHECK(!value->BooleanValue()); 7658 7659 value = Script::Compile(v8_str("x"))->Run(); 7660 CHECK_EQ(12, value->Int32Value()); 7661 7662 value = Script::Compile(v8_str("f()"))->Run(); 7663 CHECK_EQ(42, value->Int32Value()); 7664 7665 Script::Compile(v8_str("y = 42"))->Run(); 7666 CHECK_EQ(1, shadow_y_setter_call_count); 7667 value = Script::Compile(v8_str("y"))->Run(); 7668 CHECK_EQ(1, shadow_y_getter_call_count); 7669 CHECK_EQ(42, value->Int32Value()); 7670 } 7671 7672 7673 THREADED_TEST(HiddenPrototype) { 7674 v8::HandleScope handle_scope; 7675 LocalContext context; 7676 7677 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 7678 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 7679 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 7680 t1->SetHiddenPrototype(true); 7681 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 7682 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 7683 t2->SetHiddenPrototype(true); 7684 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 7685 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 7686 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 7687 7688 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 7689 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 7690 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 7691 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 7692 7693 // Setting the prototype on an object skips hidden prototypes. 7694 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 7695 o0->Set(v8_str("__proto__"), o1); 7696 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 7697 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 7698 o0->Set(v8_str("__proto__"), o2); 7699 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 7700 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 7701 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 7702 o0->Set(v8_str("__proto__"), o3); 7703 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 7704 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 7705 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 7706 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 7707 7708 // Getting the prototype of o0 should get the first visible one 7709 // which is o3. Therefore, z should not be defined on the prototype 7710 // object. 7711 Local<Value> proto = o0->Get(v8_str("__proto__")); 7712 CHECK(proto->IsObject()); 7713 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined()); 7714 } 7715 7716 7717 THREADED_TEST(SetPrototype) { 7718 v8::HandleScope handle_scope; 7719 LocalContext context; 7720 7721 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 7722 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 7723 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 7724 t1->SetHiddenPrototype(true); 7725 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 7726 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 7727 t2->SetHiddenPrototype(true); 7728 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 7729 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 7730 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 7731 7732 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 7733 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 7734 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 7735 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 7736 7737 // Setting the prototype on an object does not skip hidden prototypes. 7738 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 7739 CHECK(o0->SetPrototype(o1)); 7740 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 7741 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 7742 CHECK(o1->SetPrototype(o2)); 7743 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 7744 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 7745 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 7746 CHECK(o2->SetPrototype(o3)); 7747 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 7748 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 7749 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 7750 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 7751 7752 // Getting the prototype of o0 should get the first visible one 7753 // which is o3. Therefore, z should not be defined on the prototype 7754 // object. 7755 Local<Value> proto = o0->Get(v8_str("__proto__")); 7756 CHECK(proto->IsObject()); 7757 CHECK_EQ(proto.As<v8::Object>(), o3); 7758 7759 // However, Object::GetPrototype ignores hidden prototype. 7760 Local<Value> proto0 = o0->GetPrototype(); 7761 CHECK(proto0->IsObject()); 7762 CHECK_EQ(proto0.As<v8::Object>(), o1); 7763 7764 Local<Value> proto1 = o1->GetPrototype(); 7765 CHECK(proto1->IsObject()); 7766 CHECK_EQ(proto1.As<v8::Object>(), o2); 7767 7768 Local<Value> proto2 = o2->GetPrototype(); 7769 CHECK(proto2->IsObject()); 7770 CHECK_EQ(proto2.As<v8::Object>(), o3); 7771 } 7772 7773 7774 // Getting property names of an object with a prototype chain that 7775 // triggers dictionary elements in GetLocalPropertyNames() shouldn't 7776 // crash the runtime. 7777 THREADED_TEST(Regress91517) { 7778 i::FLAG_allow_natives_syntax = true; 7779 v8::HandleScope handle_scope; 7780 LocalContext context; 7781 7782 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 7783 t1->SetHiddenPrototype(true); 7784 t1->InstanceTemplate()->Set(v8_str("foo"), v8_num(1)); 7785 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 7786 t2->SetHiddenPrototype(true); 7787 t2->InstanceTemplate()->Set(v8_str("fuz1"), v8_num(2)); 7788 t2->InstanceTemplate()->Set(v8_str("objects"), v8::Object::New()); 7789 t2->InstanceTemplate()->Set(v8_str("fuz2"), v8_num(2)); 7790 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 7791 t3->SetHiddenPrototype(true); 7792 t3->InstanceTemplate()->Set(v8_str("boo"), v8_num(3)); 7793 Local<v8::FunctionTemplate> t4 = v8::FunctionTemplate::New(); 7794 t4->InstanceTemplate()->Set(v8_str("baz"), v8_num(4)); 7795 7796 // Force dictionary-based properties. 7797 i::ScopedVector<char> name_buf(1024); 7798 for (int i = 1; i <= 1000; i++) { 7799 i::OS::SNPrintF(name_buf, "sdf%d", i); 7800 t2->InstanceTemplate()->Set(v8_str(name_buf.start()), v8_num(2)); 7801 } 7802 7803 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 7804 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 7805 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 7806 Local<v8::Object> o4 = t4->GetFunction()->NewInstance(); 7807 7808 // Create prototype chain of hidden prototypes. 7809 CHECK(o4->SetPrototype(o3)); 7810 CHECK(o3->SetPrototype(o2)); 7811 CHECK(o2->SetPrototype(o1)); 7812 7813 // Call the runtime version of GetLocalPropertyNames() on the natively 7814 // created object through JavaScript. 7815 context->Global()->Set(v8_str("obj"), o4); 7816 CompileRun("var names = %GetLocalPropertyNames(obj);"); 7817 7818 ExpectInt32("names.length", 1006); 7819 ExpectTrue("names.indexOf(\"baz\") >= 0"); 7820 ExpectTrue("names.indexOf(\"boo\") >= 0"); 7821 ExpectTrue("names.indexOf(\"foo\") >= 0"); 7822 ExpectTrue("names.indexOf(\"fuz1\") >= 0"); 7823 ExpectTrue("names.indexOf(\"fuz2\") >= 0"); 7824 ExpectFalse("names[1005] == undefined"); 7825 } 7826 7827 7828 THREADED_TEST(FunctionReadOnlyPrototype) { 7829 v8::HandleScope handle_scope; 7830 LocalContext context; 7831 7832 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 7833 t1->PrototypeTemplate()->Set(v8_str("x"), v8::Integer::New(42)); 7834 t1->ReadOnlyPrototype(); 7835 context->Global()->Set(v8_str("func1"), t1->GetFunction()); 7836 // Configured value of ReadOnly flag. 7837 CHECK(CompileRun( 7838 "(function() {" 7839 " descriptor = Object.getOwnPropertyDescriptor(func1, 'prototype');" 7840 " return (descriptor['writable'] == false);" 7841 "})()")->BooleanValue()); 7842 CHECK_EQ(42, CompileRun("func1.prototype.x")->Int32Value()); 7843 CHECK_EQ(42, 7844 CompileRun("func1.prototype = {}; func1.prototype.x")->Int32Value()); 7845 7846 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 7847 t2->PrototypeTemplate()->Set(v8_str("x"), v8::Integer::New(42)); 7848 context->Global()->Set(v8_str("func2"), t2->GetFunction()); 7849 // Default value of ReadOnly flag. 7850 CHECK(CompileRun( 7851 "(function() {" 7852 " descriptor = Object.getOwnPropertyDescriptor(func2, 'prototype');" 7853 " return (descriptor['writable'] == true);" 7854 "})()")->BooleanValue()); 7855 CHECK_EQ(42, CompileRun("func2.prototype.x")->Int32Value()); 7856 } 7857 7858 7859 THREADED_TEST(SetPrototypeThrows) { 7860 v8::HandleScope handle_scope; 7861 LocalContext context; 7862 7863 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 7864 7865 Local<v8::Object> o0 = t->GetFunction()->NewInstance(); 7866 Local<v8::Object> o1 = t->GetFunction()->NewInstance(); 7867 7868 CHECK(o0->SetPrototype(o1)); 7869 // If setting the prototype leads to the cycle, SetPrototype should 7870 // return false and keep VM in sane state. 7871 v8::TryCatch try_catch; 7872 CHECK(!o1->SetPrototype(o0)); 7873 CHECK(!try_catch.HasCaught()); 7874 ASSERT(!i::Isolate::Current()->