test-api.cc revision 592a9fc1d8ea420377a2e7efd0600e20b058be2b
1// Copyright 2011 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28#include <limits.h> 29 30#include "v8.h" 31 32#include "api.h" 33#include "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 43static const bool kLogThreading = false; 44 45static bool IsNaN(double x) { 46#ifdef WIN32 47 return _isnan(x); 48#else 49 return isnan(x); 50#endif 51} 52 53using ::v8::AccessorInfo; 54using ::v8::Arguments; 55using ::v8::Context; 56using ::v8::Extension; 57using ::v8::Function; 58using ::v8::FunctionTemplate; 59using ::v8::Handle; 60using ::v8::HandleScope; 61using ::v8::Local; 62using ::v8::Message; 63using ::v8::MessageCallback; 64using ::v8::Object; 65using ::v8::ObjectTemplate; 66using ::v8::Persistent; 67using ::v8::Script; 68using ::v8::StackTrace; 69using ::v8::String; 70using ::v8::TryCatch; 71using ::v8::Undefined; 72using ::v8::V8; 73using ::v8::Value; 74 75 76static 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 83static 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 89static 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 96static void ExpectTrue(const char* code) { 97 ExpectBoolean(code, true); 98} 99 100 101static void ExpectFalse(const char* code) { 102 ExpectBoolean(code, false); 103} 104 105 106static void ExpectObject(const char* code, Local<Value> expected) { 107 Local<Value> result = CompileRun(code); 108 CHECK(result->Equals(expected)); 109} 110 111 112static void ExpectUndefined(const char* code) { 113 Local<Value> result = CompileRun(code); 114 CHECK(result->IsUndefined()); 115} 116 117 118static int signature_callback_count; 119static 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 130static 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 140THREADED_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 165THREADED_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 209THREADED_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 271THREADED_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 283THREADED_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 298THREADED_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 317THREADED_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 327static 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 335class 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 361class 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 385THREADED_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 410THREADED_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 432THREADED_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 // TODO(1608): This should use kAbortIncrementalMarking. 454 HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); 455 CHECK_EQ(1, dispose_count); 456} 457 458 459THREADED_TEST(ScriptMakingExternalAsciiString) { 460 int dispose_count = 0; 461 const char* c_source = "1 + 2 * 3"; 462 { 463 v8::HandleScope scope; 464 LocalContext env; 465 Local<String> source = v8_str(c_source); 466 // Trigger GCs so that the newly allocated string moves to old gen. 467 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now 468 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now 469 bool success = source->MakeExternal( 470 new TestAsciiResource(i::StrDup(c_source), &dispose_count)); 471 CHECK(success); 472 Local<Script> script = Script::Compile(source); 473 Local<Value> value = script->Run(); 474 CHECK(value->IsNumber()); 475 CHECK_EQ(7, value->Int32Value()); 476 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 477 CHECK_EQ(0, dispose_count); 478 } 479 i::Isolate::Current()->compilation_cache()->Clear(); 480 // TODO(1608): This should use kAbortIncrementalMarking. 481 HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); 482 CHECK_EQ(1, dispose_count); 483} 484 485 486TEST(MakingExternalStringConditions) { 487 v8::HandleScope scope; 488 LocalContext env; 489 490 // Free some space in the new space so that we can check freshness. 491 HEAP->CollectGarbage(i::NEW_SPACE); 492 HEAP->CollectGarbage(i::NEW_SPACE); 493 494 uint16_t* two_byte_string = AsciiToTwoByteString("s1"); 495 Local<String> small_string = String::New(two_byte_string); 496 i::DeleteArray(two_byte_string); 497 498 // We should refuse to externalize newly created small string. 499 CHECK(!small_string->CanMakeExternal()); 500 // Trigger GCs so that the newly allocated string moves to old gen. 501 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now 502 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now 503 // Old space strings should be accepted. 504 CHECK(small_string->CanMakeExternal()); 505 506 two_byte_string = AsciiToTwoByteString("small string 2"); 507 small_string = String::New(two_byte_string); 508 i::DeleteArray(two_byte_string); 509 510 // We should refuse externalizing newly created small string. 511 CHECK(!small_string->CanMakeExternal()); 512 for (int i = 0; i < 100; i++) { 513 String::Value value(small_string); 514 } 515 // Frequently used strings should be accepted. 516 CHECK(small_string->CanMakeExternal()); 517 518 const int buf_size = 10 * 1024; 519 char* buf = i::NewArray<char>(buf_size); 520 memset(buf, 'a', buf_size); 521 buf[buf_size - 1] = '\0'; 522 523 two_byte_string = AsciiToTwoByteString(buf); 524 Local<String> large_string = String::New(two_byte_string); 525 i::DeleteArray(buf); 526 i::DeleteArray(two_byte_string); 527 // Large strings should be immediately accepted. 528 CHECK(large_string->CanMakeExternal()); 529} 530 531 532TEST(MakingExternalAsciiStringConditions) { 533 v8::HandleScope scope; 534 LocalContext env; 535 536 // Free some space in the new space so that we can check freshness. 537 HEAP->CollectGarbage(i::NEW_SPACE); 538 HEAP->CollectGarbage(i::NEW_SPACE); 539 540 Local<String> small_string = String::New("s1"); 541 // We should refuse to externalize newly created small string. 542 CHECK(!small_string->CanMakeExternal()); 543 // Trigger GCs so that the newly allocated string moves to old gen. 544 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now 545 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now 546 // Old space strings should be accepted. 547 CHECK(small_string->CanMakeExternal()); 548 549 small_string = String::New("small string 2"); 550 // We should refuse externalizing newly created small string. 551 CHECK(!small_string->CanMakeExternal()); 552 for (int i = 0; i < 100; i++) { 553 String::Value value(small_string); 554 } 555 // Frequently used strings should be accepted. 556 CHECK(small_string->CanMakeExternal()); 557 558 const int buf_size = 10 * 1024; 559 char* buf = i::NewArray<char>(buf_size); 560 memset(buf, 'a', buf_size); 561 buf[buf_size - 1] = '\0'; 562 Local<String> large_string = String::New(buf); 563 i::DeleteArray(buf); 564 // Large strings should be immediately accepted. 565 CHECK(large_string->CanMakeExternal()); 566} 567 568 569THREADED_TEST(UsingExternalString) { 570 { 571 v8::HandleScope scope; 572 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 573 Local<String> string = 574 String::NewExternal(new TestResource(two_byte_string)); 575 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 576 // Trigger GCs so that the newly allocated string moves to old gen. 577 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now 578 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now 579 i::Handle<i::String> isymbol = FACTORY->SymbolFromString(istring); 580 CHECK(isymbol->IsSymbol()); 581 } 582 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 583 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 584} 585 586 587THREADED_TEST(UsingExternalAsciiString) { 588 { 589 v8::HandleScope scope; 590 const char* one_byte_string = "test string"; 591 Local<String> string = String::NewExternal( 592 new TestAsciiResource(i::StrDup(one_byte_string))); 593 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 594 // Trigger GCs so that the newly allocated string moves to old gen. 595 HEAP->CollectGarbage(i::NEW_SPACE); // in survivor space now 596 HEAP->CollectGarbage(i::NEW_SPACE); // in old gen now 597 i::Handle<i::String> isymbol = FACTORY->SymbolFromString(istring); 598 CHECK(isymbol->IsSymbol()); 599 } 600 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 601 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 602} 603 604 605THREADED_TEST(ScavengeExternalString) { 606 int dispose_count = 0; 607 bool in_new_space = false; 608 { 609 v8::HandleScope scope; 610 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 611 Local<String> string = 612 String::NewExternal(new TestResource(two_byte_string, 613 &dispose_count)); 614 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 615 HEAP->CollectGarbage(i::NEW_SPACE); 616 in_new_space = HEAP->InNewSpace(*istring); 617 CHECK(in_new_space || HEAP->old_data_space()->Contains(*istring)); 618 CHECK_EQ(0, dispose_count); 619 } 620 HEAP->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); 621 CHECK_EQ(1, dispose_count); 622} 623 624 625THREADED_TEST(ScavengeExternalAsciiString) { 626 int dispose_count = 0; 627 bool in_new_space = false; 628 { 629 v8::HandleScope scope; 630 const char* one_byte_string = "test string"; 631 Local<String> string = String::NewExternal( 632 new TestAsciiResource(i::StrDup(one_byte_string), &dispose_count)); 633 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 634 HEAP->CollectGarbage(i::NEW_SPACE); 635 in_new_space = HEAP->InNewSpace(*istring); 636 CHECK(in_new_space || HEAP->old_data_space()->Contains(*istring)); 637 CHECK_EQ(0, dispose_count); 638 } 639 HEAP->CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); 640 CHECK_EQ(1, dispose_count); 641} 642 643 644class TestAsciiResourceWithDisposeControl: public TestAsciiResource { 645 public: 646 // Only used by non-threaded tests, so it can use static fields. 647 static int dispose_calls; 648 static int dispose_count; 649 650 TestAsciiResourceWithDisposeControl(const char* data, bool dispose) 651 : TestAsciiResource(data, &dispose_count), 652 dispose_(dispose) { } 653 654 void Dispose() { 655 ++dispose_calls; 656 if (dispose_) delete this; 657 } 658 private: 659 bool dispose_; 660}; 661 662 663int TestAsciiResourceWithDisposeControl::dispose_count = 0; 664int TestAsciiResourceWithDisposeControl::dispose_calls = 0; 665 666 667TEST(ExternalStringWithDisposeHandling) { 668 const char* c_source = "1 + 2 * 3"; 669 670 // Use a stack allocated external string resource allocated object. 671 TestAsciiResourceWithDisposeControl::dispose_count = 0; 672 TestAsciiResourceWithDisposeControl::dispose_calls = 0; 673 TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false); 674 { 675 v8::HandleScope scope; 676 LocalContext env; 677 Local<String> source = String::NewExternal(&res_stack); 678 Local<Script> script = Script::Compile(source); 679 Local<Value> value = script->Run(); 680 CHECK(value->IsNumber()); 681 CHECK_EQ(7, value->Int32Value()); 682 HEAP->CollectAllAvailableGarbage(); 683 CHECK_EQ(0, TestAsciiResourceWithDisposeControl::dispose_count); 684 } 685 i::Isolate::Current()->compilation_cache()->Clear(); 686 HEAP->CollectAllAvailableGarbage(); 687 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); 688 CHECK_EQ(0, TestAsciiResourceWithDisposeControl::dispose_count); 689 690 // Use a heap allocated external string resource allocated object. 691 TestAsciiResourceWithDisposeControl::dispose_count = 0; 692 TestAsciiResourceWithDisposeControl::dispose_calls = 0; 693 TestAsciiResource* res_heap = 694 new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true); 695 { 696 v8::HandleScope scope; 697 LocalContext env; 698 Local<String> source = String::NewExternal(res_heap); 699 Local<Script> script = Script::Compile(source); 700 Local<Value> value = script->Run(); 701 CHECK(value->IsNumber()); 702 CHECK_EQ(7, value->Int32Value()); 703 HEAP->CollectAllAvailableGarbage(); 704 CHECK_EQ(0, TestAsciiResourceWithDisposeControl::dispose_count); 705 } 706 i::Isolate::Current()->compilation_cache()->Clear(); 707 HEAP->CollectAllAvailableGarbage(); 708 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); 709 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_count); 710} 711 712 713THREADED_TEST(StringConcat) { 714 { 715 v8::HandleScope scope; 716 LocalContext env; 717 const char* one_byte_string_1 = "function a_times_t"; 718 const char* two_byte_string_1 = "wo_plus_b(a, b) {return "; 719 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + "; 720 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + "; 721 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 722 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 723 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);"; 724 Local<String> left = v8_str(one_byte_string_1); 725 726 uint16_t* two_byte_source = AsciiToTwoByteString(two_byte_string_1); 727 Local<String> right = String::New(two_byte_source); 728 i::DeleteArray(two_byte_source); 729 730 Local<String> source = String::Concat(left, right); 731 right = String::NewExternal( 732 new TestAsciiResource(i::StrDup(one_byte_extern_1))); 733 source = String::Concat(source, right); 734 right = String::NewExternal( 735 new TestResource(AsciiToTwoByteString(two_byte_extern_1))); 736 source = String::Concat(source, right); 737 right = v8_str(one_byte_string_2); 738 source = String::Concat(source, right); 739 740 two_byte_source = AsciiToTwoByteString(two_byte_string_2); 741 right = String::New(two_byte_source); 742 i::DeleteArray(two_byte_source); 743 744 source = String::Concat(source, right); 745 right = String::NewExternal( 746 new TestResource(AsciiToTwoByteString(two_byte_extern_2))); 747 source = String::Concat(source, right); 748 Local<Script> script = Script::Compile(source); 749 Local<Value> value = script->Run(); 750 CHECK(value->IsNumber()); 751 CHECK_EQ(68, value->Int32Value()); 752 } 753 i::Isolate::Current()->compilation_cache()->Clear(); 754 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 755 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 756} 757 758 759THREADED_TEST(GlobalProperties) { 760 v8::HandleScope scope; 761 LocalContext env; 762 v8::Handle<v8::Object> global = env->Global(); 763 global->Set(v8_str("pi"), v8_num(3.1415926)); 764 Local<Value> pi = global->Get(v8_str("pi")); 765 CHECK_EQ(3.1415926, pi->NumberValue()); 766} 767 768 769static v8::Handle<Value> handle_call(const v8::Arguments& args) { 770 ApiTestFuzzer::Fuzz(); 771 return v8_num(102); 772} 773 774 775static v8::Handle<Value> construct_call(const v8::Arguments& args) { 776 ApiTestFuzzer::Fuzz(); 777 args.This()->Set(v8_str("x"), v8_num(1)); 778 args.This()->Set(v8_str("y"), v8_num(2)); 779 return args.This(); 780} 781 782static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) { 783 ApiTestFuzzer::Fuzz(); 784 return v8_num(239); 785} 786 787 788THREADED_TEST(FunctionTemplate) { 789 v8::HandleScope scope; 790 LocalContext env; 791 { 792 Local<v8::FunctionTemplate> fun_templ = 793 v8::FunctionTemplate::New(handle_call); 794 Local<Function> fun = fun_templ->GetFunction(); 795 env->Global()->Set(v8_str("obj"), fun); 796 Local<Script> script = v8_compile("obj()"); 797 CHECK_EQ(102, script->Run()->Int32Value()); 798 } 799 // Use SetCallHandler to initialize a function template, should work like the 800 // previous one. 801 { 802 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 803 fun_templ->SetCallHandler(handle_call); 804 Local<Function> fun = fun_templ->GetFunction(); 805 env->Global()->Set(v8_str("obj"), fun); 806 Local<Script> script = v8_compile("obj()"); 807 CHECK_EQ(102, script->Run()->Int32Value()); 808 } 809 // Test constructor calls. 810 { 811 Local<v8::FunctionTemplate> fun_templ = 812 v8::FunctionTemplate::New(construct_call); 813 fun_templ->SetClassName(v8_str("funky")); 814 fun_templ->InstanceTemplate()->SetAccessor(v8_str("m"), Return239); 815 Local<Function> fun = fun_templ->GetFunction(); 816 env->Global()->Set(v8_str("obj"), fun); 817 Local<Script> script = v8_compile("var s = new obj(); s.x"); 818 CHECK_EQ(1, script->Run()->Int32Value()); 819 820 Local<Value> result = v8_compile("(new obj()).toString()")->Run(); 821 CHECK_EQ(v8_str("[object funky]"), result); 822 823 result = v8_compile("(new obj()).m")->Run(); 824 CHECK_EQ(239, result->Int32Value()); 825 } 826} 827 828 829static void* expected_ptr; 830static v8::Handle<v8::Value> callback(const v8::Arguments& args) { 831 void* ptr = v8::External::Unwrap(args.Data()); 832 CHECK_EQ(expected_ptr, ptr); 833 return v8::True(); 834} 835 836 837static void TestExternalPointerWrapping() { 838 v8::HandleScope scope; 839 LocalContext env; 840 841 v8::Handle<v8::Value> data = v8::External::Wrap(expected_ptr); 842 843 v8::Handle<v8::Object> obj = v8::Object::New(); 844 obj->Set(v8_str("func"), 845 v8::FunctionTemplate::New(callback, data)->GetFunction()); 846 env->Global()->Set(v8_str("obj"), obj); 847 848 CHECK(CompileRun( 849 "function foo() {\n" 850 " for (var i = 0; i < 13; i++) obj.func();\n" 851 "}\n" 852 "foo(), true")->BooleanValue()); 853} 854 855 856THREADED_TEST(ExternalWrap) { 857 // Check heap allocated object. 858 int* ptr = new int; 859 expected_ptr = ptr; 860 TestExternalPointerWrapping(); 861 delete ptr; 862 863 // Check stack allocated object. 864 int foo; 865 expected_ptr = &foo; 866 TestExternalPointerWrapping(); 867 868 // Check not aligned addresses. 869 const int n = 100; 870 char* s = new char[n]; 871 for (int i = 0; i < n; i++) { 872 expected_ptr = s + i; 873 TestExternalPointerWrapping(); 874 } 875 876 delete[] s; 877 878 // Check several invalid addresses. 879 expected_ptr = reinterpret_cast<void*>(1); 880 TestExternalPointerWrapping(); 881 882 expected_ptr = reinterpret_cast<void*>(0xdeadbeef); 883 TestExternalPointerWrapping(); 884 885 expected_ptr = reinterpret_cast<void*>(0xdeadbeef + 1); 886 TestExternalPointerWrapping(); 887 888#if defined(V8_HOST_ARCH_X64) 889 // Check a value with a leading 1 bit in x64 Smi encoding. 890 expected_ptr = reinterpret_cast<void*>(0x400000000); 891 TestExternalPointerWrapping(); 892 893 expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef); 894 TestExternalPointerWrapping(); 895 896 expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef + 1); 897 TestExternalPointerWrapping(); 898#endif 899} 900 901 902THREADED_TEST(FindInstanceInPrototypeChain) { 903 v8::HandleScope scope; 904 LocalContext env; 905 906 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New(); 907 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New(); 908 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New(); 909 derived->Inherit(base); 910 911 Local<v8::Function> base_function = base->GetFunction(); 912 Local<v8::Function> derived_function = derived->GetFunction(); 913 Local<v8::Function> other_function = other->GetFunction(); 914 915 Local<v8::Object> base_instance = base_function->NewInstance(); 916 Local<v8::Object> derived_instance = derived_function->NewInstance(); 917 Local<v8::Object> derived_instance2 = derived_function->NewInstance(); 918 Local<v8::Object> other_instance = other_function->NewInstance(); 919 derived_instance2->Set(v8_str("__proto__"), derived_instance); 920 other_instance->Set(v8_str("__proto__"), derived_instance2); 921 922 // base_instance is only an instance of base. 923 CHECK_EQ(base_instance, 924 base_instance->FindInstanceInPrototypeChain(base)); 925 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty()); 926 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 927 928 // derived_instance is an instance of base and derived. 929 CHECK_EQ(derived_instance, 930 derived_instance->FindInstanceInPrototypeChain(base)); 931 CHECK_EQ(derived_instance, 932 derived_instance->FindInstanceInPrototypeChain(derived)); 933 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 934 935 // other_instance is an instance of other and its immediate 936 // prototype derived_instance2 is an instance of base and derived. 937 // Note, derived_instance is an instance of base and derived too, 938 // but it comes after derived_instance2 in the prototype chain of 939 // other_instance. 940 CHECK_EQ(derived_instance2, 941 other_instance->FindInstanceInPrototypeChain(base)); 942 CHECK_EQ(derived_instance2, 943 other_instance->FindInstanceInPrototypeChain(derived)); 944 CHECK_EQ(other_instance, 945 other_instance->FindInstanceInPrototypeChain(other)); 946} 947 948 949THREADED_TEST(TinyInteger) { 950 v8::HandleScope scope; 951 LocalContext env; 952 int32_t value = 239; 953 Local<v8::Integer> value_obj = v8::Integer::New(value); 954 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 955} 956 957 958THREADED_TEST(BigSmiInteger) { 959 v8::HandleScope scope; 960 LocalContext env; 961 int32_t value = i::Smi::kMaxValue; 962 // We cannot add one to a Smi::kMaxValue without wrapping. 963 if (i::kSmiValueSize < 32) { 964 CHECK(i::Smi::IsValid(value)); 965 CHECK(!i::Smi::IsValid(value + 1)); 966 Local<v8::Integer> value_obj = v8::Integer::New(value); 967 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 968 } 969} 970 971 972THREADED_TEST(BigInteger) { 973 v8::HandleScope scope; 974 LocalContext env; 975 // We cannot add one to a Smi::kMaxValue without wrapping. 976 if (i::kSmiValueSize < 32) { 977 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1. 978 // The code will not be run in that case, due to the "if" guard. 979 int32_t value = 980 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1); 981 CHECK(value > i::Smi::kMaxValue); 982 CHECK(!i::Smi::IsValid(value)); 983 Local<v8::Integer> value_obj = v8::Integer::New(value); 984 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 985 } 986} 987 988 989THREADED_TEST(TinyUnsignedInteger) { 990 v8::HandleScope scope; 991 LocalContext env; 992 uint32_t value = 239; 993 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 994 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 995} 996 997 998THREADED_TEST(BigUnsignedSmiInteger) { 999 v8::HandleScope scope; 1000 LocalContext env; 1001 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue); 1002 CHECK(i::Smi::IsValid(value)); 1003 CHECK(!i::Smi::IsValid(value + 1)); 1004 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 1005 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1006} 1007 1008 1009THREADED_TEST(BigUnsignedInteger) { 1010 v8::HandleScope scope; 1011 LocalContext env; 1012 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1; 1013 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue)); 1014 CHECK(!i::Smi::IsValid(value)); 1015 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 1016 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1017} 1018 1019 1020THREADED_TEST(OutOfSignedRangeUnsignedInteger) { 1021 v8::HandleScope scope; 1022 LocalContext env; 1023 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1; 1024 uint32_t value = INT32_MAX_AS_UINT + 1; 1025 CHECK(value > INT32_MAX_AS_UINT); // No overflow. 1026 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 1027 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1028} 1029 1030 1031THREADED_TEST(IsNativeError) { 1032 v8::HandleScope scope; 1033 LocalContext env; 1034 v8::Handle<Value> syntax_error = CompileRun( 1035 "var out = 0; try { eval(\"#\"); } catch(x) { out = x; } out; "); 1036 CHECK(syntax_error->IsNativeError()); 1037 v8::Handle<Value> not_error = CompileRun("{a:42}"); 1038 CHECK(!not_error->IsNativeError()); 1039 v8::Handle<Value> not_object = CompileRun("42"); 1040 CHECK(!not_object->IsNativeError()); 1041} 1042 1043 1044THREADED_TEST(StringObject) { 1045 v8::HandleScope scope; 1046 LocalContext env; 1047 v8::Handle<Value> boxed_string = CompileRun("new String(\"test\")"); 1048 CHECK(boxed_string->IsStringObject()); 1049 v8::Handle<Value> unboxed_string = CompileRun("\"test\""); 1050 CHECK(!unboxed_string->IsStringObject()); 1051 v8::Handle<Value> boxed_not_string = CompileRun("new Number(42)"); 1052 CHECK(!boxed_not_string->IsStringObject()); 1053 v8::Handle<Value> not_object = CompileRun("0"); 1054 CHECK(!not_object->IsStringObject()); 1055 v8::Handle<v8::StringObject> as_boxed = boxed_string.As<v8::StringObject>(); 1056 CHECK(!as_boxed.IsEmpty()); 1057 Local<v8::String> the_string = as_boxed->StringValue(); 1058 CHECK(!the_string.IsEmpty()); 1059 ExpectObject("\"test\"", the_string); 1060 v8::Handle<v8::Value> new_boxed_string = v8::StringObject::New(the_string); 1061 CHECK(new_boxed_string->IsStringObject()); 1062 as_boxed = new_boxed_string.As<v8::StringObject>(); 1063 the_string = as_boxed->StringValue(); 1064 CHECK(!the_string.IsEmpty()); 1065 ExpectObject("\"test\"", the_string); 1066} 1067 1068 1069THREADED_TEST(NumberObject) { 1070 v8::HandleScope scope; 1071 LocalContext env; 1072 v8::Handle<Value> boxed_number = CompileRun("new Number(42)"); 1073 CHECK(boxed_number->IsNumberObject()); 1074 v8::Handle<Value> unboxed_number = CompileRun("42"); 1075 CHECK(!unboxed_number->IsNumberObject()); 1076 v8::Handle<Value> boxed_not_number = CompileRun("new Boolean(false)"); 1077 CHECK(!boxed_not_number->IsNumberObject()); 1078 v8::Handle<v8::NumberObject> as_boxed = boxed_number.As<v8::NumberObject>(); 1079 CHECK(!as_boxed.IsEmpty()); 1080 double the_number = as_boxed->NumberValue(); 1081 CHECK_EQ(42.0, the_number); 1082 v8::Handle<v8::Value> new_boxed_number = v8::NumberObject::New(43); 1083 CHECK(new_boxed_number->IsNumberObject()); 1084 as_boxed = new_boxed_number.As<v8::NumberObject>(); 1085 the_number = as_boxed->NumberValue(); 1086 CHECK_EQ(43.0, the_number); 1087} 1088 1089 1090THREADED_TEST(BooleanObject) { 1091 v8::HandleScope scope; 1092 LocalContext env; 1093 v8::Handle<Value> boxed_boolean = CompileRun("new Boolean(true)"); 1094 CHECK(boxed_boolean->IsBooleanObject()); 1095 v8::Handle<Value> unboxed_boolean = CompileRun("true"); 1096 CHECK(!unboxed_boolean->IsBooleanObject()); 1097 v8::Handle<Value> boxed_not_boolean = CompileRun("new Number(42)"); 1098 CHECK(!boxed_not_boolean->IsBooleanObject()); 1099 v8::Handle<v8::BooleanObject> as_boxed = 1100 boxed_boolean.As<v8::BooleanObject>(); 1101 CHECK(!as_boxed.IsEmpty()); 1102 bool the_boolean = as_boxed->BooleanValue(); 1103 CHECK_EQ(true, the_boolean); 1104 v8::Handle<v8::Value> boxed_true = v8::BooleanObject::New(true); 1105 v8::Handle<v8::Value> boxed_false = v8::BooleanObject::New(false); 1106 CHECK(boxed_true->IsBooleanObject()); 1107 CHECK(boxed_false->IsBooleanObject()); 1108 as_boxed = boxed_true.As<v8::BooleanObject>(); 1109 CHECK_EQ(true, as_boxed->BooleanValue()); 1110 as_boxed = boxed_false.As<v8::BooleanObject>(); 1111 CHECK_EQ(false, as_boxed->BooleanValue()); 1112} 1113 1114 1115THREADED_TEST(Number) { 1116 v8::HandleScope scope; 1117 LocalContext env; 1118 double PI = 3.1415926; 1119 Local<v8::Number> pi_obj = v8::Number::New(PI); 1120 CHECK_EQ(PI, pi_obj->NumberValue()); 1121} 1122 1123 1124THREADED_TEST(ToNumber) { 1125 v8::HandleScope scope; 1126 LocalContext env; 1127 Local<String> str = v8_str("3.1415926"); 1128 CHECK_EQ(3.1415926, str->NumberValue()); 1129 v8::Handle<v8::Boolean> t = v8::True(); 1130 CHECK_EQ(1.0, t->NumberValue()); 1131 v8::Handle<v8::Boolean> f = v8::False(); 1132 CHECK_EQ(0.0, f->NumberValue()); 1133} 1134 1135 1136THREADED_TEST(Date) { 1137 v8::HandleScope scope; 1138 LocalContext env; 1139 double PI = 3.1415926; 1140 Local<Value> date = v8::Date::New(PI); 1141 CHECK_EQ(3.0, date->NumberValue()); 1142 date.As<v8::Date>()->Set(v8_str("property"), v8::Integer::New(42)); 1143 CHECK_EQ(42, date.As<v8::Date>()->Get(v8_str("property"))->Int32Value()); 1144} 1145 1146 1147THREADED_TEST(Boolean) { 1148 v8::HandleScope scope; 1149 LocalContext env; 1150 v8::Handle<v8::Boolean> t = v8::True(); 1151 CHECK(t->Value()); 1152 v8::Handle<v8::Boolean> f = v8::False(); 1153 CHECK(!f->Value()); 1154 v8::Handle<v8::Primitive> u = v8::Undefined(); 1155 CHECK(!u->BooleanValue()); 1156 v8::Handle<v8::Primitive> n = v8::Null(); 1157 CHECK(!n->BooleanValue()); 1158 v8::Handle<String> str1 = v8_str(""); 1159 CHECK(!str1->BooleanValue()); 1160 v8::Handle<String> str2 = v8_str("x"); 1161 CHECK(str2->BooleanValue()); 1162 CHECK(!v8::Number::New(0)->BooleanValue()); 1163 CHECK(v8::Number::New(-1)->BooleanValue()); 1164 CHECK(v8::Number::New(1)->BooleanValue()); 1165 CHECK(v8::Number::New(42)->BooleanValue()); 1166 CHECK(!v8_compile("NaN")->Run()->BooleanValue()); 1167} 1168 1169 1170static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) { 1171 ApiTestFuzzer::Fuzz(); 1172 return v8_num(13.4); 1173} 1174 1175 1176static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) { 1177 ApiTestFuzzer::Fuzz(); 1178 return v8_num(876); 1179} 1180 1181 1182THREADED_TEST(GlobalPrototype) { 1183 v8::HandleScope scope; 1184 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 1185 func_templ->PrototypeTemplate()->Set( 1186 "dummy", 1187 v8::FunctionTemplate::New(DummyCallHandler)); 1188 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate(); 1189 templ->Set("x", v8_num(200)); 1190 templ->SetAccessor(v8_str("m"), GetM); 1191 LocalContext env(0, templ); 1192 v8::Handle<v8::Object> obj(env->Global()); 1193 v8::Handle<Script> script(v8_compile("dummy()")); 1194 v8::Handle<Value> result(script->Run()); 1195 CHECK_EQ(13.4, result->NumberValue()); 1196 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value()); 1197 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value()); 1198} 1199 1200 1201THREADED_TEST(ObjectTemplate) { 1202 v8::HandleScope scope; 1203 Local<ObjectTemplate> templ1 = ObjectTemplate::New(); 1204 templ1->Set("x", v8_num(10)); 1205 templ1->Set("y", v8_num(13)); 1206 LocalContext env; 1207 Local<v8::Object> instance1 = templ1->NewInstance(); 1208 env->Global()->Set(v8_str("p"), instance1); 1209 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue()); 1210 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue()); 1211 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); 1212 fun->PrototypeTemplate()->Set("nirk", v8_num(123)); 1213 Local<ObjectTemplate> templ2 = fun->InstanceTemplate(); 1214 templ2->Set("a", v8_num(12)); 1215 templ2->Set("b", templ1); 1216 Local<v8::Object> instance2 = templ2->NewInstance(); 1217 env->Global()->Set(v8_str("q"), instance2); 1218 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue()); 1219 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue()); 1220 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue()); 1221 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue()); 1222} 1223 1224 1225static v8::Handle<Value> GetFlabby(const v8::Arguments& args) { 1226 ApiTestFuzzer::Fuzz(); 1227 return v8_num(17.2); 1228} 1229 1230 1231static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) { 1232 ApiTestFuzzer::Fuzz(); 1233 return v8_num(15.2); 1234} 1235 1236 1237THREADED_TEST(DescriptorInheritance) { 1238 v8::HandleScope scope; 1239 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New(); 1240 super->PrototypeTemplate()->Set("flabby", 1241 v8::FunctionTemplate::New(GetFlabby)); 1242 super->PrototypeTemplate()->Set("PI", v8_num(3.14)); 1243 1244 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd); 1245 1246 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New(); 1247 base1->Inherit(super); 1248 base1->PrototypeTemplate()->Set("v1", v8_num(20.1)); 1249 1250 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New(); 1251 base2->Inherit(super); 1252 base2->PrototypeTemplate()->Set("v2", v8_num(10.1)); 1253 1254 LocalContext env; 1255 1256 env->Global()->Set(v8_str("s"), super->GetFunction()); 1257 env->Global()->Set(v8_str("base1"), base1->GetFunction()); 1258 env->Global()->Set(v8_str("base2"), base2->GetFunction()); 1259 1260 // Checks right __proto__ chain. 1261 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue()); 1262 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue()); 1263 1264 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue()); 1265 1266 // Instance accessor should not be visible on function object or its prototype 1267 CHECK(CompileRun("s.knurd == undefined")->BooleanValue()); 1268 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue()); 1269 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue()); 1270 1271 env->Global()->Set(v8_str("obj"), 1272 base1->GetFunction()->NewInstance()); 1273 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue()); 1274 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue()); 1275 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue()); 1276 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue()); 1277 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue()); 1278 1279 env->Global()->Set(v8_str("obj2"), 1280 base2->GetFunction()->NewInstance()); 1281 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue()); 1282 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue()); 1283 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue()); 1284 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue()); 1285 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue()); 1286 1287 // base1 and base2 cannot cross reference to each's prototype 1288 CHECK(v8_compile("obj.v2")->Run()->IsUndefined()); 1289 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined()); 1290} 1291 1292 1293int echo_named_call_count; 1294 1295 1296static v8::Handle<Value> EchoNamedProperty(Local<String> name, 1297 const AccessorInfo& info) { 1298 ApiTestFuzzer::Fuzz(); 1299 CHECK_EQ(v8_str("data"), info.Data()); 1300 echo_named_call_count++; 1301 return name; 1302} 1303 1304// Helper functions for Interceptor/Accessor interaction tests 1305 1306Handle<Value> SimpleAccessorGetter(Local<String> name, 1307 const AccessorInfo& info) { 1308 Handle<Object> self = info.This(); 1309 return self->Get(String::Concat(v8_str("accessor_"), name)); 1310} 1311 1312void SimpleAccessorSetter(Local<String> name, Local<Value> value, 1313 const AccessorInfo& info) { 1314 Handle<Object> self = info.This(); 1315 self->Set(String::Concat(v8_str("accessor_"), name), value); 1316} 1317 1318Handle<Value> EmptyInterceptorGetter(Local<String> name, 1319 const AccessorInfo& info) { 1320 return Handle<Value>(); 1321} 1322 1323Handle<Value> EmptyInterceptorSetter(Local<String> name, 1324 Local<Value> value, 1325 const AccessorInfo& info) { 1326 return Handle<Value>(); 1327} 1328 1329Handle<Value> InterceptorGetter(Local<String> name, 1330 const AccessorInfo& info) { 1331 // Intercept names that start with 'interceptor_'. 1332 String::AsciiValue ascii(name); 1333 char* name_str = *ascii; 1334 char prefix[] = "interceptor_"; 1335 int i; 1336 for (i = 0; name_str[i] && prefix[i]; ++i) { 1337 if (name_str[i] != prefix[i]) return Handle<Value>(); 1338 } 1339 Handle<Object> self = info.This(); 1340 return self->GetHiddenValue(v8_str(name_str + i)); 1341} 1342 1343Handle<Value> InterceptorSetter(Local<String> name, 1344 Local<Value> value, 1345 const AccessorInfo& info) { 1346 // Intercept accesses that set certain integer values. 1347 if (value->IsInt32() && value->Int32Value() < 10000) { 1348 Handle<Object> self = info.This(); 1349 self->SetHiddenValue(name, value); 1350 return value; 1351 } 1352 return Handle<Value>(); 1353} 1354 1355void AddAccessor(Handle<FunctionTemplate> templ, 1356 Handle<String> name, 1357 v8::AccessorGetter getter, 1358 v8::AccessorSetter setter) { 1359 templ->PrototypeTemplate()->SetAccessor(name, getter, setter); 1360} 1361 1362void AddInterceptor(Handle<FunctionTemplate> templ, 1363 v8::NamedPropertyGetter getter, 1364 v8::NamedPropertySetter setter) { 1365 templ->InstanceTemplate()->SetNamedPropertyHandler(getter, setter); 1366} 1367 1368THREADED_TEST(EmptyInterceptorDoesNotShadowAccessors) { 1369 v8::HandleScope scope; 1370 Handle<FunctionTemplate> parent = FunctionTemplate::New(); 1371 Handle<FunctionTemplate> child = FunctionTemplate::New(); 1372 child->Inherit(parent); 1373 AddAccessor(parent, v8_str("age"), 1374 SimpleAccessorGetter, SimpleAccessorSetter); 1375 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); 1376 LocalContext env; 1377 env->Global()->Set(v8_str("Child"), child->GetFunction()); 1378 CompileRun("var child = new Child;" 1379 "child.age = 10;"); 1380 ExpectBoolean("child.hasOwnProperty('age')", false); 1381 ExpectInt32("child.age", 10); 1382 ExpectInt32("child.accessor_age", 10); 1383} 1384 1385THREADED_TEST(EmptyInterceptorDoesNotShadowJSAccessors) { 1386 v8::HandleScope scope; 1387 Handle<FunctionTemplate> parent = FunctionTemplate::New(); 1388 Handle<FunctionTemplate> child = FunctionTemplate::New(); 1389 child->Inherit(parent); 1390 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); 1391 LocalContext env; 1392 env->Global()->Set(v8_str("Child"), child->GetFunction()); 1393 CompileRun("var child = new Child;" 1394 "var parent = child.__proto__;" 1395 "Object.defineProperty(parent, 'age', " 1396 " {get: function(){ return this.accessor_age; }, " 1397 " set: function(v){ this.accessor_age = v; }, " 1398 " enumerable: true, configurable: true});" 1399 "child.age = 10;"); 1400 ExpectBoolean("child.hasOwnProperty('age')", false); 1401 ExpectInt32("child.age", 10); 1402 ExpectInt32("child.accessor_age", 10); 1403} 1404 1405THREADED_TEST(EmptyInterceptorDoesNotAffectJSProperties) { 1406 v8::HandleScope scope; 1407 Handle<FunctionTemplate> parent = FunctionTemplate::New(); 1408 Handle<FunctionTemplate> child = FunctionTemplate::New(); 1409 child->Inherit(parent); 1410 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); 1411 LocalContext env; 1412 env->Global()->Set(v8_str("Child"), child->GetFunction()); 1413 CompileRun("var child = new Child;" 1414 "var parent = child.__proto__;" 1415 "parent.name = 'Alice';"); 1416 ExpectBoolean("child.hasOwnProperty('name')", false); 1417 ExpectString("child.name", "Alice"); 1418 CompileRun("child.name = 'Bob';"); 1419 ExpectString("child.name", "Bob"); 1420 ExpectBoolean("child.hasOwnProperty('name')", true); 1421 ExpectString("parent.name", "Alice"); 1422} 1423 1424THREADED_TEST(SwitchFromInterceptorToAccessor) { 1425 v8::HandleScope scope; 1426 Handle<FunctionTemplate> parent = FunctionTemplate::New(); 1427 Handle<FunctionTemplate> child = FunctionTemplate::New(); 1428 child->Inherit(parent); 1429 AddAccessor(parent, v8_str("age"), 1430 SimpleAccessorGetter, SimpleAccessorSetter); 1431 AddInterceptor(child, InterceptorGetter, InterceptorSetter); 1432 LocalContext env; 1433 env->Global()->Set(v8_str("Child"), child->GetFunction()); 1434 CompileRun("var child = new Child;" 1435 "function setAge(i){ child.age = i; };" 1436 "for(var i = 0; i <= 10000; i++) setAge(i);"); 1437 // All i < 10000 go to the interceptor. 1438 ExpectInt32("child.interceptor_age", 9999); 1439 // The last i goes to the accessor. 1440 ExpectInt32("child.accessor_age", 10000); 1441} 1442 1443THREADED_TEST(SwitchFromAccessorToInterceptor) { 1444 v8::HandleScope scope; 1445 Handle<FunctionTemplate> parent = FunctionTemplate::New(); 1446 Handle<FunctionTemplate> child = FunctionTemplate::New(); 1447 child->Inherit(parent); 1448 AddAccessor(parent, v8_str("age"), 1449 SimpleAccessorGetter, SimpleAccessorSetter); 1450 AddInterceptor(child, InterceptorGetter, InterceptorSetter); 1451 LocalContext env; 1452 env->Global()->Set(v8_str("Child"), child->GetFunction()); 1453 CompileRun("var child = new Child;" 1454 "function setAge(i){ child.age = i; };" 1455 "for(var i = 20000; i >= 9999; i--) setAge(i);"); 1456 // All i >= 10000 go to the accessor. 1457 ExpectInt32("child.accessor_age", 10000); 1458 // The last i goes to the interceptor. 1459 ExpectInt32("child.interceptor_age", 9999); 1460} 1461 1462THREADED_TEST(SwitchFromInterceptorToProperty) { 1463 v8::HandleScope scope; 1464 Handle<FunctionTemplate> parent = FunctionTemplate::New(); 1465 Handle<FunctionTemplate> child = FunctionTemplate::New(); 1466 child->Inherit(parent); 1467 AddInterceptor(child, InterceptorGetter, InterceptorSetter); 1468 LocalContext env; 1469 env->Global()->Set(v8_str("Child"), child->GetFunction()); 1470 CompileRun("var child = new Child;" 1471 "function setAge(i){ child.age = i; };" 1472 "for(var i = 0; i <= 10000; i++) setAge(i);"); 1473 // All i < 10000 go to the interceptor. 1474 ExpectInt32("child.interceptor_age", 9999); 1475 // The last i goes to child's own property. 1476 ExpectInt32("child.age", 10000); 1477} 1478 1479THREADED_TEST(SwitchFromPropertyToInterceptor) { 1480 v8::HandleScope scope; 1481 Handle<FunctionTemplate> parent = FunctionTemplate::New(); 1482 Handle<FunctionTemplate> child = FunctionTemplate::New(); 1483 child->Inherit(parent); 1484 AddInterceptor(child, InterceptorGetter, InterceptorSetter); 1485 LocalContext env; 1486 env->Global()->Set(v8_str("Child"), child->GetFunction()); 1487 CompileRun("var child = new Child;" 1488 "function setAge(i){ child.age = i; };" 1489 "for(var i = 20000; i >= 9999; i--) setAge(i);"); 1490 // All i >= 10000 go to child's own property. 1491 ExpectInt32("child.age", 10000); 1492 // The last i goes to the interceptor. 1493 ExpectInt32("child.interceptor_age", 9999); 1494} 1495 1496THREADED_TEST(NamedPropertyHandlerGetter) { 1497 echo_named_call_count = 0; 1498 v8::HandleScope scope; 1499 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1500 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty, 1501 0, 0, 0, 0, 1502 v8_str("data")); 1503 LocalContext env; 1504 env->Global()->Set(v8_str("obj"), 1505 templ->GetFunction()->NewInstance()); 1506 CHECK_EQ(echo_named_call_count, 0); 1507 v8_compile("obj.x")->Run(); 1508 CHECK_EQ(echo_named_call_count, 1); 1509 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;"; 1510 v8::Handle<Value> str = CompileRun(code); 1511 String::AsciiValue value(str); 1512 CHECK_EQ(*value, "oddlepoddle"); 1513 // Check default behavior 1514 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10); 1515 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue()); 1516 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue()); 1517} 1518 1519 1520int echo_indexed_call_count = 0; 1521 1522 1523static v8::Handle<Value> EchoIndexedProperty(uint32_t index, 1524 const AccessorInfo& info) { 1525 ApiTestFuzzer::Fuzz(); 1526 CHECK_EQ(v8_num(637), info.Data()); 1527 echo_indexed_call_count++; 1528 return v8_num(index); 1529} 1530 1531 1532THREADED_TEST(IndexedPropertyHandlerGetter) { 1533 v8::HandleScope scope; 1534 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1535 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty, 1536 0, 0, 0, 0, 1537 v8_num(637)); 1538 LocalContext env; 1539 env->Global()->Set(v8_str("obj"), 1540 templ->GetFunction()->NewInstance()); 1541 Local<Script> script = v8_compile("obj[900]"); 1542 CHECK_EQ(script->Run()->Int32Value(), 900); 1543} 1544 1545 1546v8::Handle<v8::Object> bottom; 1547 1548static v8::Handle<Value> CheckThisIndexedPropertyHandler( 1549 uint32_t index, 1550 const AccessorInfo& info) { 1551 ApiTestFuzzer::Fuzz(); 1552 CHECK(info.This()->Equals(bottom)); 1553 return v8::Handle<Value>(); 1554} 1555 1556static v8::Handle<Value> CheckThisNamedPropertyHandler( 1557 Local<String> name, 1558 const AccessorInfo& info) { 1559 ApiTestFuzzer::Fuzz(); 1560 CHECK(info.This()->Equals(bottom)); 1561 return v8::Handle<Value>(); 1562} 1563 1564 1565v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index, 1566 Local<Value> value, 1567 const AccessorInfo& info) { 1568 ApiTestFuzzer::Fuzz(); 1569 CHECK(info.This()->Equals(bottom)); 1570 return v8::Handle<Value>(); 1571} 1572 1573 1574v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property, 1575 Local<Value> value, 1576 const AccessorInfo& info) { 1577 ApiTestFuzzer::Fuzz(); 1578 CHECK(info.This()->Equals(bottom)); 1579 return v8::Handle<Value>(); 1580} 1581 1582v8::Handle<v8::Integer> CheckThisIndexedPropertyQuery( 1583 uint32_t index, 1584 const AccessorInfo& info) { 1585 ApiTestFuzzer::Fuzz(); 1586 CHECK(info.This()->Equals(bottom)); 1587 return v8::Handle<v8::Integer>(); 1588} 1589 1590 1591v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property, 1592 const AccessorInfo& info) { 1593 ApiTestFuzzer::Fuzz(); 1594 CHECK(info.This()->Equals(bottom)); 1595 return v8::Handle<v8::Integer>(); 1596} 1597 1598 1599v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter( 1600 uint32_t index, 1601 const AccessorInfo& info) { 1602 ApiTestFuzzer::Fuzz(); 1603 CHECK(info.This()->Equals(bottom)); 1604 return v8::Handle<v8::Boolean>(); 1605} 1606 1607 1608v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter( 1609 Local<String> property, 1610 const AccessorInfo& info) { 1611 ApiTestFuzzer::Fuzz(); 1612 CHECK(info.This()->Equals(bottom)); 1613 return v8::Handle<v8::Boolean>(); 1614} 1615 1616 1617v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator( 1618 const AccessorInfo& info) { 1619 ApiTestFuzzer::Fuzz(); 1620 CHECK(info.This()->Equals(bottom)); 1621 return v8::Handle<v8::Array>(); 1622} 1623 1624 1625v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator( 1626 const AccessorInfo& info) { 1627 ApiTestFuzzer::Fuzz(); 1628 CHECK(info.This()->Equals(bottom)); 1629 return v8::Handle<v8::Array>(); 1630} 1631 1632 1633THREADED_TEST(PropertyHandlerInPrototype) { 1634 v8::HandleScope scope; 1635 LocalContext env; 1636 1637 // Set up a prototype chain with three interceptors. 1638 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1639 templ->InstanceTemplate()->SetIndexedPropertyHandler( 1640 CheckThisIndexedPropertyHandler, 1641 CheckThisIndexedPropertySetter, 1642 CheckThisIndexedPropertyQuery, 1643 CheckThisIndexedPropertyDeleter, 1644 CheckThisIndexedPropertyEnumerator); 1645 1646 templ->InstanceTemplate()->SetNamedPropertyHandler( 1647 CheckThisNamedPropertyHandler, 1648 CheckThisNamedPropertySetter, 1649 CheckThisNamedPropertyQuery, 1650 CheckThisNamedPropertyDeleter, 1651 CheckThisNamedPropertyEnumerator); 1652 1653 bottom = templ->GetFunction()->NewInstance(); 1654 Local<v8::Object> top = templ->GetFunction()->NewInstance(); 1655 Local<v8::Object> middle = templ->GetFunction()->NewInstance(); 1656 1657 bottom->Set(v8_str("__proto__"), middle); 1658 middle->Set(v8_str("__proto__"), top); 1659 env->Global()->Set(v8_str("obj"), bottom); 1660 1661 // Indexed and named get. 1662 Script::Compile(v8_str("obj[0]"))->Run(); 1663 Script::Compile(v8_str("obj.x"))->Run(); 1664 1665 // Indexed and named set. 1666 Script::Compile(v8_str("obj[1] = 42"))->Run(); 1667 Script::Compile(v8_str("obj.y = 42"))->Run(); 1668 1669 // Indexed and named query. 1670 Script::Compile(v8_str("0 in obj"))->Run(); 1671 Script::Compile(v8_str("'x' in obj"))->Run(); 1672 1673 // Indexed and named deleter. 1674 Script::Compile(v8_str("delete obj[0]"))->Run(); 1675 Script::Compile(v8_str("delete obj.x"))->Run(); 1676 1677 // Enumerators. 1678 Script::Compile(v8_str("for (var p in obj) ;"))->Run(); 1679} 1680 1681 1682static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key, 1683 const AccessorInfo& info) { 1684 ApiTestFuzzer::Fuzz(); 1685 if (v8_str("pre")->Equals(key)) { 1686 return v8_str("PrePropertyHandler: pre"); 1687 } 1688 return v8::Handle<String>(); 1689} 1690 1691 1692static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key, 1693 const AccessorInfo&) { 1694 if (v8_str("pre")->Equals(key)) { 1695 return v8::Integer::New(v8::None); 1696 } 1697 1698 return v8::Handle<v8::Integer>(); // do not intercept the call 1699} 1700 1701 1702THREADED_TEST(PrePropertyHandler) { 1703 v8::HandleScope scope; 1704 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(); 1705 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet, 1706 0, 1707 PrePropertyHandlerQuery); 1708 LocalContext env(NULL, desc->InstanceTemplate()); 1709 Script::Compile(v8_str( 1710 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run(); 1711 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run(); 1712 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre); 1713 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run(); 1714 CHECK_EQ(v8_str("Object: on"), result_on); 1715 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run(); 1716 CHECK(result_post.IsEmpty()); 1717} 1718 1719 1720THREADED_TEST(UndefinedIsNotEnumerable) { 1721 v8::HandleScope scope; 1722 LocalContext env; 1723 v8::Handle<Value> result = Script::Compile(v8_str( 1724 "this.propertyIsEnumerable(undefined)"))->Run(); 1725 CHECK(result->IsFalse()); 1726} 1727 1728 1729v8::Handle<Script> call_recursively_script; 1730static const int kTargetRecursionDepth = 200; // near maximum 1731 1732 1733static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) { 1734 ApiTestFuzzer::Fuzz(); 1735 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1736 if (depth == kTargetRecursionDepth) return v8::Undefined(); 1737 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1738 return call_recursively_script->Run(); 1739} 1740 1741 1742static v8::Handle<Value> CallFunctionRecursivelyCall( 1743 const v8::Arguments& args) { 1744 ApiTestFuzzer::Fuzz(); 1745 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1746 if (depth == kTargetRecursionDepth) { 1747 printf("[depth = %d]\n", depth); 1748 return v8::Undefined(); 1749 } 1750 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1751 v8::Handle<Value> function = 1752 args.This()->Get(v8_str("callFunctionRecursively")); 1753 return function.As<Function>()->Call(args.This(), 0, NULL); 1754} 1755 1756 1757THREADED_TEST(DeepCrossLanguageRecursion) { 1758 v8::HandleScope scope; 1759 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 1760 global->Set(v8_str("callScriptRecursively"), 1761 v8::FunctionTemplate::New(CallScriptRecursivelyCall)); 1762 global->Set(v8_str("callFunctionRecursively"), 1763 v8::FunctionTemplate::New(CallFunctionRecursivelyCall)); 1764 LocalContext env(NULL, global); 1765 1766 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1767 call_recursively_script = v8_compile("callScriptRecursively()"); 1768 v8::Handle<Value> result(call_recursively_script->Run()); 1769 call_recursively_script = v8::Handle<Script>(); 1770 1771 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1772 Script::Compile(v8_str("callFunctionRecursively()"))->Run(); 1773} 1774 1775 1776static v8::Handle<Value> 1777 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) { 1778 ApiTestFuzzer::Fuzz(); 1779 return v8::ThrowException(key); 1780} 1781 1782 1783static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key, 1784 Local<Value>, 1785 const AccessorInfo&) { 1786 v8::ThrowException(key); 1787 return v8::Undefined(); // not the same as v8::Handle<v8::Value>() 1788} 1789 1790 1791THREADED_TEST(CallbackExceptionRegression) { 1792 v8::HandleScope scope; 1793 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 1794 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet, 1795 ThrowingPropertyHandlerSet); 1796 LocalContext env; 1797 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 1798 v8::Handle<Value> otto = Script::Compile(v8_str( 1799 "try { with (obj) { otto; } } catch (e) { e; }"))->Run(); 1800 CHECK_EQ(v8_str("otto"), otto); 1801 v8::Handle<Value> netto = Script::Compile(v8_str( 1802 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run(); 1803 CHECK_EQ(v8_str("netto"), netto); 1804} 1805 1806 1807THREADED_TEST(FunctionPrototype) { 1808 v8::HandleScope scope; 1809 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New(); 1810 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321)); 1811 LocalContext env; 1812 env->Global()->Set(v8_str("Foo"), Foo->GetFunction()); 1813 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak")); 1814 CHECK_EQ(script->Run()->Int32Value(), 321); 1815} 1816 1817 1818THREADED_TEST(InternalFields) { 1819 v8::HandleScope scope; 1820 LocalContext env; 1821 1822 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1823 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1824 instance_templ->SetInternalFieldCount(1); 1825 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1826 CHECK_EQ(1, obj->InternalFieldCount()); 1827 CHECK(obj->GetInternalField(0)->IsUndefined()); 1828 obj->SetInternalField(0, v8_num(17)); 1829 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value()); 1830} 1831 1832 1833THREADED_TEST(GlobalObjectInternalFields) { 1834 v8::HandleScope scope; 1835 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 1836 global_template->SetInternalFieldCount(1); 1837 LocalContext env(NULL, global_template); 1838 v8::Handle<v8::Object> global_proxy = env->Global(); 1839 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); 1840 CHECK_EQ(1, global->InternalFieldCount()); 1841 CHECK(global->GetInternalField(0)->IsUndefined()); 1842 global->SetInternalField(0, v8_num(17)); 1843 CHECK_EQ(17, global->GetInternalField(0)->Int32Value()); 1844} 1845 1846 1847THREADED_TEST(InternalFieldsNativePointers) { 1848 v8::HandleScope scope; 1849 LocalContext env; 1850 1851 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1852 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1853 instance_templ->SetInternalFieldCount(1); 1854 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1855 CHECK_EQ(1, obj->InternalFieldCount()); 1856 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1857 1858 char* data = new char[100]; 1859 1860 void* aligned = data; 1861 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1)); 1862 void* unaligned = data + 1; 1863 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1)); 1864 1865 // Check reading and writing aligned pointers. 1866 obj->SetPointerInInternalField(0, aligned); 1867 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 1868 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1869 1870 // Check reading and writing unaligned pointers. 1871 obj->SetPointerInInternalField(0, unaligned); 1872 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 1873 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1874 1875 delete[] data; 1876} 1877 1878 1879THREADED_TEST(InternalFieldsNativePointersAndExternal) { 1880 v8::HandleScope scope; 1881 LocalContext env; 1882 1883 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1884 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1885 instance_templ->SetInternalFieldCount(1); 1886 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1887 CHECK_EQ(1, obj->InternalFieldCount()); 1888 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1889 1890 char* data = new char[100]; 1891 1892 void* aligned = data; 1893 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1)); 1894 void* unaligned = data + 1; 1895 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1)); 1896 1897 obj->SetPointerInInternalField(0, aligned); 1898 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 1899 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0))); 1900 1901 obj->SetPointerInInternalField(0, unaligned); 1902 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 1903 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0))); 1904 1905 obj->SetInternalField(0, v8::External::Wrap(aligned)); 1906 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 1907 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1908 1909 obj->SetInternalField(0, v8::External::Wrap(unaligned)); 1910 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 1911 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1912 1913 delete[] data; 1914} 1915 1916 1917THREADED_TEST(IdentityHash) { 1918 v8::HandleScope scope; 1919 LocalContext env; 1920 1921 // Ensure that the test starts with an fresh heap to test whether the hash 1922 // code is based on the address. 1923 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 1924 Local<v8::Object> obj = v8::Object::New(); 1925 int hash = obj->GetIdentityHash(); 1926 int hash1 = obj->GetIdentityHash(); 1927 CHECK_EQ(hash, hash1); 1928 int hash2 = v8::Object::New()->GetIdentityHash(); 1929 // Since the identity hash is essentially a random number two consecutive 1930 // objects should not be assigned the same hash code. If the test below fails 1931 // the random number generator should be evaluated. 1932 CHECK_NE(hash, hash2); 1933 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 1934 int hash3 = v8::Object::New()->GetIdentityHash(); 1935 // Make sure that the identity hash is not based on the initial address of 1936 // the object alone. If the test below fails the random number generator 1937 // should be evaluated. 1938 CHECK_NE(hash, hash3); 1939 int hash4 = obj->GetIdentityHash(); 1940 CHECK_EQ(hash, hash4); 1941 1942 // Check identity hashes behaviour in the presence of JS accessors. 1943 // Put a getter for 'v8::IdentityHash' on the Object's prototype: 1944 { 1945 CompileRun("Object.prototype['v8::IdentityHash'] = 42;\n"); 1946 Local<v8::Object> o1 = v8::Object::New(); 1947 Local<v8::Object> o2 = v8::Object::New(); 1948 CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash()); 1949 } 1950 { 1951 CompileRun( 1952 "function cnst() { return 42; };\n" 1953 "Object.prototype.__defineGetter__('v8::IdentityHash', cnst);\n"); 1954 Local<v8::Object> o1 = v8::Object::New(); 1955 Local<v8::Object> o2 = v8::Object::New(); 1956 CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash()); 1957 } 1958} 1959 1960 1961THREADED_TEST(HiddenProperties) { 1962 v8::HandleScope scope; 1963 LocalContext env; 1964 1965 v8::Local<v8::Object> obj = v8::Object::New(); 1966 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 1967 v8::Local<v8::String> empty = v8_str(""); 1968 v8::Local<v8::String> prop_name = v8_str("prop_name"); 1969 1970 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 1971 1972 // Make sure delete of a non-existent hidden value works 1973 CHECK(obj->DeleteHiddenValue(key)); 1974 1975 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503))); 1976 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value()); 1977 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002))); 1978 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1979 1980 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 1981 1982 // Make sure we do not find the hidden property. 1983 CHECK(!obj->Has(empty)); 1984 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1985 CHECK(obj->Get(empty)->IsUndefined()); 1986 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1987 CHECK(obj->Set(empty, v8::Integer::New(2003))); 1988 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1989 CHECK_EQ(2003, obj->Get(empty)->Int32Value()); 1990 1991 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 1992 1993 // Add another property and delete it afterwards to force the object in 1994 // slow case. 1995 CHECK(obj->Set(prop_name, v8::Integer::New(2008))); 1996 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1997 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value()); 1998 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1999 CHECK(obj->Delete(prop_name)); 2000 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 2001 2002 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 2003 2004 CHECK(obj->DeleteHiddenValue(key)); 2005 CHECK(obj->GetHiddenValue(key).IsEmpty()); 2006} 2007 2008 2009THREADED_TEST(Regress97784) { 2010 // Regression test for crbug.com/97784 2011 // Messing with the Object.prototype should not have effect on 2012 // hidden properties. 2013 v8::HandleScope scope; 2014 LocalContext env; 2015 2016 v8::Local<v8::Object> obj = v8::Object::New(); 2017 v8::Local<v8::String> key = v8_str("hidden"); 2018 2019 CompileRun( 2020 "set_called = false;" 2021 "Object.defineProperty(" 2022 " Object.prototype," 2023 " 'hidden'," 2024 " {get: function() { return 45; }," 2025 " set: function() { set_called = true; }})"); 2026 2027 CHECK(obj->GetHiddenValue(key).IsEmpty()); 2028 // Make sure that the getter and setter from Object.prototype is not invoked. 2029 // If it did we would have full access to the hidden properties in 2030 // the accessor. 2031 CHECK(obj->SetHiddenValue(key, v8::Integer::New(42))); 2032 ExpectFalse("set_called"); 2033 CHECK_EQ(42, obj->GetHiddenValue(key)->Int32Value()); 2034} 2035 2036 2037static bool interceptor_for_hidden_properties_called; 2038static v8::Handle<Value> InterceptorForHiddenProperties( 2039 Local<String> name, const AccessorInfo& info) { 2040 interceptor_for_hidden_properties_called = true; 2041 return v8::Handle<Value>(); 2042} 2043 2044 2045THREADED_TEST(HiddenPropertiesWithInterceptors) { 2046 v8::HandleScope scope; 2047 LocalContext context; 2048 2049 interceptor_for_hidden_properties_called = false; 2050 2051 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 2052 2053 // Associate an interceptor with an object and start setting hidden values. 2054 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 2055 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 2056 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties); 2057 Local<v8::Function> function = fun_templ->GetFunction(); 2058 Local<v8::Object> obj = function->NewInstance(); 2059 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302))); 2060 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value()); 2061 CHECK(!interceptor_for_hidden_properties_called); 2062} 2063 2064 2065THREADED_TEST(External) { 2066 v8::HandleScope scope; 2067 int x = 3; 2068 Local<v8::External> ext = v8::External::New(&x); 2069 LocalContext env; 2070 env->Global()->Set(v8_str("ext"), ext); 2071 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run(); 2072 v8::Handle<v8::External> reext = reext_obj.As<v8::External>(); 2073 int* ptr = static_cast<int*>(reext->Value()); 2074 CHECK_EQ(x, 3); 2075 *ptr = 10; 2076 CHECK_EQ(x, 10); 2077 2078 // Make sure unaligned pointers are wrapped properly. 2079 char* data = i::StrDup("0123456789"); 2080 Local<v8::Value> zero = v8::External::Wrap(&data[0]); 2081 Local<v8::Value> one = v8::External::Wrap(&data[1]); 2082 Local<v8::Value> two = v8::External::Wrap(&data[2]); 2083 Local<v8::Value> three = v8::External::Wrap(&data[3]); 2084 2085 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero)); 2086 CHECK_EQ('0', *char_ptr); 2087 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one)); 2088 CHECK_EQ('1', *char_ptr); 2089 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two)); 2090 CHECK_EQ('2', *char_ptr); 2091 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three)); 2092 CHECK_EQ('3', *char_ptr); 2093 i::DeleteArray(data); 2094} 2095 2096 2097THREADED_TEST(GlobalHandle) { 2098 v8::Persistent<String> global; 2099 { 2100 v8::HandleScope scope; 2101 Local<String> str = v8_str("str"); 2102 global = v8::Persistent<String>::New(str); 2103 } 2104 CHECK_EQ(global->Length(), 3); 2105 global.Dispose(); 2106} 2107 2108 2109class WeakCallCounter { 2110 public: 2111 explicit WeakCallCounter(int id) : id_(id), number_of_weak_calls_(0) { } 2112 int id() { return id_; } 2113 void increment() { number_of_weak_calls_++; } 2114 int NumberOfWeakCalls() { return number_of_weak_calls_; } 2115 private: 2116 int id_; 2117 int number_of_weak_calls_; 2118}; 2119 2120 2121static void WeakPointerCallback(Persistent<Value> handle, void* id) { 2122 WeakCallCounter* counter = reinterpret_cast<WeakCallCounter*>(id); 2123 CHECK_EQ(1234, counter->id()); 2124 counter->increment(); 2125 handle.Dispose(); 2126} 2127 2128 2129THREADED_TEST(ApiObjectGroups) { 2130 HandleScope scope; 2131 LocalContext env; 2132 2133 Persistent<Object> g1s1; 2134 Persistent<Object> g1s2; 2135 Persistent<Object> g1c1; 2136 Persistent<Object> g2s1; 2137 Persistent<Object> g2s2; 2138 Persistent<Object> g2c1; 2139 2140 WeakCallCounter counter(1234); 2141 2142 { 2143 HandleScope scope; 2144 g1s1 = Persistent<Object>::New(Object::New()); 2145 g1s2 = Persistent<Object>::New(Object::New()); 2146 g1c1 = Persistent<Object>::New(Object::New()); 2147 g1s1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2148 g1s2.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2149 g1c1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2150 2151 g2s1 = Persistent<Object>::New(Object::New()); 2152 g2s2 = Persistent<Object>::New(Object::New()); 2153 g2c1 = Persistent<Object>::New(Object::New()); 2154 g2s1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2155 g2s2.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2156 g2c1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2157 } 2158 2159 Persistent<Object> root = Persistent<Object>::New(g1s1); // make a root. 2160 2161 // Connect group 1 and 2, make a cycle. 2162 CHECK(g1s2->Set(0, g2s2)); 2163 CHECK(g2s1->Set(0, g1s1)); 2164 2165 { 2166 Persistent<Value> g1_objects[] = { g1s1, g1s2 }; 2167 Persistent<Value> g1_children[] = { g1c1 }; 2168 Persistent<Value> g2_objects[] = { g2s1, g2s2 }; 2169 Persistent<Value> g2_children[] = { g2c1 }; 2170 V8::AddObjectGroup(g1_objects, 2); 2171 V8::AddImplicitReferences(g1s1, g1_children, 1); 2172 V8::AddObjectGroup(g2_objects, 2); 2173 V8::AddImplicitReferences(g2s2, g2_children, 1); 2174 } 2175 // Do a single full GC. Use kMakeHeapIterableMask to ensure that 2176 // incremental garbage collection is stopped. 2177 HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); 2178 2179 // All object should be alive. 2180 CHECK_EQ(0, counter.NumberOfWeakCalls()); 2181 2182 // Weaken the root. 2183 root.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2184 // But make children strong roots---all the objects (except for children) 2185 // should be collectable now. 2186 g1c1.ClearWeak(); 2187 g2c1.ClearWeak(); 2188 2189 // Groups are deleted, rebuild groups. 2190 { 2191 Persistent<Value> g1_objects[] = { g1s1, g1s2 }; 2192 Persistent<Value> g1_children[] = { g1c1 }; 2193 Persistent<Value> g2_objects[] = { g2s1, g2s2 }; 2194 Persistent<Value> g2_children[] = { g2c1 }; 2195 V8::AddObjectGroup(g1_objects, 2); 2196 V8::AddImplicitReferences(g1s1, g1_children, 1); 2197 V8::AddObjectGroup(g2_objects, 2); 2198 V8::AddImplicitReferences(g2s2, g2_children, 1); 2199 } 2200 2201 HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); 2202 2203 // All objects should be gone. 5 global handles in total. 2204 CHECK_EQ(5, counter.NumberOfWeakCalls()); 2205 2206 // And now make children weak again and collect them. 2207 g1c1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2208 g2c1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2209 2210 HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); 2211 CHECK_EQ(7, counter.NumberOfWeakCalls()); 2212} 2213 2214 2215THREADED_TEST(ApiObjectGroupsCycle) { 2216 HandleScope scope; 2217 LocalContext env; 2218 2219 WeakCallCounter counter(1234); 2220 2221 Persistent<Object> g1s1; 2222 Persistent<Object> g1s2; 2223 Persistent<Object> g2s1; 2224 Persistent<Object> g2s2; 2225 Persistent<Object> g3s1; 2226 Persistent<Object> g3s2; 2227 2228 { 2229 HandleScope scope; 2230 g1s1 = Persistent<Object>::New(Object::New()); 2231 g1s2 = Persistent<Object>::New(Object::New()); 2232 g1s1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2233 g1s2.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2234 2235 g2s1 = Persistent<Object>::New(Object::New()); 2236 g2s2 = Persistent<Object>::New(Object::New()); 2237 g2s1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2238 g2s2.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2239 2240 g3s1 = Persistent<Object>::New(Object::New()); 2241 g3s2 = Persistent<Object>::New(Object::New()); 2242 g3s1.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2243 g3s2.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2244 } 2245 2246 Persistent<Object> root = Persistent<Object>::New(g1s1); // make a root. 2247 2248 // Connect groups. We're building the following cycle: 2249 // G1: { g1s1, g2s1 }, g1s1 implicitly references g2s1, ditto for other 2250 // groups. 2251 { 2252 Persistent<Value> g1_objects[] = { g1s1, g1s2 }; 2253 Persistent<Value> g1_children[] = { g2s1 }; 2254 Persistent<Value> g2_objects[] = { g2s1, g2s2 }; 2255 Persistent<Value> g2_children[] = { g3s1 }; 2256 Persistent<Value> g3_objects[] = { g3s1, g3s2 }; 2257 Persistent<Value> g3_children[] = { g1s1 }; 2258 V8::AddObjectGroup(g1_objects, 2); 2259 V8::AddImplicitReferences(g1s1, g1_children, 1); 2260 V8::AddObjectGroup(g2_objects, 2); 2261 V8::AddImplicitReferences(g2s1, g2_children, 1); 2262 V8::AddObjectGroup(g3_objects, 2); 2263 V8::AddImplicitReferences(g3s1, g3_children, 1); 2264 } 2265 // Do a single full GC 2266 HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); 2267 2268 // All object should be alive. 2269 CHECK_EQ(0, counter.NumberOfWeakCalls()); 2270 2271 // Weaken the root. 2272 root.MakeWeak(reinterpret_cast<void*>(&counter), &WeakPointerCallback); 2273 2274 // Groups are deleted, rebuild groups. 2275 { 2276 Persistent<Value> g1_objects[] = { g1s1, g1s2 }; 2277 Persistent<Value> g1_children[] = { g2s1 }; 2278 Persistent<Value> g2_objects[] = { g2s1, g2s2 }; 2279 Persistent<Value> g2_children[] = { g3s1 }; 2280 Persistent<Value> g3_objects[] = { g3s1, g3s2 }; 2281 Persistent<Value> g3_children[] = { g1s1 }; 2282 V8::AddObjectGroup(g1_objects, 2); 2283 V8::AddImplicitReferences(g1s1, g1_children, 1); 2284 V8::AddObjectGroup(g2_objects, 2); 2285 V8::AddImplicitReferences(g2s1, g2_children, 1); 2286 V8::AddObjectGroup(g3_objects, 2); 2287 V8::AddImplicitReferences(g3s1, g3_children, 1); 2288 } 2289 2290 HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); 2291 2292 // All objects should be gone. 7 global handles in total. 2293 CHECK_EQ(7, counter.NumberOfWeakCalls()); 2294} 2295 2296 2297THREADED_TEST(ScriptException) { 2298 v8::HandleScope scope; 2299 LocalContext env; 2300 Local<Script> script = Script::Compile(v8_str("throw 'panama!';")); 2301 v8::TryCatch try_catch; 2302 Local<Value> result = script->Run(); 2303 CHECK(result.IsEmpty()); 2304 CHECK(try_catch.HasCaught()); 2305 String::AsciiValue exception_value(try_catch.Exception()); 2306 CHECK_EQ(*exception_value, "panama!"); 2307} 2308 2309 2310bool message_received; 2311 2312 2313static void check_message(v8::Handle<v8::Message> message, 2314 v8::Handle<Value> data) { 2315 CHECK_EQ(5.76, data->NumberValue()); 2316 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue()); 2317 CHECK_EQ(7.56, message->GetScriptData()->NumberValue()); 2318 message_received = true; 2319} 2320 2321 2322THREADED_TEST(MessageHandlerData) { 2323 message_received = false; 2324 v8::HandleScope scope; 2325 CHECK(!message_received); 2326 v8::V8::AddMessageListener(check_message, v8_num(5.76)); 2327 LocalContext context; 2328 v8::ScriptOrigin origin = 2329 v8::ScriptOrigin(v8_str("6.75")); 2330 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"), 2331 &origin); 2332 script->SetData(v8_str("7.56")); 2333 script->Run(); 2334 CHECK(message_received); 2335 // clear out the message listener 2336 v8::V8::RemoveMessageListeners(check_message); 2337} 2338 2339 2340THREADED_TEST(GetSetProperty) { 2341 v8::HandleScope scope; 2342 LocalContext context; 2343 context->Global()->Set(v8_str("foo"), v8_num(14)); 2344 context->Global()->Set(v8_str("12"), v8_num(92)); 2345 context->Global()->Set(v8::Integer::New(16), v8_num(32)); 2346 context->Global()->Set(v8_num(13), v8_num(56)); 2347 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run(); 2348 CHECK_EQ(14, foo->Int32Value()); 2349 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run(); 2350 CHECK_EQ(92, twelve->Int32Value()); 2351 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run(); 2352 CHECK_EQ(32, sixteen->Int32Value()); 2353 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run(); 2354 CHECK_EQ(56, thirteen->Int32Value()); 2355 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value()); 2356 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value()); 2357 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value()); 2358 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value()); 2359 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value()); 2360 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value()); 2361 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value()); 2362 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value()); 2363 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value()); 2364} 2365 2366 2367THREADED_TEST(PropertyAttributes) { 2368 v8::HandleScope scope; 2369 LocalContext context; 2370 // none 2371 Local<String> prop = v8_str("none"); 2372 context->Global()->Set(prop, v8_num(7)); 2373 CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(prop)); 2374 // read-only 2375 prop = v8_str("read_only"); 2376 context->Global()->Set(prop, v8_num(7), v8::ReadOnly); 2377 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 2378 CHECK_EQ(v8::ReadOnly, context->Global()->GetPropertyAttributes(prop)); 2379 Script::Compile(v8_str("read_only = 9"))->Run(); 2380 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 2381 context->Global()->Set(prop, v8_num(10)); 2382 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 2383 // dont-delete 2384 prop = v8_str("dont_delete"); 2385 context->Global()->Set(prop, v8_num(13), v8::DontDelete); 2386 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 2387 Script::Compile(v8_str("delete dont_delete"))->Run(); 2388 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 2389 CHECK_EQ(v8::DontDelete, context->Global()->GetPropertyAttributes(prop)); 2390 // dont-enum 2391 prop = v8_str("dont_enum"); 2392 context->Global()->Set(prop, v8_num(28), v8::DontEnum); 2393 CHECK_EQ(v8::DontEnum, context->Global()->GetPropertyAttributes(prop)); 2394 // absent 2395 prop = v8_str("absent"); 2396 CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(prop)); 2397 Local<Value> fake_prop = v8_num(1); 2398 CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(fake_prop)); 2399 // exception 2400 TryCatch try_catch; 2401 Local<Value> exception = 2402 CompileRun("({ toString: function() { throw 'exception';} })"); 2403 CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(exception)); 2404 CHECK(try_catch.HasCaught()); 2405 String::AsciiValue exception_value(try_catch.Exception()); 2406 CHECK_EQ("exception", *exception_value); 2407 try_catch.Reset(); 2408} 2409 2410 2411THREADED_TEST(Array) { 2412 v8::HandleScope scope; 2413 LocalContext context; 2414 Local<v8::Array> array = v8::Array::New(); 2415 CHECK_EQ(0, array->Length()); 2416 CHECK(array->Get(0)->IsUndefined()); 2417 CHECK(!array->Has(0)); 2418 CHECK(array->Get(100)->IsUndefined()); 2419 CHECK(!array->Has(100)); 2420 array->Set(2, v8_num(7)); 2421 CHECK_EQ(3, array->Length()); 2422 CHECK(!array->Has(0)); 2423 CHECK(!array->Has(1)); 2424 CHECK(array->Has(2)); 2425 CHECK_EQ(7, array->Get(2)->Int32Value()); 2426 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run(); 2427 Local<v8::Array> arr = obj.As<v8::Array>(); 2428 CHECK_EQ(3, arr->Length()); 2429 CHECK_EQ(1, arr->Get(0)->Int32Value()); 2430 CHECK_EQ(2, arr->Get(1)->Int32Value()); 2431 CHECK_EQ(3, arr->Get(2)->Int32Value()); 2432 array = v8::Array::New(27); 2433 CHECK_EQ(27, array->Length()); 2434 array = v8::Array::New(-27); 2435 CHECK_EQ(0, array->Length()); 2436} 2437 2438 2439v8::Handle<Value> HandleF(const v8::Arguments& args) { 2440 v8::HandleScope scope; 2441 ApiTestFuzzer::Fuzz(); 2442 Local<v8::Array> result = v8::Array::New(args.Length()); 2443 for (int i = 0; i < args.Length(); i++) 2444 result->Set(i, args[i]); 2445 return scope.Close(result); 2446} 2447 2448 2449THREADED_TEST(Vector) { 2450 v8::HandleScope scope; 2451 Local<ObjectTemplate> global = ObjectTemplate::New(); 2452 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF)); 2453 LocalContext context(0, global); 2454 2455 const char* fun = "f()"; 2456 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>(); 2457 CHECK_EQ(0, a0->Length()); 2458 2459 const char* fun2 = "f(11)"; 2460 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>(); 2461 CHECK_EQ(1, a1->Length()); 2462 CHECK_EQ(11, a1->Get(0)->Int32Value()); 2463 2464 const char* fun3 = "f(12, 13)"; 2465 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>(); 2466 CHECK_EQ(2, a2->Length()); 2467 CHECK_EQ(12, a2->Get(0)->Int32Value()); 2468 CHECK_EQ(13, a2->Get(1)->Int32Value()); 2469 2470 const char* fun4 = "f(14, 15, 16)"; 2471 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>(); 2472 CHECK_EQ(3, a3->Length()); 2473 CHECK_EQ(14, a3->Get(0)->Int32Value()); 2474 CHECK_EQ(15, a3->Get(1)->Int32Value()); 2475 CHECK_EQ(16, a3->Get(2)->Int32Value()); 2476 2477 const char* fun5 = "f(17, 18, 19, 20)"; 2478 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>(); 2479 CHECK_EQ(4, a4->Length()); 2480 CHECK_EQ(17, a4->Get(0)->Int32Value()); 2481 CHECK_EQ(18, a4->Get(1)->Int32Value()); 2482 CHECK_EQ(19, a4->Get(2)->Int32Value()); 2483 CHECK_EQ(20, a4->Get(3)->Int32Value()); 2484} 2485 2486 2487THREADED_TEST(FunctionCall) { 2488 v8::HandleScope scope; 2489 LocalContext context; 2490 CompileRun( 2491 "function Foo() {" 2492 " var result = [];" 2493 " for (var i = 0; i < arguments.length; i++) {" 2494 " result.push(arguments[i]);" 2495 " }" 2496 " return result;" 2497 "}"); 2498 Local<Function> Foo = 2499 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 2500 2501 v8::Handle<Value>* args0 = NULL; 2502 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0)); 2503 CHECK_EQ(0, a0->Length()); 2504 2505 v8::Handle<Value> args1[] = { v8_num(1.1) }; 2506 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1)); 2507 CHECK_EQ(1, a1->Length()); 2508 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 2509 2510 v8::Handle<Value> args2[] = { v8_num(2.2), 2511 v8_num(3.3) }; 2512 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2)); 2513 CHECK_EQ(2, a2->Length()); 2514 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 2515 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 2516 2517 v8::Handle<Value> args3[] = { v8_num(4.4), 2518 v8_num(5.5), 2519 v8_num(6.6) }; 2520 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3)); 2521 CHECK_EQ(3, a3->Length()); 2522 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 2523 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 2524 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 2525 2526 v8::Handle<Value> args4[] = { v8_num(7.7), 2527 v8_num(8.8), 2528 v8_num(9.9), 2529 v8_num(10.11) }; 2530 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4)); 2531 CHECK_EQ(4, a4->Length()); 2532 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 2533 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 2534 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 2535 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 2536} 2537 2538 2539static const char* js_code_causing_out_of_memory = 2540 "var a = new Array(); while(true) a.push(a);"; 2541 2542 2543// These tests run for a long time and prevent us from running tests 2544// that come after them so they cannot run in parallel. 2545TEST(OutOfMemory) { 2546 // It's not possible to read a snapshot into a heap with different dimensions. 2547 if (i::Snapshot::IsEnabled()) return; 2548 // Set heap limits. 2549 static const int K = 1024; 2550 v8::ResourceConstraints constraints; 2551 constraints.set_max_young_space_size(256 * K); 2552 constraints.set_max_old_space_size(4 * K * K); 2553 v8::SetResourceConstraints(&constraints); 2554 2555 // Execute a script that causes out of memory. 2556 v8::HandleScope scope; 2557 LocalContext context; 2558 v8::V8::IgnoreOutOfMemoryException(); 2559 Local<Script> script = 2560 Script::Compile(String::New(js_code_causing_out_of_memory)); 2561 Local<Value> result = script->Run(); 2562 2563 // Check for out of memory state. 2564 CHECK(result.IsEmpty()); 2565 CHECK(context->HasOutOfMemoryException()); 2566} 2567 2568 2569v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) { 2570 ApiTestFuzzer::Fuzz(); 2571 2572 v8::HandleScope scope; 2573 LocalContext context; 2574 Local<Script> script = 2575 Script::Compile(String::New(js_code_causing_out_of_memory)); 2576 Local<Value> result = script->Run(); 2577 2578 // Check for out of memory state. 2579 CHECK(result.IsEmpty()); 2580 CHECK(context->HasOutOfMemoryException()); 2581 2582 return result; 2583} 2584 2585 2586TEST(OutOfMemoryNested) { 2587 // It's not possible to read a snapshot into a heap with different dimensions. 2588 if (i::Snapshot::IsEnabled()) return; 2589 // Set heap limits. 2590 static const int K = 1024; 2591 v8::ResourceConstraints constraints; 2592 constraints.set_max_young_space_size(256 * K); 2593 constraints.set_max_old_space_size(4 * K * K); 2594 v8::SetResourceConstraints(&constraints); 2595 2596 v8::HandleScope scope; 2597 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2598 templ->Set(v8_str("ProvokeOutOfMemory"), 2599 v8::FunctionTemplate::New(ProvokeOutOfMemory)); 2600 LocalContext context(0, templ); 2601 v8::V8::IgnoreOutOfMemoryException(); 2602 Local<Value> result = CompileRun( 2603 "var thrown = false;" 2604 "try {" 2605 " ProvokeOutOfMemory();" 2606 "} catch (e) {" 2607 " thrown = true;" 2608 "}"); 2609 // Check for out of memory state. 2610 CHECK(result.IsEmpty()); 2611 CHECK(context->HasOutOfMemoryException()); 2612} 2613 2614 2615TEST(HugeConsStringOutOfMemory) { 2616 // It's not possible to read a snapshot into a heap with different dimensions. 2617 if (i::Snapshot::IsEnabled()) return; 2618 // Set heap limits. 2619 static const int K = 1024; 2620 v8::ResourceConstraints constraints; 2621 constraints.set_max_young_space_size(256 * K); 2622 constraints.set_max_old_space_size(2 * K * K); 2623 v8::SetResourceConstraints(&constraints); 2624 2625 // Execute a script that causes out of memory. 2626 v8::V8::IgnoreOutOfMemoryException(); 2627 2628 v8::HandleScope scope; 2629 LocalContext context; 2630 2631 // Build huge string. This should fail with out of memory exception. 2632 Local<Value> result = CompileRun( 2633 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();" 2634 "for (var i = 0; i < 22; i++) { str = str + str; }"); 2635 2636 // Check for out of memory state. 2637 CHECK(result.IsEmpty()); 2638 CHECK(context->HasOutOfMemoryException()); 2639} 2640 2641 2642THREADED_TEST(ConstructCall) { 2643 v8::HandleScope scope; 2644 LocalContext context; 2645 CompileRun( 2646 "function Foo() {" 2647 " var result = [];" 2648 " for (var i = 0; i < arguments.length; i++) {" 2649 " result.push(arguments[i]);" 2650 " }" 2651 " return result;" 2652 "}"); 2653 Local<Function> Foo = 2654 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 2655 2656 v8::Handle<Value>* args0 = NULL; 2657 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0)); 2658 CHECK_EQ(0, a0->Length()); 2659 2660 v8::Handle<Value> args1[] = { v8_num(1.1) }; 2661 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1)); 2662 CHECK_EQ(1, a1->Length()); 2663 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 2664 2665 v8::Handle<Value> args2[] = { v8_num(2.2), 2666 v8_num(3.3) }; 2667 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2)); 2668 CHECK_EQ(2, a2->Length()); 2669 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 2670 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 2671 2672 v8::Handle<Value> args3[] = { v8_num(4.4), 2673 v8_num(5.5), 2674 v8_num(6.6) }; 2675 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3)); 2676 CHECK_EQ(3, a3->Length()); 2677 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 2678 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 2679 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 2680 2681 v8::Handle<Value> args4[] = { v8_num(7.7), 2682 v8_num(8.8), 2683 v8_num(9.9), 2684 v8_num(10.11) }; 2685 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4)); 2686 CHECK_EQ(4, a4->Length()); 2687 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 2688 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 2689 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 2690 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 2691} 2692 2693 2694static void CheckUncle(v8::TryCatch* try_catch) { 2695 CHECK(try_catch->HasCaught()); 2696 String::AsciiValue str_value(try_catch->Exception()); 2697 CHECK_EQ(*str_value, "uncle?"); 2698 try_catch->Reset(); 2699} 2700 2701 2702THREADED_TEST(ConversionNumber) { 2703 v8::HandleScope scope; 2704 LocalContext env; 2705 // Very large number. 2706 CompileRun("var obj = Math.pow(2,32) * 1237;"); 2707 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2708 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value()); 2709 CHECK_EQ(0, obj->ToInt32()->Value()); 2710 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned. 2711 // Large number. 2712 CompileRun("var obj = -1234567890123;"); 2713 obj = env->Global()->Get(v8_str("obj")); 2714 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value()); 2715 CHECK_EQ(-1912276171, obj->ToInt32()->Value()); 2716 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT 2717 // Small positive integer. 2718 CompileRun("var obj = 42;"); 2719 obj = env->Global()->Get(v8_str("obj")); 2720 CHECK_EQ(42.0, obj->ToNumber()->Value()); 2721 CHECK_EQ(42, obj->ToInt32()->Value()); 2722 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 2723 // Negative integer. 2724 CompileRun("var obj = -37;"); 2725 obj = env->Global()->Get(v8_str("obj")); 2726 CHECK_EQ(-37.0, obj->ToNumber()->Value()); 2727 CHECK_EQ(-37, obj->ToInt32()->Value()); 2728 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT 2729 // Positive non-int32 integer. 2730 CompileRun("var obj = 0x81234567;"); 2731 obj = env->Global()->Get(v8_str("obj")); 2732 CHECK_EQ(2166572391.0, obj->ToNumber()->Value()); 2733 CHECK_EQ(-2128394905, obj->ToInt32()->Value()); 2734 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT 2735 // Fraction. 2736 CompileRun("var obj = 42.3;"); 2737 obj = env->Global()->Get(v8_str("obj")); 2738 CHECK_EQ(42.3, obj->ToNumber()->Value()); 2739 CHECK_EQ(42, obj->ToInt32()->Value()); 2740 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 2741 // Large negative fraction. 2742 CompileRun("var obj = -5726623061.75;"); 2743 obj = env->Global()->Get(v8_str("obj")); 2744 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value()); 2745 CHECK_EQ(-1431655765, obj->ToInt32()->Value()); 2746 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT 2747} 2748 2749 2750THREADED_TEST(isNumberType) { 2751 v8::HandleScope scope; 2752 LocalContext env; 2753 // Very large number. 2754 CompileRun("var obj = Math.pow(2,32) * 1237;"); 2755 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2756 CHECK(!obj->IsInt32()); 2757 CHECK(!obj->IsUint32()); 2758 // Large negative number. 2759 CompileRun("var obj = -1234567890123;"); 2760 obj = env->Global()->Get(v8_str("obj")); 2761 CHECK(!obj->IsInt32()); 2762 CHECK(!obj->IsUint32()); 2763 // Small positive integer. 2764 CompileRun("var obj = 42;"); 2765 obj = env->Global()->Get(v8_str("obj")); 2766 CHECK(obj->IsInt32()); 2767 CHECK(obj->IsUint32()); 2768 // Negative integer. 2769 CompileRun("var obj = -37;"); 2770 obj = env->Global()->Get(v8_str("obj")); 2771 CHECK(obj->IsInt32()); 2772 CHECK(!obj->IsUint32()); 2773 // Positive non-int32 integer. 2774 CompileRun("var obj = 0x81234567;"); 2775 obj = env->Global()->Get(v8_str("obj")); 2776 CHECK(!obj->IsInt32()); 2777 CHECK(obj->IsUint32()); 2778 // Fraction. 2779 CompileRun("var obj = 42.3;"); 2780 obj = env->Global()->Get(v8_str("obj")); 2781 CHECK(!obj->IsInt32()); 2782 CHECK(!obj->IsUint32()); 2783 // Large negative fraction. 2784 CompileRun("var obj = -5726623061.75;"); 2785 obj = env->Global()->Get(v8_str("obj")); 2786 CHECK(!obj->IsInt32()); 2787 CHECK(!obj->IsUint32()); 2788} 2789 2790 2791THREADED_TEST(ConversionException) { 2792 v8::HandleScope scope; 2793 LocalContext env; 2794 CompileRun( 2795 "function TestClass() { };" 2796 "TestClass.prototype.toString = function () { throw 'uncle?'; };" 2797 "var obj = new TestClass();"); 2798 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2799 2800 v8::TryCatch try_catch; 2801 2802 Local<Value> to_string_result = obj->ToString(); 2803 CHECK(to_string_result.IsEmpty()); 2804 CheckUncle(&try_catch); 2805 2806 Local<Value> to_number_result = obj->ToNumber(); 2807 CHECK(to_number_result.IsEmpty()); 2808 CheckUncle(&try_catch); 2809 2810 Local<Value> to_integer_result = obj->ToInteger(); 2811 CHECK(to_integer_result.IsEmpty()); 2812 CheckUncle(&try_catch); 2813 2814 Local<Value> to_uint32_result = obj->ToUint32(); 2815 CHECK(to_uint32_result.IsEmpty()); 2816 CheckUncle(&try_catch); 2817 2818 Local<Value> to_int32_result = obj->ToInt32(); 2819 CHECK(to_int32_result.IsEmpty()); 2820 CheckUncle(&try_catch); 2821 2822 Local<Value> to_object_result = v8::Undefined()->ToObject(); 2823 CHECK(to_object_result.IsEmpty()); 2824 CHECK(try_catch.HasCaught()); 2825 try_catch.Reset(); 2826 2827 int32_t int32_value = obj->Int32Value(); 2828 CHECK_EQ(0, int32_value); 2829 CheckUncle(&try_catch); 2830 2831 uint32_t uint32_value = obj->Uint32Value(); 2832 CHECK_EQ(0, uint32_value); 2833 CheckUncle(&try_catch); 2834 2835 double number_value = obj->NumberValue(); 2836 CHECK_NE(0, IsNaN(number_value)); 2837 CheckUncle(&try_catch); 2838 2839 int64_t integer_value = obj->IntegerValue(); 2840 CHECK_EQ(0.0, static_cast<double>(integer_value)); 2841 CheckUncle(&try_catch); 2842} 2843 2844 2845v8::Handle<Value> ThrowFromC(const v8::Arguments& args) { 2846 ApiTestFuzzer::Fuzz(); 2847 return v8::ThrowException(v8_str("konto")); 2848} 2849 2850 2851v8::Handle<Value> CCatcher(const v8::Arguments& args) { 2852 if (args.Length() < 1) return v8::False(); 2853 v8::HandleScope scope; 2854 v8::TryCatch try_catch; 2855 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run(); 2856 CHECK(!try_catch.HasCaught() || result.IsEmpty()); 2857 return v8::Boolean::New(try_catch.HasCaught()); 2858} 2859 2860 2861THREADED_TEST(APICatch) { 2862 v8::HandleScope scope; 2863 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2864 templ->Set(v8_str("ThrowFromC"), 2865 v8::FunctionTemplate::New(ThrowFromC)); 2866 LocalContext context(0, templ); 2867 CompileRun( 2868 "var thrown = false;" 2869 "try {" 2870 " ThrowFromC();" 2871 "} catch (e) {" 2872 " thrown = true;" 2873 "}"); 2874 Local<Value> thrown = context->Global()->Get(v8_str("thrown")); 2875 CHECK(thrown->BooleanValue()); 2876} 2877 2878 2879THREADED_TEST(APIThrowTryCatch) { 2880 v8::HandleScope scope; 2881 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2882 templ->Set(v8_str("ThrowFromC"), 2883 v8::FunctionTemplate::New(ThrowFromC)); 2884 LocalContext context(0, templ); 2885 v8::TryCatch try_catch; 2886 CompileRun("ThrowFromC();"); 2887 CHECK(try_catch.HasCaught()); 2888} 2889 2890 2891// Test that a try-finally block doesn't shadow a try-catch block 2892// when setting up an external handler. 2893// 2894// BUG(271): Some of the exception propagation does not work on the 2895// ARM simulator because the simulator separates the C++ stack and the 2896// JS stack. This test therefore fails on the simulator. The test is 2897// not threaded to allow the threading tests to run on the simulator. 2898TEST(TryCatchInTryFinally) { 2899 v8::HandleScope scope; 2900 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2901 templ->Set(v8_str("CCatcher"), 2902 v8::FunctionTemplate::New(CCatcher)); 2903 LocalContext context(0, templ); 2904 Local<Value> result = CompileRun("try {" 2905 " try {" 2906 " CCatcher('throw 7;');" 2907 " } finally {" 2908 " }" 2909 "} catch (e) {" 2910 "}"); 2911 CHECK(result->IsTrue()); 2912} 2913 2914 2915static void check_reference_error_message( 2916 v8::Handle<v8::Message> message, 2917 v8::Handle<v8::Value> data) { 2918 const char* reference_error = "Uncaught ReferenceError: asdf is not defined"; 2919 CHECK(message->Get()->Equals(v8_str(reference_error))); 2920} 2921 2922 2923static v8::Handle<Value> Fail(const v8::Arguments& args) { 2924 ApiTestFuzzer::Fuzz(); 2925 CHECK(false); 2926 return v8::Undefined(); 2927} 2928 2929 2930// Test that overwritten methods are not invoked on uncaught exception 2931// formatting. However, they are invoked when performing normal error 2932// string conversions. 2933TEST(APIThrowMessageOverwrittenToString) { 2934 v8::HandleScope scope; 2935 v8::V8::AddMessageListener(check_reference_error_message); 2936 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2937 templ->Set(v8_str("fail"), v8::FunctionTemplate::New(Fail)); 2938 LocalContext context(NULL, templ); 2939 CompileRun("asdf;"); 2940 CompileRun("var limit = {};" 2941 "limit.valueOf = fail;" 2942 "Error.stackTraceLimit = limit;"); 2943 CompileRun("asdf"); 2944 CompileRun("Array.prototype.pop = fail;"); 2945 CompileRun("Object.prototype.hasOwnProperty = fail;"); 2946 CompileRun("Object.prototype.toString = function f() { return 'Yikes'; }"); 2947 CompileRun("Number.prototype.toString = function f() { return 'Yikes'; }"); 2948 CompileRun("String.prototype.toString = function f() { return 'Yikes'; }"); 2949 CompileRun("ReferenceError.prototype.toString =" 2950 " function() { return 'Whoops' }"); 2951 CompileRun("asdf;"); 2952 CompileRun("ReferenceError.prototype.constructor.name = void 0;"); 2953 CompileRun("asdf;"); 2954 CompileRun("ReferenceError.prototype.constructor = void 0;"); 2955 CompileRun("asdf;"); 2956 CompileRun("ReferenceError.prototype.__proto__ = new Object();"); 2957 CompileRun("asdf;"); 2958 CompileRun("ReferenceError.prototype = new Object();"); 2959 CompileRun("asdf;"); 2960 v8::Handle<Value> string = CompileRun("try { asdf; } catch(e) { e + ''; }"); 2961 CHECK(string->Equals(v8_str("Whoops"))); 2962 CompileRun("ReferenceError.prototype.constructor = new Object();" 2963 "ReferenceError.prototype.constructor.name = 1;" 2964 "Number.prototype.toString = function() { return 'Whoops'; };" 2965 "ReferenceError.prototype.toString = Object.prototype.toString;"); 2966 CompileRun("asdf;"); 2967 v8::V8::RemoveMessageListeners(check_message); 2968} 2969 2970 2971static void receive_message(v8::Handle<v8::Message> message, 2972 v8::Handle<v8::Value> data) { 2973 message->Get(); 2974 message_received = true; 2975} 2976 2977 2978TEST(APIThrowMessage) { 2979 message_received = false; 2980 v8::HandleScope scope; 2981 v8::V8::AddMessageListener(receive_message); 2982 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2983 templ->Set(v8_str("ThrowFromC"), 2984 v8::FunctionTemplate::New(ThrowFromC)); 2985 LocalContext context(0, templ); 2986 CompileRun("ThrowFromC();"); 2987 CHECK(message_received); 2988 v8::V8::RemoveMessageListeners(check_message); 2989} 2990 2991 2992TEST(APIThrowMessageAndVerboseTryCatch) { 2993 message_received = false; 2994 v8::HandleScope scope; 2995 v8::V8::AddMessageListener(receive_message); 2996 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2997 templ->Set(v8_str("ThrowFromC"), 2998 v8::FunctionTemplate::New(ThrowFromC)); 2999 LocalContext context(0, templ); 3000 v8::TryCatch try_catch; 3001 try_catch.SetVerbose(true); 3002 Local<Value> result = CompileRun("ThrowFromC();"); 3003 CHECK(try_catch.HasCaught()); 3004 CHECK(result.IsEmpty()); 3005 CHECK(message_received); 3006 v8::V8::RemoveMessageListeners(check_message); 3007} 3008 3009 3010TEST(APIStackOverflowAndVerboseTryCatch) { 3011 message_received = false; 3012 v8::HandleScope scope; 3013 v8::V8::AddMessageListener(receive_message); 3014 LocalContext context; 3015 v8::TryCatch try_catch; 3016 try_catch.SetVerbose(true); 3017 Local<Value> result = CompileRun("function foo() { foo(); } foo();"); 3018 CHECK(try_catch.HasCaught()); 3019 CHECK(result.IsEmpty()); 3020 CHECK(message_received); 3021 v8::V8::RemoveMessageListeners(receive_message); 3022} 3023 3024 3025THREADED_TEST(ExternalScriptException) { 3026 v8::HandleScope scope; 3027 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3028 templ->Set(v8_str("ThrowFromC"), 3029 v8::FunctionTemplate::New(ThrowFromC)); 3030 LocalContext context(0, templ); 3031 3032 v8::TryCatch try_catch; 3033 Local<Script> script 3034 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';")); 3035 Local<Value> result = script->Run(); 3036 CHECK(result.IsEmpty()); 3037 CHECK(try_catch.HasCaught()); 3038 String::AsciiValue exception_value(try_catch.Exception()); 3039 CHECK_EQ("konto", *exception_value); 3040} 3041 3042 3043 3044v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) { 3045 ApiTestFuzzer::Fuzz(); 3046 CHECK_EQ(4, args.Length()); 3047 int count = args[0]->Int32Value(); 3048 int cInterval = args[2]->Int32Value(); 3049 if (count == 0) { 3050 return v8::ThrowException(v8_str("FromC")); 3051 } else { 3052 Local<v8::Object> global = Context::GetCurrent()->Global(); 3053 Local<Value> fun = global->Get(v8_str("JSThrowCountDown")); 3054 v8::Handle<Value> argv[] = { v8_num(count - 1), 3055 args[1], 3056 args[2], 3057 args[3] }; 3058 if (count % cInterval == 0) { 3059 v8::TryCatch try_catch; 3060 Local<Value> result = fun.As<Function>()->Call(global, 4, argv); 3061 int expected = args[3]->Int32Value(); 3062 if (try_catch.HasCaught()) { 3063 CHECK_EQ(expected, count); 3064 CHECK(result.IsEmpty()); 3065 CHECK(!i::Isolate::Current()->has_scheduled_exception()); 3066 } else { 3067 CHECK_NE(expected, count); 3068 } 3069 return result; 3070 } else { 3071 return fun.As<Function>()->Call(global, 4, argv); 3072 } 3073 } 3074} 3075 3076 3077v8::Handle<Value> JSCheck(const v8::Arguments& args) { 3078 ApiTestFuzzer::Fuzz(); 3079 CHECK_EQ(3, args.Length()); 3080 bool equality = args[0]->BooleanValue(); 3081 int count = args[1]->Int32Value(); 3082 int expected = args[2]->Int32Value(); 3083 if (equality) { 3084 CHECK_EQ(count, expected); 3085 } else { 3086 CHECK_NE(count, expected); 3087 } 3088 return v8::Undefined(); 3089} 3090 3091 3092THREADED_TEST(EvalInTryFinally) { 3093 v8::HandleScope scope; 3094 LocalContext context; 3095 v8::TryCatch try_catch; 3096 CompileRun("(function() {" 3097 " try {" 3098 " eval('asldkf (*&^&*^');" 3099 " } finally {" 3100 " return;" 3101 " }" 3102 "})()"); 3103 CHECK(!try_catch.HasCaught()); 3104} 3105 3106 3107// This test works by making a stack of alternating JavaScript and C 3108// activations. These activations set up exception handlers with regular 3109// intervals, one interval for C activations and another for JavaScript 3110// activations. When enough activations have been created an exception is 3111// thrown and we check that the right activation catches the exception and that 3112// no other activations do. The right activation is always the topmost one with 3113// a handler, regardless of whether it is in JavaScript or C. 3114// 3115// The notation used to describe a test case looks like this: 3116// 3117// *JS[4] *C[3] @JS[2] C[1] JS[0] 3118// 3119// Each entry is an activation, either JS or C. The index is the count at that 3120// level. Stars identify activations with exception handlers, the @ identifies 3121// the exception handler that should catch the exception. 3122// 3123// BUG(271): Some of the exception propagation does not work on the 3124// ARM simulator because the simulator separates the C++ stack and the 3125// JS stack. This test therefore fails on the simulator. The test is 3126// not threaded to allow the threading tests to run on the simulator. 3127TEST(ExceptionOrder) { 3128 v8::HandleScope scope; 3129 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3130 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck)); 3131 templ->Set(v8_str("CThrowCountDown"), 3132 v8::FunctionTemplate::New(CThrowCountDown)); 3133 LocalContext context(0, templ); 3134 CompileRun( 3135 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {" 3136 " if (count == 0) throw 'FromJS';" 3137 " if (count % jsInterval == 0) {" 3138 " try {" 3139 " var value = CThrowCountDown(count - 1," 3140 " jsInterval," 3141 " cInterval," 3142 " expected);" 3143 " check(false, count, expected);" 3144 " return value;" 3145 " } catch (e) {" 3146 " check(true, count, expected);" 3147 " }" 3148 " } else {" 3149 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);" 3150 " }" 3151 "}"); 3152 Local<Function> fun = 3153 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown"))); 3154 3155 const int argc = 4; 3156 // count jsInterval cInterval expected 3157 3158 // *JS[4] *C[3] @JS[2] C[1] JS[0] 3159 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) }; 3160 fun->Call(fun, argc, a0); 3161 3162 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0] 3163 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) }; 3164 fun->Call(fun, argc, a1); 3165 3166 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0] 3167 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) }; 3168 fun->Call(fun, argc, a2); 3169 3170 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0] 3171 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) }; 3172 fun->Call(fun, argc, a3); 3173 3174 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0] 3175 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) }; 3176 fun->Call(fun, argc, a4); 3177 3178 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0] 3179 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) }; 3180 fun->Call(fun, argc, a5); 3181} 3182 3183 3184v8::Handle<Value> ThrowValue(const v8::Arguments& args) { 3185 ApiTestFuzzer::Fuzz(); 3186 CHECK_EQ(1, args.Length()); 3187 return v8::ThrowException(args[0]); 3188} 3189 3190 3191THREADED_TEST(ThrowValues) { 3192 v8::HandleScope scope; 3193 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3194 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue)); 3195 LocalContext context(0, templ); 3196 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 3197 "function Run(obj) {" 3198 " try {" 3199 " Throw(obj);" 3200 " } catch (e) {" 3201 " return e;" 3202 " }" 3203 " return 'no exception';" 3204 "}" 3205 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];")); 3206 CHECK_EQ(5, result->Length()); 3207 CHECK(result->Get(v8::Integer::New(0))->IsString()); 3208 CHECK(result->Get(v8::Integer::New(1))->IsNumber()); 3209 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value()); 3210 CHECK(result->Get(v8::Integer::New(2))->IsNumber()); 3211 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value()); 3212 CHECK(result->Get(v8::Integer::New(3))->IsNull()); 3213 CHECK(result->Get(v8::Integer::New(4))->IsUndefined()); 3214} 3215 3216 3217THREADED_TEST(CatchZero) { 3218 v8::HandleScope scope; 3219 LocalContext context; 3220 v8::TryCatch try_catch; 3221 CHECK(!try_catch.HasCaught()); 3222 Script::Compile(v8_str("throw 10"))->Run(); 3223 CHECK(try_catch.HasCaught()); 3224 CHECK_EQ(10, try_catch.Exception()->Int32Value()); 3225 try_catch.Reset(); 3226 CHECK(!try_catch.HasCaught()); 3227 Script::Compile(v8_str("throw 0"))->Run(); 3228 CHECK(try_catch.HasCaught()); 3229 CHECK_EQ(0, try_catch.Exception()->Int32Value()); 3230} 3231 3232 3233THREADED_TEST(CatchExceptionFromWith) { 3234 v8::HandleScope scope; 3235 LocalContext context; 3236 v8::TryCatch try_catch; 3237 CHECK(!try_catch.HasCaught()); 3238 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run(); 3239 CHECK(try_catch.HasCaught()); 3240} 3241 3242 3243THREADED_TEST(TryCatchAndFinallyHidingException) { 3244 v8::HandleScope scope; 3245 LocalContext context; 3246 v8::TryCatch try_catch; 3247 CHECK(!try_catch.HasCaught()); 3248 CompileRun("function f(k) { try { this[k]; } finally { return 0; } };"); 3249 CompileRun("f({toString: function() { throw 42; }});"); 3250 CHECK(!try_catch.HasCaught()); 3251} 3252 3253 3254v8::Handle<v8::Value> WithTryCatch(const v8::Arguments& args) { 3255 v8::TryCatch try_catch; 3256 return v8::Undefined(); 3257} 3258 3259 3260THREADED_TEST(TryCatchAndFinally) { 3261 v8::HandleScope scope; 3262 LocalContext context; 3263 context->Global()->Set( 3264 v8_str("native_with_try_catch"), 3265 v8::FunctionTemplate::New(WithTryCatch)->GetFunction()); 3266 v8::TryCatch try_catch; 3267 CHECK(!try_catch.HasCaught()); 3268 CompileRun( 3269 "try {\n" 3270 " throw new Error('a');\n" 3271 "} finally {\n" 3272 " native_with_try_catch();\n" 3273 "}\n"); 3274 CHECK(try_catch.HasCaught()); 3275} 3276 3277 3278THREADED_TEST(Equality) { 3279 v8::HandleScope scope; 3280 LocalContext context; 3281 // Check that equality works at all before relying on CHECK_EQ 3282 CHECK(v8_str("a")->Equals(v8_str("a"))); 3283 CHECK(!v8_str("a")->Equals(v8_str("b"))); 3284 3285 CHECK_EQ(v8_str("a"), v8_str("a")); 3286 CHECK_NE(v8_str("a"), v8_str("b")); 3287 CHECK_EQ(v8_num(1), v8_num(1)); 3288 CHECK_EQ(v8_num(1.00), v8_num(1)); 3289 CHECK_NE(v8_num(1), v8_num(2)); 3290 3291 // Assume String is not symbol. 3292 CHECK(v8_str("a")->StrictEquals(v8_str("a"))); 3293 CHECK(!v8_str("a")->StrictEquals(v8_str("b"))); 3294 CHECK(!v8_str("5")->StrictEquals(v8_num(5))); 3295 CHECK(v8_num(1)->StrictEquals(v8_num(1))); 3296 CHECK(!v8_num(1)->StrictEquals(v8_num(2))); 3297 CHECK(v8_num(0)->StrictEquals(v8_num(-0))); 3298 Local<Value> not_a_number = v8_num(i::OS::nan_value()); 3299 CHECK(!not_a_number->StrictEquals(not_a_number)); 3300 CHECK(v8::False()->StrictEquals(v8::False())); 3301 CHECK(!v8::False()->StrictEquals(v8::Undefined())); 3302 3303 v8::Handle<v8::Object> obj = v8::Object::New(); 3304 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj); 3305 CHECK(alias->StrictEquals(obj)); 3306 alias.Dispose(); 3307} 3308 3309 3310THREADED_TEST(MultiRun) { 3311 v8::HandleScope scope; 3312 LocalContext context; 3313 Local<Script> script = Script::Compile(v8_str("x")); 3314 for (int i = 0; i < 10; i++) 3315 script->Run(); 3316} 3317 3318 3319static v8::Handle<Value> GetXValue(Local<String> name, 3320 const AccessorInfo& info) { 3321 ApiTestFuzzer::Fuzz(); 3322 CHECK_EQ(info.Data(), v8_str("donut")); 3323 CHECK_EQ(name, v8_str("x")); 3324 return name; 3325} 3326 3327 3328THREADED_TEST(SimplePropertyRead) { 3329 v8::HandleScope scope; 3330 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3331 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 3332 LocalContext context; 3333 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3334 Local<Script> script = Script::Compile(v8_str("obj.x")); 3335 for (int i = 0; i < 10; i++) { 3336 Local<Value> result = script->Run(); 3337 CHECK_EQ(result, v8_str("x")); 3338 } 3339} 3340 3341THREADED_TEST(DefinePropertyOnAPIAccessor) { 3342 v8::HandleScope scope; 3343 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3344 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 3345 LocalContext context; 3346 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3347 3348 // Uses getOwnPropertyDescriptor to check the configurable status 3349 Local<Script> script_desc 3350 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( " 3351 "obj, 'x');" 3352 "prop.configurable;")); 3353 Local<Value> result = script_desc->Run(); 3354 CHECK_EQ(result->BooleanValue(), true); 3355 3356 // Redefine get - but still configurable 3357 Local<Script> script_define 3358 = Script::Compile(v8_str("var desc = { get: function(){return 42; }," 3359 " configurable: true };" 3360 "Object.defineProperty(obj, 'x', desc);" 3361 "obj.x")); 3362 result = script_define->Run(); 3363 CHECK_EQ(result, v8_num(42)); 3364 3365 // Check that the accessor is still configurable 3366 result = script_desc->Run(); 3367 CHECK_EQ(result->BooleanValue(), true); 3368 3369 // Redefine to a non-configurable 3370 script_define 3371 = Script::Compile(v8_str("var desc = { get: function(){return 43; }," 3372 " configurable: false };" 3373 "Object.defineProperty(obj, 'x', desc);" 3374 "obj.x")); 3375 result = script_define->Run(); 3376 CHECK_EQ(result, v8_num(43)); 3377 result = script_desc->Run(); 3378 CHECK_EQ(result->BooleanValue(), false); 3379 3380 // Make sure that it is not possible to redefine again 3381 v8::TryCatch try_catch; 3382 result = script_define->Run(); 3383 CHECK(try_catch.HasCaught()); 3384 String::AsciiValue exception_value(try_catch.Exception()); 3385 CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); 3386} 3387 3388THREADED_TEST(DefinePropertyOnDefineGetterSetter) { 3389 v8::HandleScope scope; 3390 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3391 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 3392 LocalContext context; 3393 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3394 3395 Local<Script> script_desc = Script::Compile(v8_str("var prop =" 3396 "Object.getOwnPropertyDescriptor( " 3397 "obj, 'x');" 3398 "prop.configurable;")); 3399 Local<Value> result = script_desc->Run(); 3400 CHECK_EQ(result->BooleanValue(), true); 3401 3402 Local<Script> script_define = 3403 Script::Compile(v8_str("var desc = {get: function(){return 42; }," 3404 " configurable: true };" 3405 "Object.defineProperty(obj, 'x', desc);" 3406 "obj.x")); 3407 result = script_define->Run(); 3408 CHECK_EQ(result, v8_num(42)); 3409 3410 3411 result = script_desc->Run(); 3412 CHECK_EQ(result->BooleanValue(), true); 3413 3414 3415 script_define = 3416 Script::Compile(v8_str("var desc = {get: function(){return 43; }," 3417 " configurable: false };" 3418 "Object.defineProperty(obj, 'x', desc);" 3419 "obj.x")); 3420 result = script_define->Run(); 3421 CHECK_EQ(result, v8_num(43)); 3422 result = script_desc->Run(); 3423 3424 CHECK_EQ(result->BooleanValue(), false); 3425 3426 v8::TryCatch try_catch; 3427 result = script_define->Run(); 3428 CHECK(try_catch.HasCaught()); 3429 String::AsciiValue exception_value(try_catch.Exception()); 3430 CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); 3431} 3432 3433 3434static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context, 3435 char const* name) { 3436 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name))); 3437} 3438 3439 3440THREADED_TEST(DefineAPIAccessorOnObject) { 3441 v8::HandleScope scope; 3442 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3443 LocalContext context; 3444 3445 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 3446 CompileRun("var obj2 = {};"); 3447 3448 CHECK(CompileRun("obj1.x")->IsUndefined()); 3449 CHECK(CompileRun("obj2.x")->IsUndefined()); 3450 3451 CHECK(GetGlobalProperty(&context, "obj1")-> 3452 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3453 3454 ExpectString("obj1.x", "x"); 3455 CHECK(CompileRun("obj2.x")->IsUndefined()); 3456 3457 CHECK(GetGlobalProperty(&context, "obj2")-> 3458 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3459 3460 ExpectString("obj1.x", "x"); 3461 ExpectString("obj2.x", "x"); 3462 3463 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 3464 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 3465 3466 CompileRun("Object.defineProperty(obj1, 'x'," 3467 "{ get: function() { return 'y'; }, configurable: true })"); 3468 3469 ExpectString("obj1.x", "y"); 3470 ExpectString("obj2.x", "x"); 3471 3472 CompileRun("Object.defineProperty(obj2, 'x'," 3473 "{ get: function() { return 'y'; }, configurable: true })"); 3474 3475 ExpectString("obj1.x", "y"); 3476 ExpectString("obj2.x", "y"); 3477 3478 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 3479 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 3480 3481 CHECK(GetGlobalProperty(&context, "obj1")-> 3482 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3483 CHECK(GetGlobalProperty(&context, "obj2")-> 3484 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3485 3486 ExpectString("obj1.x", "x"); 3487 ExpectString("obj2.x", "x"); 3488 3489 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 3490 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 3491 3492 // Define getters/setters, but now make them not configurable. 3493 CompileRun("Object.defineProperty(obj1, 'x'," 3494 "{ get: function() { return 'z'; }, configurable: false })"); 3495 CompileRun("Object.defineProperty(obj2, 'x'," 3496 "{ get: function() { return 'z'; }, configurable: false })"); 3497 3498 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 3499 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 3500 3501 ExpectString("obj1.x", "z"); 3502 ExpectString("obj2.x", "z"); 3503 3504 CHECK(!GetGlobalProperty(&context, "obj1")-> 3505 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3506 CHECK(!GetGlobalProperty(&context, "obj2")-> 3507 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3508 3509 ExpectString("obj1.x", "z"); 3510 ExpectString("obj2.x", "z"); 3511} 3512 3513 3514THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) { 3515 v8::HandleScope scope; 3516 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3517 LocalContext context; 3518 3519 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 3520 CompileRun("var obj2 = {};"); 3521 3522 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 3523 v8_str("x"), 3524 GetXValue, NULL, 3525 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 3526 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 3527 v8_str("x"), 3528 GetXValue, NULL, 3529 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 3530 3531 ExpectString("obj1.x", "x"); 3532 ExpectString("obj2.x", "x"); 3533 3534 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 3535 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 3536 3537 CHECK(!GetGlobalProperty(&context, "obj1")-> 3538 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3539 CHECK(!GetGlobalProperty(&context, "obj2")-> 3540 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 3541 3542 { 3543 v8::TryCatch try_catch; 3544 CompileRun("Object.defineProperty(obj1, 'x'," 3545 "{get: function() { return 'func'; }})"); 3546 CHECK(try_catch.HasCaught()); 3547 String::AsciiValue exception_value(try_catch.Exception()); 3548 CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); 3549 } 3550 { 3551 v8::TryCatch try_catch; 3552 CompileRun("Object.defineProperty(obj2, 'x'," 3553 "{get: function() { return 'func'; }})"); 3554 CHECK(try_catch.HasCaught()); 3555 String::AsciiValue exception_value(try_catch.Exception()); 3556 CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); 3557 } 3558} 3559 3560 3561static v8::Handle<Value> Get239Value(Local<String> name, 3562 const AccessorInfo& info) { 3563 ApiTestFuzzer::Fuzz(); 3564 CHECK_EQ(info.Data(), v8_str("donut")); 3565 CHECK_EQ(name, v8_str("239")); 3566 return name; 3567} 3568 3569 3570THREADED_TEST(ElementAPIAccessor) { 3571 v8::HandleScope scope; 3572 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3573 LocalContext context; 3574 3575 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 3576 CompileRun("var obj2 = {};"); 3577 3578 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 3579 v8_str("239"), 3580 Get239Value, NULL, 3581 v8_str("donut"))); 3582 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 3583 v8_str("239"), 3584 Get239Value, NULL, 3585 v8_str("donut"))); 3586 3587 ExpectString("obj1[239]", "239"); 3588 ExpectString("obj2[239]", "239"); 3589 ExpectString("obj1['239']", "239"); 3590 ExpectString("obj2['239']", "239"); 3591} 3592 3593 3594v8::Persistent<Value> xValue; 3595 3596 3597static void SetXValue(Local<String> name, 3598 Local<Value> value, 3599 const AccessorInfo& info) { 3600 CHECK_EQ(value, v8_num(4)); 3601 CHECK_EQ(info.Data(), v8_str("donut")); 3602 CHECK_EQ(name, v8_str("x")); 3603 CHECK(xValue.IsEmpty()); 3604 xValue = v8::Persistent<Value>::New(value); 3605} 3606 3607 3608THREADED_TEST(SimplePropertyWrite) { 3609 v8::HandleScope scope; 3610 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3611 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut")); 3612 LocalContext context; 3613 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3614 Local<Script> script = Script::Compile(v8_str("obj.x = 4")); 3615 for (int i = 0; i < 10; i++) { 3616 CHECK(xValue.IsEmpty()); 3617 script->Run(); 3618 CHECK_EQ(v8_num(4), xValue); 3619 xValue.Dispose(); 3620 xValue = v8::Persistent<Value>(); 3621 } 3622} 3623 3624 3625static v8::Handle<Value> XPropertyGetter(Local<String> property, 3626 const AccessorInfo& info) { 3627 ApiTestFuzzer::Fuzz(); 3628 CHECK(info.Data()->IsUndefined()); 3629 return property; 3630} 3631 3632 3633THREADED_TEST(NamedInterceptorPropertyRead) { 3634 v8::HandleScope scope; 3635 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3636 templ->SetNamedPropertyHandler(XPropertyGetter); 3637 LocalContext context; 3638 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3639 Local<Script> script = Script::Compile(v8_str("obj.x")); 3640 for (int i = 0; i < 10; i++) { 3641 Local<Value> result = script->Run(); 3642 CHECK_EQ(result, v8_str("x")); 3643 } 3644} 3645 3646 3647THREADED_TEST(NamedInterceptorDictionaryIC) { 3648 v8::HandleScope scope; 3649 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3650 templ->SetNamedPropertyHandler(XPropertyGetter); 3651 LocalContext context; 3652 // Create an object with a named interceptor. 3653 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance()); 3654 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x")); 3655 for (int i = 0; i < 10; i++) { 3656 Local<Value> result = script->Run(); 3657 CHECK_EQ(result, v8_str("x")); 3658 } 3659 // Create a slow case object and a function accessing a property in 3660 // that slow case object (with dictionary probing in generated 3661 // code). Then force object with a named interceptor into slow-case, 3662 // pass it to the function, and check that the interceptor is called 3663 // instead of accessing the local property. 3664 Local<Value> result = 3665 CompileRun("function get_x(o) { return o.x; };" 3666 "var obj = { x : 42, y : 0 };" 3667 "delete obj.y;" 3668 "for (var i = 0; i < 10; i++) get_x(obj);" 3669 "interceptor_obj.x = 42;" 3670 "interceptor_obj.y = 10;" 3671 "delete interceptor_obj.y;" 3672 "get_x(interceptor_obj)"); 3673 CHECK_EQ(result, v8_str("x")); 3674} 3675 3676 3677THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) { 3678 v8::HandleScope scope; 3679 3680 v8::Persistent<Context> context1 = Context::New(); 3681 3682 context1->Enter(); 3683 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3684 templ->SetNamedPropertyHandler(XPropertyGetter); 3685 // Create an object with a named interceptor. 3686 v8::Local<v8::Object> object = templ->NewInstance(); 3687 context1->Global()->Set(v8_str("interceptor_obj"), object); 3688 3689 // Force the object into the slow case. 3690 CompileRun("interceptor_obj.y = 0;" 3691 "delete interceptor_obj.y;"); 3692 context1->Exit(); 3693 3694 { 3695 // Introduce the object into a different context. 3696 // Repeat named loads to exercise ICs. 3697 LocalContext context2; 3698 context2->Global()->Set(v8_str("interceptor_obj"), object); 3699 Local<Value> result = 3700 CompileRun("function get_x(o) { return o.x; }" 3701 "interceptor_obj.x = 42;" 3702 "for (var i=0; i != 10; i++) {" 3703 " get_x(interceptor_obj);" 3704 "}" 3705 "get_x(interceptor_obj)"); 3706 // Check that the interceptor was actually invoked. 3707 CHECK_EQ(result, v8_str("x")); 3708 } 3709 3710 // Return to the original context and force some object to the slow case 3711 // to cause the NormalizedMapCache to verify. 3712 context1->Enter(); 3713 CompileRun("var obj = { x : 0 }; delete obj.x;"); 3714 context1->Exit(); 3715 3716 context1.Dispose(); 3717} 3718 3719 3720static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property, 3721 const AccessorInfo& info) { 3722 // Set x on the prototype object and do not handle the get request. 3723 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype(); 3724 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23)); 3725 return v8::Handle<Value>(); 3726} 3727 3728 3729// This is a regression test for http://crbug.com/20104. Map 3730// transitions should not interfere with post interceptor lookup. 3731THREADED_TEST(NamedInterceptorMapTransitionRead) { 3732 v8::HandleScope scope; 3733 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(); 3734 Local<v8::ObjectTemplate> instance_template 3735 = function_template->InstanceTemplate(); 3736 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter); 3737 LocalContext context; 3738 context->Global()->Set(v8_str("F"), function_template->GetFunction()); 3739 // Create an instance of F and introduce a map transition for x. 3740 CompileRun("var o = new F(); o.x = 23;"); 3741 // Create an instance of F and invoke the getter. The result should be 23. 3742 Local<Value> result = CompileRun("o = new F(); o.x"); 3743 CHECK_EQ(result->Int32Value(), 23); 3744} 3745 3746 3747static v8::Handle<Value> IndexedPropertyGetter(uint32_t index, 3748 const AccessorInfo& info) { 3749 ApiTestFuzzer::Fuzz(); 3750 if (index == 37) { 3751 return v8::Handle<Value>(v8_num(625)); 3752 } 3753 return v8::Handle<Value>(); 3754} 3755 3756 3757static v8::Handle<Value> IndexedPropertySetter(uint32_t index, 3758 Local<Value> value, 3759 const AccessorInfo& info) { 3760 ApiTestFuzzer::Fuzz(); 3761 if (index == 39) { 3762 return value; 3763 } 3764 return v8::Handle<Value>(); 3765} 3766 3767 3768THREADED_TEST(IndexedInterceptorWithIndexedAccessor) { 3769 v8::HandleScope scope; 3770 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3771 templ->SetIndexedPropertyHandler(IndexedPropertyGetter, 3772 IndexedPropertySetter); 3773 LocalContext context; 3774 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3775 Local<Script> getter_script = Script::Compile(v8_str( 3776 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];")); 3777 Local<Script> setter_script = Script::Compile(v8_str( 3778 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});" 3779 "obj[17] = 23;" 3780 "obj.foo;")); 3781 Local<Script> interceptor_setter_script = Script::Compile(v8_str( 3782 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});" 3783 "obj[39] = 47;" 3784 "obj.foo;")); // This setter should not run, due to the interceptor. 3785 Local<Script> interceptor_getter_script = Script::Compile(v8_str( 3786 "obj[37];")); 3787 Local<Value> result = getter_script->Run(); 3788 CHECK_EQ(v8_num(5), result); 3789 result = setter_script->Run(); 3790 CHECK_EQ(v8_num(23), result); 3791 result = interceptor_setter_script->Run(); 3792 CHECK_EQ(v8_num(23), result); 3793 result = interceptor_getter_script->Run(); 3794 CHECK_EQ(v8_num(625), result); 3795} 3796 3797 3798static v8::Handle<Value> UnboxedDoubleIndexedPropertyGetter( 3799 uint32_t index, 3800 const AccessorInfo& info) { 3801 ApiTestFuzzer::Fuzz(); 3802 if (index < 25) { 3803 return v8::Handle<Value>(v8_num(index)); 3804 } 3805 return v8::Handle<Value>(); 3806} 3807 3808 3809static v8::Handle<Value> UnboxedDoubleIndexedPropertySetter( 3810 uint32_t index, 3811 Local<Value> value, 3812 const AccessorInfo& info) { 3813 ApiTestFuzzer::Fuzz(); 3814 if (index < 25) { 3815 return v8::Handle<Value>(v8_num(index)); 3816 } 3817 return v8::Handle<Value>(); 3818} 3819 3820 3821Handle<v8::Array> UnboxedDoubleIndexedPropertyEnumerator( 3822 const AccessorInfo& info) { 3823 // Force the list of returned keys to be stored in a FastDoubleArray. 3824 Local<Script> indexed_property_names_script = Script::Compile(v8_str( 3825 "keys = new Array(); keys[125000] = 1;" 3826 "for(i = 0; i < 80000; i++) { keys[i] = i; };" 3827 "keys.length = 25; keys;")); 3828 Local<Value> result = indexed_property_names_script->Run(); 3829 return Local<v8::Array>(::v8::Array::Cast(*result)); 3830} 3831 3832 3833// Make sure that the the interceptor code in the runtime properly handles 3834// merging property name lists for double-array-backed arrays. 3835THREADED_TEST(IndexedInterceptorUnboxedDoubleWithIndexedAccessor) { 3836 v8::HandleScope scope; 3837 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3838 templ->SetIndexedPropertyHandler(UnboxedDoubleIndexedPropertyGetter, 3839 UnboxedDoubleIndexedPropertySetter, 3840 0, 3841 0, 3842 UnboxedDoubleIndexedPropertyEnumerator); 3843 LocalContext context; 3844 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3845 // When obj is created, force it to be Stored in a FastDoubleArray. 3846 Local<Script> create_unboxed_double_script = Script::Compile(v8_str( 3847 "obj[125000] = 1; for(i = 0; i < 80000; i+=2) { obj[i] = i; } " 3848 "key_count = 0; " 3849 "for (x in obj) {key_count++;};" 3850 "obj;")); 3851 Local<Value> result = create_unboxed_double_script->Run(); 3852 CHECK(result->ToObject()->HasRealIndexedProperty(2000)); 3853 Local<Script> key_count_check = Script::Compile(v8_str( 3854 "key_count;")); 3855 result = key_count_check->Run(); 3856 CHECK_EQ(v8_num(40013), result); 3857} 3858 3859 3860Handle<v8::Array> NonStrictArgsIndexedPropertyEnumerator( 3861 const AccessorInfo& info) { 3862 // Force the list of returned keys to be stored in a Arguments object. 3863 Local<Script> indexed_property_names_script = Script::Compile(v8_str( 3864 "function f(w,x) {" 3865 " return arguments;" 3866 "}" 3867 "keys = f(0, 1, 2, 3);" 3868 "keys;")); 3869 Local<Value> result = indexed_property_names_script->Run(); 3870 return Local<v8::Array>(static_cast<v8::Array*>(::v8::Object::Cast(*result))); 3871} 3872 3873 3874static v8::Handle<Value> NonStrictIndexedPropertyGetter( 3875 uint32_t index, 3876 const AccessorInfo& info) { 3877 ApiTestFuzzer::Fuzz(); 3878 if (index < 4) { 3879 return v8::Handle<Value>(v8_num(index)); 3880 } 3881 return v8::Handle<Value>(); 3882} 3883 3884 3885// Make sure that the the interceptor code in the runtime properly handles 3886// merging property name lists for non-string arguments arrays. 3887THREADED_TEST(IndexedInterceptorNonStrictArgsWithIndexedAccessor) { 3888 v8::HandleScope scope; 3889 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3890 templ->SetIndexedPropertyHandler(NonStrictIndexedPropertyGetter, 3891 0, 3892 0, 3893 0, 3894 NonStrictArgsIndexedPropertyEnumerator); 3895 LocalContext context; 3896 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3897 Local<Script> create_args_script = 3898 Script::Compile(v8_str( 3899 "var key_count = 0;" 3900 "for (x in obj) {key_count++;} key_count;")); 3901 Local<Value> result = create_args_script->Run(); 3902 CHECK_EQ(v8_num(4), result); 3903} 3904 3905 3906static v8::Handle<Value> IdentityIndexedPropertyGetter( 3907 uint32_t index, 3908 const AccessorInfo& info) { 3909 return v8::Integer::NewFromUnsigned(index); 3910} 3911 3912 3913THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) { 3914 v8::HandleScope scope; 3915 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3916 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3917 3918 LocalContext context; 3919 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3920 3921 // Check fast object case. 3922 const char* fast_case_code = 3923 "Object.getOwnPropertyDescriptor(obj, 0).value.toString()"; 3924 ExpectString(fast_case_code, "0"); 3925 3926 // Check slow case. 3927 const char* slow_case_code = 3928 "obj.x = 1; delete obj.x;" 3929 "Object.getOwnPropertyDescriptor(obj, 1).value.toString()"; 3930 ExpectString(slow_case_code, "1"); 3931} 3932 3933 3934THREADED_TEST(IndexedInterceptorWithNoSetter) { 3935 v8::HandleScope scope; 3936 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3937 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3938 3939 LocalContext context; 3940 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3941 3942 const char* code = 3943 "try {" 3944 " obj[0] = 239;" 3945 " for (var i = 0; i < 100; i++) {" 3946 " var v = obj[0];" 3947 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;" 3948 " }" 3949 " 'PASSED'" 3950 "} catch(e) {" 3951 " e" 3952 "}"; 3953 ExpectString(code, "PASSED"); 3954} 3955 3956 3957THREADED_TEST(IndexedInterceptorWithAccessorCheck) { 3958 v8::HandleScope scope; 3959 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3960 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3961 3962 LocalContext context; 3963 Local<v8::Object> obj = templ->NewInstance(); 3964 obj->TurnOnAccessCheck(); 3965 context->Global()->Set(v8_str("obj"), obj); 3966 3967 const char* code = 3968 "try {" 3969 " for (var i = 0; i < 100; i++) {" 3970 " var v = obj[0];" 3971 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;" 3972 " }" 3973 " 'PASSED'" 3974 "} catch(e) {" 3975 " e" 3976 "}"; 3977 ExpectString(code, "PASSED"); 3978} 3979 3980 3981THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) { 3982 i::FLAG_allow_natives_syntax = true; 3983 v8::HandleScope scope; 3984 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3985 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3986 3987 LocalContext context; 3988 Local<v8::Object> obj = templ->NewInstance(); 3989 context->Global()->Set(v8_str("obj"), obj); 3990 3991 const char* code = 3992 "try {" 3993 " for (var i = 0; i < 100; i++) {" 3994 " var expected = i;" 3995 " if (i == 5) {" 3996 " %EnableAccessChecks(obj);" 3997 " expected = undefined;" 3998 " }" 3999 " var v = obj[i];" 4000 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 4001 " if (i == 5) %DisableAccessChecks(obj);" 4002 " }" 4003 " 'PASSED'" 4004 "} catch(e) {" 4005 " e" 4006 "}"; 4007 ExpectString(code, "PASSED"); 4008} 4009 4010 4011THREADED_TEST(IndexedInterceptorWithDifferentIndices) { 4012 v8::HandleScope scope; 4013 Local<ObjectTemplate> templ = ObjectTemplate::New(); 4014 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 4015 4016 LocalContext context; 4017 Local<v8::Object> obj = templ->NewInstance(); 4018 context->Global()->Set(v8_str("obj"), obj); 4019 4020 const char* code = 4021 "try {" 4022 " for (var i = 0; i < 100; i++) {" 4023 " var v = obj[i];" 4024 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 4025 " }" 4026 " 'PASSED'" 4027 "} catch(e) {" 4028 " e" 4029 "}"; 4030 ExpectString(code, "PASSED"); 4031} 4032 4033 4034THREADED_TEST(IndexedInterceptorWithNegativeIndices) { 4035 v8::HandleScope scope; 4036 Local<ObjectTemplate> templ = ObjectTemplate::New(); 4037 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 4038 4039 LocalContext context; 4040 Local<v8::Object> obj = templ->NewInstance(); 4041 context->Global()->Set(v8_str("obj"), obj); 4042 4043 const char* code = 4044 "try {" 4045 " for (var i = 0; i < 100; i++) {" 4046 " var expected = i;" 4047 " var key = i;" 4048 " if (i == 25) {" 4049 " key = -1;" 4050 " expected = undefined;" 4051 " }" 4052 " if (i == 50) {" 4053 " /* probe minimal Smi number on 32-bit platforms */" 4054 " key = -(1 << 30);" 4055 " expected = undefined;" 4056 " }" 4057 " if (i == 75) {" 4058 " /* probe minimal Smi number on 64-bit platforms */" 4059 " key = 1 << 31;" 4060 " expected = undefined;" 4061 " }" 4062 " var v = obj[key];" 4063 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 4064 " }" 4065 " 'PASSED'" 4066 "} catch(e) {" 4067 " e" 4068 "}"; 4069 ExpectString(code, "PASSED"); 4070} 4071 4072 4073THREADED_TEST(IndexedInterceptorWithNotSmiLookup) { 4074 v8::HandleScope scope; 4075 Local<ObjectTemplate> templ = ObjectTemplate::New(); 4076 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 4077 4078 LocalContext context; 4079 Local<v8::Object> obj = templ->NewInstance(); 4080 context->Global()->Set(v8_str("obj"), obj); 4081 4082 const char* code = 4083 "try {" 4084 " for (var i = 0; i < 100; i++) {" 4085 " var expected = i;" 4086 " var key = i;" 4087 " if (i == 50) {" 4088 " key = 'foobar';" 4089 " expected = undefined;" 4090 " }" 4091 " var v = obj[key];" 4092 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 4093 " }" 4094 " 'PASSED'" 4095 "} catch(e) {" 4096 " e" 4097 "}"; 4098 ExpectString(code, "PASSED"); 4099} 4100 4101 4102THREADED_TEST(IndexedInterceptorGoingMegamorphic) { 4103 v8::HandleScope scope; 4104 Local<ObjectTemplate> templ = ObjectTemplate::New(); 4105 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 4106 4107 LocalContext context; 4108 Local<v8::Object> obj = templ->NewInstance(); 4109 context->Global()->Set(v8_str("obj"), obj); 4110 4111 const char* code = 4112 "var original = obj;" 4113 "try {" 4114 " for (var i = 0; i < 100; i++) {" 4115 " var expected = i;" 4116 " if (i == 50) {" 4117 " obj = {50: 'foobar'};" 4118 " expected = 'foobar';" 4119 " }" 4120 " var v = obj[i];" 4121 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 4122 " if (i == 50) obj = original;" 4123 " }" 4124 " 'PASSED'" 4125 "} catch(e) {" 4126 " e" 4127 "}"; 4128 ExpectString(code, "PASSED"); 4129} 4130 4131 4132THREADED_TEST(IndexedInterceptorReceiverTurningSmi) { 4133 v8::HandleScope scope; 4134 Local<ObjectTemplate> templ = ObjectTemplate::New(); 4135 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 4136 4137 LocalContext context; 4138 Local<v8::Object> obj = templ->NewInstance(); 4139 context->Global()->Set(v8_str("obj"), obj); 4140 4141 const char* code = 4142 "var original = obj;" 4143 "try {" 4144 " for (var i = 0; i < 100; i++) {" 4145 " var expected = i;" 4146 " if (i == 5) {" 4147 " obj = 239;" 4148 " expected = undefined;" 4149 " }" 4150 " var v = obj[i];" 4151 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 4152 " if (i == 5) obj = original;" 4153 " }" 4154 " 'PASSED'" 4155 "} catch(e) {" 4156 " e" 4157 "}"; 4158 ExpectString(code, "PASSED"); 4159} 4160 4161 4162THREADED_TEST(IndexedInterceptorOnProto) { 4163 v8::HandleScope scope; 4164 Local<ObjectTemplate> templ = ObjectTemplate::New(); 4165 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 4166 4167 LocalContext context; 4168 Local<v8::Object> obj = templ->NewInstance(); 4169 context->Global()->Set(v8_str("obj"), obj); 4170 4171 const char* code = 4172 "var o = {__proto__: obj};" 4173 "try {" 4174 " for (var i = 0; i < 100; i++) {" 4175 " var v = o[i];" 4176 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 4177 " }" 4178 " 'PASSED'" 4179 "} catch(e) {" 4180 " e" 4181 "}"; 4182 ExpectString(code, "PASSED"); 4183} 4184 4185 4186THREADED_TEST(MultiContexts) { 4187 v8::HandleScope scope; 4188 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New(); 4189 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler)); 4190 4191 Local<String> password = v8_str("Password"); 4192 4193 // Create an environment 4194 LocalContext context0(0, templ); 4195 context0->SetSecurityToken(password); 4196 v8::Handle<v8::Object> global0 = context0->Global(); 4197 global0->Set(v8_str("custom"), v8_num(1234)); 4198 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 4199 4200 // Create an independent environment 4201 LocalContext context1(0, templ); 4202 context1->SetSecurityToken(password); 4203 v8::Handle<v8::Object> global1 = context1->Global(); 4204 global1->Set(v8_str("custom"), v8_num(1234)); 4205 CHECK_NE(global0, global1); 4206 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 4207 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value()); 4208 4209 // Now create a new context with the old global 4210 LocalContext context2(0, templ, global1); 4211 context2->SetSecurityToken(password); 4212 v8::Handle<v8::Object> global2 = context2->Global(); 4213 CHECK_EQ(global1, global2); 4214 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value()); 4215 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value()); 4216} 4217 4218 4219THREADED_TEST(FunctionPrototypeAcrossContexts) { 4220 // Make sure that functions created by cloning boilerplates cannot 4221 // communicate through their __proto__ field. 4222 4223 v8::HandleScope scope; 4224 4225 LocalContext env0; 4226 v8::Handle<v8::Object> global0 = 4227 env0->Global(); 4228 v8::Handle<v8::Object> object0 = 4229 global0->Get(v8_str("Object")).As<v8::Object>(); 4230 v8::Handle<v8::Object> tostring0 = 4231 object0->Get(v8_str("toString")).As<v8::Object>(); 4232 v8::Handle<v8::Object> proto0 = 4233 tostring0->Get(v8_str("__proto__")).As<v8::Object>(); 4234 proto0->Set(v8_str("custom"), v8_num(1234)); 4235 4236 LocalContext env1; 4237 v8::Handle<v8::Object> global1 = 4238 env1->Global(); 4239 v8::Handle<v8::Object> object1 = 4240 global1->Get(v8_str("Object")).As<v8::Object>(); 4241 v8::Handle<v8::Object> tostring1 = 4242 object1->Get(v8_str("toString")).As<v8::Object>(); 4243 v8::Handle<v8::Object> proto1 = 4244 tostring1->Get(v8_str("__proto__")).As<v8::Object>(); 4245 CHECK(!proto1->Has(v8_str("custom"))); 4246} 4247 4248 4249THREADED_TEST(Regress892105) { 4250 // Make sure that object and array literals created by cloning 4251 // boilerplates cannot communicate through their __proto__ 4252 // field. This is rather difficult to check, but we try to add stuff 4253 // to Object.prototype and Array.prototype and create a new 4254 // environment. This should succeed. 4255 4256 v8::HandleScope scope; 4257 4258 Local<String> source = v8_str("Object.prototype.obj = 1234;" 4259 "Array.prototype.arr = 4567;" 4260 "8901"); 4261 4262 LocalContext env0; 4263 Local<Script> script0 = Script::Compile(source); 4264 CHECK_EQ(8901.0, script0->Run()->NumberValue()); 4265 4266 LocalContext env1; 4267 Local<Script> script1 = Script::Compile(source); 4268 CHECK_EQ(8901.0, script1->Run()->NumberValue()); 4269} 4270 4271 4272THREADED_TEST(UndetectableObject) { 4273 v8::HandleScope scope; 4274 LocalContext env; 4275 4276 Local<v8::FunctionTemplate> desc = 4277 v8::FunctionTemplate::New(0, v8::Handle<Value>()); 4278 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 4279 4280 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 4281 env->Global()->Set(v8_str("undetectable"), obj); 4282 4283 ExpectString("undetectable.toString()", "[object Object]"); 4284 ExpectString("typeof undetectable", "undefined"); 4285 ExpectString("typeof(undetectable)", "undefined"); 4286 ExpectBoolean("typeof undetectable == 'undefined'", true); 4287 ExpectBoolean("typeof undetectable == 'object'", false); 4288 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 4289 ExpectBoolean("!undetectable", true); 4290 4291 ExpectObject("true&&undetectable", obj); 4292 ExpectBoolean("false&&undetectable", false); 4293 ExpectBoolean("true||undetectable", true); 4294 ExpectObject("false||undetectable", obj); 4295 4296 ExpectObject("undetectable&&true", obj); 4297 ExpectObject("undetectable&&false", obj); 4298 ExpectBoolean("undetectable||true", true); 4299 ExpectBoolean("undetectable||false", false); 4300 4301 ExpectBoolean("undetectable==null", true); 4302 ExpectBoolean("null==undetectable", true); 4303 ExpectBoolean("undetectable==undefined", true); 4304 ExpectBoolean("undefined==undetectable", true); 4305 ExpectBoolean("undetectable==undetectable", true); 4306 4307 4308 ExpectBoolean("undetectable===null", false); 4309 ExpectBoolean("null===undetectable", false); 4310 ExpectBoolean("undetectable===undefined", false); 4311 ExpectBoolean("undefined===undetectable", false); 4312 ExpectBoolean("undetectable===undetectable", true); 4313} 4314 4315 4316THREADED_TEST(VoidLiteral) { 4317 v8::HandleScope scope; 4318 LocalContext env; 4319 4320 Local<v8::FunctionTemplate> desc = 4321 v8::FunctionTemplate::New(0, v8::Handle<Value>()); 4322 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 4323 4324 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 4325 env->Global()->Set(v8_str("undetectable"), obj); 4326 4327 ExpectBoolean("undefined == void 0", true); 4328 ExpectBoolean("undetectable == void 0", true); 4329 ExpectBoolean("null == void 0", true); 4330 ExpectBoolean("undefined === void 0", true); 4331 ExpectBoolean("undetectable === void 0", false); 4332 ExpectBoolean("null === void 0", false); 4333 4334 ExpectBoolean("void 0 == undefined", true); 4335 ExpectBoolean("void 0 == undetectable", true); 4336 ExpectBoolean("void 0 == null", true); 4337 ExpectBoolean("void 0 === undefined", true); 4338 ExpectBoolean("void 0 === undetectable", false); 4339 ExpectBoolean("void 0 === null", false); 4340 4341 ExpectString("(function() {" 4342 " try {" 4343 " return x === void 0;" 4344 " } catch(e) {" 4345 " return e.toString();" 4346 " }" 4347 "})()", 4348 "ReferenceError: x is not defined"); 4349 ExpectString("(function() {" 4350 " try {" 4351 " return void 0 === x;" 4352 " } catch(e) {" 4353 " return e.toString();" 4354 " }" 4355 "})()", 4356 "ReferenceError: x is not defined"); 4357} 4358 4359 4360THREADED_TEST(ExtensibleOnUndetectable) { 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 Local<String> source = v8_str("undetectable.x = 42;" 4372 "undetectable.x"); 4373 4374 Local<Script> script = Script::Compile(source); 4375 4376 CHECK_EQ(v8::Integer::New(42), script->Run()); 4377 4378 ExpectBoolean("Object.isExtensible(undetectable)", true); 4379 4380 source = v8_str("Object.preventExtensions(undetectable);"); 4381 script = Script::Compile(source); 4382 script->Run(); 4383 ExpectBoolean("Object.isExtensible(undetectable)", false); 4384 4385 source = v8_str("undetectable.y = 2000;"); 4386 script = Script::Compile(source); 4387 Local<Value> result(script->Run()); 4388 ExpectBoolean("undetectable.y == undefined", true); 4389} 4390 4391 4392 4393THREADED_TEST(UndetectableString) { 4394 v8::HandleScope scope; 4395 LocalContext env; 4396 4397 Local<String> obj = String::NewUndetectable("foo"); 4398 env->Global()->Set(v8_str("undetectable"), obj); 4399 4400 ExpectString("undetectable", "foo"); 4401 ExpectString("typeof undetectable", "undefined"); 4402 ExpectString("typeof(undetectable)", "undefined"); 4403 ExpectBoolean("typeof undetectable == 'undefined'", true); 4404 ExpectBoolean("typeof undetectable == 'string'", false); 4405 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 4406 ExpectBoolean("!undetectable", true); 4407 4408 ExpectObject("true&&undetectable", obj); 4409 ExpectBoolean("false&&undetectable", false); 4410 ExpectBoolean("true||undetectable", true); 4411 ExpectObject("false||undetectable", obj); 4412 4413 ExpectObject("undetectable&&true", obj); 4414 ExpectObject("undetectable&&false", obj); 4415 ExpectBoolean("undetectable||true", true); 4416 ExpectBoolean("undetectable||false", false); 4417 4418 ExpectBoolean("undetectable==null", true); 4419 ExpectBoolean("null==undetectable", true); 4420 ExpectBoolean("undetectable==undefined", true); 4421 ExpectBoolean("undefined==undetectable", true); 4422 ExpectBoolean("undetectable==undetectable", true); 4423 4424 4425 ExpectBoolean("undetectable===null", false); 4426 ExpectBoolean("null===undetectable", false); 4427 ExpectBoolean("undetectable===undefined", false); 4428 ExpectBoolean("undefined===undetectable", false); 4429 ExpectBoolean("undetectable===undetectable", true); 4430} 4431 4432 4433TEST(UndetectableOptimized) { 4434 i::FLAG_allow_natives_syntax = true; 4435 v8::HandleScope scope; 4436 LocalContext env; 4437 4438 Local<String> obj = String::NewUndetectable("foo"); 4439 env->Global()->Set(v8_str("undetectable"), obj); 4440 env->Global()->Set(v8_str("detectable"), v8_str("bar")); 4441 4442 ExpectString( 4443 "function testBranch() {" 4444 " if (!%_IsUndetectableObject(undetectable)) throw 1;" 4445 " if (%_IsUndetectableObject(detectable)) throw 2;" 4446 "}\n" 4447 "function testBool() {" 4448 " var b1 = !%_IsUndetectableObject(undetectable);" 4449 " var b2 = %_IsUndetectableObject(detectable);" 4450 " if (b1) throw 3;" 4451 " if (b2) throw 4;" 4452 " return b1 == b2;" 4453 "}\n" 4454 "%OptimizeFunctionOnNextCall(testBranch);" 4455 "%OptimizeFunctionOnNextCall(testBool);" 4456 "for (var i = 0; i < 10; i++) {" 4457 " testBranch();" 4458 " testBool();" 4459 "}\n" 4460 "\"PASS\"", 4461 "PASS"); 4462} 4463 4464 4465template <typename T> static void USE(T) { } 4466 4467 4468// This test is not intended to be run, just type checked. 4469static inline void PersistentHandles() { 4470 USE(PersistentHandles); 4471 Local<String> str = v8_str("foo"); 4472 v8::Persistent<String> p_str = v8::Persistent<String>::New(str); 4473 USE(p_str); 4474 Local<Script> scr = Script::Compile(v8_str("")); 4475 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr); 4476 USE(p_scr); 4477 Local<ObjectTemplate> templ = ObjectTemplate::New(); 4478 v8::Persistent<ObjectTemplate> p_templ = 4479 v8::Persistent<ObjectTemplate>::New(templ); 4480 USE(p_templ); 4481} 4482 4483 4484static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) { 4485 ApiTestFuzzer::Fuzz(); 4486 return v8::Undefined(); 4487} 4488 4489 4490THREADED_TEST(GlobalObjectTemplate) { 4491 v8::HandleScope handle_scope; 4492 Local<ObjectTemplate> global_template = ObjectTemplate::New(); 4493 global_template->Set(v8_str("JSNI_Log"), 4494 v8::FunctionTemplate::New(HandleLogDelegator)); 4495 v8::Persistent<Context> context = Context::New(0, global_template); 4496 Context::Scope context_scope(context); 4497 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run(); 4498 context.Dispose(); 4499} 4500 4501 4502static const char* kSimpleExtensionSource = 4503 "function Foo() {" 4504 " return 4;" 4505 "}"; 4506 4507 4508THREADED_TEST(SimpleExtensions) { 4509 v8::HandleScope handle_scope; 4510 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource)); 4511 const char* extension_names[] = { "simpletest" }; 4512 v8::ExtensionConfiguration extensions(1, extension_names); 4513 v8::Handle<Context> context = Context::New(&extensions); 4514 Context::Scope lock(context); 4515 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 4516 CHECK_EQ(result, v8::Integer::New(4)); 4517} 4518 4519 4520static const char* kEmbeddedExtensionSource = 4521 "function Ret54321(){return 54321;}~~@@$" 4522 "$%% THIS IS A SERIES OF NON-NULL-TERMINATED STRINGS."; 4523static const int kEmbeddedExtensionSourceValidLen = 34; 4524 4525 4526THREADED_TEST(ExtensionMissingSourceLength) { 4527 v8::HandleScope handle_scope; 4528 v8::RegisterExtension(new Extension("srclentest_fail", 4529 kEmbeddedExtensionSource)); 4530 const char* extension_names[] = { "srclentest_fail" }; 4531 v8::ExtensionConfiguration extensions(1, extension_names); 4532 v8::Handle<Context> context = Context::New(&extensions); 4533 CHECK_EQ(0, *context); 4534} 4535 4536 4537THREADED_TEST(ExtensionWithSourceLength) { 4538 for (int source_len = kEmbeddedExtensionSourceValidLen - 1; 4539 source_len <= kEmbeddedExtensionSourceValidLen + 1; ++source_len) { 4540 v8::HandleScope handle_scope; 4541 i::ScopedVector<char> extension_name(32); 4542 i::OS::SNPrintF(extension_name, "ext #%d", source_len); 4543 v8::RegisterExtension(new Extension(extension_name.start(), 4544 kEmbeddedExtensionSource, 0, 0, 4545 source_len)); 4546 const char* extension_names[1] = { extension_name.start() }; 4547 v8::ExtensionConfiguration extensions(1, extension_names); 4548 v8::Handle<Context> context = Context::New(&extensions); 4549 if (source_len == kEmbeddedExtensionSourceValidLen) { 4550 Context::Scope lock(context); 4551 v8::Handle<Value> result = Script::Compile(v8_str("Ret54321()"))->Run(); 4552 CHECK_EQ(v8::Integer::New(54321), result); 4553 } else { 4554 // Anything but exactly the right length should fail to compile. 4555 CHECK_EQ(0, *context); 4556 } 4557 } 4558} 4559 4560 4561static const char* kEvalExtensionSource1 = 4562 "function UseEval1() {" 4563 " var x = 42;" 4564 " return eval('x');" 4565 "}"; 4566 4567 4568static const char* kEvalExtensionSource2 = 4569 "(function() {" 4570 " var x = 42;" 4571 " function e() {" 4572 " return eval('x');" 4573 " }" 4574 " this.UseEval2 = e;" 4575 "})()"; 4576 4577 4578THREADED_TEST(UseEvalFromExtension) { 4579 v8::HandleScope handle_scope; 4580 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1)); 4581 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2)); 4582 const char* extension_names[] = { "evaltest1", "evaltest2" }; 4583 v8::ExtensionConfiguration extensions(2, extension_names); 4584 v8::Handle<Context> context = Context::New(&extensions); 4585 Context::Scope lock(context); 4586 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run(); 4587 CHECK_EQ(result, v8::Integer::New(42)); 4588 result = Script::Compile(v8_str("UseEval2()"))->Run(); 4589 CHECK_EQ(result, v8::Integer::New(42)); 4590} 4591 4592 4593static const char* kWithExtensionSource1 = 4594 "function UseWith1() {" 4595 " var x = 42;" 4596 " with({x:87}) { return x; }" 4597 "}"; 4598 4599 4600 4601static const char* kWithExtensionSource2 = 4602 "(function() {" 4603 " var x = 42;" 4604 " function e() {" 4605 " with ({x:87}) { return x; }" 4606 " }" 4607 " this.UseWith2 = e;" 4608 "})()"; 4609 4610 4611THREADED_TEST(UseWithFromExtension) { 4612 v8::HandleScope handle_scope; 4613 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1)); 4614 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2)); 4615 const char* extension_names[] = { "withtest1", "withtest2" }; 4616 v8::ExtensionConfiguration extensions(2, extension_names); 4617 v8::Handle<Context> context = Context::New(&extensions); 4618 Context::Scope lock(context); 4619 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run(); 4620 CHECK_EQ(result, v8::Integer::New(87)); 4621 result = Script::Compile(v8_str("UseWith2()"))->Run(); 4622 CHECK_EQ(result, v8::Integer::New(87)); 4623} 4624 4625 4626THREADED_TEST(AutoExtensions) { 4627 v8::HandleScope handle_scope; 4628 Extension* extension = new Extension("autotest", kSimpleExtensionSource); 4629 extension->set_auto_enable(true); 4630 v8::RegisterExtension(extension); 4631 v8::Handle<Context> context = Context::New(); 4632 Context::Scope lock(context); 4633 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 4634 CHECK_EQ(result, v8::Integer::New(4)); 4635} 4636 4637 4638static const char* kSyntaxErrorInExtensionSource = 4639 "["; 4640 4641 4642// Test that a syntax error in an extension does not cause a fatal 4643// error but results in an empty context. 4644THREADED_TEST(SyntaxErrorExtensions) { 4645 v8::HandleScope handle_scope; 4646 v8::RegisterExtension(new Extension("syntaxerror", 4647 kSyntaxErrorInExtensionSource)); 4648 const char* extension_names[] = { "syntaxerror" }; 4649 v8::ExtensionConfiguration extensions(1, extension_names); 4650 v8::Handle<Context> context = Context::New(&extensions); 4651 CHECK(context.IsEmpty()); 4652} 4653 4654 4655static const char* kExceptionInExtensionSource = 4656 "throw 42"; 4657 4658 4659// Test that an exception when installing an extension does not cause 4660// a fatal error but results in an empty context. 4661THREADED_TEST(ExceptionExtensions) { 4662 v8::HandleScope handle_scope; 4663 v8::RegisterExtension(new Extension("exception", 4664 kExceptionInExtensionSource)); 4665 const char* extension_names[] = { "exception" }; 4666 v8::ExtensionConfiguration extensions(1, extension_names); 4667 v8::Handle<Context> context = Context::New(&extensions); 4668 CHECK(context.IsEmpty()); 4669} 4670 4671 4672static const char* kNativeCallInExtensionSource = 4673 "function call_runtime_last_index_of(x) {" 4674 " return %StringLastIndexOf(x, 'bob', 10);" 4675 "}"; 4676 4677 4678static const char* kNativeCallTest = 4679 "call_runtime_last_index_of('bobbobboellebobboellebobbob');"; 4680 4681// Test that a native runtime calls are supported in extensions. 4682THREADED_TEST(NativeCallInExtensions) { 4683 v8::HandleScope handle_scope; 4684 v8::RegisterExtension(new Extension("nativecall", 4685 kNativeCallInExtensionSource)); 4686 const char* extension_names[] = { "nativecall" }; 4687 v8::ExtensionConfiguration extensions(1, extension_names); 4688 v8::Handle<Context> context = Context::New(&extensions); 4689 Context::Scope lock(context); 4690 v8::Handle<Value> result = Script::Compile(v8_str(kNativeCallTest))->Run(); 4691 CHECK_EQ(result, v8::Integer::New(3)); 4692} 4693 4694 4695class NativeFunctionExtension : public Extension { 4696 public: 4697 NativeFunctionExtension(const char* name, 4698 const char* source, 4699 v8::InvocationCallback fun = &Echo) 4700 : Extension(name, source), 4701 function_(fun) { } 4702 4703 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( 4704 v8::Handle<v8::String> name) { 4705 return v8::FunctionTemplate::New(function_); 4706 } 4707 4708 static v8::Handle<v8::Value> Echo(const v8::Arguments& args) { 4709 if (args.Length() >= 1) return (args[0]); 4710 return v8::Undefined(); 4711 } 4712 private: 4713 v8::InvocationCallback function_; 4714}; 4715 4716 4717THREADED_TEST(NativeFunctionDeclaration) { 4718 v8::HandleScope handle_scope; 4719 const char* name = "nativedecl"; 4720 v8::RegisterExtension(new NativeFunctionExtension(name, 4721 "native function foo();")); 4722 const char* extension_names[] = { name }; 4723 v8::ExtensionConfiguration extensions(1, extension_names); 4724 v8::Handle<Context> context = Context::New(&extensions); 4725 Context::Scope lock(context); 4726 v8::Handle<Value> result = Script::Compile(v8_str("foo(42);"))->Run(); 4727 CHECK_EQ(result, v8::Integer::New(42)); 4728} 4729 4730 4731THREADED_TEST(NativeFunctionDeclarationError) { 4732 v8::HandleScope handle_scope; 4733 const char* name = "nativedeclerr"; 4734 // Syntax error in extension code. 4735 v8::RegisterExtension(new NativeFunctionExtension(name, 4736 "native\nfunction foo();")); 4737 const char* extension_names[] = { name }; 4738 v8::ExtensionConfiguration extensions(1, extension_names); 4739 v8::Handle<Context> context(Context::New(&extensions)); 4740 ASSERT(context.IsEmpty()); 4741} 4742 4743THREADED_TEST(NativeFunctionDeclarationErrorEscape) { 4744 v8::HandleScope handle_scope; 4745 const char* name = "nativedeclerresc"; 4746 // Syntax error in extension code - escape code in "native" means that 4747 // it's not treated as a keyword. 4748 v8::RegisterExtension(new NativeFunctionExtension( 4749 name, 4750 "nativ\\u0065 function foo();")); 4751 const char* extension_names[] = { name }; 4752 v8::ExtensionConfiguration extensions(1, extension_names); 4753 v8::Handle<Context> context(Context::New(&extensions)); 4754 ASSERT(context.IsEmpty()); 4755} 4756 4757 4758static void CheckDependencies(const char* name, const char* expected) { 4759 v8::HandleScope handle_scope; 4760 v8::ExtensionConfiguration config(1, &name); 4761 LocalContext context(&config); 4762 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded"))); 4763} 4764 4765 4766/* 4767 * Configuration: 4768 * 4769 * /-- B <--\ 4770 * A <- -- D <-- E 4771 * \-- C <--/ 4772 */ 4773THREADED_TEST(ExtensionDependency) { 4774 static const char* kEDeps[] = { "D" }; 4775 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps)); 4776 static const char* kDDeps[] = { "B", "C" }; 4777 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps)); 4778 static const char* kBCDeps[] = { "A" }; 4779 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps)); 4780 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps)); 4781 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';")); 4782 CheckDependencies("A", "undefinedA"); 4783 CheckDependencies("B", "undefinedAB"); 4784 CheckDependencies("C", "undefinedAC"); 4785 CheckDependencies("D", "undefinedABCD"); 4786 CheckDependencies("E", "undefinedABCDE"); 4787 v8::HandleScope handle_scope; 4788 static const char* exts[2] = { "C", "E" }; 4789 v8::ExtensionConfiguration config(2, exts); 4790 LocalContext context(&config); 4791 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded"))); 4792} 4793 4794 4795static const char* kExtensionTestScript = 4796 "native function A();" 4797 "native function B();" 4798 "native function C();" 4799 "function Foo(i) {" 4800 " if (i == 0) return A();" 4801 " if (i == 1) return B();" 4802 " if (i == 2) return C();" 4803 "}"; 4804 4805 4806static v8::Handle<Value> CallFun(const v8::Arguments& args) { 4807 ApiTestFuzzer::Fuzz(); 4808 if (args.IsConstructCall()) { 4809 args.This()->Set(v8_str("data"), args.Data()); 4810 return v8::Null(); 4811 } 4812 return args.Data(); 4813} 4814 4815 4816class FunctionExtension : public Extension { 4817 public: 4818 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { } 4819 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( 4820 v8::Handle<String> name); 4821}; 4822 4823 4824static int lookup_count = 0; 4825v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction( 4826 v8::Handle<String> name) { 4827 lookup_count++; 4828 if (name->Equals(v8_str("A"))) { 4829 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8)); 4830 } else if (name->Equals(v8_str("B"))) { 4831 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7)); 4832 } else if (name->Equals(v8_str("C"))) { 4833 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6)); 4834 } else { 4835 return v8::Handle<v8::FunctionTemplate>(); 4836 } 4837} 4838 4839 4840THREADED_TEST(FunctionLookup) { 4841 v8::RegisterExtension(new FunctionExtension()); 4842 v8::HandleScope handle_scope; 4843 static const char* exts[1] = { "functiontest" }; 4844 v8::ExtensionConfiguration config(1, exts); 4845 LocalContext context(&config); 4846 CHECK_EQ(3, lookup_count); 4847 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run()); 4848 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run()); 4849 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run()); 4850} 4851 4852 4853THREADED_TEST(NativeFunctionConstructCall) { 4854 v8::RegisterExtension(new FunctionExtension()); 4855 v8::HandleScope handle_scope; 4856 static const char* exts[1] = { "functiontest" }; 4857 v8::ExtensionConfiguration config(1, exts); 4858 LocalContext context(&config); 4859 for (int i = 0; i < 10; i++) { 4860 // Run a few times to ensure that allocation of objects doesn't 4861 // change behavior of a constructor function. 4862 CHECK_EQ(v8::Integer::New(8), 4863 Script::Compile(v8_str("(new A()).data"))->Run()); 4864 CHECK_EQ(v8::Integer::New(7), 4865 Script::Compile(v8_str("(new B()).data"))->Run()); 4866 CHECK_EQ(v8::Integer::New(6), 4867 Script::Compile(v8_str("(new C()).data"))->Run()); 4868 } 4869} 4870 4871 4872static const char* last_location; 4873static const char* last_message; 4874void StoringErrorCallback(const char* location, const char* message) { 4875 if (last_location == NULL) { 4876 last_location = location; 4877 last_message = message; 4878 } 4879} 4880 4881 4882// ErrorReporting creates a circular extensions configuration and 4883// tests that the fatal error handler gets called. This renders V8 4884// unusable and therefore this test cannot be run in parallel. 4885TEST(ErrorReporting) { 4886 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 4887 static const char* aDeps[] = { "B" }; 4888 v8::RegisterExtension(new Extension("A", "", 1, aDeps)); 4889 static const char* bDeps[] = { "A" }; 4890 v8::RegisterExtension(new Extension("B", "", 1, bDeps)); 4891 last_location = NULL; 4892 v8::ExtensionConfiguration config(1, bDeps); 4893 v8::Handle<Context> context = Context::New(&config); 4894 CHECK(context.IsEmpty()); 4895 CHECK_NE(last_location, NULL); 4896} 4897 4898 4899static const char* js_code_causing_huge_string_flattening = 4900 "var str = 'X';" 4901 "for (var i = 0; i < 30; i++) {" 4902 " str = str + str;" 4903 "}" 4904 "str.match(/X/);"; 4905 4906 4907void OOMCallback(const char* location, const char* message) { 4908 exit(0); 4909} 4910 4911 4912TEST(RegexpOutOfMemory) { 4913 // Execute a script that causes out of memory when flattening a string. 4914 v8::HandleScope scope; 4915 v8::V8::SetFatalErrorHandler(OOMCallback); 4916 LocalContext context; 4917 Local<Script> script = 4918 Script::Compile(String::New(js_code_causing_huge_string_flattening)); 4919 last_location = NULL; 4920 Local<Value> result(script->Run()); 4921 4922 CHECK(false); // Should not return. 4923} 4924 4925 4926static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message, 4927 v8::Handle<Value> data) { 4928 CHECK_EQ(v8::Undefined(), data); 4929 CHECK(message->GetScriptResourceName()->IsUndefined()); 4930 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName()); 4931 message->GetLineNumber(); 4932 message->GetSourceLine(); 4933} 4934 4935 4936THREADED_TEST(ErrorWithMissingScriptInfo) { 4937 v8::HandleScope scope; 4938 LocalContext context; 4939 v8::V8::AddMessageListener(MissingScriptInfoMessageListener); 4940 Script::Compile(v8_str("throw Error()"))->Run(); 4941 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener); 4942} 4943 4944 4945int global_index = 0; 4946 4947class Snorkel { 4948 public: 4949 Snorkel() { index_ = global_index++; } 4950 int index_; 4951}; 4952 4953class Whammy { 4954 public: 4955 Whammy() { 4956 cursor_ = 0; 4957 } 4958 ~Whammy() { 4959 script_.Dispose(); 4960 } 4961 v8::Handle<Script> getScript() { 4962 if (script_.IsEmpty()) 4963 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo")); 4964 return Local<Script>(*script_); 4965 } 4966 4967 public: 4968 static const int kObjectCount = 256; 4969 int cursor_; 4970 v8::Persistent<v8::Object> objects_[kObjectCount]; 4971 v8::Persistent<Script> script_; 4972}; 4973 4974static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) { 4975 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data); 4976 delete snorkel; 4977 obj.ClearWeak(); 4978} 4979 4980v8::Handle<Value> WhammyPropertyGetter(Local<String> name, 4981 const AccessorInfo& info) { 4982 Whammy* whammy = 4983 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); 4984 4985 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_]; 4986 4987 v8::Handle<v8::Object> obj = v8::Object::New(); 4988 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj); 4989 if (!prev.IsEmpty()) { 4990 prev->Set(v8_str("next"), obj); 4991 prev.MakeWeak(new Snorkel(), &HandleWeakReference); 4992 whammy->objects_[whammy->cursor_].Clear(); 4993 } 4994 whammy->objects_[whammy->cursor_] = global; 4995 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount; 4996 return whammy->getScript()->Run(); 4997} 4998 4999THREADED_TEST(WeakReference) { 5000 v8::HandleScope handle_scope; 5001 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New(); 5002 Whammy* whammy = new Whammy(); 5003 templ->SetNamedPropertyHandler(WhammyPropertyGetter, 5004 0, 0, 0, 0, 5005 v8::External::New(whammy)); 5006 const char* extension_list[] = { "v8/gc" }; 5007 v8::ExtensionConfiguration extensions(1, extension_list); 5008 v8::Persistent<Context> context = Context::New(&extensions); 5009 Context::Scope context_scope(context); 5010 5011 v8::Handle<v8::Object> interceptor = templ->NewInstance(); 5012 context->Global()->Set(v8_str("whammy"), interceptor); 5013 const char* code = 5014 "var last;" 5015 "for (var i = 0; i < 10000; i++) {" 5016 " var obj = whammy.length;" 5017 " if (last) last.next = obj;" 5018 " last = obj;" 5019 "}" 5020 "gc();" 5021 "4"; 5022 v8::Handle<Value> result = CompileRun(code); 5023 CHECK_EQ(4.0, result->NumberValue()); 5024 delete whammy; 5025 context.Dispose(); 5026} 5027 5028 5029static void DisposeAndSetFlag(v8::Persistent<v8::Value> obj, void* data) { 5030 obj.Dispose(); 5031 obj.Clear(); 5032 *(reinterpret_cast<bool*>(data)) = true; 5033} 5034 5035 5036THREADED_TEST(IndependentWeakHandle) { 5037 v8::Persistent<Context> context = Context::New(); 5038 Context::Scope context_scope(context); 5039 5040 v8::Persistent<v8::Object> object_a; 5041 5042 { 5043 v8::HandleScope handle_scope; 5044 object_a = v8::Persistent<v8::Object>::New(v8::Object::New()); 5045 } 5046 5047 bool object_a_disposed = false; 5048 object_a.MakeWeak(&object_a_disposed, &DisposeAndSetFlag); 5049 object_a.MarkIndependent(); 5050 HEAP->PerformScavenge(); 5051 CHECK(object_a_disposed); 5052} 5053 5054 5055static void InvokeScavenge() { 5056 HEAP->PerformScavenge(); 5057} 5058 5059 5060static void InvokeMarkSweep() { 5061 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 5062} 5063 5064 5065static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) { 5066 obj.Dispose(); 5067 obj.Clear(); 5068 *(reinterpret_cast<bool*>(data)) = true; 5069 InvokeScavenge(); 5070} 5071 5072 5073static void ForceMarkSweep(v8::Persistent<v8::Value> obj, void* data) { 5074 obj.Dispose(); 5075 obj.Clear(); 5076 *(reinterpret_cast<bool*>(data)) = true; 5077 InvokeMarkSweep(); 5078} 5079 5080 5081THREADED_TEST(GCFromWeakCallbacks) { 5082 v8::Persistent<Context> context = Context::New(); 5083 Context::Scope context_scope(context); 5084 5085 static const int kNumberOfGCTypes = 2; 5086 v8::WeakReferenceCallback gc_forcing_callback[kNumberOfGCTypes] = 5087 {&ForceScavenge, &ForceMarkSweep}; 5088 5089 typedef void (*GCInvoker)(); 5090 GCInvoker invoke_gc[kNumberOfGCTypes] = {&InvokeScavenge, &InvokeMarkSweep}; 5091 5092 for (int outer_gc = 0; outer_gc < kNumberOfGCTypes; outer_gc++) { 5093 for (int inner_gc = 0; inner_gc < kNumberOfGCTypes; inner_gc++) { 5094 v8::Persistent<v8::Object> object; 5095 { 5096 v8::HandleScope handle_scope; 5097 object = v8::Persistent<v8::Object>::New(v8::Object::New()); 5098 } 5099 bool disposed = false; 5100 object.MakeWeak(&disposed, gc_forcing_callback[inner_gc]); 5101 object.MarkIndependent(); 5102 invoke_gc[outer_gc](); 5103 CHECK(disposed); 5104 } 5105 } 5106} 5107 5108 5109static void RevivingCallback(v8::Persistent<v8::Value> obj, void* data) { 5110 obj.ClearWeak(); 5111 *(reinterpret_cast<bool*>(data)) = true; 5112} 5113 5114 5115THREADED_TEST(IndependentHandleRevival) { 5116 v8::Persistent<Context> context = Context::New(); 5117 Context::Scope context_scope(context); 5118 5119 v8::Persistent<v8::Object> object; 5120 { 5121 v8::HandleScope handle_scope; 5122 object = v8::Persistent<v8::Object>::New(v8::Object::New()); 5123 object->Set(v8_str("x"), v8::Integer::New(1)); 5124 v8::Local<String> y_str = v8_str("y"); 5125 object->Set(y_str, y_str); 5126 } 5127 bool revived = false; 5128 object.MakeWeak(&revived, &RevivingCallback); 5129 object.MarkIndependent(); 5130 HEAP->PerformScavenge(); 5131 CHECK(revived); 5132 HEAP->CollectAllGarbage(true); 5133 { 5134 v8::HandleScope handle_scope; 5135 v8::Local<String> y_str = v8_str("y"); 5136 CHECK_EQ(v8::Integer::New(1), object->Get(v8_str("x"))); 5137 CHECK(object->Get(y_str)->Equals(y_str)); 5138 } 5139} 5140 5141 5142v8::Handle<Function> args_fun; 5143 5144 5145static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) { 5146 ApiTestFuzzer::Fuzz(); 5147 CHECK_EQ(args_fun, args.Callee()); 5148 CHECK_EQ(3, args.Length()); 5149 CHECK_EQ(v8::Integer::New(1), args[0]); 5150 CHECK_EQ(v8::Integer::New(2), args[1]); 5151 CHECK_EQ(v8::Integer::New(3), args[2]); 5152 CHECK_EQ(v8::Undefined(), args[3]); 5153 v8::HandleScope scope; 5154 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 5155 return v8::Undefined(); 5156} 5157 5158 5159THREADED_TEST(Arguments) { 5160 v8::HandleScope scope; 5161 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 5162 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback)); 5163 LocalContext context(NULL, global); 5164 args_fun = context->Global()->Get(v8_str("f")).As<Function>(); 5165 v8_compile("f(1, 2, 3)")->Run(); 5166} 5167 5168 5169static v8::Handle<Value> NoBlockGetterX(Local<String> name, 5170 const AccessorInfo&) { 5171 return v8::Handle<Value>(); 5172} 5173 5174 5175static v8::Handle<Value> NoBlockGetterI(uint32_t index, 5176 const AccessorInfo&) { 5177 return v8::Handle<Value>(); 5178} 5179 5180 5181static v8::Handle<v8::Boolean> PDeleter(Local<String> name, 5182 const AccessorInfo&) { 5183 if (!name->Equals(v8_str("foo"))) { 5184 return v8::Handle<v8::Boolean>(); // not intercepted 5185 } 5186 5187 return v8::False(); // intercepted, and don't delete the property 5188} 5189 5190 5191static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) { 5192 if (index != 2) { 5193 return v8::Handle<v8::Boolean>(); // not intercepted 5194 } 5195 5196 return v8::False(); // intercepted, and don't delete the property 5197} 5198 5199 5200THREADED_TEST(Deleter) { 5201 v8::HandleScope scope; 5202 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 5203 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL); 5204 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL); 5205 LocalContext context; 5206 context->Global()->Set(v8_str("k"), obj->NewInstance()); 5207 CompileRun( 5208 "k.foo = 'foo';" 5209 "k.bar = 'bar';" 5210 "k[2] = 2;" 5211 "k[4] = 4;"); 5212 CHECK(v8_compile("delete k.foo")->Run()->IsFalse()); 5213 CHECK(v8_compile("delete k.bar")->Run()->IsTrue()); 5214 5215 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo")); 5216 CHECK(v8_compile("k.bar")->Run()->IsUndefined()); 5217 5218 CHECK(v8_compile("delete k[2]")->Run()->IsFalse()); 5219 CHECK(v8_compile("delete k[4]")->Run()->IsTrue()); 5220 5221 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2)); 5222 CHECK(v8_compile("k[4]")->Run()->IsUndefined()); 5223} 5224 5225 5226static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) { 5227 ApiTestFuzzer::Fuzz(); 5228 if (name->Equals(v8_str("foo")) || 5229 name->Equals(v8_str("bar")) || 5230 name->Equals(v8_str("baz"))) { 5231 return v8::Undefined(); 5232 } 5233 return v8::Handle<Value>(); 5234} 5235 5236 5237static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) { 5238 ApiTestFuzzer::Fuzz(); 5239 if (index == 0 || index == 1) return v8::Undefined(); 5240 return v8::Handle<Value>(); 5241} 5242 5243 5244static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) { 5245 ApiTestFuzzer::Fuzz(); 5246 v8::Handle<v8::Array> result = v8::Array::New(3); 5247 result->Set(v8::Integer::New(0), v8_str("foo")); 5248 result->Set(v8::Integer::New(1), v8_str("bar")); 5249 result->Set(v8::Integer::New(2), v8_str("baz")); 5250 return result; 5251} 5252 5253 5254static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) { 5255 ApiTestFuzzer::Fuzz(); 5256 v8::Handle<v8::Array> result = v8::Array::New(2); 5257 result->Set(v8::Integer::New(0), v8_str("0")); 5258 result->Set(v8::Integer::New(1), v8_str("1")); 5259 return result; 5260} 5261 5262 5263THREADED_TEST(Enumerators) { 5264 v8::HandleScope scope; 5265 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 5266 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum); 5267 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum); 5268 LocalContext context; 5269 context->Global()->Set(v8_str("k"), obj->NewInstance()); 5270 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 5271 "k[10] = 0;" 5272 "k.a = 0;" 5273 "k[5] = 0;" 5274 "k.b = 0;" 5275 "k[4294967295] = 0;" 5276 "k.c = 0;" 5277 "k[4294967296] = 0;" 5278 "k.d = 0;" 5279 "k[140000] = 0;" 5280 "k.e = 0;" 5281 "k[30000000000] = 0;" 5282 "k.f = 0;" 5283 "var result = [];" 5284 "for (var prop in k) {" 5285 " result.push(prop);" 5286 "}" 5287 "result")); 5288 // Check that we get all the property names returned including the 5289 // ones from the enumerators in the right order: indexed properties 5290 // in numerical order, indexed interceptor properties, named 5291 // properties in insertion order, named interceptor properties. 5292 // This order is not mandated by the spec, so this test is just 5293 // documenting our behavior. 5294 CHECK_EQ(17, result->Length()); 5295 // Indexed properties in numerical order. 5296 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0))); 5297 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1))); 5298 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2))); 5299 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3))); 5300 // Indexed interceptor properties in the order they are returned 5301 // from the enumerator interceptor. 5302 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4))); 5303 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5))); 5304 // Named properties in insertion order. 5305 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6))); 5306 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7))); 5307 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8))); 5308 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9))); 5309 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10))); 5310 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11))); 5311 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12))); 5312 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13))); 5313 // Named interceptor properties. 5314 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14))); 5315 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15))); 5316 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16))); 5317} 5318 5319 5320int p_getter_count; 5321int p_getter_count2; 5322 5323 5324static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) { 5325 ApiTestFuzzer::Fuzz(); 5326 p_getter_count++; 5327 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 5328 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 5329 if (name->Equals(v8_str("p1"))) { 5330 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 5331 } else if (name->Equals(v8_str("p2"))) { 5332 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 5333 } else if (name->Equals(v8_str("p3"))) { 5334 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 5335 } else if (name->Equals(v8_str("p4"))) { 5336 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 5337 } 5338 return v8::Undefined(); 5339} 5340 5341 5342static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) { 5343 ApiTestFuzzer::Fuzz(); 5344 LocalContext context; 5345 context->Global()->Set(v8_str("o1"), obj->NewInstance()); 5346 CompileRun( 5347 "o1.__proto__ = { };" 5348 "var o2 = { __proto__: o1 };" 5349 "var o3 = { __proto__: o2 };" 5350 "var o4 = { __proto__: o3 };" 5351 "for (var i = 0; i < 10; i++) o4.p4;" 5352 "for (var i = 0; i < 10; i++) o3.p3;" 5353 "for (var i = 0; i < 10; i++) o2.p2;" 5354 "for (var i = 0; i < 10; i++) o1.p1;"); 5355} 5356 5357 5358static v8::Handle<Value> PGetter2(Local<String> name, 5359 const AccessorInfo& info) { 5360 ApiTestFuzzer::Fuzz(); 5361 p_getter_count2++; 5362 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 5363 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 5364 if (name->Equals(v8_str("p1"))) { 5365 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 5366 } else if (name->Equals(v8_str("p2"))) { 5367 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 5368 } else if (name->Equals(v8_str("p3"))) { 5369 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 5370 } else if (name->Equals(v8_str("p4"))) { 5371 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 5372 } 5373 return v8::Undefined(); 5374} 5375 5376 5377THREADED_TEST(GetterHolders) { 5378 v8::HandleScope scope; 5379 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 5380 obj->SetAccessor(v8_str("p1"), PGetter); 5381 obj->SetAccessor(v8_str("p2"), PGetter); 5382 obj->SetAccessor(v8_str("p3"), PGetter); 5383 obj->SetAccessor(v8_str("p4"), PGetter); 5384 p_getter_count = 0; 5385 RunHolderTest(obj); 5386 CHECK_EQ(40, p_getter_count); 5387} 5388 5389 5390THREADED_TEST(PreInterceptorHolders) { 5391 v8::HandleScope scope; 5392 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 5393 obj->SetNamedPropertyHandler(PGetter2); 5394 p_getter_count2 = 0; 5395 RunHolderTest(obj); 5396 CHECK_EQ(40, p_getter_count2); 5397} 5398 5399 5400THREADED_TEST(ObjectInstantiation) { 5401 v8::HandleScope scope; 5402 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5403 templ->SetAccessor(v8_str("t"), PGetter2); 5404 LocalContext context; 5405 context->Global()->Set(v8_str("o"), templ->NewInstance()); 5406 for (int i = 0; i < 100; i++) { 5407 v8::HandleScope inner_scope; 5408 v8::Handle<v8::Object> obj = templ->NewInstance(); 5409 CHECK_NE(obj, context->Global()->Get(v8_str("o"))); 5410 context->Global()->Set(v8_str("o2"), obj); 5411 v8::Handle<Value> value = 5412 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run(); 5413 CHECK_EQ(v8::True(), value); 5414 context->Global()->Set(v8_str("o"), obj); 5415 } 5416} 5417 5418 5419static int StrCmp16(uint16_t* a, uint16_t* b) { 5420 while (true) { 5421 if (*a == 0 && *b == 0) return 0; 5422 if (*a != *b) return 0 + *a - *b; 5423 a++; 5424 b++; 5425 } 5426} 5427 5428 5429static int StrNCmp16(uint16_t* a, uint16_t* b, int n) { 5430 while (true) { 5431 if (n-- == 0) return 0; 5432 if (*a == 0 && *b == 0) return 0; 5433 if (*a != *b) return 0 + *a - *b; 5434 a++; 5435 b++; 5436 } 5437} 5438 5439 5440THREADED_TEST(StringWrite) { 5441 LocalContext context; 5442 v8::HandleScope scope; 5443 v8::Handle<String> str = v8_str("abcde"); 5444 // abc<Icelandic eth><Unicode snowman>. 5445 v8::Handle<String> str2 = v8_str("abc\303\260\342\230\203"); 5446 const int kStride = 4; // Must match stride in for loops in JS below. 5447 CompileRun( 5448 "var left = '';" 5449 "for (var i = 0; i < 0xd800; i += 4) {" 5450 " left = left + String.fromCharCode(i);" 5451 "}"); 5452 CompileRun( 5453 "var right = '';" 5454 "for (var i = 0; i < 0xd800; i += 4) {" 5455 " right = String.fromCharCode(i) + right;" 5456 "}"); 5457 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 5458 Handle<String> left_tree = global->Get(v8_str("left")).As<String>(); 5459 Handle<String> right_tree = global->Get(v8_str("right")).As<String>(); 5460 5461 CHECK_EQ(5, str2->Length()); 5462 CHECK_EQ(0xd800 / kStride, left_tree->Length()); 5463 CHECK_EQ(0xd800 / kStride, right_tree->Length()); 5464 5465 char buf[100]; 5466 char utf8buf[0xd800 * 3]; 5467 uint16_t wbuf[100]; 5468 int len; 5469 int charlen; 5470 5471 memset(utf8buf, 0x1, 1000); 5472 len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen); 5473 CHECK_EQ(9, len); 5474 CHECK_EQ(5, charlen); 5475 CHECK_EQ(0, strcmp(utf8buf, "abc\303\260\342\230\203")); 5476 5477 memset(utf8buf, 0x1, 1000); 5478 len = str2->WriteUtf8(utf8buf, 8, &charlen); 5479 CHECK_EQ(8, len); 5480 CHECK_EQ(5, charlen); 5481 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\342\230\203\1", 9)); 5482 5483 memset(utf8buf, 0x1, 1000); 5484 len = str2->WriteUtf8(utf8buf, 7, &charlen); 5485 CHECK_EQ(5, len); 5486 CHECK_EQ(4, charlen); 5487 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); 5488 5489 memset(utf8buf, 0x1, 1000); 5490 len = str2->WriteUtf8(utf8buf, 6, &charlen); 5491 CHECK_EQ(5, len); 5492 CHECK_EQ(4, charlen); 5493 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); 5494 5495 memset(utf8buf, 0x1, 1000); 5496 len = str2->WriteUtf8(utf8buf, 5, &charlen); 5497 CHECK_EQ(5, len); 5498 CHECK_EQ(4, charlen); 5499 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\1", 5)); 5500 5501 memset(utf8buf, 0x1, 1000); 5502 len = str2->WriteUtf8(utf8buf, 4, &charlen); 5503 CHECK_EQ(3, len); 5504 CHECK_EQ(3, charlen); 5505 CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4)); 5506 5507 memset(utf8buf, 0x1, 1000); 5508 len = str2->WriteUtf8(utf8buf, 3, &charlen); 5509 CHECK_EQ(3, len); 5510 CHECK_EQ(3, charlen); 5511 CHECK_EQ(0, strncmp(utf8buf, "abc\1", 4)); 5512 5513 memset(utf8buf, 0x1, 1000); 5514 len = str2->WriteUtf8(utf8buf, 2, &charlen); 5515 CHECK_EQ(2, len); 5516 CHECK_EQ(2, charlen); 5517 CHECK_EQ(0, strncmp(utf8buf, "ab\1", 3)); 5518 5519 memset(utf8buf, 0x1, sizeof(utf8buf)); 5520 len = left_tree->Utf8Length(); 5521 int utf8_expected = 5522 (0x80 + (0x800 - 0x80) * 2 + (0xd800 - 0x800) * 3) / kStride; 5523 CHECK_EQ(utf8_expected, len); 5524 len = left_tree->WriteUtf8(utf8buf, utf8_expected, &charlen); 5525 CHECK_EQ(utf8_expected, len); 5526 CHECK_EQ(0xd800 / kStride, charlen); 5527 CHECK_EQ(0xed, static_cast<unsigned char>(utf8buf[utf8_expected - 3])); 5528 CHECK_EQ(0x9f, static_cast<unsigned char>(utf8buf[utf8_expected - 2])); 5529 CHECK_EQ(0xc0 - kStride, 5530 static_cast<unsigned char>(utf8buf[utf8_expected - 1])); 5531 CHECK_EQ(1, utf8buf[utf8_expected]); 5532 5533 memset(utf8buf, 0x1, sizeof(utf8buf)); 5534 len = right_tree->Utf8Length(); 5535 CHECK_EQ(utf8_expected, len); 5536 len = right_tree->WriteUtf8(utf8buf, utf8_expected, &charlen); 5537 CHECK_EQ(utf8_expected, len); 5538 CHECK_EQ(0xd800 / kStride, charlen); 5539 CHECK_EQ(0xed, static_cast<unsigned char>(utf8buf[0])); 5540 CHECK_EQ(0x9f, static_cast<unsigned char>(utf8buf[1])); 5541 CHECK_EQ(0xc0 - kStride, static_cast<unsigned char>(utf8buf[2])); 5542 CHECK_EQ(1, utf8buf[utf8_expected]); 5543 5544 memset(buf, 0x1, sizeof(buf)); 5545 memset(wbuf, 0x1, sizeof(wbuf)); 5546 len = str->WriteAscii(buf); 5547 CHECK_EQ(5, len); 5548 len = str->Write(wbuf); 5549 CHECK_EQ(5, len); 5550 CHECK_EQ(0, strcmp("abcde", buf)); 5551 uint16_t answer1[] = {'a', 'b', 'c', 'd', 'e', '\0'}; 5552 CHECK_EQ(0, StrCmp16(answer1, wbuf)); 5553 5554 memset(buf, 0x1, sizeof(buf)); 5555 memset(wbuf, 0x1, sizeof(wbuf)); 5556 len = str->WriteAscii(buf, 0, 4); 5557 CHECK_EQ(4, len); 5558 len = str->Write(wbuf, 0, 4); 5559 CHECK_EQ(4, len); 5560 CHECK_EQ(0, strncmp("abcd\1", buf, 5)); 5561 uint16_t answer2[] = {'a', 'b', 'c', 'd', 0x101}; 5562 CHECK_EQ(0, StrNCmp16(answer2, wbuf, 5)); 5563 5564 memset(buf, 0x1, sizeof(buf)); 5565 memset(wbuf, 0x1, sizeof(wbuf)); 5566 len = str->WriteAscii(buf, 0, 5); 5567 CHECK_EQ(5, len); 5568 len = str->Write(wbuf, 0, 5); 5569 CHECK_EQ(5, len); 5570 CHECK_EQ(0, strncmp("abcde\1", buf, 6)); 5571 uint16_t answer3[] = {'a', 'b', 'c', 'd', 'e', 0x101}; 5572 CHECK_EQ(0, StrNCmp16(answer3, wbuf, 6)); 5573 5574 memset(buf, 0x1, sizeof(buf)); 5575 memset(wbuf, 0x1, sizeof(wbuf)); 5576 len = str->WriteAscii(buf, 0, 6); 5577 CHECK_EQ(5, len); 5578 len = str->Write(wbuf, 0, 6); 5579 CHECK_EQ(5, len); 5580 CHECK_EQ(0, strcmp("abcde", buf)); 5581 uint16_t answer4[] = {'a', 'b', 'c', 'd', 'e', '\0'}; 5582 CHECK_EQ(0, StrCmp16(answer4, wbuf)); 5583 5584 memset(buf, 0x1, sizeof(buf)); 5585 memset(wbuf, 0x1, sizeof(wbuf)); 5586 len = str->WriteAscii(buf, 4, -1); 5587 CHECK_EQ(1, len); 5588 len = str->Write(wbuf, 4, -1); 5589 CHECK_EQ(1, len); 5590 CHECK_EQ(0, strcmp("e", buf)); 5591 uint16_t answer5[] = {'e', '\0'}; 5592 CHECK_EQ(0, StrCmp16(answer5, wbuf)); 5593 5594 memset(buf, 0x1, sizeof(buf)); 5595 memset(wbuf, 0x1, sizeof(wbuf)); 5596 len = str->WriteAscii(buf, 4, 6); 5597 CHECK_EQ(1, len); 5598 len = str->Write(wbuf, 4, 6); 5599 CHECK_EQ(1, len); 5600 CHECK_EQ(0, strcmp("e", buf)); 5601 CHECK_EQ(0, StrCmp16(answer5, wbuf)); 5602 5603 memset(buf, 0x1, sizeof(buf)); 5604 memset(wbuf, 0x1, sizeof(wbuf)); 5605 len = str->WriteAscii(buf, 4, 1); 5606 CHECK_EQ(1, len); 5607 len = str->Write(wbuf, 4, 1); 5608 CHECK_EQ(1, len); 5609 CHECK_EQ(0, strncmp("e\1", buf, 2)); 5610 uint16_t answer6[] = {'e', 0x101}; 5611 CHECK_EQ(0, StrNCmp16(answer6, wbuf, 2)); 5612 5613 memset(buf, 0x1, sizeof(buf)); 5614 memset(wbuf, 0x1, sizeof(wbuf)); 5615 len = str->WriteAscii(buf, 3, 1); 5616 CHECK_EQ(1, len); 5617 len = str->Write(wbuf, 3, 1); 5618 CHECK_EQ(1, len); 5619 CHECK_EQ(0, strncmp("d\1", buf, 2)); 5620 uint16_t answer7[] = {'d', 0x101}; 5621 CHECK_EQ(0, StrNCmp16(answer7, wbuf, 2)); 5622 5623 memset(wbuf, 0x1, sizeof(wbuf)); 5624 wbuf[5] = 'X'; 5625 len = str->Write(wbuf, 0, 6, String::NO_NULL_TERMINATION); 5626 CHECK_EQ(5, len); 5627 CHECK_EQ('X', wbuf[5]); 5628 uint16_t answer8a[] = {'a', 'b', 'c', 'd', 'e'}; 5629 uint16_t answer8b[] = {'a', 'b', 'c', 'd', 'e', '\0'}; 5630 CHECK_EQ(0, StrNCmp16(answer8a, wbuf, 5)); 5631 CHECK_NE(0, StrCmp16(answer8b, wbuf)); 5632 wbuf[5] = '\0'; 5633 CHECK_EQ(0, StrCmp16(answer8b, wbuf)); 5634 5635 memset(buf, 0x1, sizeof(buf)); 5636 buf[5] = 'X'; 5637 len = str->WriteAscii(buf, 0, 6, String::NO_NULL_TERMINATION); 5638 CHECK_EQ(5, len); 5639 CHECK_EQ('X', buf[5]); 5640 CHECK_EQ(0, strncmp("abcde", buf, 5)); 5641 CHECK_NE(0, strcmp("abcde", buf)); 5642 buf[5] = '\0'; 5643 CHECK_EQ(0, strcmp("abcde", buf)); 5644 5645 memset(utf8buf, 0x1, sizeof(utf8buf)); 5646 utf8buf[8] = 'X'; 5647 len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen, 5648 String::NO_NULL_TERMINATION); 5649 CHECK_EQ(8, len); 5650 CHECK_EQ('X', utf8buf[8]); 5651 CHECK_EQ(5, charlen); 5652 CHECK_EQ(0, strncmp(utf8buf, "abc\303\260\342\230\203", 8)); 5653 CHECK_NE(0, strcmp(utf8buf, "abc\303\260\342\230\203")); 5654 utf8buf[8] = '\0'; 5655 CHECK_EQ(0, strcmp(utf8buf, "abc\303\260\342\230\203")); 5656} 5657 5658 5659THREADED_TEST(ToArrayIndex) { 5660 v8::HandleScope scope; 5661 LocalContext context; 5662 5663 v8::Handle<String> str = v8_str("42"); 5664 v8::Handle<v8::Uint32> index = str->ToArrayIndex(); 5665 CHECK(!index.IsEmpty()); 5666 CHECK_EQ(42.0, index->Uint32Value()); 5667 str = v8_str("42asdf"); 5668 index = str->ToArrayIndex(); 5669 CHECK(index.IsEmpty()); 5670 str = v8_str("-42"); 5671 index = str->ToArrayIndex(); 5672 CHECK(index.IsEmpty()); 5673 str = v8_str("4294967295"); 5674 index = str->ToArrayIndex(); 5675 CHECK(!index.IsEmpty()); 5676 CHECK_EQ(4294967295.0, index->Uint32Value()); 5677 v8::Handle<v8::Number> num = v8::Number::New(1); 5678 index = num->ToArrayIndex(); 5679 CHECK(!index.IsEmpty()); 5680 CHECK_EQ(1.0, index->Uint32Value()); 5681 num = v8::Number::New(-1); 5682 index = num->ToArrayIndex(); 5683 CHECK(index.IsEmpty()); 5684 v8::Handle<v8::Object> obj = v8::Object::New(); 5685 index = obj->ToArrayIndex(); 5686 CHECK(index.IsEmpty()); 5687} 5688 5689 5690THREADED_TEST(ErrorConstruction) { 5691 v8::HandleScope scope; 5692 LocalContext context; 5693 5694 v8::Handle<String> foo = v8_str("foo"); 5695 v8::Handle<String> message = v8_str("message"); 5696 v8::Handle<Value> range_error = v8::Exception::RangeError(foo); 5697 CHECK(range_error->IsObject()); 5698 v8::Handle<v8::Object> range_obj(range_error.As<v8::Object>()); 5699 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo)); 5700 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo); 5701 CHECK(reference_error->IsObject()); 5702 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo)); 5703 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo); 5704 CHECK(syntax_error->IsObject()); 5705 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo)); 5706 v8::Handle<Value> type_error = v8::Exception::TypeError(foo); 5707 CHECK(type_error->IsObject()); 5708 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo)); 5709 v8::Handle<Value> error = v8::Exception::Error(foo); 5710 CHECK(error->IsObject()); 5711 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo)); 5712} 5713 5714 5715static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) { 5716 ApiTestFuzzer::Fuzz(); 5717 return v8_num(10); 5718} 5719 5720 5721static void YSetter(Local<String> name, 5722 Local<Value> value, 5723 const AccessorInfo& info) { 5724 if (info.This()->Has(name)) { 5725 info.This()->Delete(name); 5726 } 5727 info.This()->Set(name, value); 5728} 5729 5730 5731THREADED_TEST(DeleteAccessor) { 5732 v8::HandleScope scope; 5733 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 5734 obj->SetAccessor(v8_str("y"), YGetter, YSetter); 5735 LocalContext context; 5736 v8::Handle<v8::Object> holder = obj->NewInstance(); 5737 context->Global()->Set(v8_str("holder"), holder); 5738 v8::Handle<Value> result = CompileRun( 5739 "holder.y = 11; holder.y = 12; holder.y"); 5740 CHECK_EQ(12, result->Uint32Value()); 5741} 5742 5743 5744THREADED_TEST(TypeSwitch) { 5745 v8::HandleScope scope; 5746 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New(); 5747 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New(); 5748 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New(); 5749 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 }; 5750 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs); 5751 LocalContext context; 5752 v8::Handle<v8::Object> obj0 = v8::Object::New(); 5753 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance(); 5754 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance(); 5755 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance(); 5756 for (int i = 0; i < 10; i++) { 5757 CHECK_EQ(0, type_switch->match(obj0)); 5758 CHECK_EQ(1, type_switch->match(obj1)); 5759 CHECK_EQ(2, type_switch->match(obj2)); 5760 CHECK_EQ(3, type_switch->match(obj3)); 5761 CHECK_EQ(3, type_switch->match(obj3)); 5762 CHECK_EQ(2, type_switch->match(obj2)); 5763 CHECK_EQ(1, type_switch->match(obj1)); 5764 CHECK_EQ(0, type_switch->match(obj0)); 5765 } 5766} 5767 5768 5769// For use within the TestSecurityHandler() test. 5770static bool g_security_callback_result = false; 5771static bool NamedSecurityTestCallback(Local<v8::Object> global, 5772 Local<Value> name, 5773 v8::AccessType type, 5774 Local<Value> data) { 5775 // Always allow read access. 5776 if (type == v8::ACCESS_GET) 5777 return true; 5778 5779 // Sometimes allow other access. 5780 return g_security_callback_result; 5781} 5782 5783 5784static bool IndexedSecurityTestCallback(Local<v8::Object> global, 5785 uint32_t key, 5786 v8::AccessType type, 5787 Local<Value> data) { 5788 // Always allow read access. 5789 if (type == v8::ACCESS_GET) 5790 return true; 5791 5792 // Sometimes allow other access. 5793 return g_security_callback_result; 5794} 5795 5796 5797static int trouble_nesting = 0; 5798static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) { 5799 ApiTestFuzzer::Fuzz(); 5800 trouble_nesting++; 5801 5802 // Call a JS function that throws an uncaught exception. 5803 Local<v8::Object> arg_this = Context::GetCurrent()->Global(); 5804 Local<Value> trouble_callee = (trouble_nesting == 3) ? 5805 arg_this->Get(v8_str("trouble_callee")) : 5806 arg_this->Get(v8_str("trouble_caller")); 5807 CHECK(trouble_callee->IsFunction()); 5808 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL); 5809} 5810 5811 5812static int report_count = 0; 5813static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>, 5814 v8::Handle<Value>) { 5815 report_count++; 5816} 5817 5818 5819// Counts uncaught exceptions, but other tests running in parallel 5820// also have uncaught exceptions. 5821TEST(ApiUncaughtException) { 5822 report_count = 0; 5823 v8::HandleScope scope; 5824 LocalContext env; 5825 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener); 5826 5827 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 5828 v8::Local<v8::Object> global = env->Global(); 5829 global->Set(v8_str("trouble"), fun->GetFunction()); 5830 5831 Script::Compile(v8_str("function trouble_callee() {" 5832 " var x = null;" 5833 " return x.foo;" 5834 "};" 5835 "function trouble_caller() {" 5836 " trouble();" 5837 "};"))->Run(); 5838 Local<Value> trouble = global->Get(v8_str("trouble")); 5839 CHECK(trouble->IsFunction()); 5840 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee")); 5841 CHECK(trouble_callee->IsFunction()); 5842 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller")); 5843 CHECK(trouble_caller->IsFunction()); 5844 Function::Cast(*trouble_caller)->Call(global, 0, NULL); 5845 CHECK_EQ(1, report_count); 5846 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener); 5847} 5848 5849static const char* script_resource_name = "ExceptionInNativeScript.js"; 5850static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message, 5851 v8::Handle<Value>) { 5852 v8::Handle<v8::Value> name_val = message->GetScriptResourceName(); 5853 CHECK(!name_val.IsEmpty() && name_val->IsString()); 5854 v8::String::AsciiValue name(message->GetScriptResourceName()); 5855 CHECK_EQ(script_resource_name, *name); 5856 CHECK_EQ(3, message->GetLineNumber()); 5857 v8::String::AsciiValue source_line(message->GetSourceLine()); 5858 CHECK_EQ(" new o.foo();", *source_line); 5859} 5860 5861TEST(ExceptionInNativeScript) { 5862 v8::HandleScope scope; 5863 LocalContext env; 5864 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener); 5865 5866 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 5867 v8::Local<v8::Object> global = env->Global(); 5868 global->Set(v8_str("trouble"), fun->GetFunction()); 5869 5870 Script::Compile(v8_str("function trouble() {\n" 5871 " var o = {};\n" 5872 " new o.foo();\n" 5873 "};"), v8::String::New(script_resource_name))->Run(); 5874 Local<Value> trouble = global->Get(v8_str("trouble")); 5875 CHECK(trouble->IsFunction()); 5876 Function::Cast(*trouble)->Call(global, 0, NULL); 5877 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener); 5878} 5879 5880 5881TEST(CompilationErrorUsingTryCatchHandler) { 5882 v8::HandleScope scope; 5883 LocalContext env; 5884 v8::TryCatch try_catch; 5885 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile.")); 5886 CHECK_NE(NULL, *try_catch.Exception()); 5887 CHECK(try_catch.HasCaught()); 5888} 5889 5890 5891TEST(TryCatchFinallyUsingTryCatchHandler) { 5892 v8::HandleScope scope; 5893 LocalContext env; 5894 v8::TryCatch try_catch; 5895 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run(); 5896 CHECK(!try_catch.HasCaught()); 5897 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run(); 5898 CHECK(try_catch.HasCaught()); 5899 try_catch.Reset(); 5900 Script::Compile(v8_str("(function() {" 5901 "try { throw ''; } finally { return; }" 5902 "})()"))->Run(); 5903 CHECK(!try_catch.HasCaught()); 5904 Script::Compile(v8_str("(function()" 5905 " { try { throw ''; } finally { throw 0; }" 5906 "})()"))->Run(); 5907 CHECK(try_catch.HasCaught()); 5908} 5909 5910 5911// SecurityHandler can't be run twice 5912TEST(SecurityHandler) { 5913 v8::HandleScope scope0; 5914 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 5915 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback, 5916 IndexedSecurityTestCallback); 5917 // Create an environment 5918 v8::Persistent<Context> context0 = 5919 Context::New(NULL, global_template); 5920 context0->Enter(); 5921 5922 v8::Handle<v8::Object> global0 = context0->Global(); 5923 v8::Handle<Script> script0 = v8_compile("foo = 111"); 5924 script0->Run(); 5925 global0->Set(v8_str("0"), v8_num(999)); 5926 v8::Handle<Value> foo0 = global0->Get(v8_str("foo")); 5927 CHECK_EQ(111, foo0->Int32Value()); 5928 v8::Handle<Value> z0 = global0->Get(v8_str("0")); 5929 CHECK_EQ(999, z0->Int32Value()); 5930 5931 // Create another environment, should fail security checks. 5932 v8::HandleScope scope1; 5933 5934 v8::Persistent<Context> context1 = 5935 Context::New(NULL, global_template); 5936 context1->Enter(); 5937 5938 v8::Handle<v8::Object> global1 = context1->Global(); 5939 global1->Set(v8_str("othercontext"), global0); 5940 // This set will fail the security check. 5941 v8::Handle<Script> script1 = 5942 v8_compile("othercontext.foo = 222; othercontext[0] = 888;"); 5943 script1->Run(); 5944 // This read will pass the security check. 5945 v8::Handle<Value> foo1 = global0->Get(v8_str("foo")); 5946 CHECK_EQ(111, foo1->Int32Value()); 5947 // This read will pass the security check. 5948 v8::Handle<Value> z1 = global0->Get(v8_str("0")); 5949 CHECK_EQ(999, z1->Int32Value()); 5950 5951 // Create another environment, should pass security checks. 5952 { g_security_callback_result = true; // allow security handler to pass. 5953 v8::HandleScope scope2; 5954 LocalContext context2; 5955 v8::Handle<v8::Object> global2 = context2->Global(); 5956 global2->Set(v8_str("othercontext"), global0); 5957 v8::Handle<Script> script2 = 5958 v8_compile("othercontext.foo = 333; othercontext[0] = 888;"); 5959 script2->Run(); 5960 v8::Handle<Value> foo2 = global0->Get(v8_str("foo")); 5961 CHECK_EQ(333, foo2->Int32Value()); 5962 v8::Handle<Value> z2 = global0->Get(v8_str("0")); 5963 CHECK_EQ(888, z2->Int32Value()); 5964 } 5965 5966 context1->Exit(); 5967 context1.Dispose(); 5968 5969 context0->Exit(); 5970 context0.Dispose(); 5971} 5972 5973 5974THREADED_TEST(SecurityChecks) { 5975 v8::HandleScope handle_scope; 5976 LocalContext env1; 5977 v8::Persistent<Context> env2 = Context::New(); 5978 5979 Local<Value> foo = v8_str("foo"); 5980 Local<Value> bar = v8_str("bar"); 5981 5982 // Set to the same domain. 5983 env1->SetSecurityToken(foo); 5984 5985 // Create a function in env1. 5986 Script::Compile(v8_str("spy=function(){return spy;}"))->Run(); 5987 Local<Value> spy = env1->Global()->Get(v8_str("spy")); 5988 CHECK(spy->IsFunction()); 5989 5990 // Create another function accessing global objects. 5991 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run(); 5992 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2")); 5993 CHECK(spy2->IsFunction()); 5994 5995 // Switch to env2 in the same domain and invoke spy on env2. 5996 { 5997 env2->SetSecurityToken(foo); 5998 // Enter env2 5999 Context::Scope scope_env2(env2); 6000 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL); 6001 CHECK(result->IsFunction()); 6002 } 6003 6004 { 6005 env2->SetSecurityToken(bar); 6006 Context::Scope scope_env2(env2); 6007 6008 // Call cross_domain_call, it should throw an exception 6009 v8::TryCatch try_catch; 6010 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL); 6011 CHECK(try_catch.HasCaught()); 6012 } 6013 6014 env2.Dispose(); 6015} 6016 6017 6018// Regression test case for issue 1183439. 6019THREADED_TEST(SecurityChecksForPrototypeChain) { 6020 v8::HandleScope scope; 6021 LocalContext current; 6022 v8::Persistent<Context> other = Context::New(); 6023 6024 // Change context to be able to get to the Object function in the 6025 // other context without hitting the security checks. 6026 v8::Local<Value> other_object; 6027 { Context::Scope scope(other); 6028 other_object = other->Global()->Get(v8_str("Object")); 6029 other->Global()->Set(v8_num(42), v8_num(87)); 6030 } 6031 6032 current->Global()->Set(v8_str("other"), other->Global()); 6033 CHECK(v8_compile("other")->Run()->Equals(other->Global())); 6034 6035 // Make sure the security check fails here and we get an undefined 6036 // result instead of getting the Object function. Repeat in a loop 6037 // to make sure to exercise the IC code. 6038 v8::Local<Script> access_other0 = v8_compile("other.Object"); 6039 v8::Local<Script> access_other1 = v8_compile("other[42]"); 6040 for (int i = 0; i < 5; i++) { 6041 CHECK(!access_other0->Run()->Equals(other_object)); 6042 CHECK(access_other0->Run()->IsUndefined()); 6043 CHECK(!access_other1->Run()->Equals(v8_num(87))); 6044 CHECK(access_other1->Run()->IsUndefined()); 6045 } 6046 6047 // Create an object that has 'other' in its prototype chain and make 6048 // sure we cannot access the Object function indirectly through 6049 // that. Repeat in a loop to make sure to exercise the IC code. 6050 v8_compile("function F() { };" 6051 "F.prototype = other;" 6052 "var f = new F();")->Run(); 6053 v8::Local<Script> access_f0 = v8_compile("f.Object"); 6054 v8::Local<Script> access_f1 = v8_compile("f[42]"); 6055 for (int j = 0; j < 5; j++) { 6056 CHECK(!access_f0->Run()->Equals(other_object)); 6057 CHECK(access_f0->Run()->IsUndefined()); 6058 CHECK(!access_f1->Run()->Equals(v8_num(87))); 6059 CHECK(access_f1->Run()->IsUndefined()); 6060 } 6061 6062 // Now it gets hairy: Set the prototype for the other global object 6063 // to be the current global object. The prototype chain for 'f' now 6064 // goes through 'other' but ends up in the current global object. 6065 { Context::Scope scope(other); 6066 other->Global()->Set(v8_str("__proto__"), current->Global()); 6067 } 6068 // Set a named and an index property on the current global 6069 // object. To force the lookup to go through the other global object, 6070 // the properties must not exist in the other global object. 6071 current->Global()->Set(v8_str("foo"), v8_num(100)); 6072 current->Global()->Set(v8_num(99), v8_num(101)); 6073 // Try to read the properties from f and make sure that the access 6074 // gets stopped by the security checks on the other global object. 6075 Local<Script> access_f2 = v8_compile("f.foo"); 6076 Local<Script> access_f3 = v8_compile("f[99]"); 6077 for (int k = 0; k < 5; k++) { 6078 CHECK(!access_f2->Run()->Equals(v8_num(100))); 6079 CHECK(access_f2->Run()->IsUndefined()); 6080 CHECK(!access_f3->Run()->Equals(v8_num(101))); 6081 CHECK(access_f3->Run()->IsUndefined()); 6082 } 6083 other.Dispose(); 6084} 6085 6086 6087THREADED_TEST(CrossDomainDelete) { 6088 v8::HandleScope handle_scope; 6089 LocalContext env1; 6090 v8::Persistent<Context> env2 = Context::New(); 6091 6092 Local<Value> foo = v8_str("foo"); 6093 Local<Value> bar = v8_str("bar"); 6094 6095 // Set to the same domain. 6096 env1->SetSecurityToken(foo); 6097 env2->SetSecurityToken(foo); 6098 6099 env1->Global()->Set(v8_str("prop"), v8_num(3)); 6100 env2->Global()->Set(v8_str("env1"), env1->Global()); 6101 6102 // Change env2 to a different domain and delete env1.prop. 6103 env2->SetSecurityToken(bar); 6104 { 6105 Context::Scope scope_env2(env2); 6106 Local<Value> result = 6107 Script::Compile(v8_str("delete env1.prop"))->Run(); 6108 CHECK(result->IsFalse()); 6109 } 6110 6111 // Check that env1.prop still exists. 6112 Local<Value> v = env1->Global()->Get(v8_str("prop")); 6113 CHECK(v->IsNumber()); 6114 CHECK_EQ(3, v->Int32Value()); 6115 6116 env2.Dispose(); 6117} 6118 6119 6120THREADED_TEST(CrossDomainIsPropertyEnumerable) { 6121 v8::HandleScope handle_scope; 6122 LocalContext env1; 6123 v8::Persistent<Context> env2 = Context::New(); 6124 6125 Local<Value> foo = v8_str("foo"); 6126 Local<Value> bar = v8_str("bar"); 6127 6128 // Set to the same domain. 6129 env1->SetSecurityToken(foo); 6130 env2->SetSecurityToken(foo); 6131 6132 env1->Global()->Set(v8_str("prop"), v8_num(3)); 6133 env2->Global()->Set(v8_str("env1"), env1->Global()); 6134 6135 // env1.prop is enumerable in env2. 6136 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')"); 6137 { 6138 Context::Scope scope_env2(env2); 6139 Local<Value> result = Script::Compile(test)->Run(); 6140 CHECK(result->IsTrue()); 6141 } 6142 6143 // Change env2 to a different domain and test again. 6144 env2->SetSecurityToken(bar); 6145 { 6146 Context::Scope scope_env2(env2); 6147 Local<Value> result = Script::Compile(test)->Run(); 6148 CHECK(result->IsFalse()); 6149 } 6150 6151 env2.Dispose(); 6152} 6153 6154 6155THREADED_TEST(CrossDomainForIn) { 6156 v8::HandleScope handle_scope; 6157 LocalContext env1; 6158 v8::Persistent<Context> env2 = Context::New(); 6159 6160 Local<Value> foo = v8_str("foo"); 6161 Local<Value> bar = v8_str("bar"); 6162 6163 // Set to the same domain. 6164 env1->SetSecurityToken(foo); 6165 env2->SetSecurityToken(foo); 6166 6167 env1->Global()->Set(v8_str("prop"), v8_num(3)); 6168 env2->Global()->Set(v8_str("env1"), env1->Global()); 6169 6170 // Change env2 to a different domain and set env1's global object 6171 // as the __proto__ of an object in env2 and enumerate properties 6172 // in for-in. It shouldn't enumerate properties on env1's global 6173 // object. 6174 env2->SetSecurityToken(bar); 6175 { 6176 Context::Scope scope_env2(env2); 6177 Local<Value> result = 6178 CompileRun("(function(){var obj = {'__proto__':env1};" 6179 "for (var p in obj)" 6180 " if (p == 'prop') return false;" 6181 "return true;})()"); 6182 CHECK(result->IsTrue()); 6183 } 6184 env2.Dispose(); 6185} 6186 6187 6188TEST(ContextDetachGlobal) { 6189 v8::HandleScope handle_scope; 6190 LocalContext env1; 6191 v8::Persistent<Context> env2 = Context::New(); 6192 6193 Local<v8::Object> global1 = env1->Global(); 6194 6195 Local<Value> foo = v8_str("foo"); 6196 6197 // Set to the same domain. 6198 env1->SetSecurityToken(foo); 6199 env2->SetSecurityToken(foo); 6200 6201 // Enter env2 6202 env2->Enter(); 6203 6204 // Create a function in env2 and add a reference to it in env1. 6205 Local<v8::Object> global2 = env2->Global(); 6206 global2->Set(v8_str("prop"), v8::Integer::New(1)); 6207 CompileRun("function getProp() {return prop;}"); 6208 6209 env1->Global()->Set(v8_str("getProp"), 6210 global2->Get(v8_str("getProp"))); 6211 6212 // Detach env2's global, and reuse the global object of env2 6213 env2->Exit(); 6214 env2->DetachGlobal(); 6215 // env2 has a new global object. 6216 CHECK(!env2->Global()->Equals(global2)); 6217 6218 v8::Persistent<Context> env3 = 6219 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 6220 env3->SetSecurityToken(v8_str("bar")); 6221 env3->Enter(); 6222 6223 Local<v8::Object> global3 = env3->Global(); 6224 CHECK_EQ(global2, global3); 6225 CHECK(global3->Get(v8_str("prop"))->IsUndefined()); 6226 CHECK(global3->Get(v8_str("getProp"))->IsUndefined()); 6227 global3->Set(v8_str("prop"), v8::Integer::New(-1)); 6228 global3->Set(v8_str("prop2"), v8::Integer::New(2)); 6229 env3->Exit(); 6230 6231 // Call getProp in env1, and it should return the value 1 6232 { 6233 Local<Value> get_prop = global1->Get(v8_str("getProp")); 6234 CHECK(get_prop->IsFunction()); 6235 v8::TryCatch try_catch; 6236 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL); 6237 CHECK(!try_catch.HasCaught()); 6238 CHECK_EQ(1, r->Int32Value()); 6239 } 6240 6241 // Check that env3 is not accessible from env1 6242 { 6243 Local<Value> r = global3->Get(v8_str("prop2")); 6244 CHECK(r->IsUndefined()); 6245 } 6246 6247 env2.Dispose(); 6248 env3.Dispose(); 6249} 6250 6251 6252TEST(DetachAndReattachGlobal) { 6253 v8::HandleScope scope; 6254 LocalContext env1; 6255 6256 // Create second environment. 6257 v8::Persistent<Context> env2 = Context::New(); 6258 6259 Local<Value> foo = v8_str("foo"); 6260 6261 // Set same security token for env1 and env2. 6262 env1->SetSecurityToken(foo); 6263 env2->SetSecurityToken(foo); 6264 6265 // Create a property on the global object in env2. 6266 { 6267 v8::Context::Scope scope(env2); 6268 env2->Global()->Set(v8_str("p"), v8::Integer::New(42)); 6269 } 6270 6271 // Create a reference to env2 global from env1 global. 6272 env1->Global()->Set(v8_str("other"), env2->Global()); 6273 6274 // Check that we have access to other.p in env2 from env1. 6275 Local<Value> result = CompileRun("other.p"); 6276 CHECK(result->IsInt32()); 6277 CHECK_EQ(42, result->Int32Value()); 6278 6279 // Hold on to global from env2 and detach global from env2. 6280 Local<v8::Object> global2 = env2->Global(); 6281 env2->DetachGlobal(); 6282 6283 // Check that the global has been detached. No other.p property can 6284 // be found. 6285 result = CompileRun("other.p"); 6286 CHECK(result->IsUndefined()); 6287 6288 // Reuse global2 for env3. 6289 v8::Persistent<Context> env3 = 6290 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 6291 CHECK_EQ(global2, env3->Global()); 6292 6293 // Start by using the same security token for env3 as for env1 and env2. 6294 env3->SetSecurityToken(foo); 6295 6296 // Create a property on the global object in env3. 6297 { 6298 v8::Context::Scope scope(env3); 6299 env3->Global()->Set(v8_str("p"), v8::Integer::New(24)); 6300 } 6301 6302 // Check that other.p is now the property in env3 and that we have access. 6303 result = CompileRun("other.p"); 6304 CHECK(result->IsInt32()); 6305 CHECK_EQ(24, result->Int32Value()); 6306 6307 // Change security token for env3 to something different from env1 and env2. 6308 env3->SetSecurityToken(v8_str("bar")); 6309 6310 // Check that we do not have access to other.p in env1. |other| is now 6311 // the global object for env3 which has a different security token, 6312 // so access should be blocked. 6313 result = CompileRun("other.p"); 6314 CHECK(result->IsUndefined()); 6315 6316 // Detach the global for env3 and reattach it to env2. 6317 env3->DetachGlobal(); 6318 env2->ReattachGlobal(global2); 6319 6320 // Check that we have access to other.p again in env1. |other| is now 6321 // the global object for env2 which has the same security token as env1. 6322 result = CompileRun("other.p"); 6323 CHECK(result->IsInt32()); 6324 CHECK_EQ(42, result->Int32Value()); 6325 6326 env2.Dispose(); 6327 env3.Dispose(); 6328} 6329 6330 6331static bool allowed_access_type[v8::ACCESS_KEYS + 1] = { false }; 6332static bool NamedAccessBlocker(Local<v8::Object> global, 6333 Local<Value> name, 6334 v8::AccessType type, 6335 Local<Value> data) { 6336 return Context::GetCurrent()->Global()->Equals(global) || 6337 allowed_access_type[type]; 6338} 6339 6340 6341static bool IndexedAccessBlocker(Local<v8::Object> global, 6342 uint32_t key, 6343 v8::AccessType type, 6344 Local<Value> data) { 6345 return Context::GetCurrent()->Global()->Equals(global) || 6346 allowed_access_type[type]; 6347} 6348 6349 6350static int g_echo_value = -1; 6351static v8::Handle<Value> EchoGetter(Local<String> name, 6352 const AccessorInfo& info) { 6353 return v8_num(g_echo_value); 6354} 6355 6356 6357static void EchoSetter(Local<String> name, 6358 Local<Value> value, 6359 const AccessorInfo&) { 6360 if (value->IsNumber()) 6361 g_echo_value = value->Int32Value(); 6362} 6363 6364 6365static v8::Handle<Value> UnreachableGetter(Local<String> name, 6366 const AccessorInfo& info) { 6367 CHECK(false); // This function should not be called.. 6368 return v8::Undefined(); 6369} 6370 6371 6372static void UnreachableSetter(Local<String>, Local<Value>, 6373 const AccessorInfo&) { 6374 CHECK(false); // This function should nto be called. 6375} 6376 6377 6378TEST(AccessControl) { 6379 v8::HandleScope handle_scope; 6380 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 6381 6382 global_template->SetAccessCheckCallbacks(NamedAccessBlocker, 6383 IndexedAccessBlocker); 6384 6385 // Add an accessor accessible by cross-domain JS code. 6386 global_template->SetAccessor( 6387 v8_str("accessible_prop"), 6388 EchoGetter, EchoSetter, 6389 v8::Handle<Value>(), 6390 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); 6391 6392 // Add an accessor that is not accessible by cross-domain JS code. 6393 global_template->SetAccessor(v8_str("blocked_prop"), 6394 UnreachableGetter, UnreachableSetter, 6395 v8::Handle<Value>(), 6396 v8::DEFAULT); 6397 6398 // Create an environment 6399 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 6400 context0->Enter(); 6401 6402 v8::Handle<v8::Object> global0 = context0->Global(); 6403 6404 // Define a property with JS getter and setter. 6405 CompileRun( 6406 "function getter() { return 'getter'; };\n" 6407 "function setter() { return 'setter'; }\n" 6408 "Object.defineProperty(this, 'js_accessor_p', {get:getter, set:setter})"); 6409 6410 Local<Value> getter = global0->Get(v8_str("getter")); 6411 Local<Value> setter = global0->Get(v8_str("setter")); 6412 6413 // And define normal element. 6414 global0->Set(239, v8_str("239")); 6415 6416 // Define an element with JS getter and setter. 6417 CompileRun( 6418 "function el_getter() { return 'el_getter'; };\n" 6419 "function el_setter() { return 'el_setter'; };\n" 6420 "Object.defineProperty(this, '42', {get: el_getter, set: el_setter});"); 6421 6422 Local<Value> el_getter = global0->Get(v8_str("el_getter")); 6423 Local<Value> el_setter = global0->Get(v8_str("el_setter")); 6424 6425 v8::HandleScope scope1; 6426 6427 v8::Persistent<Context> context1 = Context::New(); 6428 context1->Enter(); 6429 6430 v8::Handle<v8::Object> global1 = context1->Global(); 6431 global1->Set(v8_str("other"), global0); 6432 6433 // Access blocked property. 6434 CompileRun("other.blocked_prop = 1"); 6435 6436 ExpectUndefined("other.blocked_prop"); 6437 ExpectUndefined( 6438 "Object.getOwnPropertyDescriptor(other, 'blocked_prop')"); 6439 ExpectFalse("propertyIsEnumerable.call(other, 'blocked_prop')"); 6440 6441 // Enable ACCESS_HAS 6442 allowed_access_type[v8::ACCESS_HAS] = true; 6443 ExpectUndefined("other.blocked_prop"); 6444 // ... and now we can get the descriptor... 6445 ExpectUndefined( 6446 "Object.getOwnPropertyDescriptor(other, 'blocked_prop').value"); 6447 // ... and enumerate the property. 6448 ExpectTrue("propertyIsEnumerable.call(other, 'blocked_prop')"); 6449 allowed_access_type[v8::ACCESS_HAS] = false; 6450 6451 // Access blocked element. 6452 CompileRun("other[239] = 1"); 6453 6454 ExpectUndefined("other[239]"); 6455 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '239')"); 6456 ExpectFalse("propertyIsEnumerable.call(other, '239')"); 6457 6458 // Enable ACCESS_HAS 6459 allowed_access_type[v8::ACCESS_HAS] = true; 6460 ExpectUndefined("other[239]"); 6461 // ... and now we can get the descriptor... 6462 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '239').value"); 6463 // ... and enumerate the property. 6464 ExpectTrue("propertyIsEnumerable.call(other, '239')"); 6465 allowed_access_type[v8::ACCESS_HAS] = false; 6466 6467 // Access a property with JS accessor. 6468 CompileRun("other.js_accessor_p = 2"); 6469 6470 ExpectUndefined("other.js_accessor_p"); 6471 ExpectUndefined( 6472 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p')"); 6473 6474 // Enable ACCESS_HAS. 6475 allowed_access_type[v8::ACCESS_HAS] = true; 6476 ExpectUndefined("other.js_accessor_p"); 6477 ExpectUndefined( 6478 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get"); 6479 ExpectUndefined( 6480 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set"); 6481 ExpectUndefined( 6482 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value"); 6483 allowed_access_type[v8::ACCESS_HAS] = false; 6484 6485 // Enable both ACCESS_HAS and ACCESS_GET. 6486 allowed_access_type[v8::ACCESS_HAS] = true; 6487 allowed_access_type[v8::ACCESS_GET] = true; 6488 6489 ExpectString("other.js_accessor_p", "getter"); 6490 ExpectObject( 6491 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter); 6492 ExpectUndefined( 6493 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set"); 6494 ExpectUndefined( 6495 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value"); 6496 6497 allowed_access_type[v8::ACCESS_GET] = false; 6498 allowed_access_type[v8::ACCESS_HAS] = false; 6499 6500 // Enable both ACCESS_HAS and ACCESS_SET. 6501 allowed_access_type[v8::ACCESS_HAS] = true; 6502 allowed_access_type[v8::ACCESS_SET] = true; 6503 6504 ExpectUndefined("other.js_accessor_p"); 6505 ExpectUndefined( 6506 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get"); 6507 ExpectObject( 6508 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter); 6509 ExpectUndefined( 6510 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value"); 6511 6512 allowed_access_type[v8::ACCESS_SET] = false; 6513 allowed_access_type[v8::ACCESS_HAS] = false; 6514 6515 // Enable both ACCESS_HAS, ACCESS_GET and ACCESS_SET. 6516 allowed_access_type[v8::ACCESS_HAS] = true; 6517 allowed_access_type[v8::ACCESS_GET] = true; 6518 allowed_access_type[v8::ACCESS_SET] = true; 6519 6520 ExpectString("other.js_accessor_p", "getter"); 6521 ExpectObject( 6522 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').get", getter); 6523 ExpectObject( 6524 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').set", setter); 6525 ExpectUndefined( 6526 "Object.getOwnPropertyDescriptor(other, 'js_accessor_p').value"); 6527 6528 allowed_access_type[v8::ACCESS_SET] = false; 6529 allowed_access_type[v8::ACCESS_GET] = false; 6530 allowed_access_type[v8::ACCESS_HAS] = false; 6531 6532 // Access an element with JS accessor. 6533 CompileRun("other[42] = 2"); 6534 6535 ExpectUndefined("other[42]"); 6536 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42')"); 6537 6538 // Enable ACCESS_HAS. 6539 allowed_access_type[v8::ACCESS_HAS] = true; 6540 ExpectUndefined("other[42]"); 6541 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').get"); 6542 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').set"); 6543 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value"); 6544 allowed_access_type[v8::ACCESS_HAS] = false; 6545 6546 // Enable both ACCESS_HAS and ACCESS_GET. 6547 allowed_access_type[v8::ACCESS_HAS] = true; 6548 allowed_access_type[v8::ACCESS_GET] = true; 6549 6550 ExpectString("other[42]", "el_getter"); 6551 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter); 6552 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').set"); 6553 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value"); 6554 6555 allowed_access_type[v8::ACCESS_GET] = false; 6556 allowed_access_type[v8::ACCESS_HAS] = false; 6557 6558 // Enable both ACCESS_HAS and ACCESS_SET. 6559 allowed_access_type[v8::ACCESS_HAS] = true; 6560 allowed_access_type[v8::ACCESS_SET] = true; 6561 6562 ExpectUndefined("other[42]"); 6563 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').get"); 6564 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter); 6565 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value"); 6566 6567 allowed_access_type[v8::ACCESS_SET] = false; 6568 allowed_access_type[v8::ACCESS_HAS] = false; 6569 6570 // Enable both ACCESS_HAS, ACCESS_GET and ACCESS_SET. 6571 allowed_access_type[v8::ACCESS_HAS] = true; 6572 allowed_access_type[v8::ACCESS_GET] = true; 6573 allowed_access_type[v8::ACCESS_SET] = true; 6574 6575 ExpectString("other[42]", "el_getter"); 6576 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').get", el_getter); 6577 ExpectObject("Object.getOwnPropertyDescriptor(other, '42').set", el_setter); 6578 ExpectUndefined("Object.getOwnPropertyDescriptor(other, '42').value"); 6579 6580 allowed_access_type[v8::ACCESS_SET] = false; 6581 allowed_access_type[v8::ACCESS_GET] = false; 6582 allowed_access_type[v8::ACCESS_HAS] = false; 6583 6584 v8::Handle<Value> value; 6585 6586 // Access accessible property 6587 value = CompileRun("other.accessible_prop = 3"); 6588 CHECK(value->IsNumber()); 6589 CHECK_EQ(3, value->Int32Value()); 6590 CHECK_EQ(3, g_echo_value); 6591 6592 value = CompileRun("other.accessible_prop"); 6593 CHECK(value->IsNumber()); 6594 CHECK_EQ(3, value->Int32Value()); 6595 6596 value = CompileRun( 6597 "Object.getOwnPropertyDescriptor(other, 'accessible_prop').value"); 6598 CHECK(value->IsNumber()); 6599 CHECK_EQ(3, value->Int32Value()); 6600 6601 value = CompileRun("propertyIsEnumerable.call(other, 'accessible_prop')"); 6602 CHECK(value->IsTrue()); 6603 6604 // Enumeration doesn't enumerate accessors from inaccessible objects in 6605 // the prototype chain even if the accessors are in themselves accessible. 6606 value = 6607 CompileRun("(function(){var obj = {'__proto__':other};" 6608 "for (var p in obj)" 6609 " if (p == 'accessible_prop' || p == 'blocked_prop') {" 6610 " return false;" 6611 " }" 6612 "return true;})()"); 6613 CHECK(value->IsTrue()); 6614 6615 context1->Exit(); 6616 context0->Exit(); 6617 context1.Dispose(); 6618 context0.Dispose(); 6619} 6620 6621 6622TEST(AccessControlES5) { 6623 v8::HandleScope handle_scope; 6624 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 6625 6626 global_template->SetAccessCheckCallbacks(NamedAccessBlocker, 6627 IndexedAccessBlocker); 6628 6629 // Add accessible accessor. 6630 global_template->SetAccessor( 6631 v8_str("accessible_prop"), 6632 EchoGetter, EchoSetter, 6633 v8::Handle<Value>(), 6634 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); 6635 6636 6637 // Add an accessor that is not accessible by cross-domain JS code. 6638 global_template->SetAccessor(v8_str("blocked_prop"), 6639 UnreachableGetter, UnreachableSetter, 6640 v8::Handle<Value>(), 6641 v8::DEFAULT); 6642 6643 // Create an environment 6644 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 6645 context0->Enter(); 6646 6647 v8::Handle<v8::Object> global0 = context0->Global(); 6648 6649 v8::Persistent<Context> context1 = Context::New(); 6650 context1->Enter(); 6651 v8::Handle<v8::Object> global1 = context1->Global(); 6652 global1->Set(v8_str("other"), global0); 6653 6654 // Regression test for issue 1154. 6655 ExpectTrue("Object.keys(other).indexOf('blocked_prop') == -1"); 6656 6657 ExpectUndefined("other.blocked_prop"); 6658 6659 // Regression test for issue 1027. 6660 CompileRun("Object.defineProperty(\n" 6661 " other, 'blocked_prop', {configurable: false})"); 6662 ExpectUndefined("other.blocked_prop"); 6663 ExpectUndefined( 6664 "Object.getOwnPropertyDescriptor(other, 'blocked_prop')"); 6665 6666 // Regression test for issue 1171. 6667 ExpectTrue("Object.isExtensible(other)"); 6668 CompileRun("Object.preventExtensions(other)"); 6669 ExpectTrue("Object.isExtensible(other)"); 6670 6671 // Object.seal and Object.freeze. 6672 CompileRun("Object.freeze(other)"); 6673 ExpectTrue("Object.isExtensible(other)"); 6674 6675 CompileRun("Object.seal(other)"); 6676 ExpectTrue("Object.isExtensible(other)"); 6677 6678 // Regression test for issue 1250. 6679 // Make sure that we can set the accessible accessors value using normal 6680 // assignment. 6681 CompileRun("other.accessible_prop = 42"); 6682 CHECK_EQ(42, g_echo_value); 6683 6684 v8::Handle<Value> value; 6685 // We follow Safari in ignoring assignments to host object accessors. 6686 CompileRun("Object.defineProperty(other, 'accessible_prop', {value: -1})"); 6687 value = CompileRun("other.accessible_prop == 42"); 6688 CHECK(value->IsTrue()); 6689} 6690 6691 6692static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global, 6693 Local<Value> name, 6694 v8::AccessType type, 6695 Local<Value> data) { 6696 return false; 6697} 6698 6699 6700static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global, 6701 uint32_t key, 6702 v8::AccessType type, 6703 Local<Value> data) { 6704 return false; 6705} 6706 6707 6708THREADED_TEST(AccessControlGetOwnPropertyNames) { 6709 v8::HandleScope handle_scope; 6710 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(); 6711 6712 obj_template->Set(v8_str("x"), v8::Integer::New(42)); 6713 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker, 6714 GetOwnPropertyNamesIndexedBlocker); 6715 6716 // Create an environment 6717 v8::Persistent<Context> context0 = Context::New(NULL, obj_template); 6718 context0->Enter(); 6719 6720 v8::Handle<v8::Object> global0 = context0->Global(); 6721 6722 v8::HandleScope scope1; 6723 6724 v8::Persistent<Context> context1 = Context::New(); 6725 context1->Enter(); 6726 6727 v8::Handle<v8::Object> global1 = context1->Global(); 6728 global1->Set(v8_str("other"), global0); 6729 global1->Set(v8_str("object"), obj_template->NewInstance()); 6730 6731 v8::Handle<Value> value; 6732 6733 // Attempt to get the property names of the other global object and 6734 // of an object that requires access checks. Accessing the other 6735 // global object should be blocked by access checks on the global 6736 // proxy object. Accessing the object that requires access checks 6737 // is blocked by the access checks on the object itself. 6738 value = CompileRun("Object.getOwnPropertyNames(other).length == 0"); 6739 CHECK(value->IsTrue()); 6740 6741 value = CompileRun("Object.getOwnPropertyNames(object).length == 0"); 6742 CHECK(value->IsTrue()); 6743 6744 context1->Exit(); 6745 context0->Exit(); 6746 context1.Dispose(); 6747 context0.Dispose(); 6748} 6749 6750 6751static v8::Handle<v8::Array> NamedPropertyEnumerator(const AccessorInfo& info) { 6752 v8::Handle<v8::Array> result = v8::Array::New(1); 6753 result->Set(0, v8_str("x")); 6754 return result; 6755} 6756 6757 6758THREADED_TEST(GetOwnPropertyNamesWithInterceptor) { 6759 v8::HandleScope handle_scope; 6760 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(); 6761 6762 obj_template->Set(v8_str("x"), v8::Integer::New(42)); 6763 obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL, 6764 NamedPropertyEnumerator); 6765 6766 LocalContext context; 6767 v8::Handle<v8::Object> global = context->Global(); 6768 global->Set(v8_str("object"), obj_template->NewInstance()); 6769 6770 v8::Handle<Value> value = 6771 CompileRun("Object.getOwnPropertyNames(object).join(',')"); 6772 CHECK_EQ(v8_str("x"), value); 6773} 6774 6775 6776static v8::Handle<Value> ConstTenGetter(Local<String> name, 6777 const AccessorInfo& info) { 6778 return v8_num(10); 6779} 6780 6781 6782THREADED_TEST(CrossDomainAccessors) { 6783 v8::HandleScope handle_scope; 6784 6785 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(); 6786 6787 v8::Handle<v8::ObjectTemplate> global_template = 6788 func_template->InstanceTemplate(); 6789 6790 v8::Handle<v8::ObjectTemplate> proto_template = 6791 func_template->PrototypeTemplate(); 6792 6793 // Add an accessor to proto that's accessible by cross-domain JS code. 6794 proto_template->SetAccessor(v8_str("accessible"), 6795 ConstTenGetter, 0, 6796 v8::Handle<Value>(), 6797 v8::ALL_CAN_READ); 6798 6799 // Add an accessor that is not accessible by cross-domain JS code. 6800 global_template->SetAccessor(v8_str("unreachable"), 6801 UnreachableGetter, 0, 6802 v8::Handle<Value>(), 6803 v8::DEFAULT); 6804 6805 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 6806 context0->Enter(); 6807 6808 Local<v8::Object> global = context0->Global(); 6809 // Add a normal property that shadows 'accessible' 6810 global->Set(v8_str("accessible"), v8_num(11)); 6811 6812 // Enter a new context. 6813 v8::HandleScope scope1; 6814 v8::Persistent<Context> context1 = Context::New(); 6815 context1->Enter(); 6816 6817 v8::Handle<v8::Object> global1 = context1->Global(); 6818 global1->Set(v8_str("other"), global); 6819 6820 // Should return 10, instead of 11 6821 v8::Handle<Value> value = v8_compile("other.accessible")->Run(); 6822 CHECK(value->IsNumber()); 6823 CHECK_EQ(10, value->Int32Value()); 6824 6825 value = v8_compile("other.unreachable")->Run(); 6826 CHECK(value->IsUndefined()); 6827 6828 context1->Exit(); 6829 context0->Exit(); 6830 context1.Dispose(); 6831 context0.Dispose(); 6832} 6833 6834 6835static int named_access_count = 0; 6836static int indexed_access_count = 0; 6837 6838static bool NamedAccessCounter(Local<v8::Object> global, 6839 Local<Value> name, 6840 v8::AccessType type, 6841 Local<Value> data) { 6842 named_access_count++; 6843 return true; 6844} 6845 6846 6847static bool IndexedAccessCounter(Local<v8::Object> global, 6848 uint32_t key, 6849 v8::AccessType type, 6850 Local<Value> data) { 6851 indexed_access_count++; 6852 return true; 6853} 6854 6855 6856// This one is too easily disturbed by other tests. 6857TEST(AccessControlIC) { 6858 named_access_count = 0; 6859 indexed_access_count = 0; 6860 6861 v8::HandleScope handle_scope; 6862 6863 // Create an environment. 6864 v8::Persistent<Context> context0 = Context::New(); 6865 context0->Enter(); 6866 6867 // Create an object that requires access-check functions to be 6868 // called for cross-domain access. 6869 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 6870 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 6871 IndexedAccessCounter); 6872 Local<v8::Object> object = object_template->NewInstance(); 6873 6874 v8::HandleScope scope1; 6875 6876 // Create another environment. 6877 v8::Persistent<Context> context1 = Context::New(); 6878 context1->Enter(); 6879 6880 // Make easy access to the object from the other environment. 6881 v8::Handle<v8::Object> global1 = context1->Global(); 6882 global1->Set(v8_str("obj"), object); 6883 6884 v8::Handle<Value> value; 6885 6886 // Check that the named access-control function is called every time. 6887 CompileRun("function testProp(obj) {" 6888 " for (var i = 0; i < 10; i++) obj.prop = 1;" 6889 " for (var j = 0; j < 10; j++) obj.prop;" 6890 " return obj.prop" 6891 "}"); 6892 value = CompileRun("testProp(obj)"); 6893 CHECK(value->IsNumber()); 6894 CHECK_EQ(1, value->Int32Value()); 6895 CHECK_EQ(21, named_access_count); 6896 6897 // Check that the named access-control function is called every time. 6898 CompileRun("var p = 'prop';" 6899 "function testKeyed(obj) {" 6900 " for (var i = 0; i < 10; i++) obj[p] = 1;" 6901 " for (var j = 0; j < 10; j++) obj[p];" 6902 " return obj[p];" 6903 "}"); 6904 // Use obj which requires access checks. No inline caching is used 6905 // in that case. 6906 value = CompileRun("testKeyed(obj)"); 6907 CHECK(value->IsNumber()); 6908 CHECK_EQ(1, value->Int32Value()); 6909 CHECK_EQ(42, named_access_count); 6910 // Force the inline caches into generic state and try again. 6911 CompileRun("testKeyed({ a: 0 })"); 6912 CompileRun("testKeyed({ b: 0 })"); 6913 value = CompileRun("testKeyed(obj)"); 6914 CHECK(value->IsNumber()); 6915 CHECK_EQ(1, value->Int32Value()); 6916 CHECK_EQ(63, named_access_count); 6917 6918 // Check that the indexed access-control function is called every time. 6919 CompileRun("function testIndexed(obj) {" 6920 " for (var i = 0; i < 10; i++) obj[0] = 1;" 6921 " for (var j = 0; j < 10; j++) obj[0];" 6922 " return obj[0]" 6923 "}"); 6924 value = CompileRun("testIndexed(obj)"); 6925 CHECK(value->IsNumber()); 6926 CHECK_EQ(1, value->Int32Value()); 6927 CHECK_EQ(21, indexed_access_count); 6928 // Force the inline caches into generic state. 6929 CompileRun("testIndexed(new Array(1))"); 6930 // Test that the indexed access check is called. 6931 value = CompileRun("testIndexed(obj)"); 6932 CHECK(value->IsNumber()); 6933 CHECK_EQ(1, value->Int32Value()); 6934 CHECK_EQ(42, indexed_access_count); 6935 6936 // Check that the named access check is called when invoking 6937 // functions on an object that requires access checks. 6938 CompileRun("obj.f = function() {}"); 6939 CompileRun("function testCallNormal(obj) {" 6940 " for (var i = 0; i < 10; i++) obj.f();" 6941 "}"); 6942 CompileRun("testCallNormal(obj)"); 6943 CHECK_EQ(74, named_access_count); 6944 6945 // Force obj into slow case. 6946 value = CompileRun("delete obj.prop"); 6947 CHECK(value->BooleanValue()); 6948 // Force inline caches into dictionary probing mode. 6949 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);"); 6950 // Test that the named access check is called. 6951 value = CompileRun("testProp(obj);"); 6952 CHECK(value->IsNumber()); 6953 CHECK_EQ(1, value->Int32Value()); 6954 CHECK_EQ(96, named_access_count); 6955 6956 // Force the call inline cache into dictionary probing mode. 6957 CompileRun("o.f = function() {}; testCallNormal(o)"); 6958 // Test that the named access check is still called for each 6959 // invocation of the function. 6960 value = CompileRun("testCallNormal(obj)"); 6961 CHECK_EQ(106, named_access_count); 6962 6963 context1->Exit(); 6964 context0->Exit(); 6965 context1.Dispose(); 6966 context0.Dispose(); 6967} 6968 6969 6970static bool NamedAccessFlatten(Local<v8::Object> global, 6971 Local<Value> name, 6972 v8::AccessType type, 6973 Local<Value> data) { 6974 char buf[100]; 6975 int len; 6976 6977 CHECK(name->IsString()); 6978 6979 memset(buf, 0x1, sizeof(buf)); 6980 len = name.As<String>()->WriteAscii(buf); 6981 CHECK_EQ(4, len); 6982 6983 uint16_t buf2[100]; 6984 6985 memset(buf, 0x1, sizeof(buf)); 6986 len = name.As<String>()->Write(buf2); 6987 CHECK_EQ(4, len); 6988 6989 return true; 6990} 6991 6992 6993static bool IndexedAccessFlatten(Local<v8::Object> global, 6994 uint32_t key, 6995 v8::AccessType type, 6996 Local<Value> data) { 6997 return true; 6998} 6999 7000 7001// Regression test. In access checks, operations that may cause 7002// garbage collection are not allowed. It used to be the case that 7003// using the Write operation on a string could cause a garbage 7004// collection due to flattening of the string. This is no longer the 7005// case. 7006THREADED_TEST(AccessControlFlatten) { 7007 named_access_count = 0; 7008 indexed_access_count = 0; 7009 7010 v8::HandleScope handle_scope; 7011 7012 // Create an environment. 7013 v8::Persistent<Context> context0 = Context::New(); 7014 context0->Enter(); 7015 7016 // Create an object that requires access-check functions to be 7017 // called for cross-domain access. 7018 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 7019 object_template->SetAccessCheckCallbacks(NamedAccessFlatten, 7020 IndexedAccessFlatten); 7021 Local<v8::Object> object = object_template->NewInstance(); 7022 7023 v8::HandleScope scope1; 7024 7025 // Create another environment. 7026 v8::Persistent<Context> context1 = Context::New(); 7027 context1->Enter(); 7028 7029 // Make easy access to the object from the other environment. 7030 v8::Handle<v8::Object> global1 = context1->Global(); 7031 global1->Set(v8_str("obj"), object); 7032 7033 v8::Handle<Value> value; 7034 7035 value = v8_compile("var p = 'as' + 'df';")->Run(); 7036 value = v8_compile("obj[p];")->Run(); 7037 7038 context1->Exit(); 7039 context0->Exit(); 7040 context1.Dispose(); 7041 context0.Dispose(); 7042} 7043 7044 7045static v8::Handle<Value> AccessControlNamedGetter( 7046 Local<String>, const AccessorInfo&) { 7047 return v8::Integer::New(42); 7048} 7049 7050 7051static v8::Handle<Value> AccessControlNamedSetter( 7052 Local<String>, Local<Value> value, const AccessorInfo&) { 7053 return value; 7054} 7055 7056 7057static v8::Handle<Value> AccessControlIndexedGetter( 7058 uint32_t index, 7059 const AccessorInfo& info) { 7060 return v8_num(42); 7061} 7062 7063 7064static v8::Handle<Value> AccessControlIndexedSetter( 7065 uint32_t, Local<Value> value, const AccessorInfo&) { 7066 return value; 7067} 7068 7069 7070THREADED_TEST(AccessControlInterceptorIC) { 7071 named_access_count = 0; 7072 indexed_access_count = 0; 7073 7074 v8::HandleScope handle_scope; 7075 7076 // Create an environment. 7077 v8::Persistent<Context> context0 = Context::New(); 7078 context0->Enter(); 7079 7080 // Create an object that requires access-check functions to be 7081 // called for cross-domain access. The object also has interceptors 7082 // interceptor. 7083 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 7084 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 7085 IndexedAccessCounter); 7086 object_template->SetNamedPropertyHandler(AccessControlNamedGetter, 7087 AccessControlNamedSetter); 7088 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter, 7089 AccessControlIndexedSetter); 7090 Local<v8::Object> object = object_template->NewInstance(); 7091 7092 v8::HandleScope scope1; 7093 7094 // Create another environment. 7095 v8::Persistent<Context> context1 = Context::New(); 7096 context1->Enter(); 7097 7098 // Make easy access to the object from the other environment. 7099 v8::Handle<v8::Object> global1 = context1->Global(); 7100 global1->Set(v8_str("obj"), object); 7101 7102 v8::Handle<Value> value; 7103 7104 // Check that the named access-control function is called every time 7105 // eventhough there is an interceptor on the object. 7106 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run(); 7107 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;" 7108 "obj.x")->Run(); 7109 CHECK(value->IsNumber()); 7110 CHECK_EQ(42, value->Int32Value()); 7111 CHECK_EQ(21, named_access_count); 7112 7113 value = v8_compile("var p = 'x';")->Run(); 7114 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run(); 7115 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];" 7116 "obj[p]")->Run(); 7117 CHECK(value->IsNumber()); 7118 CHECK_EQ(42, value->Int32Value()); 7119 CHECK_EQ(42, named_access_count); 7120 7121 // Check that the indexed access-control function is called every 7122 // time eventhough there is an interceptor on the object. 7123 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run(); 7124 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];" 7125 "obj[0]")->Run(); 7126 CHECK(value->IsNumber()); 7127 CHECK_EQ(42, value->Int32Value()); 7128 CHECK_EQ(21, indexed_access_count); 7129 7130 context1->Exit(); 7131 context0->Exit(); 7132 context1.Dispose(); 7133 context0.Dispose(); 7134} 7135 7136 7137THREADED_TEST(Version) { 7138 v8::V8::GetVersion(); 7139} 7140 7141 7142static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) { 7143 ApiTestFuzzer::Fuzz(); 7144 return v8_num(12); 7145} 7146 7147 7148THREADED_TEST(InstanceProperties) { 7149 v8::HandleScope handle_scope; 7150 LocalContext context; 7151 7152 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 7153 Local<ObjectTemplate> instance = t->InstanceTemplate(); 7154 7155 instance->Set(v8_str("x"), v8_num(42)); 7156 instance->Set(v8_str("f"), 7157 v8::FunctionTemplate::New(InstanceFunctionCallback)); 7158 7159 Local<Value> o = t->GetFunction()->NewInstance(); 7160 7161 context->Global()->Set(v8_str("i"), o); 7162 Local<Value> value = Script::Compile(v8_str("i.x"))->Run(); 7163 CHECK_EQ(42, value->Int32Value()); 7164 7165 value = Script::Compile(v8_str("i.f()"))->Run(); 7166 CHECK_EQ(12, value->Int32Value()); 7167} 7168 7169 7170static v8::Handle<Value> 7171GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) { 7172 ApiTestFuzzer::Fuzz(); 7173 return v8::Handle<Value>(); 7174} 7175 7176 7177THREADED_TEST(GlobalObjectInstanceProperties) { 7178 v8::HandleScope handle_scope; 7179 7180 Local<Value> global_object; 7181 7182 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 7183 t->InstanceTemplate()->SetNamedPropertyHandler( 7184 GlobalObjectInstancePropertiesGet); 7185 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 7186 instance_template->Set(v8_str("x"), v8_num(42)); 7187 instance_template->Set(v8_str("f"), 7188 v8::FunctionTemplate::New(InstanceFunctionCallback)); 7189 7190 // The script to check how Crankshaft compiles missing global function 7191 // invocations. function g is not defined and should throw on call. 7192 const char* script = 7193 "function wrapper(call) {" 7194 " var x = 0, y = 1;" 7195 " for (var i = 0; i < 1000; i++) {" 7196 " x += i * 100;" 7197 " y += i * 100;" 7198 " }" 7199 " if (call) g();" 7200 "}" 7201 "for (var i = 0; i < 17; i++) wrapper(false);" 7202 "var thrown = 0;" 7203 "try { wrapper(true); } catch (e) { thrown = 1; };" 7204 "thrown"; 7205 7206 { 7207 LocalContext env(NULL, instance_template); 7208 // Hold on to the global object so it can be used again in another 7209 // environment initialization. 7210 global_object = env->Global(); 7211 7212 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 7213 CHECK_EQ(42, value->Int32Value()); 7214 value = Script::Compile(v8_str("f()"))->Run(); 7215 CHECK_EQ(12, value->Int32Value()); 7216 value = Script::Compile(v8_str(script))->Run(); 7217 CHECK_EQ(1, value->Int32Value()); 7218 } 7219 7220 { 7221 // Create new environment reusing the global object. 7222 LocalContext env(NULL, instance_template, global_object); 7223 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 7224 CHECK_EQ(42, value->Int32Value()); 7225 value = Script::Compile(v8_str("f()"))->Run(); 7226 CHECK_EQ(12, value->Int32Value()); 7227 value = Script::Compile(v8_str(script))->Run(); 7228 CHECK_EQ(1, value->Int32Value()); 7229 } 7230} 7231 7232 7233THREADED_TEST(CallKnownGlobalReceiver) { 7234 v8::HandleScope handle_scope; 7235 7236 Local<Value> global_object; 7237 7238 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 7239 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 7240 7241 // The script to check that we leave global object not 7242 // global object proxy on stack when we deoptimize from inside 7243 // arguments evaluation. 7244 // To provoke error we need to both force deoptimization 7245 // from arguments evaluation and to force CallIC to take 7246 // CallIC_Miss code path that can't cope with global proxy. 7247 const char* script = 7248 "function bar(x, y) { try { } finally { } }" 7249 "function baz(x) { try { } finally { } }" 7250 "function bom(x) { try { } finally { } }" 7251 "function foo(x) { bar([x], bom(2)); }" 7252 "for (var i = 0; i < 10000; i++) foo(1);" 7253 "foo"; 7254 7255 Local<Value> foo; 7256 { 7257 LocalContext env(NULL, instance_template); 7258 // Hold on to the global object so it can be used again in another 7259 // environment initialization. 7260 global_object = env->Global(); 7261 foo = Script::Compile(v8_str(script))->Run(); 7262 } 7263 7264 { 7265 // Create new environment reusing the global object. 7266 LocalContext env(NULL, instance_template, global_object); 7267 env->Global()->Set(v8_str("foo"), foo); 7268 Local<Value> value(Script::Compile(v8_str("foo()"))->Run()); 7269 } 7270} 7271 7272 7273static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) { 7274 ApiTestFuzzer::Fuzz(); 7275 return v8_num(42); 7276} 7277 7278 7279static int shadow_y; 7280static int shadow_y_setter_call_count; 7281static int shadow_y_getter_call_count; 7282 7283 7284static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) { 7285 shadow_y_setter_call_count++; 7286 shadow_y = 42; 7287} 7288 7289 7290static v8::Handle<Value> ShadowYGetter(Local<String> name, 7291 const AccessorInfo& info) { 7292 ApiTestFuzzer::Fuzz(); 7293 shadow_y_getter_call_count++; 7294 return v8_num(shadow_y); 7295} 7296 7297 7298static v8::Handle<Value> ShadowIndexedGet(uint32_t index, 7299 const AccessorInfo& info) { 7300 return v8::Handle<Value>(); 7301} 7302 7303 7304static v8::Handle<Value> ShadowNamedGet(Local<String> key, 7305 const AccessorInfo&) { 7306 return v8::Handle<Value>(); 7307} 7308 7309 7310THREADED_TEST(ShadowObject) { 7311 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0; 7312 v8::HandleScope handle_scope; 7313 7314 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New(); 7315 LocalContext context(NULL, global_template); 7316 7317 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 7318 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet); 7319 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet); 7320 Local<ObjectTemplate> proto = t->PrototypeTemplate(); 7321 Local<ObjectTemplate> instance = t->InstanceTemplate(); 7322 7323 // Only allow calls of f on instances of t. 7324 Local<v8::Signature> signature = v8::Signature::New(t); 7325 proto->Set(v8_str("f"), 7326 v8::FunctionTemplate::New(ShadowFunctionCallback, 7327 Local<Value>(), 7328 signature)); 7329 proto->Set(v8_str("x"), v8_num(12)); 7330 7331 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter); 7332 7333 Local<Value> o = t->GetFunction()->NewInstance(); 7334 context->Global()->Set(v8_str("__proto__"), o); 7335 7336 Local<Value> value = 7337 Script::Compile(v8_str("this.propertyIsEnumerable(0)"))->Run(); 7338 CHECK(value->IsBoolean()); 7339 CHECK(!value->BooleanValue()); 7340 7341 value = Script::Compile(v8_str("x"))->Run(); 7342 CHECK_EQ(12, value->Int32Value()); 7343 7344 value = Script::Compile(v8_str("f()"))->Run(); 7345 CHECK_EQ(42, value->Int32Value()); 7346 7347 Script::Compile(v8_str("y = 42"))->Run(); 7348 CHECK_EQ(1, shadow_y_setter_call_count); 7349 value = Script::Compile(v8_str("y"))->Run(); 7350 CHECK_EQ(1, shadow_y_getter_call_count); 7351 CHECK_EQ(42, value->Int32Value()); 7352} 7353 7354 7355THREADED_TEST(HiddenPrototype) { 7356 v8::HandleScope handle_scope; 7357 LocalContext context; 7358 7359 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 7360 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 7361 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 7362 t1->SetHiddenPrototype(true); 7363 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 7364 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 7365 t2->SetHiddenPrototype(true); 7366 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 7367 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 7368 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 7369 7370 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 7371 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 7372 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 7373 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 7374 7375 // Setting the prototype on an object skips hidden prototypes. 7376 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 7377 o0->Set(v8_str("__proto__"), o1); 7378 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 7379 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 7380 o0->Set(v8_str("__proto__"), o2); 7381 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 7382 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 7383 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 7384 o0->Set(v8_str("__proto__"), o3); 7385 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 7386 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 7387 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 7388 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 7389 7390 // Getting the prototype of o0 should get the first visible one 7391 // which is o3. Therefore, z should not be defined on the prototype 7392 // object. 7393 Local<Value> proto = o0->Get(v8_str("__proto__")); 7394 CHECK(proto->IsObject()); 7395 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined()); 7396} 7397 7398 7399THREADED_TEST(SetPrototype) { 7400 v8::HandleScope handle_scope; 7401 LocalContext context; 7402 7403 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 7404 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 7405 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 7406 t1->SetHiddenPrototype(true); 7407 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 7408 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 7409 t2->SetHiddenPrototype(true); 7410 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 7411 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 7412 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 7413 7414 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 7415 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 7416 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 7417 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 7418 7419 // Setting the prototype on an object does not skip hidden prototypes. 7420 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 7421 CHECK(o0->SetPrototype(o1)); 7422 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 7423 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 7424 CHECK(o1->SetPrototype(o2)); 7425 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 7426 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 7427 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 7428 CHECK(o2->SetPrototype(o3)); 7429 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 7430 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 7431 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 7432 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 7433 7434 // Getting the prototype of o0 should get the first visible one 7435 // which is o3. Therefore, z should not be defined on the prototype 7436 // object. 7437 Local<Value> proto = o0->Get(v8_str("__proto__")); 7438 CHECK(proto->IsObject()); 7439 CHECK_EQ(proto.As<v8::Object>(), o3); 7440 7441 // However, Object::GetPrototype ignores hidden prototype. 7442 Local<Value> proto0 = o0->GetPrototype(); 7443 CHECK(proto0->IsObject()); 7444 CHECK_EQ(proto0.As<v8::Object>(), o1); 7445 7446 Local<Value> proto1 = o1->GetPrototype(); 7447 CHECK(proto1->IsObject()); 7448 CHECK_EQ(proto1.As<v8::Object>(), o2); 7449 7450 Local<Value> proto2 = o2->GetPrototype(); 7451 CHECK(proto2->IsObject()); 7452 CHECK_EQ(proto2.As<v8::Object>(), o3); 7453} 7454 7455 7456// Getting property names of an object with a prototype chain that 7457// triggers dictionary elements in GetLocalPropertyNames() shouldn't 7458// crash the runtime. 7459THREADED_TEST(Regress91517) { 7460 i::FLAG_allow_natives_syntax = true; 7461 v8::HandleScope handle_scope; 7462 LocalContext context; 7463 7464 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 7465 t1->SetHiddenPrototype(true); 7466 t1->InstanceTemplate()->Set(v8_str("foo"), v8_num(1)); 7467 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 7468 t2->SetHiddenPrototype(true); 7469 t2->InstanceTemplate()->Set(v8_str("fuz1"), v8_num(2)); 7470 t2->InstanceTemplate()->Set(v8_str("objects"), v8::Object::New()); 7471 t2->InstanceTemplate()->Set(v8_str("fuz2"), v8_num(2)); 7472 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 7473 t3->SetHiddenPrototype(true); 7474 t3->InstanceTemplate()->Set(v8_str("boo"), v8_num(3)); 7475 Local<v8::FunctionTemplate> t4 = v8::FunctionTemplate::New(); 7476 t4->InstanceTemplate()->Set(v8_str("baz"), v8_num(4)); 7477 7478 // Force dictionary-based properties. 7479 i::ScopedVector<char> name_buf(1024); 7480 for (int i = 1; i <= 1000; i++) { 7481 i::OS::SNPrintF(name_buf, "sdf%d", i); 7482 t2->InstanceTemplate()->Set(v8_str(name_buf.start()), v8_num(2)); 7483 } 7484 7485 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 7486 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 7487 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 7488 Local<v8::Object> o4 = t4->GetFunction()->NewInstance(); 7489 7490 // Create prototype chain of hidden prototypes. 7491 CHECK(o4->SetPrototype(o3)); 7492 CHECK(o3->SetPrototype(o2)); 7493 CHECK(o2->SetPrototype(o1)); 7494 7495 // Call the runtime version of GetLocalPropertyNames() on the natively 7496 // created object through JavaScript. 7497 context->Global()->Set(v8_str("obj"), o4); 7498 CompileRun("var names = %GetLocalPropertyNames(obj);"); 7499 7500 ExpectInt32("names.length", 1006); 7501 ExpectTrue("names.indexOf(\"baz\") >= 0"); 7502 ExpectTrue("names.indexOf(\"boo\") >= 0"); 7503 ExpectTrue("names.indexOf(\"foo\") >= 0"); 7504 ExpectTrue("names.indexOf(\"fuz1\") >= 0"); 7505 ExpectTrue("names.indexOf(\"fuz2\") >= 0"); 7506 ExpectFalse("names[1005] == undefined"); 7507} 7508 7509 7510THREADED_TEST(FunctionReadOnlyPrototype) { 7511 v8::HandleScope handle_scope; 7512 LocalContext context; 7513 7514 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 7515 t1->PrototypeTemplate()->Set(v8_str("x"), v8::Integer::New(42)); 7516 t1->ReadOnlyPrototype(); 7517 context->Global()->Set(v8_str("func1"), t1->GetFunction()); 7518 // Configured value of ReadOnly flag. 7519 CHECK(CompileRun( 7520 "(function() {" 7521 " descriptor = Object.getOwnPropertyDescriptor(func1, 'prototype');" 7522 " return (descriptor['writable'] == false);" 7523 "})()")->BooleanValue()); 7524 CHECK_EQ(42, CompileRun("func1.prototype.x")->Int32Value()); 7525 CHECK_EQ(42, 7526 CompileRun("func1.prototype = {}; func1.prototype.x")->Int32Value()); 7527 7528 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 7529 t2->PrototypeTemplate()->Set(v8_str("x"), v8::Integer::New(42)); 7530 context->Global()->Set(v8_str("func2"), t2->GetFunction()); 7531 // Default value of ReadOnly flag. 7532 CHECK(CompileRun( 7533 "(function() {" 7534 " descriptor = Object.getOwnPropertyDescriptor(func2, 'prototype');" 7535 " return (descriptor['writable'] == true);" 7536 "})()")->BooleanValue()); 7537 CHECK_EQ(42, CompileRun("func2.prototype.x")->Int32Value()); 7538} 7539 7540 7541THREADED_TEST(SetPrototypeThrows) { 7542 v8::HandleScope handle_scope; 7543 LocalContext context; 7544 7545 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 7546 7547 Local<v8::Object> o0 = t->GetFunction()->NewInstance(); 7548 Local<v8::Object> o1 = t->GetFunction()->NewInstance(); 7549 7550 CHECK(o0->SetPrototype(o1)); 7551 // If setting the prototype leads to the cycle, SetPrototype should 7552 // return false and keep VM in sane state. 7553 v8::TryCatch try_catch; 7554 CHECK(!o1->SetPrototype(o0)); 7555 CHECK(!try_catch.HasCaught()); 7556 ASSERT(!i::Isolate::Current()->has_pending_exception()); 7557 7558 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value()); 7559} 7560 7561 7562THREADED_TEST(GetterSetterExceptions) { 7563 v8::HandleScope handle_scope; 7564 LocalContext context; 7565 CompileRun( 7566 "function Foo() { };" 7567 "function Throw() { throw 5; };" 7568 "var x = { };" 7569 "x.__defineSetter__('set', Throw);" 7570 "x.__defineGetter__('get', Throw);"); 7571 Local<v8::Object> x = 7572 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x"))); 7573 v8::TryCatch try_catch; 7574 x->Set(v8_str("set"), v8::Integer::New(8)); 7575 x->Get(v8_str("get")); 7576 x->Set(v8_str("set"), v8::Integer::New(8)); 7577 x->Get(v8_str("get")); 7578 x->Set(v8_str("set"), v8::Integer::New(8)); 7579 x->Get(v8_str("get")); 7580 x->Set(v8_str("set"), v8::Integer::New(8)); 7581 x->Get(v8_str("get")); 7582} 7583 7584 7585THREADED_TEST(Constructor) { 7586 v8::HandleScope handle_scope; 7587 LocalContext context; 7588 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 7589 templ->SetClassName(v8_str("Fun")); 7590 Local<Function> cons = templ->GetFunction(); 7591 context->Global()->Set(v8_str("Fun"), cons); 7592 Local<v8::Object> inst = cons->NewInstance(); 7593 i::Handle<i::JSObject> obj(v8::Utils::OpenHandle(*inst)); 7594 Local<Value> value = CompileRun("(new Fun()).constructor === Fun"); 7595 CHECK(value->BooleanValue()); 7596} 7597 7598 7599static Handle<Value> ConstructorCallback(const Arguments& args) { 7600 ApiTestFuzzer::Fuzz(); 7601 Local<Object> This; 7602 7603 if (args.IsConstructCall()) { 7604 Local<Object> Holder = args.Holder(); 7605 This = Object::New(); 7606 Local<Value> proto = Holder->GetPrototype(); 7607 if (proto->IsObject()) { 7608 This->SetPrototype(proto); 7609 } 7610 } else { 7611 This = args.This(); 7612 } 7613 7614 This->Set(v8_str("a"), args[0]); 7615 return This; 7616} 7617 7618 7619static Handle<Value> FakeConstructorCallback(const Arguments& args) { 7620 ApiTestFuzzer::Fuzz(); 7621 return args[0]; 7622} 7623 7624 7625THREADED_TEST(ConstructorForObject) { 7626 v8::HandleScope handle_scope; 7627 LocalContext context; 7628 7629 { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); 7630 instance_template->SetCallAsFunctionHandler(ConstructorCallback); 7631 Local<Object> instance = instance_template->NewInstance(); 7632 context->Global()->Set(v8_str("obj"), instance); 7633 v8::TryCatch try_catch; 7634 Local<Value> value; 7635 CHECK(!try_catch.HasCaught()); 7636 7637 // Call the Object's constructor with a 32-bit signed integer. 7638 value = CompileRun("(function() { var o = new obj(28); return o.a; })()"); 7639 CHECK(!try_catch.HasCaught()); 7640 CHECK(value->IsInt32()); 7641 CHECK_EQ(28, value->Int32Value()); 7642 7643 Local<Value> args1[] = { v8_num(28) }; 7644 Local<Value> value_obj1 = instance->CallAsConstructor(1, args1); 7645 CHECK(value_obj1->IsObject()); 7646 Local<Object> object1 = Local<Object>::Cast(value_obj1); 7647 value = object1->Get(v8_str("a")); 7648 CHECK(value->IsInt32()); 7649 CHECK(!try_catch.HasCaught()); 7650 CHECK_EQ(28, value->Int32Value()); 7651 7652 // Call the Object's constructor with a String. 7653 value = CompileRun( 7654 "(function() { var o = new obj('tipli'); return o.a; })()"); 7655 CHECK(!try_catch.HasCaught()); 7656 CHECK(value->IsString()); 7657 String::AsciiValue string_value1(value->ToString()); 7658 CHECK_EQ("tipli", *string_value1); 7659 7660 Local<Value> args2[] = { v8_str("tipli") }; 7661 Local<Value> value_obj2 = instance->CallAsConstructor(1, args2); 7662 CHECK(value_obj2->IsObject()); 7663 Local<Object> object2 = Local<Object>::Cast(value_obj2); 7664 value = object2->Get(v8_str("a")); 7665 CHECK(!try_catch.HasCaught()); 7666 CHECK(value->IsString()); 7667 String::AsciiValue string_value2(value->ToString()); 7668 CHECK_EQ("tipli", *string_value2); 7669 7670 // Call the Object's constructor with a Boolean. 7671 value = CompileRun("(function() { var o = new obj(true); return o.a; })()"); 7672 CHECK(!try_catch.HasCaught()); 7673 CHECK(value->IsBoolean()); 7674 CHECK_EQ(true, value->BooleanValue()); 7675 7676 Handle<Value> args3[] = { v8::True() }; 7677 Local<Value> value_obj3 = instance->CallAsConstructor(1, args3); 7678 CHECK(value_obj3->IsObject()); 7679 Local<Object> object3 = Local<Object>::Cast(value_obj3); 7680 value = object3->Get(v8_str("a")); 7681 CHECK(!try_catch.HasCaught()); 7682 CHECK(value->IsBoolean()); 7683 CHECK_EQ(true, value->BooleanValue()); 7684 7685 // Call the Object's constructor with undefined. 7686 Handle<Value> args4[] = { v8::Undefined() }; 7687 Local<Value> value_obj4 = instance->CallAsConstructor(1, args4); 7688 CHECK(value_obj4->IsObject()); 7689 Local<Object> object4 = Local<Object>::Cast(value_obj4); 7690 value = object4->Get(v8_str("a")); 7691 CHECK(!try_catch.HasCaught()); 7692 CHECK(value->IsUndefined()); 7693 7694 // Call the Object's constructor with null. 7695 Handle<Value> args5[] = { v8::Null() }; 7696 Local<Value> value_obj5 = instance->CallAsConstructor(1, args5); 7697 CHECK(value_obj5->IsObject()); 7698 Local<Object> object5 = Local<Object>::Cast(value_obj5); 7699 value = object5->Get(v8_str("a")); 7700 CHECK(!try_catch.HasCaught()); 7701 CHECK(value->IsNull()); 7702 } 7703 7704 // Check exception handling when there is no constructor set for the Object. 7705 { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); 7706 Local<Object> instance = instance_template->NewInstance(); 7707 context->Global()->Set(v8_str("obj2"), instance); 7708 v8::TryCatch try_catch; 7709 Local<Value> value; 7710 CHECK(!try_catch.HasCaught()); 7711 7712 value = CompileRun("new obj2(28)"); 7713 CHECK(try_catch.HasCaught()); 7714 String::AsciiValue exception_value1(try_catch.Exception()); 7715 CHECK_EQ("TypeError: object is not a function", *exception_value1); 7716 try_catch.Reset(); 7717 7718 Local<Value> args[] = { v8_num(29) }; 7719 value = instance->CallAsConstructor(1, args); 7720 CHECK(try_catch.HasCaught()); 7721 String::AsciiValue exception_value2(try_catch.Exception()); 7722 CHECK_EQ("TypeError: #<Object> is not a function", *exception_value2); 7723 try_catch.Reset(); 7724 } 7725 7726 // Check the case when constructor throws exception. 7727 { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); 7728 instance_template->SetCallAsFunctionHandler(ThrowValue); 7729 Local<Object> instance = instance_template->NewInstance(); 7730 context->Global()->Set(v8_str("obj3"), instance); 7731 v8::TryCatch try_catch; 7732 Local<Value> value; 7733 CHECK(!try_catch.HasCaught()); 7734 7735 value = CompileRun("new obj3(22)"); 7736 CHECK(try_catch.HasCaught()); 7737 String::AsciiValue exception_value1(try_catch.Exception()); 7738 CHECK_EQ("22", *exception_value1); 7739 try_catch.Reset(); 7740 7741 Local<Value> args[] = { v8_num(23) }; 7742 value = instance->CallAsConstructor(1, args); 7743 CHECK(try_catch.HasCaught()); 7744 String::AsciiValue exception_value2(try_catch.Exception()); 7745 CHECK_EQ("23", *exception_value2); 7746 try_catch.Reset(); 7747 } 7748 7749 // Check whether constructor returns with an object or non-object. 7750 { Local<FunctionTemplate> function_template = 7751 FunctionTemplate::New(FakeConstructorCallback); 7752 Local<Function> function = function_template->GetFunction(); 7753 Local<Object> instance1 = function; 7754 context->Global()->Set(v8_str("obj4"), instance1); 7755 v8::TryCatch try_catch; 7756 Local<Value> value; 7757 CHECK(!try_catch.HasCaught()); 7758 7759 CHECK(instance1->IsObject()); 7760 CHECK(instance1->IsFunction()); 7761 7762 value = CompileRun("new obj4(28)"); 7763 CHECK(!try_catch.HasCaught()); 7764 CHECK(value->IsObject()); 7765 7766 Local<Value> args1[] = { v8_num(28) }; 7767 value = instance1->CallAsConstructor(1, args1); 7768 CHECK(!try_catch.HasCaught()); 7769 CHECK(value->IsObject()); 7770 7771 Local<ObjectTemplate> instance_template = ObjectTemplate::New(); 7772 instance_template->SetCallAsFunctionHandler(FakeConstructorCallback); 7773 Local<Object> instance2 = instance_template->NewInstance(); 7774 context->Global()->Set(v8_str("obj5"), instance2); 7775 CHECK(!try_catch.HasCaught()); 7776 7777 CHECK(instance2->IsObject()); 7778 CHECK(!instance2->IsFunction()); 7779 7780 value = CompileRun("new obj5(28)"); 7781 CHECK(!try_catch.HasCaught()); 7782 CHECK(!value->IsObject()); 7783 7784 Local<Value> args2[] = { v8_num(28) }; 7785 value = instance2->CallAsConstructor(1, args2); 7786 CHECK(!try_catch.HasCaught()); 7787 CHECK(!value->IsObject()); 7788 } 7789} 7790 7791 7792THREADED_TEST(FunctionDescriptorException) { 7793 v8::HandleScope handle_scope; 7794 LocalContext context; 7795 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 7796 templ->SetClassName(v8_str("Fun")); 7797 Local<Function> cons = templ->GetFunction(); 7798 context->Global()->Set(v8_str("Fun"), cons); 7799 Local<Value> value = CompileRun( 7800 "function test() {" 7801 " try {" 7802 " (new Fun()).blah()" 7803 " } catch (e) {" 7804 " var str = String(e);" 7805 " if (str.indexOf('TypeError') == -1) return 1;" 7806 " if (str.indexOf('[object Fun]') != -1) return 2;" 7807 " if (str.indexOf('#<Fun>') == -1) return 3;" 7808 " return 0;" 7809 " }" 7810 " return 4;" 7811 "}" 7812 "test();"); 7813 CHECK_EQ(0, value->Int32Value()); 7814} 7815 7816 7817THREADED_TEST(EvalAliasedDynamic) { 7818 v8::HandleScope scope; 7819 LocalContext current; 7820 7821 // Tests where aliased eval can only be resolved dynamically. 7822 Local<Script> script = 7823 Script::Compile(v8_str("function f(x) { " 7824 " var foo = 2;" 7825 " with (x) { return eval('foo'); }" 7826 "}" 7827 "foo = 0;" 7828 "result1 = f(new Object());" 7829 "result2 = f(this);" 7830 "var x = new Object();" 7831 "x.eval = function(x) { return 1; };" 7832 "result3 = f(x);")); 7833 script->Run(); 7834 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value()); 7835 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value()); 7836 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value()); 7837 7838 v8::TryCatch try_catch; 7839 script = 7840 Script::Compile(v8_str("function f(x) { " 7841 " var bar = 2;" 7842 " with (x) { return eval('bar'); }" 7843 "}" 7844 "result4 = f(this)")); 7845 script->Run(); 7846 CHECK(!try_catch.HasCaught()); 7847 CHECK_EQ(2, current->Global()->Get(v8_str("result4"))->Int32Value()); 7848 7849 try_catch.Reset(); 7850} 7851 7852 7853THREADED_TEST(CrossEval) { 7854 v8::HandleScope scope; 7855 LocalContext other; 7856 LocalContext current; 7857 7858 Local<String> token = v8_str("<security token>"); 7859 other->SetSecurityToken(token); 7860 current->SetSecurityToken(token); 7861 7862 // Setup reference from current to other. 7863 current->Global()->Set(v8_str("other"), other->Global()); 7864 7865 // Check that new variables are introduced in other context. 7866 Local<Script> script = 7867 Script::Compile(v8_str("other.eval('var foo = 1234')")); 7868 script->Run(); 7869 Local<Value> foo = other->Global()->Get(v8_str("foo")); 7870 CHECK_EQ(1234, foo->Int32Value()); 7871 CHECK(!current->Global()->Has(v8_str("foo"))); 7872 7873 // Check that writing to non-existing properties introduces them in 7874 // the other context. 7875 script = 7876 Script::Compile(v8_str("other.eval('na = 1234')")); 7877 script->Run(); 7878 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value()); 7879 CHECK(!current->Global()->Has(v8_str("na"))); 7880 7881 // Check that global variables in current context are not visible in other 7882 // context. 7883 v8::TryCatch try_catch; 7884 script = 7885 Script::Compile(v8_str("var bar = 42; other.eval('bar');")); 7886 Local<Value> result = script->Run(); 7887 CHECK(try_catch.HasCaught()); 7888 try_catch.Reset(); 7889 7890 // Check that local variables in current context are not visible in other 7891 // context. 7892 script = 7893 Script::Compile(v8_str("(function() { " 7894 " var baz = 87;" 7895 " return other.eval('baz');" 7896 "})();")); 7897 result = script->Run(); 7898 CHECK(try_catch.HasCaught()); 7899 try_catch.Reset(); 7900 7901 // Check that global variables in the other environment are visible 7902 // when evaluting code. 7903 other->Global()->Set(v8_str("bis"), v8_num(1234)); 7904 script = Script::Compile(v8_str("other.eval('bis')")); 7905 CHECK_EQ(1234, script->Run()->Int32Value()); 7906 CHECK(!try_catch.HasCaught()); 7907 7908 // Check that the 'this' pointer points to the global object evaluating 7909 // code. 7910 other->Global()->Set(v8_str("t"), other->Global()); 7911 script = Script::Compile(v8_str("other.eval('this == t')")); 7912 result = script->Run(); 7913 CHECK(result->IsTrue()); 7914 CHECK(!try_catch.HasCaught()); 7915 7916 // Check that variables introduced in with-statement are not visible in 7917 // other context. 7918 script = 7919 Script::Compile(v8_str("with({x:2}){other.eval('x')}")); 7920 result = script->Run(); 7921 CHECK(try_catch.HasCaught()); 7922 try_catch.Reset(); 7923 7924 // Check that you cannot use 'eval.call' with another object than the 7925 // current global object. 7926 script = 7927 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')")); 7928 result = script->Run(); 7929 CHECK(try_catch.HasCaught()); 7930} 7931 7932 7933// Test that calling eval in a context which has been detached from 7934// its global throws an exception. This behavior is consistent with 7935// other JavaScript implementations. 7936THREADED_TEST(EvalInDetachedGlobal) { 7937 v8::HandleScope scope; 7938 7939 v8::Persistent<Context> context0 = Context::New(); 7940 v8::Persistent<Context> context1 = Context::New(); 7941 7942 // Setup function in context0 that uses eval from context0. 7943 context0->Enter(); 7944 v8::Handle<v8::Value> fun = 7945 CompileRun("var x = 42;" 7946 "(function() {" 7947 " var e = eval;" 7948 " return function(s) { return e(s); }" 7949 "})()"); 7950 context0->Exit(); 7951 7952 // Put the function into context1 and call it before and after 7953 // detaching the global. Before detaching, the call succeeds and 7954 // after detaching and exception is thrown. 7955 context1->Enter(); 7956 context1->Global()->Set(v8_str("fun"), fun); 7957 v8::Handle<v8::Value> x_value = CompileRun("fun('x')"); 7958 CHECK_EQ(42, x_value->Int32Value()); 7959 context0->DetachGlobal(); 7960 v8::TryCatch catcher; 7961 x_value = CompileRun("fun('x')"); 7962 CHECK(x_value.IsEmpty()); 7963 CHECK(catcher.HasCaught()); 7964 context1->Exit(); 7965 7966 context1.Dispose(); 7967 context0.Dispose(); 7968} 7969 7970 7971THREADED_TEST(CrossLazyLoad) { 7972 v8::HandleScope scope; 7973 LocalContext other; 7974 LocalContext current; 7975 7976 Local<String> token = v8_str("<security token>"); 7977 other->SetSecurityToken(token); 7978 current->SetSecurityToken(token); 7979 7980 // Setup reference from current to other. 7981 current->Global()->Set(v8_str("other"), other->Global()); 7982 7983 // Trigger lazy loading in other context. 7984 Local<Script> script = 7985 Script::Compile(v8_str("other.eval('new Date(42)')")); 7986 Local<Value> value = script->Run(); 7987 CHECK_EQ(42.0, value->NumberValue()); 7988} 7989 7990 7991static v8::Handle<Value> call_as_function(const v8::Arguments& args) { 7992 ApiTestFuzzer::Fuzz(); 7993 if (args.IsConstructCall()) { 7994 if (args[0]->IsInt32()) { 7995 return v8_num(-args[0]->Int32Value()); 7996 } 7997 } 7998 7999 return args[0]; 8000} 8001 8002 8003// Test that a call handler can be set for objects which will allow 8004// non-function objects created through the API to be called as 8005// functions. 8006THREADED_TEST(CallAsFunction) { 8007 v8::HandleScope scope; 8008 LocalContext context; 8009 8010 { Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 8011 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 8012 instance_template->SetCallAsFunctionHandler(call_as_function); 8013 Local<v8::Object> instance = t->GetFunction()->NewInstance(); 8014 context->Global()->Set(v8_str("obj"), instance); 8015 v8::TryCatch try_catch; 8016 Local<Value> value; 8017 CHECK(!try_catch.HasCaught()); 8018 8019 value = CompileRun("obj(42)"); 8020 CHECK(!try_catch.HasCaught()); 8021 CHECK_EQ(42, value->Int32Value()); 8022 8023 value = CompileRun("(function(o){return o(49)})(obj)"); 8024 CHECK(!try_catch.HasCaught()); 8025 CHECK_EQ(49, value->Int32Value()); 8026 8027 // test special case of call as function 8028 value = CompileRun("[obj]['0'](45)"); 8029 CHECK(!try_catch.HasCaught()); 8030 CHECK_EQ(45, value->Int32Value()); 8031 8032 value = CompileRun("obj.call = Function.prototype.call;" 8033 "obj.call(null, 87)"); 8034 CHECK(!try_catch.HasCaught()); 8035 CHECK_EQ(87, value->Int32Value()); 8036 8037 // Regression tests for bug #1116356: Calling call through call/apply 8038 // must work for non-function receivers. 8039 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])"; 8040 value = CompileRun(apply_99); 8041 CHECK(!try_catch.HasCaught()); 8042 CHECK_EQ(99, value->Int32Value()); 8043 8044 const char* call_17 = "Function.prototype.call.call(obj, this, 17)"; 8045 value = CompileRun(call_17); 8046 CHECK(!try_catch.HasCaught()); 8047 CHECK_EQ(17, value->Int32Value()); 8048 8049 // Check that the call-as-function handler can be called through 8050 // new. 8051 value = CompileRun("new obj(43)"); 8052 CHECK(!try_catch.HasCaught()); 8053 CHECK_EQ(-43, value->Int32Value()); 8054 8055 // Check that the call-as-function handler can be called through 8056 // the API. 8057 v8::Handle<Value> args[] = { v8_num(28) }; 8058 value = instance->CallAsFunction(instance, 1, args); 8059 CHECK(!try_catch.HasCaught()); 8060 CHECK_EQ(28, value->Int32Value()); 8061 } 8062 8063 { Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 8064 Local<ObjectTemplate> instance_template(t->InstanceTemplate()); 8065 Local<v8::Object> instance = t->GetFunction()->NewInstance(); 8066 context->Global()->Set(v8_str("obj2"), instance); 8067 v8::TryCatch try_catch; 8068 Local<Value> value; 8069 CHECK(!try_catch.HasCaught()); 8070 8071 // Call an object without call-as-function handler through the JS 8072 value = CompileRun("obj2(28)"); 8073 CHECK(value.IsEmpty()); 8074 CHECK(try_catch.HasCaught()); 8075 String::AsciiValue exception_value1(try_catch.Exception()); 8076 CHECK_EQ("TypeError: Property 'obj2' of object #<Object> is not a function", 8077 *exception_value1); 8078 try_catch.Reset(); 8079 8080 // Call an object without call-as-function handler through the API 8081 value = CompileRun("obj2(28)"); 8082 v8::Handle<Value> args[] = { v8_num(28) }; 8083 value = instance->CallAsFunction(instance, 1, args); 8084 CHECK(value.IsEmpty()); 8085 CHECK(try_catch.HasCaught()); 8086 String::AsciiValue exception_value2(try_catch.Exception()); 8087 CHECK_EQ("TypeError: [object Object] is not a function", *exception_value2); 8088 try_catch.Reset(); 8089 } 8090 8091 { Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 8092 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 8093 instance_template->SetCallAsFunctionHandler(ThrowValue); 8094 Local<v8::Object> instance = t->GetFunction()->NewInstance(); 8095 context->Global()->Set(v8_str("obj3"), instance); 8096 v8::TryCatch try_catch; 8097 Local<Value> value; 8098 CHECK(!try_catch.HasCaught()); 8099 8100 // Catch the exception which is thrown by call-as-function handler 8101 value = CompileRun("obj3(22)"); 8102 CHECK(try_catch.HasCaught()); 8103 String::AsciiValue exception_value1(try_catch.Exception()); 8104 CHECK_EQ("22", *exception_value1); 8105 try_catch.Reset(); 8106 8107 v8::Handle<Value> args[] = { v8_num(23) }; 8108 value = instance->CallAsFunction(instance, 1, args); 8109 CHECK(try_catch.HasCaught()); 8110 String::AsciiValue exception_value2(try_catch.Exception()); 8111 CHECK_EQ("23", *exception_value2); 8112 try_catch.Reset(); 8113 } 8114} 8115 8116 8117// Check whether a non-function object is callable. 8118THREADED_TEST(CallableObject) { 8119 v8::HandleScope scope; 8120 LocalContext context; 8121 8122 { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); 8123 instance_template->SetCallAsFunctionHandler(call_as_function); 8124 Local<Object> instance = instance_template->NewInstance(); 8125 v8::TryCatch try_catch; 8126 8127 CHECK(instance->IsCallable()); 8128 CHECK(!try_catch.HasCaught()); 8129 } 8130 8131 { Local<ObjectTemplate> instance_template = ObjectTemplate::New(); 8132 Local<Object> instance = instance_template->NewInstance(); 8133 v8::TryCatch try_catch; 8134 8135 CHECK(!instance->IsCallable()); 8136 CHECK(!try_catch.HasCaught()); 8137 } 8138 8139 { Local<FunctionTemplate> function_template = 8140 FunctionTemplate::New(call_as_function); 8141 Local<Function> function = function_template->GetFunction(); 8142 Local<Object> instance = function; 8143 v8::TryCatch try_catch; 8144 8145 CHECK(instance->IsCallable()); 8146 CHECK(!try_catch.HasCaught()); 8147 } 8148 8149 { Local<FunctionTemplate> function_template = FunctionTemplate::New(); 8150 Local<Function> function = function_template->GetFunction(); 8151 Local<Object> instance = function; 8152 v8::TryCatch try_catch; 8153 8154 CHECK(instance->IsCallable()); 8155 CHECK(!try_catch.HasCaught()); 8156 } 8157} 8158 8159 8160static int CountHandles() { 8161 return v8::HandleScope::NumberOfHandles(); 8162} 8163 8164 8165static int Recurse(int depth, int iterations) { 8166 v8::HandleScope scope; 8167 if (depth == 0) return CountHandles(); 8168 for (int i = 0; i < iterations; i++) { 8169 Local<v8::Number> n(v8::Integer::New(42)); 8170 } 8171 return Recurse(depth - 1, iterations); 8172} 8173 8174 8175THREADED_TEST(HandleIteration) { 8176 static const int kIterations = 500; 8177 static const int kNesting = 200; 8178 CHECK_EQ(0, CountHandles()); 8179 { 8180 v8::HandleScope scope1; 8181 CHECK_EQ(0, CountHandles()); 8182 for (int i = 0; i < kIterations; i++) { 8183 Local<v8::Number> n(v8::Integer::New(42)); 8184 CHECK_EQ(i + 1, CountHandles()); 8185 } 8186 8187 CHECK_EQ(kIterations, CountHandles()); 8188 { 8189 v8::HandleScope scope2; 8190 for (int j = 0; j < kIterations; j++) { 8191 Local<v8::Number> n(v8::Integer::New(42)); 8192 CHECK_EQ(j + 1 + kIterations, CountHandles()); 8193 } 8194 } 8195 CHECK_EQ(kIterations, CountHandles()); 8196 } 8197 CHECK_EQ(0, CountHandles()); 8198 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations)); 8199} 8200 8201 8202static v8::Handle<Value> InterceptorHasOwnPropertyGetter( 8203 Local<String> name, 8204 const AccessorInfo& info) { 8205 ApiTestFuzzer::Fuzz(); 8206 return v8::Handle<Value>(); 8207} 8208 8209 8210THREADED_TEST(InterceptorHasOwnProperty) { 8211 v8::HandleScope scope; 8212 LocalContext context; 8213 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 8214 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 8215 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter); 8216 Local<Function> function = fun_templ->GetFunction(); 8217 context->Global()->Set(v8_str("constructor"), function); 8218 v8::Handle<Value> value = CompileRun( 8219 "var o = new constructor();" 8220 "o.hasOwnProperty('ostehaps');"); 8221 CHECK_EQ(false, value->BooleanValue()); 8222 value = CompileRun( 8223 "o.ostehaps = 42;" 8224 "o.hasOwnProperty('ostehaps');"); 8225 CHECK_EQ(true, value->BooleanValue()); 8226 value = CompileRun( 8227 "var p = new constructor();" 8228 "p.hasOwnProperty('ostehaps');"); 8229 CHECK_EQ(false, value->BooleanValue()); 8230} 8231 8232 8233static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC( 8234 Local<String> name, 8235 const AccessorInfo& info) { 8236 ApiTestFuzzer::Fuzz(); 8237 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 8238 return v8::Handle<Value>(); 8239} 8240 8241 8242THREADED_TEST(InterceptorHasOwnPropertyCausingGC) { 8243 v8::HandleScope scope; 8244 LocalContext context; 8245 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 8246 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 8247 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC); 8248 Local<Function> function = fun_templ->GetFunction(); 8249 context->Global()->Set(v8_str("constructor"), function); 8250 // Let's first make some stuff so we can be sure to get a good GC. 8251 CompileRun( 8252 "function makestr(size) {" 8253 " switch (size) {" 8254 " case 1: return 'f';" 8255 " case 2: return 'fo';" 8256 " case 3: return 'foo';" 8257 " }" 8258 " return makestr(size >> 1) + makestr((size + 1) >> 1);" 8259 "}" 8260 "var x = makestr(12345);" 8261 "x = makestr(31415);" 8262 "x = makestr(23456);"); 8263 v8::Handle<Value> value = CompileRun( 8264 "var o = new constructor();" 8265 "o.__proto__ = new String(x);" 8266 "o.hasOwnProperty('ostehaps');"); 8267 CHECK_EQ(false, value->BooleanValue()); 8268} 8269 8270 8271typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property, 8272 const AccessorInfo& info); 8273 8274 8275static void CheckInterceptorLoadIC(NamedPropertyGetter getter, 8276 const char* source, 8277 int expected) { 8278 v8::HandleScope scope; 8279 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8280 templ->SetNamedPropertyHandler(getter, 0, 0, 0, 0, v8_str("data")); 8281 LocalContext context; 8282 context->Global()->Set(v8_str("o"), templ->NewInstance()); 8283 v8::Handle<Value> value = CompileRun(source); 8284 CHECK_EQ(expected, value->Int32Value()); 8285} 8286 8287 8288static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name, 8289 const AccessorInfo& info) { 8290 ApiTestFuzzer::Fuzz(); 8291 CHECK_EQ(v8_str("data"), info.Data()); 8292 CHECK_EQ(v8_str("x"), name); 8293 return v8::Integer::New(42); 8294} 8295 8296 8297// This test should hit the load IC for the interceptor case. 8298THREADED_TEST(InterceptorLoadIC) { 8299 CheckInterceptorLoadIC(InterceptorLoadICGetter, 8300 "var result = 0;" 8301 "for (var i = 0; i < 1000; i++) {" 8302 " result = o.x;" 8303 "}", 8304 42); 8305} 8306 8307 8308// Below go several tests which verify that JITing for various 8309// configurations of interceptor and explicit fields works fine 8310// (those cases are special cased to get better performance). 8311 8312static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name, 8313 const AccessorInfo& info) { 8314 ApiTestFuzzer::Fuzz(); 8315 return v8_str("x")->Equals(name) 8316 ? v8::Integer::New(42) : v8::Handle<v8::Value>(); 8317} 8318 8319 8320THREADED_TEST(InterceptorLoadICWithFieldOnHolder) { 8321 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 8322 "var result = 0;" 8323 "o.y = 239;" 8324 "for (var i = 0; i < 1000; i++) {" 8325 " result = o.y;" 8326 "}", 8327 239); 8328} 8329 8330 8331THREADED_TEST(InterceptorLoadICWithSubstitutedProto) { 8332 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 8333 "var result = 0;" 8334 "o.__proto__ = { 'y': 239 };" 8335 "for (var i = 0; i < 1000; i++) {" 8336 " result = o.y + o.x;" 8337 "}", 8338 239 + 42); 8339} 8340 8341 8342THREADED_TEST(InterceptorLoadICWithPropertyOnProto) { 8343 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 8344 "var result = 0;" 8345 "o.__proto__.y = 239;" 8346 "for (var i = 0; i < 1000; i++) {" 8347 " result = o.y + o.x;" 8348 "}", 8349 239 + 42); 8350} 8351 8352 8353THREADED_TEST(InterceptorLoadICUndefined) { 8354 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 8355 "var result = 0;" 8356 "for (var i = 0; i < 1000; i++) {" 8357 " result = (o.y == undefined) ? 239 : 42;" 8358 "}", 8359 239); 8360} 8361 8362 8363THREADED_TEST(InterceptorLoadICWithOverride) { 8364 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 8365 "fst = new Object(); fst.__proto__ = o;" 8366 "snd = new Object(); snd.__proto__ = fst;" 8367 "var result1 = 0;" 8368 "for (var i = 0; i < 1000; i++) {" 8369 " result1 = snd.x;" 8370 "}" 8371 "fst.x = 239;" 8372 "var result = 0;" 8373 "for (var i = 0; i < 1000; i++) {" 8374 " result = snd.x;" 8375 "}" 8376 "result + result1", 8377 239 + 42); 8378} 8379 8380 8381// Test the case when we stored field into 8382// a stub, but interceptor produced value on its own. 8383THREADED_TEST(InterceptorLoadICFieldNotNeeded) { 8384 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 8385 "proto = new Object();" 8386 "o.__proto__ = proto;" 8387 "proto.x = 239;" 8388 "for (var i = 0; i < 1000; i++) {" 8389 " o.x;" 8390 // Now it should be ICed and keep a reference to x defined on proto 8391 "}" 8392 "var result = 0;" 8393 "for (var i = 0; i < 1000; i++) {" 8394 " result += o.x;" 8395 "}" 8396 "result;", 8397 42 * 1000); 8398} 8399 8400 8401// Test the case when we stored field into 8402// a stub, but it got invalidated later on. 8403THREADED_TEST(InterceptorLoadICInvalidatedField) { 8404 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 8405 "proto1 = new Object();" 8406 "proto2 = new Object();" 8407 "o.__proto__ = proto1;" 8408 "proto1.__proto__ = proto2;" 8409 "proto2.y = 239;" 8410 "for (var i = 0; i < 1000; i++) {" 8411 " o.y;" 8412 // Now it should be ICed and keep a reference to y defined on proto2 8413 "}" 8414 "proto1.y = 42;" 8415 "var result = 0;" 8416 "for (var i = 0; i < 1000; i++) {" 8417 " result += o.y;" 8418 "}" 8419 "result;", 8420 42 * 1000); 8421} 8422 8423 8424static int interceptor_load_not_handled_calls = 0; 8425static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name, 8426 const AccessorInfo& info) { 8427 ++interceptor_load_not_handled_calls; 8428 return v8::Handle<v8::Value>(); 8429} 8430 8431 8432// Test how post-interceptor lookups are done in the non-cacheable 8433// case: the interceptor should not be invoked during this lookup. 8434THREADED_TEST(InterceptorLoadICPostInterceptor) { 8435 interceptor_load_not_handled_calls = 0; 8436 CheckInterceptorLoadIC(InterceptorLoadNotHandled, 8437 "receiver = new Object();" 8438 "receiver.__proto__ = o;" 8439 "proto = new Object();" 8440 "/* Make proto a slow-case object. */" 8441 "for (var i = 0; i < 1000; i++) {" 8442 " proto[\"xxxxxxxx\" + i] = [];" 8443 "}" 8444 "proto.x = 17;" 8445 "o.__proto__ = proto;" 8446 "var result = 0;" 8447 "for (var i = 0; i < 1000; i++) {" 8448 " result += receiver.x;" 8449 "}" 8450 "result;", 8451 17 * 1000); 8452 CHECK_EQ(1000, interceptor_load_not_handled_calls); 8453} 8454 8455 8456// Test the case when we stored field into 8457// a stub, but it got invalidated later on due to override on 8458// global object which is between interceptor and fields' holders. 8459THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) { 8460 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 8461 "o.__proto__ = this;" // set a global to be a proto of o. 8462 "this.__proto__.y = 239;" 8463 "for (var i = 0; i < 10; i++) {" 8464 " if (o.y != 239) throw 'oops: ' + o.y;" 8465 // Now it should be ICed and keep a reference to y defined on field_holder. 8466 "}" 8467 "this.y = 42;" // Assign on a global. 8468 "var result = 0;" 8469 "for (var i = 0; i < 10; i++) {" 8470 " result += o.y;" 8471 "}" 8472 "result;", 8473 42 * 10); 8474} 8475 8476 8477static void SetOnThis(Local<String> name, 8478 Local<Value> value, 8479 const AccessorInfo& info) { 8480 info.This()->ForceSet(name, value); 8481} 8482 8483 8484THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) { 8485 v8::HandleScope scope; 8486 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8487 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 8488 templ->SetAccessor(v8_str("y"), Return239); 8489 LocalContext context; 8490 context->Global()->Set(v8_str("o"), templ->NewInstance()); 8491 8492 // Check the case when receiver and interceptor's holder 8493 // are the same objects. 8494 v8::Handle<Value> value = CompileRun( 8495 "var result = 0;" 8496 "for (var i = 0; i < 7; i++) {" 8497 " result = o.y;" 8498 "}"); 8499 CHECK_EQ(239, value->Int32Value()); 8500 8501 // Check the case when interceptor's holder is in proto chain 8502 // of receiver. 8503 value = CompileRun( 8504 "r = { __proto__: o };" 8505 "var result = 0;" 8506 "for (var i = 0; i < 7; i++) {" 8507 " result = r.y;" 8508 "}"); 8509 CHECK_EQ(239, value->Int32Value()); 8510} 8511 8512 8513THREADED_TEST(InterceptorLoadICWithCallbackOnProto) { 8514 v8::HandleScope scope; 8515 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 8516 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 8517 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 8518 templ_p->SetAccessor(v8_str("y"), Return239); 8519 8520 LocalContext context; 8521 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 8522 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 8523 8524 // Check the case when receiver and interceptor's holder 8525 // are the same objects. 8526 v8::Handle<Value> value = CompileRun( 8527 "o.__proto__ = p;" 8528 "var result = 0;" 8529 "for (var i = 0; i < 7; i++) {" 8530 " result = o.x + o.y;" 8531 "}"); 8532 CHECK_EQ(239 + 42, value->Int32Value()); 8533 8534 // Check the case when interceptor's holder is in proto chain 8535 // of receiver. 8536 value = CompileRun( 8537 "r = { __proto__: o };" 8538 "var result = 0;" 8539 "for (var i = 0; i < 7; i++) {" 8540 " result = r.x + r.y;" 8541 "}"); 8542 CHECK_EQ(239 + 42, value->Int32Value()); 8543} 8544 8545 8546THREADED_TEST(InterceptorLoadICForCallbackWithOverride) { 8547 v8::HandleScope scope; 8548 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8549 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 8550 templ->SetAccessor(v8_str("y"), Return239); 8551 8552 LocalContext context; 8553 context->Global()->Set(v8_str("o"), templ->NewInstance()); 8554 8555 v8::Handle<Value> value = CompileRun( 8556 "fst = new Object(); fst.__proto__ = o;" 8557 "snd = new Object(); snd.__proto__ = fst;" 8558 "var result1 = 0;" 8559 "for (var i = 0; i < 7; i++) {" 8560 " result1 = snd.x;" 8561 "}" 8562 "fst.x = 239;" 8563 "var result = 0;" 8564 "for (var i = 0; i < 7; i++) {" 8565 " result = snd.x;" 8566 "}" 8567 "result + result1"); 8568 CHECK_EQ(239 + 42, value->Int32Value()); 8569} 8570 8571 8572// Test the case when we stored callback into 8573// a stub, but interceptor produced value on its own. 8574THREADED_TEST(InterceptorLoadICCallbackNotNeeded) { 8575 v8::HandleScope scope; 8576 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 8577 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 8578 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 8579 templ_p->SetAccessor(v8_str("y"), Return239); 8580 8581 LocalContext context; 8582 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 8583 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 8584 8585 v8::Handle<Value> value = CompileRun( 8586 "o.__proto__ = p;" 8587 "for (var i = 0; i < 7; i++) {" 8588 " o.x;" 8589 // Now it should be ICed and keep a reference to x defined on p 8590 "}" 8591 "var result = 0;" 8592 "for (var i = 0; i < 7; i++) {" 8593 " result += o.x;" 8594 "}" 8595 "result"); 8596 CHECK_EQ(42 * 7, value->Int32Value()); 8597} 8598 8599 8600// Test the case when we stored callback into 8601// a stub, but it got invalidated later on. 8602THREADED_TEST(InterceptorLoadICInvalidatedCallback) { 8603 v8::HandleScope scope; 8604 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 8605 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 8606 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 8607 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 8608 8609 LocalContext context; 8610 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 8611 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 8612 8613 v8::Handle<Value> value = CompileRun( 8614 "inbetween = new Object();" 8615 "o.__proto__ = inbetween;" 8616 "inbetween.__proto__ = p;" 8617 "for (var i = 0; i < 10; i++) {" 8618 " o.y;" 8619 // Now it should be ICed and keep a reference to y defined on p 8620 "}" 8621 "inbetween.y = 42;" 8622 "var result = 0;" 8623 "for (var i = 0; i < 10; i++) {" 8624 " result += o.y;" 8625 "}" 8626 "result"); 8627 CHECK_EQ(42 * 10, value->Int32Value()); 8628} 8629 8630 8631// Test the case when we stored callback into 8632// a stub, but it got invalidated later on due to override on 8633// global object which is between interceptor and callbacks' holders. 8634THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) { 8635 v8::HandleScope scope; 8636 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 8637 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 8638 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 8639 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 8640 8641 LocalContext context; 8642 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 8643 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 8644 8645 v8::Handle<Value> value = CompileRun( 8646 "o.__proto__ = this;" 8647 "this.__proto__ = p;" 8648 "for (var i = 0; i < 10; i++) {" 8649 " if (o.y != 239) throw 'oops: ' + o.y;" 8650 // Now it should be ICed and keep a reference to y defined on p 8651 "}" 8652 "this.y = 42;" 8653 "var result = 0;" 8654 "for (var i = 0; i < 10; i++) {" 8655 " result += o.y;" 8656 "}" 8657 "result"); 8658 CHECK_EQ(42 * 10, value->Int32Value()); 8659} 8660 8661 8662static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name, 8663 const AccessorInfo& info) { 8664 ApiTestFuzzer::Fuzz(); 8665 CHECK(v8_str("x")->Equals(name)); 8666 return v8::Integer::New(0); 8667} 8668 8669 8670THREADED_TEST(InterceptorReturningZero) { 8671 CheckInterceptorLoadIC(InterceptorLoadICGetter0, 8672 "o.x == undefined ? 1 : 0", 8673 0); 8674} 8675 8676 8677static v8::Handle<Value> InterceptorStoreICSetter( 8678 Local<String> key, Local<Value> value, const AccessorInfo&) { 8679 CHECK(v8_str("x")->Equals(key)); 8680 CHECK_EQ(42, value->Int32Value()); 8681 return value; 8682} 8683 8684 8685// This test should hit the store IC for the interceptor case. 8686THREADED_TEST(InterceptorStoreIC) { 8687 v8::HandleScope scope; 8688 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8689 templ->SetNamedPropertyHandler(InterceptorLoadICGetter, 8690 InterceptorStoreICSetter, 8691 0, 0, 0, v8_str("data")); 8692 LocalContext context; 8693 context->Global()->Set(v8_str("o"), templ->NewInstance()); 8694 v8::Handle<Value> value(CompileRun( 8695 "for (var i = 0; i < 1000; i++) {" 8696 " o.x = 42;" 8697 "}")); 8698} 8699 8700 8701THREADED_TEST(InterceptorStoreICWithNoSetter) { 8702 v8::HandleScope scope; 8703 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8704 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 8705 LocalContext context; 8706 context->Global()->Set(v8_str("o"), templ->NewInstance()); 8707 v8::Handle<Value> value = CompileRun( 8708 "for (var i = 0; i < 1000; i++) {" 8709 " o.y = 239;" 8710 "}" 8711 "42 + o.y"); 8712 CHECK_EQ(239 + 42, value->Int32Value()); 8713} 8714 8715 8716 8717 8718v8::Handle<Value> call_ic_function; 8719v8::Handle<Value> call_ic_function2; 8720v8::Handle<Value> call_ic_function3; 8721 8722static v8::Handle<Value> InterceptorCallICGetter(Local<String> name, 8723 const AccessorInfo& info) { 8724 ApiTestFuzzer::Fuzz(); 8725 CHECK(v8_str("x")->Equals(name)); 8726 return call_ic_function; 8727} 8728 8729 8730// This test should hit the call IC for the interceptor case. 8731THREADED_TEST(InterceptorCallIC) { 8732 v8::HandleScope scope; 8733 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8734 templ->SetNamedPropertyHandler(InterceptorCallICGetter); 8735 LocalContext context; 8736 context->Global()->Set(v8_str("o"), templ->NewInstance()); 8737 call_ic_function = 8738 v8_compile("function f(x) { return x + 1; }; f")->Run(); 8739 v8::Handle<Value> value = CompileRun( 8740 "var result = 0;" 8741 "for (var i = 0; i < 1000; i++) {" 8742 " result = o.x(41);" 8743 "}"); 8744 CHECK_EQ(42, value->Int32Value()); 8745} 8746 8747 8748// This test checks that if interceptor doesn't provide 8749// a value, we can fetch regular value. 8750THREADED_TEST(InterceptorCallICSeesOthers) { 8751 v8::HandleScope scope; 8752 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8753 templ->SetNamedPropertyHandler(NoBlockGetterX); 8754 LocalContext context; 8755 context->Global()->Set(v8_str("o"), templ->NewInstance()); 8756 v8::Handle<Value> value = CompileRun( 8757 "o.x = function f(x) { return x + 1; };" 8758 "var result = 0;" 8759 "for (var i = 0; i < 7; i++) {" 8760 " result = o.x(41);" 8761 "}"); 8762 CHECK_EQ(42, value->Int32Value()); 8763} 8764 8765 8766static v8::Handle<Value> call_ic_function4; 8767static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name, 8768 const AccessorInfo& info) { 8769 ApiTestFuzzer::Fuzz(); 8770 CHECK(v8_str("x")->Equals(name)); 8771 return call_ic_function4; 8772} 8773 8774 8775// This test checks that if interceptor provides a function, 8776// even if we cached shadowed variant, interceptor's function 8777// is invoked 8778THREADED_TEST(InterceptorCallICCacheableNotNeeded) { 8779 v8::HandleScope scope; 8780 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8781 templ->SetNamedPropertyHandler(InterceptorCallICGetter4); 8782 LocalContext context; 8783 context->Global()->Set(v8_str("o"), templ->NewInstance()); 8784 call_ic_function4 = 8785 v8_compile("function f(x) { return x - 1; }; f")->Run(); 8786 v8::Handle<Value> value = CompileRun( 8787 "o.__proto__.x = function(x) { return x + 1; };" 8788 "var result = 0;" 8789 "for (var i = 0; i < 1000; i++) {" 8790 " result = o.x(42);" 8791 "}"); 8792 CHECK_EQ(41, value->Int32Value()); 8793} 8794 8795 8796// Test the case when we stored cacheable lookup into 8797// a stub, but it got invalidated later on 8798THREADED_TEST(InterceptorCallICInvalidatedCacheable) { 8799 v8::HandleScope scope; 8800 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8801 templ->SetNamedPropertyHandler(NoBlockGetterX); 8802 LocalContext context; 8803 context->Global()->Set(v8_str("o"), templ->NewInstance()); 8804 v8::Handle<Value> value = CompileRun( 8805 "proto1 = new Object();" 8806 "proto2 = new Object();" 8807 "o.__proto__ = proto1;" 8808 "proto1.__proto__ = proto2;" 8809 "proto2.y = function(x) { return x + 1; };" 8810 // Invoke it many times to compile a stub 8811 "for (var i = 0; i < 7; i++) {" 8812 " o.y(42);" 8813 "}" 8814 "proto1.y = function(x) { return x - 1; };" 8815 "var result = 0;" 8816 "for (var i = 0; i < 7; i++) {" 8817 " result += o.y(42);" 8818 "}"); 8819 CHECK_EQ(41 * 7, value->Int32Value()); 8820} 8821 8822 8823static v8::Handle<Value> call_ic_function5; 8824static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name, 8825 const AccessorInfo& info) { 8826 ApiTestFuzzer::Fuzz(); 8827 if (v8_str("x")->Equals(name)) 8828 return call_ic_function5; 8829 else 8830 return Local<Value>(); 8831} 8832 8833 8834// This test checks that if interceptor doesn't provide a function, 8835// cached constant function is used 8836THREADED_TEST(InterceptorCallICConstantFunctionUsed) { 8837 v8::HandleScope scope; 8838 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8839 templ->SetNamedPropertyHandler(NoBlockGetterX); 8840 LocalContext context; 8841 context->Global()->Set(v8_str("o"), templ->NewInstance()); 8842 v8::Handle<Value> value = CompileRun( 8843 "function inc(x) { return x + 1; };" 8844 "inc(1);" 8845 "o.x = inc;" 8846 "var result = 0;" 8847 "for (var i = 0; i < 1000; i++) {" 8848 " result = o.x(42);" 8849 "}"); 8850 CHECK_EQ(43, value->Int32Value()); 8851} 8852 8853 8854// This test checks that if interceptor provides a function, 8855// even if we cached constant function, interceptor's function 8856// is invoked 8857THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) { 8858 v8::HandleScope scope; 8859 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8860 templ->SetNamedPropertyHandler(InterceptorCallICGetter5); 8861 LocalContext context; 8862 context->Global()->Set(v8_str("o"), templ->NewInstance()); 8863 call_ic_function5 = 8864 v8_compile("function f(x) { return x - 1; }; f")->Run(); 8865 v8::Handle<Value> value = CompileRun( 8866 "function inc(x) { return x + 1; };" 8867 "inc(1);" 8868 "o.x = inc;" 8869 "var result = 0;" 8870 "for (var i = 0; i < 1000; i++) {" 8871 " result = o.x(42);" 8872 "}"); 8873 CHECK_EQ(41, value->Int32Value()); 8874} 8875 8876 8877// Test the case when we stored constant function into 8878// a stub, but it got invalidated later on 8879THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) { 8880 v8::HandleScope scope; 8881 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8882 templ->SetNamedPropertyHandler(NoBlockGetterX); 8883 LocalContext context; 8884 context->Global()->Set(v8_str("o"), templ->NewInstance()); 8885 v8::Handle<Value> value = CompileRun( 8886 "function inc(x) { return x + 1; };" 8887 "inc(1);" 8888 "proto1 = new Object();" 8889 "proto2 = new Object();" 8890 "o.__proto__ = proto1;" 8891 "proto1.__proto__ = proto2;" 8892 "proto2.y = inc;" 8893 // Invoke it many times to compile a stub 8894 "for (var i = 0; i < 7; i++) {" 8895 " o.y(42);" 8896 "}" 8897 "proto1.y = function(x) { return x - 1; };" 8898 "var result = 0;" 8899 "for (var i = 0; i < 7; i++) {" 8900 " result += o.y(42);" 8901 "}"); 8902 CHECK_EQ(41 * 7, value->Int32Value()); 8903} 8904 8905 8906// Test the case when we stored constant function into 8907// a stub, but it got invalidated later on due to override on 8908// global object which is between interceptor and constant function' holders. 8909THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) { 8910 v8::HandleScope scope; 8911 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 8912 templ->SetNamedPropertyHandler(NoBlockGetterX); 8913 LocalContext context; 8914 context->Global()->Set(v8_str("o"), templ->NewInstance()); 8915 v8::Handle<Value> value = CompileRun( 8916 "function inc(x) { return x + 1; };" 8917 "inc(1);" 8918 "o.__proto__ = this;" 8919 "this.__proto__.y = inc;" 8920 // Invoke it many times to compile a stub 8921 "for (var i = 0; i < 7; i++) {" 8922 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);" 8923 "}" 8924 "this.y = function(x) { return x - 1; };" 8925 "var result = 0;" 8926 "for (var i = 0; i < 7; i++) {" 8927 " result += o.y(42);" 8928 "}"); 8929 CHECK_EQ(41 * 7, value->Int32Value()); 8930} 8931 8932 8933// Test the case when actual function to call sits on global object. 8934THREADED_TEST(InterceptorCallICCachedFromGlobal) { 8935 v8::HandleScope scope; 8936 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 8937 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 8938 8939 LocalContext context; 8940 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 8941 8942 v8::Handle<Value> value = CompileRun( 8943 "try {" 8944 " o.__proto__ = this;" 8945 " for (var i = 0; i < 10; i++) {" 8946 " var v = o.parseFloat('239');" 8947 " if (v != 239) throw v;" 8948 // Now it should be ICed and keep a reference to parseFloat. 8949 " }" 8950 " var result = 0;" 8951 " for (var i = 0; i < 10; i++) {" 8952 " result += o.parseFloat('239');" 8953 " }" 8954 " result" 8955 "} catch(e) {" 8956 " e" 8957 "};"); 8958 CHECK_EQ(239 * 10, value->Int32Value()); 8959} 8960 8961static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name, 8962 const AccessorInfo& info) { 8963 ApiTestFuzzer::Fuzz(); 8964 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data())); 8965 ++(*call_count); 8966 if ((*call_count) % 20 == 0) { 8967 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 8968 } 8969 return v8::Handle<Value>(); 8970} 8971 8972static v8::Handle<Value> FastApiCallback_TrivialSignature( 8973 const v8::Arguments& args) { 8974 ApiTestFuzzer::Fuzz(); 8975 CHECK_EQ(args.This(), args.Holder()); 8976 CHECK(args.Data()->Equals(v8_str("method_data"))); 8977 return v8::Integer::New(args[0]->Int32Value() + 1); 8978} 8979 8980static v8::Handle<Value> FastApiCallback_SimpleSignature( 8981 const v8::Arguments& args) { 8982 ApiTestFuzzer::Fuzz(); 8983 CHECK_EQ(args.This()->GetPrototype(), args.Holder()); 8984 CHECK(args.Data()->Equals(v8_str("method_data"))); 8985 // Note, we're using HasRealNamedProperty instead of Has to avoid 8986 // invoking the interceptor again. 8987 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo"))); 8988 return v8::Integer::New(args[0]->Int32Value() + 1); 8989} 8990 8991// Helper to maximize the odds of object moving. 8992static void GenerateSomeGarbage() { 8993 CompileRun( 8994 "var garbage;" 8995 "for (var i = 0; i < 1000; i++) {" 8996 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];" 8997 "}" 8998 "garbage = undefined;"); 8999} 9000 9001 9002v8::Handle<v8::Value> DirectApiCallback(const v8::Arguments& args) { 9003 static int count = 0; 9004 if (count++ % 3 == 0) { 9005 HEAP-> CollectAllGarbage(true); // This should move the stub 9006 GenerateSomeGarbage(); // This should ensure the old stub memory is flushed 9007 } 9008 return v8::Handle<v8::Value>(); 9009} 9010 9011 9012THREADED_TEST(CallICFastApi_DirectCall_GCMoveStub) { 9013 v8::HandleScope scope; 9014 LocalContext context; 9015 v8::Handle<v8::ObjectTemplate> nativeobject_templ = v8::ObjectTemplate::New(); 9016 nativeobject_templ->Set("callback", 9017 v8::FunctionTemplate::New(DirectApiCallback)); 9018 v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance(); 9019 context->Global()->Set(v8_str("nativeobject"), nativeobject_obj); 9020 // call the api function multiple times to ensure direct call stub creation. 9021 CompileRun( 9022 "function f() {" 9023 " for (var i = 1; i <= 30; i++) {" 9024 " nativeobject.callback();" 9025 " }" 9026 "}" 9027 "f();"); 9028} 9029 9030 9031v8::Handle<v8::Value> ThrowingDirectApiCallback(const v8::Arguments& args) { 9032 return v8::ThrowException(v8_str("g")); 9033} 9034 9035 9036THREADED_TEST(CallICFastApi_DirectCall_Throw) { 9037 v8::HandleScope scope; 9038 LocalContext context; 9039 v8::Handle<v8::ObjectTemplate> nativeobject_templ = v8::ObjectTemplate::New(); 9040 nativeobject_templ->Set("callback", 9041 v8::FunctionTemplate::New(ThrowingDirectApiCallback)); 9042 v8::Local<v8::Object> nativeobject_obj = nativeobject_templ->NewInstance(); 9043 context->Global()->Set(v8_str("nativeobject"), nativeobject_obj); 9044 // call the api function multiple times to ensure direct call stub creation. 9045 v8::Handle<Value> result = CompileRun( 9046 "var result = '';" 9047 "function f() {" 9048 " for (var i = 1; i <= 5; i++) {" 9049 " try { nativeobject.callback(); } catch (e) { result += e; }" 9050 " }" 9051 "}" 9052 "f(); result;"); 9053 CHECK_EQ(v8_str("ggggg"), result); 9054} 9055 9056 9057v8::Handle<v8::Value> DirectGetterCallback(Local<String> name, 9058 const v8::AccessorInfo& info) { 9059 if (++p_getter_count % 3 == 0) { 9060 HEAP->CollectAllGarbage(true); 9061 GenerateSomeGarbage(); 9062 } 9063 return v8::Handle<v8::Value>(); 9064} 9065 9066 9067THREADED_TEST(LoadICFastApi_DirectCall_GCMoveStub) { 9068 v8::HandleScope scope; 9069 LocalContext context; 9070 v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(); 9071 obj->SetAccessor(v8_str("p1"), DirectGetterCallback); 9072 context->Global()->Set(v8_str("o1"), obj->NewInstance()); 9073 p_getter_count = 0; 9074 CompileRun( 9075 "function f() {" 9076 " for (var i = 0; i < 30; i++) o1.p1;" 9077 "}" 9078 "f();"); 9079 CHECK_EQ(30, p_getter_count); 9080} 9081 9082 9083v8::Handle<v8::Value> ThrowingDirectGetterCallback( 9084 Local<String> name, const v8::AccessorInfo& info) { 9085 return v8::ThrowException(v8_str("g")); 9086} 9087 9088 9089THREADED_TEST(LoadICFastApi_DirectCall_Throw) { 9090 v8::HandleScope scope; 9091 LocalContext context; 9092 v8::Handle<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(); 9093 obj->SetAccessor(v8_str("p1"), ThrowingDirectGetterCallback); 9094 context->Global()->Set(v8_str("o1"), obj->NewInstance()); 9095 v8::Handle<Value> result = CompileRun( 9096 "var result = '';" 9097 "for (var i = 0; i < 5; i++) {" 9098 " try { o1.p1; } catch (e) { result += e; }" 9099 "}" 9100 "result;"); 9101 CHECK_EQ(v8_str("ggggg"), result); 9102} 9103 9104 9105THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) { 9106 int interceptor_call_count = 0; 9107 v8::HandleScope scope; 9108 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9109 v8::Handle<v8::FunctionTemplate> method_templ = 9110 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 9111 v8_str("method_data"), 9112 v8::Handle<v8::Signature>()); 9113 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9114 proto_templ->Set(v8_str("method"), method_templ); 9115 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 9116 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 9117 NULL, NULL, NULL, NULL, 9118 v8::External::Wrap(&interceptor_call_count)); 9119 LocalContext context; 9120 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9121 GenerateSomeGarbage(); 9122 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9123 v8::Handle<Value> value(CompileRun( 9124 "var result = 0;" 9125 "for (var i = 0; i < 100; i++) {" 9126 " result = o.method(41);" 9127 "}")); 9128 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 9129 CHECK_EQ(100, interceptor_call_count); 9130} 9131 9132THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) { 9133 int interceptor_call_count = 0; 9134 v8::HandleScope scope; 9135 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9136 v8::Handle<v8::FunctionTemplate> method_templ = 9137 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 9138 v8_str("method_data"), 9139 v8::Signature::New(fun_templ)); 9140 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9141 proto_templ->Set(v8_str("method"), method_templ); 9142 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 9143 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 9144 NULL, NULL, NULL, NULL, 9145 v8::External::Wrap(&interceptor_call_count)); 9146 LocalContext context; 9147 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9148 GenerateSomeGarbage(); 9149 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9150 v8::Handle<Value> value(CompileRun( 9151 "o.foo = 17;" 9152 "var receiver = {};" 9153 "receiver.__proto__ = o;" 9154 "var result = 0;" 9155 "for (var i = 0; i < 100; i++) {" 9156 " result = receiver.method(41);" 9157 "}")); 9158 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 9159 CHECK_EQ(100, interceptor_call_count); 9160} 9161 9162THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) { 9163 int interceptor_call_count = 0; 9164 v8::HandleScope scope; 9165 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9166 v8::Handle<v8::FunctionTemplate> method_templ = 9167 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 9168 v8_str("method_data"), 9169 v8::Signature::New(fun_templ)); 9170 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9171 proto_templ->Set(v8_str("method"), method_templ); 9172 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 9173 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 9174 NULL, NULL, NULL, NULL, 9175 v8::External::Wrap(&interceptor_call_count)); 9176 LocalContext context; 9177 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9178 GenerateSomeGarbage(); 9179 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9180 v8::Handle<Value> value(CompileRun( 9181 "o.foo = 17;" 9182 "var receiver = {};" 9183 "receiver.__proto__ = o;" 9184 "var result = 0;" 9185 "var saved_result = 0;" 9186 "for (var i = 0; i < 100; i++) {" 9187 " result = receiver.method(41);" 9188 " if (i == 50) {" 9189 " saved_result = result;" 9190 " receiver = {method: function(x) { return x - 1 }};" 9191 " }" 9192 "}")); 9193 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 9194 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 9195 CHECK_GE(interceptor_call_count, 50); 9196} 9197 9198THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) { 9199 int interceptor_call_count = 0; 9200 v8::HandleScope scope; 9201 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9202 v8::Handle<v8::FunctionTemplate> method_templ = 9203 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 9204 v8_str("method_data"), 9205 v8::Signature::New(fun_templ)); 9206 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9207 proto_templ->Set(v8_str("method"), method_templ); 9208 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 9209 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 9210 NULL, NULL, NULL, NULL, 9211 v8::External::Wrap(&interceptor_call_count)); 9212 LocalContext context; 9213 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9214 GenerateSomeGarbage(); 9215 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9216 v8::Handle<Value> value(CompileRun( 9217 "o.foo = 17;" 9218 "var receiver = {};" 9219 "receiver.__proto__ = o;" 9220 "var result = 0;" 9221 "var saved_result = 0;" 9222 "for (var i = 0; i < 100; i++) {" 9223 " result = receiver.method(41);" 9224 " if (i == 50) {" 9225 " saved_result = result;" 9226 " o.method = function(x) { return x - 1 };" 9227 " }" 9228 "}")); 9229 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 9230 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 9231 CHECK_GE(interceptor_call_count, 50); 9232} 9233 9234THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) { 9235 int interceptor_call_count = 0; 9236 v8::HandleScope scope; 9237 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9238 v8::Handle<v8::FunctionTemplate> method_templ = 9239 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 9240 v8_str("method_data"), 9241 v8::Signature::New(fun_templ)); 9242 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9243 proto_templ->Set(v8_str("method"), method_templ); 9244 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 9245 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 9246 NULL, NULL, NULL, NULL, 9247 v8::External::Wrap(&interceptor_call_count)); 9248 LocalContext context; 9249 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9250 GenerateSomeGarbage(); 9251 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9252 v8::TryCatch try_catch; 9253 v8::Handle<Value> value(CompileRun( 9254 "o.foo = 17;" 9255 "var receiver = {};" 9256 "receiver.__proto__ = o;" 9257 "var result = 0;" 9258 "var saved_result = 0;" 9259 "for (var i = 0; i < 100; i++) {" 9260 " result = receiver.method(41);" 9261 " if (i == 50) {" 9262 " saved_result = result;" 9263 " receiver = 333;" 9264 " }" 9265 "}")); 9266 CHECK(try_catch.HasCaught()); 9267 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), 9268 try_catch.Exception()->ToString()); 9269 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 9270 CHECK_GE(interceptor_call_count, 50); 9271} 9272 9273THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) { 9274 int interceptor_call_count = 0; 9275 v8::HandleScope scope; 9276 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9277 v8::Handle<v8::FunctionTemplate> method_templ = 9278 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 9279 v8_str("method_data"), 9280 v8::Signature::New(fun_templ)); 9281 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9282 proto_templ->Set(v8_str("method"), method_templ); 9283 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 9284 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 9285 NULL, NULL, NULL, NULL, 9286 v8::External::Wrap(&interceptor_call_count)); 9287 LocalContext context; 9288 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9289 GenerateSomeGarbage(); 9290 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9291 v8::TryCatch try_catch; 9292 v8::Handle<Value> value(CompileRun( 9293 "o.foo = 17;" 9294 "var receiver = {};" 9295 "receiver.__proto__ = o;" 9296 "var result = 0;" 9297 "var saved_result = 0;" 9298 "for (var i = 0; i < 100; i++) {" 9299 " result = receiver.method(41);" 9300 " if (i == 50) {" 9301 " saved_result = result;" 9302 " receiver = {method: receiver.method};" 9303 " }" 9304 "}")); 9305 CHECK(try_catch.HasCaught()); 9306 CHECK_EQ(v8_str("TypeError: Illegal invocation"), 9307 try_catch.Exception()->ToString()); 9308 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 9309 CHECK_GE(interceptor_call_count, 50); 9310} 9311 9312THREADED_TEST(CallICFastApi_TrivialSignature) { 9313 v8::HandleScope scope; 9314 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9315 v8::Handle<v8::FunctionTemplate> method_templ = 9316 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 9317 v8_str("method_data"), 9318 v8::Handle<v8::Signature>()); 9319 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9320 proto_templ->Set(v8_str("method"), method_templ); 9321 v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); 9322 LocalContext context; 9323 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9324 GenerateSomeGarbage(); 9325 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9326 v8::Handle<Value> value(CompileRun( 9327 "var result = 0;" 9328 "for (var i = 0; i < 100; i++) {" 9329 " result = o.method(41);" 9330 "}")); 9331 9332 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 9333} 9334 9335THREADED_TEST(CallICFastApi_SimpleSignature) { 9336 v8::HandleScope scope; 9337 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9338 v8::Handle<v8::FunctionTemplate> method_templ = 9339 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 9340 v8_str("method_data"), 9341 v8::Signature::New(fun_templ)); 9342 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9343 proto_templ->Set(v8_str("method"), method_templ); 9344 v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); 9345 LocalContext context; 9346 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9347 GenerateSomeGarbage(); 9348 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9349 v8::Handle<Value> value(CompileRun( 9350 "o.foo = 17;" 9351 "var receiver = {};" 9352 "receiver.__proto__ = o;" 9353 "var result = 0;" 9354 "for (var i = 0; i < 100; i++) {" 9355 " result = receiver.method(41);" 9356 "}")); 9357 9358 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 9359} 9360 9361THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) { 9362 v8::HandleScope scope; 9363 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9364 v8::Handle<v8::FunctionTemplate> method_templ = 9365 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 9366 v8_str("method_data"), 9367 v8::Signature::New(fun_templ)); 9368 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9369 proto_templ->Set(v8_str("method"), method_templ); 9370 v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); 9371 LocalContext context; 9372 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9373 GenerateSomeGarbage(); 9374 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9375 v8::Handle<Value> value(CompileRun( 9376 "o.foo = 17;" 9377 "var receiver = {};" 9378 "receiver.__proto__ = o;" 9379 "var result = 0;" 9380 "var saved_result = 0;" 9381 "for (var i = 0; i < 100; i++) {" 9382 " result = receiver.method(41);" 9383 " if (i == 50) {" 9384 " saved_result = result;" 9385 " receiver = {method: function(x) { return x - 1 }};" 9386 " }" 9387 "}")); 9388 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 9389 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 9390} 9391 9392THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) { 9393 v8::HandleScope scope; 9394 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 9395 v8::Handle<v8::FunctionTemplate> method_templ = 9396 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 9397 v8_str("method_data"), 9398 v8::Signature::New(fun_templ)); 9399 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 9400 proto_templ->Set(v8_str("method"), method_templ); 9401 v8::Handle<v8::ObjectTemplate> templ(fun_templ->InstanceTemplate()); 9402 LocalContext context; 9403 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 9404 GenerateSomeGarbage(); 9405 context->Global()->Set(v8_str("o"), fun->NewInstance()); 9406 v8::TryCatch try_catch; 9407 v8::Handle<Value> value(CompileRun( 9408 "o.foo = 17;" 9409 "var receiver = {};" 9410 "receiver.__proto__ = o;" 9411 "var result = 0;" 9412 "var saved_result = 0;" 9413 "for (var i = 0; i < 100; i++) {" 9414 " result = receiver.method(41);" 9415 " if (i == 50) {" 9416 " saved_result = result;" 9417 " receiver = 333;" 9418 " }" 9419 "}")); 9420 CHECK(try_catch.HasCaught()); 9421 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), 9422 try_catch.Exception()->ToString()); 9423 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 9424} 9425 9426 9427v8::Handle<Value> keyed_call_ic_function; 9428 9429static v8::Handle<Value> InterceptorKeyedCallICGetter( 9430 Local<String> name, const AccessorInfo& info) { 9431 ApiTestFuzzer::Fuzz(); 9432 if (v8_str("x")->Equals(name)) { 9433 return keyed_call_ic_function; 9434 } 9435 return v8::Handle<Value>(); 9436} 9437 9438 9439// Test the case when we stored cacheable lookup into 9440// a stub, but the function name changed (to another cacheable function). 9441THREADED_TEST(InterceptorKeyedCallICKeyChange1) { 9442 v8::HandleScope scope; 9443 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9444 templ->SetNamedPropertyHandler(NoBlockGetterX); 9445 LocalContext context; 9446 context->Global()->Set(v8_str("o"), templ->NewInstance()); 9447 v8::Handle<Value> value(CompileRun( 9448 "proto = new Object();" 9449 "proto.y = function(x) { return x + 1; };" 9450 "proto.z = function(x) { return x - 1; };" 9451 "o.__proto__ = proto;" 9452 "var result = 0;" 9453 "var method = 'y';" 9454 "for (var i = 0; i < 10; i++) {" 9455 " if (i == 5) { method = 'z'; };" 9456 " result += o[method](41);" 9457 "}")); 9458 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 9459} 9460 9461 9462// Test the case when we stored cacheable lookup into 9463// a stub, but the function name changed (and the new function is present 9464// both before and after the interceptor in the prototype chain). 9465THREADED_TEST(InterceptorKeyedCallICKeyChange2) { 9466 v8::HandleScope scope; 9467 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9468 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter); 9469 LocalContext context; 9470 context->Global()->Set(v8_str("proto1"), templ->NewInstance()); 9471 keyed_call_ic_function = 9472 v8_compile("function f(x) { return x - 1; }; f")->Run(); 9473 v8::Handle<Value> value(CompileRun( 9474 "o = new Object();" 9475 "proto2 = new Object();" 9476 "o.y = function(x) { return x + 1; };" 9477 "proto2.y = function(x) { return x + 2; };" 9478 "o.__proto__ = proto1;" 9479 "proto1.__proto__ = proto2;" 9480 "var result = 0;" 9481 "var method = 'x';" 9482 "for (var i = 0; i < 10; i++) {" 9483 " if (i == 5) { method = 'y'; };" 9484 " result += o[method](41);" 9485 "}")); 9486 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 9487} 9488 9489 9490// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit 9491// on the global object. 9492THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) { 9493 v8::HandleScope scope; 9494 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9495 templ->SetNamedPropertyHandler(NoBlockGetterX); 9496 LocalContext context; 9497 context->Global()->Set(v8_str("o"), templ->NewInstance()); 9498 v8::Handle<Value> value(CompileRun( 9499 "function inc(x) { return x + 1; };" 9500 "inc(1);" 9501 "function dec(x) { return x - 1; };" 9502 "dec(1);" 9503 "o.__proto__ = this;" 9504 "this.__proto__.x = inc;" 9505 "this.__proto__.y = dec;" 9506 "var result = 0;" 9507 "var method = 'x';" 9508 "for (var i = 0; i < 10; i++) {" 9509 " if (i == 5) { method = 'y'; };" 9510 " result += o[method](41);" 9511 "}")); 9512 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 9513} 9514 9515 9516// Test the case when actual function to call sits on global object. 9517THREADED_TEST(InterceptorKeyedCallICFromGlobal) { 9518 v8::HandleScope scope; 9519 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 9520 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 9521 LocalContext context; 9522 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 9523 9524 v8::Handle<Value> value(CompileRun( 9525 "function len(x) { return x.length; };" 9526 "o.__proto__ = this;" 9527 "var m = 'parseFloat';" 9528 "var result = 0;" 9529 "for (var i = 0; i < 10; i++) {" 9530 " if (i == 5) {" 9531 " m = 'len';" 9532 " saved_result = result;" 9533 " };" 9534 " result = o[m]('239');" 9535 "}")); 9536 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value()); 9537 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 9538} 9539 9540// Test the map transition before the interceptor. 9541THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) { 9542 v8::HandleScope scope; 9543 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 9544 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 9545 LocalContext context; 9546 context->Global()->Set(v8_str("proto"), templ_o->NewInstance()); 9547 9548 v8::Handle<Value> value(CompileRun( 9549 "var o = new Object();" 9550 "o.__proto__ = proto;" 9551 "o.method = function(x) { return x + 1; };" 9552 "var m = 'method';" 9553 "var result = 0;" 9554 "for (var i = 0; i < 10; i++) {" 9555 " if (i == 5) { o.method = function(x) { return x - 1; }; };" 9556 " result += o[m](41);" 9557 "}")); 9558 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 9559} 9560 9561 9562// Test the map transition after the interceptor. 9563THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) { 9564 v8::HandleScope scope; 9565 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 9566 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 9567 LocalContext context; 9568 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 9569 9570 v8::Handle<Value> value(CompileRun( 9571 "var proto = new Object();" 9572 "o.__proto__ = proto;" 9573 "proto.method = function(x) { return x + 1; };" 9574 "var m = 'method';" 9575 "var result = 0;" 9576 "for (var i = 0; i < 10; i++) {" 9577 " if (i == 5) { proto.method = function(x) { return x - 1; }; };" 9578 " result += o[m](41);" 9579 "}")); 9580 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 9581} 9582 9583 9584static int interceptor_call_count = 0; 9585 9586static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name, 9587 const AccessorInfo& info) { 9588 ApiTestFuzzer::Fuzz(); 9589 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) { 9590 return call_ic_function2; 9591 } 9592 return v8::Handle<Value>(); 9593} 9594 9595 9596// This test should hit load and call ICs for the interceptor case. 9597// Once in a while, the interceptor will reply that a property was not 9598// found in which case we should get a reference error. 9599THREADED_TEST(InterceptorICReferenceErrors) { 9600 v8::HandleScope scope; 9601 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9602 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter); 9603 LocalContext context(0, templ, v8::Handle<Value>()); 9604 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run(); 9605 v8::Handle<Value> value = CompileRun( 9606 "function f() {" 9607 " for (var i = 0; i < 1000; i++) {" 9608 " try { x; } catch(e) { return true; }" 9609 " }" 9610 " return false;" 9611 "};" 9612 "f();"); 9613 CHECK_EQ(true, value->BooleanValue()); 9614 interceptor_call_count = 0; 9615 value = CompileRun( 9616 "function g() {" 9617 " for (var i = 0; i < 1000; i++) {" 9618 " try { x(42); } catch(e) { return true; }" 9619 " }" 9620 " return false;" 9621 "};" 9622 "g();"); 9623 CHECK_EQ(true, value->BooleanValue()); 9624} 9625 9626 9627static int interceptor_ic_exception_get_count = 0; 9628 9629static v8::Handle<Value> InterceptorICExceptionGetter( 9630 Local<String> name, 9631 const AccessorInfo& info) { 9632 ApiTestFuzzer::Fuzz(); 9633 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) { 9634 return call_ic_function3; 9635 } 9636 if (interceptor_ic_exception_get_count == 20) { 9637 return v8::ThrowException(v8_num(42)); 9638 } 9639 // Do not handle get for properties other than x. 9640 return v8::Handle<Value>(); 9641} 9642 9643// Test interceptor load/call IC where the interceptor throws an 9644// exception once in a while. 9645THREADED_TEST(InterceptorICGetterExceptions) { 9646 interceptor_ic_exception_get_count = 0; 9647 v8::HandleScope scope; 9648 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9649 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter); 9650 LocalContext context(0, templ, v8::Handle<Value>()); 9651 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run(); 9652 v8::Handle<Value> value = CompileRun( 9653 "function f() {" 9654 " for (var i = 0; i < 100; i++) {" 9655 " try { x; } catch(e) { return true; }" 9656 " }" 9657 " return false;" 9658 "};" 9659 "f();"); 9660 CHECK_EQ(true, value->BooleanValue()); 9661 interceptor_ic_exception_get_count = 0; 9662 value = CompileRun( 9663 "function f() {" 9664 " for (var i = 0; i < 100; i++) {" 9665 " try { x(42); } catch(e) { return true; }" 9666 " }" 9667 " return false;" 9668 "};" 9669 "f();"); 9670 CHECK_EQ(true, value->BooleanValue()); 9671} 9672 9673 9674static int interceptor_ic_exception_set_count = 0; 9675 9676static v8::Handle<Value> InterceptorICExceptionSetter( 9677 Local<String> key, Local<Value> value, const AccessorInfo&) { 9678 ApiTestFuzzer::Fuzz(); 9679 if (++interceptor_ic_exception_set_count > 20) { 9680 return v8::ThrowException(v8_num(42)); 9681 } 9682 // Do not actually handle setting. 9683 return v8::Handle<Value>(); 9684} 9685 9686// Test interceptor store IC where the interceptor throws an exception 9687// once in a while. 9688THREADED_TEST(InterceptorICSetterExceptions) { 9689 interceptor_ic_exception_set_count = 0; 9690 v8::HandleScope scope; 9691 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9692 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter); 9693 LocalContext context(0, templ, v8::Handle<Value>()); 9694 v8::Handle<Value> value = CompileRun( 9695 "function f() {" 9696 " for (var i = 0; i < 100; i++) {" 9697 " try { x = 42; } catch(e) { return true; }" 9698 " }" 9699 " return false;" 9700 "};" 9701 "f();"); 9702 CHECK_EQ(true, value->BooleanValue()); 9703} 9704 9705 9706// Test that we ignore null interceptors. 9707THREADED_TEST(NullNamedInterceptor) { 9708 v8::HandleScope scope; 9709 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9710 templ->SetNamedPropertyHandler(0); 9711 LocalContext context; 9712 templ->Set("x", v8_num(42)); 9713 v8::Handle<v8::Object> obj = templ->NewInstance(); 9714 context->Global()->Set(v8_str("obj"), obj); 9715 v8::Handle<Value> value = CompileRun("obj.x"); 9716 CHECK(value->IsInt32()); 9717 CHECK_EQ(42, value->Int32Value()); 9718} 9719 9720 9721// Test that we ignore null interceptors. 9722THREADED_TEST(NullIndexedInterceptor) { 9723 v8::HandleScope scope; 9724 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 9725 templ->SetIndexedPropertyHandler(0); 9726 LocalContext context; 9727 templ->Set("42", v8_num(42)); 9728 v8::Handle<v8::Object> obj = templ->NewInstance(); 9729 context->Global()->Set(v8_str("obj"), obj); 9730 v8::Handle<Value> value = CompileRun("obj[42]"); 9731 CHECK(value->IsInt32()); 9732 CHECK_EQ(42, value->Int32Value()); 9733} 9734 9735 9736THREADED_TEST(NamedPropertyHandlerGetterAttributes) { 9737 v8::HandleScope scope; 9738 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 9739 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter); 9740 LocalContext env; 9741 env->Global()->Set(v8_str("obj"), 9742 templ->GetFunction()->NewInstance()); 9743 ExpectTrue("obj.x === 42"); 9744 ExpectTrue("!obj.propertyIsEnumerable('x')"); 9745} 9746 9747 9748static Handle<Value> ThrowingGetter(Local<String> name, 9749 const AccessorInfo& info) { 9750 ApiTestFuzzer::Fuzz(); 9751 ThrowException(Handle<Value>()); 9752 return Undefined(); 9753} 9754 9755 9756THREADED_TEST(VariousGetPropertiesAndThrowingCallbacks) { 9757 HandleScope scope; 9758 LocalContext context; 9759 9760 Local<FunctionTemplate> templ = FunctionTemplate::New(); 9761 Local<ObjectTemplate> instance_templ = templ->InstanceTemplate(); 9762 instance_templ->SetAccessor(v8_str("f"), ThrowingGetter); 9763 9764 Local<Object> instance = templ->GetFunction()->NewInstance(); 9765 9766 Local<Object> another = Object::New(); 9767 another->SetPrototype(instance); 9768 9769 Local<Object> with_js_getter = CompileRun( 9770 "o = {};\n" 9771 "o.__defineGetter__('f', function() { throw undefined; });\n" 9772 "o\n").As<Object>(); 9773 CHECK(!with_js_getter.IsEmpty()); 9774 9775 TryCatch try_catch; 9776 9777 Local<Value> result = instance->GetRealNamedProperty(v8_str("f")); 9778 CHECK(try_catch.HasCaught()); 9779 try_catch.Reset(); 9780 CHECK(result.IsEmpty()); 9781 9782 result = another->GetRealNamedProperty(v8_str("f")); 9783 CHECK(try_catch.HasCaught()); 9784 try_catch.Reset(); 9785 CHECK(result.IsEmpty()); 9786 9787 result = another->GetRealNamedPropertyInPrototypeChain(v8_str("f")); 9788 CHECK(try_catch.HasCaught()); 9789 try_catch.Reset(); 9790 CHECK(result.IsEmpty()); 9791 9792 result = another->Get(v8_str("f")); 9793 CHECK(try_catch.HasCaught()); 9794 try_catch.Reset(); 9795 CHECK(result.IsEmpty()); 9796 9797 result = with_js_getter->GetRealNamedProperty(v8_str("f")); 9798 CHECK(try_catch.HasCaught()); 9799 try_catch.Reset(); 9800 CHECK(result.IsEmpty()); 9801 9802 result = with_js_getter->Get(v8_str("f")); 9803 CHECK(try_catch.HasCaught()); 9804 try_catch.Reset(); 9805 CHECK(result.IsEmpty()); 9806} 9807 9808 9809static Handle<Value> ThrowingCallbackWithTryCatch(const Arguments& args) { 9810 TryCatch try_catch; 9811 // Verboseness is important: it triggers message delivery which can call into 9812 // external code. 9813 try_catch.SetVerbose(true); 9814 CompileRun("throw 'from JS';"); 9815 CHECK(try_catch.HasCaught()); 9816 CHECK(!i::Isolate::Current()->has_pending_exception()); 9817 CHECK(!i::Isolate::Current()->has_scheduled_exception()); 9818 return Undefined(); 9819} 9820 9821 9822static int call_depth; 9823 9824 9825static void WithTryCatch(Handle<Message> message, Handle<Value> data) { 9826 TryCatch try_catch; 9827} 9828 9829 9830static void ThrowFromJS(Handle<Message> message, Handle<Value> data) { 9831 if (--call_depth) CompileRun("throw 'ThrowInJS';"); 9832} 9833 9834 9835static void ThrowViaApi(Handle<Message> message, Handle<Value> data) { 9836 if (--call_depth) ThrowException(v8_str("ThrowViaApi")); 9837} 9838 9839 9840static void WebKitLike(Handle<Message> message, Handle<Value> data) { 9841 Handle<String> errorMessageString = message->Get(); 9842 CHECK(!errorMessageString.IsEmpty()); 9843 message->GetStackTrace(); 9844 message->GetScriptResourceName(); 9845} 9846 9847THREADED_TEST(ExceptionsDoNotPropagatePastTryCatch) { 9848 HandleScope scope; 9849 LocalContext context; 9850 9851 Local<Function> func = 9852 FunctionTemplate::New(ThrowingCallbackWithTryCatch)->GetFunction(); 9853 context->Global()->Set(v8_str("func"), func); 9854 9855 MessageCallback callbacks[] = 9856 { NULL, WebKitLike, ThrowViaApi, ThrowFromJS, WithTryCatch }; 9857 for (unsigned i = 0; i < sizeof(callbacks)/sizeof(callbacks[0]); i++) { 9858 MessageCallback callback = callbacks[i]; 9859 if (callback != NULL) { 9860 V8::AddMessageListener(callback); 9861 } 9862 // Some small number to control number of times message handler should 9863 // throw an exception. 9864 call_depth = 5; 9865 ExpectFalse( 9866 "var thrown = false;\n" 9867 "try { func(); } catch(e) { thrown = true; }\n" 9868 "thrown\n"); 9869 if (callback != NULL) { 9870 V8::RemoveMessageListeners(callback); 9871 } 9872 } 9873} 9874 9875 9876static v8::Handle<Value> ParentGetter(Local<String> name, 9877 const AccessorInfo& info) { 9878 ApiTestFuzzer::Fuzz(); 9879 return v8_num(1); 9880} 9881 9882 9883static v8::Handle<Value> ChildGetter(Local<String> name, 9884 const AccessorInfo& info) { 9885 ApiTestFuzzer::Fuzz(); 9886 return v8_num(42); 9887} 9888 9889 9890THREADED_TEST(Overriding) { 9891 v8::HandleScope scope; 9892 LocalContext context; 9893 9894 // Parent template. 9895 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New(); 9896 Local<ObjectTemplate> parent_instance_templ = 9897 parent_templ->InstanceTemplate(); 9898 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter); 9899 9900 // Template that inherits from the parent template. 9901 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New(); 9902 Local<ObjectTemplate> child_instance_templ = 9903 child_templ->InstanceTemplate(); 9904 child_templ->Inherit(parent_templ); 9905 // Override 'f'. The child version of 'f' should get called for child 9906 // instances. 9907 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter); 9908 // Add 'g' twice. The 'g' added last should get called for instances. 9909 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter); 9910 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter); 9911 9912 // Add 'h' as an accessor to the proto template with ReadOnly attributes 9913 // so 'h' can be shadowed on the instance object. 9914 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate(); 9915 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0, 9916 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 9917 9918 // Add 'i' as an accessor to the instance template with ReadOnly attributes 9919 // but the attribute does not have effect because it is duplicated with 9920 // NULL setter. 9921 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0, 9922 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 9923 9924 9925 9926 // Instantiate the child template. 9927 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance(); 9928 9929 // Check that the child function overrides the parent one. 9930 context->Global()->Set(v8_str("o"), instance); 9931 Local<Value> value = v8_compile("o.f")->Run(); 9932 // Check that the 'g' that was added last is hit. 9933 CHECK_EQ(42, value->Int32Value()); 9934 value = v8_compile("o.g")->Run(); 9935 CHECK_EQ(42, value->Int32Value()); 9936 9937 // Check 'h' can be shadowed. 9938 value = v8_compile("o.h = 3; o.h")->Run(); 9939 CHECK_EQ(3, value->Int32Value()); 9940 9941 // Check 'i' is cannot be shadowed or changed. 9942 value = v8_compile("o.i = 3; o.i")->Run(); 9943 CHECK_EQ(42, value->Int32Value()); 9944} 9945 9946 9947static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) { 9948 ApiTestFuzzer::Fuzz(); 9949 return v8::Boolean::New(args.IsConstructCall()); 9950} 9951 9952 9953THREADED_TEST(IsConstructCall) { 9954 v8::HandleScope scope; 9955 9956 // Function template with call handler. 9957 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 9958 templ->SetCallHandler(IsConstructHandler); 9959 9960 LocalContext context; 9961 9962 context->Global()->Set(v8_str("f"), templ->GetFunction()); 9963 Local<Value> value = v8_compile("f()")->Run(); 9964 CHECK(!value->BooleanValue()); 9965 value = v8_compile("new f()")->Run(); 9966 CHECK(value->BooleanValue()); 9967} 9968 9969 9970THREADED_TEST(ObjectProtoToString) { 9971 v8::HandleScope scope; 9972 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 9973 templ->SetClassName(v8_str("MyClass")); 9974 9975 LocalContext context; 9976 9977 Local<String> customized_tostring = v8_str("customized toString"); 9978 9979 // Replace Object.prototype.toString 9980 v8_compile("Object.prototype.toString = function() {" 9981 " return 'customized toString';" 9982 "}")->Run(); 9983 9984 // Normal ToString call should call replaced Object.prototype.toString 9985 Local<v8::Object> instance = templ->GetFunction()->NewInstance(); 9986 Local<String> value = instance->ToString(); 9987 CHECK(value->IsString() && value->Equals(customized_tostring)); 9988 9989 // ObjectProtoToString should not call replace toString function. 9990 value = instance->ObjectProtoToString(); 9991 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]"))); 9992 9993 // Check global 9994 value = context->Global()->ObjectProtoToString(); 9995 CHECK(value->IsString() && value->Equals(v8_str("[object global]"))); 9996 9997 // Check ordinary object 9998 Local<Value> object = v8_compile("new Object()")->Run(); 9999 value = object.As<v8::Object>()->ObjectProtoToString(); 10000 CHECK(value->IsString() && value->Equals(v8_str("[object Object]"))); 10001} 10002 10003 10004THREADED_TEST(ObjectGetConstructorName) { 10005 v8::HandleScope scope; 10006 LocalContext context; 10007 v8_compile("function Parent() {};" 10008 "function Child() {};" 10009 "Child.prototype = new Parent();" 10010 "var outer = { inner: function() { } };" 10011 "var p = new Parent();" 10012 "var c = new Child();" 10013 "var x = new outer.inner();")->Run(); 10014 10015 Local<v8::Value> p = context->Global()->Get(v8_str("p")); 10016 CHECK(p->IsObject() && p->ToObject()->GetConstructorName()->Equals( 10017 v8_str("Parent"))); 10018 10019 Local<v8::Value> c = context->Global()->Get(v8_str("c")); 10020 CHECK(c->IsObject() && c->ToObject()->GetConstructorName()->Equals( 10021 v8_str("Child"))); 10022 10023 Local<v8::Value> x = context->Global()->Get(v8_str("x")); 10024 CHECK(x->IsObject() && x->ToObject()->GetConstructorName()->Equals( 10025 v8_str("outer.inner"))); 10026} 10027 10028 10029bool ApiTestFuzzer::fuzzing_ = false; 10030i::Semaphore* ApiTestFuzzer::all_tests_done_= 10031 i::OS::CreateSemaphore(0); 10032int ApiTestFuzzer::active_tests_; 10033int ApiTestFuzzer::tests_being_run_; 10034int ApiTestFuzzer::current_; 10035 10036 10037// We are in a callback and want to switch to another thread (if we 10038// are currently running the thread fuzzing test). 10039void ApiTestFuzzer::Fuzz() { 10040 if (!fuzzing_) return; 10041 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_; 10042 test->ContextSwitch(); 10043} 10044 10045 10046// Let the next thread go. Since it is also waiting on the V8 lock it may 10047// not start immediately. 10048bool ApiTestFuzzer::NextThread() { 10049 int test_position = GetNextTestNumber(); 10050 const char* test_name = RegisterThreadedTest::nth(current_)->name(); 10051 if (test_position == current_) { 10052 if (kLogThreading) 10053 printf("Stay with %s\n", test_name); 10054 return false; 10055 } 10056 if (kLogThreading) { 10057 printf("Switch from %s to %s\n", 10058 test_name, 10059 RegisterThreadedTest::nth(test_position)->name()); 10060 } 10061 current_ = test_position; 10062 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal(); 10063 return true; 10064} 10065 10066 10067void ApiTestFuzzer::Run() { 10068 // When it is our turn... 10069 gate_->Wait(); 10070 { 10071 // ... get the V8 lock and start running the test. 10072 v8::Locker locker; 10073 CallTest(); 10074 } 10075 // This test finished. 10076 active_ = false; 10077 active_tests_--; 10078 // If it was the last then signal that fact. 10079 if (active_tests_ == 0) { 10080 all_tests_done_->Signal(); 10081 } else { 10082 // Otherwise select a new test and start that. 10083 NextThread(); 10084 } 10085} 10086 10087 10088static unsigned linear_congruential_generator; 10089 10090 10091void ApiTestFuzzer::Setup(PartOfTest part) { 10092 linear_congruential_generator = i::FLAG_testing_prng_seed; 10093 fuzzing_ = true; 10094 int count = RegisterThreadedTest::count(); 10095 int start = count * part / (LAST_PART + 1); 10096 int end = (count * (part + 1) / (LAST_PART + 1)) - 1; 10097 active_tests_ = tests_being_run_ = end - start + 1; 10098 for (int i = 0; i < tests_being_run_; i++) { 10099 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start); 10100 } 10101 for (int i = 0; i < active_tests_; i++) { 10102 RegisterThreadedTest::nth(i)->fuzzer_->Start(); 10103 } 10104} 10105 10106 10107static void CallTestNumber(int test_number) { 10108 (RegisterThreadedTest::nth(test_number)->callback())(); 10109} 10110 10111 10112void ApiTestFuzzer::RunAllTests() { 10113 // Set off the first test. 10114 current_ = -1; 10115 NextThread(); 10116 // Wait till they are all done. 10117 all_tests_done_->Wait(); 10118} 10119 10120 10121int ApiTestFuzzer::GetNextTestNumber() { 10122 int next_test; 10123 do { 10124 next_test = (linear_congruential_generator >> 16) % tests_being_run_; 10125 linear_congruential_generator *= 1664525u; 10126 linear_congruential_generator += 1013904223u; 10127 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_); 10128 return next_test; 10129} 10130 10131 10132void ApiTestFuzzer::ContextSwitch() { 10133 // If the new thread is the same as the current thread there is nothing to do. 10134 if (NextThread()) { 10135 // Now it can start. 10136 v8::Unlocker unlocker; 10137 // Wait till someone starts us again. 10138 gate_->Wait(); 10139 // And we're off. 10140 } 10141} 10142 10143 10144void ApiTestFuzzer::TearDown() { 10145 fuzzing_ = false; 10146 for (int i = 0; i < RegisterThreadedTest::count(); i++) { 10147 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_; 10148 if (fuzzer != NULL) fuzzer->Join(); 10149 } 10150} 10151 10152 10153// Lets not be needlessly self-referential. 10154TEST(Threading) { 10155 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART); 10156 ApiTestFuzzer::RunAllTests(); 10157 ApiTestFuzzer::TearDown(); 10158} 10159 10160TEST(Threading2) { 10161 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART); 10162 ApiTestFuzzer::RunAllTests(); 10163 ApiTestFuzzer::TearDown(); 10164} 10165 10166TEST(Threading3) { 10167 ApiTestFuzzer::Setup(ApiTestFuzzer::THIRD_PART); 10168 ApiTestFuzzer::RunAllTests(); 10169 ApiTestFuzzer::TearDown(); 10170} 10171 10172TEST(Threading4) { 10173 ApiTestFuzzer::Setup(ApiTestFuzzer::FOURTH_PART); 10174 ApiTestFuzzer::RunAllTests(); 10175 ApiTestFuzzer::TearDown(); 10176} 10177 10178void ApiTestFuzzer::CallTest() { 10179 if (kLogThreading) 10180 printf("Start test %d\n", test_number_); 10181 CallTestNumber(test_number_); 10182 if (kLogThreading) 10183 printf("End test %d\n", test_number_); 10184} 10185 10186 10187static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) { 10188 CHECK(v8::Locker::IsLocked()); 10189 ApiTestFuzzer::Fuzz(); 10190 v8::Unlocker unlocker; 10191 const char* code = "throw 7;"; 10192 { 10193 v8::Locker nested_locker; 10194 v8::HandleScope scope; 10195 v8::Handle<Value> exception; 10196 { v8::TryCatch try_catch; 10197 v8::Handle<Value> value = CompileRun(code); 10198 CHECK(value.IsEmpty()); 10199 CHECK(try_catch.HasCaught()); 10200 // Make sure to wrap the exception in a new handle because 10201 // the handle returned from the TryCatch is destroyed 10202 // when the TryCatch is destroyed. 10203 exception = Local<Value>::New(try_catch.Exception()); 10204 } 10205 return v8::ThrowException(exception); 10206 } 10207} 10208 10209 10210static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) { 10211 CHECK(v8::Locker::IsLocked()); 10212 ApiTestFuzzer::Fuzz(); 10213 v8::Unlocker unlocker; 10214 const char* code = "throw 7;"; 10215 { 10216 v8::Locker nested_locker; 10217 v8::HandleScope scope; 10218 v8::Handle<Value> value = CompileRun(code); 10219 CHECK(value.IsEmpty()); 10220 return v8_str("foo"); 10221 } 10222} 10223 10224 10225// These are locking tests that don't need to be run again 10226// as part of the locking aggregation tests. 10227TEST(NestedLockers) { 10228 v8::Locker locker; 10229 CHECK(v8::Locker::IsLocked()); 10230 v8::HandleScope scope; 10231 LocalContext env; 10232 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS); 10233 Local<Function> fun = fun_templ->GetFunction(); 10234 env->Global()->Set(v8_str("throw_in_js"), fun); 10235 Local<Script> script = v8_compile("(function () {" 10236 " try {" 10237 " throw_in_js();" 10238 " return 42;" 10239 " } catch (e) {" 10240 " return e * 13;" 10241 " }" 10242 "})();"); 10243 CHECK_EQ(91, script->Run()->Int32Value()); 10244} 10245 10246 10247// These are locking tests that don't need to be run again 10248// as part of the locking aggregation tests. 10249TEST(NestedLockersNoTryCatch) { 10250 v8::Locker locker; 10251 v8::HandleScope scope; 10252 LocalContext env; 10253 Local<v8::FunctionTemplate> fun_templ = 10254 v8::FunctionTemplate::New(ThrowInJSNoCatch); 10255 Local<Function> fun = fun_templ->GetFunction(); 10256 env->Global()->Set(v8_str("throw_in_js"), fun); 10257 Local<Script> script = v8_compile("(function () {" 10258 " try {" 10259 " throw_in_js();" 10260 " return 42;" 10261 " } catch (e) {" 10262 " return e * 13;" 10263 " }" 10264 "})();"); 10265 CHECK_EQ(91, script->Run()->Int32Value()); 10266} 10267 10268 10269THREADED_TEST(RecursiveLocking) { 10270 v8::Locker locker; 10271 { 10272 v8::Locker locker2; 10273 CHECK(v8::Locker::IsLocked()); 10274 } 10275} 10276 10277 10278static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) { 10279 ApiTestFuzzer::Fuzz(); 10280 v8::Unlocker unlocker; 10281 return v8::Undefined(); 10282} 10283 10284 10285THREADED_TEST(LockUnlockLock) { 10286 { 10287 v8::Locker locker; 10288 v8::HandleScope scope; 10289 LocalContext env; 10290 Local<v8::FunctionTemplate> fun_templ = 10291 v8::FunctionTemplate::New(UnlockForAMoment); 10292 Local<Function> fun = fun_templ->GetFunction(); 10293 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 10294 Local<Script> script = v8_compile("(function () {" 10295 " unlock_for_a_moment();" 10296 " return 42;" 10297 "})();"); 10298 CHECK_EQ(42, script->Run()->Int32Value()); 10299 } 10300 { 10301 v8::Locker locker; 10302 v8::HandleScope scope; 10303 LocalContext env; 10304 Local<v8::FunctionTemplate> fun_templ = 10305 v8::FunctionTemplate::New(UnlockForAMoment); 10306 Local<Function> fun = fun_templ->GetFunction(); 10307 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 10308 Local<Script> script = v8_compile("(function () {" 10309 " unlock_for_a_moment();" 10310 " return 42;" 10311 "})();"); 10312 CHECK_EQ(42, script->Run()->Int32Value()); 10313 } 10314} 10315 10316 10317static int GetGlobalObjectsCount() { 10318 i::Isolate::Current()->heap()->EnsureHeapIsIterable(); 10319 int count = 0; 10320 i::HeapIterator it; 10321 for (i::HeapObject* object = it.next(); object != NULL; object = it.next()) 10322 if (object->IsJSGlobalObject()) count++; 10323 return count; 10324} 10325 10326 10327static void CheckSurvivingGlobalObjectsCount(int expected) { 10328 // We need to collect all garbage twice to be sure that everything 10329 // has been collected. This is because inline caches are cleared in 10330 // the first garbage collection but some of the maps have already 10331 // been marked at that point. Therefore some of the maps are not 10332 // collected until the second garbage collection. 10333 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 10334 HEAP->CollectAllGarbage(i::Heap::kMakeHeapIterableMask); 10335 int count = GetGlobalObjectsCount(); 10336#ifdef DEBUG 10337 if (count != expected) HEAP->TracePathToGlobal(); 10338#endif 10339 CHECK_EQ(expected, count); 10340} 10341 10342 10343TEST(DontLeakGlobalObjects) { 10344 // Regression test for issues 1139850 and 1174891. 10345 10346 v8::V8::Initialize(); 10347 10348 for (int i = 0; i < 5; i++) { 10349 { v8::HandleScope scope; 10350 LocalContext context; 10351 } 10352 CheckSurvivingGlobalObjectsCount(0); 10353 10354 { v8::HandleScope scope; 10355 LocalContext context; 10356 v8_compile("Date")->Run(); 10357 } 10358 CheckSurvivingGlobalObjectsCount(0); 10359 10360 { v8::HandleScope scope; 10361 LocalContext context; 10362 v8_compile("/aaa/")->Run(); 10363 } 10364 CheckSurvivingGlobalObjectsCount(0); 10365 10366 { v8::HandleScope scope; 10367 const char* extension_list[] = { "v8/gc" }; 10368 v8::ExtensionConfiguration extensions(1, extension_list); 10369 LocalContext context(&extensions); 10370 v8_compile("gc();")->Run(); 10371 } 10372 CheckSurvivingGlobalObjectsCount(0); 10373 } 10374} 10375 10376 10377v8::Persistent<v8::Object> some_object; 10378v8::Persistent<v8::Object> bad_handle; 10379 10380void NewPersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) { 10381 v8::HandleScope scope; 10382 bad_handle = v8::Persistent<v8::Object>::New(some_object); 10383 handle.Dispose(); 10384} 10385 10386 10387THREADED_TEST(NewPersistentHandleFromWeakCallback) { 10388 LocalContext context; 10389 10390 v8::Persistent<v8::Object> handle1, handle2; 10391 { 10392 v8::HandleScope scope; 10393 some_object = v8::Persistent<v8::Object>::New(v8::Object::New()); 10394 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 10395 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 10396 } 10397 // Note: order is implementation dependent alas: currently 10398 // global handle nodes are processed by PostGarbageCollectionProcessing 10399 // in reverse allocation order, so if second allocated handle is deleted, 10400 // weak callback of the first handle would be able to 'reallocate' it. 10401 handle1.MakeWeak(NULL, NewPersistentHandleCallback); 10402 handle2.Dispose(); 10403 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 10404} 10405 10406 10407v8::Persistent<v8::Object> to_be_disposed; 10408 10409void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) { 10410 to_be_disposed.Dispose(); 10411 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 10412 handle.Dispose(); 10413} 10414 10415 10416THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) { 10417 LocalContext context; 10418 10419 v8::Persistent<v8::Object> handle1, handle2; 10420 { 10421 v8::HandleScope scope; 10422 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 10423 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 10424 } 10425 handle1.MakeWeak(NULL, DisposeAndForceGcCallback); 10426 to_be_disposed = handle2; 10427 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 10428} 10429 10430void DisposingCallback(v8::Persistent<v8::Value> handle, void*) { 10431 handle.Dispose(); 10432} 10433 10434void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) { 10435 v8::HandleScope scope; 10436 v8::Persistent<v8::Object>::New(v8::Object::New()); 10437 handle.Dispose(); 10438} 10439 10440 10441THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) { 10442 LocalContext context; 10443 10444 v8::Persistent<v8::Object> handle1, handle2, handle3; 10445 { 10446 v8::HandleScope scope; 10447 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New()); 10448 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 10449 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 10450 } 10451 handle2.MakeWeak(NULL, DisposingCallback); 10452 handle3.MakeWeak(NULL, HandleCreatingCallback); 10453 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 10454} 10455 10456 10457THREADED_TEST(CheckForCrossContextObjectLiterals) { 10458 v8::V8::Initialize(); 10459 10460 const int nof = 2; 10461 const char* sources[nof] = { 10462 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }", 10463 "Object()" 10464 }; 10465 10466 for (int i = 0; i < nof; i++) { 10467 const char* source = sources[i]; 10468 { v8::HandleScope scope; 10469 LocalContext context; 10470 CompileRun(source); 10471 } 10472 { v8::HandleScope scope; 10473 LocalContext context; 10474 CompileRun(source); 10475 } 10476 } 10477} 10478 10479 10480static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) { 10481 v8::HandleScope inner; 10482 env->Enter(); 10483 v8::Handle<Value> three = v8_num(3); 10484 v8::Handle<Value> value = inner.Close(three); 10485 env->Exit(); 10486 return value; 10487} 10488 10489 10490THREADED_TEST(NestedHandleScopeAndContexts) { 10491 v8::HandleScope outer; 10492 v8::Persistent<Context> env = Context::New(); 10493 env->Enter(); 10494 v8::Handle<Value> value = NestedScope(env); 10495 v8::Handle<String> str(value->ToString()); 10496 env->Exit(); 10497 env.Dispose(); 10498} 10499 10500 10501THREADED_TEST(ExternalAllocatedMemory) { 10502 v8::HandleScope outer; 10503 v8::Persistent<Context> env(Context::New()); 10504 const int kSize = 1024*1024; 10505 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize); 10506 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0); 10507} 10508 10509 10510THREADED_TEST(DisposeEnteredContext) { 10511 v8::HandleScope scope; 10512 LocalContext outer; 10513 { v8::Persistent<v8::Context> inner = v8::Context::New(); 10514 inner->Enter(); 10515 inner.Dispose(); 10516 inner.Clear(); 10517 inner->Exit(); 10518 } 10519} 10520 10521 10522// Regression test for issue 54, object templates with internal fields 10523// but no accessors or interceptors did not get their internal field 10524// count set on instances. 10525THREADED_TEST(Regress54) { 10526 v8::HandleScope outer; 10527 LocalContext context; 10528 static v8::Persistent<v8::ObjectTemplate> templ; 10529 if (templ.IsEmpty()) { 10530 v8::HandleScope inner; 10531 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New(); 10532 local->SetInternalFieldCount(1); 10533 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local)); 10534 } 10535 v8::Handle<v8::Object> result = templ->NewInstance(); 10536 CHECK_EQ(1, result->InternalFieldCount()); 10537} 10538 10539 10540// If part of the threaded tests, this test makes ThreadingTest fail 10541// on mac. 10542TEST(CatchStackOverflow) { 10543 v8::HandleScope scope; 10544 LocalContext context; 10545 v8::TryCatch try_catch; 10546 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New( 10547 "function f() {" 10548 " return f();" 10549 "}" 10550 "" 10551 "f();")); 10552 v8::Handle<v8::Value> result = script->Run(); 10553 CHECK(result.IsEmpty()); 10554} 10555 10556 10557static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script, 10558 const char* resource_name, 10559 int line_offset) { 10560 v8::HandleScope scope; 10561 v8::TryCatch try_catch; 10562 v8::Handle<v8::Value> result = script->Run(); 10563 CHECK(result.IsEmpty()); 10564 CHECK(try_catch.HasCaught()); 10565 v8::Handle<v8::Message> message = try_catch.Message(); 10566 CHECK(!message.IsEmpty()); 10567 CHECK_EQ(10 + line_offset, message->GetLineNumber()); 10568 CHECK_EQ(91, message->GetStartPosition()); 10569 CHECK_EQ(92, message->GetEndPosition()); 10570 CHECK_EQ(2, message->GetStartColumn()); 10571 CHECK_EQ(3, message->GetEndColumn()); 10572 v8::String::AsciiValue line(message->GetSourceLine()); 10573 CHECK_EQ(" throw 'nirk';", *line); 10574 v8::String::AsciiValue name(message->GetScriptResourceName()); 10575 CHECK_EQ(resource_name, *name); 10576} 10577 10578 10579THREADED_TEST(TryCatchSourceInfo) { 10580 v8::HandleScope scope; 10581 LocalContext context; 10582 v8::Handle<v8::String> source = v8::String::New( 10583 "function Foo() {\n" 10584 " return Bar();\n" 10585 "}\n" 10586 "\n" 10587 "function Bar() {\n" 10588 " return Baz();\n" 10589 "}\n" 10590 "\n" 10591 "function Baz() {\n" 10592 " throw 'nirk';\n" 10593 "}\n" 10594 "\n" 10595 "Foo();\n"); 10596 10597 const char* resource_name; 10598 v8::Handle<v8::Script> script; 10599 resource_name = "test.js"; 10600 script = v8::Script::Compile(source, v8::String::New(resource_name)); 10601 CheckTryCatchSourceInfo(script, resource_name, 0); 10602 10603 resource_name = "test1.js"; 10604 v8::ScriptOrigin origin1(v8::String::New(resource_name)); 10605 script = v8::Script::Compile(source, &origin1); 10606 CheckTryCatchSourceInfo(script, resource_name, 0); 10607 10608 resource_name = "test2.js"; 10609 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7)); 10610 script = v8::Script::Compile(source, &origin2); 10611 CheckTryCatchSourceInfo(script, resource_name, 7); 10612} 10613 10614 10615THREADED_TEST(CompilationCache) { 10616 v8::HandleScope scope; 10617 LocalContext context; 10618 v8::Handle<v8::String> source0 = v8::String::New("1234"); 10619 v8::Handle<v8::String> source1 = v8::String::New("1234"); 10620 v8::Handle<v8::Script> script0 = 10621 v8::Script::Compile(source0, v8::String::New("test.js")); 10622 v8::Handle<v8::Script> script1 = 10623 v8::Script::Compile(source1, v8::String::New("test.js")); 10624 v8::Handle<v8::Script> script2 = 10625 v8::Script::Compile(source0); // different origin 10626 CHECK_EQ(1234, script0->Run()->Int32Value()); 10627 CHECK_EQ(1234, script1->Run()->Int32Value()); 10628 CHECK_EQ(1234, script2->Run()->Int32Value()); 10629} 10630 10631 10632static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) { 10633 ApiTestFuzzer::Fuzz(); 10634 return v8_num(42); 10635} 10636 10637 10638THREADED_TEST(CallbackFunctionName) { 10639 v8::HandleScope scope; 10640 LocalContext context; 10641 Local<ObjectTemplate> t = ObjectTemplate::New(); 10642 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback)); 10643 context->Global()->Set(v8_str("obj"), t->NewInstance()); 10644 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name"); 10645 CHECK(value->IsString()); 10646 v8::String::AsciiValue name(value); 10647 CHECK_EQ("asdf", *name); 10648} 10649 10650 10651THREADED_TEST(DateAccess) { 10652 v8::HandleScope scope; 10653 LocalContext context; 10654 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0); 10655 CHECK(date->IsDate()); 10656 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue()); 10657} 10658 10659 10660void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) { 10661 v8::Handle<v8::Object> obj = val.As<v8::Object>(); 10662 v8::Handle<v8::Array> props = obj->GetPropertyNames(); 10663 CHECK_EQ(elmc, props->Length()); 10664 for (int i = 0; i < elmc; i++) { 10665 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i))); 10666 CHECK_EQ(elmv[i], *elm); 10667 } 10668} 10669 10670 10671void CheckOwnProperties(v8::Handle<v8::Value> val, 10672 int elmc, 10673 const char* elmv[]) { 10674 v8::Handle<v8::Object> obj = val.As<v8::Object>(); 10675 v8::Handle<v8::Array> props = obj->GetOwnPropertyNames(); 10676 CHECK_EQ(elmc, props->Length()); 10677 for (int i = 0; i < elmc; i++) { 10678 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i))); 10679 CHECK_EQ(elmv[i], *elm); 10680 } 10681} 10682 10683 10684THREADED_TEST(PropertyEnumeration) { 10685 v8::HandleScope scope; 10686 LocalContext context; 10687 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New( 10688 "var result = [];" 10689 "result[0] = {};" 10690 "result[1] = {a: 1, b: 2};" 10691 "result[2] = [1, 2, 3];" 10692 "var proto = {x: 1, y: 2, z: 3};" 10693 "var x = { __proto__: proto, w: 0, z: 1 };" 10694 "result[3] = x;" 10695 "result;"))->Run(); 10696 v8::Handle<v8::Array> elms = obj.As<v8::Array>(); 10697 CHECK_EQ(4, elms->Length()); 10698 int elmc0 = 0; 10699 const char** elmv0 = NULL; 10700 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0); 10701 CheckOwnProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0); 10702 int elmc1 = 2; 10703 const char* elmv1[] = {"a", "b"}; 10704 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1); 10705 CheckOwnProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1); 10706 int elmc2 = 3; 10707 const char* elmv2[] = {"0", "1", "2"}; 10708 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2); 10709 CheckOwnProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2); 10710 int elmc3 = 4; 10711 const char* elmv3[] = {"w", "z", "x", "y"}; 10712 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3); 10713 int elmc4 = 2; 10714 const char* elmv4[] = {"w", "z"}; 10715 CheckOwnProperties(elms->Get(v8::Integer::New(3)), elmc4, elmv4); 10716} 10717 10718THREADED_TEST(PropertyEnumeration2) { 10719 v8::HandleScope scope; 10720 LocalContext context; 10721 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New( 10722 "var result = [];" 10723 "result[0] = {};" 10724 "result[1] = {a: 1, b: 2};" 10725 "result[2] = [1, 2, 3];" 10726 "var proto = {x: 1, y: 2, z: 3};" 10727 "var x = { __proto__: proto, w: 0, z: 1 };" 10728 "result[3] = x;" 10729 "result;"))->Run(); 10730 v8::Handle<v8::Array> elms = obj.As<v8::Array>(); 10731 CHECK_EQ(4, elms->Length()); 10732 int elmc0 = 0; 10733 const char** elmv0 = NULL; 10734 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0); 10735 10736 v8::Handle<v8::Value> val = elms->Get(v8::Integer::New(0)); 10737 v8::Handle<v8::Array> props = val.As<v8::Object>()->GetPropertyNames(); 10738 CHECK_EQ(0, props->Length()); 10739 for (uint32_t i = 0; i < props->Length(); i++) { 10740 printf("p[%d]\n", i); 10741 } 10742} 10743 10744static bool NamedSetAccessBlocker(Local<v8::Object> obj, 10745 Local<Value> name, 10746 v8::AccessType type, 10747 Local<Value> data) { 10748 return type != v8::ACCESS_SET; 10749} 10750 10751 10752static bool IndexedSetAccessBlocker(Local<v8::Object> obj, 10753 uint32_t key, 10754 v8::AccessType type, 10755 Local<Value> data) { 10756 return type != v8::ACCESS_SET; 10757} 10758 10759 10760THREADED_TEST(DisableAccessChecksWhileConfiguring) { 10761 v8::HandleScope scope; 10762 LocalContext context; 10763 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10764 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker, 10765 IndexedSetAccessBlocker); 10766 templ->Set(v8_str("x"), v8::True()); 10767 Local<v8::Object> instance = templ->NewInstance(); 10768 context->Global()->Set(v8_str("obj"), instance); 10769 Local<Value> value = CompileRun("obj.x"); 10770 CHECK(value->BooleanValue()); 10771} 10772 10773 10774static bool NamedGetAccessBlocker(Local<v8::Object> obj, 10775 Local<Value> name, 10776 v8::AccessType type, 10777 Local<Value> data) { 10778 return false; 10779} 10780 10781 10782static bool IndexedGetAccessBlocker(Local<v8::Object> obj, 10783 uint32_t key, 10784 v8::AccessType type, 10785 Local<Value> data) { 10786 return false; 10787} 10788 10789 10790 10791THREADED_TEST(AccessChecksReenabledCorrectly) { 10792 v8::HandleScope scope; 10793 LocalContext context; 10794 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10795 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker, 10796 IndexedGetAccessBlocker); 10797 templ->Set(v8_str("a"), v8_str("a")); 10798 // Add more than 8 (see kMaxFastProperties) properties 10799 // so that the constructor will force copying map. 10800 // Cannot sprintf, gcc complains unsafety. 10801 char buf[4]; 10802 for (char i = '0'; i <= '9' ; i++) { 10803 buf[0] = i; 10804 for (char j = '0'; j <= '9'; j++) { 10805 buf[1] = j; 10806 for (char k = '0'; k <= '9'; k++) { 10807 buf[2] = k; 10808 buf[3] = 0; 10809 templ->Set(v8_str(buf), v8::Number::New(k)); 10810 } 10811 } 10812 } 10813 10814 Local<v8::Object> instance_1 = templ->NewInstance(); 10815 context->Global()->Set(v8_str("obj_1"), instance_1); 10816 10817 Local<Value> value_1 = CompileRun("obj_1.a"); 10818 CHECK(value_1->IsUndefined()); 10819 10820 Local<v8::Object> instance_2 = templ->NewInstance(); 10821 context->Global()->Set(v8_str("obj_2"), instance_2); 10822 10823 Local<Value> value_2 = CompileRun("obj_2.a"); 10824 CHECK(value_2->IsUndefined()); 10825} 10826 10827 10828// This tests that access check information remains on the global 10829// object template when creating contexts. 10830THREADED_TEST(AccessControlRepeatedContextCreation) { 10831 v8::HandleScope handle_scope; 10832 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 10833 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker, 10834 IndexedSetAccessBlocker); 10835 i::Handle<i::ObjectTemplateInfo> internal_template = 10836 v8::Utils::OpenHandle(*global_template); 10837 CHECK(!internal_template->constructor()->IsUndefined()); 10838 i::Handle<i::FunctionTemplateInfo> constructor( 10839 i::FunctionTemplateInfo::cast(internal_template->constructor())); 10840 CHECK(!constructor->access_check_info()->IsUndefined()); 10841 v8::Persistent<Context> context0(Context::New(NULL, global_template)); 10842 CHECK(!constructor->access_check_info()->IsUndefined()); 10843} 10844 10845 10846THREADED_TEST(TurnOnAccessCheck) { 10847 v8::HandleScope handle_scope; 10848 10849 // Create an environment with access check to the global object disabled by 10850 // default. 10851 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 10852 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker, 10853 IndexedGetAccessBlocker, 10854 v8::Handle<v8::Value>(), 10855 false); 10856 v8::Persistent<Context> context = Context::New(NULL, global_template); 10857 Context::Scope context_scope(context); 10858 10859 // Set up a property and a number of functions. 10860 context->Global()->Set(v8_str("a"), v8_num(1)); 10861 CompileRun("function f1() {return a;}" 10862 "function f2() {return a;}" 10863 "function g1() {return h();}" 10864 "function g2() {return h();}" 10865 "function h() {return 1;}"); 10866 Local<Function> f1 = 10867 Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); 10868 Local<Function> f2 = 10869 Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); 10870 Local<Function> g1 = 10871 Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); 10872 Local<Function> g2 = 10873 Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); 10874 Local<Function> h = 10875 Local<Function>::Cast(context->Global()->Get(v8_str("h"))); 10876 10877 // Get the global object. 10878 v8::Handle<v8::Object> global = context->Global(); 10879 10880 // Call f1 one time and f2 a number of times. This will ensure that f1 still 10881 // uses the runtime system to retreive property a whereas f2 uses global load 10882 // inline cache. 10883 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1))); 10884 for (int i = 0; i < 4; i++) { 10885 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1))); 10886 } 10887 10888 // Same for g1 and g2. 10889 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1))); 10890 for (int i = 0; i < 4; i++) { 10891 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1))); 10892 } 10893 10894 // Detach the global and turn on access check. 10895 context->DetachGlobal(); 10896 context->Global()->TurnOnAccessCheck(); 10897 10898 // Failing access check to property get results in undefined. 10899 CHECK(f1->Call(global, 0, NULL)->IsUndefined()); 10900 CHECK(f2->Call(global, 0, NULL)->IsUndefined()); 10901 10902 // Failing access check to function call results in exception. 10903 CHECK(g1->Call(global, 0, NULL).IsEmpty()); 10904 CHECK(g2->Call(global, 0, NULL).IsEmpty()); 10905 10906 // No failing access check when just returning a constant. 10907 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1))); 10908} 10909 10910 10911v8::Handle<v8::String> a; 10912v8::Handle<v8::String> h; 10913 10914static bool NamedGetAccessBlockAandH(Local<v8::Object> obj, 10915 Local<Value> name, 10916 v8::AccessType type, 10917 Local<Value> data) { 10918 return !(name->Equals(a) || name->Equals(h)); 10919} 10920 10921 10922THREADED_TEST(TurnOnAccessCheckAndRecompile) { 10923 v8::HandleScope handle_scope; 10924 10925 // Create an environment with access check to the global object disabled by 10926 // default. When the registered access checker will block access to properties 10927 // a and h 10928 a = v8_str("a"); 10929 h = v8_str("h"); 10930 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 10931 global_template->SetAccessCheckCallbacks(NamedGetAccessBlockAandH, 10932 IndexedGetAccessBlocker, 10933 v8::Handle<v8::Value>(), 10934 false); 10935 v8::Persistent<Context> context = Context::New(NULL, global_template); 10936 Context::Scope context_scope(context); 10937 10938 // Set up a property and a number of functions. 10939 context->Global()->Set(v8_str("a"), v8_num(1)); 10940 static const char* source = "function f1() {return a;}" 10941 "function f2() {return a;}" 10942 "function g1() {return h();}" 10943 "function g2() {return h();}" 10944 "function h() {return 1;}"; 10945 10946 CompileRun(source); 10947 Local<Function> f1; 10948 Local<Function> f2; 10949 Local<Function> g1; 10950 Local<Function> g2; 10951 Local<Function> h; 10952 f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); 10953 f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); 10954 g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); 10955 g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); 10956 h = Local<Function>::Cast(context->Global()->Get(v8_str("h"))); 10957 10958 // Get the global object. 10959 v8::Handle<v8::Object> global = context->Global(); 10960 10961 // Call f1 one time and f2 a number of times. This will ensure that f1 still 10962 // uses the runtime system to retreive property a whereas f2 uses global load 10963 // inline cache. 10964 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1))); 10965 for (int i = 0; i < 4; i++) { 10966 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1))); 10967 } 10968 10969 // Same for g1 and g2. 10970 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1))); 10971 for (int i = 0; i < 4; i++) { 10972 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1))); 10973 } 10974 10975 // Detach the global and turn on access check now blocking access to property 10976 // a and function h. 10977 context->DetachGlobal(); 10978 context->Global()->TurnOnAccessCheck(); 10979 10980 // Failing access check to property get results in undefined. 10981 CHECK(f1->Call(global, 0, NULL)->IsUndefined()); 10982 CHECK(f2->Call(global, 0, NULL)->IsUndefined()); 10983 10984 // Failing access check to function call results in exception. 10985 CHECK(g1->Call(global, 0, NULL).IsEmpty()); 10986 CHECK(g2->Call(global, 0, NULL).IsEmpty()); 10987 10988 // No failing access check when just returning a constant. 10989 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1))); 10990 10991 // Now compile the source again. And get the newly compiled functions, except 10992 // for h for which access is blocked. 10993 CompileRun(source); 10994 f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); 10995 f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); 10996 g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); 10997 g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); 10998 CHECK(context->Global()->Get(v8_str("h"))->IsUndefined()); 10999 11000 // Failing access check to property get results in undefined. 11001 CHECK(f1->Call(global, 0, NULL)->IsUndefined()); 11002 CHECK(f2->Call(global, 0, NULL)->IsUndefined()); 11003 11004 // Failing access check to function call results in exception. 11005 CHECK(g1->Call(global, 0, NULL).IsEmpty()); 11006 CHECK(g2->Call(global, 0, NULL).IsEmpty()); 11007} 11008 11009 11010// This test verifies that pre-compilation (aka preparsing) can be called 11011// without initializing the whole VM. Thus we cannot run this test in a 11012// multi-threaded setup. 11013TEST(PreCompile) { 11014 // TODO(155): This test would break without the initialization of V8. This is 11015 // a workaround for now to make this test not fail. 11016 v8::V8::Initialize(); 11017 const char* script = "function foo(a) { return a+1; }"; 11018 v8::ScriptData* sd = 11019 v8::ScriptData::PreCompile(script, i::StrLength(script)); 11020 CHECK_NE(sd->Length(), 0); 11021 CHECK_NE(sd->Data(), NULL); 11022 CHECK(!sd->HasError()); 11023 delete sd; 11024} 11025 11026 11027TEST(PreCompileWithError) { 11028 v8::V8::Initialize(); 11029 const char* script = "function foo(a) { return 1 * * 2; }"; 11030 v8::ScriptData* sd = 11031 v8::ScriptData::PreCompile(script, i::StrLength(script)); 11032 CHECK(sd->HasError()); 11033 delete sd; 11034} 11035 11036 11037TEST(Regress31661) { 11038 v8::V8::Initialize(); 11039 const char* script = " The Definintive Guide"; 11040 v8::ScriptData* sd = 11041 v8::ScriptData::PreCompile(script, i::StrLength(script)); 11042 CHECK(sd->HasError()); 11043 delete sd; 11044} 11045 11046 11047// Tests that ScriptData can be serialized and deserialized. 11048TEST(PreCompileSerialization) { 11049 v8::V8::Initialize(); 11050 const char* script = "function foo(a) { return a+1; }"; 11051 v8::ScriptData* sd = 11052 v8::ScriptData::PreCompile(script, i::StrLength(script)); 11053 11054 // Serialize. 11055 int serialized_data_length = sd->Length(); 11056 char* serialized_data = i::NewArray<char>(serialized_data_length); 11057 memcpy(serialized_data, sd->Data(), serialized_data_length); 11058 11059 // Deserialize. 11060 v8::ScriptData* deserialized_sd = 11061 v8::ScriptData::New(serialized_data, serialized_data_length); 11062 11063 // Verify that the original is the same as the deserialized. 11064 CHECK_EQ(sd->Length(), deserialized_sd->Length()); 11065 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length())); 11066 CHECK_EQ(sd->HasError(), deserialized_sd->HasError()); 11067 11068 delete sd; 11069 delete deserialized_sd; 11070} 11071 11072 11073// Attempts to deserialize bad data. 11074TEST(PreCompileDeserializationError) { 11075 v8::V8::Initialize(); 11076 const char* data = "DONT CARE"; 11077 int invalid_size = 3; 11078 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size); 11079 11080 CHECK_EQ(0, sd->Length()); 11081 11082 delete sd; 11083} 11084 11085 11086// Attempts to deserialize bad data. 11087TEST(PreCompileInvalidPreparseDataError) { 11088 v8::V8::Initialize(); 11089 v8::HandleScope scope; 11090 LocalContext context; 11091 11092 const char* script = "function foo(){ return 5;}\n" 11093 "function bar(){ return 6 + 7;} foo();"; 11094 v8::ScriptData* sd = 11095 v8::ScriptData::PreCompile(script, i::StrLength(script)); 11096 CHECK(!sd->HasError()); 11097 // ScriptDataImpl private implementation details 11098 const int kHeaderSize = i::PreparseDataConstants::kHeaderSize; 11099 const int kFunctionEntrySize = i::FunctionEntry::kSize; 11100 const int kFunctionEntryStartOffset = 0; 11101 const int kFunctionEntryEndOffset = 1; 11102 unsigned* sd_data = 11103 reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data())); 11104 11105 // Overwrite function bar's end position with 0. 11106 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0; 11107 v8::TryCatch try_catch; 11108 11109 Local<String> source = String::New(script); 11110 Local<Script> compiled_script = Script::New(source, NULL, sd); 11111 CHECK(try_catch.HasCaught()); 11112 String::AsciiValue exception_value(try_catch.Message()->Get()); 11113 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar", 11114 *exception_value); 11115 11116 try_catch.Reset(); 11117 11118 // Overwrite function bar's start position with 200. The function entry 11119 // will not be found when searching for it by position and we should fall 11120 // back on eager compilation. 11121 sd = v8::ScriptData::PreCompile(script, i::StrLength(script)); 11122 sd_data = reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data())); 11123 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] = 11124 200; 11125 compiled_script = Script::New(source, NULL, sd); 11126 CHECK(!try_catch.HasCaught()); 11127 11128 delete sd; 11129} 11130 11131 11132// Verifies that the Handle<String> and const char* versions of the API produce 11133// the same results (at least for one trivial case). 11134TEST(PreCompileAPIVariationsAreSame) { 11135 v8::V8::Initialize(); 11136 v8::HandleScope scope; 11137 11138 const char* cstring = "function foo(a) { return a+1; }"; 11139 11140 v8::ScriptData* sd_from_cstring = 11141 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring)); 11142 11143 TestAsciiResource* resource = new TestAsciiResource(cstring); 11144 v8::ScriptData* sd_from_external_string = v8::ScriptData::PreCompile( 11145 v8::String::NewExternal(resource)); 11146 11147 v8::ScriptData* sd_from_string = v8::ScriptData::PreCompile( 11148 v8::String::New(cstring)); 11149 11150 CHECK_EQ(sd_from_cstring->Length(), sd_from_external_string->Length()); 11151 CHECK_EQ(0, memcmp(sd_from_cstring->Data(), 11152 sd_from_external_string->Data(), 11153 sd_from_cstring->Length())); 11154 11155 CHECK_EQ(sd_from_cstring->Length(), sd_from_string->Length()); 11156 CHECK_EQ(0, memcmp(sd_from_cstring->Data(), 11157 sd_from_string->Data(), 11158 sd_from_cstring->Length())); 11159 11160 11161 delete sd_from_cstring; 11162 delete sd_from_external_string; 11163 delete sd_from_string; 11164} 11165 11166 11167// This tests that we do not allow dictionary load/call inline caches 11168// to use functions that have not yet been compiled. The potential 11169// problem of loading a function that has not yet been compiled can 11170// arise because we share code between contexts via the compilation 11171// cache. 11172THREADED_TEST(DictionaryICLoadedFunction) { 11173 v8::HandleScope scope; 11174 // Test LoadIC. 11175 for (int i = 0; i < 2; i++) { 11176 LocalContext context; 11177 context->Global()->Set(v8_str("tmp"), v8::True()); 11178 context->Global()->Delete(v8_str("tmp")); 11179 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');"); 11180 } 11181 // Test CallIC. 11182 for (int i = 0; i < 2; i++) { 11183 LocalContext context; 11184 context->Global()->Set(v8_str("tmp"), v8::True()); 11185 context->Global()->Delete(v8_str("tmp")); 11186 CompileRun("for (var j = 0; j < 10; j++) RegExp('')"); 11187 } 11188} 11189 11190 11191// Test that cross-context new calls use the context of the callee to 11192// create the new JavaScript object. 11193THREADED_TEST(CrossContextNew) { 11194 v8::HandleScope scope; 11195 v8::Persistent<Context> context0 = Context::New(); 11196 v8::Persistent<Context> context1 = Context::New(); 11197 11198 // Allow cross-domain access. 11199 Local<String> token = v8_str("<security token>"); 11200 context0->SetSecurityToken(token); 11201 context1->SetSecurityToken(token); 11202 11203 // Set an 'x' property on the Object prototype and define a 11204 // constructor function in context0. 11205 context0->Enter(); 11206 CompileRun("Object.prototype.x = 42; function C() {};"); 11207 context0->Exit(); 11208 11209 // Call the constructor function from context0 and check that the 11210 // result has the 'x' property. 11211 context1->Enter(); 11212 context1->Global()->Set(v8_str("other"), context0->Global()); 11213 Local<Value> value = CompileRun("var instance = new other.C(); instance.x"); 11214 CHECK(value->IsInt32()); 11215 CHECK_EQ(42, value->Int32Value()); 11216 context1->Exit(); 11217 11218 // Dispose the contexts to allow them to be garbage collected. 11219 context0.Dispose(); 11220 context1.Dispose(); 11221} 11222 11223 11224class RegExpInterruptTest { 11225 public: 11226 RegExpInterruptTest() : block_(NULL) {} 11227 ~RegExpInterruptTest() { delete block_; } 11228 void RunTest() { 11229 block_ = i::OS::CreateSemaphore(0); 11230 gc_count_ = 0; 11231 gc_during_regexp_ = 0; 11232 regexp_success_ = false; 11233 gc_success_ = false; 11234 GCThread gc_thread(this); 11235 gc_thread.Start(); 11236 v8::Locker::StartPreemption(1); 11237 11238 LongRunningRegExp(); 11239 { 11240 v8::Unlocker unlock; 11241 gc_thread.Join(); 11242 } 11243 v8::Locker::StopPreemption(); 11244 CHECK(regexp_success_); 11245 CHECK(gc_success_); 11246 } 11247 11248 private: 11249 // Number of garbage collections required. 11250 static const int kRequiredGCs = 5; 11251 11252 class GCThread : public i::Thread { 11253 public: 11254 explicit GCThread(RegExpInterruptTest* test) 11255 : Thread("GCThread"), test_(test) {} 11256 virtual void Run() { 11257 test_->CollectGarbage(); 11258 } 11259 private: 11260 RegExpInterruptTest* test_; 11261 }; 11262 11263 void CollectGarbage() { 11264 block_->Wait(); 11265 while (gc_during_regexp_ < kRequiredGCs) { 11266 { 11267 v8::Locker lock; 11268 // TODO(lrn): Perhaps create some garbage before collecting. 11269 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 11270 gc_count_++; 11271 } 11272 i::OS::Sleep(1); 11273 } 11274 gc_success_ = true; 11275 } 11276 11277 void LongRunningRegExp() { 11278 block_->Signal(); // Enable garbage collection thread on next preemption. 11279 int rounds = 0; 11280 while (gc_during_regexp_ < kRequiredGCs) { 11281 int gc_before = gc_count_; 11282 { 11283 // Match 15-30 "a"'s against 14 and a "b". 11284 const char* c_source = 11285 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 11286 ".exec('aaaaaaaaaaaaaaab') === null"; 11287 Local<String> source = String::New(c_source); 11288 Local<Script> script = Script::Compile(source); 11289 Local<Value> result = script->Run(); 11290 if (!result->BooleanValue()) { 11291 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit. 11292 return; 11293 } 11294 } 11295 { 11296 // Match 15-30 "a"'s against 15 and a "b". 11297 const char* c_source = 11298 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 11299 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'"; 11300 Local<String> source = String::New(c_source); 11301 Local<Script> script = Script::Compile(source); 11302 Local<Value> result = script->Run(); 11303 if (!result->BooleanValue()) { 11304 gc_during_regexp_ = kRequiredGCs; 11305 return; 11306 } 11307 } 11308 int gc_after = gc_count_; 11309 gc_during_regexp_ += gc_after - gc_before; 11310 rounds++; 11311 i::OS::Sleep(1); 11312 } 11313 regexp_success_ = true; 11314 } 11315 11316 i::Semaphore* block_; 11317 int gc_count_; 11318 int gc_during_regexp_; 11319 bool regexp_success_; 11320 bool gc_success_; 11321}; 11322 11323 11324// Test that a regular expression execution can be interrupted and 11325// survive a garbage collection. 11326TEST(RegExpInterruption) { 11327 v8::Locker lock; 11328 v8::V8::Initialize(); 11329 v8::HandleScope scope; 11330 Local<Context> local_env; 11331 { 11332 LocalContext env; 11333 local_env = env.local(); 11334 } 11335 11336 // Local context should still be live. 11337 CHECK(!local_env.IsEmpty()); 11338 local_env->Enter(); 11339 11340 // Should complete without problems. 11341 RegExpInterruptTest().RunTest(); 11342 11343 local_env->Exit(); 11344} 11345 11346 11347class ApplyInterruptTest { 11348 public: 11349 ApplyInterruptTest() : block_(NULL) {} 11350 ~ApplyInterruptTest() { delete block_; } 11351 void RunTest() { 11352 block_ = i::OS::CreateSemaphore(0); 11353 gc_count_ = 0; 11354 gc_during_apply_ = 0; 11355 apply_success_ = false; 11356 gc_success_ = false; 11357 GCThread gc_thread(this); 11358 gc_thread.Start(); 11359 v8::Locker::StartPreemption(1); 11360 11361 LongRunningApply(); 11362 { 11363 v8::Unlocker unlock; 11364 gc_thread.Join(); 11365 } 11366 v8::Locker::StopPreemption(); 11367 CHECK(apply_success_); 11368 CHECK(gc_success_); 11369 } 11370 11371 private: 11372 // Number of garbage collections required. 11373 static const int kRequiredGCs = 2; 11374 11375 class GCThread : public i::Thread { 11376 public: 11377 explicit GCThread(ApplyInterruptTest* test) 11378 : Thread("GCThread"), test_(test) {} 11379 virtual void Run() { 11380 test_->CollectGarbage(); 11381 } 11382 private: 11383 ApplyInterruptTest* test_; 11384 }; 11385 11386 void CollectGarbage() { 11387 block_->Wait(); 11388 while (gc_during_apply_ < kRequiredGCs) { 11389 { 11390 v8::Locker lock; 11391 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 11392 gc_count_++; 11393 } 11394 i::OS::Sleep(1); 11395 } 11396 gc_success_ = true; 11397 } 11398 11399 void LongRunningApply() { 11400 block_->Signal(); 11401 int rounds = 0; 11402 while (gc_during_apply_ < kRequiredGCs) { 11403 int gc_before = gc_count_; 11404 { 11405 const char* c_source = 11406 "function do_very_little(bar) {" 11407 " this.foo = bar;" 11408 "}" 11409 "for (var i = 0; i < 100000; i++) {" 11410 " do_very_little.apply(this, ['bar']);" 11411 "}"; 11412 Local<String> source = String::New(c_source); 11413 Local<Script> script = Script::Compile(source); 11414 Local<Value> result = script->Run(); 11415 // Check that no exception was thrown. 11416 CHECK(!result.IsEmpty()); 11417 } 11418 int gc_after = gc_count_; 11419 gc_during_apply_ += gc_after - gc_before; 11420 rounds++; 11421 } 11422 apply_success_ = true; 11423 } 11424 11425 i::Semaphore* block_; 11426 int gc_count_; 11427 int gc_during_apply_; 11428 bool apply_success_; 11429 bool gc_success_; 11430}; 11431 11432 11433// Test that nothing bad happens if we get a preemption just when we were 11434// about to do an apply(). 11435TEST(ApplyInterruption) { 11436 v8::Locker lock; 11437 v8::V8::Initialize(); 11438 v8::HandleScope scope; 11439 Local<Context> local_env; 11440 { 11441 LocalContext env; 11442 local_env = env.local(); 11443 } 11444 11445 // Local context should still be live. 11446 CHECK(!local_env.IsEmpty()); 11447 local_env->Enter(); 11448 11449 // Should complete without problems. 11450 ApplyInterruptTest().RunTest(); 11451 11452 local_env->Exit(); 11453} 11454 11455 11456// Verify that we can clone an object 11457TEST(ObjectClone) { 11458 v8::HandleScope scope; 11459 LocalContext env; 11460 11461 const char* sample = 11462 "var rv = {};" \ 11463 "rv.alpha = 'hello';" \ 11464 "rv.beta = 123;" \ 11465 "rv;"; 11466 11467 // Create an object, verify basics. 11468 Local<Value> val = CompileRun(sample); 11469 CHECK(val->IsObject()); 11470 Local<v8::Object> obj = val.As<v8::Object>(); 11471 obj->Set(v8_str("gamma"), v8_str("cloneme")); 11472 11473 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha"))); 11474 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 11475 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma"))); 11476 11477 // Clone it. 11478 Local<v8::Object> clone = obj->Clone(); 11479 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha"))); 11480 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta"))); 11481 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma"))); 11482 11483 // Set a property on the clone, verify each object. 11484 clone->Set(v8_str("beta"), v8::Integer::New(456)); 11485 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 11486 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta"))); 11487} 11488 11489 11490class AsciiVectorResource : public v8::String::ExternalAsciiStringResource { 11491 public: 11492 explicit AsciiVectorResource(i::Vector<const char> vector) 11493 : data_(vector) {} 11494 virtual ~AsciiVectorResource() {} 11495 virtual size_t length() const { return data_.length(); } 11496 virtual const char* data() const { return data_.start(); } 11497 private: 11498 i::Vector<const char> data_; 11499}; 11500 11501 11502class UC16VectorResource : public v8::String::ExternalStringResource { 11503 public: 11504 explicit UC16VectorResource(i::Vector<const i::uc16> vector) 11505 : data_(vector) {} 11506 virtual ~UC16VectorResource() {} 11507 virtual size_t length() const { return data_.length(); } 11508 virtual const i::uc16* data() const { return data_.start(); } 11509 private: 11510 i::Vector<const i::uc16> data_; 11511}; 11512 11513 11514static void MorphAString(i::String* string, 11515 AsciiVectorResource* ascii_resource, 11516 UC16VectorResource* uc16_resource) { 11517 CHECK(i::StringShape(string).IsExternal()); 11518 if (string->IsAsciiRepresentation()) { 11519 // Check old map is not symbol or long. 11520 CHECK(string->map() == HEAP->external_ascii_string_map()); 11521 // Morph external string to be TwoByte string. 11522 string->set_map(HEAP->external_string_map()); 11523 i::ExternalTwoByteString* morphed = 11524 i::ExternalTwoByteString::cast(string); 11525 morphed->set_resource(uc16_resource); 11526 } else { 11527 // Check old map is not symbol or long. 11528 CHECK(string->map() == HEAP->external_string_map()); 11529 // Morph external string to be ASCII string. 11530 string->set_map(HEAP->external_ascii_string_map()); 11531 i::ExternalAsciiString* morphed = 11532 i::ExternalAsciiString::cast(string); 11533 morphed->set_resource(ascii_resource); 11534 } 11535} 11536 11537 11538// Test that we can still flatten a string if the components it is built up 11539// from have been turned into 16 bit strings in the mean time. 11540THREADED_TEST(MorphCompositeStringTest) { 11541 char utf_buffer[129]; 11542 const char* c_string = "Now is the time for all good men" 11543 " to come to the aid of the party"; 11544 uint16_t* two_byte_string = AsciiToTwoByteString(c_string); 11545 { 11546 v8::HandleScope scope; 11547 LocalContext env; 11548 AsciiVectorResource ascii_resource( 11549 i::Vector<const char>(c_string, i::StrLength(c_string))); 11550 UC16VectorResource uc16_resource( 11551 i::Vector<const uint16_t>(two_byte_string, 11552 i::StrLength(c_string))); 11553 11554 Local<String> lhs(v8::Utils::ToLocal( 11555 FACTORY->NewExternalStringFromAscii(&ascii_resource))); 11556 Local<String> rhs(v8::Utils::ToLocal( 11557 FACTORY->NewExternalStringFromAscii(&ascii_resource))); 11558 11559 env->Global()->Set(v8_str("lhs"), lhs); 11560 env->Global()->Set(v8_str("rhs"), rhs); 11561 11562 CompileRun( 11563 "var cons = lhs + rhs;" 11564 "var slice = lhs.substring(1, lhs.length - 1);" 11565 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);"); 11566 11567 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource); 11568 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource); 11569 11570 // This should UTF-8 without flattening, since everything is ASCII. 11571 Handle<String> cons = v8_compile("cons")->Run().As<String>(); 11572 CHECK_EQ(128, cons->Utf8Length()); 11573 int nchars = -1; 11574 CHECK_EQ(129, cons->WriteUtf8(utf_buffer, -1, &nchars)); 11575 CHECK_EQ(128, nchars); 11576 CHECK_EQ(0, strcmp( 11577 utf_buffer, 11578 "Now is the time for all good men to come to the aid of the party" 11579 "Now is the time for all good men to come to the aid of the party")); 11580 11581 // Now do some stuff to make sure the strings are flattened, etc. 11582 CompileRun( 11583 "/[^a-z]/.test(cons);" 11584 "/[^a-z]/.test(slice);" 11585 "/[^a-z]/.test(slice_on_cons);"); 11586 const char* expected_cons = 11587 "Now is the time for all good men to come to the aid of the party" 11588 "Now is the time for all good men to come to the aid of the party"; 11589 const char* expected_slice = 11590 "ow is the time for all good men to come to the aid of the part"; 11591 const char* expected_slice_on_cons = 11592 "ow is the time for all good men to come to the aid of the party" 11593 "Now is the time for all good men to come to the aid of the part"; 11594 CHECK_EQ(String::New(expected_cons), 11595 env->Global()->Get(v8_str("cons"))); 11596 CHECK_EQ(String::New(expected_slice), 11597 env->Global()->Get(v8_str("slice"))); 11598 CHECK_EQ(String::New(expected_slice_on_cons), 11599 env->Global()->Get(v8_str("slice_on_cons"))); 11600 } 11601 i::DeleteArray(two_byte_string); 11602} 11603 11604 11605TEST(CompileExternalTwoByteSource) { 11606 v8::HandleScope scope; 11607 LocalContext context; 11608 11609 // This is a very short list of sources, which currently is to check for a 11610 // regression caused by r2703. 11611 const char* ascii_sources[] = { 11612 "0.5", 11613 "-0.5", // This mainly testes PushBack in the Scanner. 11614 "--0.5", // This mainly testes PushBack in the Scanner. 11615 NULL 11616 }; 11617 11618 // Compile the sources as external two byte strings. 11619 for (int i = 0; ascii_sources[i] != NULL; i++) { 11620 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]); 11621 UC16VectorResource uc16_resource( 11622 i::Vector<const uint16_t>(two_byte_string, 11623 i::StrLength(ascii_sources[i]))); 11624 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource); 11625 v8::Script::Compile(source); 11626 i::DeleteArray(two_byte_string); 11627 } 11628} 11629 11630 11631class RegExpStringModificationTest { 11632 public: 11633 RegExpStringModificationTest() 11634 : block_(i::OS::CreateSemaphore(0)), 11635 morphs_(0), 11636 morphs_during_regexp_(0), 11637 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)), 11638 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {} 11639 ~RegExpStringModificationTest() { delete block_; } 11640 void RunTest() { 11641 regexp_success_ = false; 11642 morph_success_ = false; 11643 11644 // Initialize the contents of two_byte_content_ to be a uc16 representation 11645 // of "aaaaaaaaaaaaaab". 11646 for (int i = 0; i < 14; i++) { 11647 two_byte_content_[i] = 'a'; 11648 } 11649 two_byte_content_[14] = 'b'; 11650 11651 // Create the input string for the regexp - the one we are going to change 11652 // properties of. 11653 input_ = FACTORY->NewExternalStringFromAscii(&ascii_resource_); 11654 11655 // Inject the input as a global variable. 11656 i::Handle<i::String> input_name = 11657 FACTORY->NewStringFromAscii(i::Vector<const char>("input", 5)); 11658 i::Isolate::Current()->global_context()->global()->SetProperty( 11659 *input_name, 11660 *input_, 11661 NONE, 11662 i::kNonStrictMode)->ToObjectChecked(); 11663 11664 MorphThread morph_thread(this); 11665 morph_thread.Start(); 11666 v8::Locker::StartPreemption(1); 11667 LongRunningRegExp(); 11668 { 11669 v8::Unlocker unlock; 11670 morph_thread.Join(); 11671 } 11672 v8::Locker::StopPreemption(); 11673 CHECK(regexp_success_); 11674 CHECK(morph_success_); 11675 } 11676 11677 private: 11678 // Number of string modifications required. 11679 static const int kRequiredModifications = 5; 11680 static const int kMaxModifications = 100; 11681 11682 class MorphThread : public i::Thread { 11683 public: 11684 explicit MorphThread(RegExpStringModificationTest* test) 11685 : Thread("MorphThread"), test_(test) {} 11686 virtual void Run() { 11687 test_->MorphString(); 11688 } 11689 private: 11690 RegExpStringModificationTest* test_; 11691 }; 11692 11693 void MorphString() { 11694 block_->Wait(); 11695 while (morphs_during_regexp_ < kRequiredModifications && 11696 morphs_ < kMaxModifications) { 11697 { 11698 v8::Locker lock; 11699 // Swap string between ascii and two-byte representation. 11700 i::String* string = *input_; 11701 MorphAString(string, &ascii_resource_, &uc16_resource_); 11702 morphs_++; 11703 } 11704 i::OS::Sleep(1); 11705 } 11706 morph_success_ = true; 11707 } 11708 11709 void LongRunningRegExp() { 11710 block_->Signal(); // Enable morphing thread on next preemption. 11711 while (morphs_during_regexp_ < kRequiredModifications && 11712 morphs_ < kMaxModifications) { 11713 int morphs_before = morphs_; 11714 { 11715 v8::HandleScope scope; 11716 // Match 15-30 "a"'s against 14 and a "b". 11717 const char* c_source = 11718 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 11719 ".exec(input) === null"; 11720 Local<String> source = String::New(c_source); 11721 Local<Script> script = Script::Compile(source); 11722 Local<Value> result = script->Run(); 11723 CHECK(result->IsTrue()); 11724 } 11725 int morphs_after = morphs_; 11726 morphs_during_regexp_ += morphs_after - morphs_before; 11727 } 11728 regexp_success_ = true; 11729 } 11730 11731 i::uc16 two_byte_content_[15]; 11732 i::Semaphore* block_; 11733 int morphs_; 11734 int morphs_during_regexp_; 11735 bool regexp_success_; 11736 bool morph_success_; 11737 i::Handle<i::String> input_; 11738 AsciiVectorResource ascii_resource_; 11739 UC16VectorResource uc16_resource_; 11740}; 11741 11742 11743// Test that a regular expression execution can be interrupted and 11744// the string changed without failing. 11745TEST(RegExpStringModification) { 11746 v8::Locker lock; 11747 v8::V8::Initialize(); 11748 v8::HandleScope scope; 11749 Local<Context> local_env; 11750 { 11751 LocalContext env; 11752 local_env = env.local(); 11753 } 11754 11755 // Local context should still be live. 11756 CHECK(!local_env.IsEmpty()); 11757 local_env->Enter(); 11758 11759 // Should complete without problems. 11760 RegExpStringModificationTest().RunTest(); 11761 11762 local_env->Exit(); 11763} 11764 11765 11766// Test that we can set a property on the global object even if there 11767// is a read-only property in the prototype chain. 11768TEST(ReadOnlyPropertyInGlobalProto) { 11769 v8::HandleScope scope; 11770 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 11771 LocalContext context(0, templ); 11772 v8::Handle<v8::Object> global = context->Global(); 11773 v8::Handle<v8::Object> global_proto = 11774 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__"))); 11775 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly); 11776 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly); 11777 // Check without 'eval' or 'with'. 11778 v8::Handle<v8::Value> res = 11779 CompileRun("function f() { x = 42; return x; }; f()"); 11780 // Check with 'eval'. 11781 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()"); 11782 CHECK_EQ(v8::Integer::New(42), res); 11783 // Check with 'with'. 11784 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()"); 11785 CHECK_EQ(v8::Integer::New(42), res); 11786} 11787 11788static int force_set_set_count = 0; 11789static int force_set_get_count = 0; 11790bool pass_on_get = false; 11791 11792static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name, 11793 const v8::AccessorInfo& info) { 11794 force_set_get_count++; 11795 if (pass_on_get) { 11796 return v8::Handle<v8::Value>(); 11797 } else { 11798 return v8::Int32::New(3); 11799 } 11800} 11801 11802static void ForceSetSetter(v8::Local<v8::String> name, 11803 v8::Local<v8::Value> value, 11804 const v8::AccessorInfo& info) { 11805 force_set_set_count++; 11806} 11807 11808static v8::Handle<v8::Value> ForceSetInterceptSetter( 11809 v8::Local<v8::String> name, 11810 v8::Local<v8::Value> value, 11811 const v8::AccessorInfo& info) { 11812 force_set_set_count++; 11813 return v8::Undefined(); 11814} 11815 11816TEST(ForceSet) { 11817 force_set_get_count = 0; 11818 force_set_set_count = 0; 11819 pass_on_get = false; 11820 11821 v8::HandleScope scope; 11822 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 11823 v8::Handle<v8::String> access_property = v8::String::New("a"); 11824 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter); 11825 LocalContext context(NULL, templ); 11826 v8::Handle<v8::Object> global = context->Global(); 11827 11828 // Ordinary properties 11829 v8::Handle<v8::String> simple_property = v8::String::New("p"); 11830 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly); 11831 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 11832 // This should fail because the property is read-only 11833 global->Set(simple_property, v8::Int32::New(5)); 11834 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 11835 // This should succeed even though the property is read-only 11836 global->ForceSet(simple_property, v8::Int32::New(6)); 11837 CHECK_EQ(6, global->Get(simple_property)->Int32Value()); 11838 11839 // Accessors 11840 CHECK_EQ(0, force_set_set_count); 11841 CHECK_EQ(0, force_set_get_count); 11842 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 11843 // CHECK_EQ the property shouldn't override it, just call the setter 11844 // which in this case does nothing. 11845 global->Set(access_property, v8::Int32::New(7)); 11846 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 11847 CHECK_EQ(1, force_set_set_count); 11848 CHECK_EQ(2, force_set_get_count); 11849 // Forcing the property to be set should override the accessor without 11850 // calling it 11851 global->ForceSet(access_property, v8::Int32::New(8)); 11852 CHECK_EQ(8, global->Get(access_property)->Int32Value()); 11853 CHECK_EQ(1, force_set_set_count); 11854 CHECK_EQ(2, force_set_get_count); 11855} 11856 11857TEST(ForceSetWithInterceptor) { 11858 force_set_get_count = 0; 11859 force_set_set_count = 0; 11860 pass_on_get = false; 11861 11862 v8::HandleScope scope; 11863 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 11864 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter); 11865 LocalContext context(NULL, templ); 11866 v8::Handle<v8::Object> global = context->Global(); 11867 11868 v8::Handle<v8::String> some_property = v8::String::New("a"); 11869 CHECK_EQ(0, force_set_set_count); 11870 CHECK_EQ(0, force_set_get_count); 11871 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 11872 // Setting the property shouldn't override it, just call the setter 11873 // which in this case does nothing. 11874 global->Set(some_property, v8::Int32::New(7)); 11875 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 11876 CHECK_EQ(1, force_set_set_count); 11877 CHECK_EQ(2, force_set_get_count); 11878 // Getting the property when the interceptor returns an empty handle 11879 // should yield undefined, since the property isn't present on the 11880 // object itself yet. 11881 pass_on_get = true; 11882 CHECK(global->Get(some_property)->IsUndefined()); 11883 CHECK_EQ(1, force_set_set_count); 11884 CHECK_EQ(3, force_set_get_count); 11885 // Forcing the property to be set should cause the value to be 11886 // set locally without calling the interceptor. 11887 global->ForceSet(some_property, v8::Int32::New(8)); 11888 CHECK_EQ(8, global->Get(some_property)->Int32Value()); 11889 CHECK_EQ(1, force_set_set_count); 11890 CHECK_EQ(4, force_set_get_count); 11891 // Reenabling the interceptor should cause it to take precedence over 11892 // the property 11893 pass_on_get = false; 11894 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 11895 CHECK_EQ(1, force_set_set_count); 11896 CHECK_EQ(5, force_set_get_count); 11897 // The interceptor should also work for other properties 11898 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value()); 11899 CHECK_EQ(1, force_set_set_count); 11900 CHECK_EQ(6, force_set_get_count); 11901} 11902 11903 11904THREADED_TEST(ForceDelete) { 11905 v8::HandleScope scope; 11906 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 11907 LocalContext context(NULL, templ); 11908 v8::Handle<v8::Object> global = context->Global(); 11909 11910 // Ordinary properties 11911 v8::Handle<v8::String> simple_property = v8::String::New("p"); 11912 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete); 11913 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 11914 // This should fail because the property is dont-delete. 11915 CHECK(!global->Delete(simple_property)); 11916 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 11917 // This should succeed even though the property is dont-delete. 11918 CHECK(global->ForceDelete(simple_property)); 11919 CHECK(global->Get(simple_property)->IsUndefined()); 11920} 11921 11922 11923static int force_delete_interceptor_count = 0; 11924static bool pass_on_delete = false; 11925 11926 11927static v8::Handle<v8::Boolean> ForceDeleteDeleter( 11928 v8::Local<v8::String> name, 11929 const v8::AccessorInfo& info) { 11930 force_delete_interceptor_count++; 11931 if (pass_on_delete) { 11932 return v8::Handle<v8::Boolean>(); 11933 } else { 11934 return v8::True(); 11935 } 11936} 11937 11938 11939THREADED_TEST(ForceDeleteWithInterceptor) { 11940 force_delete_interceptor_count = 0; 11941 pass_on_delete = false; 11942 11943 v8::HandleScope scope; 11944 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 11945 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter); 11946 LocalContext context(NULL, templ); 11947 v8::Handle<v8::Object> global = context->Global(); 11948 11949 v8::Handle<v8::String> some_property = v8::String::New("a"); 11950 global->Set(some_property, v8::Integer::New(42), v8::DontDelete); 11951 11952 // Deleting a property should get intercepted and nothing should 11953 // happen. 11954 CHECK_EQ(0, force_delete_interceptor_count); 11955 CHECK(global->Delete(some_property)); 11956 CHECK_EQ(1, force_delete_interceptor_count); 11957 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 11958 // Deleting the property when the interceptor returns an empty 11959 // handle should not delete the property since it is DontDelete. 11960 pass_on_delete = true; 11961 CHECK(!global->Delete(some_property)); 11962 CHECK_EQ(2, force_delete_interceptor_count); 11963 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 11964 // Forcing the property to be deleted should delete the value 11965 // without calling the interceptor. 11966 CHECK(global->ForceDelete(some_property)); 11967 CHECK(global->Get(some_property)->IsUndefined()); 11968 CHECK_EQ(2, force_delete_interceptor_count); 11969} 11970 11971 11972// Make sure that forcing a delete invalidates any IC stubs, so we 11973// don't read the hole value. 11974THREADED_TEST(ForceDeleteIC) { 11975 v8::HandleScope scope; 11976 LocalContext context; 11977 // Create a DontDelete variable on the global object. 11978 CompileRun("this.__proto__ = { foo: 'horse' };" 11979 "var foo = 'fish';" 11980 "function f() { return foo.length; }"); 11981 // Initialize the IC for foo in f. 11982 CompileRun("for (var i = 0; i < 4; i++) f();"); 11983 // Make sure the value of foo is correct before the deletion. 11984 CHECK_EQ(4, CompileRun("f()")->Int32Value()); 11985 // Force the deletion of foo. 11986 CHECK(context->Global()->ForceDelete(v8_str("foo"))); 11987 // Make sure the value for foo is read from the prototype, and that 11988 // we don't get in trouble with reading the deleted cell value 11989 // sentinel. 11990 CHECK_EQ(5, CompileRun("f()")->Int32Value()); 11991} 11992 11993 11994v8::Persistent<Context> calling_context0; 11995v8::Persistent<Context> calling_context1; 11996v8::Persistent<Context> calling_context2; 11997 11998 11999// Check that the call to the callback is initiated in 12000// calling_context2, the directly calling context is calling_context1 12001// and the callback itself is in calling_context0. 12002static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) { 12003 ApiTestFuzzer::Fuzz(); 12004 CHECK(Context::GetCurrent() == calling_context0); 12005 CHECK(Context::GetCalling() == calling_context1); 12006 CHECK(Context::GetEntered() == calling_context2); 12007 return v8::Integer::New(42); 12008} 12009 12010 12011THREADED_TEST(GetCallingContext) { 12012 v8::HandleScope scope; 12013 12014 calling_context0 = Context::New(); 12015 calling_context1 = Context::New(); 12016 calling_context2 = Context::New(); 12017 12018 // Allow cross-domain access. 12019 Local<String> token = v8_str("<security token>"); 12020 calling_context0->SetSecurityToken(token); 12021 calling_context1->SetSecurityToken(token); 12022 calling_context2->SetSecurityToken(token); 12023 12024 // Create an object with a C++ callback in context0. 12025 calling_context0->Enter(); 12026 Local<v8::FunctionTemplate> callback_templ = 12027 v8::FunctionTemplate::New(GetCallingContextCallback); 12028 calling_context0->Global()->Set(v8_str("callback"), 12029 callback_templ->GetFunction()); 12030 calling_context0->Exit(); 12031 12032 // Expose context0 in context1 and setup a function that calls the 12033 // callback function. 12034 calling_context1->Enter(); 12035 calling_context1->Global()->Set(v8_str("context0"), 12036 calling_context0->Global()); 12037 CompileRun("function f() { context0.callback() }"); 12038 calling_context1->Exit(); 12039 12040 // Expose context1 in context2 and call the callback function in 12041 // context0 indirectly through f in context1. 12042 calling_context2->Enter(); 12043 calling_context2->Global()->Set(v8_str("context1"), 12044 calling_context1->Global()); 12045 CompileRun("context1.f()"); 12046 calling_context2->Exit(); 12047 12048 // Dispose the contexts to allow them to be garbage collected. 12049 calling_context0.Dispose(); 12050 calling_context1.Dispose(); 12051 calling_context2.Dispose(); 12052 calling_context0.Clear(); 12053 calling_context1.Clear(); 12054 calling_context2.Clear(); 12055} 12056 12057 12058// Check that a variable declaration with no explicit initialization 12059// value does not shadow an existing property in the prototype chain. 12060// 12061// This is consistent with Firefox and Safari. 12062// 12063// See http://crbug.com/12548. 12064THREADED_TEST(InitGlobalVarInProtoChain) { 12065 v8::HandleScope scope; 12066 LocalContext context; 12067 // Introduce a variable in the prototype chain. 12068 CompileRun("__proto__.x = 42"); 12069 v8::Handle<v8::Value> result = CompileRun("var x; x"); 12070 CHECK(!result->IsUndefined()); 12071 CHECK_EQ(42, result->Int32Value()); 12072} 12073 12074 12075// Regression test for issue 398. 12076// If a function is added to an object, creating a constant function 12077// field, and the result is cloned, replacing the constant function on the 12078// original should not affect the clone. 12079// See http://code.google.com/p/v8/issues/detail?id=398 12080THREADED_TEST(ReplaceConstantFunction) { 12081 v8::HandleScope scope; 12082 LocalContext context; 12083 v8::Handle<v8::Object> obj = v8::Object::New(); 12084 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 12085 v8::Handle<v8::String> foo_string = v8::String::New("foo"); 12086 obj->Set(foo_string, func_templ->GetFunction()); 12087 v8::Handle<v8::Object> obj_clone = obj->Clone(); 12088 obj_clone->Set(foo_string, v8::String::New("Hello")); 12089 CHECK(!obj->Get(foo_string)->IsUndefined()); 12090} 12091 12092 12093// Regression test for http://crbug.com/16276. 12094THREADED_TEST(Regress16276) { 12095 v8::HandleScope scope; 12096 LocalContext context; 12097 // Force the IC in f to be a dictionary load IC. 12098 CompileRun("function f(obj) { return obj.x; }\n" 12099 "var obj = { x: { foo: 42 }, y: 87 };\n" 12100 "var x = obj.x;\n" 12101 "delete obj.y;\n" 12102 "for (var i = 0; i < 5; i++) f(obj);"); 12103 // Detach the global object to make 'this' refer directly to the 12104 // global object (not the proxy), and make sure that the dictionary 12105 // load IC doesn't mess up loading directly from the global object. 12106 context->DetachGlobal(); 12107 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value()); 12108} 12109 12110 12111THREADED_TEST(PixelArray) { 12112 v8::HandleScope scope; 12113 LocalContext context; 12114 const int kElementCount = 260; 12115 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); 12116 i::Handle<i::ExternalPixelArray> pixels = 12117 i::Handle<i::ExternalPixelArray>::cast( 12118 FACTORY->NewExternalArray(kElementCount, 12119 v8::kExternalPixelArray, 12120 pixel_data)); 12121 // Force GC to trigger verification. 12122 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 12123 for (int i = 0; i < kElementCount; i++) { 12124 pixels->set(i, i % 256); 12125 } 12126 // Force GC to trigger verification. 12127 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 12128 for (int i = 0; i < kElementCount; i++) { 12129 CHECK_EQ(i % 256, pixels->get_scalar(i)); 12130 CHECK_EQ(i % 256, pixel_data[i]); 12131 } 12132 12133 v8::Handle<v8::Object> obj = v8::Object::New(); 12134 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 12135 // Set the elements to be the pixels. 12136 // jsobj->set_elements(*pixels); 12137 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount); 12138 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 12139 obj->Set(v8_str("field"), v8::Int32::New(1503)); 12140 context->Global()->Set(v8_str("pixels"), obj); 12141 v8::Handle<v8::Value> result = CompileRun("pixels.field"); 12142 CHECK_EQ(1503, result->Int32Value()); 12143 result = CompileRun("pixels[1]"); 12144 CHECK_EQ(1, result->Int32Value()); 12145 12146 result = CompileRun("var sum = 0;" 12147 "for (var i = 0; i < 8; i++) {" 12148 " sum += pixels[i] = pixels[i] = -i;" 12149 "}" 12150 "sum;"); 12151 CHECK_EQ(-28, result->Int32Value()); 12152 12153 result = CompileRun("var sum = 0;" 12154 "for (var i = 0; i < 8; i++) {" 12155 " sum += pixels[i] = pixels[i] = 0;" 12156 "}" 12157 "sum;"); 12158 CHECK_EQ(0, result->Int32Value()); 12159 12160 result = CompileRun("var sum = 0;" 12161 "for (var i = 0; i < 8; i++) {" 12162 " sum += pixels[i] = pixels[i] = 255;" 12163 "}" 12164 "sum;"); 12165 CHECK_EQ(8 * 255, result->Int32Value()); 12166 12167 result = CompileRun("var sum = 0;" 12168 "for (var i = 0; i < 8; i++) {" 12169 " sum += pixels[i] = pixels[i] = 256 + i;" 12170 "}" 12171 "sum;"); 12172 CHECK_EQ(2076, result->Int32Value()); 12173 12174 result = CompileRun("var sum = 0;" 12175 "for (var i = 0; i < 8; i++) {" 12176 " sum += pixels[i] = pixels[i] = i;" 12177 "}" 12178 "sum;"); 12179 CHECK_EQ(28, result->Int32Value()); 12180 12181 result = CompileRun("var sum = 0;" 12182 "for (var i = 0; i < 8; i++) {" 12183 " sum += pixels[i];" 12184 "}" 12185 "sum;"); 12186 CHECK_EQ(28, result->Int32Value()); 12187 12188 i::Handle<i::Smi> value(i::Smi::FromInt(2)); 12189 i::Handle<i::Object> no_failure; 12190 no_failure = i::SetElement(jsobj, 1, value, i::kNonStrictMode); 12191 ASSERT(!no_failure.is_null()); 12192 i::USE(no_failure); 12193 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 12194 *value.location() = i::Smi::FromInt(256); 12195 no_failure = i::SetElement(jsobj, 1, value, i::kNonStrictMode); 12196 ASSERT(!no_failure.is_null()); 12197 i::USE(no_failure); 12198 CHECK_EQ(255, 12199 i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 12200 *value.location() = i::Smi::FromInt(-1); 12201 no_failure = i::SetElement(jsobj, 1, value, i::kNonStrictMode); 12202 ASSERT(!no_failure.is_null()); 12203 i::USE(no_failure); 12204 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 12205 12206 result = CompileRun("for (var i = 0; i < 8; i++) {" 12207 " pixels[i] = (i * 65) - 109;" 12208 "}" 12209 "pixels[1] + pixels[6];"); 12210 CHECK_EQ(255, result->Int32Value()); 12211 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value()); 12212 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 12213 CHECK_EQ(21, 12214 i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value()); 12215 CHECK_EQ(86, 12216 i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value()); 12217 CHECK_EQ(151, 12218 i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value()); 12219 CHECK_EQ(216, 12220 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 12221 CHECK_EQ(255, 12222 i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value()); 12223 CHECK_EQ(255, 12224 i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value()); 12225 result = CompileRun("var sum = 0;" 12226 "for (var i = 0; i < 8; i++) {" 12227 " sum += pixels[i];" 12228 "}" 12229 "sum;"); 12230 CHECK_EQ(984, result->Int32Value()); 12231 12232 result = CompileRun("for (var i = 0; i < 8; i++) {" 12233 " pixels[i] = (i * 1.1);" 12234 "}" 12235 "pixels[1] + pixels[6];"); 12236 CHECK_EQ(8, result->Int32Value()); 12237 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value()); 12238 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 12239 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value()); 12240 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value()); 12241 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value()); 12242 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 12243 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value()); 12244 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value()); 12245 12246 result = CompileRun("for (var i = 0; i < 8; i++) {" 12247 " pixels[7] = undefined;" 12248 "}" 12249 "pixels[7];"); 12250 CHECK_EQ(0, result->Int32Value()); 12251 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value()); 12252 12253 result = CompileRun("for (var i = 0; i < 8; i++) {" 12254 " pixels[6] = '2.3';" 12255 "}" 12256 "pixels[6];"); 12257 CHECK_EQ(2, result->Int32Value()); 12258 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value()); 12259 12260 result = CompileRun("for (var i = 0; i < 8; i++) {" 12261 " pixels[5] = NaN;" 12262 "}" 12263 "pixels[5];"); 12264 CHECK_EQ(0, result->Int32Value()); 12265 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 12266 12267 result = CompileRun("for (var i = 0; i < 8; i++) {" 12268 " pixels[8] = Infinity;" 12269 "}" 12270 "pixels[8];"); 12271 CHECK_EQ(255, result->Int32Value()); 12272 CHECK_EQ(255, 12273 i::Smi::cast(jsobj->GetElement(8)->ToObjectChecked())->value()); 12274 12275 result = CompileRun("for (var i = 0; i < 8; i++) {" 12276 " pixels[9] = -Infinity;" 12277 "}" 12278 "pixels[9];"); 12279 CHECK_EQ(0, result->Int32Value()); 12280 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9)->ToObjectChecked())->value()); 12281 12282 result = CompileRun("pixels[3] = 33;" 12283 "delete pixels[3];" 12284 "pixels[3];"); 12285 CHECK_EQ(33, result->Int32Value()); 12286 12287 result = CompileRun("pixels[0] = 10; pixels[1] = 11;" 12288 "pixels[2] = 12; pixels[3] = 13;" 12289 "pixels.__defineGetter__('2'," 12290 "function() { return 120; });" 12291 "pixels[2];"); 12292 CHECK_EQ(12, result->Int32Value()); 12293 12294 result = CompileRun("var js_array = new Array(40);" 12295 "js_array[0] = 77;" 12296 "js_array;"); 12297 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 12298 12299 result = CompileRun("pixels[1] = 23;" 12300 "pixels.__proto__ = [];" 12301 "js_array.__proto__ = pixels;" 12302 "js_array.concat(pixels);"); 12303 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 12304 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 12305 12306 result = CompileRun("pixels[1] = 23;"); 12307 CHECK_EQ(23, result->Int32Value()); 12308 12309 // Test for index greater than 255. Regression test for: 12310 // http://code.google.com/p/chromium/issues/detail?id=26337. 12311 result = CompileRun("pixels[256] = 255;"); 12312 CHECK_EQ(255, result->Int32Value()); 12313 result = CompileRun("var i = 0;" 12314 "for (var j = 0; j < 8; j++) { i = pixels[256]; }" 12315 "i"); 12316 CHECK_EQ(255, result->Int32Value()); 12317 12318 // Make sure that pixel array ICs recognize when a non-pixel array 12319 // is passed to it. 12320 result = CompileRun("function pa_load(p) {" 12321 " var sum = 0;" 12322 " for (var j = 0; j < 256; j++) { sum += p[j]; }" 12323 " return sum;" 12324 "}" 12325 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" 12326 "for (var i = 0; i < 10; ++i) { pa_load(pixels); }" 12327 "just_ints = new Object();" 12328 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" 12329 "for (var i = 0; i < 10; ++i) {" 12330 " result = pa_load(just_ints);" 12331 "}" 12332 "result"); 12333 CHECK_EQ(32640, result->Int32Value()); 12334 12335 // Make sure that pixel array ICs recognize out-of-bound accesses. 12336 result = CompileRun("function pa_load(p, start) {" 12337 " var sum = 0;" 12338 " for (var j = start; j < 256; j++) { sum += p[j]; }" 12339 " return sum;" 12340 "}" 12341 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" 12342 "for (var i = 0; i < 10; ++i) { pa_load(pixels,0); }" 12343 "for (var i = 0; i < 10; ++i) {" 12344 " result = pa_load(pixels,-10);" 12345 "}" 12346 "result"); 12347 CHECK_EQ(0, result->Int32Value()); 12348 12349 // Make sure that generic ICs properly handles a pixel array. 12350 result = CompileRun("function pa_load(p) {" 12351 " var sum = 0;" 12352 " for (var j = 0; j < 256; j++) { sum += p[j]; }" 12353 " return sum;" 12354 "}" 12355 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" 12356 "just_ints = new Object();" 12357 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" 12358 "for (var i = 0; i < 10; ++i) { pa_load(just_ints); }" 12359 "for (var i = 0; i < 10; ++i) {" 12360 " result = pa_load(pixels);" 12361 "}" 12362 "result"); 12363 CHECK_EQ(32640, result->Int32Value()); 12364 12365 // Make sure that generic load ICs recognize out-of-bound accesses in 12366 // pixel arrays. 12367 result = CompileRun("function pa_load(p, start) {" 12368 " var sum = 0;" 12369 " for (var j = start; j < 256; j++) { sum += p[j]; }" 12370 " return sum;" 12371 "}" 12372 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" 12373 "just_ints = new Object();" 12374 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" 12375 "for (var i = 0; i < 10; ++i) { pa_load(just_ints,0); }" 12376 "for (var i = 0; i < 10; ++i) { pa_load(pixels,0); }" 12377 "for (var i = 0; i < 10; ++i) {" 12378 " result = pa_load(pixels,-10);" 12379 "}" 12380 "result"); 12381 CHECK_EQ(0, result->Int32Value()); 12382 12383 // Make sure that generic ICs properly handles other types than pixel 12384 // arrays (that the inlined fast pixel array test leaves the right information 12385 // in the right registers). 12386 result = CompileRun("function pa_load(p) {" 12387 " var sum = 0;" 12388 " for (var j = 0; j < 256; j++) { sum += p[j]; }" 12389 " return sum;" 12390 "}" 12391 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" 12392 "just_ints = new Object();" 12393 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" 12394 "for (var i = 0; i < 10; ++i) { pa_load(just_ints); }" 12395 "for (var i = 0; i < 10; ++i) { pa_load(pixels); }" 12396 "sparse_array = new Object();" 12397 "for (var i = 0; i < 256; ++i) { sparse_array[i] = i; }" 12398 "sparse_array[1000000] = 3;" 12399 "for (var i = 0; i < 10; ++i) {" 12400 " result = pa_load(sparse_array);" 12401 "}" 12402 "result"); 12403 CHECK_EQ(32640, result->Int32Value()); 12404 12405 // Make sure that pixel array store ICs clamp values correctly. 12406 result = CompileRun("function pa_store(p) {" 12407 " for (var j = 0; j < 256; j++) { p[j] = j * 2; }" 12408 "}" 12409 "pa_store(pixels);" 12410 "var sum = 0;" 12411 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }" 12412 "sum"); 12413 CHECK_EQ(48896, result->Int32Value()); 12414 12415 // Make sure that pixel array stores correctly handle accesses outside 12416 // of the pixel array.. 12417 result = CompileRun("function pa_store(p,start) {" 12418 " for (var j = 0; j < 256; j++) {" 12419 " p[j+start] = j * 2;" 12420 " }" 12421 "}" 12422 "pa_store(pixels,0);" 12423 "pa_store(pixels,-128);" 12424 "var sum = 0;" 12425 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }" 12426 "sum"); 12427 CHECK_EQ(65280, result->Int32Value()); 12428 12429 // Make sure that the generic store stub correctly handle accesses outside 12430 // of the pixel array.. 12431 result = CompileRun("function pa_store(p,start) {" 12432 " for (var j = 0; j < 256; j++) {" 12433 " p[j+start] = j * 2;" 12434 " }" 12435 "}" 12436 "pa_store(pixels,0);" 12437 "just_ints = new Object();" 12438 "for (var i = 0; i < 256; ++i) { just_ints[i] = i; }" 12439 "pa_store(just_ints, 0);" 12440 "pa_store(pixels,-128);" 12441 "var sum = 0;" 12442 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }" 12443 "sum"); 12444 CHECK_EQ(65280, result->Int32Value()); 12445 12446 // Make sure that the generic keyed store stub clamps pixel array values 12447 // correctly. 12448 result = CompileRun("function pa_store(p) {" 12449 " for (var j = 0; j < 256; j++) { p[j] = j * 2; }" 12450 "}" 12451 "pa_store(pixels);" 12452 "just_ints = new Object();" 12453 "pa_store(just_ints);" 12454 "pa_store(pixels);" 12455 "var sum = 0;" 12456 "for (var j = 0; j < 256; j++) { sum += pixels[j]; }" 12457 "sum"); 12458 CHECK_EQ(48896, result->Int32Value()); 12459 12460 // Make sure that pixel array loads are optimized by crankshaft. 12461 result = CompileRun("function pa_load(p) {" 12462 " var sum = 0;" 12463 " for (var i=0; i<256; ++i) {" 12464 " sum += p[i];" 12465 " }" 12466 " return sum; " 12467 "}" 12468 "for (var i = 0; i < 256; ++i) { pixels[i] = i; }" 12469 "for (var i = 0; i < 5000; ++i) {" 12470 " result = pa_load(pixels);" 12471 "}" 12472 "result"); 12473 CHECK_EQ(32640, result->Int32Value()); 12474 12475 // Make sure that pixel array stores are optimized by crankshaft. 12476 result = CompileRun("function pa_init(p) {" 12477 "for (var i = 0; i < 256; ++i) { p[i] = i; }" 12478 "}" 12479 "function pa_load(p) {" 12480 " var sum = 0;" 12481 " for (var i=0; i<256; ++i) {" 12482 " sum += p[i];" 12483 " }" 12484 " return sum; " 12485 "}" 12486 "for (var i = 0; i < 5000; ++i) {" 12487 " pa_init(pixels);" 12488 "}" 12489 "result = pa_load(pixels);" 12490 "result"); 12491 CHECK_EQ(32640, result->Int32Value()); 12492 12493 free(pixel_data); 12494} 12495 12496 12497THREADED_TEST(PixelArrayInfo) { 12498 v8::HandleScope scope; 12499 LocalContext context; 12500 for (int size = 0; size < 100; size += 10) { 12501 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size)); 12502 v8::Handle<v8::Object> obj = v8::Object::New(); 12503 obj->SetIndexedPropertiesToPixelData(pixel_data, size); 12504 CHECK(obj->HasIndexedPropertiesInPixelData()); 12505 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData()); 12506 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength()); 12507 free(pixel_data); 12508 } 12509} 12510 12511 12512static v8::Handle<Value> NotHandledIndexedPropertyGetter( 12513 uint32_t index, 12514 const AccessorInfo& info) { 12515 ApiTestFuzzer::Fuzz(); 12516 return v8::Handle<Value>(); 12517} 12518 12519 12520static v8::Handle<Value> NotHandledIndexedPropertySetter( 12521 uint32_t index, 12522 Local<Value> value, 12523 const AccessorInfo& info) { 12524 ApiTestFuzzer::Fuzz(); 12525 return v8::Handle<Value>(); 12526} 12527 12528 12529THREADED_TEST(PixelArrayWithInterceptor) { 12530 v8::HandleScope scope; 12531 LocalContext context; 12532 const int kElementCount = 260; 12533 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); 12534 i::Handle<i::ExternalPixelArray> pixels = 12535 i::Handle<i::ExternalPixelArray>::cast( 12536 FACTORY->NewExternalArray(kElementCount, 12537 v8::kExternalPixelArray, 12538 pixel_data)); 12539 for (int i = 0; i < kElementCount; i++) { 12540 pixels->set(i, i % 256); 12541 } 12542 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 12543 templ->SetIndexedPropertyHandler(NotHandledIndexedPropertyGetter, 12544 NotHandledIndexedPropertySetter); 12545 v8::Handle<v8::Object> obj = templ->NewInstance(); 12546 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount); 12547 context->Global()->Set(v8_str("pixels"), obj); 12548 v8::Handle<v8::Value> result = CompileRun("pixels[1]"); 12549 CHECK_EQ(1, result->Int32Value()); 12550 result = CompileRun("var sum = 0;" 12551 "for (var i = 0; i < 8; i++) {" 12552 " sum += pixels[i] = pixels[i] = -i;" 12553 "}" 12554 "sum;"); 12555 CHECK_EQ(-28, result->Int32Value()); 12556 result = CompileRun("pixels.hasOwnProperty('1')"); 12557 CHECK(result->BooleanValue()); 12558 free(pixel_data); 12559} 12560 12561 12562static int ExternalArrayElementSize(v8::ExternalArrayType array_type) { 12563 switch (array_type) { 12564 case v8::kExternalByteArray: 12565 case v8::kExternalUnsignedByteArray: 12566 case v8::kExternalPixelArray: 12567 return 1; 12568 break; 12569 case v8::kExternalShortArray: 12570 case v8::kExternalUnsignedShortArray: 12571 return 2; 12572 break; 12573 case v8::kExternalIntArray: 12574 case v8::kExternalUnsignedIntArray: 12575 case v8::kExternalFloatArray: 12576 return 4; 12577 break; 12578 case v8::kExternalDoubleArray: 12579 return 8; 12580 break; 12581 default: 12582 UNREACHABLE(); 12583 return -1; 12584 } 12585 UNREACHABLE(); 12586 return -1; 12587} 12588 12589 12590template <class ExternalArrayClass, class ElementType> 12591static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, 12592 int64_t low, 12593 int64_t high) { 12594 v8::HandleScope scope; 12595 LocalContext context; 12596 const int kElementCount = 40; 12597 int element_size = ExternalArrayElementSize(array_type); 12598 ElementType* array_data = 12599 static_cast<ElementType*>(malloc(kElementCount * element_size)); 12600 i::Handle<ExternalArrayClass> array = 12601 i::Handle<ExternalArrayClass>::cast( 12602 FACTORY->NewExternalArray(kElementCount, array_type, array_data)); 12603 // Force GC to trigger verification. 12604 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 12605 for (int i = 0; i < kElementCount; i++) { 12606 array->set(i, static_cast<ElementType>(i)); 12607 } 12608 // Force GC to trigger verification. 12609 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 12610 for (int i = 0; i < kElementCount; i++) { 12611 CHECK_EQ(static_cast<int64_t>(i), 12612 static_cast<int64_t>(array->get_scalar(i))); 12613 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i])); 12614 } 12615 12616 v8::Handle<v8::Object> obj = v8::Object::New(); 12617 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 12618 // Set the elements to be the external array. 12619 obj->SetIndexedPropertiesToExternalArrayData(array_data, 12620 array_type, 12621 kElementCount); 12622 CHECK_EQ( 12623 1, static_cast<int>(jsobj->GetElement(1)->ToObjectChecked()->Number())); 12624 obj->Set(v8_str("field"), v8::Int32::New(1503)); 12625 context->Global()->Set(v8_str("ext_array"), obj); 12626 v8::Handle<v8::Value> result = CompileRun("ext_array.field"); 12627 CHECK_EQ(1503, result->Int32Value()); 12628 result = CompileRun("ext_array[1]"); 12629 CHECK_EQ(1, result->Int32Value()); 12630 12631 // Check pass through of assigned smis 12632 result = CompileRun("var sum = 0;" 12633 "for (var i = 0; i < 8; i++) {" 12634 " sum += ext_array[i] = ext_array[i] = -i;" 12635 "}" 12636 "sum;"); 12637 CHECK_EQ(-28, result->Int32Value()); 12638 12639 // Check assigned smis 12640 result = CompileRun("for (var i = 0; i < 8; i++) {" 12641 " ext_array[i] = i;" 12642 "}" 12643 "var sum = 0;" 12644 "for (var i = 0; i < 8; i++) {" 12645 " sum += ext_array[i];" 12646 "}" 12647 "sum;"); 12648 CHECK_EQ(28, result->Int32Value()); 12649 12650 // Check assigned smis in reverse order 12651 result = CompileRun("for (var i = 8; --i >= 0; ) {" 12652 " ext_array[i] = i;" 12653 "}" 12654 "var sum = 0;" 12655 "for (var i = 0; i < 8; i++) {" 12656 " sum += ext_array[i];" 12657 "}" 12658 "sum;"); 12659 CHECK_EQ(28, result->Int32Value()); 12660 12661 // Check pass through of assigned HeapNumbers 12662 result = CompileRun("var sum = 0;" 12663 "for (var i = 0; i < 16; i+=2) {" 12664 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);" 12665 "}" 12666 "sum;"); 12667 CHECK_EQ(-28, result->Int32Value()); 12668 12669 // Check assigned HeapNumbers 12670 result = CompileRun("for (var i = 0; i < 16; i+=2) {" 12671 " ext_array[i] = (i * 0.5);" 12672 "}" 12673 "var sum = 0;" 12674 "for (var i = 0; i < 16; i+=2) {" 12675 " sum += ext_array[i];" 12676 "}" 12677 "sum;"); 12678 CHECK_EQ(28, result->Int32Value()); 12679 12680 // Check assigned HeapNumbers in reverse order 12681 result = CompileRun("for (var i = 14; i >= 0; i-=2) {" 12682 " ext_array[i] = (i * 0.5);" 12683 "}" 12684 "var sum = 0;" 12685 "for (var i = 0; i < 16; i+=2) {" 12686 " sum += ext_array[i];" 12687 "}" 12688 "sum;"); 12689 CHECK_EQ(28, result->Int32Value()); 12690 12691 i::ScopedVector<char> test_buf(1024); 12692 12693 // Check legal boundary conditions. 12694 // The repeated loads and stores ensure the ICs are exercised. 12695 const char* boundary_program = 12696 "var res = 0;" 12697 "for (var i = 0; i < 16; i++) {" 12698 " ext_array[i] = %lld;" 12699 " if (i > 8) {" 12700 " res = ext_array[i];" 12701 " }" 12702 "}" 12703 "res;"; 12704 i::OS::SNPrintF(test_buf, 12705 boundary_program, 12706 low); 12707 result = CompileRun(test_buf.start()); 12708 CHECK_EQ(low, result->IntegerValue()); 12709 12710 i::OS::SNPrintF(test_buf, 12711 boundary_program, 12712 high); 12713 result = CompileRun(test_buf.start()); 12714 CHECK_EQ(high, result->IntegerValue()); 12715 12716 // Check misprediction of type in IC. 12717 result = CompileRun("var tmp_array = ext_array;" 12718 "var sum = 0;" 12719 "for (var i = 0; i < 8; i++) {" 12720 " tmp_array[i] = i;" 12721 " sum += tmp_array[i];" 12722 " if (i == 4) {" 12723 " tmp_array = {};" 12724 " }" 12725 "}" 12726 "sum;"); 12727 // Force GC to trigger verification. 12728 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 12729 CHECK_EQ(28, result->Int32Value()); 12730 12731 // Make sure out-of-range loads do not throw. 12732 i::OS::SNPrintF(test_buf, 12733 "var caught_exception = false;" 12734 "try {" 12735 " ext_array[%d];" 12736 "} catch (e) {" 12737 " caught_exception = true;" 12738 "}" 12739 "caught_exception;", 12740 kElementCount); 12741 result = CompileRun(test_buf.start()); 12742 CHECK_EQ(false, result->BooleanValue()); 12743 12744 // Make sure out-of-range stores do not throw. 12745 i::OS::SNPrintF(test_buf, 12746 "var caught_exception = false;" 12747 "try {" 12748 " ext_array[%d] = 1;" 12749 "} catch (e) {" 12750 " caught_exception = true;" 12751 "}" 12752 "caught_exception;", 12753 kElementCount); 12754 result = CompileRun(test_buf.start()); 12755 CHECK_EQ(false, result->BooleanValue()); 12756 12757 // Check other boundary conditions, values and operations. 12758 result = CompileRun("for (var i = 0; i < 8; i++) {" 12759 " ext_array[7] = undefined;" 12760 "}" 12761 "ext_array[7];"); 12762 CHECK_EQ(0, result->Int32Value()); 12763 CHECK_EQ( 12764 0, static_cast<int>(jsobj->GetElement(7)->ToObjectChecked()->Number())); 12765 12766 result = CompileRun("for (var i = 0; i < 8; i++) {" 12767 " ext_array[6] = '2.3';" 12768 "}" 12769 "ext_array[6];"); 12770 CHECK_EQ(2, result->Int32Value()); 12771 CHECK_EQ( 12772 2, static_cast<int>(jsobj->GetElement(6)->ToObjectChecked()->Number())); 12773 12774 if (array_type != v8::kExternalFloatArray && 12775 array_type != v8::kExternalDoubleArray) { 12776 // Though the specification doesn't state it, be explicit about 12777 // converting NaNs and +/-Infinity to zero. 12778 result = CompileRun("for (var i = 0; i < 8; i++) {" 12779 " ext_array[i] = 5;" 12780 "}" 12781 "for (var i = 0; i < 8; i++) {" 12782 " ext_array[i] = NaN;" 12783 "}" 12784 "ext_array[5];"); 12785 CHECK_EQ(0, result->Int32Value()); 12786 CHECK_EQ(0, 12787 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 12788 12789 result = CompileRun("for (var i = 0; i < 8; i++) {" 12790 " ext_array[i] = 5;" 12791 "}" 12792 "for (var i = 0; i < 8; i++) {" 12793 " ext_array[i] = Infinity;" 12794 "}" 12795 "ext_array[5];"); 12796 int expected_value = 12797 (array_type == v8::kExternalPixelArray) ? 255 : 0; 12798 CHECK_EQ(expected_value, result->Int32Value()); 12799 CHECK_EQ(expected_value, 12800 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 12801 12802 result = CompileRun("for (var i = 0; i < 8; i++) {" 12803 " ext_array[i] = 5;" 12804 "}" 12805 "for (var i = 0; i < 8; i++) {" 12806 " ext_array[i] = -Infinity;" 12807 "}" 12808 "ext_array[5];"); 12809 CHECK_EQ(0, result->Int32Value()); 12810 CHECK_EQ(0, 12811 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 12812 12813 // Check truncation behavior of integral arrays. 12814 const char* unsigned_data = 12815 "var source_data = [0.6, 10.6];" 12816 "var expected_results = [0, 10];"; 12817 const char* signed_data = 12818 "var source_data = [0.6, 10.6, -0.6, -10.6];" 12819 "var expected_results = [0, 10, 0, -10];"; 12820 const char* pixel_data = 12821 "var source_data = [0.6, 10.6];" 12822 "var expected_results = [1, 11];"; 12823 bool is_unsigned = 12824 (array_type == v8::kExternalUnsignedByteArray || 12825 array_type == v8::kExternalUnsignedShortArray || 12826 array_type == v8::kExternalUnsignedIntArray); 12827 bool is_pixel_data = array_type == v8::kExternalPixelArray; 12828 12829 i::OS::SNPrintF(test_buf, 12830 "%s" 12831 "var all_passed = true;" 12832 "for (var i = 0; i < source_data.length; i++) {" 12833 " for (var j = 0; j < 8; j++) {" 12834 " ext_array[j] = source_data[i];" 12835 " }" 12836 " all_passed = all_passed &&" 12837 " (ext_array[5] == expected_results[i]);" 12838 "}" 12839 "all_passed;", 12840 (is_unsigned ? 12841 unsigned_data : 12842 (is_pixel_data ? pixel_data : signed_data))); 12843 result = CompileRun(test_buf.start()); 12844 CHECK_EQ(true, result->BooleanValue()); 12845 } 12846 12847 for (int i = 0; i < kElementCount; i++) { 12848 array->set(i, static_cast<ElementType>(i)); 12849 } 12850 // Test complex assignments 12851 result = CompileRun("function ee_op_test_complex_func(sum) {" 12852 " for (var i = 0; i < 40; ++i) {" 12853 " sum += (ext_array[i] += 1);" 12854 " sum += (ext_array[i] -= 1);" 12855 " } " 12856 " return sum;" 12857 "}" 12858 "sum=0;" 12859 "for (var i=0;i<10000;++i) {" 12860 " sum=ee_op_test_complex_func(sum);" 12861 "}" 12862 "sum;"); 12863 CHECK_EQ(16000000, result->Int32Value()); 12864 12865 // Test count operations 12866 result = CompileRun("function ee_op_test_count_func(sum) {" 12867 " for (var i = 0; i < 40; ++i) {" 12868 " sum += (++ext_array[i]);" 12869 " sum += (--ext_array[i]);" 12870 " } " 12871 " return sum;" 12872 "}" 12873 "sum=0;" 12874 "for (var i=0;i<10000;++i) {" 12875 " sum=ee_op_test_count_func(sum);" 12876 "}" 12877 "sum;"); 12878 CHECK_EQ(16000000, result->Int32Value()); 12879 12880 result = CompileRun("ext_array[3] = 33;" 12881 "delete ext_array[3];" 12882 "ext_array[3];"); 12883 CHECK_EQ(33, result->Int32Value()); 12884 12885 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;" 12886 "ext_array[2] = 12; ext_array[3] = 13;" 12887 "ext_array.__defineGetter__('2'," 12888 "function() { return 120; });" 12889 "ext_array[2];"); 12890 CHECK_EQ(12, result->Int32Value()); 12891 12892 result = CompileRun("var js_array = new Array(40);" 12893 "js_array[0] = 77;" 12894 "js_array;"); 12895 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 12896 12897 result = CompileRun("ext_array[1] = 23;" 12898 "ext_array.__proto__ = [];" 12899 "js_array.__proto__ = ext_array;" 12900 "js_array.concat(ext_array);"); 12901 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 12902 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 12903 12904 result = CompileRun("ext_array[1] = 23;"); 12905 CHECK_EQ(23, result->Int32Value()); 12906 12907 // Test more complex manipulations which cause eax to contain values 12908 // that won't be completely overwritten by loads from the arrays. 12909 // This catches bugs in the instructions used for the KeyedLoadIC 12910 // for byte and word types. 12911 { 12912 const int kXSize = 300; 12913 const int kYSize = 300; 12914 const int kLargeElementCount = kXSize * kYSize * 4; 12915 ElementType* large_array_data = 12916 static_cast<ElementType*>(malloc(kLargeElementCount * element_size)); 12917 i::Handle<ExternalArrayClass> large_array( 12918 i::Handle<ExternalArrayClass>::cast( 12919 FACTORY->NewExternalArray(kLargeElementCount, 12920 array_type, 12921 array_data))); 12922 v8::Handle<v8::Object> large_obj = v8::Object::New(); 12923 // Set the elements to be the external array. 12924 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data, 12925 array_type, 12926 kLargeElementCount); 12927 context->Global()->Set(v8_str("large_array"), large_obj); 12928 // Initialize contents of a few rows. 12929 for (int x = 0; x < 300; x++) { 12930 int row = 0; 12931 int offset = row * 300 * 4; 12932 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 12933 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 12934 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 12935 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 12936 row = 150; 12937 offset = row * 300 * 4; 12938 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 12939 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 12940 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 12941 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 12942 row = 298; 12943 offset = row * 300 * 4; 12944 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 12945 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 12946 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 12947 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 12948 } 12949 // The goal of the code below is to make "offset" large enough 12950 // that the computation of the index (which goes into eax) has 12951 // high bits set which will not be overwritten by a byte or short 12952 // load. 12953 result = CompileRun("var failed = false;" 12954 "var offset = 0;" 12955 "for (var i = 0; i < 300; i++) {" 12956 " if (large_array[4 * i] != 127 ||" 12957 " large_array[4 * i + 1] != 0 ||" 12958 " large_array[4 * i + 2] != 0 ||" 12959 " large_array[4 * i + 3] != 127) {" 12960 " failed = true;" 12961 " }" 12962 "}" 12963 "offset = 150 * 300 * 4;" 12964 "for (var i = 0; i < 300; i++) {" 12965 " if (large_array[offset + 4 * i] != 127 ||" 12966 " large_array[offset + 4 * i + 1] != 0 ||" 12967 " large_array[offset + 4 * i + 2] != 0 ||" 12968 " large_array[offset + 4 * i + 3] != 127) {" 12969 " failed = true;" 12970 " }" 12971 "}" 12972 "offset = 298 * 300 * 4;" 12973 "for (var i = 0; i < 300; i++) {" 12974 " if (large_array[offset + 4 * i] != 127 ||" 12975 " large_array[offset + 4 * i + 1] != 0 ||" 12976 " large_array[offset + 4 * i + 2] != 0 ||" 12977 " large_array[offset + 4 * i + 3] != 127) {" 12978 " failed = true;" 12979 " }" 12980 "}" 12981 "!failed;"); 12982 CHECK_EQ(true, result->BooleanValue()); 12983 free(large_array_data); 12984 } 12985 12986 // The "" property descriptor is overloaded to store information about 12987 // the external array. Ensure that setting and accessing the "" property 12988 // works (it should overwrite the information cached about the external 12989 // array in the DescriptorArray) in various situations. 12990 result = CompileRun("ext_array[''] = 23; ext_array['']"); 12991 CHECK_EQ(23, result->Int32Value()); 12992 12993 // Property "" set after the external array is associated with the object. 12994 { 12995 v8::Handle<v8::Object> obj2 = v8::Object::New(); 12996 obj2->Set(v8_str("ee_test_field"), v8::Int32::New(256)); 12997 obj2->Set(v8_str(""), v8::Int32::New(1503)); 12998 // Set the elements to be the external array. 12999 obj2->SetIndexedPropertiesToExternalArrayData(array_data, 13000 array_type, 13001 kElementCount); 13002 context->Global()->Set(v8_str("ext_array"), obj2); 13003 result = CompileRun("ext_array['']"); 13004 CHECK_EQ(1503, result->Int32Value()); 13005 } 13006 13007 // Property "" set after the external array is associated with the object. 13008 { 13009 v8::Handle<v8::Object> obj2 = v8::Object::New(); 13010 obj2->Set(v8_str("ee_test_field_2"), v8::Int32::New(256)); 13011 // Set the elements to be the external array. 13012 obj2->SetIndexedPropertiesToExternalArrayData(array_data, 13013 array_type, 13014 kElementCount); 13015 obj2->Set(v8_str(""), v8::Int32::New(1503)); 13016 context->Global()->Set(v8_str("ext_array"), obj2); 13017 result = CompileRun("ext_array['']"); 13018 CHECK_EQ(1503, result->Int32Value()); 13019 } 13020 13021 // Should reuse the map from previous test. 13022 { 13023 v8::Handle<v8::Object> obj2 = v8::Object::New(); 13024 obj2->Set(v8_str("ee_test_field_2"), v8::Int32::New(256)); 13025 // Set the elements to be the external array. Should re-use the map 13026 // from previous test. 13027 obj2->SetIndexedPropertiesToExternalArrayData(array_data, 13028 array_type, 13029 kElementCount); 13030 context->Global()->Set(v8_str("ext_array"), obj2); 13031 result = CompileRun("ext_array['']"); 13032 } 13033 13034 // Property "" is a constant function that shouldn't not be interfered with 13035 // when an external array is set. 13036 { 13037 v8::Handle<v8::Object> obj2 = v8::Object::New(); 13038 // Start 13039 obj2->Set(v8_str("ee_test_field3"), v8::Int32::New(256)); 13040 13041 // Add a constant function to an object. 13042 context->Global()->Set(v8_str("ext_array"), obj2); 13043 result = CompileRun("ext_array[''] = function() {return 1503;};" 13044 "ext_array['']();"); 13045 13046 // Add an external array transition to the same map that 13047 // has the constant transition. 13048 v8::Handle<v8::Object> obj3 = v8::Object::New(); 13049 obj3->Set(v8_str("ee_test_field3"), v8::Int32::New(256)); 13050 obj3->SetIndexedPropertiesToExternalArrayData(array_data, 13051 array_type, 13052 kElementCount); 13053 context->Global()->Set(v8_str("ext_array"), obj3); 13054 } 13055 13056 // If a external array transition is in the map, it should get clobbered 13057 // by a constant function. 13058 { 13059 // Add an external array transition. 13060 v8::Handle<v8::Object> obj3 = v8::Object::New(); 13061 obj3->Set(v8_str("ee_test_field4"), v8::Int32::New(256)); 13062 obj3->SetIndexedPropertiesToExternalArrayData(array_data, 13063 array_type, 13064 kElementCount); 13065 13066 // Add a constant function to the same map that just got an external array 13067 // transition. 13068 v8::Handle<v8::Object> obj2 = v8::Object::New(); 13069 obj2->Set(v8_str("ee_test_field4"), v8::Int32::New(256)); 13070 context->Global()->Set(v8_str("ext_array"), obj2); 13071 result = CompileRun("ext_array[''] = function() {return 1503;};" 13072 "ext_array['']();"); 13073 } 13074 13075 free(array_data); 13076} 13077 13078 13079THREADED_TEST(ExternalByteArray) { 13080 ExternalArrayTestHelper<i::ExternalByteArray, int8_t>( 13081 v8::kExternalByteArray, 13082 -128, 13083 127); 13084} 13085 13086 13087THREADED_TEST(ExternalUnsignedByteArray) { 13088 ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>( 13089 v8::kExternalUnsignedByteArray, 13090 0, 13091 255); 13092} 13093 13094 13095THREADED_TEST(ExternalPixelArray) { 13096 ExternalArrayTestHelper<i::ExternalPixelArray, uint8_t>( 13097 v8::kExternalPixelArray, 13098 0, 13099 255); 13100} 13101 13102 13103THREADED_TEST(ExternalShortArray) { 13104 ExternalArrayTestHelper<i::ExternalShortArray, int16_t>( 13105 v8::kExternalShortArray, 13106 -32768, 13107 32767); 13108} 13109 13110 13111THREADED_TEST(ExternalUnsignedShortArray) { 13112 ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>( 13113 v8::kExternalUnsignedShortArray, 13114 0, 13115 65535); 13116} 13117 13118 13119THREADED_TEST(ExternalIntArray) { 13120 ExternalArrayTestHelper<i::ExternalIntArray, int32_t>( 13121 v8::kExternalIntArray, 13122 INT_MIN, // -2147483648 13123 INT_MAX); // 2147483647 13124} 13125 13126 13127THREADED_TEST(ExternalUnsignedIntArray) { 13128 ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>( 13129 v8::kExternalUnsignedIntArray, 13130 0, 13131 UINT_MAX); // 4294967295 13132} 13133 13134 13135THREADED_TEST(ExternalFloatArray) { 13136 ExternalArrayTestHelper<i::ExternalFloatArray, float>( 13137 v8::kExternalFloatArray, 13138 -500, 13139 500); 13140} 13141 13142 13143THREADED_TEST(ExternalDoubleArray) { 13144 ExternalArrayTestHelper<i::ExternalDoubleArray, double>( 13145 v8::kExternalDoubleArray, 13146 -500, 13147 500); 13148} 13149 13150 13151THREADED_TEST(ExternalArrays) { 13152 TestExternalByteArray(); 13153 TestExternalUnsignedByteArray(); 13154 TestExternalShortArray(); 13155 TestExternalUnsignedShortArray(); 13156 TestExternalIntArray(); 13157 TestExternalUnsignedIntArray(); 13158 TestExternalFloatArray(); 13159} 13160 13161 13162void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) { 13163 v8::HandleScope scope; 13164 LocalContext context; 13165 for (int size = 0; size < 100; size += 10) { 13166 int element_size = ExternalArrayElementSize(array_type); 13167 void* external_data = malloc(size * element_size); 13168 v8::Handle<v8::Object> obj = v8::Object::New(); 13169 obj->SetIndexedPropertiesToExternalArrayData( 13170 external_data, array_type, size); 13171 CHECK(obj->HasIndexedPropertiesInExternalArrayData()); 13172 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData()); 13173 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType()); 13174 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength()); 13175 free(external_data); 13176 } 13177} 13178 13179 13180THREADED_TEST(ExternalArrayInfo) { 13181 ExternalArrayInfoTestHelper(v8::kExternalByteArray); 13182 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray); 13183 ExternalArrayInfoTestHelper(v8::kExternalShortArray); 13184 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray); 13185 ExternalArrayInfoTestHelper(v8::kExternalIntArray); 13186 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray); 13187 ExternalArrayInfoTestHelper(v8::kExternalFloatArray); 13188 ExternalArrayInfoTestHelper(v8::kExternalDoubleArray); 13189 ExternalArrayInfoTestHelper(v8::kExternalPixelArray); 13190} 13191 13192 13193THREADED_TEST(ScriptContextDependence) { 13194 v8::HandleScope scope; 13195 LocalContext c1; 13196 const char *source = "foo"; 13197 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source)); 13198 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source)); 13199 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100)); 13200 CHECK_EQ(dep->Run()->Int32Value(), 100); 13201 CHECK_EQ(indep->Run()->Int32Value(), 100); 13202 LocalContext c2; 13203 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101)); 13204 CHECK_EQ(dep->Run()->Int32Value(), 100); 13205 CHECK_EQ(indep->Run()->Int32Value(), 101); 13206} 13207 13208 13209THREADED_TEST(StackTrace) { 13210 v8::HandleScope scope; 13211 LocalContext context; 13212 v8::TryCatch try_catch; 13213 const char *source = "function foo() { FAIL.FAIL; }; foo();"; 13214 v8::Handle<v8::String> src = v8::String::New(source); 13215 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test"); 13216 v8::Script::New(src, origin)->Run(); 13217 CHECK(try_catch.HasCaught()); 13218 v8::String::Utf8Value stack(try_catch.StackTrace()); 13219 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL); 13220} 13221 13222 13223// Checks that a StackFrame has certain expected values. 13224void checkStackFrame(const char* expected_script_name, 13225 const char* expected_func_name, int expected_line_number, 13226 int expected_column, bool is_eval, bool is_constructor, 13227 v8::Handle<v8::StackFrame> frame) { 13228 v8::HandleScope scope; 13229 v8::String::Utf8Value func_name(frame->GetFunctionName()); 13230 v8::String::Utf8Value script_name(frame->GetScriptName()); 13231 if (*script_name == NULL) { 13232 // The situation where there is no associated script, like for evals. 13233 CHECK(expected_script_name == NULL); 13234 } else { 13235 CHECK(strstr(*script_name, expected_script_name) != NULL); 13236 } 13237 CHECK(strstr(*func_name, expected_func_name) != NULL); 13238 CHECK_EQ(expected_line_number, frame->GetLineNumber()); 13239 CHECK_EQ(expected_column, frame->GetColumn()); 13240 CHECK_EQ(is_eval, frame->IsEval()); 13241 CHECK_EQ(is_constructor, frame->IsConstructor()); 13242} 13243 13244 13245v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) { 13246 v8::HandleScope scope; 13247 const char* origin = "capture-stack-trace-test"; 13248 const int kOverviewTest = 1; 13249 const int kDetailedTest = 2; 13250 13251 ASSERT(args.Length() == 1); 13252 13253 int testGroup = args[0]->Int32Value(); 13254 if (testGroup == kOverviewTest) { 13255 v8::Handle<v8::StackTrace> stackTrace = 13256 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview); 13257 CHECK_EQ(4, stackTrace->GetFrameCount()); 13258 checkStackFrame(origin, "bar", 2, 10, false, false, 13259 stackTrace->GetFrame(0)); 13260 checkStackFrame(origin, "foo", 6, 3, false, false, 13261 stackTrace->GetFrame(1)); 13262 // This is the source string inside the eval which has the call to foo. 13263 checkStackFrame(NULL, "", 1, 5, false, false, 13264 stackTrace->GetFrame(2)); 13265 // The last frame is an anonymous function which has the initial eval call. 13266 checkStackFrame(origin, "", 8, 7, false, false, 13267 stackTrace->GetFrame(3)); 13268 13269 CHECK(stackTrace->AsArray()->IsArray()); 13270 } else if (testGroup == kDetailedTest) { 13271 v8::Handle<v8::StackTrace> stackTrace = 13272 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); 13273 CHECK_EQ(4, stackTrace->GetFrameCount()); 13274 checkStackFrame(origin, "bat", 4, 22, false, false, 13275 stackTrace->GetFrame(0)); 13276 checkStackFrame(origin, "baz", 8, 3, false, true, 13277 stackTrace->GetFrame(1)); 13278#ifdef ENABLE_DEBUGGER_SUPPORT 13279 bool is_eval = true; 13280#else // ENABLE_DEBUGGER_SUPPORT 13281 bool is_eval = false; 13282#endif // ENABLE_DEBUGGER_SUPPORT 13283 13284 // This is the source string inside the eval which has the call to baz. 13285 checkStackFrame(NULL, "", 1, 5, is_eval, false, 13286 stackTrace->GetFrame(2)); 13287 // The last frame is an anonymous function which has the initial eval call. 13288 checkStackFrame(origin, "", 10, 1, false, false, 13289 stackTrace->GetFrame(3)); 13290 13291 CHECK(stackTrace->AsArray()->IsArray()); 13292 } 13293 return v8::Undefined(); 13294} 13295 13296 13297// Tests the C++ StackTrace API. 13298// TODO(3074796): Reenable this as a THREADED_TEST once it passes. 13299// THREADED_TEST(CaptureStackTrace) { 13300TEST(CaptureStackTrace) { 13301 v8::HandleScope scope; 13302 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test"); 13303 Local<ObjectTemplate> templ = ObjectTemplate::New(); 13304 templ->Set(v8_str("AnalyzeStackInNativeCode"), 13305 v8::FunctionTemplate::New(AnalyzeStackInNativeCode)); 13306 LocalContext context(0, templ); 13307 13308 // Test getting OVERVIEW information. Should ignore information that is not 13309 // script name, function name, line number, and column offset. 13310 const char *overview_source = 13311 "function bar() {\n" 13312 " var y; AnalyzeStackInNativeCode(1);\n" 13313 "}\n" 13314 "function foo() {\n" 13315 "\n" 13316 " bar();\n" 13317 "}\n" 13318 "var x;eval('new foo();');"; 13319 v8::Handle<v8::String> overview_src = v8::String::New(overview_source); 13320 v8::Handle<Value> overview_result( 13321 v8::Script::New(overview_src, origin)->Run()); 13322 ASSERT(!overview_result.IsEmpty()); 13323 ASSERT(overview_result->IsObject()); 13324 13325 // Test getting DETAILED information. 13326 const char *detailed_source = 13327 "function bat() {AnalyzeStackInNativeCode(2);\n" 13328 "}\n" 13329 "\n" 13330 "function baz() {\n" 13331 " bat();\n" 13332 "}\n" 13333 "eval('new baz();');"; 13334 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source); 13335 // Make the script using a non-zero line and column offset. 13336 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3); 13337 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5); 13338 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset); 13339 v8::Handle<v8::Script> detailed_script( 13340 v8::Script::New(detailed_src, &detailed_origin)); 13341 v8::Handle<Value> detailed_result(detailed_script->Run()); 13342 ASSERT(!detailed_result.IsEmpty()); 13343 ASSERT(detailed_result->IsObject()); 13344} 13345 13346 13347static void StackTraceForUncaughtExceptionListener( 13348 v8::Handle<v8::Message> message, 13349 v8::Handle<Value>) { 13350 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace(); 13351 CHECK_EQ(2, stack_trace->GetFrameCount()); 13352 checkStackFrame("origin", "foo", 2, 3, false, false, 13353 stack_trace->GetFrame(0)); 13354 checkStackFrame("origin", "bar", 5, 3, false, false, 13355 stack_trace->GetFrame(1)); 13356} 13357 13358TEST(CaptureStackTraceForUncaughtException) { 13359 report_count = 0; 13360 v8::HandleScope scope; 13361 LocalContext env; 13362 v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener); 13363 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); 13364 13365 Script::Compile(v8_str("function foo() {\n" 13366 " throw 1;\n" 13367 "};\n" 13368 "function bar() {\n" 13369 " foo();\n" 13370 "};"), 13371 v8_str("origin"))->Run(); 13372 v8::Local<v8::Object> global = env->Global(); 13373 Local<Value> trouble = global->Get(v8_str("bar")); 13374 CHECK(trouble->IsFunction()); 13375 Function::Cast(*trouble)->Call(global, 0, NULL); 13376 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); 13377 v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener); 13378} 13379 13380 13381TEST(CaptureStackTraceForUncaughtExceptionAndSetters) { 13382 v8::HandleScope scope; 13383 LocalContext env; 13384 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true, 13385 1024, 13386 v8::StackTrace::kDetailed); 13387 13388 CompileRun( 13389 "var setters = ['column', 'lineNumber', 'scriptName',\n" 13390 " 'scriptNameOrSourceURL', 'functionName', 'isEval',\n" 13391 " 'isConstructor'];\n" 13392 "for (var i = 0; i < setters.length; i++) {\n" 13393 " var prop = setters[i];\n" 13394 " Object.prototype.__defineSetter__(prop, function() { throw prop; });\n" 13395 "}\n"); 13396 CompileRun("throw 'exception';"); 13397 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); 13398} 13399 13400 13401v8::Handle<Value> AnalyzeStackOfEvalWithSourceURL(const v8::Arguments& args) { 13402 v8::HandleScope scope; 13403 v8::Handle<v8::StackTrace> stackTrace = 13404 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); 13405 CHECK_EQ(5, stackTrace->GetFrameCount()); 13406 v8::Handle<v8::String> url = v8_str("eval_url"); 13407 for (int i = 0; i < 3; i++) { 13408 v8::Handle<v8::String> name = 13409 stackTrace->GetFrame(i)->GetScriptNameOrSourceURL(); 13410 CHECK(!name.IsEmpty()); 13411 CHECK_EQ(url, name); 13412 } 13413 return v8::Undefined(); 13414} 13415 13416 13417TEST(SourceURLInStackTrace) { 13418 v8::HandleScope scope; 13419 Local<ObjectTemplate> templ = ObjectTemplate::New(); 13420 templ->Set(v8_str("AnalyzeStackOfEvalWithSourceURL"), 13421 v8::FunctionTemplate::New(AnalyzeStackOfEvalWithSourceURL)); 13422 LocalContext context(0, templ); 13423 13424 const char *source = 13425 "function outer() {\n" 13426 "function bar() {\n" 13427 " AnalyzeStackOfEvalWithSourceURL();\n" 13428 "}\n" 13429 "function foo() {\n" 13430 "\n" 13431 " bar();\n" 13432 "}\n" 13433 "foo();\n" 13434 "}\n" 13435 "eval('(' + outer +')()//@ sourceURL=eval_url');"; 13436 CHECK(CompileRun(source)->IsUndefined()); 13437} 13438 13439 13440// Test that idle notification can be handled and eventually returns true. 13441THREADED_TEST(IdleNotification) { 13442 bool rv = false; 13443 for (int i = 0; i < 100; i++) { 13444 rv = v8::V8::IdleNotification(); 13445 if (rv) 13446 break; 13447 } 13448 CHECK(rv == true); 13449} 13450 13451 13452static uint32_t* stack_limit; 13453 13454static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) { 13455 stack_limit = reinterpret_cast<uint32_t*>( 13456 i::Isolate::Current()->stack_guard()->real_climit()); 13457 return v8::Undefined(); 13458} 13459 13460 13461// Uses the address of a local variable to determine the stack top now. 13462// Given a size, returns an address that is that far from the current 13463// top of stack. 13464static uint32_t* ComputeStackLimit(uint32_t size) { 13465 uint32_t* answer = &size - (size / sizeof(size)); 13466 // If the size is very large and the stack is very near the bottom of 13467 // memory then the calculation above may wrap around and give an address 13468 // that is above the (downwards-growing) stack. In that case we return 13469 // a very low address. 13470 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size)); 13471 return answer; 13472} 13473 13474 13475TEST(SetResourceConstraints) { 13476 static const int K = 1024; 13477 uint32_t* set_limit = ComputeStackLimit(128 * K); 13478 13479 // Set stack limit. 13480 v8::ResourceConstraints constraints; 13481 constraints.set_stack_limit(set_limit); 13482 CHECK(v8::SetResourceConstraints(&constraints)); 13483 13484 // Execute a script. 13485 v8::HandleScope scope; 13486 LocalContext env; 13487 Local<v8::FunctionTemplate> fun_templ = 13488 v8::FunctionTemplate::New(GetStackLimitCallback); 13489 Local<Function> fun = fun_templ->GetFunction(); 13490 env->Global()->Set(v8_str("get_stack_limit"), fun); 13491 CompileRun("get_stack_limit();"); 13492 13493 CHECK(stack_limit == set_limit); 13494} 13495 13496 13497TEST(SetResourceConstraintsInThread) { 13498 uint32_t* set_limit; 13499 { 13500 v8::Locker locker; 13501 static const int K = 1024; 13502 set_limit = ComputeStackLimit(128 * K); 13503 13504 // Set stack limit. 13505 v8::ResourceConstraints constraints; 13506 constraints.set_stack_limit(set_limit); 13507 CHECK(v8::SetResourceConstraints(&constraints)); 13508 13509 // Execute a script. 13510 v8::HandleScope scope; 13511 LocalContext env; 13512 Local<v8::FunctionTemplate> fun_templ = 13513 v8::FunctionTemplate::New(GetStackLimitCallback); 13514 Local<Function> fun = fun_templ->GetFunction(); 13515 env->Global()->Set(v8_str("get_stack_limit"), fun); 13516 CompileRun("get_stack_limit();"); 13517 13518 CHECK(stack_limit == set_limit); 13519 } 13520 { 13521 v8::Locker locker; 13522 CHECK(stack_limit == set_limit); 13523 } 13524} 13525 13526 13527THREADED_TEST(GetHeapStatistics) { 13528 v8::HandleScope scope; 13529 LocalContext c1; 13530 v8::HeapStatistics heap_statistics; 13531 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0); 13532 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0); 13533 v8::V8::GetHeapStatistics(&heap_statistics); 13534 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0); 13535 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0); 13536} 13537 13538 13539static double DoubleFromBits(uint64_t value) { 13540 double target; 13541 memcpy(&target, &value, sizeof(target)); 13542 return target; 13543} 13544 13545 13546static uint64_t DoubleToBits(double value) { 13547 uint64_t target; 13548 memcpy(&target, &value, sizeof(target)); 13549 return target; 13550} 13551 13552 13553static double DoubleToDateTime(double input) { 13554 double date_limit = 864e13; 13555 if (IsNaN(input) || input < -date_limit || input > date_limit) { 13556 return i::OS::nan_value(); 13557 } 13558 return (input < 0) ? -(floor(-input)) : floor(input); 13559} 13560 13561// We don't have a consistent way to write 64-bit constants syntactically, so we 13562// split them into two 32-bit constants and combine them programmatically. 13563static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) { 13564 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits); 13565} 13566 13567 13568THREADED_TEST(QuietSignalingNaNs) { 13569 v8::HandleScope scope; 13570 LocalContext context; 13571 v8::TryCatch try_catch; 13572 13573 // Special double values. 13574 double snan = DoubleFromBits(0x7ff00000, 0x00000001); 13575 double qnan = DoubleFromBits(0x7ff80000, 0x00000000); 13576 double infinity = DoubleFromBits(0x7ff00000, 0x00000000); 13577 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu); 13578 double min_normal = DoubleFromBits(0x00100000, 0x00000000); 13579 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu); 13580 double min_denormal = DoubleFromBits(0x00000000, 0x00000001); 13581 13582 // Date values are capped at +/-100000000 days (times 864e5 ms per day) 13583 // on either side of the epoch. 13584 double date_limit = 864e13; 13585 13586 double test_values[] = { 13587 snan, 13588 qnan, 13589 infinity, 13590 max_normal, 13591 date_limit + 1, 13592 date_limit, 13593 min_normal, 13594 max_denormal, 13595 min_denormal, 13596 0, 13597 -0, 13598 -min_denormal, 13599 -max_denormal, 13600 -min_normal, 13601 -date_limit, 13602 -date_limit - 1, 13603 -max_normal, 13604 -infinity, 13605 -qnan, 13606 -snan 13607 }; 13608 int num_test_values = 20; 13609 13610 for (int i = 0; i < num_test_values; i++) { 13611 double test_value = test_values[i]; 13612 13613 // Check that Number::New preserves non-NaNs and quiets SNaNs. 13614 v8::Handle<v8::Value> number = v8::Number::New(test_value); 13615 double stored_number = number->NumberValue(); 13616 if (!IsNaN(test_value)) { 13617 CHECK_EQ(test_value, stored_number); 13618 } else { 13619 uint64_t stored_bits = DoubleToBits(stored_number); 13620 // Check if quiet nan (bits 51..62 all set). 13621#if defined(V8_TARGET_ARCH_MIPS) && !defined(USE_SIMULATOR) 13622 // Most significant fraction bit for quiet nan is set to 0 13623 // on MIPS architecture. Allowed by IEEE-754. 13624 CHECK_EQ(0xffe, static_cast<int>((stored_bits >> 51) & 0xfff)); 13625#else 13626 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 13627#endif 13628 } 13629 13630 // Check that Date::New preserves non-NaNs in the date range and 13631 // quiets SNaNs. 13632 v8::Handle<v8::Value> date = v8::Date::New(test_value); 13633 double expected_stored_date = DoubleToDateTime(test_value); 13634 double stored_date = date->NumberValue(); 13635 if (!IsNaN(expected_stored_date)) { 13636 CHECK_EQ(expected_stored_date, stored_date); 13637 } else { 13638 uint64_t stored_bits = DoubleToBits(stored_date); 13639 // Check if quiet nan (bits 51..62 all set). 13640#if defined(V8_TARGET_ARCH_MIPS) && !defined(USE_SIMULATOR) 13641 // Most significant fraction bit for quiet nan is set to 0 13642 // on MIPS architecture. Allowed by IEEE-754. 13643 CHECK_EQ(0xffe, static_cast<int>((stored_bits >> 51) & 0xfff)); 13644#else 13645 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 13646#endif 13647 } 13648 } 13649} 13650 13651 13652static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) { 13653 v8::HandleScope scope; 13654 v8::TryCatch tc; 13655 v8::Handle<v8::String> str(args[0]->ToString()); 13656 if (tc.HasCaught()) 13657 return tc.ReThrow(); 13658 return v8::Undefined(); 13659} 13660 13661 13662// Test that an exception can be propagated down through a spaghetti 13663// stack using ReThrow. 13664THREADED_TEST(SpaghettiStackReThrow) { 13665 v8::HandleScope scope; 13666 LocalContext context; 13667 context->Global()->Set( 13668 v8::String::New("s"), 13669 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction()); 13670 v8::TryCatch try_catch; 13671 CompileRun( 13672 "var i = 0;" 13673 "var o = {" 13674 " toString: function () {" 13675 " if (i == 10) {" 13676 " throw 'Hey!';" 13677 " } else {" 13678 " i++;" 13679 " return s(o);" 13680 " }" 13681 " }" 13682 "};" 13683 "s(o);"); 13684 CHECK(try_catch.HasCaught()); 13685 v8::String::Utf8Value value(try_catch.Exception()); 13686 CHECK_EQ(0, strcmp(*value, "Hey!")); 13687} 13688 13689 13690TEST(Regress528) { 13691 v8::V8::Initialize(); 13692 13693 v8::HandleScope scope; 13694 v8::Persistent<Context> context; 13695 v8::Persistent<Context> other_context; 13696 int gc_count; 13697 13698 // Create a context used to keep the code from aging in the compilation 13699 // cache. 13700 other_context = Context::New(); 13701 13702 // Context-dependent context data creates reference from the compilation 13703 // cache to the global object. 13704 const char* source_simple = "1"; 13705 context = Context::New(); 13706 { 13707 v8::HandleScope scope; 13708 13709 context->Enter(); 13710 Local<v8::String> obj = v8::String::New(""); 13711 context->SetData(obj); 13712 CompileRun(source_simple); 13713 context->Exit(); 13714 } 13715 context.Dispose(); 13716 for (gc_count = 1; gc_count < 10; gc_count++) { 13717 other_context->Enter(); 13718 CompileRun(source_simple); 13719 other_context->Exit(); 13720 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 13721 if (GetGlobalObjectsCount() == 1) break; 13722 } 13723 CHECK_GE(2, gc_count); 13724 CHECK_EQ(1, GetGlobalObjectsCount()); 13725 13726 // Eval in a function creates reference from the compilation cache to the 13727 // global object. 13728 const char* source_eval = "function f(){eval('1')}; f()"; 13729 context = Context::New(); 13730 { 13731 v8::HandleScope scope; 13732 13733 context->Enter(); 13734 CompileRun(source_eval); 13735 context->Exit(); 13736 } 13737 context.Dispose(); 13738 for (gc_count = 1; gc_count < 10; gc_count++) { 13739 other_context->Enter(); 13740 CompileRun(source_eval); 13741 other_context->Exit(); 13742 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 13743 if (GetGlobalObjectsCount() == 1) break; 13744 } 13745 CHECK_GE(2, gc_count); 13746 CHECK_EQ(1, GetGlobalObjectsCount()); 13747 13748 // Looking up the line number for an exception creates reference from the 13749 // compilation cache to the global object. 13750 const char* source_exception = "function f(){throw 1;} f()"; 13751 context = Context::New(); 13752 { 13753 v8::HandleScope scope; 13754 13755 context->Enter(); 13756 v8::TryCatch try_catch; 13757 CompileRun(source_exception); 13758 CHECK(try_catch.HasCaught()); 13759 v8::Handle<v8::Message> message = try_catch.Message(); 13760 CHECK(!message.IsEmpty()); 13761 CHECK_EQ(1, message->GetLineNumber()); 13762 context->Exit(); 13763 } 13764 context.Dispose(); 13765 for (gc_count = 1; gc_count < 10; gc_count++) { 13766 other_context->Enter(); 13767 CompileRun(source_exception); 13768 other_context->Exit(); 13769 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 13770 if (GetGlobalObjectsCount() == 1) break; 13771 } 13772 CHECK_GE(2, gc_count); 13773 CHECK_EQ(1, GetGlobalObjectsCount()); 13774 13775 other_context.Dispose(); 13776} 13777 13778 13779THREADED_TEST(ScriptOrigin) { 13780 v8::HandleScope scope; 13781 LocalContext env; 13782 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 13783 v8::Handle<v8::String> script = v8::String::New( 13784 "function f() {}\n\nfunction g() {}"); 13785 v8::Script::Compile(script, &origin)->Run(); 13786 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 13787 env->Global()->Get(v8::String::New("f"))); 13788 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 13789 env->Global()->Get(v8::String::New("g"))); 13790 13791 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin(); 13792 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName())); 13793 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value()); 13794 13795 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin(); 13796 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName())); 13797 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value()); 13798} 13799 13800 13801THREADED_TEST(ScriptLineNumber) { 13802 v8::HandleScope scope; 13803 LocalContext env; 13804 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 13805 v8::Handle<v8::String> script = v8::String::New( 13806 "function f() {}\n\nfunction g() {}"); 13807 v8::Script::Compile(script, &origin)->Run(); 13808 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 13809 env->Global()->Get(v8::String::New("f"))); 13810 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 13811 env->Global()->Get(v8::String::New("g"))); 13812 CHECK_EQ(0, f->GetScriptLineNumber()); 13813 CHECK_EQ(2, g->GetScriptLineNumber()); 13814} 13815 13816 13817THREADED_TEST(ScriptColumnNumber) { 13818 v8::HandleScope scope; 13819 LocalContext env; 13820 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"), 13821 v8::Integer::New(3), v8::Integer::New(2)); 13822 v8::Handle<v8::String> script = v8::String::New( 13823 "function foo() {}\n\n function bar() {}"); 13824 v8::Script::Compile(script, &origin)->Run(); 13825 v8::Local<v8::Function> foo = v8::Local<v8::Function>::Cast( 13826 env->Global()->Get(v8::String::New("foo"))); 13827 v8::Local<v8::Function> bar = v8::Local<v8::Function>::Cast( 13828 env->Global()->Get(v8::String::New("bar"))); 13829 CHECK_EQ(14, foo->GetScriptColumnNumber()); 13830 CHECK_EQ(17, bar->GetScriptColumnNumber()); 13831} 13832 13833 13834THREADED_TEST(FunctionGetScriptId) { 13835 v8::HandleScope scope; 13836 LocalContext env; 13837 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"), 13838 v8::Integer::New(3), v8::Integer::New(2)); 13839 v8::Handle<v8::String> scriptSource = v8::String::New( 13840 "function foo() {}\n\n function bar() {}"); 13841 v8::Local<v8::Script> script(v8::Script::Compile(scriptSource, &origin)); 13842 script->Run(); 13843 v8::Local<v8::Function> foo = v8::Local<v8::Function>::Cast( 13844 env->Global()->Get(v8::String::New("foo"))); 13845 v8::Local<v8::Function> bar = v8::Local<v8::Function>::Cast( 13846 env->Global()->Get(v8::String::New("bar"))); 13847 CHECK_EQ(script->Id(), foo->GetScriptId()); 13848 CHECK_EQ(script->Id(), bar->GetScriptId()); 13849} 13850 13851 13852static v8::Handle<Value> GetterWhichReturns42(Local<String> name, 13853 const AccessorInfo& info) { 13854 return v8_num(42); 13855} 13856 13857 13858static void SetterWhichSetsYOnThisTo23(Local<String> name, 13859 Local<Value> value, 13860 const AccessorInfo& info) { 13861 info.This()->Set(v8_str("y"), v8_num(23)); 13862} 13863 13864 13865TEST(SetterOnConstructorPrototype) { 13866 v8::HandleScope scope; 13867 Local<ObjectTemplate> templ = ObjectTemplate::New(); 13868 templ->SetAccessor(v8_str("x"), 13869 GetterWhichReturns42, 13870 SetterWhichSetsYOnThisTo23); 13871 LocalContext context; 13872 context->Global()->Set(v8_str("P"), templ->NewInstance()); 13873 CompileRun("function C1() {" 13874 " this.x = 23;" 13875 "};" 13876 "C1.prototype = P;" 13877 "function C2() {" 13878 " this.x = 23" 13879 "};" 13880 "C2.prototype = { };" 13881 "C2.prototype.__proto__ = P;"); 13882 13883 v8::Local<v8::Script> script; 13884 script = v8::Script::Compile(v8_str("new C1();")); 13885 for (int i = 0; i < 10; i++) { 13886 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 13887 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); 13888 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); 13889 } 13890 13891 script = v8::Script::Compile(v8_str("new C2();")); 13892 for (int i = 0; i < 10; i++) { 13893 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 13894 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value()); 13895 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value()); 13896 } 13897} 13898 13899 13900static v8::Handle<Value> NamedPropertyGetterWhichReturns42( 13901 Local<String> name, const AccessorInfo& info) { 13902 return v8_num(42); 13903} 13904 13905 13906static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23( 13907 Local<String> name, Local<Value> value, const AccessorInfo& info) { 13908 if (name->Equals(v8_str("x"))) { 13909 info.This()->Set(v8_str("y"), v8_num(23)); 13910 } 13911 return v8::Handle<Value>(); 13912} 13913 13914 13915THREADED_TEST(InterceptorOnConstructorPrototype) { 13916 v8::HandleScope scope; 13917 Local<ObjectTemplate> templ = ObjectTemplate::New(); 13918 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42, 13919 NamedPropertySetterWhichSetsYOnThisTo23); 13920 LocalContext context; 13921 context->Global()->Set(v8_str("P"), templ->NewInstance()); 13922 CompileRun("function C1() {" 13923 " this.x = 23;" 13924 "};" 13925 "C1.prototype = P;" 13926 "function C2() {" 13927 " this.x = 23" 13928 "};" 13929 "C2.prototype = { };" 13930 "C2.prototype.__proto__ = P;"); 13931 13932 v8::Local<v8::Script> script; 13933 script = v8::Script::Compile(v8_str("new C1();")); 13934 for (int i = 0; i < 10; i++) { 13935 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 13936 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); 13937 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); 13938 } 13939 13940 script = v8::Script::Compile(v8_str("new C2();")); 13941 for (int i = 0; i < 10; i++) { 13942 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 13943 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value()); 13944 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value()); 13945 } 13946} 13947 13948 13949TEST(Bug618) { 13950 const char* source = "function C1() {" 13951 " this.x = 23;" 13952 "};" 13953 "C1.prototype = P;"; 13954 13955 v8::HandleScope scope; 13956 LocalContext context; 13957 v8::Local<v8::Script> script; 13958 13959 // Use a simple object as prototype. 13960 v8::Local<v8::Object> prototype = v8::Object::New(); 13961 prototype->Set(v8_str("y"), v8_num(42)); 13962 context->Global()->Set(v8_str("P"), prototype); 13963 13964 // This compile will add the code to the compilation cache. 13965 CompileRun(source); 13966 13967 script = v8::Script::Compile(v8_str("new C1();")); 13968 // Allow enough iterations for the inobject slack tracking logic 13969 // to finalize instance size and install the fast construct stub. 13970 for (int i = 0; i < 256; i++) { 13971 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 13972 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); 13973 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); 13974 } 13975 13976 // Use an API object with accessors as prototype. 13977 Local<ObjectTemplate> templ = ObjectTemplate::New(); 13978 templ->SetAccessor(v8_str("x"), 13979 GetterWhichReturns42, 13980 SetterWhichSetsYOnThisTo23); 13981 context->Global()->Set(v8_str("P"), templ->NewInstance()); 13982 13983 // This compile will get the code from the compilation cache. 13984 CompileRun(source); 13985 13986 script = v8::Script::Compile(v8_str("new C1();")); 13987 for (int i = 0; i < 10; i++) { 13988 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 13989 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); 13990 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); 13991 } 13992} 13993 13994int prologue_call_count = 0; 13995int epilogue_call_count = 0; 13996int prologue_call_count_second = 0; 13997int epilogue_call_count_second = 0; 13998 13999void PrologueCallback(v8::GCType, v8::GCCallbackFlags) { 14000 ++prologue_call_count; 14001} 14002 14003void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) { 14004 ++epilogue_call_count; 14005} 14006 14007void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { 14008 ++prologue_call_count_second; 14009} 14010 14011void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { 14012 ++epilogue_call_count_second; 14013} 14014 14015TEST(GCCallbacks) { 14016 LocalContext context; 14017 14018 v8::V8::AddGCPrologueCallback(PrologueCallback); 14019 v8::V8::AddGCEpilogueCallback(EpilogueCallback); 14020 CHECK_EQ(0, prologue_call_count); 14021 CHECK_EQ(0, epilogue_call_count); 14022 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 14023 CHECK_EQ(1, prologue_call_count); 14024 CHECK_EQ(1, epilogue_call_count); 14025 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond); 14026 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond); 14027 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 14028 CHECK_EQ(2, prologue_call_count); 14029 CHECK_EQ(2, epilogue_call_count); 14030 CHECK_EQ(1, prologue_call_count_second); 14031 CHECK_EQ(1, epilogue_call_count_second); 14032 v8::V8::RemoveGCPrologueCallback(PrologueCallback); 14033 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback); 14034 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 14035 CHECK_EQ(2, prologue_call_count); 14036 CHECK_EQ(2, epilogue_call_count); 14037 CHECK_EQ(2, prologue_call_count_second); 14038 CHECK_EQ(2, epilogue_call_count_second); 14039 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond); 14040 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond); 14041 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 14042 CHECK_EQ(2, prologue_call_count); 14043 CHECK_EQ(2, epilogue_call_count); 14044 CHECK_EQ(2, prologue_call_count_second); 14045 CHECK_EQ(2, epilogue_call_count_second); 14046} 14047 14048 14049THREADED_TEST(AddToJSFunctionResultCache) { 14050 i::FLAG_allow_natives_syntax = true; 14051 v8::HandleScope scope; 14052 14053 LocalContext context; 14054 14055 const char* code = 14056 "(function() {" 14057 " var key0 = 'a';" 14058 " var key1 = 'b';" 14059 " var r0 = %_GetFromCache(0, key0);" 14060 " var r1 = %_GetFromCache(0, key1);" 14061 " var r0_ = %_GetFromCache(0, key0);" 14062 " if (r0 !== r0_)" 14063 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;" 14064 " var r1_ = %_GetFromCache(0, key1);" 14065 " if (r1 !== r1_)" 14066 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;" 14067 " return 'PASSED';" 14068 "})()"; 14069 HEAP->ClearJSFunctionResultCaches(); 14070 ExpectString(code, "PASSED"); 14071} 14072 14073 14074static const int k0CacheSize = 16; 14075 14076THREADED_TEST(FillJSFunctionResultCache) { 14077 i::FLAG_allow_natives_syntax = true; 14078 v8::HandleScope scope; 14079 14080 LocalContext context; 14081 14082 const char* code = 14083 "(function() {" 14084 " var k = 'a';" 14085 " var r = %_GetFromCache(0, k);" 14086 " for (var i = 0; i < 16; i++) {" 14087 " %_GetFromCache(0, 'a' + i);" 14088 " };" 14089 " if (r === %_GetFromCache(0, k))" 14090 " return 'FAILED: k0CacheSize is too small';" 14091 " return 'PASSED';" 14092 "})()"; 14093 HEAP->ClearJSFunctionResultCaches(); 14094 ExpectString(code, "PASSED"); 14095} 14096 14097 14098THREADED_TEST(RoundRobinGetFromCache) { 14099 i::FLAG_allow_natives_syntax = true; 14100 v8::HandleScope scope; 14101 14102 LocalContext context; 14103 14104 const char* code = 14105 "(function() {" 14106 " var keys = [];" 14107 " for (var i = 0; i < 16; i++) keys.push(i);" 14108 " var values = [];" 14109 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" 14110 " for (var i = 0; i < 16; i++) {" 14111 " var v = %_GetFromCache(0, keys[i]);" 14112 " if (v !== values[i])" 14113 " return 'Wrong value for ' + " 14114 " keys[i] + ': ' + v + ' vs. ' + values[i];" 14115 " };" 14116 " return 'PASSED';" 14117 "})()"; 14118 HEAP->ClearJSFunctionResultCaches(); 14119 ExpectString(code, "PASSED"); 14120} 14121 14122 14123THREADED_TEST(ReverseGetFromCache) { 14124 i::FLAG_allow_natives_syntax = true; 14125 v8::HandleScope scope; 14126 14127 LocalContext context; 14128 14129 const char* code = 14130 "(function() {" 14131 " var keys = [];" 14132 " for (var i = 0; i < 16; i++) keys.push(i);" 14133 " var values = [];" 14134 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" 14135 " for (var i = 15; i >= 16; i--) {" 14136 " var v = %_GetFromCache(0, keys[i]);" 14137 " if (v !== values[i])" 14138 " return 'Wrong value for ' + " 14139 " keys[i] + ': ' + v + ' vs. ' + values[i];" 14140 " };" 14141 " return 'PASSED';" 14142 "})()"; 14143 HEAP->ClearJSFunctionResultCaches(); 14144 ExpectString(code, "PASSED"); 14145} 14146 14147 14148THREADED_TEST(TestEviction) { 14149 i::FLAG_allow_natives_syntax = true; 14150 v8::HandleScope scope; 14151 14152 LocalContext context; 14153 14154 const char* code = 14155 "(function() {" 14156 " for (var i = 0; i < 2*16; i++) {" 14157 " %_GetFromCache(0, 'a' + i);" 14158 " };" 14159 " return 'PASSED';" 14160 "})()"; 14161 HEAP->ClearJSFunctionResultCaches(); 14162 ExpectString(code, "PASSED"); 14163} 14164 14165 14166THREADED_TEST(TwoByteStringInAsciiCons) { 14167 // See Chromium issue 47824. 14168 v8::HandleScope scope; 14169 14170 LocalContext context; 14171 const char* init_code = 14172 "var str1 = 'abelspendabel';" 14173 "var str2 = str1 + str1 + str1;" 14174 "str2;"; 14175 Local<Value> result = CompileRun(init_code); 14176 14177 Local<Value> indexof = CompileRun("str2.indexOf('els')"); 14178 Local<Value> lastindexof = CompileRun("str2.lastIndexOf('dab')"); 14179 14180 CHECK(result->IsString()); 14181 i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result)); 14182 int length = string->length(); 14183 CHECK(string->IsAsciiRepresentation()); 14184 14185 FlattenString(string); 14186 i::Handle<i::String> flat_string = FlattenGetString(string); 14187 14188 CHECK(string->IsAsciiRepresentation()); 14189 CHECK(flat_string->IsAsciiRepresentation()); 14190 14191 // Create external resource. 14192 uint16_t* uc16_buffer = new uint16_t[length + 1]; 14193 14194 i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length); 14195 uc16_buffer[length] = 0; 14196 14197 TestResource resource(uc16_buffer); 14198 14199 flat_string->MakeExternal(&resource); 14200 14201 CHECK(flat_string->IsTwoByteRepresentation()); 14202 14203 // At this point, we should have a Cons string which is flat and ASCII, 14204 // with a first half that is a two-byte string (although it only contains 14205 // ASCII characters). This is a valid sequence of steps, and it can happen 14206 // in real pages. 14207 14208 CHECK(string->IsAsciiRepresentation()); 14209 i::ConsString* cons = i::ConsString::cast(*string); 14210 CHECK_EQ(0, cons->second()->length()); 14211 CHECK(cons->first()->IsTwoByteRepresentation()); 14212 14213 // Check that some string operations work. 14214 14215 // Atom RegExp. 14216 Local<Value> reresult = CompileRun("str2.match(/abel/g).length;"); 14217 CHECK_EQ(6, reresult->Int32Value()); 14218 14219 // Nonatom RegExp. 14220 reresult = CompileRun("str2.match(/abe./g).length;"); 14221 CHECK_EQ(6, reresult->Int32Value()); 14222 14223 reresult = CompileRun("str2.search(/bel/g);"); 14224 CHECK_EQ(1, reresult->Int32Value()); 14225 14226 reresult = CompileRun("str2.search(/be./g);"); 14227 CHECK_EQ(1, reresult->Int32Value()); 14228 14229 ExpectTrue("/bel/g.test(str2);"); 14230 14231 ExpectTrue("/be./g.test(str2);"); 14232 14233 reresult = CompileRun("/bel/g.exec(str2);"); 14234 CHECK(!reresult->IsNull()); 14235 14236 reresult = CompileRun("/be./g.exec(str2);"); 14237 CHECK(!reresult->IsNull()); 14238 14239 ExpectString("str2.substring(2, 10);", "elspenda"); 14240 14241 ExpectString("str2.substring(2, 20);", "elspendabelabelspe"); 14242 14243 ExpectString("str2.charAt(2);", "e"); 14244 14245 ExpectObject("str2.indexOf('els');", indexof); 14246 14247 ExpectObject("str2.lastIndexOf('dab');", lastindexof); 14248 14249 reresult = CompileRun("str2.charCodeAt(2);"); 14250 CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value()); 14251} 14252 14253 14254// Failed access check callback that performs a GC on each invocation. 14255void FailedAccessCheckCallbackGC(Local<v8::Object> target, 14256 v8::AccessType type, 14257 Local<v8::Value> data) { 14258 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 14259} 14260 14261 14262TEST(GCInFailedAccessCheckCallback) { 14263 // Install a failed access check callback that performs a GC on each 14264 // invocation. Then force the callback to be called from va 14265 14266 v8::V8::Initialize(); 14267 v8::V8::SetFailedAccessCheckCallbackFunction(&FailedAccessCheckCallbackGC); 14268 14269 v8::HandleScope scope; 14270 14271 // Create an ObjectTemplate for global objects and install access 14272 // check callbacks that will block access. 14273 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 14274 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker, 14275 IndexedGetAccessBlocker, 14276 v8::Handle<v8::Value>(), 14277 false); 14278 14279 // Create a context and set an x property on it's global object. 14280 LocalContext context0(NULL, global_template); 14281 context0->Global()->Set(v8_str("x"), v8_num(42)); 14282 v8::Handle<v8::Object> global0 = context0->Global(); 14283 14284 // Create a context with a different security token so that the 14285 // failed access check callback will be called on each access. 14286 LocalContext context1(NULL, global_template); 14287 context1->Global()->Set(v8_str("other"), global0); 14288 14289 // Get property with failed access check. 14290 ExpectUndefined("other.x"); 14291 14292 // Get element with failed access check. 14293 ExpectUndefined("other[0]"); 14294 14295 // Set property with failed access check. 14296 v8::Handle<v8::Value> result = CompileRun("other.x = new Object()"); 14297 CHECK(result->IsObject()); 14298 14299 // Set element with failed access check. 14300 result = CompileRun("other[0] = new Object()"); 14301 CHECK(result->IsObject()); 14302 14303 // Get property attribute with failed access check. 14304 ExpectFalse("\'x\' in other"); 14305 14306 // Get property attribute for element with failed access check. 14307 ExpectFalse("0 in other"); 14308 14309 // Delete property. 14310 ExpectFalse("delete other.x"); 14311 14312 // Delete element. 14313 CHECK_EQ(false, global0->Delete(0)); 14314 14315 // DefineAccessor. 14316 CHECK_EQ(false, 14317 global0->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("x"))); 14318 14319 // Define JavaScript accessor. 14320 ExpectUndefined("Object.prototype.__defineGetter__.call(" 14321 " other, \'x\', function() { return 42; })"); 14322 14323 // LookupAccessor. 14324 ExpectUndefined("Object.prototype.__lookupGetter__.call(" 14325 " other, \'x\')"); 14326 14327 // HasLocalElement. 14328 ExpectFalse("Object.prototype.hasOwnProperty.call(other, \'0\')"); 14329 14330 CHECK_EQ(false, global0->HasRealIndexedProperty(0)); 14331 CHECK_EQ(false, global0->HasRealNamedProperty(v8_str("x"))); 14332 CHECK_EQ(false, global0->HasRealNamedCallbackProperty(v8_str("x"))); 14333 14334 // Reset the failed access check callback so it does not influence 14335 // the other tests. 14336 v8::V8::SetFailedAccessCheckCallbackFunction(NULL); 14337} 14338 14339TEST(DefaultIsolateGetCurrent) { 14340 CHECK(v8::Isolate::GetCurrent() != NULL); 14341 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 14342 CHECK(reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate()); 14343 printf("*** %s\n", "DefaultIsolateGetCurrent success"); 14344} 14345 14346TEST(IsolateNewDispose) { 14347 v8::Isolate* current_isolate = v8::Isolate::GetCurrent(); 14348 v8::Isolate* isolate = v8::Isolate::New(); 14349 CHECK(isolate != NULL); 14350 CHECK(!reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate()); 14351 CHECK(current_isolate != isolate); 14352 CHECK(current_isolate == v8::Isolate::GetCurrent()); 14353 14354 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 14355 last_location = last_message = NULL; 14356 isolate->Dispose(); 14357 CHECK_EQ(last_location, NULL); 14358 CHECK_EQ(last_message, NULL); 14359} 14360 14361TEST(IsolateEnterExitDefault) { 14362 v8::HandleScope scope; 14363 LocalContext context; 14364 v8::Isolate* current_isolate = v8::Isolate::GetCurrent(); 14365 CHECK(current_isolate != NULL); // Default isolate. 14366 ExpectString("'hello'", "hello"); 14367 current_isolate->Enter(); 14368 ExpectString("'still working'", "still working"); 14369 current_isolate->Exit(); 14370 ExpectString("'still working 2'", "still working 2"); 14371 current_isolate->Exit(); 14372 // Default isolate is always, well, 'default current'. 14373 CHECK_EQ(v8::Isolate::GetCurrent(), current_isolate); 14374 // Still working since default isolate is auto-entering any thread 14375 // that has no isolate and attempts to execute V8 APIs. 14376 ExpectString("'still working 3'", "still working 3"); 14377} 14378 14379TEST(DisposeDefaultIsolate) { 14380 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 14381 14382 // Run some V8 code to trigger default isolate to become 'current'. 14383 v8::HandleScope scope; 14384 LocalContext context; 14385 ExpectString("'run some V8'", "run some V8"); 14386 14387 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 14388 CHECK(reinterpret_cast<i::Isolate*>(isolate)->IsDefaultIsolate()); 14389 last_location = last_message = NULL; 14390 isolate->Dispose(); 14391 // It is not possible to dispose default isolate via Isolate API. 14392 CHECK_NE(last_location, NULL); 14393 CHECK_NE(last_message, NULL); 14394} 14395 14396TEST(RunDefaultAndAnotherIsolate) { 14397 v8::HandleScope scope; 14398 LocalContext context; 14399 14400 // Enter new isolate. 14401 v8::Isolate* isolate = v8::Isolate::New(); 14402 CHECK(isolate); 14403 isolate->Enter(); 14404 { // Need this block because subsequent Exit() will deallocate Heap, 14405 // so we need all scope objects to be deconstructed when it happens. 14406 v8::HandleScope scope_new; 14407 LocalContext context_new; 14408 14409 // Run something in new isolate. 14410 CompileRun("var foo = 153;"); 14411 ExpectTrue("function f() { return foo == 153; }; f()"); 14412 } 14413 isolate->Exit(); 14414 14415 // This runs automatically in default isolate. 14416 // Variables in another isolate should be not available. 14417 ExpectTrue("function f() {" 14418 " try {" 14419 " foo;" 14420 " return false;" 14421 " } catch(e) {" 14422 " return true;" 14423 " }" 14424 "};" 14425 "var bar = 371;" 14426 "f()"); 14427 14428 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 14429 last_location = last_message = NULL; 14430 isolate->Dispose(); 14431 CHECK_EQ(last_location, NULL); 14432 CHECK_EQ(last_message, NULL); 14433 14434 // Check that default isolate still runs. 14435 ExpectTrue("function f() { return bar == 371; }; f()"); 14436} 14437 14438TEST(DisposeIsolateWhenInUse) { 14439 v8::Isolate* isolate = v8::Isolate::New(); 14440 CHECK(isolate); 14441 isolate->Enter(); 14442 v8::HandleScope scope; 14443 LocalContext context; 14444 // Run something in this isolate. 14445 ExpectTrue("true"); 14446 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 14447 last_location = last_message = NULL; 14448 // Still entered, should fail. 14449 isolate->Dispose(); 14450 CHECK_NE(last_location, NULL); 14451 CHECK_NE(last_message, NULL); 14452} 14453 14454TEST(RunTwoIsolatesOnSingleThread) { 14455 // Run isolate 1. 14456 v8::Isolate* isolate1 = v8::Isolate::New(); 14457 isolate1->Enter(); 14458 v8::Persistent<v8::Context> context1 = v8::Context::New(); 14459 14460 { 14461 v8::Context::Scope cscope(context1); 14462 v8::HandleScope scope; 14463 // Run something in new isolate. 14464 CompileRun("var foo = 'isolate 1';"); 14465 ExpectString("function f() { return foo; }; f()", "isolate 1"); 14466 } 14467 14468 // Run isolate 2. 14469 v8::Isolate* isolate2 = v8::Isolate::New(); 14470 v8::Persistent<v8::Context> context2; 14471 14472 { 14473 v8::Isolate::Scope iscope(isolate2); 14474 context2 = v8::Context::New(); 14475 v8::Context::Scope cscope(context2); 14476 v8::HandleScope scope; 14477 14478 // Run something in new isolate. 14479 CompileRun("var foo = 'isolate 2';"); 14480 ExpectString("function f() { return foo; }; f()", "isolate 2"); 14481 } 14482 14483 { 14484 v8::Context::Scope cscope(context1); 14485 v8::HandleScope scope; 14486 // Now again in isolate 1 14487 ExpectString("function f() { return foo; }; f()", "isolate 1"); 14488 } 14489 14490 isolate1->Exit(); 14491 14492 // Run some stuff in default isolate. 14493 v8::Persistent<v8::Context> context_default = v8::Context::New(); 14494 14495 { 14496 v8::Context::Scope cscope(context_default); 14497 v8::HandleScope scope; 14498 // Variables in other isolates should be not available, verify there 14499 // is an exception. 14500 ExpectTrue("function f() {" 14501 " try {" 14502 " foo;" 14503 " return false;" 14504 " } catch(e) {" 14505 " return true;" 14506 " }" 14507 "};" 14508 "var isDefaultIsolate = true;" 14509 "f()"); 14510 } 14511 14512 isolate1->Enter(); 14513 14514 { 14515 v8::Isolate::Scope iscope(isolate2); 14516 v8::Context::Scope cscope(context2); 14517 v8::HandleScope scope; 14518 ExpectString("function f() { return foo; }; f()", "isolate 2"); 14519 } 14520 14521 { 14522 v8::Context::Scope cscope(context1); 14523 v8::HandleScope scope; 14524 ExpectString("function f() { return foo; }; f()", "isolate 1"); 14525 } 14526 14527 { 14528 v8::Isolate::Scope iscope(isolate2); 14529 context2.Dispose(); 14530 } 14531 14532 context1.Dispose(); 14533 isolate1->Exit(); 14534 14535 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 14536 last_location = last_message = NULL; 14537 14538 isolate1->Dispose(); 14539 CHECK_EQ(last_location, NULL); 14540 CHECK_EQ(last_message, NULL); 14541 14542 isolate2->Dispose(); 14543 CHECK_EQ(last_location, NULL); 14544 CHECK_EQ(last_message, NULL); 14545 14546 // Check that default isolate still runs. 14547 { 14548 v8::Context::Scope cscope(context_default); 14549 v8::HandleScope scope; 14550 ExpectTrue("function f() { return isDefaultIsolate; }; f()"); 14551 } 14552} 14553 14554static int CalcFibonacci(v8::Isolate* isolate, int limit) { 14555 v8::Isolate::Scope isolate_scope(isolate); 14556 v8::HandleScope scope; 14557 LocalContext context; 14558 i::ScopedVector<char> code(1024); 14559 i::OS::SNPrintF(code, "function fib(n) {" 14560 " if (n <= 2) return 1;" 14561 " return fib(n-1) + fib(n-2);" 14562 "}" 14563 "fib(%d)", limit); 14564 Local<Value> value = CompileRun(code.start()); 14565 CHECK(value->IsNumber()); 14566 return static_cast<int>(value->NumberValue()); 14567} 14568 14569class IsolateThread : public v8::internal::Thread { 14570 public: 14571 IsolateThread(v8::Isolate* isolate, int fib_limit) 14572 : Thread("IsolateThread"), 14573 isolate_(isolate), 14574 fib_limit_(fib_limit), 14575 result_(0) { } 14576 14577 void Run() { 14578 result_ = CalcFibonacci(isolate_, fib_limit_); 14579 } 14580 14581 int result() { return result_; } 14582 14583 private: 14584 v8::Isolate* isolate_; 14585 int fib_limit_; 14586 int result_; 14587}; 14588 14589TEST(MultipleIsolatesOnIndividualThreads) { 14590 v8::Isolate* isolate1 = v8::Isolate::New(); 14591 v8::Isolate* isolate2 = v8::Isolate::New(); 14592 14593 IsolateThread thread1(isolate1, 21); 14594 IsolateThread thread2(isolate2, 12); 14595 14596 // Compute some fibonacci numbers on 3 threads in 3 isolates. 14597 thread1.Start(); 14598 thread2.Start(); 14599 14600 int result1 = CalcFibonacci(v8::Isolate::GetCurrent(), 21); 14601 int result2 = CalcFibonacci(v8::Isolate::GetCurrent(), 12); 14602 14603 thread1.Join(); 14604 thread2.Join(); 14605 14606 // Compare results. The actual fibonacci numbers for 12 and 21 are taken 14607 // (I'm lazy!) from http://en.wikipedia.org/wiki/Fibonacci_number 14608 CHECK_EQ(result1, 10946); 14609 CHECK_EQ(result2, 144); 14610 CHECK_EQ(result1, thread1.result()); 14611 CHECK_EQ(result2, thread2.result()); 14612 14613 isolate1->Dispose(); 14614 isolate2->Dispose(); 14615} 14616 14617TEST(IsolateDifferentContexts) { 14618 v8::Isolate* isolate = v8::Isolate::New(); 14619 Persistent<v8::Context> context; 14620 { 14621 v8::Isolate::Scope isolate_scope(isolate); 14622 v8::HandleScope handle_scope; 14623 context = v8::Context::New(); 14624 v8::Context::Scope context_scope(context); 14625 Local<Value> v = CompileRun("2"); 14626 CHECK(v->IsNumber()); 14627 CHECK_EQ(2, static_cast<int>(v->NumberValue())); 14628 } 14629 { 14630 v8::Isolate::Scope isolate_scope(isolate); 14631 v8::HandleScope handle_scope; 14632 context = v8::Context::New(); 14633 v8::Context::Scope context_scope(context); 14634 Local<Value> v = CompileRun("22"); 14635 CHECK(v->IsNumber()); 14636 CHECK_EQ(22, static_cast<int>(v->NumberValue())); 14637 } 14638} 14639 14640class InitDefaultIsolateThread : public v8::internal::Thread { 14641 public: 14642 enum TestCase { 14643 IgnoreOOM, 14644 SetResourceConstraints, 14645 SetFatalHandler, 14646 SetCounterFunction, 14647 SetCreateHistogramFunction, 14648 SetAddHistogramSampleFunction 14649 }; 14650 14651 explicit InitDefaultIsolateThread(TestCase testCase) 14652 : Thread("InitDefaultIsolateThread"), 14653 testCase_(testCase), 14654 result_(false) { } 14655 14656 void Run() { 14657 switch (testCase_) { 14658 case IgnoreOOM: 14659 v8::V8::IgnoreOutOfMemoryException(); 14660 break; 14661 14662 case SetResourceConstraints: { 14663 static const int K = 1024; 14664 v8::ResourceConstraints constraints; 14665 constraints.set_max_young_space_size(256 * K); 14666 constraints.set_max_old_space_size(4 * K * K); 14667 v8::SetResourceConstraints(&constraints); 14668 break; 14669 } 14670 14671 case SetFatalHandler: 14672 v8::V8::SetFatalErrorHandler(NULL); 14673 break; 14674 14675 case SetCounterFunction: 14676 v8::V8::SetCounterFunction(NULL); 14677 break; 14678 14679 case SetCreateHistogramFunction: 14680 v8::V8::SetCreateHistogramFunction(NULL); 14681 break; 14682 14683 case SetAddHistogramSampleFunction: 14684 v8::V8::SetAddHistogramSampleFunction(NULL); 14685 break; 14686 } 14687 result_ = true; 14688 } 14689 14690 bool result() { return result_; } 14691 14692 private: 14693 TestCase testCase_; 14694 bool result_; 14695}; 14696 14697 14698static void InitializeTestHelper(InitDefaultIsolateThread::TestCase testCase) { 14699 InitDefaultIsolateThread thread(testCase); 14700 thread.Start(); 14701 thread.Join(); 14702 CHECK_EQ(thread.result(), true); 14703} 14704 14705TEST(InitializeDefaultIsolateOnSecondaryThread1) { 14706 InitializeTestHelper(InitDefaultIsolateThread::IgnoreOOM); 14707} 14708 14709TEST(InitializeDefaultIsolateOnSecondaryThread2) { 14710 InitializeTestHelper(InitDefaultIsolateThread::SetResourceConstraints); 14711} 14712 14713TEST(InitializeDefaultIsolateOnSecondaryThread3) { 14714 InitializeTestHelper(InitDefaultIsolateThread::SetFatalHandler); 14715} 14716 14717TEST(InitializeDefaultIsolateOnSecondaryThread4) { 14718 InitializeTestHelper(InitDefaultIsolateThread::SetCounterFunction); 14719} 14720 14721TEST(InitializeDefaultIsolateOnSecondaryThread5) { 14722 InitializeTestHelper(InitDefaultIsolateThread::SetCreateHistogramFunction); 14723} 14724 14725TEST(InitializeDefaultIsolateOnSecondaryThread6) { 14726 InitializeTestHelper(InitDefaultIsolateThread::SetAddHistogramSampleFunction); 14727} 14728 14729 14730TEST(StringCheckMultipleContexts) { 14731 const char* code = 14732 "(function() { return \"a\".charAt(0); })()"; 14733 14734 { 14735 // Run the code twice in the first context to initialize the call IC. 14736 v8::HandleScope scope; 14737 LocalContext context1; 14738 ExpectString(code, "a"); 14739 ExpectString(code, "a"); 14740 } 14741 14742 { 14743 // Change the String.prototype in the second context and check 14744 // that the right function gets called. 14745 v8::HandleScope scope; 14746 LocalContext context2; 14747 CompileRun("String.prototype.charAt = function() { return \"not a\"; }"); 14748 ExpectString(code, "not a"); 14749 } 14750} 14751 14752 14753TEST(NumberCheckMultipleContexts) { 14754 const char* code = 14755 "(function() { return (42).toString(); })()"; 14756 14757 { 14758 // Run the code twice in the first context to initialize the call IC. 14759 v8::HandleScope scope; 14760 LocalContext context1; 14761 ExpectString(code, "42"); 14762 ExpectString(code, "42"); 14763 } 14764 14765 { 14766 // Change the Number.prototype in the second context and check 14767 // that the right function gets called. 14768 v8::HandleScope scope; 14769 LocalContext context2; 14770 CompileRun("Number.prototype.toString = function() { return \"not 42\"; }"); 14771 ExpectString(code, "not 42"); 14772 } 14773} 14774 14775 14776TEST(BooleanCheckMultipleContexts) { 14777 const char* code = 14778 "(function() { return true.toString(); })()"; 14779 14780 { 14781 // Run the code twice in the first context to initialize the call IC. 14782 v8::HandleScope scope; 14783 LocalContext context1; 14784 ExpectString(code, "true"); 14785 ExpectString(code, "true"); 14786 } 14787 14788 { 14789 // Change the Boolean.prototype in the second context and check 14790 // that the right function gets called. 14791 v8::HandleScope scope; 14792 LocalContext context2; 14793 CompileRun("Boolean.prototype.toString = function() { return \"\"; }"); 14794 ExpectString(code, ""); 14795 } 14796} 14797 14798 14799TEST(DontDeleteCellLoadIC) { 14800 const char* function_code = 14801 "function readCell() { while (true) { return cell; } }"; 14802 14803 { 14804 // Run the code twice in the first context to initialize the load 14805 // IC for a don't delete cell. 14806 v8::HandleScope scope; 14807 LocalContext context1; 14808 CompileRun("var cell = \"first\";"); 14809 ExpectBoolean("delete cell", false); 14810 CompileRun(function_code); 14811 ExpectString("readCell()", "first"); 14812 ExpectString("readCell()", "first"); 14813 } 14814 14815 { 14816 // Use a deletable cell in the second context. 14817 v8::HandleScope scope; 14818 LocalContext context2; 14819 CompileRun("cell = \"second\";"); 14820 CompileRun(function_code); 14821 ExpectString("readCell()", "second"); 14822 ExpectBoolean("delete cell", true); 14823 ExpectString("(function() {" 14824 " try {" 14825 " return readCell();" 14826 " } catch(e) {" 14827 " return e.toString();" 14828 " }" 14829 "})()", 14830 "ReferenceError: cell is not defined"); 14831 CompileRun("cell = \"new_second\";"); 14832 HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); 14833 ExpectString("readCell()", "new_second"); 14834 ExpectString("readCell()", "new_second"); 14835 } 14836} 14837 14838 14839TEST(DontDeleteCellLoadICForceDelete) { 14840 const char* function_code = 14841 "function readCell() { while (true) { return cell; } }"; 14842 14843 // Run the code twice to initialize the load IC for a don't delete 14844 // cell. 14845 v8::HandleScope scope; 14846 LocalContext context; 14847 CompileRun("var cell = \"value\";"); 14848 ExpectBoolean("delete cell", false); 14849 CompileRun(function_code); 14850 ExpectString("readCell()", "value"); 14851 ExpectString("readCell()", "value"); 14852 14853 // Delete the cell using the API and check the inlined code works 14854 // correctly. 14855 CHECK(context->Global()->ForceDelete(v8_str("cell"))); 14856 ExpectString("(function() {" 14857 " try {" 14858 " return readCell();" 14859 " } catch(e) {" 14860 " return e.toString();" 14861 " }" 14862 "})()", 14863 "ReferenceError: cell is not defined"); 14864} 14865 14866 14867TEST(DontDeleteCellLoadICAPI) { 14868 const char* function_code = 14869 "function readCell() { while (true) { return cell; } }"; 14870 14871 // Run the code twice to initialize the load IC for a don't delete 14872 // cell created using the API. 14873 v8::HandleScope scope; 14874 LocalContext context; 14875 context->Global()->Set(v8_str("cell"), v8_str("value"), v8::DontDelete); 14876 ExpectBoolean("delete cell", false); 14877 CompileRun(function_code); 14878 ExpectString("readCell()", "value"); 14879 ExpectString("readCell()", "value"); 14880 14881 // Delete the cell using the API and check the inlined code works 14882 // correctly. 14883 CHECK(context->Global()->ForceDelete(v8_str("cell"))); 14884 ExpectString("(function() {" 14885 " try {" 14886 " return readCell();" 14887 " } catch(e) {" 14888 " return e.toString();" 14889 " }" 14890 "})()", 14891 "ReferenceError: cell is not defined"); 14892} 14893 14894 14895TEST(RegExp) { 14896 v8::HandleScope scope; 14897 LocalContext context; 14898 14899 v8::Handle<v8::RegExp> re = v8::RegExp::New(v8_str("foo"), v8::RegExp::kNone); 14900 CHECK(re->IsRegExp()); 14901 CHECK(re->GetSource()->Equals(v8_str("foo"))); 14902 CHECK_EQ(v8::RegExp::kNone, re->GetFlags()); 14903 14904 re = v8::RegExp::New(v8_str("bar"), 14905 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | 14906 v8::RegExp::kGlobal)); 14907 CHECK(re->IsRegExp()); 14908 CHECK(re->GetSource()->Equals(v8_str("bar"))); 14909 CHECK_EQ(v8::RegExp::kIgnoreCase | v8::RegExp::kGlobal, 14910 static_cast<int>(re->GetFlags())); 14911 14912 re = v8::RegExp::New(v8_str("baz"), 14913 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | 14914 v8::RegExp::kMultiline)); 14915 CHECK(re->IsRegExp()); 14916 CHECK(re->GetSource()->Equals(v8_str("baz"))); 14917 CHECK_EQ(v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline, 14918 static_cast<int>(re->GetFlags())); 14919 14920 re = CompileRun("/quux/").As<v8::RegExp>(); 14921 CHECK(re->IsRegExp()); 14922 CHECK(re->GetSource()->Equals(v8_str("quux"))); 14923 CHECK_EQ(v8::RegExp::kNone, re->GetFlags()); 14924 14925 re = CompileRun("/quux/gm").As<v8::RegExp>(); 14926 CHECK(re->IsRegExp()); 14927 CHECK(re->GetSource()->Equals(v8_str("quux"))); 14928 CHECK_EQ(v8::RegExp::kGlobal | v8::RegExp::kMultiline, 14929 static_cast<int>(re->GetFlags())); 14930 14931 // Override the RegExp constructor and check the API constructor 14932 // still works. 14933 CompileRun("RegExp = function() {}"); 14934 14935 re = v8::RegExp::New(v8_str("foobar"), v8::RegExp::kNone); 14936 CHECK(re->IsRegExp()); 14937 CHECK(re->GetSource()->Equals(v8_str("foobar"))); 14938 CHECK_EQ(v8::RegExp::kNone, re->GetFlags()); 14939 14940 re = v8::RegExp::New(v8_str("foobarbaz"), 14941 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | 14942 v8::RegExp::kMultiline)); 14943 CHECK(re->IsRegExp()); 14944 CHECK(re->GetSource()->Equals(v8_str("foobarbaz"))); 14945 CHECK_EQ(v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline, 14946 static_cast<int>(re->GetFlags())); 14947 14948 context->Global()->Set(v8_str("re"), re); 14949 ExpectTrue("re.test('FoobarbaZ')"); 14950 14951 // RegExps are objects on which you can set properties. 14952 re->Set(v8_str("property"), v8::Integer::New(32)); 14953 v8::Handle<v8::Value> value(CompileRun("re.property")); 14954 ASSERT_EQ(32, value->Int32Value()); 14955 14956 v8::TryCatch try_catch; 14957 re = v8::RegExp::New(v8_str("foo["), v8::RegExp::kNone); 14958 CHECK(re.IsEmpty()); 14959 CHECK(try_catch.HasCaught()); 14960 context->Global()->Set(v8_str("ex"), try_catch.Exception()); 14961 ExpectTrue("ex instanceof SyntaxError"); 14962} 14963 14964 14965THREADED_TEST(Equals) { 14966 v8::HandleScope handleScope; 14967 LocalContext localContext; 14968 14969 v8::Handle<v8::Object> globalProxy = localContext->Global(); 14970 v8::Handle<Value> global = globalProxy->GetPrototype(); 14971 14972 CHECK(global->StrictEquals(global)); 14973 CHECK(!global->StrictEquals(globalProxy)); 14974 CHECK(!globalProxy->StrictEquals(global)); 14975 CHECK(globalProxy->StrictEquals(globalProxy)); 14976 14977 CHECK(global->Equals(global)); 14978 CHECK(!global->Equals(globalProxy)); 14979 CHECK(!globalProxy->Equals(global)); 14980 CHECK(globalProxy->Equals(globalProxy)); 14981} 14982 14983 14984static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, 14985 const v8::AccessorInfo& info ) { 14986 return v8_str("42!"); 14987} 14988 14989 14990static v8::Handle<v8::Array> Enumerator(const v8::AccessorInfo& info) { 14991 v8::Handle<v8::Array> result = v8::Array::New(); 14992 result->Set(0, v8_str("universalAnswer")); 14993 return result; 14994} 14995 14996 14997TEST(NamedEnumeratorAndForIn) { 14998 v8::HandleScope handle_scope; 14999 LocalContext context; 15000 v8::Context::Scope context_scope(context.local()); 15001 15002 v8::Handle<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New(); 15003 tmpl->SetNamedPropertyHandler(Getter, NULL, NULL, NULL, Enumerator); 15004 context->Global()->Set(v8_str("o"), tmpl->NewInstance()); 15005 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 15006 "var result = []; for (var k in o) result.push(k); result")); 15007 CHECK_EQ(1, result->Length()); 15008 CHECK_EQ(v8_str("universalAnswer"), result->Get(0)); 15009} 15010 15011 15012TEST(DefinePropertyPostDetach) { 15013 v8::HandleScope scope; 15014 LocalContext context; 15015 v8::Handle<v8::Object> proxy = context->Global(); 15016 v8::Handle<v8::Function> define_property = 15017 CompileRun("(function() {" 15018 " Object.defineProperty(" 15019 " this," 15020 " 1," 15021 " { configurable: true, enumerable: true, value: 3 });" 15022 "})").As<Function>(); 15023 context->DetachGlobal(); 15024 define_property->Call(proxy, 0, NULL); 15025} 15026 15027 15028static void InstallContextId(v8::Handle<Context> context, int id) { 15029 Context::Scope scope(context); 15030 CompileRun("Object.prototype").As<Object>()-> 15031 Set(v8_str("context_id"), v8::Integer::New(id)); 15032} 15033 15034 15035static void CheckContextId(v8::Handle<Object> object, int expected) { 15036 CHECK_EQ(expected, object->Get(v8_str("context_id"))->Int32Value()); 15037} 15038 15039 15040THREADED_TEST(CreationContext) { 15041 HandleScope handle_scope; 15042 Persistent<Context> context1 = Context::New(); 15043 InstallContextId(context1, 1); 15044 Persistent<Context> context2 = Context::New(); 15045 InstallContextId(context2, 2); 15046 Persistent<Context> context3 = Context::New(); 15047 InstallContextId(context3, 3); 15048 15049 Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New(); 15050 15051 Local<Object> object1; 15052 Local<Function> func1; 15053 { 15054 Context::Scope scope(context1); 15055 object1 = Object::New(); 15056 func1 = tmpl->GetFunction(); 15057 } 15058 15059 Local<Object> object2; 15060 Local<Function> func2; 15061 { 15062 Context::Scope scope(context2); 15063 object2 = Object::New(); 15064 func2 = tmpl->GetFunction(); 15065 } 15066 15067 Local<Object> instance1; 15068 Local<Object> instance2; 15069 15070 { 15071 Context::Scope scope(context3); 15072 instance1 = func1->NewInstance(); 15073 instance2 = func2->NewInstance(); 15074 } 15075 15076 CHECK(object1->CreationContext() == context1); 15077 CheckContextId(object1, 1); 15078 CHECK(func1->CreationContext() == context1); 15079 CheckContextId(func1, 1); 15080 CHECK(instance1->CreationContext() == context1); 15081 CheckContextId(instance1, 1); 15082 CHECK(object2->CreationContext() == context2); 15083 CheckContextId(object2, 2); 15084 CHECK(func2->CreationContext() == context2); 15085 CheckContextId(func2, 2); 15086 CHECK(instance2->CreationContext() == context2); 15087 CheckContextId(instance2, 2); 15088 15089 { 15090 Context::Scope scope(context1); 15091 CHECK(object1->CreationContext() == context1); 15092 CheckContextId(object1, 1); 15093 CHECK(func1->CreationContext() == context1); 15094 CheckContextId(func1, 1); 15095 CHECK(instance1->CreationContext() == context1); 15096 CheckContextId(instance1, 1); 15097 CHECK(object2->CreationContext() == context2); 15098 CheckContextId(object2, 2); 15099 CHECK(func2->CreationContext() == context2); 15100 CheckContextId(func2, 2); 15101 CHECK(instance2->CreationContext() == context2); 15102 CheckContextId(instance2, 2); 15103 } 15104 15105 { 15106 Context::Scope scope(context2); 15107 CHECK(object1->CreationContext() == context1); 15108 CheckContextId(object1, 1); 15109 CHECK(func1->CreationContext() == context1); 15110 CheckContextId(func1, 1); 15111 CHECK(instance1->CreationContext() == context1); 15112 CheckContextId(instance1, 1); 15113 CHECK(object2->CreationContext() == context2); 15114 CheckContextId(object2, 2); 15115 CHECK(func2->CreationContext() == context2); 15116 CheckContextId(func2, 2); 15117 CHECK(instance2->CreationContext() == context2); 15118 CheckContextId(instance2, 2); 15119 } 15120 15121 context1.Dispose(); 15122 context2.Dispose(); 15123 context3.Dispose(); 15124} 15125 15126 15127THREADED_TEST(CreationContextOfJsFunction) { 15128 HandleScope handle_scope; 15129 Persistent<Context> context = Context::New(); 15130 InstallContextId(context, 1); 15131 15132 Local<Object> function; 15133 { 15134 Context::Scope scope(context); 15135 function = CompileRun("function foo() {}; foo").As<Object>(); 15136 } 15137 15138 CHECK(function->CreationContext() == context); 15139 CheckContextId(function, 1); 15140 15141 context.Dispose(); 15142} 15143 15144 15145Handle<Value> HasOwnPropertyIndexedPropertyGetter(uint32_t index, 15146 const AccessorInfo& info) { 15147 if (index == 42) return v8_str("yes"); 15148 return Handle<v8::Integer>(); 15149} 15150 15151 15152Handle<Value> HasOwnPropertyNamedPropertyGetter(Local<String> property, 15153 const AccessorInfo& info) { 15154 if (property->Equals(v8_str("foo"))) return v8_str("yes"); 15155 return Handle<Value>(); 15156} 15157 15158 15159Handle<v8::Integer> HasOwnPropertyIndexedPropertyQuery( 15160 uint32_t index, const AccessorInfo& info) { 15161 if (index == 42) return v8_num(1).As<v8::Integer>(); 15162 return Handle<v8::Integer>(); 15163} 15164 15165 15166Handle<v8::Integer> HasOwnPropertyNamedPropertyQuery( 15167 Local<String> property, const AccessorInfo& info) { 15168 if (property->Equals(v8_str("foo"))) return v8_num(1).As<v8::Integer>(); 15169 return Handle<v8::Integer>(); 15170} 15171 15172 15173Handle<v8::Integer> HasOwnPropertyNamedPropertyQuery2( 15174 Local<String> property, const AccessorInfo& info) { 15175 if (property->Equals(v8_str("bar"))) return v8_num(1).As<v8::Integer>(); 15176 return Handle<v8::Integer>(); 15177} 15178 15179 15180Handle<Value> HasOwnPropertyAccessorGetter(Local<String> property, 15181 const AccessorInfo& info) { 15182 return v8_str("yes"); 15183} 15184 15185 15186TEST(HasOwnProperty) { 15187 v8::HandleScope scope; 15188 LocalContext env; 15189 { // Check normal properties and defined getters. 15190 Handle<Value> value = CompileRun( 15191 "function Foo() {" 15192 " this.foo = 11;" 15193 " this.__defineGetter__('baz', function() { return 1; });" 15194 "};" 15195 "function Bar() { " 15196 " this.bar = 13;" 15197 " this.__defineGetter__('bla', function() { return 2; });" 15198 "};" 15199 "Bar.prototype = new Foo();" 15200 "new Bar();"); 15201 CHECK(value->IsObject()); 15202 Handle<Object> object = value->ToObject(); 15203 CHECK(object->Has(v8_str("foo"))); 15204 CHECK(!object->HasOwnProperty(v8_str("foo"))); 15205 CHECK(object->HasOwnProperty(v8_str("bar"))); 15206 CHECK(object->Has(v8_str("baz"))); 15207 CHECK(!object->HasOwnProperty(v8_str("baz"))); 15208 CHECK(object->HasOwnProperty(v8_str("bla"))); 15209 } 15210 { // Check named getter interceptors. 15211 Handle<ObjectTemplate> templ = ObjectTemplate::New(); 15212 templ->SetNamedPropertyHandler(HasOwnPropertyNamedPropertyGetter); 15213 Handle<Object> instance = templ->NewInstance(); 15214 CHECK(!instance->HasOwnProperty(v8_str("42"))); 15215 CHECK(instance->HasOwnProperty(v8_str("foo"))); 15216 CHECK(!instance->HasOwnProperty(v8_str("bar"))); 15217 } 15218 { // Check indexed getter interceptors. 15219 Handle<ObjectTemplate> templ = ObjectTemplate::New(); 15220 templ->SetIndexedPropertyHandler(HasOwnPropertyIndexedPropertyGetter); 15221 Handle<Object> instance = templ->NewInstance(); 15222 CHECK(instance->HasOwnProperty(v8_str("42"))); 15223 CHECK(!instance->HasOwnProperty(v8_str("43"))); 15224 CHECK(!instance->HasOwnProperty(v8_str("foo"))); 15225 } 15226 { // Check named query interceptors. 15227 Handle<ObjectTemplate> templ = ObjectTemplate::New(); 15228 templ->SetNamedPropertyHandler(0, 0, HasOwnPropertyNamedPropertyQuery); 15229 Handle<Object> instance = templ->NewInstance(); 15230 CHECK(instance->HasOwnProperty(v8_str("foo"))); 15231 CHECK(!instance->HasOwnProperty(v8_str("bar"))); 15232 } 15233 { // Check indexed query interceptors. 15234 Handle<ObjectTemplate> templ = ObjectTemplate::New(); 15235 templ->SetIndexedPropertyHandler(0, 0, HasOwnPropertyIndexedPropertyQuery); 15236 Handle<Object> instance = templ->NewInstance(); 15237 CHECK(instance->HasOwnProperty(v8_str("42"))); 15238 CHECK(!instance->HasOwnProperty(v8_str("41"))); 15239 } 15240 { // Check callbacks. 15241 Handle<ObjectTemplate> templ = ObjectTemplate::New(); 15242 templ->SetAccessor(v8_str("foo"), HasOwnPropertyAccessorGetter); 15243 Handle<Object> instance = templ->NewInstance(); 15244 CHECK(instance->HasOwnProperty(v8_str("foo"))); 15245 CHECK(!instance->HasOwnProperty(v8_str("bar"))); 15246 } 15247 { // Check that query wins on disagreement. 15248 Handle<ObjectTemplate> templ = ObjectTemplate::New(); 15249 templ->SetNamedPropertyHandler(HasOwnPropertyNamedPropertyGetter, 15250 0, 15251 HasOwnPropertyNamedPropertyQuery2); 15252 Handle<Object> instance = templ->NewInstance(); 15253 CHECK(!instance->HasOwnProperty(v8_str("foo"))); 15254 CHECK(instance->HasOwnProperty(v8_str("bar"))); 15255 } 15256} 15257 15258 15259void CheckCodeGenerationAllowed() { 15260 Handle<Value> result = CompileRun("eval('42')"); 15261 CHECK_EQ(42, result->Int32Value()); 15262 result = CompileRun("(function(e) { return e('42'); })(eval)"); 15263 CHECK_EQ(42, result->Int32Value()); 15264 result = CompileRun("var f = new Function('return 42'); f()"); 15265 CHECK_EQ(42, result->Int32Value()); 15266} 15267 15268 15269void CheckCodeGenerationDisallowed() { 15270 TryCatch try_catch; 15271 15272 Handle<Value> result = CompileRun("eval('42')"); 15273 CHECK(result.IsEmpty()); 15274 CHECK(try_catch.HasCaught()); 15275 try_catch.Reset(); 15276 15277 result = CompileRun("(function(e) { return e('42'); })(eval)"); 15278 CHECK(result.IsEmpty()); 15279 CHECK(try_catch.HasCaught()); 15280 try_catch.Reset(); 15281 15282 result = CompileRun("var f = new Function('return 42'); f()"); 15283 CHECK(result.IsEmpty()); 15284 CHECK(try_catch.HasCaught()); 15285} 15286 15287 15288bool CodeGenerationAllowed(Local<Context> context) { 15289 ApiTestFuzzer::Fuzz(); 15290 return true; 15291} 15292 15293 15294bool CodeGenerationDisallowed(Local<Context> context) { 15295 ApiTestFuzzer::Fuzz(); 15296 return false; 15297} 15298 15299 15300THREADED_TEST(AllowCodeGenFromStrings) { 15301 v8::HandleScope scope; 15302 LocalContext context; 15303 15304 // eval and the Function constructor allowed by default. 15305 CheckCodeGenerationAllowed(); 15306 15307 // Disallow eval and the Function constructor. 15308 context->AllowCodeGenerationFromStrings(false); 15309 CheckCodeGenerationDisallowed(); 15310 15311 // Allow again. 15312 context->AllowCodeGenerationFromStrings(true); 15313 CheckCodeGenerationAllowed(); 15314 15315 // Disallow but setting a global callback that will allow the calls. 15316 context->AllowCodeGenerationFromStrings(false); 15317 V8::SetAllowCodeGenerationFromStringsCallback(&CodeGenerationAllowed); 15318 CheckCodeGenerationAllowed(); 15319 15320 // Set a callback that disallows the code generation. 15321 V8::SetAllowCodeGenerationFromStringsCallback(&CodeGenerationDisallowed); 15322 CheckCodeGenerationDisallowed(); 15323} 15324 15325 15326static v8::Handle<Value> NonObjectThis(const v8::Arguments& args) { 15327 return v8::Undefined(); 15328} 15329 15330 15331THREADED_TEST(CallAPIFunctionOnNonObject) { 15332 v8::HandleScope scope; 15333 LocalContext context; 15334 Handle<FunctionTemplate> templ = v8::FunctionTemplate::New(NonObjectThis); 15335 Handle<Function> function = templ->GetFunction(); 15336 context->Global()->Set(v8_str("f"), function); 15337 TryCatch try_catch; 15338 CompileRun("f.call(2)"); 15339} 15340 15341 15342// Regression test for issue 1470. 15343THREADED_TEST(ReadOnlyIndexedProperties) { 15344 v8::HandleScope scope; 15345 Local<ObjectTemplate> templ = ObjectTemplate::New(); 15346 15347 LocalContext context; 15348 Local<v8::Object> obj = templ->NewInstance(); 15349 context->Global()->Set(v8_str("obj"), obj); 15350 obj->Set(v8_str("1"), v8_str("DONT_CHANGE"), v8::ReadOnly); 15351 obj->Set(v8_str("1"), v8_str("foobar")); 15352 CHECK_EQ(v8_str("DONT_CHANGE"), obj->Get(v8_str("1"))); 15353 obj->Set(v8_num(2), v8_str("DONT_CHANGE"), v8::ReadOnly); 15354 obj->Set(v8_num(2), v8_str("foobar")); 15355 CHECK_EQ(v8_str("DONT_CHANGE"), obj->Get(v8_num(2))); 15356 15357 // Test non-smi case. 15358 obj->Set(v8_str("2000000000"), v8_str("DONT_CHANGE"), v8::ReadOnly); 15359 obj->Set(v8_str("2000000000"), v8_str("foobar")); 15360 CHECK_EQ(v8_str("DONT_CHANGE"), obj->Get(v8_str("2000000000"))); 15361} 15362 15363 15364THREADED_TEST(Regress1516) { 15365 v8::HandleScope scope; 15366 15367 LocalContext context; 15368 { v8::HandleScope temp_scope; 15369 CompileRun("({'a': 0})"); 15370 } 15371 15372 int elements; 15373 { i::MapCache* map_cache = 15374 i::MapCache::cast(i::Isolate::Current()->context()->map_cache()); 15375 elements = map_cache->NumberOfElements(); 15376 CHECK_LE(1, elements); 15377 } 15378 15379 i::Isolate::Current()->heap()->CollectAllGarbage(true); 15380 { i::Object* raw_map_cache = i::Isolate::Current()->context()->map_cache(); 15381 if (raw_map_cache != i::Isolate::Current()->heap()->undefined_value()) { 15382 i::MapCache* map_cache = i::MapCache::cast(raw_map_cache); 15383 CHECK_GT(elements, map_cache->NumberOfElements()); 15384 } 15385 } 15386} 15387 15388 15389static bool BlockProtoNamedSecurityTestCallback(Local<v8::Object> global, 15390 Local<Value> name, 15391 v8::AccessType type, 15392 Local<Value> data) { 15393 // Only block read access to __proto__. 15394 if (type == v8::ACCESS_GET && 15395 name->IsString() && 15396 name->ToString()->Length() == 9 && 15397 name->ToString()->Utf8Length() == 9) { 15398 char buffer[10]; 15399 CHECK_EQ(10, name->ToString()->WriteUtf8(buffer)); 15400 return strncmp(buffer, "__proto__", 9) != 0; 15401 } 15402 15403 return true; 15404} 15405 15406 15407THREADED_TEST(Regress93759) { 15408 HandleScope scope; 15409 15410 // Template for object with security check. 15411 Local<ObjectTemplate> no_proto_template = v8::ObjectTemplate::New(); 15412 // We don't do indexing, so any callback can be used for that. 15413 no_proto_template->SetAccessCheckCallbacks( 15414 BlockProtoNamedSecurityTestCallback, 15415 IndexedSecurityTestCallback); 15416 15417 // Templates for objects with hidden prototypes and possibly security check. 15418 Local<FunctionTemplate> hidden_proto_template = v8::FunctionTemplate::New(); 15419 hidden_proto_template->SetHiddenPrototype(true); 15420 15421 Local<FunctionTemplate> protected_hidden_proto_template = 15422 v8::FunctionTemplate::New(); 15423 protected_hidden_proto_template->InstanceTemplate()->SetAccessCheckCallbacks( 15424 BlockProtoNamedSecurityTestCallback, 15425 IndexedSecurityTestCallback); 15426 protected_hidden_proto_template->SetHiddenPrototype(true); 15427 15428 // Context for "foreign" objects used in test. 15429 Persistent<Context> context = v8::Context::New(); 15430 context->Enter(); 15431 15432 // Plain object, no security check. 15433 Local<Object> simple_object = Object::New(); 15434 15435 // Object with explicit security check. 15436 Local<Object> protected_object = 15437 no_proto_template->NewInstance(); 15438 15439 // JSGlobalProxy object, always have security check. 15440 Local<Object> proxy_object = 15441 context->Global(); 15442 15443 // Global object, the prototype of proxy_object. No security checks. 15444 Local<Object> global_object = 15445 proxy_object->GetPrototype()->ToObject(); 15446 15447 // Hidden prototype without security check. 15448 Local<Object> hidden_prototype = 15449 hidden_proto_template->GetFunction()->NewInstance(); 15450 Local<Object> object_with_hidden = 15451 Object::New(); 15452 object_with_hidden->SetPrototype(hidden_prototype); 15453 15454 // Hidden prototype with security check on the hidden prototype. 15455 Local<Object> protected_hidden_prototype = 15456 protected_hidden_proto_template->GetFunction()->NewInstance(); 15457 Local<Object> object_with_protected_hidden = 15458 Object::New(); 15459 object_with_protected_hidden->SetPrototype(protected_hidden_prototype); 15460 15461 context->Exit(); 15462 15463 // Template for object for second context. Values to test are put on it as 15464 // properties. 15465 Local<ObjectTemplate> global_template = ObjectTemplate::New(); 15466 global_template->Set(v8_str("simple"), simple_object); 15467 global_template->Set(v8_str("protected"), protected_object); 15468 global_template->Set(v8_str("global"), global_object); 15469 global_template->Set(v8_str("proxy"), proxy_object); 15470 global_template->Set(v8_str("hidden"), object_with_hidden); 15471 global_template->Set(v8_str("phidden"), object_with_protected_hidden); 15472 15473 LocalContext context2(NULL, global_template); 15474 15475 Local<Value> result1 = CompileRun("Object.getPrototypeOf(simple)"); 15476 CHECK(result1->Equals(simple_object->GetPrototype())); 15477 15478 Local<Value> result2 = CompileRun("Object.getPrototypeOf(protected)"); 15479 CHECK(result2->Equals(Undefined())); 15480 15481 Local<Value> result3 = CompileRun("Object.getPrototypeOf(global)"); 15482 CHECK(result3->Equals(global_object->GetPrototype())); 15483 15484 Local<Value> result4 = CompileRun("Object.getPrototypeOf(proxy)"); 15485 CHECK(result4->Equals(Undefined())); 15486 15487 Local<Value> result5 = CompileRun("Object.getPrototypeOf(hidden)"); 15488 CHECK(result5->Equals( 15489 object_with_hidden->GetPrototype()->ToObject()->GetPrototype())); 15490 15491 Local<Value> result6 = CompileRun("Object.getPrototypeOf(phidden)"); 15492 CHECK(result6->Equals(Undefined())); 15493 15494 context.Dispose(); 15495} 15496 15497 15498static void TestReceiver(Local<Value> expected_result, 15499 Local<Value> expected_receiver, 15500 const char* code) { 15501 Local<Value> result = CompileRun(code); 15502 CHECK(result->IsObject()); 15503 CHECK(expected_receiver->Equals(result->ToObject()->Get(1))); 15504 CHECK(expected_result->Equals(result->ToObject()->Get(0))); 15505} 15506 15507 15508THREADED_TEST(ForeignFunctionReceiver) { 15509 HandleScope scope; 15510 15511 // Create two contexts with different "id" properties ('i' and 'o'). 15512 // Call a function both from its own context and from a the foreign 15513 // context, and see what "this" is bound to (returning both "this" 15514 // and "this.id" for comparison). 15515 15516 Persistent<Context> foreign_context = v8::Context::New(); 15517 foreign_context->Enter(); 15518 Local<Value> foreign_function = 15519 CompileRun("function func() { return { 0: this.id, " 15520 " 1: this, " 15521 " toString: function() { " 15522 " return this[0];" 15523 " }" 15524 " };" 15525 "}" 15526 "var id = 'i';" 15527 "func;"); 15528 CHECK(foreign_function->IsFunction()); 15529 foreign_context->Exit(); 15530 15531 LocalContext context; 15532 15533 Local<String> password = v8_str("Password"); 15534 // Don't get hit by security checks when accessing foreign_context's 15535 // global receiver (aka. global proxy). 15536 context->SetSecurityToken(password); 15537 foreign_context->SetSecurityToken(password); 15538 15539 Local<String> i = v8_str("i"); 15540 Local<String> o = v8_str("o"); 15541 Local<String> id = v8_str("id"); 15542 15543 CompileRun("function ownfunc() { return { 0: this.id, " 15544 " 1: this, " 15545 " toString: function() { " 15546 " return this[0];" 15547 " }" 15548 " };" 15549 "}" 15550 "var id = 'o';" 15551 "ownfunc"); 15552 context->Global()->Set(v8_str("func"), foreign_function); 15553 15554 // Sanity check the contexts. 15555 CHECK(i->Equals(foreign_context->Global()->Get(id))); 15556 CHECK(o->Equals(context->Global()->Get(id))); 15557 15558 // Checking local function's receiver. 15559 // Calling function using its call/apply methods. 15560 TestReceiver(o, context->Global(), "ownfunc.call()"); 15561 TestReceiver(o, context->Global(), "ownfunc.apply()"); 15562 // Making calls through built-in functions. 15563 TestReceiver(o, context->Global(), "[1].map(ownfunc)[0]"); 15564 CHECK(o->Equals(CompileRun("'abcbd'.replace(/b/,ownfunc)[1]"))); 15565 CHECK(o->Equals(CompileRun("'abcbd'.replace(/b/g,ownfunc)[1]"))); 15566 CHECK(o->Equals(CompileRun("'abcbd'.replace(/b/g,ownfunc)[3]"))); 15567 // Calling with environment record as base. 15568 TestReceiver(o, context->Global(), "ownfunc()"); 15569 // Calling with no base. 15570 TestReceiver(o, context->Global(), "(1,ownfunc)()"); 15571 15572 // Checking foreign function return value. 15573 // Calling function using its call/apply methods. 15574 TestReceiver(i, foreign_context->Global(), "func.call()"); 15575 TestReceiver(i, foreign_context->Global(), "func.apply()"); 15576 // Calling function using another context's call/apply methods. 15577 TestReceiver(i, foreign_context->Global(), 15578 "Function.prototype.call.call(func)"); 15579 TestReceiver(i, foreign_context->Global(), 15580 "Function.prototype.call.apply(func)"); 15581 TestReceiver(i, foreign_context->Global(), 15582 "Function.prototype.apply.call(func)"); 15583 TestReceiver(i, foreign_context->Global(), 15584 "Function.prototype.apply.apply(func)"); 15585 // Making calls through built-in functions. 15586 TestReceiver(i, foreign_context->Global(), "[1].map(func)[0]"); 15587 // ToString(func()) is func()[0], i.e., the returned this.id. 15588 CHECK(i->Equals(CompileRun("'abcbd'.replace(/b/,func)[1]"))); 15589 CHECK(i->Equals(CompileRun("'abcbd'.replace(/b/g,func)[1]"))); 15590 CHECK(i->Equals(CompileRun("'abcbd'.replace(/b/g,func)[3]"))); 15591 15592 // TODO(1547): Make the following also return "i". 15593 // Calling with environment record as base. 15594 TestReceiver(o, context->Global(), "func()"); 15595 // Calling with no base. 15596 TestReceiver(o, context->Global(), "(1,func)()"); 15597 15598 foreign_context.Dispose(); 15599} 15600