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()->has_pending_exception()); 7875 7876 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value()); 7877 } 7878 7879 7880 THREADED_TEST(GetterSetterExceptions) { 7881 v8::HandleScope handle_scope; 7882 LocalContext context; 7883 CompileRun( 7884 "function Foo() { };" 7885 "function Throw() { throw 5; };" 7886 "var x = { };" 7887 "x.__defineSetter__('set', Throw);" 7888 "x.__defineGetter__('get', Throw);"); 7889 Local<v8::Object> x = 7890 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x"))); 7891 v8::TryCatch try_catch; 7892 x->Set(v8_str("set"), v8::Integer::New(8)); 7893 x->Get(v8_str("get")); 7894 x->Set(v8_str("set"), v8::Integer::New(8)); 7895 x->Get(v8_str("get")); 7896 x->Set(v8_str("set"), v8::Integer::New(8)); 7897 x->Get(v8_str("get")); 7898 x->Set(v8_str("set"), v8::Integer::New(8)); 7899 x->Get(v8_str("get")); 7900 } 7901 7902 7903 THREADED_TEST(Constructor) { 7904 v8::HandleScope handle_scope; 7905 LocalContext context; 7906 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 7907 templ->SetClassName(v8_str("Fun")); 7908 Local<Function> cons = templ->GetFunction(); 7909 context->Global()->Set(v8_str("Fun"), cons); 7910 Local<v8::Object> inst = cons->NewInstance(); 7911 i::Handle<i::JSObject> obj(v8::Utils::OpenHandle(*inst)); 7912 CHECK(obj->IsJSObject()); 7913 Local<Value> value = CompileRun("(new Fun()).constructor === Fun"); 7914 CHECK(value->BooleanValue()); 7915 } 7916 7917 7918 static Handle<Value> ConstructorCallback(const Arguments& args) { 7919 ApiTestFuzzer::Fuzz(); 7920 Local<Object> This; 7921 7922 if (args.IsConstructCall()) { 7923 Local<Object> Holder = args.Holder(); 7924 This = Object::New(); 7925 Local<Value> proto = Holder->GetPrototype(); 7926 if (proto->IsObject()) { 7927 This->SetPrototype(proto); 7928 } 7929 } else { 7930 This = args.This(); 7931 } 7932 7933 This->Set(v8_str("a"), args[0]); 7934 return This; 7935 } 7936 7937 7938 static Handle<Value> FakeConstructorCallback(const Arguments& args) { 7939 ApiTestFuzzer::Fuzz(); 7940 return args[0]; 7941 } 7942 7943 7944 THREADED_TEST(ConstructorForObject) { 7945 v8::HandleScope handle_scope; 7946 LocalContext context; 7947 7948 { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); 7949 instance_template->SetCallAsFunctionHandler(ConstructorCallback); 7950 Local<Object> instance = instance_template->NewInstance(); 7951 context->Global()->Set(v8_str("obj"), instance); 7952 v8::TryCatch try_catch; 7953 Local<Value> value; 7954 CHECK(!try_catch.HasCaught()); 7955 7956 // Call the Object's constructor with a 32-bit signed integer. 7957 value = CompileRun("(function() { var o = new obj(28); return o.a; })()"); 7958 CHECK(!try_catch.HasCaught()); 7959 CHECK(value->IsInt32()); 7960 CHECK_EQ(28, value->Int32Value()); 7961 7962 Local<Value> args1[] = { v8_num(28) }; 7963 Local<Value> value_obj1 = instance->CallAsConstructor(1, args1); 7964 CHECK(value_obj1->IsObject()); 7965 Local<Object> object1 = Local<Object>::Cast(value_obj1); 7966 value = object1->Get(v8_str("a")); 7967 CHECK(value->IsInt32()); 7968 CHECK(!try_catch.HasCaught()); 7969 CHECK_EQ(28, value->Int32Value()); 7970 7971 // Call the Object's constructor with a String. 7972 value = CompileRun( 7973 "(function() { var o = new obj('tipli'); return o.a; })()"); 7974 CHECK(!try_catch.HasCaught()); 7975 CHECK(value->IsString()); 7976 String::AsciiValue string_value1(value->ToString()); 7977 CHECK_EQ("tipli", *string_value1); 7978 7979 Local<Value> args2[] = { v8_str("tipli") }; 7980 Local<Value> value_obj2 = instance->CallAsConstructor(1, args2); 7981 CHECK(value_obj2->IsObject()); 7982 Local<Object> object2 = Local<Object>::Cast(value_obj2); 7983 value = object2->Get(v8_str("a")); 7984 CHECK(!try_catch.HasCaught()); 7985 CHECK(value->IsString()); 7986 String::AsciiValue string_value2(value->ToString()); 7987 CHECK_EQ("tipli", *string_value2); 7988 7989 // Call the Object's constructor with a Boolean. 7990 value = CompileRun("(function() { var o = new obj(true); return o.a; })()"); 7991 CHECK(!try_catch.HasCaught()); 7992 CHECK(value->IsBoolean()); 7993 CHECK_EQ(true, value->BooleanValue()); 7994 7995 Handle<Value> args3[] = { v8::True() }; 7996 Local<Value> value_obj3 = instance->CallAsConstructor(1, args3); 7997 CHECK(value_obj3->IsObject()); 7998 Local<Object> object3 = Local<Object>::Cast(value_obj3); 7999 value = object3->Get(v8_str("a")); 8000 CHECK(!try_catch.HasCaught()); 8001 CHECK(value->IsBoolean()); 8002 CHECK_EQ(true, value->BooleanValue()); 8003 8004 // Call the Object's constructor with undefined. 8005 Handle<Value> args4[] = { v8::Undefined() }; 8006 Local<Value> value_obj4 = instance->CallAsConstructor(1, args4); 8007 CHECK(value_obj4->IsObject()); 8008 Local<Object> object4 = Local<Object>::Cast(value_obj4); 8009 value = object4->Get(v8_str("a")); 8010 CHECK(!try_catch.HasCaught()); 8011 CHECK(value->IsUndefined()); 8012 8013 // Call the Object's constructor with null. 8014 Handle<Value> args5[] = { v8::Null() }; 8015 Local<Value> value_obj5 = instance->CallAsConstructor(1, args5); 8016 CHECK(value_obj5->IsObject()); 8017 Local<Object> object5 = Local<Object>::Cast(value_obj5); 8018 value = object5->Get(v8_str("a")); 8019 CHECK(!try_catch.HasCaught()); 8020 CHECK(value->IsNull()); 8021 } 8022 8023 // Check exception handling when there is no constructor set for the Object. 8024 { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); 8025 Local<Object> instance = instance_template->NewInstance(); 8026 context->Global()->Set(v8_str("obj2"), instance); 8027 v8::TryCatch try_catch; 8028 Local<Value> value; 8029 CHECK(!try_catch.HasCaught()); 8030 8031 value = CompileRun("new obj2(28)"); 8032 CHECK(try_catch.HasCaught()); 8033 String::AsciiValue exception_value1(try_catch.Exception()); 8034 CHECK_EQ("TypeError: object is not a function", *exception_value1); 8035 try_catch.Reset(); 8036 8037 Local<Value> args[] = { v8_num(29) }; 8038 value = instance->CallAsConstructor(1, args); 8039 CHECK(try_catch.HasCaught()); 8040 String::AsciiValue exception_value2(try_catch.Exception()); 8041 CHECK_EQ("TypeError: #<Object> is not a function", *exception_value2); 8042 try_catch.Reset(); 8043 } 8044 8045 // Check the case when constructor throws exception. 8046 { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); 8047 instance_template->SetCallAsFunctionHandler(ThrowValue); 8048 Local<Object> instance = instance_template->NewInstance(); 8049 context->Global()->Set(v8_str("obj3"), instance); 8050 v8::TryCatch try_catch; 8051 Local<Value> value; 8052 CHECK(!try_catch.HasCaught()); 8053 8054 value = CompileRun("new obj3(22)"); 8055 CHECK(try_catch.HasCaught()); 8056 String::AsciiValue exception_value1(try_catch.Exception()); 8057 CHECK_EQ("22", *exception_value1); 8058 try_catch.Reset(); 8059 8060 Local<Value> args[] = { v8_num(23) }; 8061 value = instance->CallAsConstructor(1, args); 8062 CHECK(try_catch.HasCaught()); 8063 String::AsciiValue exception_value2(try_catch.Exception()); 8064 CHECK_EQ("23", *exception_value2); 8065 try_catch.Reset(); 8066 } 8067 8068 // Check whether constructor returns with an object or non-object. 8069 { Local<FunctionTemplate> function_template = 8070 FunctionTemplate::New(FakeConstructorCallback); 8071 Local<Function> function = function_template->GetFunction(); 8072 Local<Object> instance1 = function; 8073 context->Global()->Set(v8_str("obj4"), instance1); 8074 v8::TryCatch try_catch; 8075 Local<Value> value; 8076 CHECK(!try_catch.HasCaught()); 8077 8078 CHECK(instance1->IsObject()); 8079 CHECK(instance1->IsFunction()); 8080 8081 value = CompileRun("new obj4(28)"); 8082 CHECK(!try_catch.HasCaught()); 8083 CHECK(value->IsObject()); 8084 8085 Local<Value> args1[] = { v8_num(28) }; 8086 value = instance1->CallAsConstructor(1, args1); 8087 CHECK(!try_catch.HasCaught()); 8088 CHECK(value->IsObject()); 8089 8090 Local<ObjectTemplate> instance_template = ObjectTemplate::New(); 8091 instance_template->SetCallAsFunctionHandler(FakeConstructorCallback); 8092 Local<Object> instance2 = instance_template->NewInstance(); 8093 context->Global()->Set(v8_str("obj5"), instance2); 8094 CHECK(!try_catch.HasCaught()); 8095 8096 CHECK(instance2->IsObject()); 8097 CHECK(!instance2->IsFunction()); 8098 8099 value = CompileRun("new obj5(28)"); 8100 CHECK(!try_catch.HasCaught()); 8101 CHECK(!value->IsObject()); 8102 8103 Local<Value> args2[] = { v8_num(28) }; 8104 value = instance2->CallAsConstructor(1, args2); 8105 CHECK(!try_catch.HasCaught()); 8106 CHECK(!value->IsObject()); 8107 } 8108 } 8109 8110 8111 THREADED_TEST(FunctionDescriptorException) { 8112 v8::HandleScope handle_scope; 8113 LocalContext context; 8114 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 8115 templ->SetClassName(v8_str("Fun")); 8116 Local<Function> cons = templ->GetFunction(); 8117 context->Global()->Set(v8_str("Fun"), cons); 8118 Local<Value> value = CompileRun( 8119 "function test() {" 8120 " try {" 8121 " (new Fun()).blah()" 8122 " } catch (e) {" 8123 " var str = String(e);" 8124 " if (str.indexOf('TypeError') == -1) return 1;" 8125 " if (str.indexOf('[object Fun]') != -1) return 2;" 8126 " if (str.indexOf('#<Fun>') == -1) return 3;" 8127 " return 0;" 8128 " }" 8129 " return 4;" 8130 "}" 8131 "test();"); 8132 CHECK_EQ(0, value->Int32Value()); 8133 } 8134 8135 8136 THREADED_TEST(EvalAliasedDynamic) { 8137 v8::HandleScope scope; 8138 LocalContext current; 8139 8140 // Tests where aliased eval can only be resolved dynamically. 8141 Local<Script> script = 8142 Script::Compile(v8_str("function f(x) { " 8143 " var foo = 2;" 8144 " with (x) { return eval('foo'); }" 8145 "}" 8146 "foo = 0;" 8147 "result1 = f(new Object());" 8148 "result2 = f(this);" 8149 "var x = new Object();" 8150 "x.eval = function(x) { return 1; };" 8151 "result3 = f(x);")); 8152 script->Run(); 8153 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value()); 8154 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value()); 8155 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value()); 8156 8157 v8::TryCatch try_catch; 8158 script = 8159 Script::Compile(v8_str("function f(x) { " 8160 " var bar = 2;" 8161 " with (x) { return eval('bar'); }" 8162 "}" 8163 "result4 = f(this)")); 8164 script->Run(); 8165 CHECK(!try_catch.HasCaught()); 8166 CHECK_EQ(2, current->Global()->Get(v8_str("result4"))->Int32Value()); 8167 8168 try_catch.Reset(); 8169 } 8170 8171 8172 THREADED_TEST(CrossEval) { 8173 v8::HandleScope scope; 8174 LocalContext other; 8175 LocalContext current; 8176 8177 Local<String> token = v8_str("<security token>"); 8178 other->SetSecurityToken(token); 8179 current->SetSecurityToken(token); 8180 8181 // Set up reference from current to other. 8182 current->Global()->Set(v8_str("other"), other->Global()); 8183 8184 // Check that new variables are introduced in other context. 8185 Local<Script> script = 8186 Script::Compile(v8_str("other.eval('var foo = 1234')")); 8187 script->Run(); 8188 Local<Value> foo = other->Global()->Get(v8_str("foo")); 8189 CHECK_EQ(1234, foo->Int32Value()); 8190 CHECK(!current->Global()->Has(v8_str("foo"))); 8191 8192 // Check that writing to non-existing properties introduces them in 8193 // the other context. 8194 script = 8195 Script::Compile(v8_str("other.eval('na = 1234')")); 8196 script->Run(); 8197 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value()); 8198 CHECK(!current->Global()->Has(v8_str("na"))); 8199 8200 // Check that global variables in current context are not visible in other 8201 // context. 8202 v8::TryCatch try_catch; 8203 script = 8204 Script::Compile(v8_str("var bar = 42; other.eval('bar');")); 8205 Local<Value> result = script->Run(); 8206 CHECK(try_catch.HasCaught()); 8207 try_catch.Reset(); 8208 8209 // Check that local variables in current context are not visible in other 8210 // context. 8211 script = 8212 Script::Compile(v8_str("(function() { " 8213 " var baz = 87;" 8214 " return other.eval('baz');" 8215 "})();")); 8216 result = script->Run(); 8217 CHECK(try_catch.HasCaught()); 8218 try_catch.Reset(); 8219 8220 // Check that global variables in the other environment are visible 8221 // when evaluting code. 8222 other->Global()->Set(v8_str("bis"), v8_num(1234)); 8223 script = Script::Compile(v8_str("other.eval('bis')")); 8224 CHECK_EQ(1234, script->Run()->Int32Value()); 8225 CHECK(!try_catch.HasCaught()); 8226 8227 // Check that the 'this' pointer points to the global object evaluating 8228 // code. 8229 other->Global()->Set(v8_str("t"), other->Global()); 8230 script = Script::Compile(v8_str("other.eval('this == t')")); 8231 result = script->Run(); 8232 CHECK(result->IsTrue()); 8233 CHECK(!try_catch.HasCaught()); 8234 8235 // Check that variables introduced in with-statement are not visible in 8236 // other context. 8237 script = 8238 Script::Compile(v8_str("with({x:2}){other.eval('x')}")); 8239 result = script->Run(); 8240 CHECK(try_catch.HasCaught()); 8241 try_catch.Reset(); 8242 8243 // Check that you cannot use 'eval.call' with another object than the 8244 // current global object. 8245 script = 8246 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')")); 8247 result = script->Run(); 8248 CHECK(try_catch.HasCaught()); 8249 } 8250 8251 8252 // Test that calling eval in a context which has been detached from 8253 // its global throws an exception. This behavior is consistent with 8254 // other JavaScript implementations. 8255 THREADED_TEST(EvalInDetachedGlobal) { 8256 v8::HandleScope scope; 8257 8258 v8::Persistent<Context> context0 = Context::New(); 8259 v8::Persistent<Context> context1 = Context::New(); 8260 8261 // Set up function in context0 that uses eval from context0. 8262 context0->Enter(); 8263 v8::Handle<v8::Value> fun = 8264 CompileRun("var x = 42;" 8265 "(function() {" 8266 " var e = eval;" 8267 " return function(s) { return e(s); }" 8268 "})()"); 8269 context0->Exit(); 8270 8271 // Put the function into context1 and call it before and after 8272 // detaching the global. Before detaching, the call succeeds and 8273 // after detaching and exception is thrown. 8274 context1->Enter(); 8275 context1->Global()->Set(v8_str("fun"), fun); 8276 v8::Handle<v8::Value> x_value = CompileRun("fun('x')"); 8277 CHECK_EQ(42, x_value->Int32Value()); 8278 context0->DetachGlobal(); 8279 v8::TryCatch catcher; 8280 x_value = CompileRun("fun('x')"); 8281 CHECK(x_value.IsEmpty()); 8282 CHECK(catcher.HasCaught()); 8283 context1->Exit(); 8284 8285 context1.Dispose(); 8286 context0.Dispose(); 8287 } 8288 8289 8290 THREADED_TEST(CrossLazyLoad) { 8291 v8::HandleScope scope; 8292 LocalContext other; 8293 LocalContext current; 8294 8295 Local<String> token = v8_str("<security token>"); 8296 other->SetSecurityToken(token); 8297 current->SetSecurityToken(token); 8298 8299 // Set up reference from current to other. 8300 current->Global()->Set(v8_str("other"), other->Global()); 8301 8302 // Trigger lazy loading in other context. 8303 Local<Script> script = 8304 Script::Compile(v8_str("other.eval('new Date(42)')")); 8305 Local<Value> value = script->Run(); 8306 CHECK_EQ(42.0, value->NumberValue()); 8307 } 8308 8309 8310 static v8::Handle<Value> call_as_function(const v8::Arguments& args) { 8311 ApiTestFuzzer::Fuzz(); 8312 if (args.IsConstructCall()) { 8313 if (args[0]->IsInt32()) { 8314 return v8_num(-args[0]->Int32Value()); 8315 } 8316 } 8317 8318 return args[0]; 8319 } 8320 8321 8322 // Test that a call handler can be set for objects which will allow 8323 // non-function objects created through the API to be called as 8324 // functions. 8325 THREADED_TEST(CallAsFunction) { 8326 v8::HandleScope scope; 8327 LocalContext context; 8328 8329 { Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 8330 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 8331 instance_template->SetCallAsFunctionHandler(call_as_function); 8332 Local<v8::Object> instance = t->GetFunction()->NewInstance(); 8333 context->Global()->Set(v8_str("obj"), instance); 8334 v8::TryCatch try_catch; 8335 Local<Value> value; 8336 CHECK(!try_catch.HasCaught()); 8337 8338 value = CompileRun("obj(42)"); 8339 CHECK(!try_catch.HasCaught()); 8340 CHECK_EQ(42, value->Int32Value()); 8341 8342 value = CompileRun("(function(o){return o(49)})(obj)"); 8343 CHECK(!try_catch.HasCaught()); 8344 CHECK_EQ(49, value->Int32Value()); 8345 8346 // test special case of call as function 8347 value = CompileRun("[obj]['0'](45)"); 8348 CHECK(!try_catch.HasCaught()); 8349 CHECK_EQ(45, value->Int32Value()); 8350 8351 value = CompileRun("obj.call = Function.prototype.call;" 8352 "obj.call(null, 87)"); 8353 CHECK(!try_catch.HasCaught()); 8354 CHECK_EQ(87, value->Int32Value()); 8355 8356 // Regression tests for bug #1116356: Calling call through call/apply 8357 // must work for non-function receivers. 8358 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])"; 8359 value = CompileRun(apply_99); 8360 CHECK(!try_catch.HasCaught()); 8361 CHECK_EQ(99, value->Int32Value()); 8362 8363 const char* call_17 = "Function.prototype.call.call(obj, this, 17)"; 8364 value = CompileRun(call_17); 8365 CHECK(!try_catch.HasCaught()); 8366 CHECK_EQ(17, value->Int32Value()); 8367 8368 // Check that the call-as-function handler can be called through 8369 // new. 8370 value = CompileRun("new obj(43)"); 8371 CHECK(!try_catch.HasCaught()); 8372 CHECK_EQ(-43, value->Int32Value()); 8373 8374 // Check that the call-as-function handler can be called through 8375 // the API. 8376 v8::Handle<Value> args[] = { v8_num(28) }; 8377 value = instance->CallAsFunction(instance, 1, args); 8378 CHECK(!try_catch.HasCaught()); 8379 CHECK_EQ(28, value->Int32Value()); 8380 } 8381 8382 { Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 8383 Local<ObjectTemplate> instance_template(t->InstanceTemplate()); 8384 USE(instance_template); 8385 Local<v8::Object> instance = t->GetFunction()->NewInstance(); 8386 context->Global()->Set(v8_str("obj2"), instance); 8387 v8::TryCatch try_catch; 8388 Local<Value> value; 8389 CHECK(!try_catch.HasCaught()); 8390 8391 // Call an object without call-as-function handler through the JS 8392 value = CompileRun("obj2(28)"); 8393 CHECK(value.IsEmpty()); 8394 CHECK(try_catch.HasCaught()); 8395 String::AsciiValue exception_value1(try_catch.Exception()); 8396 CHECK_EQ("TypeError: Property 'obj2' of object #<Object> is not a function", 8397 *exception_value1); 8398 try_catch.Reset(); 8399 8400 // Call an object without call-as-function handler through the API 8401 value = CompileRun("obj2(28)"); 8402 v8::Handle<Value> args[] = { v8_num(28) }; 8403 value = instance->CallAsFunction(instance, 1, args); 8404 CHECK(value.IsEmpty()); 8405 CHECK(try_catch.HasCaught()); 8406 String::AsciiValue exception_value2(try_catch.Exception()); 8407 CHECK_EQ("TypeError: [object Object] is not a function", *exception_value2); 8408 try_catch.Reset(); 8409 } 8410 8411 { Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 8412 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 8413 instance_template->SetCallAsFunctionHandler(ThrowValue); 8414 Local<v8::Object> instance = t->GetFunction()->NewInstance(); 8415 context->Global()->Set(v8_str("obj3"), instance); 8416 v8::TryCatch try_catch; 8417 Local<Value> value; 8418 CHECK(!try_catch.HasCaught()); 8419 8420 // Catch the exception which is thrown by call-as-function handler 8421 value = CompileRun("obj3(22)"); 8422 CHECK(try_catch.HasCaught()); 8423 String::AsciiValue exception_value1(try_catch.Exception()); 8424 CHECK_EQ("22", *exception_value1); 8425 try_catch.Reset(); 8426 8427 v8::Handle<Value> args[] = { v8_num(23) }; 8428 value = instance->CallAsFunction(instance, 1, args); 8429 CHECK(try_catch.HasCaught()); 8430 String::AsciiValue exception_value2(try_catch.Exception()); 8431 CHECK_EQ("23", *exception_value2); 8432 try_catch.Reset(); 8433 } 8434 } 8435 8436 8437 // Check whether a non-function object is callable. 8438 THREADED_TEST(CallableObject) { 8439 v8::HandleScope scope; 8440 LocalContext context; 8441 8442 { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); 8443 instance_template->SetCallAsFunctionHandler(call_as_function); 8444 Local<Object> instance = instance_template->NewInstance(); 8445 v8::TryCatch try_catch; 8446 8447 CHECK(instance->IsCallable()); 8448 CHECK(!try_catch.HasCaught()); 8449 } 8450 8451 { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); 8452 Local<Object> instance = instance_template->NewInstance(); 8453 v8::TryCatch try_catch; 8454 8455 CHECK(!instance->IsCallable()); 8456 CHECK(!try_catch.HasCaught()); 8457 } 8458 8459 { Local<FunctionTemplate> function_template = 8460 FunctionTemplate::New(call_as_function); 8461 Local<Function> function = function_template->GetFunction(); 8462 Local<Object> instance = function; 8463 v8::TryCatch try_catch; 8464 8465 CHECK(instance->IsCallable()); 8466 CHECK(!try_catch.HasCaught()); 8467 } 8468 8469 { Local<FunctionTemplate> function_template = FunctionTemplate::New(); 8470 Local<Function> function = function_template->GetFunction(); 8471 Local<Object> instance = function; 8472 v8::TryCatch try_catch; 8473 8474 CHECK(instance->IsCallable()); 8475 CHECK(!try_catch.HasCaught()); 8476 } 8477 } 8478 8479 8480 static int CountHandles() { 8481 return v8::HandleScope::NumberOfHandles(); 8482 } 8483 8484 8485 static int Recurse(int depth, int iterations) { 8486 v8::HandleScope scope; 8487 if (depth == 0) return CountHandles(); 8488 for (int i = 0; i < iterations; i++) { 8489 Local<v8::Number> n(v8::Integer::New(42)); 8490 } 8491 return Recurse(depth - 1, iterations); 8492 } 8493 8494 8495 THREADED_TEST(HandleIteration) { 8496 static const int kIterations = 500; 8497 static const int kNesting = 200; 8498 CHECK_EQ(0, CountHandles()); 8499 { 8500 v8::HandleScope scope1; 8501 CHECK_EQ(0, CountHandles()); 8502 for (int i = 0; i < kIterations; i++) { 8503 Local<v8::Number> n(v8::Integer::New(42)); 8504 CHECK_EQ(i + 1, CountHandles()); 8505 } 8506 8507 CHECK_EQ(kIterations, CountHandles()); 8508 { 8509 v8::HandleScope scope2; 8510 for (int j = 0; j < kIterations; j++) { 8511 Local<v8::Number> n(v8::Integer::New(42)); 8512 CHECK_EQ(j + 1 + kIterations, CountHandles()); 8513 } 8514 } 8515 CHECK_EQ(kIterations, CountHandles()); 8516 } 8517 CHECK_EQ(0, CountHandles()); 8518 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations)); 8519 } 8520 8521 8522 static v8::Handle<Value> InterceptorHasOwnPropertyGetter( 8523 Local<String> name, 8524 const AccessorInfo& info) { 8525 ApiTestFuzzer::Fuzz(); 8526 return v8::Handle<Value>(); 8527 } 8528 8529 8530 THREADED_TEST(InterceptorHasOwnProperty) { 8531 v8::HandleScope scope; 8532 LocalContext context; 8533 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 8534 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 8535 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter); 8536 Local<Function> function = fun_templ->GetFunction(); 8537 context->Global()->Set(v8_str("constructor"), function); 8538 v8::Handle<Value> value = CompileRun( 8539 "var o = new constructor();" 8540 "o.hasOwnProperty('ostehaps');"); 8541 CHECK_EQ(false, value->BooleanValue()); 8542 value = CompileRun( 8543 "o.ostehaps = 42;" 8544 "o.hasOwnProperty('ostehaps');"); 8545 CHECK_EQ(true, value->BooleanValue()); 8546 value = CompileRun( 8547 "var p = new constructor();" 8548 "p.hasOwnProperty('ostehaps');"); 8549 CHECK_EQ(false, value->BooleanValue()); 8550 } 8551 8552 8553 static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC( 8554 Local<String> name, 8555 const AccessorInfo& info) { 8556 ApiTestFuzzer::Fuzz(); 8557 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 8558 return v8::Handle<Value>(); 8559 } 8560 8561 8562 THREADED_TEST(InterceptorHasOwnPropertyCausingGC) { 8563 v8::HandleScope scope; 8564 LocalContext context; 8565 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 8566 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 8567 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC); 8568 Local<Function> function = fun_templ->GetFunction(); 8569 context->Global()->Set(v8_str("constructor"), function); 8570 // Let's first make some stuff so we can be sure to get a good GC. 8571 CompileRun( 8572 "function makestr(size) {" 8573 " switch (size) {" 8574 " case 1: return 'f';" 8575 " case 2: return 'fo';" 8576 " case 3: return 'foo';" 8577 " }" 8578 " return makestr(size >> 1) + makestr((size + 1) >> 1);" 8579 "}" 8580 "var x = makestr(12345);" 8581 "x = makestr(31415);" 8582 "x = makestr(23456);"); 8583 v8::Handle<Value> value = CompileRun( 8584 "var o = new constructor();" 8585 "o.__proto__ = new String(x);" 8586 "o.hasOwnProperty('ostehaps');"); 8587 CHECK_EQ(false, value->BooleanValue()); 8588 } 8589 8590 8591 typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property, 8592 const AccessorInfo& info); 8593 8594 8595 static void CheckInterceptorLoadIC(NamedPropertyGetter getter, 8596 const char* source, 8597 int expected) { 8598 v8::HandleScope scope; 8599 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8600 templ->SetNamedPropertyHandler(getter, 0, 0, 0, 0, v8_str("data")); 8601 LocalContext context; 8602 context->Global()->Set(v8_str("o"), templ->NewInstance()); 8603 v8::Handle<Value> value = CompileRun(source); 8604 CHECK_EQ(expected, value->Int32Value()); 8605 } 8606 8607 8608 static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name, 8609 const AccessorInfo& info) { 8610 ApiTestFuzzer::Fuzz(); 8611 CHECK_EQ(v8_str("data"), info.Data()); 8612 CHECK_EQ(v8_str("x"), name); 8613 return v8::Integer::New(42); 8614 } 8615 8616 8617 // This test should hit the load IC for the interceptor case. 8618 THREADED_TEST(InterceptorLoadIC) { 8619 CheckInterceptorLoadIC(InterceptorLoadICGetter, 8620 "var result = 0;" 8621 "for (var i = 0; i < 1000; i++) {" 8622 " result = o.x;" 8623 "}", 8624 42); 8625 } 8626 8627 8628 // Below go several tests which verify that JITing for various 8629 // configurations of interceptor and explicit fields works fine 8630 // (those cases are special cased to get better performance). 8631 8632 static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name, 8633 const AccessorInfo& info) { 8634 ApiTestFuzzer::Fuzz(); 8635 return v8_str("x")->Equals(name) 8636 ? v8::Integer::New(42) : v8::Handle<v8::Value>(); 8637 } 8638 8639 8640 THREADED_TEST(InterceptorLoadICWithFieldOnHolder) { 8641 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 8642 "var result = 0;" 8643 "o.y = 239;" 8644 "for (var i = 0; i < 1000; i++) {" 8645 " result = o.y;" 8646 "}", 8647 239); 8648 } 8649 8650 8651 THREADED_TEST(InterceptorLoadICWithSubstitutedProto) { 8652 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 8653 "var result = 0;" 8654 "o.__proto__ = { 'y': 239 };" 8655 "for (var i = 0; i < 1000; i++) {" 8656 " result = o.y + o.x;" 8657 "}", 8658 239 + 42); 8659 } 8660 8661 8662 THREADED_TEST(InterceptorLoadICWithPropertyOnProto) { 8663 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 8664 "var result = 0;" 8665 "o.__proto__.y = 239;" 8666 "for (var i = 0; i < 1000; i++) {" 8667 " result = o.y + o.x;" 8668 "}", 8669 239 + 42); 8670 } 8671 8672 8673 THREADED_TEST(InterceptorLoadICUndefined) { 8674 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 8675 "var result = 0;" 8676 "for (var i = 0; i < 1000; i++) {" 8677 " result = (o.y == undefined) ? 239 : 42;" 8678 "}", 8679 239); 8680 } 8681 8682 8683 THREADED_TEST(InterceptorLoadICWithOverride) { 8684 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 8685 "fst = new Object(); fst.__proto__ = o;" 8686 "snd = new Object(); snd.__proto__ = fst;" 8687 "var result1 = 0;" 8688 "for (var i = 0; i < 1000; i++) {" 8689 " result1 = snd.x;" 8690 "}" 8691 "fst.x = 239;" 8692 "var result = 0;" 8693 "for (var i = 0; i < 1000; i++) {" 8694 " result = snd.x;" 8695 "}" 8696 "result + result1", 8697 239 + 42); 8698 } 8699 8700 8701 // Test the case when we stored field into 8702 // a stub, but interceptor produced value on its own. 8703 THREADED_TEST(InterceptorLoadICFieldNotNeeded) { 8704 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 8705 "proto = new Object();" 8706 "o.__proto__ = proto;" 8707 "proto.x = 239;" 8708 "for (var i = 0; i < 1000; i++) {" 8709 " o.x;" 8710 // Now it should be ICed and keep a reference to x defined on proto 8711 "}" 8712 "var result = 0;" 8713 "for (var i = 0; i < 1000; i++) {" 8714 " result += o.x;" 8715 "}" 8716 "result;", 8717 42 * 1000); 8718 } 8719 8720 8721 // Test the case when we stored field into 8722 // a stub, but it got invalidated later on. 8723 THREADED_TEST(InterceptorLoadICInvalidatedField) { 8724 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 8725 "proto1 = new Object();" 8726 "proto2 = new Object();" 8727 "o.__proto__ = proto1;" 8728 "proto1.__proto__ = proto2;" 8729 "proto2.y = 239;" 8730 "for (var i = 0; i < 1000; i++) {" 8731 " o.y;" 8732 // Now it should be ICed and keep a reference to y defined on proto2 8733 "}" 8734 "proto1.y = 42;" 8735 "var result = 0;" 8736 "for (var i = 0; i < 1000; i++) {" 8737 " result += o.y;" 8738 "}" 8739 "result;", 8740 42 * 1000); 8741 } 8742 8743 8744 static int interceptor_load_not_handled_calls = 0; 8745 static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name, 8746 const AccessorInfo& info) { 8747 ++interceptor_load_not_handled_calls; 8748 return v8::Handle<v8::Value>(); 8749 } 8750 8751 8752 // Test how post-interceptor lookups are done in the non-cacheable 8753 // case: the interceptor should not be invoked during this lookup. 8754 THREADED_TEST(InterceptorLoadICPostInterceptor) { 8755 interceptor_load_not_handled_calls = 0; 8756 CheckInterceptorLoadIC(InterceptorLoadNotHandled, 8757 "receiver = new Object();" 8758 "receiver.__proto__ = o;" 8759 "proto = new Object();" 8760 "/* Make proto a slow-case object. */" 8761 "for (var i = 0; i < 1000; i++) {" 8762 " proto[\"xxxxxxxx\" + i] = [];" 8763 "}" 8764 "proto.x = 17;" 8765 "o.__proto__ = proto;" 8766 "var result = 0;" 8767 "for (var i = 0; i < 1000; i++) {" 8768 " result += receiver.x;" 8769 "}" 8770 "result;", 8771 17 * 1000); 8772 CHECK_EQ(1000, interceptor_load_not_handled_calls); 8773 } 8774 8775 8776 // Test the case when we stored field into 8777 // a stub, but it got invalidated later on due to override on 8778 // global object which is between interceptor and fields' holders. 8779 THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) { 8780 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 8781 "o.__proto__ = this;" // set a global to be a proto of o. 8782 "this.__proto__.y = 239;" 8783 "for (var i = 0; i < 10; i++) {" 8784 " if (o.y != 239) throw 'oops: ' + o.y;" 8785 // Now it should be ICed and keep a reference to y defined on field_holder. 8786 "}" 8787 "this.y = 42;" // Assign on a global. 8788 "var result = 0;" 8789 "for (var i = 0; i < 10; i++) {" 8790 " result += o.y;" 8791 "}" 8792 "result;", 8793 42 * 10); 8794 } 8795 8796 8797 static void SetOnThis(Local<String> name, 8798 Local<Value> value, 8799 const AccessorInfo& info) { 8800 info.This()->ForceSet(name, value); 8801 } 8802 8803 8804 THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) { 8805 v8::HandleScope scope; 8806 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8807 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 8808 templ->SetAccessor(v8_str("y"), Return239); 8809 LocalContext context; 8810 context->Global()->Set(v8_str("o"), templ->NewInstance()); 8811 8812 // Check the case when receiver and interceptor's holder 8813 // are the same objects. 8814 v8::Handle<Value> value = CompileRun( 8815 "var result = 0;" 8816 "for (var i = 0; i < 7; i++) {" 8817 " result = o.y;" 8818 "}"); 8819 CHECK_EQ(239, value->Int32Value()); 8820 8821 // Check the case when interceptor's holder is in proto chain 8822 // of receiver. 8823 value = CompileRun( 8824 "r = { __proto__: o };" 8825 "var result = 0;" 8826 "for (var i = 0; i < 7; i++) {" 8827 " result = r.y;" 8828 "}"); 8829 CHECK_EQ(239, value->Int32Value()); 8830 } 8831 8832 8833 THREADED_TEST(InterceptorLoadICWithCallbackOnProto) { 8834 v8::HandleScope scope; 8835 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 8836 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 8837 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 8838 templ_p->SetAccessor(v8_str("y"), Return239); 8839 8840 LocalContext context; 8841 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 8842 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 8843 8844 // Check the case when receiver and interceptor's holder 8845 // are the same objects. 8846 v8::Handle<Value> value = CompileRun( 8847 "o.__proto__ = p;" 8848 "var result = 0;" 8849 "for (var i = 0; i < 7; i++) {" 8850 " result = o.x + o.y;" 8851 "}"); 8852 CHECK_EQ(239 + 42, value->Int32Value()); 8853 8854 // Check the case when interceptor's holder is in proto chain 8855 // of receiver. 8856 value = CompileRun( 8857 "r = { __proto__: o };" 8858 "var result = 0;" 8859 "for (var i = 0; i < 7; i++) {" 8860 " result = r.x + r.y;" 8861 "}"); 8862 CHECK_EQ(239 + 42, value->Int32Value()); 8863 } 8864 8865 8866 THREADED_TEST(InterceptorLoadICForCallbackWithOverride) { 8867 v8::HandleScope scope; 8868 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8869 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 8870 templ->SetAccessor(v8_str("y"), Return239); 8871 8872 LocalContext context; 8873 context->Global()->Set(v8_str("o"), templ->NewInstance()); 8874 8875 v8::Handle<Value> value = CompileRun( 8876 "fst = new Object(); fst.__proto__ = o;" 8877 "snd = new Object(); snd.__proto__ = fst;" 8878 "var result1 = 0;" 8879 "for (var i = 0; i < 7; i++) {" 8880 " result1 = snd.x;" 8881 "}" 8882 "fst.x = 239;" 8883 "var result = 0;" 8884 "for (var i = 0; i < 7; i++) {" 8885 " result = snd.x;" 8886 "}" 8887 "result + result1"); 8888 CHECK_EQ(239 + 42, value->Int32Value()); 8889 } 8890 8891 8892 // Test the case when we stored callback into 8893 // a stub, but interceptor produced value on its own. 8894 THREADED_TEST(InterceptorLoadICCallbackNotNeeded) { 8895 v8::HandleScope scope; 8896 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 8897 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 8898 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 8899 templ_p->SetAccessor(v8_str("y"), Return239); 8900 8901 LocalContext context; 8902 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 8903 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 8904 8905 v8::Handle<Value> value = CompileRun( 8906 "o.__proto__ = p;" 8907 "for (var i = 0; i < 7; i++) {" 8908 " o.x;" 8909 // Now it should be ICed and keep a reference to x defined on p 8910 "}" 8911 "var result = 0;" 8912 "for (var i = 0; i < 7; i++) {" 8913 " result += o.x;" 8914 "}" 8915 "result"); 8916 CHECK_EQ(42 * 7, value->Int32Value()); 8917 } 8918 8919 8920 // Test the case when we stored callback into 8921 // a stub, but it got invalidated later on. 8922 THREADED_TEST(InterceptorLoadICInvalidatedCallback) { 8923 v8::HandleScope scope; 8924 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 8925 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 8926 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 8927 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 8928 8929 LocalContext context; 8930 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 8931 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 8932 8933 v8::Handle<Value> value = CompileRun( 8934 "inbetween = new Object();" 8935 "o.__proto__ = inbetween;" 8936 "inbetween.__proto__ = p;" 8937 "for (var i = 0; i < 10; i++) {" 8938 " o.y;" 8939 // Now it should be ICed and keep a reference to y defined on p 8940 "}" 8941 "inbetween.y = 42;" 8942 "var result = 0;" 8943 "for (var i = 0; i < 10; i++) {" 8944 " result += o.y;" 8945 "}" 8946 "result"); 8947 CHECK_EQ(42 * 10, value->Int32Value()); 8948 } 8949 8950 8951 // Test the case when we stored callback into 8952 // a stub, but it got invalidated later on due to override on 8953 // global object which is between interceptor and callbacks' holders. 8954 THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) { 8955 v8::HandleScope scope; 8956 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 8957 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 8958 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 8959 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 8960 8961 LocalContext context; 8962 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 8963 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 8964 8965 v8::Handle<Value> value = CompileRun( 8966 "o.__proto__ = this;" 8967 "this.__proto__ = p;" 8968 "for (var i = 0; i < 10; i++) {" 8969 " if (o.y != 239) throw 'oops: ' + o.y;" 8970 // Now it should be ICed and keep a reference to y defined on p 8971 "}" 8972 "this.y = 42;" 8973 "var result = 0;" 8974 "for (var i = 0; i < 10; i++) {" 8975 " result += o.y;" 8976 "}" 8977 "result"); 8978 CHECK_EQ(42 * 10, value->Int32Value()); 8979 } 8980 8981 8982 static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name, 8983 const AccessorInfo& info) { 8984 ApiTestFuzzer::Fuzz(); 8985 CHECK(v8_str("x")->Equals(name)); 8986 return v8::Integer::New(0); 8987 } 8988 8989 8990 THREADED_TEST(InterceptorReturningZero) { 8991 CheckInterceptorLoadIC(InterceptorLoadICGetter0, 8992 "o.x == undefined ? 1 : 0", 8993 0); 8994 } 8995 8996 8997 static v8::Handle<Value> InterceptorStoreICSetter( 8998 Local<String> key, Local<Value> value, const AccessorInfo&) { 8999 CHECK(v8_str("x")->Equals(key)); 9000 CHECK_EQ(42, value->Int32Value()); 9001 return value; 9002 } 9003 9004 9005 // This test should hit the store IC for the interceptor case. 9006 THREADED_TEST(InterceptorStoreIC) { 9007 v8::HandleScope scope; 9008 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9009 templ->SetNamedPropertyHandler(InterceptorLoadICGetter, 9010 InterceptorStoreICSetter, 9011 0, 0, 0, v8_str("data")); 9012 LocalContext context; 9013 context->Global()->Set(v8_str("o"), templ->NewInstance()); 9014 CompileRun( 9015 "for (var i = 0; i < 1000; i++) {" 9016 " o.x = 42;" 9017 "}"); 9018 } 9019 9020 9021 THREADED_TEST(InterceptorStoreICWithNoSetter) { 9022 v8::HandleScope scope; 9023 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9024 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 9025 LocalContext context; 9026 context->Global()->Set(v8_str("o"), templ->NewInstance()); 9027 v8::Handle<Value> value = CompileRun( 9028 "for (var i = 0; i < 1000; i++) {" 9029 " o.y = 239;" 9030 "}" 9031 "42 + o.y"); 9032 CHECK_EQ(239 + 42, value->Int32Value()); 9033 } 9034 9035 9036 9037 9038 v8::Handle<Value> call_ic_function; 9039 v8::Handle<Value> call_ic_function2; 9040 v8::Handle<Value> call_ic_function3; 9041 9042 static v8::Handle<Value> InterceptorCallICGetter(Local<String> name, 9043 const AccessorInfo& info) { 9044 ApiTestFuzzer::Fuzz(); 9045 CHECK(v8_str("x")->Equals(name)); 9046 return call_ic_function; 9047 } 9048 9049 9050 // This test should hit the call IC for the interceptor case. 9051 THREADED_TEST(InterceptorCallIC) { 9052 v8::HandleScope scope; 9053 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9054 templ->SetNamedPropertyHandler(InterceptorCallICGetter); 9055 LocalContext context; 9056 context->Global()->Set(v8_str("o"), templ->NewInstance()); 9057 call_ic_function = 9058 v8_compile("function f(x) { return x + 1; }; f")->Run(); 9059 v8::Handle<Value> value = CompileRun( 9060 "var result = 0;" 9061 "for (var i = 0; i < 1000; i++) {" 9062 " result = o.x(41);" 9063 "}"); 9064 CHECK_EQ(42, value->Int32Value()); 9065 } 9066 9067 9068 // This test checks that if interceptor doesn't provide 9069 // a value, we can fetch regular value. 9070 THREADED_TEST(InterceptorCallICSeesOthers) { 9071 v8::HandleScope scope; 9072 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9073 templ->SetNamedPropertyHandler(NoBlockGetterX); 9074 LocalContext context; 9075 context->Global()->Set(v8_str("o"), templ->NewInstance()); 9076 v8::Handle<Value> value = CompileRun( 9077 "o.x = function f(x) { return x + 1; };" 9078 "var result = 0;" 9079 "for (var i = 0; i < 7; i++) {" 9080 " result = o.x(41);" 9081 "}"); 9082 CHECK_EQ(42, value->Int32Value()); 9083 } 9084 9085 9086 static v8::Handle<Value> call_ic_function4; 9087 static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name, 9088 const AccessorInfo& info) { 9089 ApiTestFuzzer::Fuzz(); 9090 CHECK(v8_str("x")->Equals(name)); 9091 return call_ic_function4; 9092 } 9093 9094 9095 // This test checks that if interceptor provides a function, 9096 // even if we cached shadowed variant, interceptor's function 9097 // is invoked 9098 THREADED_TEST(InterceptorCallICCacheableNotNeeded) { 9099 v8::HandleScope scope; 9100 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9101 templ->SetNamedPropertyHandler(InterceptorCallICGetter4); 9102 LocalContext context; 9103 context->Global()->Set(v8_str("o"), templ->NewInstance()); 9104 call_ic_function4 = 9105 v8_compile("function f(x) { return x - 1; }; f")->Run(); 9106 v8::Handle<Value> value = CompileRun( 9107 "o.__proto__.x = function(x) { return x + 1; };" 9108 "var result = 0;" 9109 "for (var i = 0; i < 1000; i++) {" 9110 " result = o.x(42);" 9111 "}"); 9112 CHECK_EQ(41, value->Int32Value()); 9113 } 9114 9115 9116 // Test the case when we stored cacheable lookup into 9117 // a stub, but it got invalidated later on 9118 THREADED_TEST(InterceptorCallICInvalidatedCacheable) { 9119 v8::HandleScope scope; 9120 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9121 templ->SetNamedPropertyHandler(NoBlockGetterX); 9122 LocalContext context; 9123 context->Global()->Set(v8_str("o"), templ->NewInstance()); 9124 v8::Handle<Value> value = CompileRun( 9125 "proto1 = new Object();" 9126 "proto2 = new Object();" 9127 "o.__proto__ = proto1;" 9128 "proto1.__proto__ = proto2;" 9129 "proto2.y = function(x) { return x + 1; };" 9130 // Invoke it many times to compile a stub 9131 "for (var i = 0; i < 7; i++) {" 9132 " o.y(42);" 9133 "}" 9134 "proto1.y = function(x) { return x - 1; };" 9135 "var result = 0;" 9136 "for (var i = 0; i < 7; i++) {" 9137 " result += o.y(42);" 9138 "}"); 9139 CHECK_EQ(41 * 7, value->Int32Value()); 9140 } 9141 9142 9143 // This test checks that if interceptor doesn't provide a function, 9144 // cached constant function is used 9145 THREADED_TEST(InterceptorCallICConstantFunctionUsed) { 9146 v8::HandleScope scope; 9147 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9148 templ->SetNamedPropertyHandler(NoBlockGetterX); 9149 LocalContext context; 9150 context->Global()->Set(v8_str("o"), templ->NewInstance()); 9151 v8::Handle<Value> value = CompileRun( 9152 "function inc(x) { return x + 1; };" 9153 "inc(1);" 9154 "o.x = inc;" 9155 "var result = 0;" 9156 "for (var i = 0; i < 1000; i++) {" 9157 " result = o.x(42);" 9158 "}"); 9159 CHECK_EQ(43, value->Int32Value()); 9160 } 9161 9162 9163 static v8::Handle<Value> call_ic_function5; 9164 static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name, 9165 const AccessorInfo& info) { 9166 ApiTestFuzzer::Fuzz(); 9167 if (v8_str("x")->Equals(name)) 9168 return call_ic_function5; 9169 else 9170 return Local<Value>(); 9171 } 9172 9173 9174 // This test checks that if interceptor provides a function, 9175 // even if we cached constant function, interceptor's function 9176 // is invoked 9177 THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) { 9178 v8::HandleScope scope; 9179 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9180 templ->SetNamedPropertyHandler(InterceptorCallICGetter5); 9181 LocalContext context; 9182 context->Global()->Set(v8_str("o"), templ->NewInstance()); 9183 call_ic_function5 = 9184 v8_compile("function f(x) { return x - 1; }; f")->Run(); 9185 v8::Handle<Value> value = CompileRun( 9186 "function inc(x) { return x + 1; };" 9187 "inc(1);" 9188 "o.x = inc;" 9189 "var result = 0;" 9190 "for (var i = 0; i < 1000; i++) {" 9191 " result = o.x(42);" 9192 "}"); 9193 CHECK_EQ(41, value->Int32Value()); 9194 } 9195 9196 9197 static v8::Handle<Value> call_ic_function6; 9198 static v8::Handle<Value> InterceptorCallICGetter6(Local<String> name, 9199 const AccessorInfo& info) { 9200 ApiTestFuzzer::Fuzz(); 9201 if (v8_str("x")->Equals(name)) 9202 return call_ic_function6; 9203 else 9204 return Local<Value>(); 9205 } 9206 9207 9208 // Same test as above, except the code is wrapped in a function 9209 // to test the optimized compiler. 9210 THREADED_TEST(InterceptorCallICConstantFunctionNotNeededWrapped) { 9211 i::FLAG_allow_natives_syntax = true; 9212 v8::HandleScope scope; 9213 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9214 templ->SetNamedPropertyHandler(InterceptorCallICGetter6); 9215 LocalContext context; 9216 context->Global()->Set(v8_str("o"), templ->NewInstance()); 9217 call_ic_function6 = 9218 v8_compile("function f(x) { return x - 1; }; f")->Run(); 9219 v8::Handle<Value> value = CompileRun( 9220 "function inc(x) { return x + 1; };" 9221 "inc(1);" 9222 "o.x = inc;" 9223 "function test() {" 9224 " var result = 0;" 9225 " for (var i = 0; i < 1000; i++) {" 9226 " result = o.x(42);" 9227 " }" 9228 " return result;" 9229 "};" 9230 "test();" 9231 "test();" 9232 "test();" 9233 "%OptimizeFunctionOnNextCall(test);" 9234 "test()"); 9235 CHECK_EQ(41, value->Int32Value()); 9236 } 9237 9238 9239 // Test the case when we stored constant function into 9240 // a stub, but it got invalidated later on 9241 THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) { 9242 v8::HandleScope scope; 9243 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9244 templ->SetNamedPropertyHandler(NoBlockGetterX); 9245 LocalContext context; 9246 context->Global()->Set(v8_str("o"), templ->NewInstance()); 9247 v8::Handle<Value> value = CompileRun( 9248 "function inc(x) { return x + 1; };" 9249 "inc(1);" 9250 "proto1 = new Object();" 9251 "proto2 = new Object();" 9252 "o.__proto__ = proto1;" 9253 "proto1.__proto__ = proto2;" 9254 "proto2.y = inc;" 9255 // Invoke it many times to compile a stub 9256 "for (var i = 0; i < 7; i++) {" 9257 " o.y(42);" 9258 "}" 9259 "proto1.y = function(x) { return x - 1; };" 9260 "var result = 0;" 9261 "for (var i = 0; i < 7; i++) {" 9262 " result += o.y(42);" 9263 "}"); 9264 CHECK_EQ(41 * 7, value->Int32Value()); 9265 } 9266 9267 9268 // Test the case when we stored constant function into 9269 // a stub, but it got invalidated later on due to override on 9270 // global object which is between interceptor and constant function' holders. 9271 THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) { 9272 v8::HandleScope scope; 9273 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9274 templ->SetNamedPropertyHandler(NoBlockGetterX); 9275 LocalContext context; 9276 context->Global()->Set(v8_str("o"), templ->NewInstance()); 9277 v8::Handle<Value> value = CompileRun( 9278 "function inc(x) { return x + 1; };" 9279 "inc(1);" 9280 "o.__proto__ = this;" 9281 "this.__proto__.y = inc;" 9282 // Invoke it many times to compile a stub 9283 "for (var i = 0; i < 7; i++) {" 9284 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);" 9285 "}" 9286 "this.y = function(x) { return x - 1; };" 9287 "var result = 0;" 9288 "for (var i = 0; i < 7; i++) {" 9289 " result += o.y(42);" 9290 "}"); 9291 CHECK_EQ(41 * 7, value->Int32Value()); 9292 } 9293 9294 9295 // Test the case when actual function to call sits on global object. 9296 THREADED_TEST(InterceptorCallICCachedFromGlobal) { 9297 v8::HandleScope scope; 9298 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 9299 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 9300 9301 LocalContext context; 9302 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 9303 9304 v8::Handle<Value> value = CompileRun( 9305 "try {" 9306 " o.__proto__ = this;" 9307 " for (var i = 0; i < 10; i++) {" 9308 " var v = o.parseFloat('239');" 9309 " if (v != 239) throw v;" 9310 // Now it should be ICed and keep a reference to parseFloat. 9311 " }" 9312 " var result = 0;" 9313 " for (var i = 0; i < 10; i++) {" 9314 " result += o.parseFloat('239');" 9315 " }" 9316 " result" 9317 "} catch(e) {" 9318 " e" 9319 "};"); 9320 CHECK_EQ(239 * 10, value->Int32Value()); 9321 } 9322 9323 static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name, 9324 const AccessorInfo& info) { 9325 ApiTestFuzzer::Fuzz(); 9326 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data())); 9327 ++(*call_count); 9328 if ((*call_count) % 20 == 0) { 9329 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 9330 } 9331 return v8::Handle<Value>(); 9332 } 9333 9334 static v8::Handle<Value> FastApiCallback_TrivialSignature( 9335 const v8::Arguments& args) { 9336 ApiTestFuzzer::Fuzz(); 9337 CHECK_EQ(args.This(), args.Holder()); 9338 CHECK(args.Data()->Equals(v8_str("method_data"))); 9339 return v8::Integer::New(args[0]->Int32Value() + 1); 9340 } 9341 9342 static v8::Handle<Value> FastApiCallback_SimpleSignature( 9343 const v8::Arguments& args) { 9344 ApiTestFuzzer::Fuzz(); 9345 CHECK_EQ(args.This()->GetPrototype(), args.Holder()); 9346 CHECK(args.Data()->Equals(v8_str("method_data"))); 9347 // Note, we're using HasRealNamedProperty instead of Has to avoid 9348 // invoking the interceptor again. 9349 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo"))); 9350 return v8::Integer::New(args[0]->Int32Value() + 1); 9351 } 9352 9353 // Helper to maximize the odds of object moving. 9354 static void GenerateSomeGarbage() { 9355 CompileRun( 9356 "var garbage;" 9357 "for (var i = 0; i < 1000; i++) {" 9358 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];" 9359 "}" 9360 "garbage = undefined;"); 9361 } 9362 9363 9364 v8::Handle<v8::Value> DirectApiCallback(const v8::Arguments& args) { 9365 static int count = 0; 9366 if (count++ % 3 == 0) { 9367 HEAP-> CollectAllGarbage(true); // This should move the stub 9368 GenerateSomeGarbage(); // This should ensure the old stub memory is flushed 9369 } 9370 return v8::Handle<v8::Value>(); 9371 } 9372 9373 9374 THREADED_TEST(CallICFastApi_DirectCall_GCMoveStub) { 9375 v8::HandleScope scope; 9376 LocalContext context; 9377 v8::Handle<v8::ObjectTemplate> nativeobject_templ = v8::ObjectTemplate::New(); 9378 nativeobject_templ->Set("callback", 9379 v8::FunctionTemplate::New(DirectApiCallback)); 9380 v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance(); 9381 context->Global()->Set(v8_str("nativeobject"), nativeobject_obj); 9382 // call the api function multiple times to ensure direct call stub creation. 9383 CompileRun( 9384 "function f() {" 9385 " for (var i = 1; i <= 30; i++) {" 9386 " nativeobject.callback();" 9387 " }" 9388 "}" 9389 "f();"); 9390 } 9391 9392 9393 v8::Handle<v8::Value> ThrowingDirectApiCallback(const v8::Arguments& args) { 9394 return v8::ThrowException(v8_str("g")); 9395 } 9396 9397 9398 THREADED_TEST(CallICFastApi_DirectCall_Throw) { 9399 v8::HandleScope scope; 9400 LocalContext context; 9401 v8::Handle<v8::ObjectTemplate> nativeobject_templ = v8::ObjectTemplate::New(); 9402 nativeobject_templ->Set("callback", 9403 v8::FunctionTemplate::New(ThrowingDirectApiCallback)); 9404 v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance(); 9405 context->Global()->Set(v8_str("nativeobject"), nativeobject_obj); 9406 // call the api function multiple times to ensure direct call stub creation. 9407 v8::Handle<Value> result = CompileRun( 9408 "var result = '';" 9409 "function f() {" 9410 " for (var i = 1; i <= 5; i++) {" 9411 " try { nativeobject.callback(); } catch (e) { result += e; }" 9412 " }" 9413 "}" 9414 "f(); result;"); 9415 CHECK_EQ(v8_str("ggggg"), result); 9416 } 9417 9418 9419 v8::Handle<v8::Value> DirectGetterCallback(Local<String> name, 9420 const v8::AccessorInfo& info) { 9421 if (++p_getter_count % 3 == 0) { 9422 HEAP->CollectAllGarbage(true); 9423 GenerateSomeGarbage(); 9424 } 9425 return v8::Handle<v8::Value>(); 9426 } 9427 9428 9429 THREADED_TEST(LoadICFastApi_DirectCall_GCMoveStub) { 9430 v8::HandleScope scope; 9431 LocalContext context; 9432 v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(); 9433 obj->SetAccessor(v8_str("p1"), DirectGetterCallback); 9434 context->Global()->Set(v8_str("o1"), obj->NewInstance()); 9435 p_getter_count = 0; 9436 CompileRun( 9437 "function f() {" 9438 " for (var i = 0; i < 30; i++) o1.p1;" 9439 "}" 9440 "f();"); 9441 CHECK_EQ(30, p_getter_count); 9442 } 9443 9444 9445 v8::Handle<v8::Value> ThrowingDirectGetterCallback( 9446 Local<String> name, const v8::AccessorInfo& info) { 9447 return v8::ThrowException(v8_str("g")); 9448 } 9449 9450 9451 THREADED_TEST(LoadICFastApi_DirectCall_Throw) { 9452 v8::HandleScope scope; 9453 LocalContext context; 9454 v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(); 9455 obj->SetAccessor(v8_str("p1"), ThrowingDirectGetterCallback); 9456 context->Global()->Set(v8_str("o1"), obj->NewInstance()); 9457 v8::Handle<Value> result = CompileRun( 9458 "var result = '';" 9459 "for (var i = 0; i < 5; i++) {" 9460 " try { o1.p1; } catch (e) { result += e; }" 9461 "}" 9462 "result;"); 9463 CHECK_EQ(v8_str("ggggg"), result); 9464 } 9465 9466 9467 THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) { 9468 int interceptor_call_count = 0; 9469 v8::HandleScope scope; 9470 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9471 v8::Handle<v8::FunctionTemplate> method_templ = 9472 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 9473 v8_str("method_data"), 9474 v8::Handle<v8::Signature>()); 9475 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9476 proto_templ->Set(v8_str("method"), method_templ); 9477 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 9478 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 9479 NULL, NULL, NULL, NULL, 9480 v8::External::Wrap(&interceptor_call_count)); 9481 LocalContext context; 9482 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9483 GenerateSomeGarbage(); 9484 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9485 CompileRun( 9486 "var result = 0;" 9487 "for (var i = 0; i < 100; i++) {" 9488 " result = o.method(41);" 9489 "}"); 9490 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 9491 CHECK_EQ(100, interceptor_call_count); 9492 } 9493 9494 THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) { 9495 int interceptor_call_count = 0; 9496 v8::HandleScope scope; 9497 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9498 v8::Handle<v8::FunctionTemplate> method_templ = 9499 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 9500 v8_str("method_data"), 9501 v8::Signature::New(fun_templ)); 9502 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9503 proto_templ->Set(v8_str("method"), method_templ); 9504 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 9505 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 9506 NULL, NULL, NULL, NULL, 9507 v8::External::Wrap(&interceptor_call_count)); 9508 LocalContext context; 9509 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9510 GenerateSomeGarbage(); 9511 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9512 CompileRun( 9513 "o.foo = 17;" 9514 "var receiver = {};" 9515 "receiver.__proto__ = o;" 9516 "var result = 0;" 9517 "for (var i = 0; i < 100; i++) {" 9518 " result = receiver.method(41);" 9519 "}"); 9520 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 9521 CHECK_EQ(100, interceptor_call_count); 9522 } 9523 9524 THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) { 9525 int interceptor_call_count = 0; 9526 v8::HandleScope scope; 9527 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9528 v8::Handle<v8::FunctionTemplate> method_templ = 9529 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 9530 v8_str("method_data"), 9531 v8::Signature::New(fun_templ)); 9532 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9533 proto_templ->Set(v8_str("method"), method_templ); 9534 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 9535 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 9536 NULL, NULL, NULL, NULL, 9537 v8::External::Wrap(&interceptor_call_count)); 9538 LocalContext context; 9539 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9540 GenerateSomeGarbage(); 9541 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9542 CompileRun( 9543 "o.foo = 17;" 9544 "var receiver = {};" 9545 "receiver.__proto__ = o;" 9546 "var result = 0;" 9547 "var saved_result = 0;" 9548 "for (var i = 0; i < 100; i++) {" 9549 " result = receiver.method(41);" 9550 " if (i == 50) {" 9551 " saved_result = result;" 9552 " receiver = {method: function(x) { return x - 1 }};" 9553 " }" 9554 "}"); 9555 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 9556 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 9557 CHECK_GE(interceptor_call_count, 50); 9558 } 9559 9560 THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) { 9561 int interceptor_call_count = 0; 9562 v8::HandleScope scope; 9563 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9564 v8::Handle<v8::FunctionTemplate> method_templ = 9565 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 9566 v8_str("method_data"), 9567 v8::Signature::New(fun_templ)); 9568 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9569 proto_templ->Set(v8_str("method"), method_templ); 9570 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 9571 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 9572 NULL, NULL, NULL, NULL, 9573 v8::External::Wrap(&interceptor_call_count)); 9574 LocalContext context; 9575 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9576 GenerateSomeGarbage(); 9577 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9578 CompileRun( 9579 "o.foo = 17;" 9580 "var receiver = {};" 9581 "receiver.__proto__ = o;" 9582 "var result = 0;" 9583 "var saved_result = 0;" 9584 "for (var i = 0; i < 100; i++) {" 9585 " result = receiver.method(41);" 9586 " if (i == 50) {" 9587 " saved_result = result;" 9588 " o.method = function(x) { return x - 1 };" 9589 " }" 9590 "}"); 9591 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 9592 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 9593 CHECK_GE(interceptor_call_count, 50); 9594 } 9595 9596 THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) { 9597 int interceptor_call_count = 0; 9598 v8::HandleScope scope; 9599 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9600 v8::Handle<v8::FunctionTemplate> method_templ = 9601 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 9602 v8_str("method_data"), 9603 v8::Signature::New(fun_templ)); 9604 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9605 proto_templ->Set(v8_str("method"), method_templ); 9606 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 9607 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 9608 NULL, NULL, NULL, NULL, 9609 v8::External::Wrap(&interceptor_call_count)); 9610 LocalContext context; 9611 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9612 GenerateSomeGarbage(); 9613 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9614 v8::TryCatch try_catch; 9615 CompileRun( 9616 "o.foo = 17;" 9617 "var receiver = {};" 9618 "receiver.__proto__ = o;" 9619 "var result = 0;" 9620 "var saved_result = 0;" 9621 "for (var i = 0; i < 100; i++) {" 9622 " result = receiver.method(41);" 9623 " if (i == 50) {" 9624 " saved_result = result;" 9625 " receiver = 333;" 9626 " }" 9627 "}"); 9628 CHECK(try_catch.HasCaught()); 9629 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), 9630 try_catch.Exception()->ToString()); 9631 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 9632 CHECK_GE(interceptor_call_count, 50); 9633 } 9634 9635 THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) { 9636 int interceptor_call_count = 0; 9637 v8::HandleScope scope; 9638 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9639 v8::Handle<v8::FunctionTemplate> method_templ = 9640 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 9641 v8_str("method_data"), 9642 v8::Signature::New(fun_templ)); 9643 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9644 proto_templ->Set(v8_str("method"), method_templ); 9645 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 9646 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 9647 NULL, NULL, NULL, NULL, 9648 v8::External::Wrap(&interceptor_call_count)); 9649 LocalContext context; 9650 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9651 GenerateSomeGarbage(); 9652 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9653 v8::TryCatch try_catch; 9654 CompileRun( 9655 "o.foo = 17;" 9656 "var receiver = {};" 9657 "receiver.__proto__ = o;" 9658 "var result = 0;" 9659 "var saved_result = 0;" 9660 "for (var i = 0; i < 100; i++) {" 9661 " result = receiver.method(41);" 9662 " if (i == 50) {" 9663 " saved_result = result;" 9664 " receiver = {method: receiver.method};" 9665 " }" 9666 "}"); 9667 CHECK(try_catch.HasCaught()); 9668 CHECK_EQ(v8_str("TypeError: Illegal invocation"), 9669 try_catch.Exception()->ToString()); 9670 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 9671 CHECK_GE(interceptor_call_count, 50); 9672 } 9673 9674 THREADED_TEST(CallICFastApi_TrivialSignature) { 9675 v8::HandleScope scope; 9676 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9677 v8::Handle<v8::FunctionTemplate> method_templ = 9678 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 9679 v8_str("method_data"), 9680 v8::Handle<v8::Signature>()); 9681 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9682 proto_templ->Set(v8_str("method"), method_templ); 9683 v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); 9684 USE(templ); 9685 LocalContext context; 9686 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9687 GenerateSomeGarbage(); 9688 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9689 CompileRun( 9690 "var result = 0;" 9691 "for (var i = 0; i < 100; i++) {" 9692 " result = o.method(41);" 9693 "}"); 9694 9695 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 9696 } 9697 9698 THREADED_TEST(CallICFastApi_SimpleSignature) { 9699 v8::HandleScope scope; 9700 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9701 v8::Handle<v8::FunctionTemplate> method_templ = 9702 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 9703 v8_str("method_data"), 9704 v8::Signature::New(fun_templ)); 9705 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9706 proto_templ->Set(v8_str("method"), method_templ); 9707 v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); 9708 CHECK(!templ.IsEmpty()); 9709 LocalContext context; 9710 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9711 GenerateSomeGarbage(); 9712 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9713 CompileRun( 9714 "o.foo = 17;" 9715 "var receiver = {};" 9716 "receiver.__proto__ = o;" 9717 "var result = 0;" 9718 "for (var i = 0; i < 100; i++) {" 9719 " result = receiver.method(41);" 9720 "}"); 9721 9722 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 9723 } 9724 9725 THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) { 9726 v8::HandleScope scope; 9727 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9728 v8::Handle<v8::FunctionTemplate> method_templ = 9729 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 9730 v8_str("method_data"), 9731 v8::Signature::New(fun_templ)); 9732 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9733 proto_templ->Set(v8_str("method"), method_templ); 9734 v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); 9735 CHECK(!templ.IsEmpty()); 9736 LocalContext context; 9737 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9738 GenerateSomeGarbage(); 9739 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9740 CompileRun( 9741 "o.foo = 17;" 9742 "var receiver = {};" 9743 "receiver.__proto__ = o;" 9744 "var result = 0;" 9745 "var saved_result = 0;" 9746 "for (var i = 0; i < 100; i++) {" 9747 " result = receiver.method(41);" 9748 " if (i == 50) {" 9749 " saved_result = result;" 9750 " receiver = {method: function(x) { return x - 1 }};" 9751 " }" 9752 "}"); 9753 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 9754 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 9755 } 9756 9757 THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) { 9758 v8::HandleScope scope; 9759 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9760 v8::Handle<v8::FunctionTemplate> method_templ = 9761 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 9762 v8_str("method_data"), 9763 v8::Signature::New(fun_templ)); 9764 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9765 proto_templ->Set(v8_str("method"), method_templ); 9766 v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); 9767 CHECK(!templ.IsEmpty()); 9768 LocalContext context; 9769 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9770 GenerateSomeGarbage(); 9771 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9772 v8::TryCatch try_catch; 9773 CompileRun( 9774 "o.foo = 17;" 9775 "var receiver = {};" 9776 "receiver.__proto__ = o;" 9777 "var result = 0;" 9778 "var saved_result = 0;" 9779 "for (var i = 0; i < 100; i++) {" 9780 " result = receiver.method(41);" 9781 " if (i == 50) {" 9782 " saved_result = result;" 9783 " receiver = 333;" 9784 " }" 9785 "}"); 9786 CHECK(try_catch.HasCaught()); 9787 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), 9788 try_catch.Exception()->ToString()); 9789 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 9790 } 9791 9792 9793 v8::Handle<Value> keyed_call_ic_function; 9794 9795 static v8::Handle<Value> InterceptorKeyedCallICGetter( 9796 Local<String> name, const AccessorInfo& info) { 9797 ApiTestFuzzer::Fuzz(); 9798 if (v8_str("x")->Equals(name)) { 9799 return keyed_call_ic_function; 9800 } 9801 return v8::Handle<Value>(); 9802 } 9803 9804 9805 // Test the case when we stored cacheable lookup into 9806 // a stub, but the function name changed (to another cacheable function). 9807 THREADED_TEST(InterceptorKeyedCallICKeyChange1) { 9808 v8::HandleScope scope; 9809 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9810 templ->SetNamedPropertyHandler(NoBlockGetterX); 9811 LocalContext context; 9812 context->Global()->Set(v8_str("o"), templ->NewInstance()); 9813 CompileRun( 9814 "proto = new Object();" 9815 "proto.y = function(x) { return x + 1; };" 9816 "proto.z = function(x) { return x - 1; };" 9817 "o.__proto__ = proto;" 9818 "var result = 0;" 9819 "var method = 'y';" 9820 "for (var i = 0; i < 10; i++) {" 9821 " if (i == 5) { method = 'z'; };" 9822 " result += o[method](41);" 9823 "}"); 9824 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 9825 } 9826 9827 9828 // Test the case when we stored cacheable lookup into 9829 // a stub, but the function name changed (and the new function is present 9830 // both before and after the interceptor in the prototype chain). 9831 THREADED_TEST(InterceptorKeyedCallICKeyChange2) { 9832 v8::HandleScope scope; 9833 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9834 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter); 9835 LocalContext context; 9836 context->Global()->Set(v8_str("proto1"), templ->NewInstance()); 9837 keyed_call_ic_function = 9838 v8_compile("function f(x) { return x - 1; }; f")->Run(); 9839 CompileRun( 9840 "o = new Object();" 9841 "proto2 = new Object();" 9842 "o.y = function(x) { return x + 1; };" 9843 "proto2.y = function(x) { return x + 2; };" 9844 "o.__proto__ = proto1;" 9845 "proto1.__proto__ = proto2;" 9846 "var result = 0;" 9847 "var method = 'x';" 9848 "for (var i = 0; i < 10; i++) {" 9849 " if (i == 5) { method = 'y'; };" 9850 " result += o[method](41);" 9851 "}"); 9852 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 9853 } 9854 9855 9856 // Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit 9857 // on the global object. 9858 THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) { 9859 v8::HandleScope scope; 9860 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9861 templ->SetNamedPropertyHandler(NoBlockGetterX); 9862 LocalContext context; 9863 context->Global()->Set(v8_str("o"), templ->NewInstance()); 9864 CompileRun( 9865 "function inc(x) { return x + 1; };" 9866 "inc(1);" 9867 "function dec(x) { return x - 1; };" 9868 "dec(1);" 9869 "o.__proto__ = this;" 9870 "this.__proto__.x = inc;" 9871 "this.__proto__.y = dec;" 9872 "var result = 0;" 9873 "var method = 'x';" 9874 "for (var i = 0; i < 10; i++) {" 9875 " if (i == 5) { method = 'y'; };" 9876 " result += o[method](41);" 9877 "}"); 9878 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 9879 } 9880 9881 9882 // Test the case when actual function to call sits on global object. 9883 THREADED_TEST(InterceptorKeyedCallICFromGlobal) { 9884 v8::HandleScope scope; 9885 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 9886 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 9887 LocalContext context; 9888 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 9889 9890 CompileRun( 9891 "function len(x) { return x.length; };" 9892 "o.__proto__ = this;" 9893 "var m = 'parseFloat';" 9894 "var result = 0;" 9895 "for (var i = 0; i < 10; i++) {" 9896 " if (i == 5) {" 9897 " m = 'len';" 9898 " saved_result = result;" 9899 " };" 9900 " result = o[m]('239');" 9901 "}"); 9902 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value()); 9903 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 9904 } 9905 9906 // Test the map transition before the interceptor. 9907 THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) { 9908 v8::HandleScope scope; 9909 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 9910 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 9911 LocalContext context; 9912 context->Global()->Set(v8_str("proto"), templ_o->NewInstance()); 9913 9914 CompileRun( 9915 "var o = new Object();" 9916 "o.__proto__ = proto;" 9917 "o.method = function(x) { return x + 1; };" 9918 "var m = 'method';" 9919 "var result = 0;" 9920 "for (var i = 0; i < 10; i++) {" 9921 " if (i == 5) { o.method = function(x) { return x - 1; }; };" 9922 " result += o[m](41);" 9923 "}"); 9924 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 9925 } 9926 9927 9928 // Test the map transition after the interceptor. 9929 THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) { 9930 v8::HandleScope scope; 9931 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 9932 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 9933 LocalContext context; 9934 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 9935 9936 CompileRun( 9937 "var proto = new Object();" 9938 "o.__proto__ = proto;" 9939 "proto.method = function(x) { return x + 1; };" 9940 "var m = 'method';" 9941 "var result = 0;" 9942 "for (var i = 0; i < 10; i++) {" 9943 " if (i == 5) { proto.method = function(x) { return x - 1; }; };" 9944 " result += o[m](41);" 9945 "}"); 9946 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 9947 } 9948 9949 9950 static int interceptor_call_count = 0; 9951 9952 static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name, 9953 const AccessorInfo& info) { 9954 ApiTestFuzzer::Fuzz(); 9955 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) { 9956 return call_ic_function2; 9957 } 9958 return v8::Handle<Value>(); 9959 } 9960 9961 9962 // This test should hit load and call ICs for the interceptor case. 9963 // Once in a while, the interceptor will reply that a property was not 9964 // found in which case we should get a reference error. 9965 THREADED_TEST(InterceptorICReferenceErrors) { 9966 v8::HandleScope scope; 9967 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9968 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter); 9969 LocalContext context(0, templ, v8::Handle<Value>()); 9970 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run(); 9971 v8::Handle<Value> value = CompileRun( 9972 "function f() {" 9973 " for (var i = 0; i < 1000; i++) {" 9974 " try { x; } catch(e) { return true; }" 9975 " }" 9976 " return false;" 9977 "};" 9978 "f();"); 9979 CHECK_EQ(true, value->BooleanValue()); 9980 interceptor_call_count = 0; 9981 value = CompileRun( 9982 "function g() {" 9983 " for (var i = 0; i < 1000; i++) {" 9984 " try { x(42); } catch(e) { return true; }" 9985 " }" 9986 " return false;" 9987 "};" 9988 "g();"); 9989 CHECK_EQ(true, value->BooleanValue()); 9990 } 9991 9992 9993 static int interceptor_ic_exception_get_count = 0; 9994 9995 static v8::Handle<Value> InterceptorICExceptionGetter( 9996 Local<String> name, 9997 const AccessorInfo& info) { 9998 ApiTestFuzzer::Fuzz(); 9999 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) { 10000 return call_ic_function3; 10001 } 10002 if (interceptor_ic_exception_get_count == 20) { 10003 return v8::ThrowException(v8_num(42)); 10004 } 10005 // Do not handle get for properties other than x. 10006 return v8::Handle<Value>(); 10007 } 10008 10009 // Test interceptor load/call IC where the interceptor throws an 10010 // exception once in a while. 10011 THREADED_TEST(InterceptorICGetterExceptions) { 10012 interceptor_ic_exception_get_count = 0; 10013 v8::HandleScope scope; 10014 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 10015 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter); 10016 LocalContext context(0, templ, v8::Handle<Value>()); 10017 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run(); 10018 v8::Handle<Value> value = CompileRun( 10019 "function f() {" 10020 " for (var i = 0; i < 100; i++) {" 10021 " try { x; } catch(e) { return true; }" 10022 " }" 10023 " return false;" 10024 "};" 10025 "f();"); 10026 CHECK_EQ(true, value->BooleanValue()); 10027 interceptor_ic_exception_get_count = 0; 10028 value = CompileRun( 10029 "function f() {" 10030 " for (var i = 0; i < 100; i++) {" 10031 " try { x(42); } catch(e) { return true; }" 10032 " }" 10033 " return false;" 10034 "};" 10035 "f();"); 10036 CHECK_EQ(true, value->BooleanValue()); 10037 } 10038 10039 10040 static int interceptor_ic_exception_set_count = 0; 10041 10042 static v8::Handle<Value> InterceptorICExceptionSetter( 10043 Local<String> key, Local<Value> value, const AccessorInfo&) { 10044 ApiTestFuzzer::Fuzz(); 10045 if (++interceptor_ic_exception_set_count > 20) { 10046 return v8::ThrowException(v8_num(42)); 10047 } 10048 // Do not actually handle setting. 10049 return v8::Handle<Value>(); 10050 } 10051 10052 // Test interceptor store IC where the interceptor throws an exception 10053 // once in a while. 10054 THREADED_TEST(InterceptorICSetterExceptions) { 10055 interceptor_ic_exception_set_count = 0; 10056 v8::HandleScope scope; 10057 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 10058 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter); 10059 LocalContext context(0, templ, v8::Handle<Value>()); 10060 v8::Handle<Value> value = CompileRun( 10061 "function f() {" 10062 " for (var i = 0; i < 100; i++) {" 10063 " try { x = 42; } catch(e) { return true; }" 10064 " }" 10065 " return false;" 10066 "};" 10067 "f();"); 10068 CHECK_EQ(true, value->BooleanValue()); 10069 } 10070 10071 10072 // Test that we ignore null interceptors. 10073 THREADED_TEST(NullNamedInterceptor) { 10074 v8::HandleScope scope; 10075 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 10076 templ->SetNamedPropertyHandler(0); 10077 LocalContext context; 10078 templ->Set("x", v8_num(42)); 10079 v8::Handle<v8::Object> obj = templ->NewInstance(); 10080 context->Global()->Set(v8_str("obj"), obj); 10081 v8::Handle<Value> value = CompileRun("obj.x"); 10082 CHECK(value->IsInt32()); 10083 CHECK_EQ(42, value->Int32Value()); 10084 } 10085 10086 10087 // Test that we ignore null interceptors. 10088 THREADED_TEST(NullIndexedInterceptor) { 10089 v8::HandleScope scope; 10090 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 10091 templ->SetIndexedPropertyHandler(0); 10092 LocalContext context; 10093 templ->Set("42", v8_num(42)); 10094 v8::Handle<v8::Object> obj = templ->NewInstance(); 10095 context->Global()->Set(v8_str("obj"), obj); 10096 v8::Handle<Value> value = CompileRun("obj[42]"); 10097 CHECK(value->IsInt32()); 10098 CHECK_EQ(42, value->Int32Value()); 10099 } 10100 10101 10102 THREADED_TEST(NamedPropertyHandlerGetterAttributes) { 10103 v8::HandleScope scope; 10104 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 10105 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter); 10106 LocalContext env; 10107 env->Global()->Set(v8_str("obj"), 10108 templ->GetFunction()->NewInstance()); 10109 ExpectTrue("obj.x === 42"); 10110 ExpectTrue("!obj.propertyIsEnumerable('x')"); 10111 } 10112 10113 10114 static Handle<Value> ThrowingGetter(Local<String> name, 10115 const AccessorInfo& info) { 10116 ApiTestFuzzer::Fuzz(); 10117 ThrowException(Handle<Value>()); 10118 return Undefined(); 10119 } 10120 10121 10122 THREADED_TEST(VariousGetPropertiesAndThrowingCallbacks) { 10123 HandleScope scope; 10124 LocalContext context; 10125 10126 Local<FunctionTemplate> templ = FunctionTemplate::New(); 10127 Local<ObjectTemplate> instance_templ = templ->InstanceTemplate(); 10128 instance_templ->SetAccessor(v8_str("f"), ThrowingGetter); 10129 10130 Local<Object> instance = templ->GetFunction()->NewInstance(); 10131 10132 Local<Object> another = Object::New(); 10133 another->SetPrototype(instance); 10134 10135 Local<Object> with_js_getter = CompileRun( 10136 "o = {};\n" 10137 "o.__defineGetter__('f', function() { throw undefined; });\n" 10138 "o\n").As<Object>(); 10139 CHECK(!with_js_getter.IsEmpty()); 10140 10141 TryCatch try_catch; 10142 10143 Local<Value> result = instance->GetRealNamedProperty(v8_str("f")); 10144 CHECK(try_catch.HasCaught()); 10145 try_catch.Reset(); 10146 CHECK(result.IsEmpty()); 10147 10148 result = another->GetRealNamedProperty(v8_str("f")); 10149 CHECK(try_catch.HasCaught()); 10150 try_catch.Reset(); 10151 CHECK(result.IsEmpty()); 10152 10153 result = another->GetRealNamedPropertyInPrototypeChain(v8_str("f")); 10154 CHECK(try_catch.HasCaught()); 10155 try_catch.Reset(); 10156 CHECK(result.IsEmpty()); 10157 10158 result = another->Get(v8_str("f")); 10159 CHECK(try_catch.HasCaught()); 10160 try_catch.Reset(); 10161 CHECK(result.IsEmpty()); 10162 10163 result = with_js_getter->GetRealNamedProperty(v8_str("f")); 10164 CHECK(try_catch.HasCaught()); 10165 try_catch.Reset(); 10166 CHECK(result.IsEmpty()); 10167 10168 result = with_js_getter->Get(v8_str("f")); 10169 CHECK(try_catch.HasCaught()); 10170 try_catch.Reset(); 10171 CHECK(result.IsEmpty()); 10172 } 10173 10174 10175 static Handle<Value> ThrowingCallbackWithTryCatch(const Arguments& args) { 10176 TryCatch try_catch; 10177 // Verboseness is important: it triggers message delivery which can call into 10178 // external code. 10179 try_catch.SetVerbose(true); 10180 CompileRun("throw 'from JS';"); 10181 CHECK(try_catch.HasCaught()); 10182 CHECK(!i::Isolate::Current()->has_pending_exception()); 10183 CHECK(!i::Isolate::Current()->has_scheduled_exception()); 10184 return Undefined(); 10185 } 10186 10187 10188 static int call_depth; 10189 10190 10191 static void WithTryCatch(Handle<Message> message, Handle<Value> data) { 10192 TryCatch try_catch; 10193 } 10194 10195 10196 static void ThrowFromJS(Handle<Message> message, Handle<Value> data) { 10197 if (--call_depth) CompileRun("throw 'ThrowInJS';"); 10198 } 10199 10200 10201 static void ThrowViaApi(Handle<Message> message, Handle<Value> data) { 10202 if (--call_depth) ThrowException(v8_str("ThrowViaApi")); 10203 } 10204 10205 10206 static void WebKitLike(Handle<Message> message, Handle<Value> data) { 10207 Handle<String> errorMessageString = message->Get(); 10208 CHECK(!errorMessageString.IsEmpty()); 10209 message->GetStackTrace(); 10210 message->GetScriptResourceName(); 10211 } 10212 10213 THREADED_TEST(ExceptionsDoNotPropagatePastTryCatch) { 10214 HandleScope scope; 10215 LocalContext context; 10216 10217 Local<Function> func = 10218 FunctionTemplate::New(ThrowingCallbackWithTryCatch)->GetFunction(); 10219 context->Global()->Set(v8_str("func"), func); 10220 10221 MessageCallback callbacks[] = 10222 { NULL, WebKitLike, ThrowViaApi, ThrowFromJS, WithTryCatch }; 10223 for (unsigned i = 0; i < sizeof(callbacks)/sizeof(callbacks[0]); i++) { 10224 MessageCallback callback = callbacks[i]; 10225 if (callback != NULL) { 10226 V8::AddMessageListener(callback); 10227 } 10228 // Some small number to control number of times message handler should 10229 // throw an exception. 10230 call_depth = 5; 10231 ExpectFalse( 10232 "var thrown = false;\n" 10233 "try { func(); } catch(e) { thrown = true; }\n" 10234 "thrown\n"); 10235 if (callback != NULL) { 10236 V8::RemoveMessageListeners(callback); 10237 } 10238 } 10239 } 10240 10241 10242 static v8::Handle<Value> ParentGetter(Local<String> name, 10243 const AccessorInfo& info) { 10244 ApiTestFuzzer::Fuzz(); 10245 return v8_num(1); 10246 } 10247 10248 10249 static v8::Handle<Value> ChildGetter(Local<String> name, 10250 const AccessorInfo& info) { 10251 ApiTestFuzzer::Fuzz(); 10252 return v8_num(42); 10253 } 10254 10255 10256 THREADED_TEST(Overriding) { 10257 v8::HandleScope scope; 10258 LocalContext context; 10259 10260 // Parent template. 10261 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New(); 10262 Local<ObjectTemplate> parent_instance_templ = 10263 parent_templ->InstanceTemplate(); 10264 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter); 10265 10266 // Template that inherits from the parent template. 10267 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New(); 10268 Local<ObjectTemplate> child_instance_templ = 10269 child_templ->InstanceTemplate(); 10270 child_templ->Inherit(parent_templ); 10271 // Override 'f'. The child version of 'f' should get called for child 10272 // instances. 10273 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter); 10274 // Add 'g' twice. The 'g' added last should get called for instances. 10275 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter); 10276 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter); 10277 10278 // Add 'h' as an accessor to the proto template with ReadOnly attributes 10279 // so 'h' can be shadowed on the instance object. 10280 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate(); 10281 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0, 10282 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 10283 10284 // Add 'i' as an accessor to the instance template with ReadOnly attributes 10285 // but the attribute does not have effect because it is duplicated with 10286 // NULL setter. 10287 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0, 10288 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 10289 10290 10291 10292 // Instantiate the child template. 10293 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance(); 10294 10295 // Check that the child function overrides the parent one. 10296 context->Global()->Set(v8_str("o"), instance); 10297 Local<Value> value = v8_compile("o.f")->Run(); 10298 // Check that the 'g' that was added last is hit. 10299 CHECK_EQ(42, value->Int32Value()); 10300 value = v8_compile("o.g")->Run(); 10301 CHECK_EQ(42, value->Int32Value()); 10302 10303 // Check 'h' can be shadowed. 10304 value = v8_compile("o.h = 3; o.h")->Run(); 10305 CHECK_EQ(3, value->Int32Value()); 10306 10307 // Check 'i' is cannot be shadowed or changed. 10308 value = v8_compile("o.i = 3; o.i")->Run(); 10309 CHECK_EQ(42, value->Int32Value()); 10310 } 10311 10312 10313 static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) { 10314 ApiTestFuzzer::Fuzz(); 10315 return v8::Boolean::New(args.IsConstructCall()); 10316 } 10317 10318 10319 THREADED_TEST(IsConstructCall) { 10320 v8::HandleScope scope; 10321 10322 // Function template with call handler. 10323 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 10324 templ->SetCallHandler(IsConstructHandler); 10325 10326 LocalContext context; 10327 10328 context->Global()->Set(v8_str("f"), templ->GetFunction()); 10329 Local<Value> value = v8_compile("f()")->Run(); 10330 CHECK(!value->BooleanValue()); 10331 value = v8_compile("new f()")->Run(); 10332 CHECK(value->BooleanValue()); 10333 } 10334 10335 10336 THREADED_TEST(ObjectProtoToString) { 10337 v8::HandleScope scope; 10338 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 10339 templ->SetClassName(v8_str("MyClass")); 10340 10341 LocalContext context; 10342 10343 Local<String> customized_tostring = v8_str("customized toString"); 10344 10345 // Replace Object.prototype.toString 10346 v8_compile("Object.prototype.toString = function() {" 10347 " return 'customized toString';" 10348 "}")->Run(); 10349 10350 // Normal ToString call should call replaced Object.prototype.toString 10351 Local<v8::Object> instance = templ->GetFunction()->NewInstance(); 10352 Local<String> value = instance->ToString(); 10353 CHECK(value->IsString() && value->Equals(customized_tostring)); 10354 10355 // ObjectProtoToString should not call replace toString function. 10356 value = instance->ObjectProtoToString(); 10357 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]"))); 10358 10359 // Check global 10360 value = context->Global()->ObjectProtoToString(); 10361 CHECK(value->IsString() && value->Equals(v8_str("[object global]"))); 10362 10363 // Check ordinary object 10364 Local<Value> object = v8_compile("new Object()")->Run(); 10365 value = object.As<v8::Object>()->ObjectProtoToString(); 10366 CHECK(value->IsString() && value->Equals(v8_str("[object Object]"))); 10367 } 10368 10369 10370 THREADED_TEST(ObjectGetConstructorName) { 10371 v8::HandleScope scope; 10372 LocalContext context; 10373 v8_compile("function Parent() {};" 10374 "function Child() {};" 10375 "Child.prototype = new Parent();" 10376 "var outer = { inner: function() { } };" 10377 "var p = new Parent();" 10378 "var c = new Child();" 10379 "var x = new outer.inner();")->Run(); 10380 10381 Local<v8::Value> p = context->Global()->Get(v8_str("p")); 10382 CHECK(p->IsObject() && p->ToObject()->GetConstructorName()->Equals( 10383 v8_str("Parent"))); 10384 10385 Local<v8::Value> c = context->Global()->Get(v8_str("c")); 10386 CHECK(c->IsObject() && c->ToObject()->GetConstructorName()->Equals( 10387 v8_str("Child"))); 10388 10389 Local<v8::Value> x = context->Global()->Get(v8_str("x")); 10390 CHECK(x->IsObject() && x->ToObject()->GetConstructorName()->Equals( 10391 v8_str("outer.inner"))); 10392 } 10393 10394 10395 bool ApiTestFuzzer::fuzzing_ = false; 10396 i::Semaphore* ApiTestFuzzer::all_tests_done_= 10397 i::OS::CreateSemaphore(0); 10398 int ApiTestFuzzer::active_tests_; 10399 int ApiTestFuzzer::tests_being_run_; 10400 int ApiTestFuzzer::current_; 10401 10402 10403 // We are in a callback and want to switch to another thread (if we 10404 // are currently running the thread fuzzing test). 10405 void ApiTestFuzzer::Fuzz() { 10406 if (!fuzzing_) return; 10407 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_; 10408 test->ContextSwitch(); 10409 } 10410 10411 10412 // Let the next thread go. Since it is also waiting on the V8 lock it may 10413 // not start immediately. 10414 bool ApiTestFuzzer::NextThread() { 10415 int test_position = GetNextTestNumber(); 10416 const char* test_name = RegisterThreadedTest::nth(current_)->name(); 10417 if (test_position == current_) { 10418 if (kLogThreading) 10419 printf("Stay with %s\n", test_name); 10420 return false; 10421 } 10422 if (kLogThreading) { 10423 printf("Switch from %s to %s\n", 10424 test_name, 10425 RegisterThreadedTest::nth(test_position)->name()); 10426 } 10427 current_ = test_position; 10428 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal(); 10429 return true; 10430 } 10431 10432 10433 void ApiTestFuzzer::Run() { 10434 // When it is our turn... 10435 gate_->Wait(); 10436 { 10437 // ... get the V8 lock and start running the test. 10438 v8::Locker locker; 10439 CallTest(); 10440 } 10441 // This test finished. 10442 active_ = false; 10443 active_tests_--; 10444 // If it was the last then signal that fact. 10445 if (active_tests_ == 0) { 10446 all_tests_done_->Signal(); 10447 } else { 10448 // Otherwise select a new test and start that. 10449 NextThread(); 10450 } 10451 } 10452 10453 10454 static unsigned linear_congruential_generator; 10455 10456 10457 void ApiTestFuzzer::SetUp(PartOfTest part) { 10458 linear_congruential_generator = i::FLAG_testing_prng_seed; 10459 fuzzing_ = true; 10460 int count = RegisterThreadedTest::count(); 10461 int start = count * part / (LAST_PART + 1); 10462 int end = (count * (part + 1) / (LAST_PART + 1)) - 1; 10463 active_tests_ = tests_being_run_ = end - start + 1; 10464 for (int i = 0; i < tests_being_run_; i++) { 10465 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start); 10466 } 10467 for (int i = 0; i < active_tests_; i++) { 10468 RegisterThreadedTest::nth(i)->fuzzer_->Start(); 10469 } 10470 } 10471 10472 10473 static void CallTestNumber(int test_number) { 10474 (RegisterThreadedTest::nth(test_number)->callback())(); 10475 } 10476 10477 10478 void ApiTestFuzzer::RunAllTests() { 10479 // Set off the first test. 10480 current_ = -1; 10481 NextThread(); 10482 // Wait till they are all done. 10483 all_tests_done_->Wait(); 10484 } 10485 10486 10487 int ApiTestFuzzer::GetNextTestNumber() { 10488 int next_test; 10489 do { 10490 next_test = (linear_congruential_generator >> 16) % tests_being_run_; 10491 linear_congruential_generator *= 1664525u; 10492 linear_congruential_generator += 1013904223u; 10493 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_); 10494 return next_test; 10495 } 10496 10497 10498 void ApiTestFuzzer::ContextSwitch() { 10499 // If the new thread is the same as the current thread there is nothing to do. 10500 if (NextThread()) { 10501 // Now it can start. 10502 v8::Unlocker unlocker; 10503 // Wait till someone starts us again. 10504 gate_->Wait(); 10505 // And we're off. 10506 } 10507 } 10508 10509 10510 void ApiTestFuzzer::TearDown() { 10511 fuzzing_ = false; 10512 for (int i = 0; i < RegisterThreadedTest::count(); i++) { 10513 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_; 10514 if (fuzzer != NULL) fuzzer->Join(); 10515 } 10516 } 10517 10518 10519 // Lets not be needlessly self-referential. 10520 TEST(Threading) { 10521 ApiTestFuzzer::SetUp(ApiTestFuzzer::FIRST_PART); 10522 ApiTestFuzzer::RunAllTests(); 10523 ApiTestFuzzer::TearDown(); 10524 } 10525 10526 TEST(Threading2) { 10527 ApiTestFuzzer::SetUp(ApiTestFuzzer::SECOND_PART); 10528 ApiTestFuzzer::RunAllTests(); 10529 ApiTestFuzzer::TearDown(); 10530 } 10531 10532 TEST(Threading3) { 10533 ApiTestFuzzer::SetUp(ApiTestFuzzer::THIRD_PART); 10534 ApiTestFuzzer::RunAllTests(); 10535 ApiTestFuzzer::TearDown(); 10536 } 10537 10538 TEST(Threading4) { 10539 ApiTestFuzzer::SetUp(ApiTestFuzzer::FOURTH_PART); 10540 ApiTestFuzzer::RunAllTests(); 10541 ApiTestFuzzer::TearDown(); 10542 } 10543 10544 void ApiTestFuzzer::CallTest() { 10545 if (kLogThreading) 10546 printf("Start test %d\n", test_number_); 10547 CallTestNumber(test_number_); 10548 if (kLogThreading) 10549 printf("End test %d\n", test_number_); 10550 } 10551 10552 10553 static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) { 10554 CHECK(v8::Locker::IsLocked()); 10555 ApiTestFuzzer::Fuzz(); 10556 v8::Unlocker unlocker; 10557 const char* code = "throw 7;"; 10558 { 10559 v8::Locker nested_locker; 10560 v8::HandleScope scope; 10561 v8::Handle<Value> exception; 10562 { v8::TryCatch try_catch; 10563 v8::Handle<Value> value = CompileRun(code); 10564 CHECK(value.IsEmpty()); 10565 CHECK(try_catch.HasCaught()); 10566 // Make sure to wrap the exception in a new handle because 10567 // the handle returned from the TryCatch is destroyed 10568 // when the TryCatch is destroyed. 10569 exception = Local<Value>::New(try_catch.Exception()); 10570 } 10571 return v8::ThrowException(exception); 10572 } 10573 } 10574 10575 10576 static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) { 10577 CHECK(v8::Locker::IsLocked()); 10578 ApiTestFuzzer::Fuzz(); 10579 v8::Unlocker unlocker; 10580 const char* code = "throw 7;"; 10581 { 10582 v8::Locker nested_locker; 10583 v8::HandleScope scope; 10584 v8::Handle<Value> value = CompileRun(code); 10585 CHECK(value.IsEmpty()); 10586 return v8_str("foo"); 10587 } 10588 } 10589 10590 10591 // These are locking tests that don't need to be run again 10592 // as part of the locking aggregation tests. 10593 TEST(NestedLockers) { 10594 v8::Locker locker; 10595 CHECK(v8::Locker::IsLocked()); 10596 v8::HandleScope scope; 10597 LocalContext env; 10598 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS); 10599 Local<Function> fun = fun_templ->GetFunction(); 10600 env->Global()->Set(v8_str("throw_in_js"), fun); 10601 Local<Script> script = v8_compile("(function () {" 10602 " try {" 10603 " throw_in_js();" 10604 " return 42;" 10605 " } catch (e) {" 10606 " return e * 13;" 10607 " }" 10608 "})();"); 10609 CHECK_EQ(91, script->Run()->Int32Value()); 10610 } 10611 10612 10613 // These are locking tests that don't need to be run again 10614 // as part of the locking aggregation tests. 10615 TEST(NestedLockersNoTryCatch) { 10616 v8::Locker locker; 10617 v8::HandleScope scope; 10618 LocalContext env; 10619 Local<v8::FunctionTemplate> fun_templ = 10620 v8::FunctionTemplate::New(ThrowInJSNoCatch); 10621 Local<Function> fun = fun_templ->GetFunction(); 10622 env->Global()->Set(v8_str("throw_in_js"), fun); 10623 Local<Script> script = v8_compile("(function () {" 10624 " try {" 10625 " throw_in_js();" 10626 " return 42;" 10627 " } catch (e) {" 10628 " return e * 13;" 10629 " }" 10630 "})();"); 10631 CHECK_EQ(91, script->Run()->Int32Value()); 10632 } 10633 10634 10635 THREADED_TEST(RecursiveLocking) { 10636 v8::Locker locker; 10637 { 10638 v8::Locker locker2; 10639 CHECK(v8::Locker::IsLocked()); 10640 } 10641 } 10642 10643 10644 static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) { 10645 ApiTestFuzzer::Fuzz(); 10646 v8::Unlocker unlocker; 10647 return v8::Undefined(); 10648 } 10649 10650 10651 THREADED_TEST(LockUnlockLock) { 10652 { 10653 v8::Locker locker; 10654 v8::HandleScope scope; 10655 LocalContext env; 10656 Local<v8::FunctionTemplate> fun_templ = 10657 v8::FunctionTemplate::New(UnlockForAMoment); 10658 Local<Function> fun = fun_templ->GetFunction(); 10659 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 10660 Local<Script> script = v8_compile("(function () {" 10661 " unlock_for_a_moment();" 10662 " return 42;" 10663 "})();"); 10664 CHECK_EQ(42, script->Run()->Int32Value()); 10665 } 10666 { 10667 v8::Locker locker; 10668 v8::HandleScope scope; 10669 LocalContext env; 10670 Local<v8::FunctionTemplate> fun_templ = 10671 v8::FunctionTemplate::New(UnlockForAMoment); 10672 Local<Function> fun = fun_templ->GetFunction(); 10673 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 10674 Local<Script> script = v8_compile("(function () {" 10675 " unlock_for_a_moment();" 10676 " return 42;" 10677 "})();"); 10678 CHECK_EQ(42, script->Run()->Int32Value()); 10679 } 10680 } 10681 10682 10683 static int GetGlobalObjectsCount() { 10684 i::Isolate::Current()->heap()->EnsureHeapIsIterable(); 10685 int count = 0; 10686 i::HeapIterator it; 10687 for (i::HeapObject* object = it.next(); object != NULL; object = it.next()) 10688 if (object->IsJSGlobalObject()) count++; 10689 return count; 10690 } 10691 10692 10693 static void CheckSurvivingGlobalObjectsCount(int expected) { 10694 // We need to collect all garbage twice to be sure that everything 10695 // has been collected. This is because inline caches are cleared in 10696 // the first garbage collection but some of the maps have already 10697 // been marked at that point. Therefore some of the maps are not 10698 // collected until the second garbage collection. 10699 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 10700 HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); 10701 int count = GetGlobalObjectsCount(); 10702 #ifdef DEBUG 10703 if (count != expected) HEAP->TracePathToGlobal(); 10704 #endif 10705 CHECK_EQ(expected, count); 10706 } 10707 10708 10709 TEST(DontLeakGlobalObjects) { 10710 // Regression test for issues 1139850 and 1174891. 10711 10712 v8::V8::Initialize(); 10713 10714 for (int i = 0; i < 5; i++) { 10715 { v8::HandleScope scope; 10716 LocalContext context; 10717 } 10718 CheckSurvivingGlobalObjectsCount(0); 10719 10720 { v8::HandleScope scope; 10721 LocalContext context; 10722 v8_compile("Date")->Run(); 10723 } 10724 CheckSurvivingGlobalObjectsCount(0); 10725 10726 { v8::HandleScope scope; 10727 LocalContext context; 10728 v8_compile("/aaa/")->Run(); 10729 } 10730 CheckSurvivingGlobalObjectsCount(0); 10731 10732 { v8::HandleScope scope; 10733 const char* extension_list[] = { "v8/gc" }; 10734 v8::ExtensionConfiguration extensions(1, extension_list); 10735 LocalContext context(&extensions); 10736 v8_compile("gc();")->Run(); 10737 } 10738 CheckSurvivingGlobalObjectsCount(0); 10739 } 10740 } 10741 10742 10743 v8::Persistent<v8::Object> some_object; 10744 v8::Persistent<v8::Object> bad_handle; 10745 10746 void NewPersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) { 10747 v8::HandleScope scope; 10748 bad_handle = v8::Persistent<v8::Object>::New(some_object); 10749 handle.Dispose(); 10750 } 10751 10752 10753 THREADED_TEST(NewPersistentHandleFromWeakCallback) { 10754 LocalContext context; 10755 10756 v8::Persistent<v8::Object> handle1, handle2; 10757 { 10758 v8::HandleScope scope; 10759 some_object = v8::Persistent<v8::Object>::New(v8::Object::New()); 10760 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 10761 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 10762 } 10763 // Note: order is implementation dependent alas: currently 10764 // global handle nodes are processed by PostGarbageCollectionProcessing 10765 // in reverse allocation order, so if second allocated handle is deleted, 10766 // weak callback of the first handle would be able to 'reallocate' it. 10767 handle1.MakeWeak(NULL, NewPersistentHandleCallback); 10768 handle2.Dispose(); 10769 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 10770 } 10771 10772 10773 v8::Persistent<v8::Object> to_be_disposed; 10774 10775 void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) { 10776 to_be_disposed.Dispose(); 10777 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 10778 handle.Dispose(); 10779 } 10780 10781 10782 THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) { 10783 LocalContext context; 10784 10785 v8::Persistent<v8::Object> handle1, handle2; 10786 { 10787 v8::HandleScope scope; 10788 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 10789 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 10790 } 10791 handle1.MakeWeak(NULL, DisposeAndForceGcCallback); 10792 to_be_disposed = handle2; 10793 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 10794 } 10795 10796 void DisposingCallback(v8::Persistent<v8::Value> handle, void*) { 10797 handle.Dispose(); 10798 } 10799 10800 void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) { 10801 v8::HandleScope scope; 10802 v8::Persistent<v8::Object>::New(v8::Object::New()); 10803 handle.Dispose(); 10804 } 10805 10806 10807 THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) { 10808 LocalContext context; 10809 10810 v8::Persistent<v8::Object> handle1, handle2, handle3; 10811 { 10812 v8::HandleScope scope; 10813 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New()); 10814 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 10815 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 10816 } 10817 handle2.MakeWeak(NULL, DisposingCallback); 10818 handle3.MakeWeak(NULL, HandleCreatingCallback); 10819 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 10820 } 10821 10822 10823 THREADED_TEST(CheckForCrossContextObjectLiterals) { 10824 v8::V8::Initialize(); 10825 10826 const int nof = 2; 10827 const char* sources[nof] = { 10828 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }", 10829 "Object()" 10830 }; 10831 10832 for (int i = 0; i < nof; i++) { 10833 const char* source = sources[i]; 10834 { v8::HandleScope scope; 10835 LocalContext context; 10836 CompileRun(source); 10837 } 10838 { v8::HandleScope scope; 10839 LocalContext context; 10840 CompileRun(source); 10841 } 10842 } 10843 } 10844 10845 10846 static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) { 10847 v8::HandleScope inner; 10848 env->Enter(); 10849 v8::Handle<Value> three = v8_num(3); 10850 v8::Handle<Value> value = inner.Close(three); 10851 env->Exit(); 10852 return value; 10853 } 10854 10855 10856 THREADED_TEST(NestedHandleScopeAndContexts) { 10857 v8::HandleScope outer; 10858 v8::Persistent<Context> env = Context::New(); 10859 env->Enter(); 10860 v8::Handle<Value> value = NestedScope(env); 10861 v8::Handle<String> str(value->ToString()); 10862 CHECK(!str.IsEmpty()); 10863 env->Exit(); 10864 env.Dispose(); 10865 } 10866 10867 10868 THREADED_TEST(ExternalAllocatedMemory) { 10869 v8::HandleScope outer; 10870 v8::Persistent<Context> env(Context::New()); 10871 CHECK(!env.IsEmpty()); 10872 const int kSize = 1024*1024; 10873 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize); 10874 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0); 10875 } 10876 10877 10878 THREADED_TEST(DisposeEnteredContext) { 10879 v8::HandleScope scope; 10880 LocalContext outer; 10881 { v8::Persistent<v8::Context> inner = v8::Context::New(); 10882 inner->Enter(); 10883 inner.Dispose(); 10884 inner.Clear(); 10885 inner->Exit(); 10886 } 10887 } 10888 10889 10890 // Regression test for issue 54, object templates with internal fields 10891 // but no accessors or interceptors did not get their internal field 10892 // count set on instances. 10893 THREADED_TEST(Regress54) { 10894 v8::HandleScope outer; 10895 LocalContext context; 10896 static v8::Persistent<v8::ObjectTemplate> templ; 10897 if (templ.IsEmpty()) { 10898 v8::HandleScope inner; 10899 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New(); 10900 local->SetInternalFieldCount(1); 10901 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local)); 10902 } 10903 v8::Handle<v8::Object> result = templ->NewInstance(); 10904 CHECK_EQ(1, result->InternalFieldCount()); 10905 } 10906 10907 10908 // If part of the threaded tests, this test makes ThreadingTest fail 10909 // on mac. 10910 TEST(CatchStackOverflow) { 10911 v8::HandleScope scope; 10912 LocalContext context; 10913 v8::TryCatch try_catch; 10914 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New( 10915 "function f() {" 10916 " return f();" 10917 "}" 10918 "" 10919 "f();")); 10920 v8::Handle<v8::Value> result = script->Run(); 10921 CHECK(result.IsEmpty()); 10922 } 10923 10924 10925 static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script, 10926 const char* resource_name, 10927 int line_offset) { 10928 v8::HandleScope scope; 10929 v8::TryCatch try_catch; 10930 v8::Handle<v8::Value> result = script->Run(); 10931 CHECK(result.IsEmpty()); 10932 CHECK(try_catch.HasCaught()); 10933 v8::Handle<v8::Message> message = try_catch.Message(); 10934 CHECK(!message.IsEmpty()); 10935 CHECK_EQ(10 + line_offset, message->GetLineNumber()); 10936 CHECK_EQ(91, message->GetStartPosition()); 10937 CHECK_EQ(92, message->GetEndPosition()); 10938 CHECK_EQ(2, message->GetStartColumn()); 10939 CHECK_EQ(3, message->GetEndColumn()); 10940 v8::String::AsciiValue line(message->GetSourceLine()); 10941 CHECK_EQ(" throw 'nirk';", *line); 10942 v8::String::AsciiValue name(message->GetScriptResourceName()); 10943 CHECK_EQ(resource_name, *name); 10944 } 10945 10946 10947 THREADED_TEST(TryCatchSourceInfo) { 10948 v8::HandleScope scope; 10949 LocalContext context; 10950 v8::Handle<v8::String> source = v8::String::New( 10951 "function Foo() {\n" 10952 " return Bar();\n" 10953 "}\n" 10954 "\n" 10955 "function Bar() {\n" 10956 " return Baz();\n" 10957 "}\n" 10958 "\n" 10959 "function Baz() {\n" 10960 " throw 'nirk';\n" 10961 "}\n" 10962 "\n" 10963 "Foo();\n"); 10964 10965 const char* resource_name; 10966 v8::Handle<v8::Script> script; 10967 resource_name = "test.js"; 10968 script = v8::Script::Compile(source, v8::String::New(resource_name)); 10969 CheckTryCatchSourceInfo(script, resource_name, 0); 10970 10971 resource_name = "test1.js"; 10972 v8::ScriptOrigin origin1(v8::String::New(resource_name)); 10973 script = v8::Script::Compile(source, &origin1); 10974 CheckTryCatchSourceInfo(script, resource_name, 0); 10975 10976 resource_name = "test2.js"; 10977 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7)); 10978 script = v8::Script::Compile(source, &origin2); 10979 CheckTryCatchSourceInfo(script, resource_name, 7); 10980 } 10981 10982 10983 THREADED_TEST(CompilationCache) { 10984 v8::HandleScope scope; 10985 LocalContext context; 10986 v8::Handle<v8::String> source0 = v8::String::New("1234"); 10987 v8::Handle<v8::String> source1 = v8::String::New("1234"); 10988 v8::Handle<v8::Script> script0 = 10989 v8::Script::Compile(source0, v8::String::New("test.js")); 10990 v8::Handle<v8::Script> script1 = 10991 v8::Script::Compile(source1, v8::String::New("test.js")); 10992 v8::Handle<v8::Script> script2 = 10993 v8::Script::Compile(source0); // different origin 10994 CHECK_EQ(1234, script0->Run()->Int32Value()); 10995 CHECK_EQ(1234, script1->Run()->Int32Value()); 10996 CHECK_EQ(1234, script2->Run()->Int32Value()); 10997 } 10998 10999 11000 static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) { 11001 ApiTestFuzzer::Fuzz(); 11002 return v8_num(42); 11003 } 11004 11005 11006 THREADED_TEST(CallbackFunctionName) { 11007 v8::HandleScope scope; 11008 LocalContext context; 11009 Local<ObjectTemplate> t = ObjectTemplate::New(); 11010 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback)); 11011 context->Global()->Set(v8_str("obj"), t->NewInstance()); 11012 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name"); 11013 CHECK(value->IsString()); 11014 v8::String::AsciiValue name(value); 11015 CHECK_EQ("asdf", *name); 11016 } 11017 11018 11019 THREADED_TEST(DateAccess) { 11020 v8::HandleScope scope; 11021 LocalContext context; 11022 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0); 11023 CHECK(date->IsDate()); 11024 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue()); 11025 } 11026 11027 11028 void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) { 11029 v8::Handle<v8::Object> obj = val.As<v8::Object>(); 11030 v8::Handle<v8::Array> props = obj->GetPropertyNames(); 11031 CHECK_EQ(elmc, props->Length()); 11032 for (int i = 0; i < elmc; i++) { 11033 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i))); 11034 CHECK_EQ(elmv[i], *elm); 11035 } 11036 } 11037 11038 11039 void CheckOwnProperties(v8::Handle<v8::Value> val, 11040 int elmc, 11041 const char* elmv[]) { 11042 v8::Handle<v8::Object> obj = val.As<v8::Object>(); 11043 v8::Handle<v8::Array> props = obj->GetOwnPropertyNames(); 11044 CHECK_EQ(elmc, props->Length()); 11045 for (int i = 0; i < elmc; i++) { 11046 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i))); 11047 CHECK_EQ(elmv[i], *elm); 11048 } 11049 } 11050 11051 11052 THREADED_TEST(PropertyEnumeration) { 11053 v8::HandleScope scope; 11054 LocalContext context; 11055 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New( 11056 "var result = [];" 11057 "result[0] = {};" 11058 "result[1] = {a: 1, b: 2};" 11059 "result[2] = [1, 2, 3];" 11060 "var proto = {x: 1, y: 2, z: 3};" 11061 "var x = { __proto__: proto, w: 0, z: 1 };" 11062 "result[3] = x;" 11063 "result;"))->Run(); 11064 v8::Handle<v8::Array> elms = obj.As<v8::Array>(); 11065 CHECK_EQ(4, elms->Length()); 11066 int elmc0 = 0; 11067 const char** elmv0 = NULL; 11068 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0); 11069 CheckOwnProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0); 11070 int elmc1 = 2; 11071 const char* elmv1[] = {"a", "b"}; 11072 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1); 11073 CheckOwnProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1); 11074 int elmc2 = 3; 11075 const char* elmv2[] = {"0", "1", "2"}; 11076 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2); 11077 CheckOwnProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2); 11078 int elmc3 = 4; 11079 const char* elmv3[] = {"w", "z", "x", "y"}; 11080 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3); 11081 int elmc4 = 2; 11082 const char* elmv4[] = {"w", "z"}; 11083 CheckOwnProperties(elms->Get(v8::Integer::New(3)), elmc4, elmv4); 11084 } 11085 11086 THREADED_TEST(PropertyEnumeration2) { 11087 v8::HandleScope scope; 11088 LocalContext context; 11089 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New( 11090 "var result = [];" 11091 "result[0] = {};" 11092 "result[1] = {a: 1, b: 2};" 11093 "result[2] = [1, 2, 3];" 11094 "var proto = {x: 1, y: 2, z: 3};" 11095 "var x = { __proto__: proto, w: 0, z: 1 };" 11096 "result[3] = x;" 11097 "result;"))->Run(); 11098 v8::Handle<v8::Array> elms = obj.As<v8::Array>(); 11099 CHECK_EQ(4, elms->Length()); 11100 int elmc0 = 0; 11101 const char** elmv0 = NULL; 11102 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0); 11103 11104 v8::Handle<v8::Value> val = elms->Get(v8::Integer::New(0)); 11105 v8::Handle<v8::Array> props = val.As<v8::Object>()->GetPropertyNames(); 11106 CHECK_EQ(0, props->Length()); 11107 for (uint32_t i = 0; i < props->Length(); i++) { 11108 printf("p[%d]\n", i); 11109 } 11110 } 11111 11112 static bool NamedSetAccessBlocker(Local<v8::Object> obj, 11113 Local<Value> name, 11114 v8::AccessType type, 11115 Local<Value> data) { 11116 return type != v8::ACCESS_SET; 11117 } 11118 11119 11120 static bool IndexedSetAccessBlocker(Local<v8::Object> obj, 11121 uint32_t key, 11122 v8::AccessType type, 11123 Local<Value> data) { 11124 return type != v8::ACCESS_SET; 11125 } 11126 11127 11128 THREADED_TEST(DisableAccessChecksWhileConfiguring) { 11129 v8::HandleScope scope; 11130 LocalContext context; 11131 Local<ObjectTemplate> templ = ObjectTemplate::New(); 11132 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker, 11133 IndexedSetAccessBlocker); 11134 templ->Set(v8_str("x"), v8::True()); 11135 Local<v8::Object> instance = templ->NewInstance(); 11136 context->Global()->Set(v8_str("obj"), instance); 11137 Local<Value> value = CompileRun("obj.x"); 11138 CHECK(value->BooleanValue()); 11139 } 11140 11141 11142 static bool NamedGetAccessBlocker(Local<v8::Object> obj, 11143 Local<Value> name, 11144 v8::AccessType type, 11145 Local<Value> data) { 11146 return false; 11147 } 11148 11149 11150 static bool IndexedGetAccessBlocker(Local<v8::Object> obj, 11151 uint32_t key, 11152 v8::AccessType type, 11153 Local<Value> data) { 11154 return false; 11155 } 11156 11157 11158 11159 THREADED_TEST(AccessChecksReenabledCorrectly) { 11160 v8::HandleScope scope; 11161 LocalContext context; 11162 Local<ObjectTemplate> templ = ObjectTemplate::New(); 11163 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker, 11164 IndexedGetAccessBlocker); 11165 templ->Set(v8_str("a"), v8_str("a")); 11166 // Add more than 8 (see kMaxFastProperties) properties 11167 // so that the constructor will force copying map. 11168 // Cannot sprintf, gcc complains unsafety. 11169 char buf[4]; 11170 for (char i = '0'; i <= '9' ; i++) { 11171 buf[0] = i; 11172 for (char j = '0'; j <= '9'; j++) { 11173 buf[1] = j; 11174 for (char k = '0'; k <= '9'; k++) { 11175 buf[2] = k; 11176 buf[3] = 0; 11177 templ->Set(v8_str(buf), v8::Number::New(k)); 11178 } 11179 } 11180 } 11181 11182 Local<v8::Object> instance_1 = templ->NewInstance(); 11183 context->Global()->Set(v8_str("obj_1"), instance_1); 11184 11185 Local<Value> value_1 = CompileRun("obj_1.a"); 11186 CHECK(value_1->IsUndefined()); 11187 11188 Local<v8::Object> instance_2 = templ->NewInstance(); 11189 context->Global()->Set(v8_str("obj_2"), instance_2); 11190 11191 Local<Value> value_2 = CompileRun("obj_2.a"); 11192 CHECK(value_2->IsUndefined()); 11193 } 11194 11195 11196 // This tests that access check information remains on the global 11197 // object template when creating contexts. 11198 THREADED_TEST(AccessControlRepeatedContextCreation) { 11199 v8::HandleScope handle_scope; 11200 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 11201 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker, 11202 IndexedSetAccessBlocker); 11203 i::Handle<i::ObjectTemplateInfo> internal_template = 11204 v8::Utils::OpenHandle(*global_template); 11205 CHECK(!internal_template->constructor()->IsUndefined()); 11206 i::Handle<i::FunctionTemplateInfo> constructor( 11207 i::FunctionTemplateInfo::cast(internal_template->constructor())); 11208 CHECK(!constructor->access_check_info()->IsUndefined()); 11209 v8::Persistent<Context> context0(Context::New(NULL, global_template)); 11210 CHECK(!context0.IsEmpty()); 11211 CHECK(!constructor->access_check_info()->IsUndefined()); 11212 } 11213 11214 11215 THREADED_TEST(TurnOnAccessCheck) { 11216 v8::HandleScope handle_scope; 11217 11218 // Create an environment with access check to the global object disabled by 11219 // default. 11220 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 11221 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker, 11222 IndexedGetAccessBlocker, 11223 v8::Handle<v8::Value>(), 11224 false); 11225 v8::Persistent<Context> context = Context::New(NULL, global_template); 11226 Context::Scope context_scope(context); 11227 11228 // Set up a property and a number of functions. 11229 context->Global()->Set(v8_str("a"), v8_num(1)); 11230 CompileRun("function f1() {return a;}" 11231 "function f2() {return a;}" 11232 "function g1() {return h();}" 11233 "function g2() {return h();}" 11234 "function h() {return 1;}"); 11235 Local<Function> f1 = 11236 Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); 11237 Local<Function> f2 = 11238 Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); 11239 Local<Function> g1 = 11240 Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); 11241 Local<Function> g2 = 11242 Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); 11243 Local<Function> h = 11244 Local<Function>::Cast(context->Global()->Get(v8_str("h"))); 11245 11246 // Get the global object. 11247 v8::Handle<v8::Object> global = context->Global(); 11248 11249 // Call f1 one time and f2 a number of times. This will ensure that f1 still 11250 // uses the runtime system to retreive property a whereas f2 uses global load 11251 // inline cache. 11252 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1))); 11253 for (int i = 0; i < 4; i++) { 11254 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1))); 11255 } 11256 11257 // Same for g1 and g2. 11258 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1))); 11259 for (int i = 0; i < 4; i++) { 11260 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1))); 11261 } 11262 11263 // Detach the global and turn on access check. 11264 context->DetachGlobal(); 11265 context->Global()->TurnOnAccessCheck(); 11266 11267 // Failing access check to property get results in undefined. 11268 CHECK(f1->Call(global, 0, NULL)->IsUndefined()); 11269 CHECK(f2->Call(global, 0, NULL)->IsUndefined()); 11270 11271 // Failing access check to function call results in exception. 11272 CHECK(g1->Call(global, 0, NULL).IsEmpty()); 11273 CHECK(g2->Call(global, 0, NULL).IsEmpty()); 11274 11275 // No failing access check when just returning a constant. 11276 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1))); 11277 } 11278 11279 11280 static const char* kPropertyA = "a"; 11281 static const char* kPropertyH = "h"; 11282 11283 static bool NamedGetAccessBlockAandH(Local<v8::Object> obj, 11284 Local<Value> name, 11285 v8::AccessType type, 11286 Local<Value> data) { 11287 if (!name->IsString()) return false; 11288 i::Handle<i::String> name_handle = 11289 v8::Utils::OpenHandle(String::Cast(*name)); 11290 return !name_handle->IsEqualTo(i::CStrVector(kPropertyA)) 11291 && !name_handle->IsEqualTo(i::CStrVector(kPropertyH)); 11292 } 11293 11294 11295 THREADED_TEST(TurnOnAccessCheckAndRecompile) { 11296 v8::HandleScope handle_scope; 11297 11298 // Create an environment with access check to the global object disabled by 11299 // default. When the registered access checker will block access to properties 11300 // a and h. 11301 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 11302 global_template->SetAccessCheckCallbacks(NamedGetAccessBlockAandH, 11303 IndexedGetAccessBlocker, 11304 v8::Handle<v8::Value>(), 11305 false); 11306 v8::Persistent<Context> context = Context::New(NULL, global_template); 11307 Context::Scope context_scope(context); 11308 11309 // Set up a property and a number of functions. 11310 context->Global()->Set(v8_str("a"), v8_num(1)); 11311 static const char* source = "function f1() {return a;}" 11312 "function f2() {return a;}" 11313 "function g1() {return h();}" 11314 "function g2() {return h();}" 11315 "function h() {return 1;}"; 11316 11317 CompileRun(source); 11318 Local<Function> f1; 11319 Local<Function> f2; 11320 Local<Function> g1; 11321 Local<Function> g2; 11322 Local<Function> h; 11323 f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); 11324 f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); 11325 g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); 11326 g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); 11327 h = Local<Function>::Cast(context->Global()->Get(v8_str("h"))); 11328 11329 // Get the global object. 11330 v8::Handle<v8::Object> global = context->Global(); 11331 11332 // Call f1 one time and f2 a number of times. This will ensure that f1 still 11333 // uses the runtime system to retreive property a whereas f2 uses global load 11334 // inline cache. 11335 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1))); 11336 for (int i = 0; i < 4; i++) { 11337 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1))); 11338 } 11339 11340 // Same for g1 and g2. 11341 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1))); 11342 for (int i = 0; i < 4; i++) { 11343 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1))); 11344 } 11345 11346 // Detach the global and turn on access check now blocking access to property 11347 // a and function h. 11348 context->DetachGlobal(); 11349 context->Global()->TurnOnAccessCheck(); 11350 11351 // Failing access check to property get results in undefined. 11352 CHECK(f1->Call(global, 0, NULL)->IsUndefined()); 11353 CHECK(f2->Call(global, 0, NULL)->IsUndefined()); 11354 11355 // Failing access check to function call results in exception. 11356 CHECK(g1->Call(global, 0, NULL).IsEmpty()); 11357 CHECK(g2->Call(global, 0, NULL).IsEmpty()); 11358 11359 // No failing access check when just returning a constant. 11360 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1))); 11361 11362 // Now compile the source again. And get the newly compiled functions, except 11363 // for h for which access is blocked. 11364 CompileRun(source); 11365 f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); 11366 f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); 11367 g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); 11368 g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); 11369 CHECK(context->Global()->Get(v8_str("h"))->IsUndefined()); 11370 11371 // Failing access check to property get results in undefined. 11372 CHECK(f1->Call(global, 0, NULL)->IsUndefined()); 11373 CHECK(f2->Call(global, 0, NULL)->IsUndefined()); 11374 11375 // Failing access check to function call results in exception. 11376 CHECK(g1->Call(global, 0, NULL).IsEmpty()); 11377 CHECK(g2->Call(global, 0, NULL).IsEmpty()); 11378 } 11379 11380 11381 // This test verifies that pre-compilation (aka preparsing) can be called 11382 // without initializing the whole VM. Thus we cannot run this test in a 11383 // multi-threaded setup. 11384 TEST(PreCompile) { 11385 // TODO(155): This test would break without the initialization of V8. This is 11386 // a workaround for now to make this test not fail. 11387 v8::V8::Initialize(); 11388 const char* script = "function foo(a) { return a+1; }"; 11389 v8::ScriptData* sd = 11390 v8::ScriptData::PreCompile(script, i::StrLength(script)); 11391 CHECK_NE(sd->Length(), 0); 11392 CHECK_NE(sd->Data(), NULL); 11393 CHECK(!sd->HasError()); 11394 delete sd; 11395 } 11396 11397 11398 TEST(PreCompileWithError) { 11399 v8::V8::Initialize(); 11400 const char* script = "function foo(a) { return 1 * * 2; }"; 11401 v8::ScriptData* sd = 11402 v8::ScriptData::PreCompile(script, i::StrLength(script)); 11403 CHECK(sd->HasError()); 11404 delete sd; 11405 } 11406 11407 11408 TEST(Regress31661) { 11409 v8::V8::Initialize(); 11410 const char* script = " The Definintive Guide"; 11411 v8::ScriptData* sd = 11412 v8::ScriptData::PreCompile(script, i::StrLength(script)); 11413 CHECK(sd->HasError()); 11414 delete sd; 11415 } 11416 11417 11418 // Tests that ScriptData can be serialized and deserialized. 11419 TEST(PreCompileSerialization) { 11420 v8::V8::Initialize(); 11421 const char* script = "function foo(a) { return a+1; }"; 11422 v8::ScriptData* sd = 11423 v8::ScriptData::PreCompile(script, i::StrLength(script)); 11424 11425 // Serialize. 11426 int serialized_data_length = sd->Length(); 11427 char* serialized_data = i::NewArray<char>(serialized_data_length); 11428 memcpy(serialized_data, sd->Data(), serialized_data_length); 11429 11430 // Deserialize. 11431 v8::ScriptData* deserialized_sd = 11432 v8::ScriptData::New(serialized_data, serialized_data_length); 11433 11434 // Verify that the original is the same as the deserialized. 11435 CHECK_EQ(sd->Length(), deserialized_sd->Length()); 11436 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length())); 11437 CHECK_EQ(sd->HasError(), deserialized_sd->HasError()); 11438 11439 delete sd; 11440 delete deserialized_sd; 11441 } 11442 11443 11444 // Attempts to deserialize bad data. 11445 TEST(PreCompileDeserializationError) { 11446 v8::V8::Initialize(); 11447 const char* data = "DONT CARE"; 11448 int invalid_size = 3; 11449 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size); 11450 11451 CHECK_EQ(0, sd->Length()); 11452 11453 delete sd; 11454 } 11455 11456 11457 // Attempts to deserialize bad data. 11458 TEST(PreCompileInvalidPreparseDataError) { 11459 v8::V8::Initialize(); 11460 v8::HandleScope scope; 11461 LocalContext context; 11462 11463 const char* script = "function foo(){ return 5;}\n" 11464 "function bar(){ return 6 + 7;} foo();"; 11465 v8::ScriptData* sd = 11466 v8::ScriptData::PreCompile(script, i::StrLength(script)); 11467 CHECK(!sd->HasError()); 11468 // ScriptDataImpl private implementation details 11469 const int kHeaderSize = i::PreparseDataConstants::kHeaderSize; 11470 const int kFunctionEntrySize = i::FunctionEntry::kSize; 11471 const int kFunctionEntryStartOffset = 0; 11472 const int kFunctionEntryEndOffset = 1; 11473 unsigned* sd_data = 11474 reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data())); 11475 11476 // Overwrite function bar's end position with 0. 11477 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0; 11478 v8::TryCatch try_catch; 11479 11480 Local<String> source = String::New(script); 11481 Local<Script> compiled_script = Script::New(source, NULL, sd); 11482 CHECK(try_catch.HasCaught()); 11483 String::AsciiValue exception_value(try_catch.Message()->Get()); 11484 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar", 11485 *exception_value); 11486 11487 try_catch.Reset(); 11488 11489 // Overwrite function bar's start position with 200. The function entry 11490 // will not be found when searching for it by position and we should fall 11491 // back on eager compilation. 11492 sd = v8::ScriptData::PreCompile(script, i::StrLength(script)); 11493 sd_data = reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data())); 11494 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] = 11495 200; 11496 compiled_script = Script::New(source, NULL, sd); 11497 CHECK(!try_catch.HasCaught()); 11498 11499 delete sd; 11500 } 11501 11502 11503 // Verifies that the Handle<String> and const char* versions of the API produce 11504 // the same results (at least for one trivial case). 11505 TEST(PreCompileAPIVariationsAreSame) { 11506 v8::V8::Initialize(); 11507 v8::HandleScope scope; 11508 11509 const char* cstring = "function foo(a) { return a+1; }"; 11510 11511 v8::ScriptData* sd_from_cstring = 11512 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring)); 11513 11514 TestAsciiResource* resource = new TestAsciiResource(cstring); 11515 v8::ScriptData* sd_from_external_string = v8::ScriptData::PreCompile( 11516 v8::String::NewExternal(resource)); 11517 11518 v8::ScriptData* sd_from_string = v8::ScriptData::PreCompile( 11519 v8::String::New(cstring)); 11520 11521 CHECK_EQ(sd_from_cstring->Length(), sd_from_external_string->Length()); 11522 CHECK_EQ(0, memcmp(sd_from_cstring->Data(), 11523 sd_from_external_string->Data(), 11524 sd_from_cstring->Length())); 11525 11526 CHECK_EQ(sd_from_cstring->Length(), sd_from_string->Length()); 11527 CHECK_EQ(0, memcmp(sd_from_cstring->Data(), 11528 sd_from_string->Data(), 11529 sd_from_cstring->Length())); 11530 11531 11532 delete sd_from_cstring; 11533 delete sd_from_external_string; 11534 delete sd_from_string; 11535 } 11536 11537 11538 // This tests that we do not allow dictionary load/call inline caches 11539 // to use functions that have not yet been compiled. The potential 11540 // problem of loading a function that has not yet been compiled can 11541 // arise because we share code between contexts via the compilation 11542 // cache. 11543 THREADED_TEST(DictionaryICLoadedFunction) { 11544 v8::HandleScope scope; 11545 // Test LoadIC. 11546 for (int i = 0; i < 2; i++) { 11547 LocalContext context; 11548 context->Global()->Set(v8_str("tmp"), v8::True()); 11549 context->Global()->Delete(v8_str("tmp")); 11550 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');"); 11551 } 11552 // Test CallIC. 11553 for (int i = 0; i < 2; i++) { 11554 LocalContext context; 11555 context->Global()->Set(v8_str("tmp"), v8::True()); 11556 context->Global()->Delete(v8_str("tmp")); 11557 CompileRun("for (var j = 0; j < 10; j++) RegExp('')"); 11558 } 11559 } 11560 11561 11562 // Test that cross-context new calls use the context of the callee to 11563 // create the new JavaScript object. 11564 THREADED_TEST(CrossContextNew) { 11565 v8::HandleScope scope; 11566 v8::Persistent<Context> context0 = Context::New(); 11567 v8::Persistent<Context> context1 = Context::New(); 11568 11569 // Allow cross-domain access. 11570 Local<String> token = v8_str("<security token>"); 11571 context0->SetSecurityToken(token); 11572 context1->SetSecurityToken(token); 11573 11574 // Set an 'x' property on the Object prototype and define a 11575 // constructor function in context0. 11576 context0->Enter(); 11577 CompileRun("Object.prototype.x = 42; function C() {};"); 11578 context0->Exit(); 11579 11580 // Call the constructor function from context0 and check that the 11581 // result has the 'x' property. 11582 context1->Enter(); 11583 context1->Global()->Set(v8_str("other"), context0->Global()); 11584 Local<Value> value = CompileRun("var instance = new other.C(); instance.x"); 11585 CHECK(value->IsInt32()); 11586 CHECK_EQ(42, value->Int32Value()); 11587 context1->Exit(); 11588 11589 // Dispose the contexts to allow them to be garbage collected. 11590 context0.Dispose(); 11591 context1.Dispose(); 11592 } 11593 11594 11595 class RegExpInterruptTest { 11596 public: 11597 RegExpInterruptTest() : block_(NULL) {} 11598 ~RegExpInterruptTest() { delete block_; } 11599 void RunTest() { 11600 block_ = i::OS::CreateSemaphore(0); 11601 gc_count_ = 0; 11602 gc_during_regexp_ = 0; 11603 regexp_success_ = false; 11604 gc_success_ = false; 11605 GCThread gc_thread(this); 11606 gc_thread.Start(); 11607 v8::Locker::StartPreemption(1); 11608 11609 LongRunningRegExp(); 11610 { 11611 v8::Unlocker unlock; 11612 gc_thread.Join(); 11613 } 11614 v8::Locker::StopPreemption(); 11615 CHECK(regexp_success_); 11616 CHECK(gc_success_); 11617 } 11618 11619 private: 11620 // Number of garbage collections required. 11621 static const int kRequiredGCs = 5; 11622 11623 class GCThread : public i::Thread { 11624 public: 11625 explicit GCThread(RegExpInterruptTest* test) 11626 : Thread("GCThread"), test_(test) {} 11627 virtual void Run() { 11628 test_->CollectGarbage(); 11629 } 11630 private: 11631 RegExpInterruptTest* test_; 11632 }; 11633 11634 void CollectGarbage() { 11635 block_->Wait(); 11636 while (gc_during_regexp_ < kRequiredGCs) { 11637 { 11638 v8::Locker lock; 11639 // TODO(lrn): Perhaps create some garbage before collecting. 11640 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 11641 gc_count_++; 11642 } 11643 i::OS::Sleep(1); 11644 } 11645 gc_success_ = true; 11646 } 11647 11648 void LongRunningRegExp() { 11649 block_->Signal(); // Enable garbage collection thread on next preemption. 11650 int rounds = 0; 11651 while (gc_during_regexp_ < kRequiredGCs) { 11652 int gc_before = gc_count_; 11653 { 11654 // Match 15-30 "a"'s against 14 and a "b". 11655 const char* c_source = 11656 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 11657 ".exec('aaaaaaaaaaaaaaab') === null"; 11658 Local<String> source = String::New(c_source); 11659 Local<Script> script = Script::Compile(source); 11660 Local<Value> result = script->Run(); 11661 if (!result->BooleanValue()) { 11662 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit. 11663 return; 11664 } 11665 } 11666 { 11667 // Match 15-30 "a"'s against 15 and a "b". 11668 const char* c_source = 11669 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 11670 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'"; 11671 Local<String> source = String::New(c_source); 11672 Local<Script> script = Script::Compile(source); 11673 Local<Value> result = script->Run(); 11674 if (!result->BooleanValue()) { 11675 gc_during_regexp_ = kRequiredGCs; 11676 return; 11677 } 11678 } 11679 int gc_after = gc_count_; 11680 gc_during_regexp_ += gc_after - gc_before; 11681 rounds++; 11682 i::OS::Sleep(1); 11683 } 11684 regexp_success_ = true; 11685 } 11686 11687 i::Semaphore* block_; 11688 int gc_count_; 11689 int gc_during_regexp_; 11690 bool regexp_success_; 11691 bool gc_success_; 11692 }; 11693 11694 11695 // Test that a regular expression execution can be interrupted and 11696 // survive a garbage collection. 11697 TEST(RegExpInterruption) { 11698 v8::Locker lock; 11699 v8::V8::Initialize(); 11700 v8::HandleScope scope; 11701 Local<Context> local_env; 11702 { 11703 LocalContext env; 11704 local_env = env.local(); 11705 } 11706 11707 // Local context should still be live. 11708 CHECK(!local_env.IsEmpty()); 11709 local_env->Enter(); 11710 11711 // Should complete without problems. 11712 RegExpInterruptTest().RunTest(); 11713 11714 local_env->Exit(); 11715 } 11716 11717 11718 class ApplyInterruptTest { 11719 public: 11720 ApplyInterruptTest() : block_(NULL) {} 11721 ~ApplyInterruptTest() { delete block_; } 11722 void RunTest() { 11723 block_ = i::OS::CreateSemaphore(0); 11724 gc_count_ = 0; 11725 gc_during_apply_ = 0; 11726 apply_success_ = false; 11727 gc_success_ = false; 11728 GCThread gc_thread(this); 11729 gc_thread.Start(); 11730 v8::Locker::StartPreemption(1); 11731 11732 LongRunningApply(); 11733 { 11734 v8::Unlocker unlock; 11735 gc_thread.Join(); 11736 } 11737 v8::Locker::StopPreemption(); 11738 CHECK(apply_success_); 11739 CHECK(gc_success_); 11740 } 11741 11742 private: 11743 // Number of garbage collections required. 11744 static const int kRequiredGCs = 2; 11745 11746 class GCThread : public i::Thread { 11747 public: 11748 explicit GCThread(ApplyInterruptTest* test) 11749 : Thread("GCThread"), test_(test) {} 11750 virtual void Run() { 11751 test_->CollectGarbage(); 11752 } 11753 private: 11754 ApplyInterruptTest* test_; 11755 }; 11756 11757 void CollectGarbage() { 11758 block_->Wait(); 11759 while (gc_during_apply_ < kRequiredGCs) { 11760 { 11761 v8::Locker lock; 11762 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 11763 gc_count_++; 11764 } 11765 i::OS::Sleep(1); 11766 } 11767 gc_success_ = true; 11768 } 11769 11770 void LongRunningApply() { 11771 block_->Signal(); 11772 int rounds = 0; 11773 while (gc_during_apply_ < kRequiredGCs) { 11774 int gc_before = gc_count_; 11775 { 11776 const char* c_source = 11777 "function do_very_little(bar) {" 11778 " this.foo = bar;" 11779 "}" 11780 "for (var i = 0; i < 100000; i++) {" 11781 " do_very_little.apply(this, ['bar']);" 11782 "}"; 11783 Local<String> source = String::New(c_source); 11784 Local<Script> script = Script::Compile(source); 11785 Local<Value> result = script->Run(); 11786 // Check that no exception was thrown. 11787 CHECK(!result.IsEmpty()); 11788 } 11789 int gc_after = gc_count_; 11790 gc_during_apply_ += gc_after - gc_before; 11791 rounds++; 11792 } 11793 apply_success_ = true; 11794 } 11795 11796 i::Semaphore* block_; 11797 int gc_count_; 11798 int gc_during_apply_; 11799 bool apply_success_; 11800 bool gc_success_; 11801 }; 11802 11803 11804 // Test that nothing bad happens if we get a preemption just when we were 11805 // about to do an apply(). 11806 TEST(ApplyInterruption) { 11807 v8::Locker lock; 11808 v8::V8::Initialize(); 11809 v8::HandleScope scope; 11810 Local<Context> local_env; 11811 { 11812 LocalContext env; 11813 local_env = env.local(); 11814 } 11815 11816 // Local context should still be live. 11817 CHECK(!local_env.IsEmpty()); 11818 local_env->Enter(); 11819 11820 // Should complete without problems. 11821 ApplyInterruptTest().RunTest(); 11822 11823 local_env->Exit(); 11824 } 11825 11826 11827 // Verify that we can clone an object 11828 TEST(ObjectClone) { 11829 v8::HandleScope scope; 11830 LocalContext env; 11831 11832 const char* sample = 11833 "var rv = {};" \ 11834 "rv.alpha = 'hello';" \ 11835 "rv.beta = 123;" \ 11836 "rv;"; 11837 11838 // Create an object, verify basics. 11839 Local<Value> val = CompileRun(sample); 11840 CHECK(val->IsObject()); 11841 Local<v8::Object> obj = val.As<v8::Object>(); 11842 obj->Set(v8_str("gamma"), v8_str("cloneme")); 11843 11844 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha"))); 11845 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 11846 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma"))); 11847 11848 // Clone it. 11849 Local<v8::Object> clone = obj->Clone(); 11850 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha"))); 11851 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta"))); 11852 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma"))); 11853 11854 // Set a property on the clone, verify each object. 11855 clone->Set(v8_str("beta"), v8::Integer::New(456)); 11856 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 11857 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta"))); 11858 } 11859 11860 11861 class AsciiVectorResource : public v8::String::ExternalAsciiStringResource { 11862 public: 11863 explicit AsciiVectorResource(i::Vector<const char> vector) 11864 : data_(vector) {} 11865 virtual ~AsciiVectorResource() {} 11866 virtual size_t length() const { return data_.length(); } 11867 virtual const char* data() const { return data_.start(); } 11868 private: 11869 i::Vector<const char> data_; 11870 }; 11871 11872 11873 class UC16VectorResource : public v8::String::ExternalStringResource { 11874 public: 11875 explicit UC16VectorResource(i::Vector<const i::uc16> vector) 11876 : data_(vector) {} 11877 virtual ~UC16VectorResource() {} 11878 virtual size_t length() const { return data_.length(); } 11879 virtual const i::uc16* data() const { return data_.start(); } 11880 private: 11881 i::Vector<const i::uc16> data_; 11882 }; 11883 11884 11885 static void MorphAString(i::String* string, 11886 AsciiVectorResource* ascii_resource, 11887 UC16VectorResource* uc16_resource) { 11888 CHECK(i::StringShape(string).IsExternal()); 11889 if (string->IsAsciiRepresentation()) { 11890 // Check old map is not symbol or long. 11891 CHECK(string->map() == HEAP->external_ascii_string_map()); 11892 // Morph external string to be TwoByte string. 11893 string->set_map(HEAP->external_string_map()); 11894 i::ExternalTwoByteString* morphed = 11895 i::ExternalTwoByteString::cast(string); 11896 morphed->set_resource(uc16_resource); 11897 } else { 11898 // Check old map is not symbol or long. 11899 CHECK(string->map() == HEAP->external_string_map()); 11900 // Morph external string to be ASCII string. 11901 string->set_map(HEAP->external_ascii_string_map()); 11902 i::ExternalAsciiString* morphed = 11903 i::ExternalAsciiString::cast(string); 11904 morphed->set_resource(ascii_resource); 11905 } 11906 } 11907 11908 11909 // Test that we can still flatten a string if the components it is built up 11910 // from have been turned into 16 bit strings in the mean time. 11911 THREADED_TEST(MorphCompositeStringTest) { 11912 char utf_buffer[129]; 11913 const char* c_string = "Now is the time for all good men" 11914 " to come to the aid of the party"; 11915 uint16_t* two_byte_string = AsciiToTwoByteString(c_string); 11916 { 11917 v8::HandleScope scope; 11918 LocalContext env; 11919 AsciiVectorResource ascii_resource( 11920 i::Vector<const char>(c_string, i::StrLength(c_string))); 11921 UC16VectorResource uc16_resource( 11922 i::Vector<const uint16_t>(two_byte_string, 11923 i::StrLength(c_string))); 11924 11925 Local<String> lhs(v8::Utils::ToLocal( 11926 FACTORY->NewExternalStringFromAscii(&ascii_resource))); 11927 Local<String> rhs(v8::Utils::ToLocal( 11928 FACTORY->NewExternalStringFromAscii(&ascii_resource))); 11929 11930 env->Global()->Set(v8_str("lhs"), lhs); 11931 env->Global()->Set(v8_str("rhs"), rhs); 11932 11933 CompileRun( 11934 "var cons = lhs + rhs;" 11935 "var slice = lhs.substring(1, lhs.length - 1);" 11936 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);"); 11937 11938 CHECK(!lhs->MayContainNonAscii()); 11939 CHECK(!rhs->MayContainNonAscii()); 11940 11941 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource); 11942 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource); 11943 11944 // This should UTF-8 without flattening, since everything is ASCII. 11945 Handle<String> cons = v8_compile("cons")->Run().As<String>(); 11946 CHECK_EQ(128, cons->Utf8Length()); 11947 int nchars = -1; 11948 CHECK_EQ(129, cons->WriteUtf8(utf_buffer, -1, &nchars)); 11949 CHECK_EQ(128, nchars); 11950 CHECK_EQ(0, strcmp( 11951 utf_buffer, 11952 "Now is the time for all good men to come to the aid of the party" 11953 "Now is the time for all good men to come to the aid of the party")); 11954 11955 // Now do some stuff to make sure the strings are flattened, etc. 11956 CompileRun( 11957 "/[^a-z]/.test(cons);" 11958 "/[^a-z]/.test(slice);" 11959 "/[^a-z]/.test(slice_on_cons);"); 11960 const char* expected_cons = 11961 "Now is the time for all good men to come to the aid of the party" 11962 "Now is the time for all good men to come to the aid of the party"; 11963 const char* expected_slice = 11964 "ow is the time for all good men to come to the aid of the part"; 11965 const char* expected_slice_on_cons = 11966 "ow is the time for all good men to come to the aid of the party" 11967 "Now is the time for all good men to come to the aid of the part"; 11968 CHECK_EQ(String::New(expected_cons), 11969 env->Global()->Get(v8_str("cons"))); 11970 CHECK_EQ(String::New(expected_slice), 11971 env->Global()->Get(v8_str("slice"))); 11972 CHECK_EQ(String::New(expected_slice_on_cons), 11973 env->Global()->Get(v8_str("slice_on_cons"))); 11974 } 11975 i::DeleteArray(two_byte_string); 11976 } 11977 11978 11979 TEST(CompileExternalTwoByteSource) { 11980 v8::HandleScope scope; 11981 LocalContext context; 11982 11983 // This is a very short list of sources, which currently is to check for a 11984 // regression caused by r2703. 11985 const char* ascii_sources[] = { 11986 "0.5", 11987 "-0.5", // This mainly testes PushBack in the Scanner. 11988 "--0.5", // This mainly testes PushBack in the Scanner. 11989 NULL 11990 }; 11991 11992 // Compile the sources as external two byte strings. 11993 for (int i = 0; ascii_sources[i] != NULL; i++) { 11994 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]); 11995 UC16VectorResource uc16_resource( 11996 i::Vector<const uint16_t>(two_byte_string, 11997 i::StrLength(ascii_sources[i]))); 11998 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource); 11999 v8::Script::Compile(source); 12000 i::DeleteArray(two_byte_string); 12001 } 12002 } 12003 12004 12005 class RegExpStringModificationTest { 12006 public: 12007 RegExpStringModificationTest() 12008 : block_(i::OS::CreateSemaphore(0)), 12009 morphs_(0), 12010 morphs_during_regexp_(0), 12011 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)), 12012 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {} 12013 ~RegExpStringModificationTest() { delete block_; } 12014 void RunTest() { 12015 regexp_success_ = false; 12016 morph_success_ = false; 12017 12018 // Initialize the contents of two_byte_content_ to be a uc16 representation 12019 // of "aaaaaaaaaaaaaab". 12020 for (int i = 0; i < 14; i++) { 12021 two_byte_content_[i] = 'a'; 12022 } 12023 two_byte_content_[14] = 'b'; 12024 12025 // Create the input string for the regexp - the one we are going to change 12026 // properties of. 12027 input_ = FACTORY->NewExternalStringFromAscii(&ascii_resource_); 12028 12029 // Inject the input as a global variable. 12030 i::Handle<i::String> input_name = 12031 FACTORY->NewStringFromAscii(i::Vector<const char>("input", 5)); 12032 i::Isolate::Current()->global_context()->global()->SetProperty( 12033 *input_name, 12034 *input_, 12035 NONE, 12036 i::kNonStrictMode)->ToObjectChecked(); 12037 12038 MorphThread morph_thread(this); 12039 morph_thread.Start(); 12040 v8::Locker::StartPreemption(1); 12041 LongRunningRegExp(); 12042 { 12043 v8::Unlocker unlock; 12044 morph_thread.Join(); 12045 } 12046 v8::Locker::StopPreemption(); 12047 CHECK(regexp_success_); 12048 CHECK(morph_success_); 12049 } 12050 12051 private: 12052 // Number of string modifications required. 12053 static const int kRequiredModifications = 5; 12054 static const int kMaxModifications = 100; 12055 12056 class MorphThread : public i::Thread { 12057 public: 12058 explicit MorphThread(RegExpStringModificationTest* test) 12059 : Thread("MorphThread"), test_(test) {} 12060 virtual void Run() { 12061 test_->MorphString(); 12062 } 12063 private: 12064 RegExpStringModificationTest* test_; 12065 }; 12066 12067 void MorphString() { 12068 block_->Wait(); 12069 while (morphs_during_regexp_ < kRequiredModifications && 12070 morphs_ < kMaxModifications) { 12071 { 12072 v8::Locker lock; 12073 // Swap string between ascii and two-byte representation. 12074 i::String* string = *input_; 12075 MorphAString(string, &ascii_resource_, &uc16_resource_); 12076 morphs_++; 12077 } 12078 i::OS::Sleep(1); 12079 } 12080 morph_success_ = true; 12081 } 12082 12083 void LongRunningRegExp() { 12084 block_->Signal(); // Enable morphing thread on next preemption. 12085 while (morphs_during_regexp_ < kRequiredModifications && 12086 morphs_ < kMaxModifications) { 12087 int morphs_before = morphs_; 12088 { 12089 v8::HandleScope scope; 12090 // Match 15-30 "a"'s against 14 and a "b". 12091 const char* c_source = 12092 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 12093 ".exec(input) === null"; 12094 Local<String> source = String::New(c_source); 12095 Local<Script> script = Script::Compile(source); 12096 Local<Value> result = script->Run(); 12097 CHECK(result->IsTrue()); 12098 } 12099 int morphs_after = morphs_; 12100 morphs_during_regexp_ += morphs_after - morphs_before; 12101 } 12102 regexp_success_ = true; 12103 } 12104 12105 i::uc16 two_byte_content_[15]; 12106 i::Semaphore* block_; 12107 int morphs_; 12108 int morphs_during_regexp_; 12109 bool regexp_success_; 12110 bool morph_success_; 12111 i::Handle<i::String> input_; 12112 AsciiVectorResource ascii_resource_; 12113 UC16VectorResource uc16_resource_; 12114 }; 12115 12116 12117 // Test that a regular expression execution can be interrupted and 12118 // the string changed without failing. 12119 TEST(RegExpStringModification) { 12120 v8::Locker lock; 12121 v8::V8::Initialize(); 12122 v8::HandleScope scope; 12123 Local<Context> local_env; 12124 { 12125 LocalContext env; 12126 local_env = env.local(); 12127 } 12128 12129 // Local context should still be live. 12130 CHECK(!local_env.IsEmpty()); 12131 local_env->Enter(); 12132 12133 // Should complete without problems. 12134 RegExpStringModificationTest().RunTest(); 12135 12136 local_env->Exit(); 12137 } 12138 12139 12140 // Test that we can set a property on the global object even if there 12141 // is a read-only property in the prototype chain. 12142 TEST(ReadOnlyPropertyInGlobalProto) { 12143 v8::HandleScope scope; 12144 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 12145 LocalContext context(0, templ); 12146 v8::Handle<v8::Object> global = context->Global(); 12147 v8::Handle<v8::Object> global_proto = 12148 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__"))); 12149 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly); 12150 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly); 12151 // Check without 'eval' or 'with'. 12152 v8::Handle<v8::Value> res = 12153 CompileRun("function f() { x = 42; return x; }; f()"); 12154 // Check with 'eval'. 12155 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()"); 12156 CHECK_EQ(v8::Integer::New(42), res); 12157 // Check with 'with'. 12158 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()"); 12159 CHECK_EQ(v8::Integer::New(42), res); 12160 } 12161 12162 static int force_set_set_count = 0; 12163 static int force_set_get_count = 0; 12164 bool pass_on_get = false; 12165 12166 static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name, 12167 const v8::AccessorInfo& info) { 12168 force_set_get_count++; 12169 if (pass_on_get) { 12170 return v8::Handle<v8::Value>(); 12171 } else { 12172 return v8::Int32::New(3); 12173 } 12174 } 12175 12176 static void ForceSetSetter(v8::Local<v8::String> name, 12177 v8::Local<v8::Value> value, 12178 const v8::AccessorInfo& info) { 12179 force_set_set_count++; 12180 } 12181 12182 static v8::Handle<v8::Value> ForceSetInterceptSetter( 12183 v8::Local<v8::String> name, 12184 v8::Local<v8::Value> value, 12185 const v8::AccessorInfo& info) { 12186 force_set_set_count++; 12187 return v8::Undefined(); 12188 } 12189 12190 TEST(ForceSet) { 12191 force_set_get_count = 0; 12192 force_set_set_count = 0; 12193 pass_on_get = false; 12194 12195 v8::HandleScope scope; 12196 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 12197 v8::Handle<v8::String> access_property = v8::String::New("a"); 12198 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter); 12199 LocalContext context(NULL, templ); 12200 v8::Handle<v8::Object> global = context->Global(); 12201 12202 // Ordinary properties 12203 v8::Handle<v8::String> simple_property = v8::String::New("p"); 12204 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly); 12205 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 12206 // This should fail because the property is read-only 12207 global->Set(simple_property, v8::Int32::New(5)); 12208 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 12209 // This should succeed even though the property is read-only 12210 global->ForceSet(simple_property, v8::Int32::New(6)); 12211 CHECK_EQ(6, global->Get(simple_property)->Int32Value()); 12212 12213 // Accessors 12214 CHECK_EQ(0, force_set_set_count); 12215 CHECK_EQ(0, force_set_get_count); 12216 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 12217 // CHECK_EQ the property shouldn't override it, just call the setter 12218 // which in this case does nothing. 12219 global->Set(access_property, v8::Int32::New(7)); 12220 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 12221 CHECK_EQ(1, force_set_set_count); 12222 CHECK_EQ(2, force_set_get_count); 12223 // Forcing the property to be set should override the accessor without 12224 // calling it 12225 global->ForceSet(access_property, v8::Int32::New(8)); 12226 CHECK_EQ(8, global->Get(access_property)->Int32Value()); 12227 CHECK_EQ(1, force_set_set_count); 12228 CHECK_EQ(2, force_set_get_count); 12229 } 12230 12231 TEST(ForceSetWithInterceptor) { 12232 force_set_get_count = 0; 12233 force_set_set_count = 0; 12234 pass_on_get = false; 12235 12236 v8::HandleScope scope; 12237 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 12238 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter); 12239 LocalContext context(NULL, templ); 12240 v8::Handle<v8::Object> global = context->Global(); 12241 12242 v8::Handle<v8::String> some_property = v8::String::New("a"); 12243 CHECK_EQ(0, force_set_set_count); 12244 CHECK_EQ(0, force_set_get_count); 12245 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 12246 // Setting the property shouldn't override it, just call the setter 12247 // which in this case does nothing. 12248 global->Set(some_property, v8::Int32::New(7)); 12249 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 12250 CHECK_EQ(1, force_set_set_count); 12251 CHECK_EQ(2, force_set_get_count); 12252 // Getting the property when the interceptor returns an empty handle 12253 // should yield undefined, since the property isn't present on the 12254 // object itself yet. 12255 pass_on_get = true; 12256 CHECK(global->Get(some_property)->IsUndefined()); 12257 CHECK_EQ(1, force_set_set_count); 12258 CHECK_EQ(3, force_set_get_count); 12259 // Forcing the property to be set should cause the value to be 12260 // set locally without calling the interceptor. 12261 global->ForceSet(some_property, v8::Int32::New(8)); 12262 CHECK_EQ(8, global->Get(some_property)->Int32Value()); 12263 CHECK_EQ(1, force_set_set_count); 12264 CHECK_EQ(4, force_set_get_count); 12265 // Reenabling the interceptor should cause it to take precedence over 12266 // the property 12267 pass_on_get = false; 12268 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 12269 CHECK_EQ(1, force_set_set_count); 12270 CHECK_EQ(5, force_set_get_count); 12271 // The interceptor should also work for other properties 12272 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value()); 12273 CHECK_EQ(1, force_set_set_count); 12274 CHECK_EQ(6, force_set_get_count); 12275 } 12276 12277 12278 THREADED_TEST(ForceDelete) { 12279 v8::HandleScope scope; 12280 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 12281 LocalContext context(NULL, templ); 12282 v8::Handle<v8::Object> global = context->Global(); 12283 12284 // Ordinary properties 12285 v8::Handle<v8::String> simple_property = v8::String::New("p"); 12286 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete); 12287 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 12288 // This should fail because the property is dont-delete. 12289 CHECK(!global->Delete(simple_property)); 12290 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 12291 // This should succeed even though the property is dont-delete. 12292 CHECK(global->ForceDelete(simple_property)); 12293 CHECK(global->Get(simple_property)->IsUndefined()); 12294 } 12295 12296 12297 static int force_delete_interceptor_count = 0; 12298 static bool pass_on_delete = false; 12299 12300 12301 static v8::Handle<v8::Boolean> ForceDeleteDeleter( 12302 v8::Local<v8::String> name, 12303 const v8::AccessorInfo& info) { 12304 force_delete_interceptor_count++; 12305 if (pass_on_delete) { 12306 return v8::Handle<v8::Boolean>(); 12307 } else { 12308 return v8::True(); 12309 } 12310 } 12311 12312 12313 THREADED_TEST(ForceDeleteWithInterceptor) { 12314 force_delete_interceptor_count = 0; 12315 pass_on_delete = false; 12316 12317 v8::HandleScope scope; 12318 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 12319 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter); 12320 LocalContext context(NULL, templ); 12321 v8::Handle<v8::Object> global = context->Global(); 12322 12323 v8::Handle<v8::String> some_property = v8::String::New("a"); 12324 global->Set(some_property, v8::Integer::New(42), v8::DontDelete); 12325 12326 // Deleting a property should get intercepted and nothing should 12327 // happen. 12328 CHECK_EQ(0, force_delete_interceptor_count); 12329 CHECK(global->Delete(some_property)); 12330 CHECK_EQ(1, force_delete_interceptor_count); 12331 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 12332 // Deleting the property when the interceptor returns an empty 12333 // handle should not delete the property since it is DontDelete. 12334 pass_on_delete = true; 12335 CHECK(!global->Delete(some_property)); 12336 CHECK_EQ(2, force_delete_interceptor_count); 12337 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 12338 // Forcing the property to be deleted should delete the value 12339 // without calling the interceptor. 12340 CHECK(global->ForceDelete(some_property)); 12341 CHECK(global->Get(some_property)->IsUndefined()); 12342 CHECK_EQ(2, force_delete_interceptor_count); 12343 } 12344 12345 12346 // Make sure that forcing a delete invalidates any IC stubs, so we 12347 // don't read the hole value. 12348 THREADED_TEST(ForceDeleteIC) { 12349 v8::HandleScope scope; 12350 LocalContext context; 12351 // Create a DontDelete variable on the global object. 12352 CompileRun("this.__proto__ = { foo: 'horse' };" 12353 "var foo = 'fish';" 12354 "function f() { return foo.length; }"); 12355 // Initialize the IC for foo in f. 12356 CompileRun("for (var i = 0; i < 4; i++) f();"); 12357 // Make sure the value of foo is correct before the deletion. 12358 CHECK_EQ(4, CompileRun("f()")->Int32Value()); 12359 // Force the deletion of foo. 12360 CHECK(context->Global()->ForceDelete(v8_str("foo"))); 12361 // Make sure the value for foo is read from the prototype, and that 12362 // we don't get in trouble with reading the deleted cell value 12363 // sentinel. 12364 CHECK_EQ(5, CompileRun("f()")->Int32Value()); 12365 } 12366 12367 12368 v8::Persistent<Context> calling_context0; 12369 v8::Persistent<Context> calling_context1; 12370 v8::Persistent<Context> calling_context2; 12371 12372 12373 // Check that the call to the callback is initiated in 12374 // calling_context2, the directly calling context is calling_context1 12375 // and the callback itself is in calling_context0. 12376 static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) { 12377 ApiTestFuzzer::Fuzz(); 12378 CHECK(Context::GetCurrent() == calling_context0); 12379 CHECK(Context::GetCalling() == calling_context1); 12380 CHECK(Context::GetEntered() == calling_context2); 12381 return v8::Integer::New(42); 12382 } 12383 12384 12385 THREADED_TEST(GetCallingContext) { 12386 v8::HandleScope scope; 12387 12388 calling_context0 = Context::New(); 12389 calling_context1 = Context::New(); 12390 calling_context2 = Context::New(); 12391 12392 // Allow cross-domain access. 12393 Local<String> token = v8_str("<security token>"); 12394 calling_context0->SetSecurityToken(token); 12395 calling_context1->SetSecurityToken(token); 12396 calling_context2->SetSecurityToken(token); 12397 12398 // Create an object with a C++ callback in context0. 12399 calling_context0->Enter(); 12400 Local<v8::FunctionTemplate> callback_templ = 12401 v8::FunctionTemplate::New(GetCallingContextCallback); 12402 calling_context0->Global()->Set(v8_str("callback"), 12403 callback_templ->GetFunction()); 12404 calling_context0->Exit(); 12405 12406 // Expose context0 in context1 and set up a function that calls the 12407 // callback function. 12408 calling_context1->Enter(); 12409 calling_context1->Global()->Set(v8_str("context0"), 12410 calling_context0->Global()); 12411 CompileRun("function f() { context0.callback() }"); 12412 calling_context1->Exit(); 12413 12414 // Expose context1 in context2 and call the callback function in 12415 // context0 indirectly through f in context1. 12416 calling_context2->Enter(); 12417 calling_context2->Global()->Set(v8_str("context1"), 12418 calling_context1->Global()); 12419 CompileRun("context1.f()"); 12420 calling_context2->Exit(); 12421 12422 // Dispose the contexts to allow them to be garbage collected. 12423 calling_context0.Dispose(); 12424 calling_context1.Dispose(); 12425 calling_context2.Dispose(); 12426 calling_context0.Clear(); 12427 calling_context1.Clear(); 12428 calling_context2.Clear(); 12429 } 12430 12431 12432 // Check that a variable declaration with no explicit initialization 12433 // value does not shadow an existing property in the prototype chain. 12434 // 12435 // This is consistent with Firefox and Safari. 12436 // 12437 // See http://crbug.com/12548. 12438 THREADED_TEST(InitGlobalVarInProtoChain) { 12439 v8::HandleScope scope; 12440 LocalContext context; 12441 // Introduce a variable in the prototype chain. 12442 CompileRun("__proto__.x = 42"); 12443 v8::Handle<v8::Value> result = CompileRun("var x; x"); 12444 CHECK(!result->IsUndefined()); 12445 CHECK_EQ(42, result->Int32Value()); 12446 } 12447 12448 12449 // Regression test for issue 398. 12450 // If a function is added to an object, creating a constant function 12451 // field, and the result is cloned, replacing the constant function on the 12452 // original should not affect the clone. 12453 // See http://code.google.com/p/v8/issues/detail?id=398 12454 THREADED_TEST(ReplaceConstantFunction) { 12455 v8::HandleScope scope; 12456 LocalContext context; 12457 v8::Handle<v8::Object> obj = v8::Object::New(); 12458 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 12459 v8::Handle<v8::String> foo_string = v8::String::New("foo"); 12460 obj->Set(foo_string, func_templ->GetFunction()); 12461 v8::Handle<v8::Object> obj_clone = obj->Clone(); 12462 obj_clone->Set(foo_string, v8::String::New("Hello")); 12463 CHECK(!obj->Get(foo_string)->IsUndefined()); 12464 } 12465 12466 12467 // Regression test for http://crbug.com/16276. 12468 THREADED_TEST(Regress16276) { 12469 v8::HandleScope scope; 12470 LocalContext context; 12471 // Force the IC in f to be a dictionary load IC. 12472 CompileRun("function f(obj) { return obj.x; }\n" 12473 "var obj = { x: { foo: 42 }, y: 87 };\n" 12474 "var x = obj.x;\n" 12475 "delete obj.y;\n" 12476 "for (var i = 0; i < 5; i++) f(obj);"); 12477 // Detach the global object to make 'this' refer directly to the 12478 // global object (not the proxy), and make sure that the dictionary 12479 // load IC doesn't mess up loading directly from the global object. 12480 context->DetachGlobal(); 12481 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value()); 12482 } 12483 12484 12485 THREADED_TEST(PixelArray) { 12486 v8::HandleScope scope; 12487 LocalContext context; 12488 const int kElementCount = 260; 12489 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); 12490 i::Handle<i::ExternalPixelArray> pixels = 12491 i::Handle<i::ExternalPixelArray>::cast( 12492 FACTORY->NewExternalArray(kElementCount, 12493 v8::kExternalPixelArray, 12494 pixel_data)); 12495 // Force GC to trigger verification. 12496 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 12497 for (int i = 0; i < kElementCount; i++) { 12498 pixels->set(i, i % 256); 12499 } 12500 // Force GC to trigger verification. 12501 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 12502 for (int i = 0; i < kElementCount; i++) { 12503 CHECK_EQ(i % 256, pixels->get_scalar(i)); 12504 CHECK_EQ(i % 256, pixel_data[i]); 12505 } 12506 12507 v8::Handle<v8::Object> obj = v8::Object::New(); 12508 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 12509 // Set the elements to be the pixels. 12510 // jsobj->set_elements(*pixels); 12511 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount); 12512 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 12513 obj->Set(v8_str("field"), v8::Int32::New(1503)); 12514 context->Global()->Set(v8_str("pixels"), obj); 12515 v8::Handle<v8::Value> result = CompileRun("pixels.field"); 12516 CHECK_EQ(1503, result->Int32Value()); 12517 result = CompileRun("pixels[1]"); 12518 CHECK_EQ(1, result->Int32Value()); 12519 12520 result = CompileRun("var sum = 0;" 12521 "for (var i = 0; i < 8; i++) {" 12522 " sum += pixels[i] = pixels[i] = -i;" 12523 "}" 12524 "sum;"); 12525 CHECK_EQ(-28, result->Int32Value()); 12526 12527 result = CompileRun("var sum = 0;" 12528 "for (var i = 0; i < 8; i++) {" 12529 " sum += pixels[i] = pixels[i] = 0;" 12530 "}" 12531 "sum;"); 12532 CHECK_EQ(0, result->Int32Value()); 12533 12534 result = CompileRun("var sum = 0;" 12535 "for (var i = 0; i < 8; i++) {" 12536 " sum += pixels[i] = pixels[i] = 255;" 12537 "}" 12538 "sum;"); 12539 CHECK_EQ(8 * 255, result->Int32Value()); 12540 12541 result = CompileRun("var sum = 0;" 12542 "for (var i = 0; i < 8; i++) {" 12543 " sum += pixels[i] = pixels[i] = 256 + i;" 12544 "}" 12545 "sum;"); 12546 CHECK_EQ(2076, result->Int32Value()); 12547 12548 result = CompileRun("var sum = 0;" 12549 "for (var i = 0; i < 8; i++) {" 12550 " sum += pixels[i] = pixels[i] = i;" 12551 "}" 12552 "sum;"); 12553 CHECK_EQ(28, result->Int32Value()); 12554 12555 result = CompileRun("var sum = 0;" 12556 "for (var i = 0; i < 8; i++) {" 12557 " sum += pixels[i];" 12558 "}" 12559 "sum;"); 12560 CHECK_EQ(28, result->Int32Value()); 12561 12562 i::Handle<i::Smi> value(i::Smi::FromInt(2)); 12563 i::Handle<i::Object> no_failure; 12564 no_failure = 12565 i::JSObject::SetElement(jsobj, 1, value, NONE, i::kNonStrictMode); 12566 ASSERT(!no_failure.is_null()); 12567 i::USE(no_failure); 12568 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 12569 *value.location() = i::Smi::FromInt(256); 12570 no_failure = 12571 i::JSObject::SetElement(jsobj, 1, value, NONE, i::kNonStrictMode); 12572 ASSERT(!no_failure.is_null()); 12573 i::USE(no_failure); 12574 CHECK_EQ(255, 12575 i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 12576 *value.location() = i::Smi::FromInt(-1); 12577 no_failure = 12578 i::JSObject::SetElement(jsobj, 1, value, NONE, i::kNonStrictMode); 12579 ASSERT(!no_failure.is_null()); 12580 i::USE(no_failure); 12581 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 12582 12583 result = CompileRun("for (var i = 0; i < 8; i++) {" 12584 " pixels[i] = (i * 65) - 109;" 12585 "}" 12586 "pixels[1] + pixels[6];"); 12587 CHECK_EQ(255, result->Int32Value()); 12588 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value()); 12589 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 12590 CHECK_EQ(21, 12591 i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value()); 12592 CHECK_EQ(86, 12593 i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value()); 12594 CHECK_EQ(151, 12595 i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value()); 12596 CHECK_EQ(216, 12597 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 12598 CHECK_EQ(255, 12599 i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value()); 12600 CHECK_EQ(255, 12601 i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value()); 12602 result = CompileRun("var sum = 0;" 12603 "for (var i = 0; i < 8; i++) {" 12604 " sum += pixels[i];" 12605 "}" 12606 "sum;"); 12607 CHECK_EQ(984, result->Int32Value()); 12608 12609 result = CompileRun("for (var i = 0; i < 8; i++) {" 12610 " pixels[i] = (i * 1.1);" 12611 "}" 12612 "pixels[1] + pixels[6];"); 12613 CHECK_EQ(8, result->Int32Value()); 12614 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value()); 12615 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 12616 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value()); 12617 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value()); 12618 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value()); 12619 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 12620 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value()); 12621 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value()); 12622 12623 result = CompileRun("for (var i = 0; i < 8; i++) {" 12624 " pixels[7] = undefined;" 12625 "}" 12626 "pixels[7];"); 12627 CHECK_EQ(0, result->Int32Value()); 12628 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value()); 12629 12630 result = CompileRun("for (var i = 0; i < 8; i++) {" 12631 " pixels[6] = '2.3';" 12632 "}" 12633 "pixels[6];"); 12634 CHECK_EQ(2, result->Int32Value()); 12635 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value()); 12636 12637 result = CompileRun("for (var i = 0; i < 8; i++) {" 12638 " pixels[5] = NaN;" 12639 "}" 12640 "pixels[5];"); 12641 CHECK_EQ(0, result->Int32Value()); 12642 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 12643 12644 result = CompileRun("for (var i = 0; i < 8; i++) {" 12645 " pixels[8] = Infinity;" 12646 "}" 12647 "pixels[8];"); 12648 CHECK_EQ(255, result->Int32Value()); 12649 CHECK_EQ(255, 12650 i::Smi::cast(jsobj->GetElement(8)->ToObjectChecked())->value()); 12651 12652 result = CompileRun("for (var i = 0; i < 8; i++) {" 12653 " pixels[9] = -Infinity;" 12654 "}" 12655 "pixels[9];"); 12656 CHECK_EQ(0, result->Int32Value()); 12657 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9)->ToObjectChecked())->value()); 12658 12659 result = CompileRun("pixels[3] = 33;" 12660 "delete pixels[3];" 12661 "pixels[3];"); 12662 CHECK_EQ(33, result->Int32Value()); 12663 12664 result = CompileRun("pixels[0] = 10; pixels[1] = 11;" 12665 "pixels[2] = 12; pixels[3] = 13;" 12666 "pixels.__defineGetter__('2'," 12667 "function() { return 120; });" 12668 "pixels[2];"); 12669 CHECK_EQ(12, result->Int32Value()); 12670 12671 result = CompileRun("var js_array = new Array(40);" 12672 "js_array[0] = 77;" 12673 "js_array;"); 12674 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 12675 12676 result = CompileRun("pixels[1] = 23;" 12677 "pixels.__proto__ = [];" 12678 "js_array.__proto__ = pixels;" 12679 "js_array.concat(pixels);"); 12680 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 12681 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 12682 12683 result = CompileRun("pixels[1] = 23;"); 12684 CHECK_EQ(23, result->Int32Value()); 12685 12686 // Test for index greater than 255. Regression test for: 12687 // http://code.google.com/p/chromium/issues/detail?id=26337. 12688 result = CompileRun("pixels[256] = 255;"); 12689 CHECK_EQ(255, result->Int32Value()); 12690 result = CompileRun("var i = 0;" 12691 "for (var j = 0; j < 8; j++) { i = pixels[256]; }" 12692 "i"); 12693 CHECK_EQ(255, result->Int32Value()); 12694 12695 // Make sure that pixel array ICs recognize when a non-pixel array 12696 // is passed to it. 12697 result = CompileRun("function pa_load(p) {" 12698 " var sum = 0;" 12699 " for (var j = 0; j < 256; j++) { sum += p[j]; }" 12700 " return sum;" 12701 "}" 12702 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" 12703 "for (var i = 0; i < 10; ++i) { pa_load(pixels); }" 12704 "just_ints = new Object();" 12705 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" 12706 "for (var i = 0; i < 10; ++i) {" 12707 " result = pa_load(just_ints);" 12708 "}" 12709 "result"); 12710 CHECK_EQ(32640, result->Int32Value()); 12711 12712 // Make sure that pixel array ICs recognize out-of-bound accesses. 12713 result = CompileRun("function pa_load(p, start) {" 12714 " var sum = 0;" 12715 " for (var j = start; j < 256; j++) { sum += p[j]; }" 12716 " return sum;" 12717 "}" 12718 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" 12719 "for (var i = 0; i < 10; ++i) { pa_load(pixels,0); }" 12720 "for (var i = 0; i < 10; ++i) {" 12721 " result = pa_load(pixels,-10);" 12722 "}" 12723 "result"); 12724 CHECK_EQ(0, result->Int32Value()); 12725 12726 // Make sure that generic ICs properly handles a pixel array. 12727 result = CompileRun("function pa_load(p) {" 12728 " var sum = 0;" 12729 " for (var j = 0; j < 256; j++) { sum += p[j]; }" 12730 " return sum;" 12731 "}" 12732 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" 12733 "just_ints = new Object();" 12734 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" 12735 "for (var i = 0; i < 10; ++i) { pa_load(just_ints); }" 12736 "for (var i = 0; i < 10; ++i) {" 12737 " result = pa_load(pixels);" 12738 "}" 12739 "result"); 12740 CHECK_EQ(32640, result->Int32Value()); 12741 12742 // Make sure that generic load ICs recognize out-of-bound accesses in 12743 // pixel arrays. 12744 result = CompileRun("function pa_load(p, start) {" 12745 " var sum = 0;" 12746 " for (var j = start; j < 256; j++) { sum += p[j]; }" 12747 " return sum;" 12748 "}" 12749 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" 12750 "just_ints = new Object();" 12751 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" 12752 "for (var i = 0; i < 10; ++i) { pa_load(just_ints,0); }" 12753 "for (var i = 0; i < 10; ++i) { pa_load(pixels,0); }" 12754 "for (var i = 0; i < 10; ++i) {" 12755 " result = pa_load(pixels,-10);" 12756 "}" 12757 "result"); 12758 CHECK_EQ(0, result->Int32Value()); 12759 12760 // Make sure that generic ICs properly handles other types than pixel 12761 // arrays (that the inlined fast pixel array test leaves the right information 12762 // in the right registers). 12763 result = CompileRun("function pa_load(p) {" 12764 " var sum = 0;" 12765 " for (var j = 0; j < 256; j++) { sum += p[j]; }" 12766 " return sum;" 12767 "}" 12768 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" 12769 "just_ints = new Object();" 12770 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" 12771 "for (var i = 0; i < 10; ++i) { pa_load(just_ints); }" 12772 "for (var i = 0; i < 10; ++i) { pa_load(pixels); }" 12773 "sparse_array = new Object();" 12774 "for (var i = 0; i < 256; ++i) { sparse_array[i] = i; }" 12775 "sparse_array[1000000] = 3;" 12776 "for (var i = 0; i < 10; ++i) {" 12777 " result = pa_load(sparse_array);" 12778 "}" 12779 "result"); 12780 CHECK_EQ(32640, result->Int32Value()); 12781 12782 // Make sure that pixel array store ICs clamp values correctly. 12783 result = CompileRun("function pa_store(p) {" 12784 " for (var j = 0; j < 256; j++) { p[j] = j * 2; }" 12785 "}" 12786 "pa_store(pixels);" 12787 "var sum = 0;" 12788 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }" 12789 "sum"); 12790 CHECK_EQ(48896, result->Int32Value()); 12791 12792 // Make sure that pixel array stores correctly handle accesses outside 12793 // of the pixel array.. 12794 result = CompileRun("function pa_store(p,start) {" 12795 " for (var j = 0; j < 256; j++) {" 12796 " p[j+start] = j * 2;" 12797 " }" 12798 "}" 12799 "pa_store(pixels,0);" 12800 "pa_store(pixels,-128);" 12801 "var sum = 0;" 12802 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }" 12803 "sum"); 12804 CHECK_EQ(65280, result->Int32Value()); 12805 12806 // Make sure that the generic store stub correctly handle accesses outside 12807 // of the pixel array.. 12808 result = CompileRun("function pa_store(p,start) {" 12809 " for (var j = 0; j < 256; j++) {" 12810 " p[j+start] = j * 2;" 12811 " }" 12812 "}" 12813 "pa_store(pixels,0);" 12814 "just_ints = new Object();" 12815 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" 12816 "pa_store(just_ints, 0);" 12817 "pa_store(pixels,-128);" 12818 "var sum = 0;" 12819 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }" 12820 "sum"); 12821 CHECK_EQ(65280, result->Int32Value()); 12822 12823 // Make sure that the generic keyed store stub clamps pixel array values 12824 // correctly. 12825 result = CompileRun("function pa_store(p) {" 12826 " for (var j = 0; j < 256; j++) { p[j] = j * 2; }" 12827 "}" 12828 "pa_store(pixels);" 12829 "just_ints = new Object();" 12830 "pa_store(just_ints);" 12831 "pa_store(pixels);" 12832 "var sum = 0;" 12833 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }" 12834 "sum"); 12835 CHECK_EQ(48896, result->Int32Value()); 12836 12837 // Make sure that pixel array loads are optimized by crankshaft. 12838 result = CompileRun("function pa_load(p) {" 12839 " var sum = 0;" 12840 " for (var i=0; i<256; ++i) {" 12841 " sum += p[i];" 12842 " }" 12843 " return sum; " 12844 "}" 12845 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" 12846 "for (var i = 0; i < 5000; ++i) {" 12847 " result = pa_load(pixels);" 12848 "}" 12849 "result"); 12850 CHECK_EQ(32640, result->Int32Value()); 12851 12852 // Make sure that pixel array stores are optimized by crankshaft. 12853 result = CompileRun("function pa_init(p) {" 12854 "for (var i = 0; i < 256; ++i) { p[i] = i; }" 12855 "}" 12856 "function pa_load(p) {" 12857 " var sum = 0;" 12858 " for (var i=0; i<256; ++i) {" 12859 " sum += p[i];" 12860 " }" 12861 " return sum; " 12862 "}" 12863 "for (var i = 0; i < 5000; ++i) {" 12864 " pa_init(pixels);" 12865 "}" 12866 "result = pa_load(pixels);" 12867 "result"); 12868 CHECK_EQ(32640, result->Int32Value()); 12869 12870 free(pixel_data); 12871 } 12872 12873 12874 THREADED_TEST(PixelArrayInfo) { 12875 v8::HandleScope scope; 12876 LocalContext context; 12877 for (int size = 0; size < 100; size += 10) { 12878 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size)); 12879 v8::Handle<v8::Object> obj = v8::Object::New(); 12880 obj->SetIndexedPropertiesToPixelData(pixel_data, size); 12881 CHECK(obj->HasIndexedPropertiesInPixelData()); 12882 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData()); 12883 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength()); 12884 free(pixel_data); 12885 } 12886 } 12887 12888 12889 static v8::Handle<Value> NotHandledIndexedPropertyGetter( 12890 uint32_t index, 12891 const AccessorInfo& info) { 12892 ApiTestFuzzer::Fuzz(); 12893 return v8::Handle<Value>(); 12894 } 12895 12896 12897 static v8::Handle<Value> NotHandledIndexedPropertySetter( 12898 uint32_t index, 12899 Local<Value> value, 12900 const AccessorInfo& info) { 12901 ApiTestFuzzer::Fuzz(); 12902 return v8::Handle<Value>(); 12903 } 12904 12905 12906 THREADED_TEST(PixelArrayWithInterceptor) { 12907 v8::HandleScope scope; 12908 LocalContext context; 12909 const int kElementCount = 260; 12910 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); 12911 i::Handle<i::ExternalPixelArray> pixels = 12912 i::Handle<i::ExternalPixelArray>::cast( 12913 FACTORY->NewExternalArray(kElementCount, 12914 v8::kExternalPixelArray, 12915 pixel_data)); 12916 for (int i = 0; i < kElementCount; i++) { 12917 pixels->set(i, i % 256); 12918 } 12919 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 12920 templ->SetIndexedPropertyHandler(NotHandledIndexedPropertyGetter, 12921 NotHandledIndexedPropertySetter); 12922 v8::Handle<v8::Object> obj = templ->NewInstance(); 12923 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount); 12924 context->Global()->Set(v8_str("pixels"), obj); 12925 v8::Handle<v8::Value> result = CompileRun("pixels[1]"); 12926 CHECK_EQ(1, result->Int32Value()); 12927 result = CompileRun("var sum = 0;" 12928 "for (var i = 0; i < 8; i++) {" 12929 " sum += pixels[i] = pixels[i] = -i;" 12930 "}" 12931 "sum;"); 12932 CHECK_EQ(-28, result->Int32Value()); 12933 result = CompileRun("pixels.hasOwnProperty('1')"); 12934 CHECK(result->BooleanValue()); 12935 free(pixel_data); 12936 } 12937 12938 12939 static int ExternalArrayElementSize(v8::ExternalArrayType array_type) { 12940 switch (array_type) { 12941 case v8::kExternalByteArray: 12942 case v8::kExternalUnsignedByteArray: 12943 case v8::kExternalPixelArray: 12944 return 1; 12945 break; 12946 case v8::kExternalShortArray: 12947 case v8::kExternalUnsignedShortArray: 12948 return 2; 12949 break; 12950 case v8::kExternalIntArray: 12951 case v8::kExternalUnsignedIntArray: 12952 case v8::kExternalFloatArray: 12953 return 4; 12954 break; 12955 case v8::kExternalDoubleArray: 12956 return 8; 12957 break; 12958 default: 12959 UNREACHABLE(); 12960 return -1; 12961 } 12962 UNREACHABLE(); 12963 return -1; 12964 } 12965 12966 12967 template <class ExternalArrayClass, class ElementType> 12968 static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, 12969 int64_t low, 12970 int64_t high) { 12971 v8::HandleScope scope; 12972 LocalContext context; 12973 const int kElementCount = 40; 12974 int element_size = ExternalArrayElementSize(array_type); 12975 ElementType* array_data = 12976 static_cast<ElementType*>(malloc(kElementCount * element_size)); 12977 i::Handle<ExternalArrayClass> array = 12978 i::Handle<ExternalArrayClass>::cast( 12979 FACTORY->NewExternalArray(kElementCount, array_type, array_data)); 12980 // Force GC to trigger verification. 12981 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 12982 for (int i = 0; i < kElementCount; i++) { 12983 array->set(i, static_cast<ElementType>(i)); 12984 } 12985 // Force GC to trigger verification. 12986 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 12987 for (int i = 0; i < kElementCount; i++) { 12988 CHECK_EQ(static_cast<int64_t>(i), 12989 static_cast<int64_t>(array->get_scalar(i))); 12990 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i])); 12991 } 12992 12993 v8::Handle<v8::Object> obj = v8::Object::New(); 12994 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 12995 // Set the elements to be the external array. 12996 obj->SetIndexedPropertiesToExternalArrayData(array_data, 12997 array_type, 12998 kElementCount); 12999 CHECK_EQ( 13000 1, static_cast<int>(jsobj->GetElement(1)->ToObjectChecked()->Number())); 13001 obj->Set(v8_str("field"), v8::Int32::New(1503)); 13002 context->Global()->Set(v8_str("ext_array"), obj); 13003 v8::Handle<v8::Value> result = CompileRun("ext_array.field"); 13004 CHECK_EQ(1503, result->Int32Value()); 13005 result = CompileRun("ext_array[1]"); 13006 CHECK_EQ(1, result->Int32Value()); 13007 13008 // Check pass through of assigned smis 13009 result = CompileRun("var sum = 0;" 13010 "for (var i = 0; i < 8; i++) {" 13011 " sum += ext_array[i] = ext_array[i] = -i;" 13012 "}" 13013 "sum;"); 13014 CHECK_EQ(-28, result->Int32Value()); 13015 13016 // Check assigned smis 13017 result = CompileRun("for (var i = 0; i < 8; i++) {" 13018 " ext_array[i] = i;" 13019 "}" 13020 "var sum = 0;" 13021 "for (var i = 0; i < 8; i++) {" 13022 " sum += ext_array[i];" 13023 "}" 13024 "sum;"); 13025 CHECK_EQ(28, result->Int32Value()); 13026 13027 // Check assigned smis in reverse order 13028 result = CompileRun("for (var i = 8; --i >= 0; ) {" 13029 " ext_array[i] = i;" 13030 "}" 13031 "var sum = 0;" 13032 "for (var i = 0; i < 8; i++) {" 13033 " sum += ext_array[i];" 13034 "}" 13035 "sum;"); 13036 CHECK_EQ(28, result->Int32Value()); 13037 13038 // Check pass through of assigned HeapNumbers 13039 result = CompileRun("var sum = 0;" 13040 "for (var i = 0; i < 16; i+=2) {" 13041 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);" 13042 "}" 13043 "sum;"); 13044 CHECK_EQ(-28, result->Int32Value()); 13045 13046 // Check assigned HeapNumbers 13047 result = CompileRun("for (var i = 0; i < 16; i+=2) {" 13048 " ext_array[i] = (i * 0.5);" 13049 "}" 13050 "var sum = 0;" 13051 "for (var i = 0; i < 16; i+=2) {" 13052 " sum += ext_array[i];" 13053 "}" 13054 "sum;"); 13055 CHECK_EQ(28, result->Int32Value()); 13056 13057 // Check assigned HeapNumbers in reverse order 13058 result = CompileRun("for (var i = 14; i >= 0; i-=2) {" 13059 " ext_array[i] = (i * 0.5);" 13060 "}" 13061 "var sum = 0;" 13062 "for (var i = 0; i < 16; i+=2) {" 13063 " sum += ext_array[i];" 13064 "}" 13065 "sum;"); 13066 CHECK_EQ(28, result->Int32Value()); 13067 13068 i::ScopedVector<char> test_buf(1024); 13069 13070 // Check legal boundary conditions. 13071 // The repeated loads and stores ensure the ICs are exercised. 13072 const char* boundary_program = 13073 "var res = 0;" 13074 "for (var i = 0; i < 16; i++) {" 13075 " ext_array[i] = %lld;" 13076 " if (i > 8) {" 13077 " res = ext_array[i];" 13078 " }" 13079 "}" 13080 "res;"; 13081 i::OS::SNPrintF(test_buf, 13082 boundary_program, 13083 low); 13084 result = CompileRun(test_buf.start()); 13085 CHECK_EQ(low, result->IntegerValue()); 13086 13087 i::OS::SNPrintF(test_buf, 13088 boundary_program, 13089 high); 13090 result = CompileRun(test_buf.start()); 13091 CHECK_EQ(high, result->IntegerValue()); 13092 13093 // Check misprediction of type in IC. 13094 result = CompileRun("var tmp_array = ext_array;" 13095 "var sum = 0;" 13096 "for (var i = 0; i < 8; i++) {" 13097 " tmp_array[i] = i;" 13098 " sum += tmp_array[i];" 13099 " if (i == 4) {" 13100 " tmp_array = {};" 13101 " }" 13102 "}" 13103 "sum;"); 13104 // Force GC to trigger verification. 13105 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 13106 CHECK_EQ(28, result->Int32Value()); 13107 13108 // Make sure out-of-range loads do not throw. 13109 i::OS::SNPrintF(test_buf, 13110 "var caught_exception = false;" 13111 "try {" 13112 " ext_array[%d];" 13113 "} catch (e) {" 13114 " caught_exception = true;" 13115 "}" 13116 "caught_exception;", 13117 kElementCount); 13118 result = CompileRun(test_buf.start()); 13119 CHECK_EQ(false, result->BooleanValue()); 13120 13121 // Make sure out-of-range stores do not throw. 13122 i::OS::SNPrintF(test_buf, 13123 "var caught_exception = false;" 13124 "try {" 13125 " ext_array[%d] = 1;" 13126 "} catch (e) {" 13127 " caught_exception = true;" 13128 "}" 13129 "caught_exception;", 13130 kElementCount); 13131 result = CompileRun(test_buf.start()); 13132 CHECK_EQ(false, result->BooleanValue()); 13133 13134 // Check other boundary conditions, values and operations. 13135 result = CompileRun("for (var i = 0; i < 8; i++) {" 13136 " ext_array[7] = undefined;" 13137 "}" 13138 "ext_array[7];"); 13139 CHECK_EQ(0, result->Int32Value()); 13140 if (array_type == v8::kExternalDoubleArray || 13141 array_type == v8::kExternalFloatArray) { 13142 CHECK_EQ( 13143 static_cast<int>(i::OS::nan_value()), 13144 static_cast<int>(jsobj->GetElement(7)->ToObjectChecked()->Number())); 13145 } else { 13146 CHECK_EQ(0, static_cast<int>( 13147 jsobj->GetElement(7)->ToObjectChecked()->Number())); 13148 } 13149 13150 result = CompileRun("for (var i = 0; i < 8; i++) {" 13151 " ext_array[6] = '2.3';" 13152 "}" 13153 "ext_array[6];"); 13154 CHECK_EQ(2, result->Int32Value()); 13155 CHECK_EQ( 13156 2, static_cast<int>(jsobj->GetElement(6)->ToObjectChecked()->Number())); 13157 13158 if (array_type != v8::kExternalFloatArray && 13159 array_type != v8::kExternalDoubleArray) { 13160 // Though the specification doesn't state it, be explicit about 13161 // converting NaNs and +/-Infinity to zero. 13162 result = CompileRun("for (var i = 0; i < 8; i++) {" 13163 " ext_array[i] = 5;" 13164 "}" 13165 "for (var i = 0; i < 8; i++) {" 13166 " ext_array[i] = NaN;" 13167 "}" 13168 "ext_array[5];"); 13169 CHECK_EQ(0, result->Int32Value()); 13170 CHECK_EQ(0, 13171 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 13172 13173 result = CompileRun("for (var i = 0; i < 8; i++) {" 13174 " ext_array[i] = 5;" 13175 "}" 13176 "for (var i = 0; i < 8; i++) {" 13177 " ext_array[i] = Infinity;" 13178 "}" 13179 "ext_array[5];"); 13180 int expected_value = 13181 (array_type == v8::kExternalPixelArray) ? 255 : 0; 13182 CHECK_EQ(expected_value, result->Int32Value()); 13183 CHECK_EQ(expected_value, 13184 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 13185 13186 result = CompileRun("for (var i = 0; i < 8; i++) {" 13187 " ext_array[i] = 5;" 13188 "}" 13189 "for (var i = 0; i < 8; i++) {" 13190 " ext_array[i] = -Infinity;" 13191 "}" 13192 "ext_array[5];"); 13193 CHECK_EQ(0, result->Int32Value()); 13194 CHECK_EQ(0, 13195 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 13196 13197 // Check truncation behavior of integral arrays. 13198 const char* unsigned_data = 13199 "var source_data = [0.6, 10.6];" 13200 "var expected_results = [0, 10];"; 13201 const char* signed_data = 13202 "var source_data = [0.6, 10.6, -0.6, -10.6];" 13203 "var expected_results = [0, 10, 0, -10];"; 13204 const char* pixel_data = 13205 "var source_data = [0.6, 10.6];" 13206 "var expected_results = [1, 11];"; 13207 bool is_unsigned = 13208 (array_type == v8::kExternalUnsignedByteArray || 13209 array_type == v8::kExternalUnsignedShortArray || 13210 array_type == v8::kExternalUnsignedIntArray); 13211 bool is_pixel_data = array_type == v8::kExternalPixelArray; 13212 13213 i::OS::SNPrintF(test_buf, 13214 "%s" 13215 "var all_passed = true;" 13216 "for (var i = 0; i < source_data.length; i++) {" 13217 " for (var j = 0; j < 8; j++) {" 13218 " ext_array[j] = source_data[i];" 13219 " }" 13220 " all_passed = all_passed &&" 13221 " (ext_array[5] == expected_results[i]);" 13222 "}" 13223 "all_passed;", 13224 (is_unsigned ? 13225 unsigned_data : 13226 (is_pixel_data ? pixel_data : signed_data))); 13227 result = CompileRun(test_buf.start()); 13228 CHECK_EQ(true, result->BooleanValue()); 13229 } 13230 13231 for (int i = 0; i < kElementCount; i++) { 13232 array->set(i, static_cast<ElementType>(i)); 13233 } 13234 // Test complex assignments 13235 result = CompileRun("function ee_op_test_complex_func(sum) {" 13236 " for (var i = 0; i < 40; ++i) {" 13237 " sum += (ext_array[i] += 1);" 13238 " sum += (ext_array[i] -= 1);" 13239 " } " 13240 " return sum;" 13241 "}" 13242 "sum=0;" 13243 "for (var i=0;i<10000;++i) {" 13244 " sum=ee_op_test_complex_func(sum);" 13245 "}" 13246 "sum;"); 13247 CHECK_EQ(16000000, result->Int32Value()); 13248 13249 // Test count operations 13250 result = CompileRun("function ee_op_test_count_func(sum) {" 13251 " for (var i = 0; i < 40; ++i) {" 13252 " sum += (++ext_array[i]);" 13253 " sum += (--ext_array[i]);" 13254 " } " 13255 " return sum;" 13256 "}" 13257 "sum=0;" 13258 "for (var i=0;i<10000;++i) {" 13259 " sum=ee_op_test_count_func(sum);" 13260 "}" 13261 "sum;"); 13262 CHECK_EQ(16000000, result->Int32Value()); 13263 13264 result = CompileRun("ext_array[3] = 33;" 13265 "delete ext_array[3];" 13266 "ext_array[3];"); 13267 CHECK_EQ(33, result->Int32Value()); 13268 13269 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;" 13270 "ext_array[2] = 12; ext_array[3] = 13;" 13271 "ext_array.__defineGetter__('2'," 13272 "function() { return 120; });" 13273 "ext_array[2];"); 13274 CHECK_EQ(12, result->Int32Value()); 13275 13276 result = CompileRun("var js_array = new Array(40);" 13277 "js_array[0] = 77;" 13278 "js_array;"); 13279 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 13280 13281 result = CompileRun("ext_array[1] = 23;" 13282 "ext_array.__proto__ = [];" 13283 "js_array.__proto__ = ext_array;" 13284 "js_array.concat(ext_array);"); 13285 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 13286 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 13287 13288 result = CompileRun("ext_array[1] = 23;"); 13289 CHECK_EQ(23, result->Int32Value()); 13290 13291 // Test more complex manipulations which cause eax to contain values 13292 // that won't be completely overwritten by loads from the arrays. 13293 // This catches bugs in the instructions used for the KeyedLoadIC 13294 // for byte and word types. 13295 { 13296 const int kXSize = 300; 13297 const int kYSize = 300; 13298 const int kLargeElementCount = kXSize * kYSize * 4; 13299 ElementType* large_array_data = 13300 static_cast<ElementType*>(malloc(kLargeElementCount * element_size)); 13301 v8::Handle<v8::Object> large_obj = v8::Object::New(); 13302 // Set the elements to be the external array. 13303 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data, 13304 array_type, 13305 kLargeElementCount); 13306 context->Global()->Set(v8_str("large_array"), large_obj); 13307 // Initialize contents of a few rows. 13308 for (int x = 0; x < 300; x++) { 13309 int row = 0; 13310 int offset = row * 300 * 4; 13311 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 13312 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 13313 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 13314 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 13315 row = 150; 13316 offset = row * 300 * 4; 13317 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 13318 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 13319 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 13320 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 13321 row = 298; 13322 offset = row * 300 * 4; 13323 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 13324 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 13325 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 13326 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 13327 } 13328 // The goal of the code below is to make "offset" large enough 13329 // that the computation of the index (which goes into eax) has 13330 // high bits set which will not be overwritten by a byte or short 13331 // load. 13332 result = CompileRun("var failed = false;" 13333 "var offset = 0;" 13334 "for (var i = 0; i < 300; i++) {" 13335 " if (large_array[4 * i] != 127 ||" 13336 " large_array[4 * i + 1] != 0 ||" 13337 " large_array[4 * i + 2] != 0 ||" 13338 " large_array[4 * i + 3] != 127) {" 13339 " failed = true;" 13340 " }" 13341 "}" 13342 "offset = 150 * 300 * 4;" 13343 "for (var i = 0; i < 300; i++) {" 13344 " if (large_array[offset + 4 * i] != 127 ||" 13345 " large_array[offset + 4 * i + 1] != 0 ||" 13346 " large_array[offset + 4 * i + 2] != 0 ||" 13347 " large_array[offset + 4 * i + 3] != 127) {" 13348 " failed = true;" 13349 " }" 13350 "}" 13351 "offset = 298 * 300 * 4;" 13352 "for (var i = 0; i < 300; i++) {" 13353 " if (large_array[offset + 4 * i] != 127 ||" 13354 " large_array[offset + 4 * i + 1] != 0 ||" 13355 " large_array[offset + 4 * i + 2] != 0 ||" 13356 " large_array[offset + 4 * i + 3] != 127) {" 13357 " failed = true;" 13358 " }" 13359 "}" 13360 "!failed;"); 13361 CHECK_EQ(true, result->BooleanValue()); 13362 free(large_array_data); 13363 } 13364 13365 // The "" property descriptor is overloaded to store information about 13366 // the external array. Ensure that setting and accessing the "" property 13367 // works (it should overwrite the information cached about the external 13368 // array in the DescriptorArray) in various situations. 13369 result = CompileRun("ext_array[''] = 23; ext_array['']"); 13370 CHECK_EQ(23, result->Int32Value()); 13371 13372 // Property "" set after the external array is associated with the object. 13373 { 13374 v8::Handle<v8::Object> obj2 = v8::Object::New(); 13375 obj2->Set(v8_str("ee_test_field"), v8::Int32::New(256)); 13376 obj2->Set(v8_str(""), v8::Int32::New(1503)); 13377 // Set the elements to be the external array. 13378 obj2->SetIndexedPropertiesToExternalArrayData(array_data, 13379 array_type, 13380 kElementCount); 13381 context->Global()->Set(v8_str("ext_array"), obj2); 13382 result = CompileRun("ext_array['']"); 13383 CHECK_EQ(1503, result->Int32Value()); 13384 } 13385 13386 // Property "" set after the external array is associated with the object. 13387 { 13388 v8::Handle<v8::Object> obj2 = v8::Object::New(); 13389 obj2->Set(v8_str("ee_test_field_2"), v8::Int32::New(256)); 13390 // Set the elements to be the external array. 13391 obj2->SetIndexedPropertiesToExternalArrayData(array_data, 13392 array_type, 13393 kElementCount); 13394 obj2->Set(v8_str(""), v8::Int32::New(1503)); 13395 context->Global()->Set(v8_str("ext_array"), obj2); 13396 result = CompileRun("ext_array['']"); 13397 CHECK_EQ(1503, result->Int32Value()); 13398 } 13399 13400 // Should reuse the map from previous test. 13401 { 13402 v8::Handle<v8::Object> obj2 = v8::Object::New(); 13403 obj2->Set(v8_str("ee_test_field_2"), v8::Int32::New(256)); 13404 // Set the elements to be the external array. Should re-use the map 13405 // from previous test. 13406 obj2->SetIndexedPropertiesToExternalArrayData(array_data, 13407 array_type, 13408 kElementCount); 13409 context->Global()->Set(v8_str("ext_array"), obj2); 13410 result = CompileRun("ext_array['']"); 13411 } 13412 13413 // Property "" is a constant function that shouldn't not be interfered with 13414 // when an external array is set. 13415 { 13416 v8::Handle<v8::Object> obj2 = v8::Object::New(); 13417 // Start 13418 obj2->Set(v8_str("ee_test_field3"), v8::Int32::New(256)); 13419 13420 // Add a constant function to an object. 13421 context->Global()->Set(v8_str("ext_array"), obj2); 13422 result = CompileRun("ext_array[''] = function() {return 1503;};" 13423 "ext_array['']();"); 13424 13425 // Add an external array transition to the same map that 13426 // has the constant transition. 13427 v8::Handle<v8::Object> obj3 = v8::Object::New(); 13428 obj3->Set(v8_str("ee_test_field3"), v8::Int32::New(256)); 13429 obj3->SetIndexedPropertiesToExternalArrayData(array_data, 13430 array_type, 13431 kElementCount); 13432 context->Global()->Set(v8_str("ext_array"), obj3); 13433 } 13434 13435 // If a external array transition is in the map, it should get clobbered 13436 // by a constant function. 13437 { 13438 // Add an external array transition. 13439 v8::Handle<v8::Object> obj3 = v8::Object::New(); 13440 obj3->Set(v8_str("ee_test_field4"), v8::Int32::New(256)); 13441 obj3->SetIndexedPropertiesToExternalArrayData(array_data, 13442 array_type, 13443 kElementCount); 13444 13445 // Add a constant function to the same map that just got an external array 13446 // transition. 13447 v8::Handle<v8::Object> obj2 = v8::Object::New(); 13448 obj2->Set(v8_str("ee_test_field4"), v8::Int32::New(256)); 13449 context->Global()->Set(v8_str("ext_array"), obj2); 13450 result = CompileRun("ext_array[''] = function() {return 1503;};" 13451 "ext_array['']();"); 13452 } 13453 13454 free(array_data); 13455 } 13456 13457 13458 THREADED_TEST(ExternalByteArray) { 13459 ExternalArrayTestHelper<i::ExternalByteArray, int8_t>( 13460 v8::kExternalByteArray, 13461 -128, 13462 127); 13463 } 13464 13465 13466 THREADED_TEST(ExternalUnsignedByteArray) { 13467 ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>( 13468 v8::kExternalUnsignedByteArray, 13469 0, 13470 255); 13471 } 13472 13473 13474 THREADED_TEST(ExternalPixelArray) { 13475 ExternalArrayTestHelper<i::ExternalPixelArray, uint8_t>( 13476 v8::kExternalPixelArray, 13477 0, 13478 255); 13479 } 13480 13481 13482 THREADED_TEST(ExternalShortArray) { 13483 ExternalArrayTestHelper<i::ExternalShortArray, int16_t>( 13484 v8::kExternalShortArray, 13485 -32768, 13486 32767); 13487 } 13488 13489 13490 THREADED_TEST(ExternalUnsignedShortArray) { 13491 ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>( 13492 v8::kExternalUnsignedShortArray, 13493 0, 13494 65535); 13495 } 13496 13497 13498 THREADED_TEST(ExternalIntArray) { 13499 ExternalArrayTestHelper<i::ExternalIntArray, int32_t>( 13500 v8::kExternalIntArray, 13501 INT_MIN, // -2147483648 13502 INT_MAX); // 2147483647 13503 } 13504 13505 13506 THREADED_TEST(ExternalUnsignedIntArray) { 13507 ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>( 13508 v8::kExternalUnsignedIntArray, 13509 0, 13510 UINT_MAX); // 4294967295 13511 } 13512 13513 13514 THREADED_TEST(ExternalFloatArray) { 13515 ExternalArrayTestHelper<i::ExternalFloatArray, float>( 13516 v8::kExternalFloatArray, 13517 -500, 13518 500); 13519 } 13520 13521 13522 THREADED_TEST(ExternalDoubleArray) { 13523 ExternalArrayTestHelper<i::ExternalDoubleArray, double>( 13524 v8::kExternalDoubleArray, 13525 -500, 13526 500); 13527 } 13528 13529 13530 THREADED_TEST(ExternalArrays) { 13531 TestExternalByteArray(); 13532 TestExternalUnsignedByteArray(); 13533 TestExternalShortArray(); 13534 TestExternalUnsignedShortArray(); 13535 TestExternalIntArray(); 13536 TestExternalUnsignedIntArray(); 13537 TestExternalFloatArray(); 13538 } 13539 13540 13541 void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) { 13542 v8::HandleScope scope; 13543 LocalContext context; 13544 for (int size = 0; size < 100; size += 10) { 13545 int element_size = ExternalArrayElementSize(array_type); 13546 void* external_data = malloc(size * element_size); 13547 v8::Handle<v8::Object> obj = v8::Object::New(); 13548 obj->SetIndexedPropertiesToExternalArrayData( 13549 external_data, array_type, size); 13550 CHECK(obj->HasIndexedPropertiesInExternalArrayData()); 13551 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData()); 13552 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType()); 13553 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength()); 13554 free(external_data); 13555 } 13556 } 13557 13558 13559 THREADED_TEST(ExternalArrayInfo) { 13560 ExternalArrayInfoTestHelper(v8::kExternalByteArray); 13561 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray); 13562 ExternalArrayInfoTestHelper(v8::kExternalShortArray); 13563 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray); 13564 ExternalArrayInfoTestHelper(v8::kExternalIntArray); 13565 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray); 13566 ExternalArrayInfoTestHelper(v8::kExternalFloatArray); 13567 ExternalArrayInfoTestHelper(v8::kExternalDoubleArray); 13568 ExternalArrayInfoTestHelper(v8::kExternalPixelArray); 13569 } 13570 13571 13572 THREADED_TEST(ScriptContextDependence) { 13573 v8::HandleScope scope; 13574 LocalContext c1; 13575 const char *source = "foo"; 13576 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source)); 13577 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source)); 13578 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100)); 13579 CHECK_EQ(dep->Run()->Int32Value(), 100); 13580 CHECK_EQ(indep->Run()->Int32Value(), 100); 13581 LocalContext c2; 13582 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101)); 13583 CHECK_EQ(dep->Run()->Int32Value(), 100); 13584 CHECK_EQ(indep->Run()->Int32Value(), 101); 13585 } 13586 13587 13588 THREADED_TEST(StackTrace) { 13589 v8::HandleScope scope; 13590 LocalContext context; 13591 v8::TryCatch try_catch; 13592 const char *source = "function foo() { FAIL.FAIL; }; foo();"; 13593 v8::Handle<v8::String> src = v8::String::New(source); 13594 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test"); 13595 v8::Script::New(src, origin)->Run(); 13596 CHECK(try_catch.HasCaught()); 13597 v8::String::Utf8Value stack(try_catch.StackTrace()); 13598 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL); 13599 } 13600 13601 13602 // Checks that a StackFrame has certain expected values. 13603 void checkStackFrame(const char* expected_script_name, 13604 const char* expected_func_name, int expected_line_number, 13605 int expected_column, bool is_eval, bool is_constructor, 13606 v8::Handle<v8::StackFrame> frame) { 13607 v8::HandleScope scope; 13608 v8::String::Utf8Value func_name(frame->GetFunctionName()); 13609 v8::String::Utf8Value script_name(frame->GetScriptName()); 13610 if (*script_name == NULL) { 13611 // The situation where there is no associated script, like for evals. 13612 CHECK(expected_script_name == NULL); 13613 } else { 13614 CHECK(strstr(*script_name, expected_script_name) != NULL); 13615 } 13616 CHECK(strstr(*func_name, expected_func_name) != NULL); 13617 CHECK_EQ(expected_line_number, frame->GetLineNumber()); 13618 CHECK_EQ(expected_column, frame->GetColumn()); 13619 CHECK_EQ(is_eval, frame->IsEval()); 13620 CHECK_EQ(is_constructor, frame->IsConstructor()); 13621 } 13622 13623 13624 v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) { 13625 v8::HandleScope scope; 13626 const char* origin = "capture-stack-trace-test"; 13627 const int kOverviewTest = 1; 13628 const int kDetailedTest = 2; 13629 13630 ASSERT(args.Length() == 1); 13631 13632 int testGroup = args[0]->Int32Value(); 13633 if (testGroup == kOverviewTest) { 13634 v8::Handle<v8::StackTrace> stackTrace = 13635 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview); 13636 CHECK_EQ(4, stackTrace->GetFrameCount()); 13637 checkStackFrame(origin, "bar", 2, 10, false, false, 13638 stackTrace->GetFrame(0)); 13639 checkStackFrame(origin, "foo", 6, 3, false, false, 13640 stackTrace->GetFrame(1)); 13641 // This is the source string inside the eval which has the call to foo. 13642 checkStackFrame(NULL, "", 1, 5, false, false, 13643 stackTrace->GetFrame(2)); 13644 // The last frame is an anonymous function which has the initial eval call. 13645 checkStackFrame(origin, "", 8, 7, false, false, 13646 stackTrace->GetFrame(3)); 13647 13648 CHECK(stackTrace->AsArray()->IsArray()); 13649 } else if (testGroup == kDetailedTest) { 13650 v8::Handle<v8::StackTrace> stackTrace = 13651 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); 13652 CHECK_EQ(4, stackTrace->GetFrameCount()); 13653 checkStackFrame(origin, "bat", 4, 22, false, false, 13654 stackTrace->GetFrame(0)); 13655 checkStackFrame(origin, "baz", 8, 3, false, true, 13656 stackTrace->GetFrame(1)); 13657 #ifdef ENABLE_DEBUGGER_SUPPORT 13658 bool is_eval = true; 13659 #else // ENABLE_DEBUGGER_SUPPORT 13660 bool is_eval = false; 13661 #endif // ENABLE_DEBUGGER_SUPPORT 13662 13663 // This is the source string inside the eval which has the call to baz. 13664 checkStackFrame(NULL, "", 1, 5, is_eval, false, 13665 stackTrace->GetFrame(2)); 13666 // The last frame is an anonymous function which has the initial eval call. 13667 checkStackFrame(origin, "", 10, 1, false, false, 13668 stackTrace->GetFrame(3)); 13669 13670 CHECK(stackTrace->AsArray()->IsArray()); 13671 } 13672 return v8::Undefined(); 13673 } 13674 13675 13676 // Tests the C++ StackTrace API. 13677 // TODO(3074796): Reenable this as a THREADED_TEST once it passes. 13678 // THREADED_TEST(CaptureStackTrace) { 13679 TEST(CaptureStackTrace) { 13680 v8::HandleScope scope; 13681 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test"); 13682 Local<ObjectTemplate> templ = ObjectTemplate::New(); 13683 templ->Set(v8_str("AnalyzeStackInNativeCode"), 13684 v8::FunctionTemplate::New(AnalyzeStackInNativeCode)); 13685 LocalContext context(0, templ); 13686 13687 // Test getting OVERVIEW information. Should ignore information that is not 13688 // script name, function name, line number, and column offset. 13689 const char *overview_source = 13690 "function bar() {\n" 13691 " var y; AnalyzeStackInNativeCode(1);\n" 13692 "}\n" 13693 "function foo() {\n" 13694 "\n" 13695 " bar();\n" 13696 "}\n" 13697 "var x;eval('new foo();');"; 13698 v8::Handle<v8::String> overview_src = v8::String::New(overview_source); 13699 v8::Handle<Value> overview_result( 13700 v8::Script::New(overview_src, origin)->Run()); 13701 CHECK(!overview_result.IsEmpty()); 13702 CHECK(overview_result->IsObject()); 13703 13704 // Test getting DETAILED information. 13705 const char *detailed_source = 13706 "function bat() {AnalyzeStackInNativeCode(2);\n" 13707 "}\n" 13708 "\n" 13709 "function baz() {\n" 13710 " bat();\n" 13711 "}\n" 13712 "eval('new baz();');"; 13713 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source); 13714 // Make the script using a non-zero line and column offset. 13715 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3); 13716 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5); 13717 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset); 13718 v8::Handle<v8::Script> detailed_script( 13719 v8::Script::New(detailed_src, &detailed_origin)); 13720 v8::Handle<Value> detailed_result(detailed_script->Run()); 13721 CHECK(!detailed_result.IsEmpty()); 13722 CHECK(detailed_result->IsObject()); 13723 } 13724 13725 13726 static void StackTraceForUncaughtExceptionListener( 13727 v8::Handle<v8::Message> message, 13728 v8::Handle<Value>) { 13729 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace(); 13730 CHECK_EQ(2, stack_trace->GetFrameCount()); 13731 checkStackFrame("origin", "foo", 2, 3, false, false, 13732 stack_trace->GetFrame(0)); 13733 checkStackFrame("origin", "bar", 5, 3, false, false, 13734 stack_trace->GetFrame(1)); 13735 } 13736 13737 TEST(CaptureStackTraceForUncaughtException) { 13738 report_count = 0; 13739 v8::HandleScope scope; 13740 LocalContext env; 13741 v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener); 13742 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); 13743 13744 Script::Compile(v8_str("function foo() {\n" 13745 " throw 1;\n" 13746 "};\n" 13747 "function bar() {\n" 13748 " foo();\n" 13749 "};"), 13750 v8_str("origin"))->Run(); 13751 v8::Local<v8::Object> global = env->Global(); 13752 Local<Value> trouble = global->Get(v8_str("bar")); 13753 CHECK(trouble->IsFunction()); 13754 Function::Cast(*trouble)->Call(global, 0, NULL); 13755 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); 13756 v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener); 13757 } 13758 13759 13760 TEST(CaptureStackTraceForUncaughtExceptionAndSetters) { 13761 v8::HandleScope scope; 13762 LocalContext env; 13763 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true, 13764 1024, 13765 v8::StackTrace::kDetailed); 13766 13767 CompileRun( 13768 "var setters = ['column', 'lineNumber', 'scriptName',\n" 13769 " 'scriptNameOrSourceURL', 'functionName', 'isEval',\n" 13770 " 'isConstructor'];\n" 13771 "for (var i = 0; i < setters.length; i++) {\n" 13772 " var prop = setters[i];\n" 13773 " Object.prototype.__defineSetter__(prop, function() { throw prop; });\n" 13774 "}\n"); 13775 CompileRun("throw 'exception';"); 13776 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); 13777 } 13778 13779 13780 static void RethrowStackTraceHandler(v8::Handle<v8::Message> message, 13781 v8::Handle<v8::Value> data) { 13782 // Use the frame where JavaScript is called from. 13783 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace(); 13784 CHECK(!stack_trace.IsEmpty()); 13785 int frame_count = stack_trace->GetFrameCount(); 13786 CHECK_EQ(3, frame_count); 13787 int line_number[] = {1, 2, 5}; 13788 for (int i = 0; i < frame_count; i++) { 13789 CHECK_EQ(line_number[i], stack_trace->GetFrame(i)->GetLineNumber()); 13790 } 13791 } 13792 13793 13794 // Test that we only return the stack trace at the site where the exception 13795 // is first thrown (not where it is rethrown). 13796 TEST(RethrowStackTrace) { 13797 v8::HandleScope scope; 13798 LocalContext env; 13799 // We make sure that 13800 // - the stack trace of the ReferenceError in g() is reported. 13801 // - the stack trace is not overwritten when e1 is rethrown by t(). 13802 // - the stack trace of e2 does not overwrite that of e1. 13803 const char* source = 13804 "function g() { error; } \n" 13805 "function f() { g(); } \n" 13806 "function t(e) { throw e; } \n" 13807 "try { \n" 13808 " f(); \n" 13809 "} catch (e1) { \n" 13810 " try { \n" 13811 " error; \n" 13812 " } catch (e2) { \n" 13813 " t(e1); \n" 13814 " } \n" 13815 "} \n"; 13816 v8::V8::AddMessageListener(RethrowStackTraceHandler); 13817 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); 13818 CompileRun(source); 13819 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); 13820 v8::V8::RemoveMessageListeners(RethrowStackTraceHandler); 13821 } 13822 13823 13824 static void RethrowPrimitiveStackTraceHandler(v8::Handle<v8::Message> message, 13825 v8::Handle<v8::Value> data) { 13826 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace(); 13827 CHECK(!stack_trace.IsEmpty()); 13828 int frame_count = stack_trace->GetFrameCount(); 13829 CHECK_EQ(2, frame_count); 13830 int line_number[] = {3, 7}; 13831 for (int i = 0; i < frame_count; i++) { 13832 CHECK_EQ(line_number[i], stack_trace->GetFrame(i)->GetLineNumber()); 13833 } 13834 } 13835 13836 13837 // Test that we do not recognize identity for primitive exceptions. 13838 TEST(RethrowPrimitiveStackTrace) { 13839 v8::HandleScope scope; 13840 LocalContext env; 13841 // We do not capture stack trace for non Error objects on creation time. 13842 // Instead, we capture the stack trace on last throw. 13843 const char* source = 13844 "function g() { throw 404; } \n" 13845 "function f() { g(); } \n" 13846 "function t(e) { throw e; } \n" 13847 "try { \n" 13848 " f(); \n" 13849 "} catch (e1) { \n" 13850 " t(e1) \n" 13851 "} \n"; 13852 v8::V8::AddMessageListener(RethrowPrimitiveStackTraceHandler); 13853 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); 13854 CompileRun(source); 13855 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); 13856 v8::V8::RemoveMessageListeners(RethrowPrimitiveStackTraceHandler); 13857 } 13858 13859 13860 static void RethrowExistingStackTraceHandler(v8::Handle<v8::Message> message, 13861 v8::Handle<v8::Value> data) { 13862 // Use the frame where JavaScript is called from. 13863 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace(); 13864 CHECK(!stack_trace.IsEmpty()); 13865 CHECK_EQ(1, stack_trace->GetFrameCount()); 13866 CHECK_EQ(1, stack_trace->GetFrame(0)->GetLineNumber()); 13867 } 13868 13869 13870 // Test that the stack trace is captured when the error object is created and 13871 // not where it is thrown. 13872 TEST(RethrowExistingStackTrace) { 13873 v8::HandleScope scope; 13874 LocalContext env; 13875 const char* source = 13876 "var e = new Error(); \n" 13877 "throw e; \n"; 13878 v8::V8::AddMessageListener(RethrowExistingStackTraceHandler); 13879 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); 13880 CompileRun(source); 13881 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); 13882 v8::V8::RemoveMessageListeners(RethrowExistingStackTraceHandler); 13883 } 13884 13885 13886 static void RethrowBogusErrorStackTraceHandler(v8::Handle<v8::Message> message, 13887 v8::Handle<v8::Value> data) { 13888 // Use the frame where JavaScript is called from. 13889 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace(); 13890 CHECK(!stack_trace.IsEmpty()); 13891 CHECK_EQ(1, stack_trace->GetFrameCount()); 13892 CHECK_EQ(2, stack_trace->GetFrame(0)->GetLineNumber()); 13893 } 13894 13895 13896 // Test that the stack trace is captured where the bogus Error object is thrown. 13897 TEST(RethrowBogusErrorStackTrace) { 13898 v8::HandleScope scope; 13899 LocalContext env; 13900 const char* source = 13901 "var e = {__proto__: new Error()} \n" 13902 "throw e; \n"; 13903 v8::V8::AddMessageListener(RethrowBogusErrorStackTraceHandler); 13904 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); 13905 CompileRun(source); 13906 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); 13907 v8::V8::RemoveMessageListeners(RethrowBogusErrorStackTraceHandler); 13908 } 13909 13910 13911 v8::Handle<Value> AnalyzeStackOfEvalWithSourceURL(const v8::Arguments& args) { 13912 v8::HandleScope scope; 13913 v8::Handle<v8::StackTrace> stackTrace = 13914 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); 13915 CHECK_EQ(5, stackTrace->GetFrameCount()); 13916 v8::Handle<v8::String> url = v8_str("eval_url"); 13917 for (int i = 0; i < 3; i++) { 13918 v8::Handle<v8::String> name = 13919 stackTrace->GetFrame(i)->GetScriptNameOrSourceURL(); 13920 CHECK(!name.IsEmpty()); 13921 CHECK_EQ(url, name); 13922 } 13923 return v8::Undefined(); 13924 } 13925 13926 13927 TEST(SourceURLInStackTrace) { 13928 v8::HandleScope scope; 13929 Local<ObjectTemplate> templ = ObjectTemplate::New(); 13930 templ->Set(v8_str("AnalyzeStackOfEvalWithSourceURL"), 13931 v8::FunctionTemplate::New(AnalyzeStackOfEvalWithSourceURL)); 13932 LocalContext context(0, templ); 13933 13934 const char *source = 13935 "function outer() {\n" 13936 "function bar() {\n" 13937 " AnalyzeStackOfEvalWithSourceURL();\n" 13938 "}\n" 13939 "function foo() {\n" 13940 "\n" 13941 " bar();\n" 13942 "}\n" 13943 "foo();\n" 13944 "}\n" 13945 "eval('(' + outer +')()//@ sourceURL=eval_url');"; 13946 CHECK(CompileRun(source)->IsUndefined()); 13947 } 13948 13949 13950 // Test that idle notification can be handled and eventually returns true. 13951 // This just checks the contract of the IdleNotification() function, 13952 // and does not verify that it does reasonable work. 13953 THREADED_TEST(IdleNotification) { 13954 v8::HandleScope scope; 13955 LocalContext env; 13956 { 13957 // Create garbage in old-space to generate work for idle notification. 13958 i::AlwaysAllocateScope always_allocate; 13959 for (int i = 0; i < 100; i++) { 13960 FACTORY->NewFixedArray(1000, i::TENURED); 13961 } 13962 } 13963 bool finshed_idle_work = false; 13964 for (int i = 0; i < 100 && !finshed_idle_work; i++) { 13965 finshed_idle_work = v8::V8::IdleNotification(); 13966 } 13967 CHECK(finshed_idle_work); 13968 } 13969 13970 // Test that idle notification can be handled and eventually returns true. 13971 // This just checks the contract of the IdleNotification() function, 13972 // and does not verify that it does reasonable work. 13973 TEST(IdleNotificationWithSmallHint) { 13974 v8::HandleScope scope; 13975 LocalContext env; 13976 { 13977 // Create garbage in old-space to generate work for idle notification. 13978 i::AlwaysAllocateScope always_allocate; 13979 for (int i = 0; i < 100; i++) { 13980 FACTORY->NewFixedArray(1000, i::TENURED); 13981 } 13982 } 13983 intptr_t old_size = HEAP->SizeOfObjects(); 13984 bool finshed_idle_work = false; 13985 bool no_idle_work = v8::V8::IdleNotification(10); 13986 for (int i = 0; i < 200 && !finshed_idle_work; i++) { 13987 finshed_idle_work = v8::V8::IdleNotification(10); 13988 } 13989 intptr_t new_size = HEAP->SizeOfObjects(); 13990 CHECK(finshed_idle_work); 13991 CHECK(no_idle_work || new_size < old_size); 13992 } 13993 13994 13995 // This just checks the contract of the IdleNotification() function, 13996 // and does not verify that it does reasonable work. 13997 TEST(IdleNotificationWithLargeHint) { 13998 v8::HandleScope scope; 13999 LocalContext env; 14000 { 14001 // Create garbage in old-space to generate work for idle notification. 14002 i::AlwaysAllocateScope always_allocate; 14003 for (int i = 0; i < 100; i++) { 14004 FACTORY->NewFixedArray(1000, i::TENURED); 14005 } 14006 } 14007 intptr_t old_size = HEAP->SizeOfObjects(); 14008 bool finshed_idle_work = false; 14009 bool no_idle_work = v8::V8::IdleNotification(900); 14010 for (int i = 0; i < 200 && !finshed_idle_work; i++) { 14011 finshed_idle_work = v8::V8::IdleNotification(900); 14012 } 14013 intptr_t new_size = HEAP->SizeOfObjects(); 14014 CHECK(finshed_idle_work); 14015 CHECK(no_idle_work || new_size < old_size); 14016 } 14017 14018 14019 static uint32_t* stack_limit; 14020 14021 static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) { 14022 stack_limit = reinterpret_cast<uint32_t*>( 14023 i::Isolate::Current()->stack_guard()->real_climit()); 14024 return v8::Undefined(); 14025 } 14026 14027 14028 // Uses the address of a local variable to determine the stack top now. 14029 // Given a size, returns an address that is that far from the current 14030 // top of stack. 14031 static uint32_t* ComputeStackLimit(uint32_t size) { 14032 uint32_t* answer = &size - (size / sizeof(size)); 14033 // If the size is very large and the stack is very near the bottom of 14034 // memory then the calculation above may wrap around and give an address 14035 // that is above the (downwards-growing) stack. In that case we return 14036 // a very low address. 14037 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size)); 14038 return answer; 14039 } 14040 14041 14042 TEST(SetResourceConstraints) { 14043 static const int K = 1024; 14044 uint32_t* set_limit = ComputeStackLimit(128 * K); 14045 14046 // Set stack limit. 14047 v8::ResourceConstraints constraints; 14048 constraints.set_stack_limit(set_limit); 14049 CHECK(v8::SetResourceConstraints(&constraints)); 14050 14051 // Execute a script. 14052 v8::HandleScope scope; 14053 LocalContext env; 14054 Local<v8::FunctionTemplate> fun_templ = 14055 v8::FunctionTemplate::New(GetStackLimitCallback); 14056 Local<Function> fun = fun_templ->GetFunction(); 14057 env->Global()->Set(v8_str("get_stack_limit"), fun); 14058 CompileRun("get_stack_limit();"); 14059 14060 CHECK(stack_limit == set_limit); 14061 } 14062 14063 14064 TEST(SetResourceConstraintsInThread) { 14065 uint32_t* set_limit; 14066 { 14067 v8::Locker locker; 14068 static const int K = 1024; 14069 set_limit = ComputeStackLimit(128 * K); 14070 14071 // Set stack limit. 14072 v8::ResourceConstraints constraints; 14073 constraints.set_stack_limit(set_limit); 14074 CHECK(v8::SetResourceConstraints(&constraints)); 14075 14076 // Execute a script. 14077 v8::HandleScope scope; 14078 LocalContext env; 14079 Local<v8::FunctionTemplate> fun_templ = 14080 v8::FunctionTemplate::New(GetStackLimitCallback); 14081 Local<Function> fun = fun_templ->GetFunction(); 14082 env->Global()->Set(v8_str("get_stack_limit"), fun); 14083 CompileRun("get_stack_limit();"); 14084 14085 CHECK(stack_limit == set_limit); 14086 } 14087 { 14088 v8::Locker locker; 14089 CHECK(stack_limit == set_limit); 14090 } 14091 } 14092 14093 14094 THREADED_TEST(GetHeapStatistics) { 14095 v8::HandleScope scope; 14096 LocalContext c1; 14097 v8::HeapStatistics heap_statistics; 14098 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0); 14099 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0); 14100 v8::V8::GetHeapStatistics(&heap_statistics); 14101 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0); 14102 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0); 14103 } 14104 14105 14106 class VisitorImpl : public v8::ExternalResourceVisitor { 14107 public: 14108 VisitorImpl(TestResource* r1, TestResource* r2) 14109 : resource1_(r1), 14110 resource2_(r2), 14111 found_resource1_(false), 14112 found_resource2_(false) {} 14113 virtual ~VisitorImpl() {} 14114 virtual void VisitExternalString(v8::Handle<v8::String> string) { 14115 if (!string->IsExternal()) { 14116 CHECK(string->IsExternalAscii()); 14117 return; 14118 } 14119 v8::String::ExternalStringResource* resource = 14120 string->GetExternalStringResource(); 14121 CHECK(resource); 14122 if (resource1_ == resource) { 14123 CHECK(!found_resource1_); 14124 found_resource1_ = true; 14125 } 14126 if (resource2_ == resource) { 14127 CHECK(!found_resource2_); 14128 found_resource2_ = true; 14129 } 14130 } 14131 void CheckVisitedResources() { 14132 CHECK(found_resource1_); 14133 CHECK(found_resource2_); 14134 } 14135 14136 private: 14137 v8::String::ExternalStringResource* resource1_; 14138 v8::String::ExternalStringResource* resource2_; 14139 bool found_resource1_; 14140 bool found_resource2_; 14141 }; 14142 14143 TEST(VisitExternalStrings) { 14144 v8::HandleScope scope; 14145 LocalContext env; 14146 const char* string = "Some string"; 14147 uint16_t* two_byte_string = AsciiToTwoByteString(string); 14148 TestResource* resource1 = new TestResource(two_byte_string); 14149 v8::Local<v8::String> string1 = v8::String::NewExternal(resource1); 14150 TestResource* resource2 = new TestResource(two_byte_string); 14151 v8::Local<v8::String> string2 = v8::String::NewExternal(resource2); 14152 14153 // We need to add usages for string1 and string2 to avoid warnings in GCC 4.7 14154 CHECK(string1->IsExternal()); 14155 CHECK(string2->IsExternal()); 14156 14157 VisitorImpl visitor(resource1, resource2); 14158 v8::V8::VisitExternalResources(&visitor); 14159 visitor.CheckVisitedResources(); 14160 } 14161 14162 14163 static double DoubleFromBits(uint64_t value) { 14164 double target; 14165 memcpy(&target, &value, sizeof(target)); 14166 return target; 14167 } 14168 14169 14170 static uint64_t DoubleToBits(double value) { 14171 uint64_t target; 14172 memcpy(&target, &value, sizeof(target)); 14173 return target; 14174 } 14175 14176 14177 static double DoubleToDateTime(double input) { 14178 double date_limit = 864e13; 14179 if (IsNaN(input) || input < -date_limit || input > date_limit) { 14180 return i::OS::nan_value(); 14181 } 14182 return (input < 0) ? -(floor(-input)) : floor(input); 14183 } 14184 14185 // We don't have a consistent way to write 64-bit constants syntactically, so we 14186 // split them into two 32-bit constants and combine them programmatically. 14187 static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) { 14188 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits); 14189 } 14190 14191 14192 THREADED_TEST(QuietSignalingNaNs) { 14193 v8::HandleScope scope; 14194 LocalContext context; 14195 v8::TryCatch try_catch; 14196 14197 // Special double values. 14198 double snan = DoubleFromBits(0x7ff00000, 0x00000001); 14199 double qnan = DoubleFromBits(0x7ff80000, 0x00000000); 14200 double infinity = DoubleFromBits(0x7ff00000, 0x00000000); 14201 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu); 14202 double min_normal = DoubleFromBits(0x00100000, 0x00000000); 14203 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu); 14204 double min_denormal = DoubleFromBits(0x00000000, 0x00000001); 14205 14206 // Date values are capped at +/-100000000 days (times 864e5 ms per day) 14207 // on either side of the epoch. 14208 double date_limit = 864e13; 14209 14210 double test_values[] = { 14211 snan, 14212 qnan, 14213 infinity, 14214 max_normal, 14215 date_limit + 1, 14216 date_limit, 14217 min_normal, 14218 max_denormal, 14219 min_denormal, 14220 0, 14221 -0, 14222 -min_denormal, 14223 -max_denormal, 14224 -min_normal, 14225 -date_limit, 14226 -date_limit - 1, 14227 -max_normal, 14228 -infinity, 14229 -qnan, 14230 -snan 14231 }; 14232 int num_test_values = 20; 14233 14234 for (int i = 0; i < num_test_values; i++) { 14235 double test_value = test_values[i]; 14236 14237 // Check that Number::New preserves non-NaNs and quiets SNaNs. 14238 v8::Handle<v8::Value> number = v8::Number::New(test_value); 14239 double stored_number = number->NumberValue(); 14240 if (!IsNaN(test_value)) { 14241 CHECK_EQ(test_value, stored_number); 14242 } else { 14243 uint64_t stored_bits = DoubleToBits(stored_number); 14244 // Check if quiet nan (bits 51..62 all set). 14245 #if defined(V8_TARGET_ARCH_MIPS) && !defined(USE_SIMULATOR) 14246 // Most significant fraction bit for quiet nan is set to 0 14247 // on MIPS architecture. Allowed by IEEE-754. 14248 CHECK_EQ(0xffe, static_cast<int>((stored_bits >> 51) & 0xfff)); 14249 #else 14250 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 14251 #endif 14252 } 14253 14254 // Check that Date::New preserves non-NaNs in the date range and 14255 // quiets SNaNs. 14256 v8::Handle<v8::Value> date = v8::Date::New(test_value); 14257 double expected_stored_date = DoubleToDateTime(test_value); 14258 double stored_date = date->NumberValue(); 14259 if (!IsNaN(expected_stored_date)) { 14260 CHECK_EQ(expected_stored_date, stored_date); 14261 } else { 14262 uint64_t stored_bits = DoubleToBits(stored_date); 14263 // Check if quiet nan (bits 51..62 all set). 14264 #if defined(V8_TARGET_ARCH_MIPS) && !defined(USE_SIMULATOR) 14265 // Most significant fraction bit for quiet nan is set to 0 14266 // on MIPS architecture. Allowed by IEEE-754. 14267 CHECK_EQ(0xffe, static_cast<int>((stored_bits >> 51) & 0xfff)); 14268 #else 14269 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 14270 #endif 14271 } 14272 } 14273 } 14274 14275 14276 static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) { 14277 v8::HandleScope scope; 14278 v8::TryCatch tc; 14279 v8::Handle<v8::String> str(args[0]->ToString()); 14280 USE(str); 14281 if (tc.HasCaught()) 14282 return tc.ReThrow(); 14283 return v8::Undefined(); 14284 } 14285 14286 14287 // Test that an exception can be propagated down through a spaghetti 14288 // stack using ReThrow. 14289 THREADED_TEST(SpaghettiStackReThrow) { 14290 v8::HandleScope scope; 14291 LocalContext context; 14292 context->Global()->Set( 14293 v8::String::New("s"), 14294 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction()); 14295 v8::TryCatch try_catch; 14296 CompileRun( 14297 "var i = 0;" 14298 "var o = {" 14299 " toString: function () {" 14300 " if (i == 10) {" 14301 " throw 'Hey!';" 14302 " } else {" 14303 " i++;" 14304 " return s(o);" 14305 " }" 14306 " }" 14307 "};" 14308 "s(o);"); 14309 CHECK(try_catch.HasCaught()); 14310 v8::String::Utf8Value value(try_catch.Exception()); 14311 CHECK_EQ(0, strcmp(*value, "Hey!")); 14312 } 14313 14314 14315 TEST(Regress528) { 14316 v8::V8::Initialize(); 14317 14318 v8::HandleScope scope; 14319 v8::Persistent<Context> context; 14320 v8::Persistent<Context> other_context; 14321 int gc_count; 14322 14323 // Create a context used to keep the code from aging in the compilation 14324 // cache. 14325 other_context = Context::New(); 14326 14327 // Context-dependent context data creates reference from the compilation 14328 // cache to the global object. 14329 const char* source_simple = "1"; 14330 context = Context::New(); 14331 { 14332 v8::HandleScope scope; 14333 14334 context->Enter(); 14335 Local<v8::String> obj = v8::String::New(""); 14336 context->SetData(obj); 14337 CompileRun(source_simple); 14338 context->Exit(); 14339 } 14340 context.Dispose(); 14341 for (gc_count = 1; gc_count < 10; gc_count++) { 14342 other_context->Enter(); 14343 CompileRun(source_simple); 14344 other_context->Exit(); 14345 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 14346 if (GetGlobalObjectsCount() == 1) break; 14347 } 14348 CHECK_GE(2, gc_count); 14349 CHECK_EQ(1, GetGlobalObjectsCount()); 14350 14351 // Eval in a function creates reference from the compilation cache to the 14352 // global object. 14353 const char* source_eval = "function f(){eval('1')}; f()"; 14354 context = Context::New(); 14355 { 14356 v8::HandleScope scope; 14357 14358 context->Enter(); 14359 CompileRun(source_eval); 14360 context->Exit(); 14361 } 14362 context.Dispose(); 14363 for (gc_count = 1; gc_count < 10; gc_count++) { 14364 other_context->Enter(); 14365 CompileRun(source_eval); 14366 other_context->Exit(); 14367 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 14368 if (GetGlobalObjectsCount() == 1) break; 14369 } 14370 CHECK_GE(2, gc_count); 14371 CHECK_EQ(1, GetGlobalObjectsCount()); 14372 14373 // Looking up the line number for an exception creates reference from the 14374 // compilation cache to the global object. 14375 const char* source_exception = "function f(){throw 1;} f()"; 14376 context = Context::New(); 14377 { 14378 v8::HandleScope scope; 14379 14380 context->Enter(); 14381 v8::TryCatch try_catch; 14382 CompileRun(source_exception); 14383 CHECK(try_catch.HasCaught()); 14384 v8::Handle<v8::Message> message = try_catch.Message(); 14385 CHECK(!message.IsEmpty()); 14386 CHECK_EQ(1, message->GetLineNumber()); 14387 context->Exit(); 14388 } 14389 context.Dispose(); 14390 for (gc_count = 1; gc_count < 10; gc_count++) { 14391 other_context->Enter(); 14392 CompileRun(source_exception); 14393 other_context->Exit(); 14394 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 14395 if (GetGlobalObjectsCount() == 1) break; 14396 } 14397 CHECK_GE(2, gc_count); 14398 CHECK_EQ(1, GetGlobalObjectsCount()); 14399 14400 other_context.Dispose(); 14401 } 14402 14403 14404 THREADED_TEST(ScriptOrigin) { 14405 v8::HandleScope scope; 14406 LocalContext env; 14407 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 14408 v8::Handle<v8::String> script = v8::String::New( 14409 "function f() {}\n\nfunction g() {}"); 14410 v8::Script::Compile(script, &origin)->Run(); 14411 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 14412 env->Global()->Get(v8::String::New("f"))); 14413 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 14414 env->Global()->Get(v8::String::New("g"))); 14415 14416 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin(); 14417 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName())); 14418 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value()); 14419 14420 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin(); 14421 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName())); 14422 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value()); 14423 } 14424 14425 THREADED_TEST(FunctionGetInferredName) { 14426 v8::HandleScope scope; 14427 LocalContext env; 14428 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 14429 v8::Handle<v8::String> script = v8::String::New( 14430 "var foo = { bar : { baz : function() {}}}; var f = foo.bar.baz;"); 14431 v8::Script::Compile(script, &origin)->Run(); 14432 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 14433 env->Global()->Get(v8::String::New("f"))); 14434 CHECK_EQ("foo.bar.baz", *v8::String::AsciiValue(f->GetInferredName())); 14435 } 14436 14437 THREADED_TEST(ScriptLineNumber) { 14438 v8::HandleScope scope; 14439 LocalContext env; 14440 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 14441 v8::Handle<v8::String> script = v8::String::New( 14442 "function f() {}\n\nfunction g() {}"); 14443 v8::Script::Compile(script, &origin)->Run(); 14444 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 14445 env->Global()->Get(v8::String::New("f"))); 14446 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 14447 env->Global()->Get(v8::String::New("g"))); 14448 CHECK_EQ(0, f->GetScriptLineNumber()); 14449 CHECK_EQ(2, g->GetScriptLineNumber()); 14450 } 14451 14452 14453 THREADED_TEST(ScriptColumnNumber) { 14454 v8::HandleScope scope; 14455 LocalContext env; 14456 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"), 14457 v8::Integer::New(3), v8::Integer::New(2)); 14458 v8::Handle<v8::String> script = v8::String::New( 14459 "function foo() {}\n\n function bar() {}"); 14460 v8::Script::Compile(script, &origin)->Run(); 14461 v8::Local<v8::Function> foo = v8::Local<v8::Function>::Cast( 14462 env->Global()->Get(v8::String::New("foo"))); 14463 v8::Local<v8::Function> bar = v8::Local<v8::Function>::Cast( 14464 env->Global()->Get(v8::String::New("bar"))); 14465 CHECK_EQ(14, foo->GetScriptColumnNumber()); 14466 CHECK_EQ(17, bar->GetScriptColumnNumber()); 14467 } 14468 14469 14470 THREADED_TEST(FunctionGetScriptId) { 14471 v8::HandleScope scope; 14472 LocalContext env; 14473 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"), 14474 v8::Integer::New(3), v8::Integer::New(2)); 14475 v8::Handle<v8::String> scriptSource = v8::String::New( 14476 "function foo() {}\n\n function bar() {}"); 14477 v8::Local<v8::Script> script(v8::Script::Compile(scriptSource, &origin)); 14478 script->Run(); 14479 v8::Local<v8::Function> foo = v8::Local<v8::Function>::Cast( 14480 env->Global()->Get(v8::String::New("foo"))); 14481 v8::Local<v8::Function> bar = v8::Local<v8::Function>::Cast( 14482 env->Global()->Get(v8::String::New("bar"))); 14483 CHECK_EQ(script->Id(), foo->GetScriptId()); 14484 CHECK_EQ(script->Id(), bar->GetScriptId()); 14485 } 14486 14487 14488 static v8::Handle<Value> GetterWhichReturns42(Local<String> name, 14489 const AccessorInfo& info) { 14490 return v8_num(42); 14491 } 14492 14493 14494 static void SetterWhichSetsYOnThisTo23(Local<String> name, 14495 Local<Value> value, 14496 const AccessorInfo& info) { 14497 info.This()->Set(v8_str("y"), v8_num(23)); 14498 } 14499 14500 14501 TEST(SetterOnConstructorPrototype) { 14502 v8::HandleScope scope; 14503 Local<ObjectTemplate> templ = ObjectTemplate::New(); 14504 templ->SetAccessor(v8_str("x"), 14505 GetterWhichReturns42, 14506 SetterWhichSetsYOnThisTo23); 14507 LocalContext context; 14508 context->Global()->Set(v8_str("P"), templ->NewInstance()); 14509 CompileRun("function C1() {" 14510 " this.x = 23;" 14511 "};" 14512 "C1.prototype = P;" 14513 "function C2() {" 14514 " this.x = 23" 14515 "};" 14516 "C2.prototype = { };" 14517 "C2.prototype.__proto__ = P;"); 14518 14519 v8::Local<v8::Script> script; 14520 script = v8::Script::Compile(v8_str("new C1();")); 14521 for (int i = 0; i < 10; i++) { 14522 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 14523 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); 14524 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); 14525 } 14526 14527 script = v8::Script::Compile(v8_str("new C2();")); 14528 for (int i = 0; i < 10; i++) { 14529 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 14530 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value()); 14531 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value()); 14532 } 14533 } 14534 14535 14536 static v8::Handle<Value> NamedPropertyGetterWhichReturns42( 14537 Local<String> name, const AccessorInfo& info) { 14538 return v8_num(42); 14539 } 14540 14541 14542 static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23( 14543 Local<String> name, Local<Value> value, const AccessorInfo& info) { 14544 if (name->Equals(v8_str("x"))) { 14545 info.This()->Set(v8_str("y"), v8_num(23)); 14546 } 14547 return v8::Handle<Value>(); 14548 } 14549 14550 14551 THREADED_TEST(InterceptorOnConstructorPrototype) { 14552 v8::HandleScope scope; 14553 Local<ObjectTemplate> templ = ObjectTemplate::New(); 14554 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42, 14555 NamedPropertySetterWhichSetsYOnThisTo23); 14556 LocalContext context; 14557 context->Global()->Set(v8_str("P"), templ->NewInstance()); 14558 CompileRun("function C1() {" 14559 " this.x = 23;" 14560 "};" 14561 "C1.prototype = P;" 14562 "function C2() {" 14563 " this.x = 23" 14564 "};" 14565 "C2.prototype = { };" 14566 "C2.prototype.__proto__ = P;"); 14567 14568 v8::Local<v8::Script> script; 14569 script = v8::Script::Compile(v8_str("new C1();")); 14570 for (int i = 0; i < 10; i++) { 14571 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 14572 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); 14573 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); 14574 } 14575 14576 script = v8::Script::Compile(v8_str("new C2();")); 14577 for (int i = 0; i < 10; i++) { 14578 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 14579 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value()); 14580 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value()); 14581 } 14582 } 14583 14584 14585 TEST(Bug618) { 14586 const char* source = "function C1() {" 14587 " this.x = 23;" 14588 "};" 14589 "C1.prototype = P;"; 14590 14591 v8::HandleScope scope; 14592 LocalContext context; 14593 v8::Local<v8::Script> script; 14594 14595 // Use a simple object as prototype. 14596 v8::Local<v8::Object> prototype = v8::Object::New(); 14597 prototype->Set(v8_str("y"), v8_num(42)); 14598 context->Global()->Set(v8_str("P"), prototype); 14599 14600 // This compile will add the code to the compilation cache. 14601 CompileRun(source); 14602 14603 script = v8::Script::Compile(v8_str("new C1();")); 14604 // Allow enough iterations for the inobject slack tracking logic 14605 // to finalize instance size and install the fast construct stub. 14606 for (int i = 0; i < 256; i++) { 14607 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 14608 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); 14609 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); 14610 } 14611 14612 // Use an API object with accessors as prototype. 14613 Local<ObjectTemplate> templ = ObjectTemplate::New(); 14614 templ->SetAccessor(v8_str("x"), 14615 GetterWhichReturns42, 14616 SetterWhichSetsYOnThisTo23); 14617 context->Global()->Set(v8_str("P"), templ->NewInstance()); 14618 14619 // This compile will get the code from the compilation cache. 14620 CompileRun(source); 14621 14622 script = v8::Script::Compile(v8_str("new C1();")); 14623 for (int i = 0; i < 10; i++) { 14624 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 14625 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); 14626 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); 14627 } 14628 } 14629 14630 int prologue_call_count = 0; 14631 int epilogue_call_count = 0; 14632 int prologue_call_count_second = 0; 14633 int epilogue_call_count_second = 0; 14634 14635 void PrologueCallback(v8::GCType, v8::GCCallbackFlags) { 14636 ++prologue_call_count; 14637 } 14638 14639 void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) { 14640 ++epilogue_call_count; 14641 } 14642 14643 void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { 14644 ++prologue_call_count_second; 14645 } 14646 14647 void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { 14648 ++epilogue_call_count_second; 14649 } 14650 14651 TEST(GCCallbacks) { 14652 LocalContext context; 14653 14654 v8::V8::AddGCPrologueCallback(PrologueCallback); 14655 v8::V8::AddGCEpilogueCallback(EpilogueCallback); 14656 CHECK_EQ(0, prologue_call_count); 14657 CHECK_EQ(0, epilogue_call_count); 14658 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 14659 CHECK_EQ(1, prologue_call_count); 14660 CHECK_EQ(1, epilogue_call_count); 14661 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond); 14662 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond); 14663 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 14664 CHECK_EQ(2, prologue_call_count); 14665 CHECK_EQ(2, epilogue_call_count); 14666 CHECK_EQ(1, prologue_call_count_second); 14667 CHECK_EQ(1, epilogue_call_count_second); 14668 v8::V8::RemoveGCPrologueCallback(PrologueCallback); 14669 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback); 14670 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 14671 CHECK_EQ(2, prologue_call_count); 14672 CHECK_EQ(2, epilogue_call_count); 14673 CHECK_EQ(2, prologue_call_count_second); 14674 CHECK_EQ(2, epilogue_call_count_second); 14675 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond); 14676 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond); 14677 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 14678 CHECK_EQ(2, prologue_call_count); 14679 CHECK_EQ(2, epilogue_call_count); 14680 CHECK_EQ(2, prologue_call_count_second); 14681 CHECK_EQ(2, epilogue_call_count_second); 14682 } 14683 14684 14685 THREADED_TEST(AddToJSFunctionResultCache) { 14686 i::FLAG_allow_natives_syntax = true; 14687 v8::HandleScope scope; 14688 14689 LocalContext context; 14690 14691 const char* code = 14692 "(function() {" 14693 " var key0 = 'a';" 14694 " var key1 = 'b';" 14695 " var r0 = %_GetFromCache(0, key0);" 14696 " var r1 = %_GetFromCache(0, key1);" 14697 " var r0_ = %_GetFromCache(0, key0);" 14698 " if (r0 !== r0_)" 14699 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;" 14700 " var r1_ = %_GetFromCache(0, key1);" 14701 " if (r1 !== r1_)" 14702 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;" 14703 " return 'PASSED';" 14704 "})()"; 14705 HEAP->ClearJSFunctionResultCaches(); 14706 ExpectString(code, "PASSED"); 14707 } 14708 14709 14710 static const int k0CacheSize = 16; 14711 14712 THREADED_TEST(FillJSFunctionResultCache) { 14713 i::FLAG_allow_natives_syntax = true; 14714 v8::HandleScope scope; 14715 14716 LocalContext context; 14717 14718 const char* code = 14719 "(function() {" 14720 " var k = 'a';" 14721 " var r = %_GetFromCache(0, k);" 14722 " for (var i = 0; i < 16; i++) {" 14723 " %_GetFromCache(0, 'a' + i);" 14724 " };" 14725 " if (r === %_GetFromCache(0, k))" 14726 " return 'FAILED: k0CacheSize is too small';" 14727 " return 'PASSED';" 14728 "})()"; 14729 HEAP->ClearJSFunctionResultCaches(); 14730 ExpectString(code, "PASSED"); 14731 } 14732 14733 14734 THREADED_TEST(RoundRobinGetFromCache) { 14735 i::FLAG_allow_natives_syntax = true; 14736 v8::HandleScope scope; 14737 14738 LocalContext context; 14739 14740 const char* code = 14741 "(function() {" 14742 " var keys = [];" 14743 " for (var i = 0; i < 16; i++) keys.push(i);" 14744 " var values = [];" 14745 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" 14746 " for (var i = 0; i < 16; i++) {" 14747 " var v = %_GetFromCache(0, keys[i]);" 14748 " if (v.toString() !== values[i].toString())" 14749 " return 'Wrong value for ' + " 14750 " keys[i] + ': ' + v + ' vs. ' + values[i];" 14751 " };" 14752 " return 'PASSED';" 14753 "})()"; 14754 HEAP->ClearJSFunctionResultCaches(); 14755 ExpectString(code, "PASSED"); 14756 } 14757 14758 14759 THREADED_TEST(ReverseGetFromCache) { 14760 i::FLAG_allow_natives_syntax = true; 14761 v8::HandleScope scope; 14762 14763 LocalContext context; 14764 14765 const char* code = 14766 "(function() {" 14767 " var keys = [];" 14768 " for (var i = 0; i < 16; i++) keys.push(i);" 14769 " var values = [];" 14770 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" 14771 " for (var i = 15; i >= 16; i--) {" 14772 " var v = %_GetFromCache(0, keys[i]);" 14773 " if (v !== values[i])" 14774 " return 'Wrong value for ' + " 14775 " keys[i] + ': ' + v + ' vs. ' + values[i];" 14776 " };" 14777 " return 'PASSED';" 14778 "})()"; 14779 HEAP->ClearJSFunctionResultCaches(); 14780 ExpectString(code, "PASSED"); 14781 } 14782 14783 14784 THREADED_TEST(TestEviction) { 14785 i::FLAG_allow_natives_syntax = true; 14786 v8::HandleScope scope; 14787 14788 LocalContext context; 14789 14790 const char* code = 14791 "(function() {" 14792 " for (var i = 0; i < 2*16; i++) {" 14793 " %_GetFromCache(0, 'a' + i);" 14794 " };" 14795 " return 'PASSED';" 14796 "})()"; 14797 HEAP->ClearJSFunctionResultCaches(); 14798 ExpectString(code, "PASSED"); 14799 } 14800 14801 14802 THREADED_TEST(TwoByteStringInAsciiCons) { 14803 // See Chromium issue 47824. 14804 v8::HandleScope scope; 14805 14806 LocalContext context; 14807 const char* init_code = 14808 "var str1 = 'abelspendabel';" 14809 "var str2 = str1 + str1 + str1;" 14810 "str2;"; 14811 Local<Value> result = CompileRun(init_code); 14812 14813 Local<Value> indexof = CompileRun("str2.indexOf('els')"); 14814 Local<Value> lastindexof = CompileRun("str2.lastIndexOf('dab')"); 14815 14816 CHECK(result->IsString()); 14817 i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result)); 14818 int length = string->length(); 14819 CHECK(string->IsAsciiRepresentation()); 14820 14821 FlattenString(string); 14822 i::Handle<i::String> flat_string = FlattenGetString(string); 14823 14824 CHECK(string->IsAsciiRepresentation()); 14825 CHECK(flat_string->IsAsciiRepresentation()); 14826 14827 // Create external resource. 14828 uint16_t* uc16_buffer = new uint16_t[length + 1]; 14829 14830 i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length); 14831 uc16_buffer[length] = 0; 14832 14833 TestResource resource(uc16_buffer); 14834 14835 flat_string->MakeExternal(&resource); 14836 14837 CHECK(flat_string->IsTwoByteRepresentation()); 14838 14839 // At this point, we should have a Cons string which is flat and ASCII, 14840 // with a first half that is a two-byte string (although it only contains 14841 // ASCII characters). This is a valid sequence of steps, and it can happen 14842 // in real pages. 14843 14844 CHECK(string->IsAsciiRepresentation()); 14845 i::ConsString* cons = i::ConsString::cast(*string); 14846 CHECK_EQ(0, cons->second()->length()); 14847 CHECK(cons->first()->IsTwoByteRepresentation()); 14848 14849 // Check that some string operations work. 14850 14851 // Atom RegExp. 14852 Local<Value> reresult = CompileRun("str2.match(/abel/g).length;"); 14853 CHECK_EQ(6, reresult->Int32Value()); 14854 14855 // Nonatom RegExp. 14856 reresult = CompileRun("str2.match(/abe./g).length;"); 14857 CHECK_EQ(6, reresult->Int32Value()); 14858 14859 reresult = CompileRun("str2.search(/bel/g);"); 14860 CHECK_EQ(1, reresult->Int32Value()); 14861 14862 reresult = CompileRun("str2.search(/be./g);"); 14863 CHECK_EQ(1, reresult->Int32Value()); 14864 14865 ExpectTrue("/bel/g.test(str2);"); 14866 14867 ExpectTrue("/be./g.test(str2);"); 14868 14869 reresult = CompileRun("/bel/g.exec(str2);"); 14870 CHECK(!reresult->IsNull()); 14871 14872 reresult = CompileRun("/be./g.exec(str2);"); 14873 CHECK(!reresult->IsNull()); 14874 14875 ExpectString("str2.substring(2, 10);", "elspenda"); 14876 14877 ExpectString("str2.substring(2, 20);", "elspendabelabelspe"); 14878 14879 ExpectString("str2.charAt(2);", "e"); 14880 14881 ExpectObject("str2.indexOf('els');", indexof); 14882 14883 ExpectObject("str2.lastIndexOf('dab');", lastindexof); 14884 14885 reresult = CompileRun("str2.charCodeAt(2);"); 14886 CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value()); 14887 } 14888 14889 14890 // Failed access check callback that performs a GC on each invocation. 14891 void FailedAccessCheckCallbackGC(Local<v8::Object> target, 14892 v8::AccessType type, 14893 Local<v8::Value> data) { 14894 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 14895 } 14896 14897 14898 TEST(GCInFailedAccessCheckCallback) { 14899 // Install a failed access check callback that performs a GC on each 14900 // invocation. Then force the callback to be called from va 14901 14902 v8::V8::Initialize(); 14903 v8::V8::SetFailedAccessCheckCallbackFunction(&FailedAccessCheckCallbackGC); 14904 14905 v8::HandleScope scope; 14906 14907 // Create an ObjectTemplate for global objects and install access 14908 // check callbacks that will block access. 14909 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 14910 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker, 14911 IndexedGetAccessBlocker, 14912 v8::Handle<v8::Value>(), 14913 false); 14914 14915 // Create a context and set an x property on it's global object. 14916 LocalContext context0(NULL, global_template); 14917 context0->Global()->Set(v8_str("x"), v8_num(42)); 14918 v8::Handle<v8::Object> global0 = context0->Global(); 14919 14920 // Create a context with a different security token so that the 14921 // failed access check callback will be called on each access. 14922 LocalContext context1(NULL, global_template); 14923 context1->Global()->Set(v8_str("other"), global0); 14924 14925 // Get property with failed access check. 14926 ExpectUndefined("other.x"); 14927 14928 // Get element with failed access check. 14929 ExpectUndefined("other[0]"); 14930 14931 // Set property with failed access check. 14932 v8::Handle<v8::Value> result = CompileRun("other.x = new Object()"); 14933 CHECK(result->IsObject()); 14934 14935 // Set element with failed access check. 14936 result = CompileRun("other[0] = new Object()"); 14937 CHECK(result->IsObject()); 14938 14939 // Get property attribute with failed access check. 14940 ExpectFalse("\'x\' in other"); 14941 14942 // Get property attribute for element with failed access check. 14943 ExpectFalse("0 in other"); 14944 14945 // Delete property. 14946 ExpectFalse("delete other.x"); 14947 14948 // Delete element. 14949 CHECK_EQ(false, global0->Delete(0)); 14950 14951 // DefineAccessor. 14952 CHECK_EQ(false, 14953 global0->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("x"))); 14954 14955 // Define JavaScript accessor. 14956 ExpectUndefined("Object.prototype.__defineGetter__.call(" 14957 " other, \'x\', function() { return 42; })"); 14958 14959 // LookupAccessor. 14960 ExpectUndefined("Object.prototype.__lookupGetter__.call(" 14961 " other, \'x\')"); 14962 14963 // HasLocalElement. 14964 ExpectFalse("Object.prototype.hasOwnProperty.call(other, \'0\')"); 14965 14966 CHECK_EQ(false, global0->HasRealIndexedProperty(0)); 14967 CHECK_EQ(false, global0->HasRealNamedProperty(v8_str("x"))); 14968 CHECK_EQ(false, global0->HasRealNamedCallbackProperty(v8_str("x"))); 14969 14970 // Reset the failed access check callback so it does not influence 14971 // the other tests. 14972 v8::V8::SetFailedAccessCheckCallbackFunction(NULL); 14973 } 14974 14975 TEST(DefaultIsolateGetCurrent) { 14976 CHECK(v8::Isolate::GetCurrent() != NULL); 14977 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 14978 CHECK(reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate()); 14979 printf("*** %s\n", "DefaultIsolateGetCurrent success"); 14980 } 14981 14982 TEST(IsolateNewDispose) { 14983 v8::Isolate* current_isolate = v8::Isolate::GetCurrent(); 14984 v8::Isolate* isolate = v8::Isolate::New(); 14985 CHECK(isolate != NULL); 14986 CHECK(!reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate()); 14987 CHECK(current_isolate != isolate); 14988 CHECK(current_isolate == v8::Isolate::GetCurrent()); 14989 14990 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 14991 last_location = last_message = NULL; 14992 isolate->Dispose(); 14993 CHECK_EQ(last_location, NULL); 14994 CHECK_EQ(last_message, NULL); 14995 } 14996 14997 TEST(IsolateEnterExitDefault) { 14998 v8::HandleScope scope; 14999 LocalContext context; 15000 v8::Isolate* current_isolate = v8::Isolate::GetCurrent(); 15001 CHECK(current_isolate != NULL); // Default isolate. 15002 ExpectString("'hello'", "hello"); 15003 current_isolate->Enter(); 15004 ExpectString("'still working'", "still working"); 15005 current_isolate->Exit(); 15006 ExpectString("'still working 2'", "still working 2"); 15007 current_isolate->Exit(); 15008 // Default isolate is always, well, 'default current'. 15009 CHECK_EQ(v8::Isolate::GetCurrent(), current_isolate); 15010 // Still working since default isolate is auto-entering any thread 15011 // that has no isolate and attempts to execute V8 APIs. 15012 ExpectString("'still working 3'", "still working 3"); 15013 } 15014 15015 TEST(DisposeDefaultIsolate) { 15016 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 15017 15018 // Run some V8 code to trigger default isolate to become 'current'. 15019 v8::HandleScope scope; 15020 LocalContext context; 15021 ExpectString("'run some V8'", "run some V8"); 15022 15023 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 15024 CHECK(reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate()); 15025 last_location = last_message = NULL; 15026 isolate->Dispose(); 15027 // It is not possible to dispose default isolate via Isolate API. 15028 CHECK_NE(last_location, NULL); 15029 CHECK_NE(last_message, NULL); 15030 } 15031 15032 TEST(RunDefaultAndAnotherIsolate) { 15033 v8::HandleScope scope; 15034 LocalContext context; 15035 15036 // Enter new isolate. 15037 v8::Isolate* isolate = v8::Isolate::New(); 15038 CHECK(isolate); 15039 isolate->Enter(); 15040 { // Need this block because subsequent Exit() will deallocate Heap, 15041 // so we need all scope objects to be deconstructed when it happens. 15042 v8::HandleScope scope_new; 15043 LocalContext context_new; 15044 15045 // Run something in new isolate. 15046 CompileRun("var foo = 153;"); 15047 ExpectTrue("function f() { return foo == 153; }; f()"); 15048 } 15049 isolate->Exit(); 15050 15051 // This runs automatically in default isolate. 15052 // Variables in another isolate should be not available. 15053 ExpectTrue("function f() {" 15054 " try {" 15055 " foo;" 15056 " return false;" 15057 " } catch(e) {" 15058 " return true;" 15059 " }" 15060 "};" 15061 "var bar = 371;" 15062 "f()"); 15063 15064 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 15065 last_location = last_message = NULL; 15066 isolate->Dispose(); 15067 CHECK_EQ(last_location, NULL); 15068 CHECK_EQ(last_message, NULL); 15069 15070 // Check that default isolate still runs. 15071 ExpectTrue("function f() { return bar == 371; }; f()"); 15072 } 15073 15074 TEST(DisposeIsolateWhenInUse) { 15075 v8::Isolate* isolate = v8::Isolate::New(); 15076 CHECK(isolate); 15077 isolate->Enter(); 15078 v8::HandleScope scope; 15079 LocalContext context; 15080 // Run something in this isolate. 15081 ExpectTrue("true"); 15082 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 15083 last_location = last_message = NULL; 15084 // Still entered, should fail. 15085 isolate->Dispose(); 15086 CHECK_NE(last_location, NULL); 15087 CHECK_NE(last_message, NULL); 15088 } 15089 15090 TEST(RunTwoIsolatesOnSingleThread) { 15091 // Run isolate 1. 15092 v8::Isolate* isolate1 = v8::Isolate::New(); 15093 isolate1->Enter(); 15094 v8::Persistent<v8::Context> context1 = v8::Context::New(); 15095 15096 { 15097 v8::Context::Scope cscope(context1); 15098 v8::HandleScope scope; 15099 // Run something in new isolate. 15100 CompileRun("var foo = 'isolate 1';"); 15101 ExpectString("function f() { return foo; }; f()", "isolate 1"); 15102 } 15103 15104 // Run isolate 2. 15105 v8::Isolate* isolate2 = v8::Isolate::New(); 15106 v8::Persistent<v8::Context> context2; 15107 15108 { 15109 v8::Isolate::Scope iscope(isolate2); 15110 context2 = v8::Context::New(); 15111 v8::Context::Scope cscope(context2); 15112 v8::HandleScope scope; 15113 15114 // Run something in new isolate. 15115 CompileRun("var foo = 'isolate 2';"); 15116 ExpectString("function f() { return foo; }; f()", "isolate 2"); 15117 } 15118 15119 { 15120 v8::Context::Scope cscope(context1); 15121 v8::HandleScope scope; 15122 // Now again in isolate 1 15123 ExpectString("function f() { return foo; }; f()", "isolate 1"); 15124 } 15125 15126 isolate1->Exit(); 15127 15128 // Run some stuff in default isolate. 15129 v8::Persistent<v8::Context> context_default = v8::Context::New(); 15130 15131 { 15132 v8::Context::Scope cscope(context_default); 15133 v8::HandleScope scope; 15134 // Variables in other isolates should be not available, verify there 15135 // is an exception. 15136 ExpectTrue("function f() {" 15137 " try {" 15138 " foo;" 15139 " return false;" 15140 " } catch(e) {" 15141 " return true;" 15142 " }" 15143 "};" 15144 "var isDefaultIsolate = true;" 15145 "f()"); 15146 } 15147 15148 isolate1->Enter(); 15149 15150 { 15151 v8::Isolate::Scope iscope(isolate2); 15152 v8::Context::Scope cscope(context2); 15153 v8::HandleScope scope; 15154 ExpectString("function f() { return foo; }; f()", "isolate 2"); 15155 } 15156 15157 { 15158 v8::Context::Scope cscope(context1); 15159 v8::HandleScope scope; 15160 ExpectString("function f() { return foo; }; f()", "isolate 1"); 15161 } 15162 15163 { 15164 v8::Isolate::Scope iscope(isolate2); 15165 context2.Dispose(); 15166 } 15167 15168 context1.Dispose(); 15169 isolate1->Exit(); 15170 15171 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 15172 last_location = last_message = NULL; 15173 15174 isolate1->Dispose(); 15175 CHECK_EQ(last_location, NULL); 15176 CHECK_EQ(last_message, NULL); 15177 15178 isolate2->Dispose(); 15179 CHECK_EQ(last_location, NULL); 15180 CHECK_EQ(last_message, NULL); 15181 15182 // Check that default isolate still runs. 15183 { 15184 v8::Context::Scope cscope(context_default); 15185 v8::HandleScope scope; 15186 ExpectTrue("function f() { return isDefaultIsolate; }; f()"); 15187 } 15188 } 15189 15190 static int CalcFibonacci(v8::Isolate* isolate, int limit) { 15191 v8::Isolate::Scope isolate_scope(isolate); 15192 v8::HandleScope scope; 15193 LocalContext context; 15194 i::ScopedVector<char> code(1024); 15195 i::OS::SNPrintF(code, "function fib(n) {" 15196 " if (n <= 2) return 1;" 15197 " return fib(n-1) + fib(n-2);" 15198 "}" 15199 "fib(%d)", limit); 15200 Local<Value> value = CompileRun(code.start()); 15201 CHECK(value->IsNumber()); 15202 return static_cast<int>(value->NumberValue()); 15203 } 15204 15205 class IsolateThread : public v8::internal::Thread { 15206 public: 15207 IsolateThread(v8::Isolate* isolate, int fib_limit) 15208 : Thread("IsolateThread"), 15209 isolate_(isolate), 15210 fib_limit_(fib_limit), 15211 result_(0) { } 15212 15213 void Run() { 15214 result_ = CalcFibonacci(isolate_, fib_limit_); 15215 } 15216 15217 int result() { return result_; } 15218 15219 private: 15220 v8::Isolate* isolate_; 15221 int fib_limit_; 15222 int result_; 15223 }; 15224 15225 TEST(MultipleIsolatesOnIndividualThreads) { 15226 v8::Isolate* isolate1 = v8::Isolate::New(); 15227 v8::Isolate* isolate2 = v8::Isolate::New(); 15228 15229 IsolateThread thread1(isolate1, 21); 15230 IsolateThread thread2(isolate2, 12); 15231 15232 // Compute some fibonacci numbers on 3 threads in 3 isolates. 15233 thread1.Start(); 15234 thread2.Start(); 15235 15236 int result1 = CalcFibonacci(v8::Isolate::GetCurrent(), 21); 15237 int result2 = CalcFibonacci(v8::Isolate::GetCurrent(), 12); 15238 15239 thread1.Join(); 15240 thread2.Join(); 15241 15242 // Compare results. The actual fibonacci numbers for 12 and 21 are taken 15243 // (I'm lazy!) from http://en.wikipedia.org/wiki/Fibonacci_number 15244 CHECK_EQ(result1, 10946); 15245 CHECK_EQ(result2, 144); 15246 CHECK_EQ(result1, thread1.result()); 15247 CHECK_EQ(result2, thread2.result()); 15248 15249 isolate1->Dispose(); 15250 isolate2->Dispose(); 15251 } 15252 15253 TEST(IsolateDifferentContexts) { 15254 v8::Isolate* isolate = v8::Isolate::New(); 15255 Persistent<v8::Context> context; 15256 { 15257 v8::Isolate::Scope isolate_scope(isolate); 15258 v8::HandleScope handle_scope; 15259 context = v8::Context::New(); 15260 v8::Context::Scope context_scope(context); 15261 Local<Value> v = CompileRun("2"); 15262 CHECK(v->IsNumber()); 15263 CHECK_EQ(2, static_cast<int>(v->NumberValue())); 15264 } 15265 { 15266 v8::Isolate::Scope isolate_scope(isolate); 15267 v8::HandleScope handle_scope; 15268 context = v8::Context::New(); 15269 v8::Context::Scope context_scope(context); 15270 Local<Value> v = CompileRun("22"); 15271 CHECK(v->IsNumber()); 15272 CHECK_EQ(22, static_cast<int>(v->NumberValue())); 15273 } 15274 } 15275 15276 class InitDefaultIsolateThread : public v8::internal::Thread { 15277 public: 15278 enum TestCase { 15279 IgnoreOOM, 15280 SetResourceConstraints, 15281 SetFatalHandler, 15282 SetCounterFunction, 15283 SetCreateHistogramFunction, 15284 SetAddHistogramSampleFunction 15285 }; 15286 15287 explicit InitDefaultIsolateThread(TestCase testCase) 15288 : Thread("InitDefaultIsolateThread"), 15289 testCase_(testCase), 15290 result_(false) { } 15291 15292 void Run() { 15293 switch (testCase_) { 15294 case IgnoreOOM: 15295 v8::V8::IgnoreOutOfMemoryException(); 15296 break; 15297 15298 case SetResourceConstraints: { 15299 static const int K = 1024; 15300 v8::ResourceConstraints constraints; 15301 constraints.set_max_young_space_size(256 * K); 15302 constraints.set_max_old_space_size(4 * K * K); 15303 v8::SetResourceConstraints(&constraints); 15304 break; 15305 } 15306 15307 case SetFatalHandler: 15308 v8::V8::SetFatalErrorHandler(NULL); 15309 break; 15310 15311 case SetCounterFunction: 15312 v8::V8::SetCounterFunction(NULL); 15313 break; 15314 15315 case SetCreateHistogramFunction: 15316 v8::V8::SetCreateHistogramFunction(NULL); 15317 break; 15318 15319 case SetAddHistogramSampleFunction: 15320 v8::V8::SetAddHistogramSampleFunction(NULL); 15321 break; 15322 } 15323 result_ = true; 15324 } 15325 15326 bool result() { return result_; } 15327 15328 private: 15329 TestCase testCase_; 15330 bool result_; 15331 }; 15332 15333 15334 static void InitializeTestHelper(InitDefaultIsolateThread::TestCase testCase) { 15335 InitDefaultIsolateThread thread(testCase); 15336 thread.Start(); 15337 thread.Join(); 15338 CHECK_EQ(thread.result(), true); 15339 } 15340 15341 TEST(InitializeDefaultIsolateOnSecondaryThread1) { 15342 InitializeTestHelper(InitDefaultIsolateThread::IgnoreOOM); 15343 } 15344 15345 TEST(InitializeDefaultIsolateOnSecondaryThread2) { 15346 InitializeTestHelper(InitDefaultIsolateThread::SetResourceConstraints); 15347 } 15348 15349 TEST(InitializeDefaultIsolateOnSecondaryThread3) { 15350 InitializeTestHelper(InitDefaultIsolateThread::SetFatalHandler); 15351 } 15352 15353 TEST(InitializeDefaultIsolateOnSecondaryThread4) { 15354 InitializeTestHelper(InitDefaultIsolateThread::SetCounterFunction); 15355 } 15356 15357 TEST(InitializeDefaultIsolateOnSecondaryThread5) { 15358 InitializeTestHelper(InitDefaultIsolateThread::SetCreateHistogramFunction); 15359 } 15360 15361 TEST(InitializeDefaultIsolateOnSecondaryThread6) { 15362 InitializeTestHelper(InitDefaultIsolateThread::SetAddHistogramSampleFunction); 15363 } 15364 15365 15366 TEST(StringCheckMultipleContexts) { 15367 const char* code = 15368 "(function() { return \"a\".charAt(0); })()"; 15369 15370 { 15371 // Run the code twice in the first context to initialize the call IC. 15372 v8::HandleScope scope; 15373 LocalContext context1; 15374 ExpectString(code, "a"); 15375 ExpectString(code, "a"); 15376 } 15377 15378 { 15379 // Change the String.prototype in the second context and check 15380 // that the right function gets called. 15381 v8::HandleScope scope; 15382 LocalContext context2; 15383 CompileRun("String.prototype.charAt = function() { return \"not a\"; }"); 15384 ExpectString(code, "not a"); 15385 } 15386 } 15387 15388 15389 TEST(NumberCheckMultipleContexts) { 15390 const char* code = 15391 "(function() { return (42).toString(); })()"; 15392 15393 { 15394 // Run the code twice in the first context to initialize the call IC. 15395 v8::HandleScope scope; 15396 LocalContext context1; 15397 ExpectString(code, "42"); 15398 ExpectString(code, "42"); 15399 } 15400 15401 { 15402 // Change the Number.prototype in the second context and check 15403 // that the right function gets called. 15404 v8::HandleScope scope; 15405 LocalContext context2; 15406 CompileRun("Number.prototype.toString = function() { return \"not 42\"; }"); 15407 ExpectString(code, "not 42"); 15408 } 15409 } 15410 15411 15412 TEST(BooleanCheckMultipleContexts) { 15413 const char* code = 15414 "(function() { return true.toString(); })()"; 15415 15416 { 15417 // Run the code twice in the first context to initialize the call IC. 15418 v8::HandleScope scope; 15419 LocalContext context1; 15420 ExpectString(code, "true"); 15421 ExpectString(code, "true"); 15422 } 15423 15424 { 15425 // Change the Boolean.prototype in the second context and check 15426 // that the right function gets called. 15427 v8::HandleScope scope; 15428 LocalContext context2; 15429 CompileRun("Boolean.prototype.toString = function() { return \"\"; }"); 15430 ExpectString(code, ""); 15431 } 15432 } 15433 15434 15435 TEST(DontDeleteCellLoadIC) { 15436 const char* function_code = 15437 "function readCell() { while (true) { return cell; } }"; 15438 15439 { 15440 // Run the code twice in the first context to initialize the load 15441 // IC for a don't delete cell. 15442 v8::HandleScope scope; 15443 LocalContext context1; 15444 CompileRun("var cell = \"first\";"); 15445 ExpectBoolean("delete cell", false); 15446 CompileRun(function_code); 15447 ExpectString("readCell()", "first"); 15448 ExpectString("readCell()", "first"); 15449 } 15450 15451 { 15452 // Use a deletable cell in the second context. 15453 v8::HandleScope scope; 15454 LocalContext context2; 15455 CompileRun("cell = \"second\";"); 15456 CompileRun(function_code); 15457 ExpectString("readCell()", "second"); 15458 ExpectBoolean("delete cell", true); 15459 ExpectString("(function() {" 15460 " try {" 15461 " return readCell();" 15462 " } catch(e) {" 15463 " return e.toString();" 15464 " }" 15465 "})()", 15466 "ReferenceError: cell is not defined"); 15467 CompileRun("cell = \"new_second\";"); 15468 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 15469 ExpectString("readCell()", "new_second"); 15470 ExpectString("readCell()", "new_second"); 15471 } 15472 } 15473 15474 15475 TEST(DontDeleteCellLoadICForceDelete) { 15476 const char* function_code = 15477 "function readCell() { while (true) { return cell; } }"; 15478 15479 // Run the code twice to initialize the load IC for a don't delete 15480 // cell. 15481 v8::HandleScope scope; 15482 LocalContext context; 15483 CompileRun("var cell = \"value\";"); 15484 ExpectBoolean("delete cell", false); 15485 CompileRun(function_code); 15486 ExpectString("readCell()", "value"); 15487 ExpectString("readCell()", "value"); 15488 15489 // Delete the cell using the API and check the inlined code works 15490 // correctly. 15491 CHECK(context->Global()->ForceDelete(v8_str("cell"))); 15492 ExpectString("(function() {" 15493 " try {" 15494 " return readCell();" 15495 " } catch(e) {" 15496 " return e.toString();" 15497 " }" 15498 "})()", 15499 "ReferenceError: cell is not defined"); 15500 } 15501 15502 15503 TEST(DontDeleteCellLoadICAPI) { 15504 const char* function_code = 15505 "function readCell() { while (true) { return cell; } }"; 15506 15507 // Run the code twice to initialize the load IC for a don't delete 15508 // cell created using the API. 15509 v8::HandleScope scope; 15510 LocalContext context; 15511 context->Global()->Set(v8_str("cell"), v8_str("value"), v8::DontDelete); 15512 ExpectBoolean("delete cell", false); 15513 CompileRun(function_code); 15514 ExpectString("readCell()", "value"); 15515 ExpectString("readCell()", "value"); 15516 15517 // Delete the cell using the API and check the inlined code works 15518 // correctly. 15519 CHECK(context->Global()->ForceDelete(v8_str("cell"))); 15520 ExpectString("(function() {" 15521 " try {" 15522 " return readCell();" 15523 " } catch(e) {" 15524 " return e.toString();" 15525 " }" 15526 "})()", 15527 "ReferenceError: cell is not defined"); 15528 } 15529 15530 15531 TEST(RegExp) { 15532 v8::HandleScope scope; 15533 LocalContext context; 15534 15535 v8::Handle<v8::RegExp> re = v8::RegExp::New(v8_str("foo"), v8::RegExp::kNone); 15536 CHECK(re->IsRegExp()); 15537 CHECK(re->GetSource()->Equals(v8_str("foo"))); 15538 CHECK_EQ(v8::RegExp::kNone, re->GetFlags()); 15539 15540 re = v8::RegExp::New(v8_str("bar"), 15541 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | 15542 v8::RegExp::kGlobal)); 15543 CHECK(re->IsRegExp()); 15544 CHECK(re->GetSource()->Equals(v8_str("bar"))); 15545 CHECK_EQ(v8::RegExp::kIgnoreCase | v8::RegExp::kGlobal, 15546 static_cast<int>(re->GetFlags())); 15547 15548 re = v8::RegExp::New(v8_str("baz"), 15549 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | 15550 v8::RegExp::kMultiline)); 15551 CHECK(re->IsRegExp()); 15552 CHECK(re->GetSource()->Equals(v8_str("baz"))); 15553 CHECK_EQ(v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline, 15554 static_cast<int>(re->GetFlags())); 15555 15556 re = CompileRun("/quux/").As<v8::RegExp>(); 15557 CHECK(re->IsRegExp()); 15558 CHECK(re->GetSource()->Equals(v8_str("quux"))); 15559 CHECK_EQ(v8::RegExp::kNone, re->GetFlags()); 15560 15561 re = CompileRun("/quux/gm").As<v8::RegExp>(); 15562 CHECK(re->IsRegExp()); 15563 CHECK(re->GetSource()->Equals(v8_str("quux"))); 15564 CHECK_EQ(v8::RegExp::kGlobal | v8::RegExp::kMultiline, 15565 static_cast<int>(re->GetFlags())); 15566 15567 // Override the RegExp constructor and check the API constructor 15568 // still works. 15569 CompileRun("RegExp = function() {}"); 15570 15571 re = v8::RegExp::New(v8_str("foobar"), v8::RegExp::kNone); 15572 CHECK(re->IsRegExp()); 15573 CHECK(re->GetSource()->Equals(v8_str("foobar"))); 15574 CHECK_EQ(v8::RegExp::kNone, re->GetFlags()); 15575 15576 re = v8::RegExp::New(v8_str("foobarbaz"), 15577 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | 15578 v8::RegExp::kMultiline)); 15579 CHECK(re->IsRegExp()); 15580 CHECK(re->GetSource()->Equals(v8_str("foobarbaz"))); 15581 CHECK_EQ(v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline, 15582 static_cast<int>(re->GetFlags())); 15583 15584 context->Global()->Set(v8_str("re"), re); 15585 ExpectTrue("re.test('FoobarbaZ')"); 15586 15587 // RegExps are objects on which you can set properties. 15588 re->Set(v8_str("property"), v8::Integer::New(32)); 15589 v8::Handle<v8::Value> value(CompileRun("re.property")); 15590 CHECK_EQ(32, value->Int32Value()); 15591 15592 v8::TryCatch try_catch; 15593 re = v8::RegExp::New(v8_str("foo["), v8::RegExp::kNone); 15594 CHECK(re.IsEmpty()); 15595 CHECK(try_catch.HasCaught()); 15596 context->Global()->Set(v8_str("ex"), try_catch.Exception()); 15597 ExpectTrue("ex instanceof SyntaxError"); 15598 } 15599 15600 15601 THREADED_TEST(Equals) { 15602 v8::HandleScope handleScope; 15603 LocalContext localContext; 15604 15605 v8::Handle<v8::Object> globalProxy = localContext->Global(); 15606 v8::Handle<Value> global = globalProxy->GetPrototype(); 15607 15608 CHECK(global->StrictEquals(global)); 15609 CHECK(!global->StrictEquals(globalProxy)); 15610 CHECK(!globalProxy->StrictEquals(global)); 15611 CHECK(globalProxy->StrictEquals(globalProxy)); 15612 15613 CHECK(global->Equals(global)); 15614 CHECK(!global->Equals(globalProxy)); 15615 CHECK(!globalProxy->Equals(global)); 15616 CHECK(globalProxy->Equals(globalProxy)); 15617 } 15618 15619 15620 static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, 15621 const v8::AccessorInfo& info ) { 15622 return v8_str("42!"); 15623 } 15624 15625 15626 static v8::Handle<v8::Array> Enumerator(const v8::AccessorInfo& info) { 15627 v8::Handle<v8::Array> result = v8::Array::New(); 15628 result->Set(0, v8_str("universalAnswer")); 15629 return result; 15630 } 15631 15632 15633 TEST(NamedEnumeratorAndForIn) { 15634 v8::HandleScope handle_scope; 15635 LocalContext context; 15636 v8::Context::Scope context_scope(context.local()); 15637 15638 v8::Handle<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New(); 15639 tmpl->SetNamedPropertyHandler(Getter, NULL, NULL, NULL, Enumerator); 15640 context->Global()->Set(v8_str("o"), tmpl->NewInstance()); 15641 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 15642 "var result = []; for (var k in o) result.push(k); result")); 15643 CHECK_EQ(1, result->Length()); 15644 CHECK_EQ(v8_str("universalAnswer"), result->Get(0)); 15645 } 15646 15647 15648 TEST(DefinePropertyPostDetach) { 15649 v8::HandleScope scope; 15650 LocalContext context; 15651 v8::Handle<v8::Object> proxy = context->Global(); 15652 v8::Handle<v8::Function> define_property = 15653 CompileRun("(function() {" 15654 " Object.defineProperty(" 15655 " this," 15656 " 1," 15657 " { configurable: true, enumerable: true, value: 3 });" 15658 "})").As<Function>(); 15659 context->DetachGlobal(); 15660 define_property->Call(proxy, 0, NULL); 15661 } 15662 15663 15664 static void InstallContextId(v8::Handle<Context> context, int id) { 15665 Context::Scope scope(context); 15666 CompileRun("Object.prototype").As<Object>()-> 15667 Set(v8_str("context_id"), v8::Integer::New(id)); 15668 } 15669 15670 15671 static void CheckContextId(v8::Handle<Object> object, int expected) { 15672 CHECK_EQ(expected, object->Get(v8_str("context_id"))->Int32Value()); 15673 } 15674 15675 15676 THREADED_TEST(CreationContext) { 15677 HandleScope handle_scope; 15678 Persistent<Context> context1 = Context::New(); 15679 InstallContextId(context1, 1); 15680 Persistent<Context> context2 = Context::New(); 15681 InstallContextId(context2, 2); 15682 Persistent<Context> context3 = Context::New(); 15683 InstallContextId(context3, 3); 15684 15685 Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New(); 15686 15687 Local<Object> object1; 15688 Local<Function> func1; 15689 { 15690 Context::Scope scope(context1); 15691 object1 = Object::New(); 15692 func1 = tmpl->GetFunction(); 15693 } 15694 15695 Local<Object> object2; 15696 Local<Function> func2; 15697 { 15698 Context::Scope scope(context2); 15699 object2 = Object::New(); 15700 func2 = tmpl->GetFunction(); 15701 } 15702 15703 Local<Object> instance1; 15704 Local<Object> instance2; 15705 15706 { 15707 Context::Scope scope(context3); 15708 instance1 = func1->NewInstance(); 15709 instance2 = func2->NewInstance(); 15710 } 15711 15712 CHECK(object1->CreationContext() == context1); 15713 CheckContextId(object1, 1); 15714 CHECK(func1->CreationContext() == context1); 15715 CheckContextId(func1, 1); 15716 CHECK(instance1->CreationContext() == context1); 15717 CheckContextId(instance1, 1); 15718 CHECK(object2->CreationContext() == context2); 15719 CheckContextId(object2, 2); 15720 CHECK(func2->CreationContext() == context2); 15721 CheckContextId(func2, 2); 15722 CHECK(instance2->CreationContext() == context2); 15723 CheckContextId(instance2, 2); 15724 15725 { 15726 Context::Scope scope(context1); 15727 CHECK(object1->CreationContext() == context1); 15728 CheckContextId(object1, 1); 15729 CHECK(func1->CreationContext() == context1); 15730 CheckContextId(func1, 1); 15731 CHECK(instance1->CreationContext() == context1); 15732 CheckContextId(instance1, 1); 15733 CHECK(object2->CreationContext() == context2); 15734 CheckContextId(object2, 2); 15735 CHECK(func2->CreationContext() == context2); 15736 CheckContextId(func2, 2); 15737 CHECK(instance2->CreationContext() == context2); 15738 CheckContextId(instance2, 2); 15739 } 15740 15741 { 15742 Context::Scope scope(context2); 15743 CHECK(object1->CreationContext() == context1); 15744 CheckContextId(object1, 1); 15745 CHECK(func1->CreationContext() == context1); 15746 CheckContextId(func1, 1); 15747 CHECK(instance1->CreationContext() == context1); 15748 CheckContextId(instance1, 1); 15749 CHECK(object2->CreationContext() == context2); 15750 CheckContextId(object2, 2); 15751 CHECK(func2->CreationContext() == context2); 15752 CheckContextId(func2, 2); 15753 CHECK(instance2->CreationContext() == context2); 15754 CheckContextId(instance2, 2); 15755 } 15756 15757 context1.Dispose(); 15758 context2.Dispose(); 15759 context3.Dispose(); 15760 } 15761 15762 15763 THREADED_TEST(CreationContextOfJsFunction) { 15764 HandleScope handle_scope; 15765 Persistent<Context> context = Context::New(); 15766 InstallContextId(context, 1); 15767 15768 Local<Object> function; 15769 { 15770 Context::Scope scope(context); 15771 function = CompileRun("function foo() {}; foo").As<Object>(); 15772 } 15773 15774 CHECK(function->CreationContext() == context); 15775 CheckContextId(function, 1); 15776 15777 context.Dispose(); 15778 } 15779 15780 15781 Handle<Value> HasOwnPropertyIndexedPropertyGetter(uint32_t index, 15782 const AccessorInfo& info) { 15783 if (index == 42) return v8_str("yes"); 15784 return Handle<v8::Integer>(); 15785 } 15786 15787 15788 Handle<Value> HasOwnPropertyNamedPropertyGetter(Local<String> property, 15789 const AccessorInfo& info) { 15790 if (property->Equals(v8_str("foo"))) return v8_str("yes"); 15791 return Handle<Value>(); 15792 } 15793 15794 15795 Handle<v8::Integer> HasOwnPropertyIndexedPropertyQuery( 15796 uint32_t index, const AccessorInfo& info) { 15797 if (index == 42) return v8_num(1).As<v8::Integer>(); 15798 return Handle<v8::Integer>(); 15799 } 15800 15801 15802 Handle<v8::Integer> HasOwnPropertyNamedPropertyQuery( 15803 Local<String> property, const AccessorInfo& info) { 15804 if (property->Equals(v8_str("foo"))) return v8_num(1).As<v8::Integer>(); 15805 return Handle<v8::Integer>(); 15806 } 15807 15808 15809 Handle<v8::Integer> HasOwnPropertyNamedPropertyQuery2( 15810 Local<String> property, const AccessorInfo& info) { 15811 if (property->Equals(v8_str("bar"))) return v8_num(1).As<v8::Integer>(); 15812 return Handle<v8::Integer>(); 15813 } 15814 15815 15816 Handle<Value> HasOwnPropertyAccessorGetter(Local<String> property, 15817 const AccessorInfo& info) { 15818 return v8_str("yes"); 15819 } 15820 15821 15822 TEST(HasOwnProperty) { 15823 v8::HandleScope scope; 15824 LocalContext env; 15825 { // Check normal properties and defined getters. 15826 Handle<Value> value = CompileRun( 15827 "function Foo() {" 15828 " this.foo = 11;" 15829 " this.__defineGetter__('baz', function() { return 1; });" 15830 "};" 15831 "function Bar() { " 15832 " this.bar = 13;" 15833 " this.__defineGetter__('bla', function() { return 2; });" 15834 "};" 15835 "Bar.prototype = new Foo();" 15836 "new Bar();"); 15837 CHECK(value->IsObject()); 15838 Handle<Object> object = value->ToObject(); 15839 CHECK(object->Has(v8_str("foo"))); 15840 CHECK(!object->HasOwnProperty(v8_str("foo"))); 15841 CHECK(object->HasOwnProperty(v8_str("bar"))); 15842 CHECK(object->Has(v8_str("baz"))); 15843 CHECK(!object->HasOwnProperty(v8_str("baz"))); 15844 CHECK(object->HasOwnProperty(v8_str("bla"))); 15845 } 15846 { // Check named getter interceptors. 15847 Handle<ObjectTemplate> templ = ObjectTemplate::New(); 15848 templ->SetNamedPropertyHandler(HasOwnPropertyNamedPropertyGetter); 15849 Handle<Object> instance = templ->NewInstance(); 15850 CHECK(!instance->HasOwnProperty(v8_str("42"))); 15851 CHECK(instance->HasOwnProperty(v8_str("foo"))); 15852 CHECK(!instance->HasOwnProperty(v8_str("bar"))); 15853 } 15854 { // Check indexed getter interceptors. 15855 Handle<ObjectTemplate> templ = ObjectTemplate::New(); 15856 templ->SetIndexedPropertyHandler(HasOwnPropertyIndexedPropertyGetter); 15857 Handle<Object> instance = templ->NewInstance(); 15858 CHECK(instance->HasOwnProperty(v8_str("42"))); 15859 CHECK(!instance->HasOwnProperty(v8_str("43"))); 15860 CHECK(!instance->HasOwnProperty(v8_str("foo"))); 15861 } 15862 { // Check named query interceptors. 15863 Handle<ObjectTemplate> templ = ObjectTemplate::New(); 15864 templ->SetNamedPropertyHandler(0, 0, HasOwnPropertyNamedPropertyQuery); 15865 Handle<Object> instance = templ->NewInstance(); 15866 CHECK(instance->HasOwnProperty(v8_str("foo"))); 15867 CHECK(!instance->HasOwnProperty(v8_str("bar"))); 15868 } 15869 { // Check indexed query interceptors. 15870 Handle<ObjectTemplate> templ = ObjectTemplate::New(); 15871 templ->SetIndexedPropertyHandler(0, 0, HasOwnPropertyIndexedPropertyQuery); 15872 Handle<Object> instance = templ->NewInstance(); 15873 CHECK(instance->HasOwnProperty(v8_str("42"))); 15874 CHECK(!instance->HasOwnProperty(v8_str("41"))); 15875 } 15876 { // Check callbacks. 15877 Handle<ObjectTemplate> templ = ObjectTemplate::New(); 15878 templ->SetAccessor(v8_str("foo"), HasOwnPropertyAccessorGetter); 15879 Handle<Object> instance = templ->NewInstance(); 15880 CHECK(instance->HasOwnProperty(v8_str("foo"))); 15881 CHECK(!instance->HasOwnProperty(v8_str("bar"))); 15882 } 15883 { // Check that query wins on disagreement. 15884 Handle<ObjectTemplate> templ = ObjectTemplate::New(); 15885 templ->SetNamedPropertyHandler(HasOwnPropertyNamedPropertyGetter, 15886 0, 15887 HasOwnPropertyNamedPropertyQuery2); 15888 Handle<Object> instance = templ->NewInstance(); 15889 CHECK(!instance->HasOwnProperty(v8_str("foo"))); 15890 CHECK(instance->HasOwnProperty(v8_str("bar"))); 15891 } 15892 } 15893 15894 15895 void CheckCodeGenerationAllowed() { 15896 Handle<Value> result = CompileRun("eval('42')"); 15897 CHECK_EQ(42, result->Int32Value()); 15898 result = CompileRun("(function(e) { return e('42'); })(eval)"); 15899 CHECK_EQ(42, result->Int32Value()); 15900 result = CompileRun("var f = new Function('return 42'); f()"); 15901 CHECK_EQ(42, result->Int32Value()); 15902 } 15903 15904 15905 void CheckCodeGenerationDisallowed() { 15906 TryCatch try_catch; 15907 15908 Handle<Value> result = CompileRun("eval('42')"); 15909 CHECK(result.IsEmpty()); 15910 CHECK(try_catch.HasCaught()); 15911 try_catch.Reset(); 15912 15913 result = CompileRun("(function(e) { return e('42'); })(eval)"); 15914 CHECK(result.IsEmpty()); 15915 CHECK(try_catch.HasCaught()); 15916 try_catch.Reset(); 15917 15918 result = CompileRun("var f = new Function('return 42'); f()"); 15919 CHECK(result.IsEmpty()); 15920 CHECK(try_catch.HasCaught()); 15921 } 15922 15923 15924 bool CodeGenerationAllowed(Local<Context> context) { 15925 ApiTestFuzzer::Fuzz(); 15926 return true; 15927 } 15928 15929 15930 bool CodeGenerationDisallowed(Local<Context> context) { 15931 ApiTestFuzzer::Fuzz(); 15932 return false; 15933 } 15934 15935 15936 THREADED_TEST(AllowCodeGenFromStrings) { 15937 v8::HandleScope scope; 15938 LocalContext context; 15939 15940 // eval and the Function constructor allowed by default. 15941 CHECK(context->IsCodeGenerationFromStringsAllowed()); 15942 CheckCodeGenerationAllowed(); 15943 15944 // Disallow eval and the Function constructor. 15945 context->AllowCodeGenerationFromStrings(false); 15946 CHECK(!context->IsCodeGenerationFromStringsAllowed()); 15947