test-api.cc revision 8a31eba00023874d4a1dcdc5f411cc4336776874
1// Copyright 2007-2009 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28#include <limits.h> 29 30#include "v8.h" 31 32#include "api.h" 33#include "compilation-cache.h" 34#include "execution.h" 35#include "snapshot.h" 36#include "platform.h" 37#include "top.h" 38#include "utils.h" 39#include "cctest.h" 40#include "parser.h" 41#include "unicode-inl.h" 42 43static const bool kLogThreading = true; 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::ObjectTemplate; 54using ::v8::Value; 55using ::v8::Context; 56using ::v8::Local; 57using ::v8::String; 58using ::v8::Script; 59using ::v8::Function; 60using ::v8::AccessorInfo; 61using ::v8::Extension; 62 63namespace i = ::i; 64 65 66static void ExpectString(const char* code, const char* expected) { 67 Local<Value> result = CompileRun(code); 68 CHECK(result->IsString()); 69 String::AsciiValue ascii(result); 70 CHECK_EQ(expected, *ascii); 71} 72 73 74static void ExpectBoolean(const char* code, bool expected) { 75 Local<Value> result = CompileRun(code); 76 CHECK(result->IsBoolean()); 77 CHECK_EQ(expected, result->BooleanValue()); 78} 79 80 81static void ExpectTrue(const char* code) { 82 ExpectBoolean(code, true); 83} 84 85 86static void ExpectFalse(const char* code) { 87 ExpectBoolean(code, false); 88} 89 90 91static void ExpectObject(const char* code, Local<Value> expected) { 92 Local<Value> result = CompileRun(code); 93 CHECK(result->Equals(expected)); 94} 95 96 97static void ExpectUndefined(const char* code) { 98 Local<Value> result = CompileRun(code); 99 CHECK(result->IsUndefined()); 100} 101 102 103static int signature_callback_count; 104static v8::Handle<Value> IncrementingSignatureCallback( 105 const v8::Arguments& args) { 106 ApiTestFuzzer::Fuzz(); 107 signature_callback_count++; 108 v8::Handle<v8::Array> result = v8::Array::New(args.Length()); 109 for (int i = 0; i < args.Length(); i++) 110 result->Set(v8::Integer::New(i), args[i]); 111 return result; 112} 113 114 115static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) { 116 ApiTestFuzzer::Fuzz(); 117 v8::Handle<v8::Array> result = v8::Array::New(args.Length()); 118 for (int i = 0; i < args.Length(); i++) { 119 result->Set(v8::Integer::New(i), args[i]); 120 } 121 return result; 122} 123 124 125THREADED_TEST(Handles) { 126 v8::HandleScope scope; 127 Local<Context> local_env; 128 { 129 LocalContext env; 130 local_env = env.local(); 131 } 132 133 // Local context should still be live. 134 CHECK(!local_env.IsEmpty()); 135 local_env->Enter(); 136 137 v8::Handle<v8::Primitive> undef = v8::Undefined(); 138 CHECK(!undef.IsEmpty()); 139 CHECK(undef->IsUndefined()); 140 141 const char* c_source = "1 + 2 + 3"; 142 Local<String> source = String::New(c_source); 143 Local<Script> script = Script::Compile(source); 144 CHECK_EQ(6, script->Run()->Int32Value()); 145 146 local_env->Exit(); 147} 148 149 150THREADED_TEST(ReceiverSignature) { 151 v8::HandleScope scope; 152 LocalContext env; 153 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); 154 v8::Handle<v8::Signature> sig = v8::Signature::New(fun); 155 fun->PrototypeTemplate()->Set( 156 v8_str("m"), 157 v8::FunctionTemplate::New(IncrementingSignatureCallback, 158 v8::Handle<Value>(), 159 sig)); 160 env->Global()->Set(v8_str("Fun"), fun->GetFunction()); 161 signature_callback_count = 0; 162 CompileRun( 163 "var o = new Fun();" 164 "o.m();"); 165 CHECK_EQ(1, signature_callback_count); 166 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New(); 167 sub_fun->Inherit(fun); 168 env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction()); 169 CompileRun( 170 "var o = new SubFun();" 171 "o.m();"); 172 CHECK_EQ(2, signature_callback_count); 173 174 v8::TryCatch try_catch; 175 CompileRun( 176 "var o = { };" 177 "o.m = Fun.prototype.m;" 178 "o.m();"); 179 CHECK_EQ(2, signature_callback_count); 180 CHECK(try_catch.HasCaught()); 181 try_catch.Reset(); 182 v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New(); 183 sub_fun->Inherit(fun); 184 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction()); 185 CompileRun( 186 "var o = new UnrelFun();" 187 "o.m = Fun.prototype.m;" 188 "o.m();"); 189 CHECK_EQ(2, signature_callback_count); 190 CHECK(try_catch.HasCaught()); 191} 192 193 194 195 196THREADED_TEST(ArgumentSignature) { 197 v8::HandleScope scope; 198 LocalContext env; 199 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New(); 200 cons->SetClassName(v8_str("Cons")); 201 v8::Handle<v8::Signature> sig = 202 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons); 203 v8::Handle<v8::FunctionTemplate> fun = 204 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig); 205 env->Global()->Set(v8_str("Cons"), cons->GetFunction()); 206 env->Global()->Set(v8_str("Fun1"), fun->GetFunction()); 207 208 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';"); 209 CHECK(value1->IsTrue()); 210 211 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';"); 212 CHECK(value2->IsTrue()); 213 214 v8::Handle<Value> value3 = CompileRun("Fun1() == '';"); 215 CHECK(value3->IsTrue()); 216 217 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New(); 218 cons1->SetClassName(v8_str("Cons1")); 219 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New(); 220 cons2->SetClassName(v8_str("Cons2")); 221 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New(); 222 cons3->SetClassName(v8_str("Cons3")); 223 224 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 }; 225 v8::Handle<v8::Signature> wsig = 226 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args); 227 v8::Handle<v8::FunctionTemplate> fun2 = 228 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig); 229 230 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction()); 231 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction()); 232 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction()); 233 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction()); 234 v8::Handle<Value> value4 = CompileRun( 235 "Fun2(new Cons1(), new Cons2(), new Cons3()) ==" 236 "'[object Cons1],[object Cons2],[object Cons3]'"); 237 CHECK(value4->IsTrue()); 238 239 v8::Handle<Value> value5 = CompileRun( 240 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'"); 241 CHECK(value5->IsTrue()); 242 243 v8::Handle<Value> value6 = CompileRun( 244 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'"); 245 CHECK(value6->IsTrue()); 246 247 v8::Handle<Value> value7 = CompileRun( 248 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == " 249 "'[object Cons1],[object Cons2],[object Cons3],d';"); 250 CHECK(value7->IsTrue()); 251 252 v8::Handle<Value> value8 = CompileRun( 253 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'"); 254 CHECK(value8->IsTrue()); 255} 256 257 258THREADED_TEST(HulIgennem) { 259 v8::HandleScope scope; 260 LocalContext env; 261 v8::Handle<v8::Primitive> undef = v8::Undefined(); 262 Local<String> undef_str = undef->ToString(); 263 char* value = i::NewArray<char>(undef_str->Length() + 1); 264 undef_str->WriteAscii(value); 265 CHECK_EQ(0, strcmp(value, "undefined")); 266 i::DeleteArray(value); 267} 268 269 270THREADED_TEST(Access) { 271 v8::HandleScope scope; 272 LocalContext env; 273 Local<v8::Object> obj = v8::Object::New(); 274 Local<Value> foo_before = obj->Get(v8_str("foo")); 275 CHECK(foo_before->IsUndefined()); 276 Local<String> bar_str = v8_str("bar"); 277 obj->Set(v8_str("foo"), bar_str); 278 Local<Value> foo_after = obj->Get(v8_str("foo")); 279 CHECK(!foo_after->IsUndefined()); 280 CHECK(foo_after->IsString()); 281 CHECK_EQ(bar_str, foo_after); 282} 283 284 285THREADED_TEST(AccessElement) { 286 v8::HandleScope scope; 287 LocalContext env; 288 Local<v8::Object> obj = v8::Object::New(); 289 Local<Value> before = obj->Get(1); 290 CHECK(before->IsUndefined()); 291 Local<String> bar_str = v8_str("bar"); 292 obj->Set(1, bar_str); 293 Local<Value> after = obj->Get(1); 294 CHECK(!after->IsUndefined()); 295 CHECK(after->IsString()); 296 CHECK_EQ(bar_str, after); 297 298 Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>(); 299 CHECK_EQ(v8_str("a"), value->Get(0)); 300 CHECK_EQ(v8_str("b"), value->Get(1)); 301} 302 303 304THREADED_TEST(Script) { 305 v8::HandleScope scope; 306 LocalContext env; 307 const char* c_source = "1 + 2 + 3"; 308 Local<String> source = String::New(c_source); 309 Local<Script> script = Script::Compile(source); 310 CHECK_EQ(6, script->Run()->Int32Value()); 311} 312 313 314static uint16_t* AsciiToTwoByteString(const char* source) { 315 int array_length = i::StrLength(source) + 1; 316 uint16_t* converted = i::NewArray<uint16_t>(array_length); 317 for (int i = 0; i < array_length; i++) converted[i] = source[i]; 318 return converted; 319} 320 321 322class TestResource: public String::ExternalStringResource { 323 public: 324 static int dispose_count; 325 326 explicit TestResource(uint16_t* data) 327 : data_(data), length_(0) { 328 while (data[length_]) ++length_; 329 } 330 331 ~TestResource() { 332 i::DeleteArray(data_); 333 ++dispose_count; 334 } 335 336 const uint16_t* data() const { 337 return data_; 338 } 339 340 size_t length() const { 341 return length_; 342 } 343 private: 344 uint16_t* data_; 345 size_t length_; 346}; 347 348 349int TestResource::dispose_count = 0; 350 351 352class TestAsciiResource: public String::ExternalAsciiStringResource { 353 public: 354 static int dispose_count; 355 356 explicit TestAsciiResource(const char* data) 357 : data_(data), 358 length_(strlen(data)) { } 359 360 ~TestAsciiResource() { 361 i::DeleteArray(data_); 362 ++dispose_count; 363 } 364 365 const char* data() const { 366 return data_; 367 } 368 369 size_t length() const { 370 return length_; 371 } 372 private: 373 const char* data_; 374 size_t length_; 375}; 376 377 378int TestAsciiResource::dispose_count = 0; 379 380 381THREADED_TEST(ScriptUsingStringResource) { 382 TestResource::dispose_count = 0; 383 const char* c_source = "1 + 2 * 3"; 384 uint16_t* two_byte_source = AsciiToTwoByteString(c_source); 385 { 386 v8::HandleScope scope; 387 LocalContext env; 388 TestResource* resource = new TestResource(two_byte_source); 389 Local<String> source = String::NewExternal(resource); 390 Local<Script> script = Script::Compile(source); 391 Local<Value> value = script->Run(); 392 CHECK(value->IsNumber()); 393 CHECK_EQ(7, value->Int32Value()); 394 CHECK(source->IsExternal()); 395 CHECK_EQ(resource, 396 static_cast<TestResource*>(source->GetExternalStringResource())); 397 i::Heap::CollectAllGarbage(false); 398 CHECK_EQ(0, TestResource::dispose_count); 399 } 400 i::CompilationCache::Clear(); 401 i::Heap::CollectAllGarbage(false); 402 CHECK_EQ(1, TestResource::dispose_count); 403} 404 405 406THREADED_TEST(ScriptUsingAsciiStringResource) { 407 TestAsciiResource::dispose_count = 0; 408 const char* c_source = "1 + 2 * 3"; 409 { 410 v8::HandleScope scope; 411 LocalContext env; 412 Local<String> source = 413 String::NewExternal(new TestAsciiResource(i::StrDup(c_source))); 414 Local<Script> script = Script::Compile(source); 415 Local<Value> value = script->Run(); 416 CHECK(value->IsNumber()); 417 CHECK_EQ(7, value->Int32Value()); 418 i::Heap::CollectAllGarbage(false); 419 CHECK_EQ(0, TestAsciiResource::dispose_count); 420 } 421 i::CompilationCache::Clear(); 422 i::Heap::CollectAllGarbage(false); 423 CHECK_EQ(1, TestAsciiResource::dispose_count); 424} 425 426 427THREADED_TEST(ScriptMakingExternalString) { 428 TestResource::dispose_count = 0; 429 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3"); 430 { 431 v8::HandleScope scope; 432 LocalContext env; 433 Local<String> source = String::New(two_byte_source); 434 // Trigger GCs so that the newly allocated string moves to old gen. 435 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now 436 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now 437 bool success = source->MakeExternal(new TestResource(two_byte_source)); 438 CHECK(success); 439 Local<Script> script = Script::Compile(source); 440 Local<Value> value = script->Run(); 441 CHECK(value->IsNumber()); 442 CHECK_EQ(7, value->Int32Value()); 443 i::Heap::CollectAllGarbage(false); 444 CHECK_EQ(0, TestResource::dispose_count); 445 } 446 i::CompilationCache::Clear(); 447 i::Heap::CollectAllGarbage(false); 448 CHECK_EQ(1, TestResource::dispose_count); 449} 450 451 452THREADED_TEST(ScriptMakingExternalAsciiString) { 453 TestAsciiResource::dispose_count = 0; 454 const char* c_source = "1 + 2 * 3"; 455 { 456 v8::HandleScope scope; 457 LocalContext env; 458 Local<String> source = v8_str(c_source); 459 // Trigger GCs so that the newly allocated string moves to old gen. 460 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now 461 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now 462 bool success = source->MakeExternal( 463 new TestAsciiResource(i::StrDup(c_source))); 464 CHECK(success); 465 Local<Script> script = Script::Compile(source); 466 Local<Value> value = script->Run(); 467 CHECK(value->IsNumber()); 468 CHECK_EQ(7, value->Int32Value()); 469 i::Heap::CollectAllGarbage(false); 470 CHECK_EQ(0, TestAsciiResource::dispose_count); 471 } 472 i::CompilationCache::Clear(); 473 i::Heap::CollectAllGarbage(false); 474 CHECK_EQ(1, TestAsciiResource::dispose_count); 475} 476 477 478TEST(MakingExternalStringConditions) { 479 v8::HandleScope scope; 480 LocalContext env; 481 482 // Free some space in the new space so that we can check freshness. 483 i::Heap::CollectGarbage(i::NEW_SPACE); 484 i::Heap::CollectGarbage(i::NEW_SPACE); 485 486 uint16_t* two_byte_string = AsciiToTwoByteString("small"); 487 Local<String> small_string = String::New(two_byte_string); 488 i::DeleteArray(two_byte_string); 489 490 // We should refuse to externalize newly created small string. 491 CHECK(!small_string->CanMakeExternal()); 492 // Trigger GCs so that the newly allocated string moves to old gen. 493 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now 494 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now 495 // Old space strings should be accepted. 496 CHECK(small_string->CanMakeExternal()); 497 498 two_byte_string = AsciiToTwoByteString("small 2"); 499 small_string = String::New(two_byte_string); 500 i::DeleteArray(two_byte_string); 501 502 // We should refuse externalizing newly created small string. 503 CHECK(!small_string->CanMakeExternal()); 504 for (int i = 0; i < 100; i++) { 505 String::Value value(small_string); 506 } 507 // Frequently used strings should be accepted. 508 CHECK(small_string->CanMakeExternal()); 509 510 const int buf_size = 10 * 1024; 511 char* buf = i::NewArray<char>(buf_size); 512 memset(buf, 'a', buf_size); 513 buf[buf_size - 1] = '\0'; 514 515 two_byte_string = AsciiToTwoByteString(buf); 516 Local<String> large_string = String::New(two_byte_string); 517 i::DeleteArray(buf); 518 i::DeleteArray(two_byte_string); 519 // Large strings should be immediately accepted. 520 CHECK(large_string->CanMakeExternal()); 521} 522 523 524TEST(MakingExternalAsciiStringConditions) { 525 v8::HandleScope scope; 526 LocalContext env; 527 528 // Free some space in the new space so that we can check freshness. 529 i::Heap::CollectGarbage(i::NEW_SPACE); 530 i::Heap::CollectGarbage(i::NEW_SPACE); 531 532 Local<String> small_string = String::New("small"); 533 // We should refuse to externalize newly created small string. 534 CHECK(!small_string->CanMakeExternal()); 535 // Trigger GCs so that the newly allocated string moves to old gen. 536 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now 537 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now 538 // Old space strings should be accepted. 539 CHECK(small_string->CanMakeExternal()); 540 541 small_string = String::New("small 2"); 542 // We should refuse externalizing newly created small string. 543 CHECK(!small_string->CanMakeExternal()); 544 for (int i = 0; i < 100; i++) { 545 String::Value value(small_string); 546 } 547 // Frequently used strings should be accepted. 548 CHECK(small_string->CanMakeExternal()); 549 550 const int buf_size = 10 * 1024; 551 char* buf = i::NewArray<char>(buf_size); 552 memset(buf, 'a', buf_size); 553 buf[buf_size - 1] = '\0'; 554 Local<String> large_string = String::New(buf); 555 i::DeleteArray(buf); 556 // Large strings should be immediately accepted. 557 CHECK(large_string->CanMakeExternal()); 558} 559 560 561THREADED_TEST(UsingExternalString) { 562 { 563 v8::HandleScope scope; 564 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 565 Local<String> string = 566 String::NewExternal(new TestResource(two_byte_string)); 567 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 568 // Trigger GCs so that the newly allocated string moves to old gen. 569 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now 570 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now 571 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring); 572 CHECK(isymbol->IsSymbol()); 573 } 574 i::Heap::CollectAllGarbage(false); 575 i::Heap::CollectAllGarbage(false); 576} 577 578 579THREADED_TEST(UsingExternalAsciiString) { 580 { 581 v8::HandleScope scope; 582 const char* one_byte_string = "test string"; 583 Local<String> string = String::NewExternal( 584 new TestAsciiResource(i::StrDup(one_byte_string))); 585 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 586 // Trigger GCs so that the newly allocated string moves to old gen. 587 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now 588 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now 589 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring); 590 CHECK(isymbol->IsSymbol()); 591 } 592 i::Heap::CollectAllGarbage(false); 593 i::Heap::CollectAllGarbage(false); 594} 595 596 597THREADED_TEST(ScavengeExternalString) { 598 TestResource::dispose_count = 0; 599 bool in_new_space = false; 600 { 601 v8::HandleScope scope; 602 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 603 Local<String> string = 604 String::NewExternal(new TestResource(two_byte_string)); 605 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 606 i::Heap::CollectGarbage(i::NEW_SPACE); 607 in_new_space = i::Heap::InNewSpace(*istring); 608 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring)); 609 CHECK_EQ(0, TestResource::dispose_count); 610 } 611 i::Heap::CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); 612 CHECK_EQ(1, TestResource::dispose_count); 613} 614 615 616THREADED_TEST(ScavengeExternalAsciiString) { 617 TestAsciiResource::dispose_count = 0; 618 bool in_new_space = false; 619 { 620 v8::HandleScope scope; 621 const char* one_byte_string = "test string"; 622 Local<String> string = String::NewExternal( 623 new TestAsciiResource(i::StrDup(one_byte_string))); 624 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 625 i::Heap::CollectGarbage(i::NEW_SPACE); 626 in_new_space = i::Heap::InNewSpace(*istring); 627 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring)); 628 CHECK_EQ(0, TestAsciiResource::dispose_count); 629 } 630 i::Heap::CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); 631 CHECK_EQ(1, TestAsciiResource::dispose_count); 632} 633 634 635class TestAsciiResourceWithDisposeControl: public TestAsciiResource { 636 public: 637 static int dispose_calls; 638 639 TestAsciiResourceWithDisposeControl(const char* data, bool dispose) 640 : TestAsciiResource(data), 641 dispose_(dispose) { } 642 643 void Dispose() { 644 ++dispose_calls; 645 if (dispose_) delete this; 646 } 647 private: 648 bool dispose_; 649}; 650 651 652int TestAsciiResourceWithDisposeControl::dispose_calls = 0; 653 654 655TEST(ExternalStringWithDisposeHandling) { 656 const char* c_source = "1 + 2 * 3"; 657 658 // Use a stack allocated external string resource allocated object. 659 TestAsciiResource::dispose_count = 0; 660 TestAsciiResourceWithDisposeControl::dispose_calls = 0; 661 TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false); 662 { 663 v8::HandleScope scope; 664 LocalContext env; 665 Local<String> source = String::NewExternal(&res_stack); 666 Local<Script> script = Script::Compile(source); 667 Local<Value> value = script->Run(); 668 CHECK(value->IsNumber()); 669 CHECK_EQ(7, value->Int32Value()); 670 i::Heap::CollectAllGarbage(false); 671 CHECK_EQ(0, TestAsciiResource::dispose_count); 672 } 673 i::CompilationCache::Clear(); 674 i::Heap::CollectAllGarbage(false); 675 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); 676 CHECK_EQ(0, TestAsciiResource::dispose_count); 677 678 // Use a heap allocated external string resource allocated object. 679 TestAsciiResource::dispose_count = 0; 680 TestAsciiResourceWithDisposeControl::dispose_calls = 0; 681 TestAsciiResource* res_heap = 682 new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true); 683 { 684 v8::HandleScope scope; 685 LocalContext env; 686 Local<String> source = String::NewExternal(res_heap); 687 Local<Script> script = Script::Compile(source); 688 Local<Value> value = script->Run(); 689 CHECK(value->IsNumber()); 690 CHECK_EQ(7, value->Int32Value()); 691 i::Heap::CollectAllGarbage(false); 692 CHECK_EQ(0, TestAsciiResource::dispose_count); 693 } 694 i::CompilationCache::Clear(); 695 i::Heap::CollectAllGarbage(false); 696 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); 697 CHECK_EQ(1, TestAsciiResource::dispose_count); 698} 699 700 701THREADED_TEST(StringConcat) { 702 { 703 v8::HandleScope scope; 704 LocalContext env; 705 const char* one_byte_string_1 = "function a_times_t"; 706 const char* two_byte_string_1 = "wo_plus_b(a, b) {return "; 707 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + "; 708 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + "; 709 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 710 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 711 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);"; 712 Local<String> left = v8_str(one_byte_string_1); 713 714 uint16_t* two_byte_source = AsciiToTwoByteString(two_byte_string_1); 715 Local<String> right = String::New(two_byte_source); 716 i::DeleteArray(two_byte_source); 717 718 Local<String> source = String::Concat(left, right); 719 right = String::NewExternal( 720 new TestAsciiResource(i::StrDup(one_byte_extern_1))); 721 source = String::Concat(source, right); 722 right = String::NewExternal( 723 new TestResource(AsciiToTwoByteString(two_byte_extern_1))); 724 source = String::Concat(source, right); 725 right = v8_str(one_byte_string_2); 726 source = String::Concat(source, right); 727 728 two_byte_source = AsciiToTwoByteString(two_byte_string_2); 729 right = String::New(two_byte_source); 730 i::DeleteArray(two_byte_source); 731 732 source = String::Concat(source, right); 733 right = String::NewExternal( 734 new TestResource(AsciiToTwoByteString(two_byte_extern_2))); 735 source = String::Concat(source, right); 736 Local<Script> script = Script::Compile(source); 737 Local<Value> value = script->Run(); 738 CHECK(value->IsNumber()); 739 CHECK_EQ(68, value->Int32Value()); 740 } 741 i::CompilationCache::Clear(); 742 i::Heap::CollectAllGarbage(false); 743 i::Heap::CollectAllGarbage(false); 744} 745 746 747THREADED_TEST(GlobalProperties) { 748 v8::HandleScope scope; 749 LocalContext env; 750 v8::Handle<v8::Object> global = env->Global(); 751 global->Set(v8_str("pi"), v8_num(3.1415926)); 752 Local<Value> pi = global->Get(v8_str("pi")); 753 CHECK_EQ(3.1415926, pi->NumberValue()); 754} 755 756 757static v8::Handle<Value> handle_call(const v8::Arguments& args) { 758 ApiTestFuzzer::Fuzz(); 759 return v8_num(102); 760} 761 762 763static v8::Handle<Value> construct_call(const v8::Arguments& args) { 764 ApiTestFuzzer::Fuzz(); 765 args.This()->Set(v8_str("x"), v8_num(1)); 766 args.This()->Set(v8_str("y"), v8_num(2)); 767 return args.This(); 768} 769 770static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) { 771 ApiTestFuzzer::Fuzz(); 772 return v8_num(239); 773} 774 775 776THREADED_TEST(FunctionTemplate) { 777 v8::HandleScope scope; 778 LocalContext env; 779 { 780 Local<v8::FunctionTemplate> fun_templ = 781 v8::FunctionTemplate::New(handle_call); 782 Local<Function> fun = fun_templ->GetFunction(); 783 env->Global()->Set(v8_str("obj"), fun); 784 Local<Script> script = v8_compile("obj()"); 785 CHECK_EQ(102, script->Run()->Int32Value()); 786 } 787 // Use SetCallHandler to initialize a function template, should work like the 788 // previous one. 789 { 790 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 791 fun_templ->SetCallHandler(handle_call); 792 Local<Function> fun = fun_templ->GetFunction(); 793 env->Global()->Set(v8_str("obj"), fun); 794 Local<Script> script = v8_compile("obj()"); 795 CHECK_EQ(102, script->Run()->Int32Value()); 796 } 797 // Test constructor calls. 798 { 799 Local<v8::FunctionTemplate> fun_templ = 800 v8::FunctionTemplate::New(construct_call); 801 fun_templ->SetClassName(v8_str("funky")); 802 fun_templ->InstanceTemplate()->SetAccessor(v8_str("m"), Return239); 803 Local<Function> fun = fun_templ->GetFunction(); 804 env->Global()->Set(v8_str("obj"), fun); 805 Local<Script> script = v8_compile("var s = new obj(); s.x"); 806 CHECK_EQ(1, script->Run()->Int32Value()); 807 808 Local<Value> result = v8_compile("(new obj()).toString()")->Run(); 809 CHECK_EQ(v8_str("[object funky]"), result); 810 811 result = v8_compile("(new obj()).m")->Run(); 812 CHECK_EQ(239, result->Int32Value()); 813 } 814} 815 816 817THREADED_TEST(FindInstanceInPrototypeChain) { 818 v8::HandleScope scope; 819 LocalContext env; 820 821 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New(); 822 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New(); 823 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New(); 824 derived->Inherit(base); 825 826 Local<v8::Function> base_function = base->GetFunction(); 827 Local<v8::Function> derived_function = derived->GetFunction(); 828 Local<v8::Function> other_function = other->GetFunction(); 829 830 Local<v8::Object> base_instance = base_function->NewInstance(); 831 Local<v8::Object> derived_instance = derived_function->NewInstance(); 832 Local<v8::Object> derived_instance2 = derived_function->NewInstance(); 833 Local<v8::Object> other_instance = other_function->NewInstance(); 834 derived_instance2->Set(v8_str("__proto__"), derived_instance); 835 other_instance->Set(v8_str("__proto__"), derived_instance2); 836 837 // base_instance is only an instance of base. 838 CHECK_EQ(base_instance, 839 base_instance->FindInstanceInPrototypeChain(base)); 840 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty()); 841 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 842 843 // derived_instance is an instance of base and derived. 844 CHECK_EQ(derived_instance, 845 derived_instance->FindInstanceInPrototypeChain(base)); 846 CHECK_EQ(derived_instance, 847 derived_instance->FindInstanceInPrototypeChain(derived)); 848 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 849 850 // other_instance is an instance of other and its immediate 851 // prototype derived_instance2 is an instance of base and derived. 852 // Note, derived_instance is an instance of base and derived too, 853 // but it comes after derived_instance2 in the prototype chain of 854 // other_instance. 855 CHECK_EQ(derived_instance2, 856 other_instance->FindInstanceInPrototypeChain(base)); 857 CHECK_EQ(derived_instance2, 858 other_instance->FindInstanceInPrototypeChain(derived)); 859 CHECK_EQ(other_instance, 860 other_instance->FindInstanceInPrototypeChain(other)); 861} 862 863 864THREADED_TEST(TinyInteger) { 865 v8::HandleScope scope; 866 LocalContext env; 867 int32_t value = 239; 868 Local<v8::Integer> value_obj = v8::Integer::New(value); 869 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 870} 871 872 873THREADED_TEST(BigSmiInteger) { 874 v8::HandleScope scope; 875 LocalContext env; 876 int32_t value = i::Smi::kMaxValue; 877 // We cannot add one to a Smi::kMaxValue without wrapping. 878 if (i::kSmiValueSize < 32) { 879 CHECK(i::Smi::IsValid(value)); 880 CHECK(!i::Smi::IsValid(value + 1)); 881 Local<v8::Integer> value_obj = v8::Integer::New(value); 882 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 883 } 884} 885 886 887THREADED_TEST(BigInteger) { 888 v8::HandleScope scope; 889 LocalContext env; 890 // We cannot add one to a Smi::kMaxValue without wrapping. 891 if (i::kSmiValueSize < 32) { 892 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1. 893 // The code will not be run in that case, due to the "if" guard. 894 int32_t value = 895 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1); 896 CHECK(value > i::Smi::kMaxValue); 897 CHECK(!i::Smi::IsValid(value)); 898 Local<v8::Integer> value_obj = v8::Integer::New(value); 899 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 900 } 901} 902 903 904THREADED_TEST(TinyUnsignedInteger) { 905 v8::HandleScope scope; 906 LocalContext env; 907 uint32_t value = 239; 908 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 909 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 910} 911 912 913THREADED_TEST(BigUnsignedSmiInteger) { 914 v8::HandleScope scope; 915 LocalContext env; 916 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue); 917 CHECK(i::Smi::IsValid(value)); 918 CHECK(!i::Smi::IsValid(value + 1)); 919 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 920 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 921} 922 923 924THREADED_TEST(BigUnsignedInteger) { 925 v8::HandleScope scope; 926 LocalContext env; 927 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1; 928 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue)); 929 CHECK(!i::Smi::IsValid(value)); 930 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 931 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 932} 933 934 935THREADED_TEST(OutOfSignedRangeUnsignedInteger) { 936 v8::HandleScope scope; 937 LocalContext env; 938 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1; 939 uint32_t value = INT32_MAX_AS_UINT + 1; 940 CHECK(value > INT32_MAX_AS_UINT); // No overflow. 941 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 942 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 943} 944 945 946THREADED_TEST(Number) { 947 v8::HandleScope scope; 948 LocalContext env; 949 double PI = 3.1415926; 950 Local<v8::Number> pi_obj = v8::Number::New(PI); 951 CHECK_EQ(PI, pi_obj->NumberValue()); 952} 953 954 955THREADED_TEST(ToNumber) { 956 v8::HandleScope scope; 957 LocalContext env; 958 Local<String> str = v8_str("3.1415926"); 959 CHECK_EQ(3.1415926, str->NumberValue()); 960 v8::Handle<v8::Boolean> t = v8::True(); 961 CHECK_EQ(1.0, t->NumberValue()); 962 v8::Handle<v8::Boolean> f = v8::False(); 963 CHECK_EQ(0.0, f->NumberValue()); 964} 965 966 967THREADED_TEST(Date) { 968 v8::HandleScope scope; 969 LocalContext env; 970 double PI = 3.1415926; 971 Local<Value> date_obj = v8::Date::New(PI); 972 CHECK_EQ(3.0, date_obj->NumberValue()); 973} 974 975 976THREADED_TEST(Boolean) { 977 v8::HandleScope scope; 978 LocalContext env; 979 v8::Handle<v8::Boolean> t = v8::True(); 980 CHECK(t->Value()); 981 v8::Handle<v8::Boolean> f = v8::False(); 982 CHECK(!f->Value()); 983 v8::Handle<v8::Primitive> u = v8::Undefined(); 984 CHECK(!u->BooleanValue()); 985 v8::Handle<v8::Primitive> n = v8::Null(); 986 CHECK(!n->BooleanValue()); 987 v8::Handle<String> str1 = v8_str(""); 988 CHECK(!str1->BooleanValue()); 989 v8::Handle<String> str2 = v8_str("x"); 990 CHECK(str2->BooleanValue()); 991 CHECK(!v8::Number::New(0)->BooleanValue()); 992 CHECK(v8::Number::New(-1)->BooleanValue()); 993 CHECK(v8::Number::New(1)->BooleanValue()); 994 CHECK(v8::Number::New(42)->BooleanValue()); 995 CHECK(!v8_compile("NaN")->Run()->BooleanValue()); 996} 997 998 999static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) { 1000 ApiTestFuzzer::Fuzz(); 1001 return v8_num(13.4); 1002} 1003 1004 1005static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) { 1006 ApiTestFuzzer::Fuzz(); 1007 return v8_num(876); 1008} 1009 1010 1011THREADED_TEST(GlobalPrototype) { 1012 v8::HandleScope scope; 1013 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 1014 func_templ->PrototypeTemplate()->Set( 1015 "dummy", 1016 v8::FunctionTemplate::New(DummyCallHandler)); 1017 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate(); 1018 templ->Set("x", v8_num(200)); 1019 templ->SetAccessor(v8_str("m"), GetM); 1020 LocalContext env(0, templ); 1021 v8::Handle<v8::Object> obj = env->Global(); 1022 v8::Handle<Script> script = v8_compile("dummy()"); 1023 v8::Handle<Value> result = script->Run(); 1024 CHECK_EQ(13.4, result->NumberValue()); 1025 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value()); 1026 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value()); 1027} 1028 1029 1030THREADED_TEST(ObjectTemplate) { 1031 v8::HandleScope scope; 1032 Local<ObjectTemplate> templ1 = ObjectTemplate::New(); 1033 templ1->Set("x", v8_num(10)); 1034 templ1->Set("y", v8_num(13)); 1035 LocalContext env; 1036 Local<v8::Object> instance1 = templ1->NewInstance(); 1037 env->Global()->Set(v8_str("p"), instance1); 1038 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue()); 1039 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue()); 1040 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); 1041 fun->PrototypeTemplate()->Set("nirk", v8_num(123)); 1042 Local<ObjectTemplate> templ2 = fun->InstanceTemplate(); 1043 templ2->Set("a", v8_num(12)); 1044 templ2->Set("b", templ1); 1045 Local<v8::Object> instance2 = templ2->NewInstance(); 1046 env->Global()->Set(v8_str("q"), instance2); 1047 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue()); 1048 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue()); 1049 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue()); 1050 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue()); 1051} 1052 1053 1054static v8::Handle<Value> GetFlabby(const v8::Arguments& args) { 1055 ApiTestFuzzer::Fuzz(); 1056 return v8_num(17.2); 1057} 1058 1059 1060static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) { 1061 ApiTestFuzzer::Fuzz(); 1062 return v8_num(15.2); 1063} 1064 1065 1066THREADED_TEST(DescriptorInheritance) { 1067 v8::HandleScope scope; 1068 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New(); 1069 super->PrototypeTemplate()->Set("flabby", 1070 v8::FunctionTemplate::New(GetFlabby)); 1071 super->PrototypeTemplate()->Set("PI", v8_num(3.14)); 1072 1073 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd); 1074 1075 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New(); 1076 base1->Inherit(super); 1077 base1->PrototypeTemplate()->Set("v1", v8_num(20.1)); 1078 1079 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New(); 1080 base2->Inherit(super); 1081 base2->PrototypeTemplate()->Set("v2", v8_num(10.1)); 1082 1083 LocalContext env; 1084 1085 env->Global()->Set(v8_str("s"), super->GetFunction()); 1086 env->Global()->Set(v8_str("base1"), base1->GetFunction()); 1087 env->Global()->Set(v8_str("base2"), base2->GetFunction()); 1088 1089 // Checks right __proto__ chain. 1090 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue()); 1091 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue()); 1092 1093 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue()); 1094 1095 // Instance accessor should not be visible on function object or its prototype 1096 CHECK(CompileRun("s.knurd == undefined")->BooleanValue()); 1097 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue()); 1098 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue()); 1099 1100 env->Global()->Set(v8_str("obj"), 1101 base1->GetFunction()->NewInstance()); 1102 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue()); 1103 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue()); 1104 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue()); 1105 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue()); 1106 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue()); 1107 1108 env->Global()->Set(v8_str("obj2"), 1109 base2->GetFunction()->NewInstance()); 1110 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue()); 1111 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue()); 1112 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue()); 1113 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue()); 1114 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue()); 1115 1116 // base1 and base2 cannot cross reference to each's prototype 1117 CHECK(v8_compile("obj.v2")->Run()->IsUndefined()); 1118 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined()); 1119} 1120 1121 1122int echo_named_call_count; 1123 1124 1125static v8::Handle<Value> EchoNamedProperty(Local<String> name, 1126 const AccessorInfo& info) { 1127 ApiTestFuzzer::Fuzz(); 1128 CHECK_EQ(v8_str("data"), info.Data()); 1129 echo_named_call_count++; 1130 return name; 1131} 1132 1133 1134THREADED_TEST(NamedPropertyHandlerGetter) { 1135 echo_named_call_count = 0; 1136 v8::HandleScope scope; 1137 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1138 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty, 1139 0, 0, 0, 0, 1140 v8_str("data")); 1141 LocalContext env; 1142 env->Global()->Set(v8_str("obj"), 1143 templ->GetFunction()->NewInstance()); 1144 CHECK_EQ(echo_named_call_count, 0); 1145 v8_compile("obj.x")->Run(); 1146 CHECK_EQ(echo_named_call_count, 1); 1147 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;"; 1148 v8::Handle<Value> str = CompileRun(code); 1149 String::AsciiValue value(str); 1150 CHECK_EQ(*value, "oddlepoddle"); 1151 // Check default behavior 1152 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10); 1153 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue()); 1154 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue()); 1155} 1156 1157 1158int echo_indexed_call_count = 0; 1159 1160 1161static v8::Handle<Value> EchoIndexedProperty(uint32_t index, 1162 const AccessorInfo& info) { 1163 ApiTestFuzzer::Fuzz(); 1164 CHECK_EQ(v8_num(637), info.Data()); 1165 echo_indexed_call_count++; 1166 return v8_num(index); 1167} 1168 1169 1170THREADED_TEST(IndexedPropertyHandlerGetter) { 1171 v8::HandleScope scope; 1172 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1173 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty, 1174 0, 0, 0, 0, 1175 v8_num(637)); 1176 LocalContext env; 1177 env->Global()->Set(v8_str("obj"), 1178 templ->GetFunction()->NewInstance()); 1179 Local<Script> script = v8_compile("obj[900]"); 1180 CHECK_EQ(script->Run()->Int32Value(), 900); 1181} 1182 1183 1184v8::Handle<v8::Object> bottom; 1185 1186static v8::Handle<Value> CheckThisIndexedPropertyHandler( 1187 uint32_t index, 1188 const AccessorInfo& info) { 1189 ApiTestFuzzer::Fuzz(); 1190 CHECK(info.This()->Equals(bottom)); 1191 return v8::Handle<Value>(); 1192} 1193 1194static v8::Handle<Value> CheckThisNamedPropertyHandler( 1195 Local<String> name, 1196 const AccessorInfo& info) { 1197 ApiTestFuzzer::Fuzz(); 1198 CHECK(info.This()->Equals(bottom)); 1199 return v8::Handle<Value>(); 1200} 1201 1202 1203v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index, 1204 Local<Value> value, 1205 const AccessorInfo& info) { 1206 ApiTestFuzzer::Fuzz(); 1207 CHECK(info.This()->Equals(bottom)); 1208 return v8::Handle<Value>(); 1209} 1210 1211 1212v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property, 1213 Local<Value> value, 1214 const AccessorInfo& info) { 1215 ApiTestFuzzer::Fuzz(); 1216 CHECK(info.This()->Equals(bottom)); 1217 return v8::Handle<Value>(); 1218} 1219 1220v8::Handle<v8::Integer> CheckThisIndexedPropertyQuery( 1221 uint32_t index, 1222 const AccessorInfo& info) { 1223 ApiTestFuzzer::Fuzz(); 1224 CHECK(info.This()->Equals(bottom)); 1225 return v8::Handle<v8::Integer>(); 1226} 1227 1228 1229v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property, 1230 const AccessorInfo& info) { 1231 ApiTestFuzzer::Fuzz(); 1232 CHECK(info.This()->Equals(bottom)); 1233 return v8::Handle<v8::Integer>(); 1234} 1235 1236 1237v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter( 1238 uint32_t index, 1239 const AccessorInfo& info) { 1240 ApiTestFuzzer::Fuzz(); 1241 CHECK(info.This()->Equals(bottom)); 1242 return v8::Handle<v8::Boolean>(); 1243} 1244 1245 1246v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter( 1247 Local<String> property, 1248 const AccessorInfo& info) { 1249 ApiTestFuzzer::Fuzz(); 1250 CHECK(info.This()->Equals(bottom)); 1251 return v8::Handle<v8::Boolean>(); 1252} 1253 1254 1255v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator( 1256 const AccessorInfo& info) { 1257 ApiTestFuzzer::Fuzz(); 1258 CHECK(info.This()->Equals(bottom)); 1259 return v8::Handle<v8::Array>(); 1260} 1261 1262 1263v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator( 1264 const AccessorInfo& info) { 1265 ApiTestFuzzer::Fuzz(); 1266 CHECK(info.This()->Equals(bottom)); 1267 return v8::Handle<v8::Array>(); 1268} 1269 1270 1271THREADED_TEST(PropertyHandlerInPrototype) { 1272 v8::HandleScope scope; 1273 LocalContext env; 1274 1275 // Set up a prototype chain with three interceptors. 1276 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1277 templ->InstanceTemplate()->SetIndexedPropertyHandler( 1278 CheckThisIndexedPropertyHandler, 1279 CheckThisIndexedPropertySetter, 1280 CheckThisIndexedPropertyQuery, 1281 CheckThisIndexedPropertyDeleter, 1282 CheckThisIndexedPropertyEnumerator); 1283 1284 templ->InstanceTemplate()->SetNamedPropertyHandler( 1285 CheckThisNamedPropertyHandler, 1286 CheckThisNamedPropertySetter, 1287 CheckThisNamedPropertyQuery, 1288 CheckThisNamedPropertyDeleter, 1289 CheckThisNamedPropertyEnumerator); 1290 1291 bottom = templ->GetFunction()->NewInstance(); 1292 Local<v8::Object> top = templ->GetFunction()->NewInstance(); 1293 Local<v8::Object> middle = templ->GetFunction()->NewInstance(); 1294 1295 bottom->Set(v8_str("__proto__"), middle); 1296 middle->Set(v8_str("__proto__"), top); 1297 env->Global()->Set(v8_str("obj"), bottom); 1298 1299 // Indexed and named get. 1300 Script::Compile(v8_str("obj[0]"))->Run(); 1301 Script::Compile(v8_str("obj.x"))->Run(); 1302 1303 // Indexed and named set. 1304 Script::Compile(v8_str("obj[1] = 42"))->Run(); 1305 Script::Compile(v8_str("obj.y = 42"))->Run(); 1306 1307 // Indexed and named query. 1308 Script::Compile(v8_str("0 in obj"))->Run(); 1309 Script::Compile(v8_str("'x' in obj"))->Run(); 1310 1311 // Indexed and named deleter. 1312 Script::Compile(v8_str("delete obj[0]"))->Run(); 1313 Script::Compile(v8_str("delete obj.x"))->Run(); 1314 1315 // Enumerators. 1316 Script::Compile(v8_str("for (var p in obj) ;"))->Run(); 1317} 1318 1319 1320static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key, 1321 const AccessorInfo& info) { 1322 ApiTestFuzzer::Fuzz(); 1323 if (v8_str("pre")->Equals(key)) { 1324 return v8_str("PrePropertyHandler: pre"); 1325 } 1326 return v8::Handle<String>(); 1327} 1328 1329 1330static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key, 1331 const AccessorInfo&) { 1332 if (v8_str("pre")->Equals(key)) { 1333 return v8::Integer::New(v8::None); 1334 } 1335 1336 return v8::Handle<v8::Integer>(); // do not intercept the call 1337} 1338 1339 1340THREADED_TEST(PrePropertyHandler) { 1341 v8::HandleScope scope; 1342 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(); 1343 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet, 1344 0, 1345 PrePropertyHandlerQuery); 1346 LocalContext env(NULL, desc->InstanceTemplate()); 1347 Script::Compile(v8_str( 1348 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run(); 1349 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run(); 1350 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre); 1351 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run(); 1352 CHECK_EQ(v8_str("Object: on"), result_on); 1353 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run(); 1354 CHECK(result_post.IsEmpty()); 1355} 1356 1357 1358THREADED_TEST(UndefinedIsNotEnumerable) { 1359 v8::HandleScope scope; 1360 LocalContext env; 1361 v8::Handle<Value> result = Script::Compile(v8_str( 1362 "this.propertyIsEnumerable(undefined)"))->Run(); 1363 CHECK(result->IsFalse()); 1364} 1365 1366 1367v8::Handle<Script> call_recursively_script; 1368static const int kTargetRecursionDepth = 200; // near maximum 1369 1370 1371static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) { 1372 ApiTestFuzzer::Fuzz(); 1373 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1374 if (depth == kTargetRecursionDepth) return v8::Undefined(); 1375 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1376 return call_recursively_script->Run(); 1377} 1378 1379 1380static v8::Handle<Value> CallFunctionRecursivelyCall( 1381 const v8::Arguments& args) { 1382 ApiTestFuzzer::Fuzz(); 1383 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1384 if (depth == kTargetRecursionDepth) { 1385 printf("[depth = %d]\n", depth); 1386 return v8::Undefined(); 1387 } 1388 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1389 v8::Handle<Value> function = 1390 args.This()->Get(v8_str("callFunctionRecursively")); 1391 return function.As<Function>()->Call(args.This(), 0, NULL); 1392} 1393 1394 1395THREADED_TEST(DeepCrossLanguageRecursion) { 1396 v8::HandleScope scope; 1397 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 1398 global->Set(v8_str("callScriptRecursively"), 1399 v8::FunctionTemplate::New(CallScriptRecursivelyCall)); 1400 global->Set(v8_str("callFunctionRecursively"), 1401 v8::FunctionTemplate::New(CallFunctionRecursivelyCall)); 1402 LocalContext env(NULL, global); 1403 1404 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1405 call_recursively_script = v8_compile("callScriptRecursively()"); 1406 v8::Handle<Value> result = call_recursively_script->Run(); 1407 call_recursively_script = v8::Handle<Script>(); 1408 1409 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1410 Script::Compile(v8_str("callFunctionRecursively()"))->Run(); 1411} 1412 1413 1414static v8::Handle<Value> 1415 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) { 1416 ApiTestFuzzer::Fuzz(); 1417 return v8::ThrowException(key); 1418} 1419 1420 1421static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key, 1422 Local<Value>, 1423 const AccessorInfo&) { 1424 v8::ThrowException(key); 1425 return v8::Undefined(); // not the same as v8::Handle<v8::Value>() 1426} 1427 1428 1429THREADED_TEST(CallbackExceptionRegression) { 1430 v8::HandleScope scope; 1431 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 1432 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet, 1433 ThrowingPropertyHandlerSet); 1434 LocalContext env; 1435 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 1436 v8::Handle<Value> otto = Script::Compile(v8_str( 1437 "try { with (obj) { otto; } } catch (e) { e; }"))->Run(); 1438 CHECK_EQ(v8_str("otto"), otto); 1439 v8::Handle<Value> netto = Script::Compile(v8_str( 1440 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run(); 1441 CHECK_EQ(v8_str("netto"), netto); 1442} 1443 1444 1445THREADED_TEST(FunctionPrototype) { 1446 v8::HandleScope scope; 1447 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New(); 1448 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321)); 1449 LocalContext env; 1450 env->Global()->Set(v8_str("Foo"), Foo->GetFunction()); 1451 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak")); 1452 CHECK_EQ(script->Run()->Int32Value(), 321); 1453} 1454 1455 1456THREADED_TEST(InternalFields) { 1457 v8::HandleScope scope; 1458 LocalContext env; 1459 1460 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1461 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1462 instance_templ->SetInternalFieldCount(1); 1463 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1464 CHECK_EQ(1, obj->InternalFieldCount()); 1465 CHECK(obj->GetInternalField(0)->IsUndefined()); 1466 obj->SetInternalField(0, v8_num(17)); 1467 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value()); 1468} 1469 1470 1471THREADED_TEST(GlobalObjectInternalFields) { 1472 v8::HandleScope scope; 1473 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 1474 global_template->SetInternalFieldCount(1); 1475 LocalContext env(NULL, global_template); 1476 v8::Handle<v8::Object> global_proxy = env->Global(); 1477 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); 1478 CHECK_EQ(1, global->InternalFieldCount()); 1479 CHECK(global->GetInternalField(0)->IsUndefined()); 1480 global->SetInternalField(0, v8_num(17)); 1481 CHECK_EQ(17, global->GetInternalField(0)->Int32Value()); 1482} 1483 1484 1485THREADED_TEST(InternalFieldsNativePointers) { 1486 v8::HandleScope scope; 1487 LocalContext env; 1488 1489 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1490 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1491 instance_templ->SetInternalFieldCount(1); 1492 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1493 CHECK_EQ(1, obj->InternalFieldCount()); 1494 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1495 1496 char* data = new char[100]; 1497 1498 void* aligned = data; 1499 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1)); 1500 void* unaligned = data + 1; 1501 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1)); 1502 1503 // Check reading and writing aligned pointers. 1504 obj->SetPointerInInternalField(0, aligned); 1505 i::Heap::CollectAllGarbage(false); 1506 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1507 1508 // Check reading and writing unaligned pointers. 1509 obj->SetPointerInInternalField(0, unaligned); 1510 i::Heap::CollectAllGarbage(false); 1511 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1512 1513 delete[] data; 1514} 1515 1516 1517THREADED_TEST(InternalFieldsNativePointersAndExternal) { 1518 v8::HandleScope scope; 1519 LocalContext env; 1520 1521 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1522 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1523 instance_templ->SetInternalFieldCount(1); 1524 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1525 CHECK_EQ(1, obj->InternalFieldCount()); 1526 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1527 1528 char* data = new char[100]; 1529 1530 void* aligned = data; 1531 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1)); 1532 void* unaligned = data + 1; 1533 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1)); 1534 1535 obj->SetPointerInInternalField(0, aligned); 1536 i::Heap::CollectAllGarbage(false); 1537 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0))); 1538 1539 obj->SetPointerInInternalField(0, unaligned); 1540 i::Heap::CollectAllGarbage(false); 1541 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0))); 1542 1543 obj->SetInternalField(0, v8::External::Wrap(aligned)); 1544 i::Heap::CollectAllGarbage(false); 1545 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1546 1547 obj->SetInternalField(0, v8::External::Wrap(unaligned)); 1548 i::Heap::CollectAllGarbage(false); 1549 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1550 1551 delete[] data; 1552} 1553 1554 1555THREADED_TEST(IdentityHash) { 1556 v8::HandleScope scope; 1557 LocalContext env; 1558 1559 // Ensure that the test starts with an fresh heap to test whether the hash 1560 // code is based on the address. 1561 i::Heap::CollectAllGarbage(false); 1562 Local<v8::Object> obj = v8::Object::New(); 1563 int hash = obj->GetIdentityHash(); 1564 int hash1 = obj->GetIdentityHash(); 1565 CHECK_EQ(hash, hash1); 1566 int hash2 = v8::Object::New()->GetIdentityHash(); 1567 // Since the identity hash is essentially a random number two consecutive 1568 // objects should not be assigned the same hash code. If the test below fails 1569 // the random number generator should be evaluated. 1570 CHECK_NE(hash, hash2); 1571 i::Heap::CollectAllGarbage(false); 1572 int hash3 = v8::Object::New()->GetIdentityHash(); 1573 // Make sure that the identity hash is not based on the initial address of 1574 // the object alone. If the test below fails the random number generator 1575 // should be evaluated. 1576 CHECK_NE(hash, hash3); 1577 int hash4 = obj->GetIdentityHash(); 1578 CHECK_EQ(hash, hash4); 1579} 1580 1581 1582THREADED_TEST(HiddenProperties) { 1583 v8::HandleScope scope; 1584 LocalContext env; 1585 1586 v8::Local<v8::Object> obj = v8::Object::New(); 1587 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 1588 v8::Local<v8::String> empty = v8_str(""); 1589 v8::Local<v8::String> prop_name = v8_str("prop_name"); 1590 1591 i::Heap::CollectAllGarbage(false); 1592 1593 // Make sure delete of a non-existent hidden value works 1594 CHECK(obj->DeleteHiddenValue(key)); 1595 1596 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503))); 1597 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value()); 1598 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002))); 1599 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1600 1601 i::Heap::CollectAllGarbage(false); 1602 1603 // Make sure we do not find the hidden property. 1604 CHECK(!obj->Has(empty)); 1605 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1606 CHECK(obj->Get(empty)->IsUndefined()); 1607 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1608 CHECK(obj->Set(empty, v8::Integer::New(2003))); 1609 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1610 CHECK_EQ(2003, obj->Get(empty)->Int32Value()); 1611 1612 i::Heap::CollectAllGarbage(false); 1613 1614 // Add another property and delete it afterwards to force the object in 1615 // slow case. 1616 CHECK(obj->Set(prop_name, v8::Integer::New(2008))); 1617 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1618 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value()); 1619 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1620 CHECK(obj->Delete(prop_name)); 1621 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1622 1623 i::Heap::CollectAllGarbage(false); 1624 1625 CHECK(obj->DeleteHiddenValue(key)); 1626 CHECK(obj->GetHiddenValue(key).IsEmpty()); 1627} 1628 1629 1630static bool interceptor_for_hidden_properties_called; 1631static v8::Handle<Value> InterceptorForHiddenProperties( 1632 Local<String> name, const AccessorInfo& info) { 1633 interceptor_for_hidden_properties_called = true; 1634 return v8::Handle<Value>(); 1635} 1636 1637 1638THREADED_TEST(HiddenPropertiesWithInterceptors) { 1639 v8::HandleScope scope; 1640 LocalContext context; 1641 1642 interceptor_for_hidden_properties_called = false; 1643 1644 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 1645 1646 // Associate an interceptor with an object and start setting hidden values. 1647 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 1648 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 1649 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties); 1650 Local<v8::Function> function = fun_templ->GetFunction(); 1651 Local<v8::Object> obj = function->NewInstance(); 1652 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302))); 1653 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value()); 1654 CHECK(!interceptor_for_hidden_properties_called); 1655} 1656 1657 1658THREADED_TEST(External) { 1659 v8::HandleScope scope; 1660 int x = 3; 1661 Local<v8::External> ext = v8::External::New(&x); 1662 LocalContext env; 1663 env->Global()->Set(v8_str("ext"), ext); 1664 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run(); 1665 v8::Handle<v8::External> reext = reext_obj.As<v8::External>(); 1666 int* ptr = static_cast<int*>(reext->Value()); 1667 CHECK_EQ(x, 3); 1668 *ptr = 10; 1669 CHECK_EQ(x, 10); 1670 1671 // Make sure unaligned pointers are wrapped properly. 1672 char* data = i::StrDup("0123456789"); 1673 Local<v8::Value> zero = v8::External::Wrap(&data[0]); 1674 Local<v8::Value> one = v8::External::Wrap(&data[1]); 1675 Local<v8::Value> two = v8::External::Wrap(&data[2]); 1676 Local<v8::Value> three = v8::External::Wrap(&data[3]); 1677 1678 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero)); 1679 CHECK_EQ('0', *char_ptr); 1680 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one)); 1681 CHECK_EQ('1', *char_ptr); 1682 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two)); 1683 CHECK_EQ('2', *char_ptr); 1684 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three)); 1685 CHECK_EQ('3', *char_ptr); 1686 i::DeleteArray(data); 1687} 1688 1689 1690THREADED_TEST(GlobalHandle) { 1691 v8::Persistent<String> global; 1692 { 1693 v8::HandleScope scope; 1694 Local<String> str = v8_str("str"); 1695 global = v8::Persistent<String>::New(str); 1696 } 1697 CHECK_EQ(global->Length(), 3); 1698 global.Dispose(); 1699} 1700 1701 1702THREADED_TEST(ScriptException) { 1703 v8::HandleScope scope; 1704 LocalContext env; 1705 Local<Script> script = Script::Compile(v8_str("throw 'panama!';")); 1706 v8::TryCatch try_catch; 1707 Local<Value> result = script->Run(); 1708 CHECK(result.IsEmpty()); 1709 CHECK(try_catch.HasCaught()); 1710 String::AsciiValue exception_value(try_catch.Exception()); 1711 CHECK_EQ(*exception_value, "panama!"); 1712} 1713 1714 1715bool message_received; 1716 1717 1718static void check_message(v8::Handle<v8::Message> message, 1719 v8::Handle<Value> data) { 1720 CHECK_EQ(5.76, data->NumberValue()); 1721 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue()); 1722 CHECK_EQ(7.56, message->GetScriptData()->NumberValue()); 1723 message_received = true; 1724} 1725 1726 1727THREADED_TEST(MessageHandlerData) { 1728 message_received = false; 1729 v8::HandleScope scope; 1730 CHECK(!message_received); 1731 v8::V8::AddMessageListener(check_message, v8_num(5.76)); 1732 LocalContext context; 1733 v8::ScriptOrigin origin = 1734 v8::ScriptOrigin(v8_str("6.75")); 1735 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"), 1736 &origin); 1737 script->SetData(v8_str("7.56")); 1738 script->Run(); 1739 CHECK(message_received); 1740 // clear out the message listener 1741 v8::V8::RemoveMessageListeners(check_message); 1742} 1743 1744 1745THREADED_TEST(GetSetProperty) { 1746 v8::HandleScope scope; 1747 LocalContext context; 1748 context->Global()->Set(v8_str("foo"), v8_num(14)); 1749 context->Global()->Set(v8_str("12"), v8_num(92)); 1750 context->Global()->Set(v8::Integer::New(16), v8_num(32)); 1751 context->Global()->Set(v8_num(13), v8_num(56)); 1752 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run(); 1753 CHECK_EQ(14, foo->Int32Value()); 1754 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run(); 1755 CHECK_EQ(92, twelve->Int32Value()); 1756 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run(); 1757 CHECK_EQ(32, sixteen->Int32Value()); 1758 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run(); 1759 CHECK_EQ(56, thirteen->Int32Value()); 1760 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value()); 1761 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value()); 1762 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value()); 1763 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value()); 1764 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value()); 1765 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value()); 1766 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value()); 1767 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value()); 1768 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value()); 1769} 1770 1771 1772THREADED_TEST(PropertyAttributes) { 1773 v8::HandleScope scope; 1774 LocalContext context; 1775 // read-only 1776 Local<String> prop = v8_str("read_only"); 1777 context->Global()->Set(prop, v8_num(7), v8::ReadOnly); 1778 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1779 Script::Compile(v8_str("read_only = 9"))->Run(); 1780 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1781 context->Global()->Set(prop, v8_num(10)); 1782 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1783 // dont-delete 1784 prop = v8_str("dont_delete"); 1785 context->Global()->Set(prop, v8_num(13), v8::DontDelete); 1786 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 1787 Script::Compile(v8_str("delete dont_delete"))->Run(); 1788 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 1789} 1790 1791 1792THREADED_TEST(Array) { 1793 v8::HandleScope scope; 1794 LocalContext context; 1795 Local<v8::Array> array = v8::Array::New(); 1796 CHECK_EQ(0, array->Length()); 1797 CHECK(array->Get(0)->IsUndefined()); 1798 CHECK(!array->Has(0)); 1799 CHECK(array->Get(100)->IsUndefined()); 1800 CHECK(!array->Has(100)); 1801 array->Set(2, v8_num(7)); 1802 CHECK_EQ(3, array->Length()); 1803 CHECK(!array->Has(0)); 1804 CHECK(!array->Has(1)); 1805 CHECK(array->Has(2)); 1806 CHECK_EQ(7, array->Get(2)->Int32Value()); 1807 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run(); 1808 Local<v8::Array> arr = obj.As<v8::Array>(); 1809 CHECK_EQ(3, arr->Length()); 1810 CHECK_EQ(1, arr->Get(0)->Int32Value()); 1811 CHECK_EQ(2, arr->Get(1)->Int32Value()); 1812 CHECK_EQ(3, arr->Get(2)->Int32Value()); 1813} 1814 1815 1816v8::Handle<Value> HandleF(const v8::Arguments& args) { 1817 v8::HandleScope scope; 1818 ApiTestFuzzer::Fuzz(); 1819 Local<v8::Array> result = v8::Array::New(args.Length()); 1820 for (int i = 0; i < args.Length(); i++) 1821 result->Set(i, args[i]); 1822 return scope.Close(result); 1823} 1824 1825 1826THREADED_TEST(Vector) { 1827 v8::HandleScope scope; 1828 Local<ObjectTemplate> global = ObjectTemplate::New(); 1829 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF)); 1830 LocalContext context(0, global); 1831 1832 const char* fun = "f()"; 1833 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>(); 1834 CHECK_EQ(0, a0->Length()); 1835 1836 const char* fun2 = "f(11)"; 1837 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>(); 1838 CHECK_EQ(1, a1->Length()); 1839 CHECK_EQ(11, a1->Get(0)->Int32Value()); 1840 1841 const char* fun3 = "f(12, 13)"; 1842 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>(); 1843 CHECK_EQ(2, a2->Length()); 1844 CHECK_EQ(12, a2->Get(0)->Int32Value()); 1845 CHECK_EQ(13, a2->Get(1)->Int32Value()); 1846 1847 const char* fun4 = "f(14, 15, 16)"; 1848 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>(); 1849 CHECK_EQ(3, a3->Length()); 1850 CHECK_EQ(14, a3->Get(0)->Int32Value()); 1851 CHECK_EQ(15, a3->Get(1)->Int32Value()); 1852 CHECK_EQ(16, a3->Get(2)->Int32Value()); 1853 1854 const char* fun5 = "f(17, 18, 19, 20)"; 1855 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>(); 1856 CHECK_EQ(4, a4->Length()); 1857 CHECK_EQ(17, a4->Get(0)->Int32Value()); 1858 CHECK_EQ(18, a4->Get(1)->Int32Value()); 1859 CHECK_EQ(19, a4->Get(2)->Int32Value()); 1860 CHECK_EQ(20, a4->Get(3)->Int32Value()); 1861} 1862 1863 1864THREADED_TEST(FunctionCall) { 1865 v8::HandleScope scope; 1866 LocalContext context; 1867 CompileRun( 1868 "function Foo() {" 1869 " var result = [];" 1870 " for (var i = 0; i < arguments.length; i++) {" 1871 " result.push(arguments[i]);" 1872 " }" 1873 " return result;" 1874 "}"); 1875 Local<Function> Foo = 1876 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 1877 1878 v8::Handle<Value>* args0 = NULL; 1879 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0)); 1880 CHECK_EQ(0, a0->Length()); 1881 1882 v8::Handle<Value> args1[] = { v8_num(1.1) }; 1883 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1)); 1884 CHECK_EQ(1, a1->Length()); 1885 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 1886 1887 v8::Handle<Value> args2[] = { v8_num(2.2), 1888 v8_num(3.3) }; 1889 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2)); 1890 CHECK_EQ(2, a2->Length()); 1891 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 1892 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 1893 1894 v8::Handle<Value> args3[] = { v8_num(4.4), 1895 v8_num(5.5), 1896 v8_num(6.6) }; 1897 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3)); 1898 CHECK_EQ(3, a3->Length()); 1899 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 1900 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 1901 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 1902 1903 v8::Handle<Value> args4[] = { v8_num(7.7), 1904 v8_num(8.8), 1905 v8_num(9.9), 1906 v8_num(10.11) }; 1907 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4)); 1908 CHECK_EQ(4, a4->Length()); 1909 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 1910 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 1911 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 1912 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 1913} 1914 1915 1916static const char* js_code_causing_out_of_memory = 1917 "var a = new Array(); while(true) a.push(a);"; 1918 1919 1920// These tests run for a long time and prevent us from running tests 1921// that come after them so they cannot run in parallel. 1922TEST(OutOfMemory) { 1923 // It's not possible to read a snapshot into a heap with different dimensions. 1924 if (i::Snapshot::IsEnabled()) return; 1925 // Set heap limits. 1926 static const int K = 1024; 1927 v8::ResourceConstraints constraints; 1928 constraints.set_max_young_space_size(256 * K); 1929 constraints.set_max_old_space_size(4 * K * K); 1930 v8::SetResourceConstraints(&constraints); 1931 1932 // Execute a script that causes out of memory. 1933 v8::HandleScope scope; 1934 LocalContext context; 1935 v8::V8::IgnoreOutOfMemoryException(); 1936 Local<Script> script = 1937 Script::Compile(String::New(js_code_causing_out_of_memory)); 1938 Local<Value> result = script->Run(); 1939 1940 // Check for out of memory state. 1941 CHECK(result.IsEmpty()); 1942 CHECK(context->HasOutOfMemoryException()); 1943} 1944 1945 1946v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) { 1947 ApiTestFuzzer::Fuzz(); 1948 1949 v8::HandleScope scope; 1950 LocalContext context; 1951 Local<Script> script = 1952 Script::Compile(String::New(js_code_causing_out_of_memory)); 1953 Local<Value> result = script->Run(); 1954 1955 // Check for out of memory state. 1956 CHECK(result.IsEmpty()); 1957 CHECK(context->HasOutOfMemoryException()); 1958 1959 return result; 1960} 1961 1962 1963TEST(OutOfMemoryNested) { 1964 // It's not possible to read a snapshot into a heap with different dimensions. 1965 if (i::Snapshot::IsEnabled()) return; 1966 // Set heap limits. 1967 static const int K = 1024; 1968 v8::ResourceConstraints constraints; 1969 constraints.set_max_young_space_size(256 * K); 1970 constraints.set_max_old_space_size(4 * K * K); 1971 v8::SetResourceConstraints(&constraints); 1972 1973 v8::HandleScope scope; 1974 Local<ObjectTemplate> templ = ObjectTemplate::New(); 1975 templ->Set(v8_str("ProvokeOutOfMemory"), 1976 v8::FunctionTemplate::New(ProvokeOutOfMemory)); 1977 LocalContext context(0, templ); 1978 v8::V8::IgnoreOutOfMemoryException(); 1979 Local<Value> result = CompileRun( 1980 "var thrown = false;" 1981 "try {" 1982 " ProvokeOutOfMemory();" 1983 "} catch (e) {" 1984 " thrown = true;" 1985 "}"); 1986 // Check for out of memory state. 1987 CHECK(result.IsEmpty()); 1988 CHECK(context->HasOutOfMemoryException()); 1989} 1990 1991 1992TEST(HugeConsStringOutOfMemory) { 1993 // It's not possible to read a snapshot into a heap with different dimensions. 1994 if (i::Snapshot::IsEnabled()) return; 1995 v8::HandleScope scope; 1996 LocalContext context; 1997 // Set heap limits. 1998 static const int K = 1024; 1999 v8::ResourceConstraints constraints; 2000 constraints.set_max_young_space_size(256 * K); 2001 constraints.set_max_old_space_size(2 * K * K); 2002 v8::SetResourceConstraints(&constraints); 2003 2004 // Execute a script that causes out of memory. 2005 v8::V8::IgnoreOutOfMemoryException(); 2006 2007 // Build huge string. This should fail with out of memory exception. 2008 Local<Value> result = CompileRun( 2009 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();" 2010 "for (var i = 0; i < 22; i++) { str = str + str; }"); 2011 2012 // Check for out of memory state. 2013 CHECK(result.IsEmpty()); 2014 CHECK(context->HasOutOfMemoryException()); 2015} 2016 2017 2018THREADED_TEST(ConstructCall) { 2019 v8::HandleScope scope; 2020 LocalContext context; 2021 CompileRun( 2022 "function Foo() {" 2023 " var result = [];" 2024 " for (var i = 0; i < arguments.length; i++) {" 2025 " result.push(arguments[i]);" 2026 " }" 2027 " return result;" 2028 "}"); 2029 Local<Function> Foo = 2030 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 2031 2032 v8::Handle<Value>* args0 = NULL; 2033 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0)); 2034 CHECK_EQ(0, a0->Length()); 2035 2036 v8::Handle<Value> args1[] = { v8_num(1.1) }; 2037 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1)); 2038 CHECK_EQ(1, a1->Length()); 2039 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 2040 2041 v8::Handle<Value> args2[] = { v8_num(2.2), 2042 v8_num(3.3) }; 2043 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2)); 2044 CHECK_EQ(2, a2->Length()); 2045 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 2046 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 2047 2048 v8::Handle<Value> args3[] = { v8_num(4.4), 2049 v8_num(5.5), 2050 v8_num(6.6) }; 2051 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3)); 2052 CHECK_EQ(3, a3->Length()); 2053 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 2054 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 2055 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 2056 2057 v8::Handle<Value> args4[] = { v8_num(7.7), 2058 v8_num(8.8), 2059 v8_num(9.9), 2060 v8_num(10.11) }; 2061 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4)); 2062 CHECK_EQ(4, a4->Length()); 2063 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 2064 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 2065 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 2066 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 2067} 2068 2069 2070static void CheckUncle(v8::TryCatch* try_catch) { 2071 CHECK(try_catch->HasCaught()); 2072 String::AsciiValue str_value(try_catch->Exception()); 2073 CHECK_EQ(*str_value, "uncle?"); 2074 try_catch->Reset(); 2075} 2076 2077 2078THREADED_TEST(ConversionNumber) { 2079 v8::HandleScope scope; 2080 LocalContext env; 2081 // Very large number. 2082 CompileRun("var obj = Math.pow(2,32) * 1237;"); 2083 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2084 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value()); 2085 CHECK_EQ(0, obj->ToInt32()->Value()); 2086 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned. 2087 // Large number. 2088 CompileRun("var obj = -1234567890123;"); 2089 obj = env->Global()->Get(v8_str("obj")); 2090 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value()); 2091 CHECK_EQ(-1912276171, obj->ToInt32()->Value()); 2092 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT 2093 // Small positive integer. 2094 CompileRun("var obj = 42;"); 2095 obj = env->Global()->Get(v8_str("obj")); 2096 CHECK_EQ(42.0, obj->ToNumber()->Value()); 2097 CHECK_EQ(42, obj->ToInt32()->Value()); 2098 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 2099 // Negative integer. 2100 CompileRun("var obj = -37;"); 2101 obj = env->Global()->Get(v8_str("obj")); 2102 CHECK_EQ(-37.0, obj->ToNumber()->Value()); 2103 CHECK_EQ(-37, obj->ToInt32()->Value()); 2104 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT 2105 // Positive non-int32 integer. 2106 CompileRun("var obj = 0x81234567;"); 2107 obj = env->Global()->Get(v8_str("obj")); 2108 CHECK_EQ(2166572391.0, obj->ToNumber()->Value()); 2109 CHECK_EQ(-2128394905, obj->ToInt32()->Value()); 2110 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT 2111 // Fraction. 2112 CompileRun("var obj = 42.3;"); 2113 obj = env->Global()->Get(v8_str("obj")); 2114 CHECK_EQ(42.3, obj->ToNumber()->Value()); 2115 CHECK_EQ(42, obj->ToInt32()->Value()); 2116 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 2117 // Large negative fraction. 2118 CompileRun("var obj = -5726623061.75;"); 2119 obj = env->Global()->Get(v8_str("obj")); 2120 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value()); 2121 CHECK_EQ(-1431655765, obj->ToInt32()->Value()); 2122 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT 2123} 2124 2125 2126THREADED_TEST(isNumberType) { 2127 v8::HandleScope scope; 2128 LocalContext env; 2129 // Very large number. 2130 CompileRun("var obj = Math.pow(2,32) * 1237;"); 2131 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2132 CHECK(!obj->IsInt32()); 2133 CHECK(!obj->IsUint32()); 2134 // Large negative number. 2135 CompileRun("var obj = -1234567890123;"); 2136 obj = env->Global()->Get(v8_str("obj")); 2137 CHECK(!obj->IsInt32()); 2138 CHECK(!obj->IsUint32()); 2139 // Small positive integer. 2140 CompileRun("var obj = 42;"); 2141 obj = env->Global()->Get(v8_str("obj")); 2142 CHECK(obj->IsInt32()); 2143 CHECK(obj->IsUint32()); 2144 // Negative integer. 2145 CompileRun("var obj = -37;"); 2146 obj = env->Global()->Get(v8_str("obj")); 2147 CHECK(obj->IsInt32()); 2148 CHECK(!obj->IsUint32()); 2149 // Positive non-int32 integer. 2150 CompileRun("var obj = 0x81234567;"); 2151 obj = env->Global()->Get(v8_str("obj")); 2152 CHECK(!obj->IsInt32()); 2153 CHECK(obj->IsUint32()); 2154 // Fraction. 2155 CompileRun("var obj = 42.3;"); 2156 obj = env->Global()->Get(v8_str("obj")); 2157 CHECK(!obj->IsInt32()); 2158 CHECK(!obj->IsUint32()); 2159 // Large negative fraction. 2160 CompileRun("var obj = -5726623061.75;"); 2161 obj = env->Global()->Get(v8_str("obj")); 2162 CHECK(!obj->IsInt32()); 2163 CHECK(!obj->IsUint32()); 2164} 2165 2166 2167THREADED_TEST(ConversionException) { 2168 v8::HandleScope scope; 2169 LocalContext env; 2170 CompileRun( 2171 "function TestClass() { };" 2172 "TestClass.prototype.toString = function () { throw 'uncle?'; };" 2173 "var obj = new TestClass();"); 2174 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2175 2176 v8::TryCatch try_catch; 2177 2178 Local<Value> to_string_result = obj->ToString(); 2179 CHECK(to_string_result.IsEmpty()); 2180 CheckUncle(&try_catch); 2181 2182 Local<Value> to_number_result = obj->ToNumber(); 2183 CHECK(to_number_result.IsEmpty()); 2184 CheckUncle(&try_catch); 2185 2186 Local<Value> to_integer_result = obj->ToInteger(); 2187 CHECK(to_integer_result.IsEmpty()); 2188 CheckUncle(&try_catch); 2189 2190 Local<Value> to_uint32_result = obj->ToUint32(); 2191 CHECK(to_uint32_result.IsEmpty()); 2192 CheckUncle(&try_catch); 2193 2194 Local<Value> to_int32_result = obj->ToInt32(); 2195 CHECK(to_int32_result.IsEmpty()); 2196 CheckUncle(&try_catch); 2197 2198 Local<Value> to_object_result = v8::Undefined()->ToObject(); 2199 CHECK(to_object_result.IsEmpty()); 2200 CHECK(try_catch.HasCaught()); 2201 try_catch.Reset(); 2202 2203 int32_t int32_value = obj->Int32Value(); 2204 CHECK_EQ(0, int32_value); 2205 CheckUncle(&try_catch); 2206 2207 uint32_t uint32_value = obj->Uint32Value(); 2208 CHECK_EQ(0, uint32_value); 2209 CheckUncle(&try_catch); 2210 2211 double number_value = obj->NumberValue(); 2212 CHECK_NE(0, IsNaN(number_value)); 2213 CheckUncle(&try_catch); 2214 2215 int64_t integer_value = obj->IntegerValue(); 2216 CHECK_EQ(0.0, static_cast<double>(integer_value)); 2217 CheckUncle(&try_catch); 2218} 2219 2220 2221v8::Handle<Value> ThrowFromC(const v8::Arguments& args) { 2222 ApiTestFuzzer::Fuzz(); 2223 return v8::ThrowException(v8_str("konto")); 2224} 2225 2226 2227v8::Handle<Value> CCatcher(const v8::Arguments& args) { 2228 if (args.Length() < 1) return v8::Boolean::New(false); 2229 v8::HandleScope scope; 2230 v8::TryCatch try_catch; 2231 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run(); 2232 CHECK(!try_catch.HasCaught() || result.IsEmpty()); 2233 return v8::Boolean::New(try_catch.HasCaught()); 2234} 2235 2236 2237THREADED_TEST(APICatch) { 2238 v8::HandleScope scope; 2239 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2240 templ->Set(v8_str("ThrowFromC"), 2241 v8::FunctionTemplate::New(ThrowFromC)); 2242 LocalContext context(0, templ); 2243 CompileRun( 2244 "var thrown = false;" 2245 "try {" 2246 " ThrowFromC();" 2247 "} catch (e) {" 2248 " thrown = true;" 2249 "}"); 2250 Local<Value> thrown = context->Global()->Get(v8_str("thrown")); 2251 CHECK(thrown->BooleanValue()); 2252} 2253 2254 2255THREADED_TEST(APIThrowTryCatch) { 2256 v8::HandleScope scope; 2257 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2258 templ->Set(v8_str("ThrowFromC"), 2259 v8::FunctionTemplate::New(ThrowFromC)); 2260 LocalContext context(0, templ); 2261 v8::TryCatch try_catch; 2262 CompileRun("ThrowFromC();"); 2263 CHECK(try_catch.HasCaught()); 2264} 2265 2266 2267// Test that a try-finally block doesn't shadow a try-catch block 2268// when setting up an external handler. 2269// 2270// BUG(271): Some of the exception propagation does not work on the 2271// ARM simulator because the simulator separates the C++ stack and the 2272// JS stack. This test therefore fails on the simulator. The test is 2273// not threaded to allow the threading tests to run on the simulator. 2274TEST(TryCatchInTryFinally) { 2275 v8::HandleScope scope; 2276 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2277 templ->Set(v8_str("CCatcher"), 2278 v8::FunctionTemplate::New(CCatcher)); 2279 LocalContext context(0, templ); 2280 Local<Value> result = CompileRun("try {" 2281 " try {" 2282 " CCatcher('throw 7;');" 2283 " } finally {" 2284 " }" 2285 "} catch (e) {" 2286 "}"); 2287 CHECK(result->IsTrue()); 2288} 2289 2290 2291static void receive_message(v8::Handle<v8::Message> message, 2292 v8::Handle<v8::Value> data) { 2293 message->Get(); 2294 message_received = true; 2295} 2296 2297 2298TEST(APIThrowMessage) { 2299 message_received = false; 2300 v8::HandleScope scope; 2301 v8::V8::AddMessageListener(receive_message); 2302 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2303 templ->Set(v8_str("ThrowFromC"), 2304 v8::FunctionTemplate::New(ThrowFromC)); 2305 LocalContext context(0, templ); 2306 CompileRun("ThrowFromC();"); 2307 CHECK(message_received); 2308 v8::V8::RemoveMessageListeners(check_message); 2309} 2310 2311 2312TEST(APIThrowMessageAndVerboseTryCatch) { 2313 message_received = false; 2314 v8::HandleScope scope; 2315 v8::V8::AddMessageListener(receive_message); 2316 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2317 templ->Set(v8_str("ThrowFromC"), 2318 v8::FunctionTemplate::New(ThrowFromC)); 2319 LocalContext context(0, templ); 2320 v8::TryCatch try_catch; 2321 try_catch.SetVerbose(true); 2322 Local<Value> result = CompileRun("ThrowFromC();"); 2323 CHECK(try_catch.HasCaught()); 2324 CHECK(result.IsEmpty()); 2325 CHECK(message_received); 2326 v8::V8::RemoveMessageListeners(check_message); 2327} 2328 2329 2330THREADED_TEST(ExternalScriptException) { 2331 v8::HandleScope scope; 2332 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2333 templ->Set(v8_str("ThrowFromC"), 2334 v8::FunctionTemplate::New(ThrowFromC)); 2335 LocalContext context(0, templ); 2336 2337 v8::TryCatch try_catch; 2338 Local<Script> script 2339 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';")); 2340 Local<Value> result = script->Run(); 2341 CHECK(result.IsEmpty()); 2342 CHECK(try_catch.HasCaught()); 2343 String::AsciiValue exception_value(try_catch.Exception()); 2344 CHECK_EQ("konto", *exception_value); 2345} 2346 2347 2348 2349v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) { 2350 ApiTestFuzzer::Fuzz(); 2351 CHECK_EQ(4, args.Length()); 2352 int count = args[0]->Int32Value(); 2353 int cInterval = args[2]->Int32Value(); 2354 if (count == 0) { 2355 return v8::ThrowException(v8_str("FromC")); 2356 } else { 2357 Local<v8::Object> global = Context::GetCurrent()->Global(); 2358 Local<Value> fun = global->Get(v8_str("JSThrowCountDown")); 2359 v8::Handle<Value> argv[] = { v8_num(count - 1), 2360 args[1], 2361 args[2], 2362 args[3] }; 2363 if (count % cInterval == 0) { 2364 v8::TryCatch try_catch; 2365 Local<Value> result = fun.As<Function>()->Call(global, 4, argv); 2366 int expected = args[3]->Int32Value(); 2367 if (try_catch.HasCaught()) { 2368 CHECK_EQ(expected, count); 2369 CHECK(result.IsEmpty()); 2370 CHECK(!i::Top::has_scheduled_exception()); 2371 } else { 2372 CHECK_NE(expected, count); 2373 } 2374 return result; 2375 } else { 2376 return fun.As<Function>()->Call(global, 4, argv); 2377 } 2378 } 2379} 2380 2381 2382v8::Handle<Value> JSCheck(const v8::Arguments& args) { 2383 ApiTestFuzzer::Fuzz(); 2384 CHECK_EQ(3, args.Length()); 2385 bool equality = args[0]->BooleanValue(); 2386 int count = args[1]->Int32Value(); 2387 int expected = args[2]->Int32Value(); 2388 if (equality) { 2389 CHECK_EQ(count, expected); 2390 } else { 2391 CHECK_NE(count, expected); 2392 } 2393 return v8::Undefined(); 2394} 2395 2396 2397THREADED_TEST(EvalInTryFinally) { 2398 v8::HandleScope scope; 2399 LocalContext context; 2400 v8::TryCatch try_catch; 2401 CompileRun("(function() {" 2402 " try {" 2403 " eval('asldkf (*&^&*^');" 2404 " } finally {" 2405 " return;" 2406 " }" 2407 "})()"); 2408 CHECK(!try_catch.HasCaught()); 2409} 2410 2411 2412// This test works by making a stack of alternating JavaScript and C 2413// activations. These activations set up exception handlers with regular 2414// intervals, one interval for C activations and another for JavaScript 2415// activations. When enough activations have been created an exception is 2416// thrown and we check that the right activation catches the exception and that 2417// no other activations do. The right activation is always the topmost one with 2418// a handler, regardless of whether it is in JavaScript or C. 2419// 2420// The notation used to describe a test case looks like this: 2421// 2422// *JS[4] *C[3] @JS[2] C[1] JS[0] 2423// 2424// Each entry is an activation, either JS or C. The index is the count at that 2425// level. Stars identify activations with exception handlers, the @ identifies 2426// the exception handler that should catch the exception. 2427// 2428// BUG(271): Some of the exception propagation does not work on the 2429// ARM simulator because the simulator separates the C++ stack and the 2430// JS stack. This test therefore fails on the simulator. The test is 2431// not threaded to allow the threading tests to run on the simulator. 2432TEST(ExceptionOrder) { 2433 v8::HandleScope scope; 2434 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2435 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck)); 2436 templ->Set(v8_str("CThrowCountDown"), 2437 v8::FunctionTemplate::New(CThrowCountDown)); 2438 LocalContext context(0, templ); 2439 CompileRun( 2440 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {" 2441 " if (count == 0) throw 'FromJS';" 2442 " if (count % jsInterval == 0) {" 2443 " try {" 2444 " var value = CThrowCountDown(count - 1," 2445 " jsInterval," 2446 " cInterval," 2447 " expected);" 2448 " check(false, count, expected);" 2449 " return value;" 2450 " } catch (e) {" 2451 " check(true, count, expected);" 2452 " }" 2453 " } else {" 2454 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);" 2455 " }" 2456 "}"); 2457 Local<Function> fun = 2458 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown"))); 2459 2460 const int argc = 4; 2461 // count jsInterval cInterval expected 2462 2463 // *JS[4] *C[3] @JS[2] C[1] JS[0] 2464 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) }; 2465 fun->Call(fun, argc, a0); 2466 2467 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0] 2468 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) }; 2469 fun->Call(fun, argc, a1); 2470 2471 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0] 2472 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) }; 2473 fun->Call(fun, argc, a2); 2474 2475 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0] 2476 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) }; 2477 fun->Call(fun, argc, a3); 2478 2479 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0] 2480 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) }; 2481 fun->Call(fun, argc, a4); 2482 2483 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0] 2484 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) }; 2485 fun->Call(fun, argc, a5); 2486} 2487 2488 2489v8::Handle<Value> ThrowValue(const v8::Arguments& args) { 2490 ApiTestFuzzer::Fuzz(); 2491 CHECK_EQ(1, args.Length()); 2492 return v8::ThrowException(args[0]); 2493} 2494 2495 2496THREADED_TEST(ThrowValues) { 2497 v8::HandleScope scope; 2498 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2499 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue)); 2500 LocalContext context(0, templ); 2501 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 2502 "function Run(obj) {" 2503 " try {" 2504 " Throw(obj);" 2505 " } catch (e) {" 2506 " return e;" 2507 " }" 2508 " return 'no exception';" 2509 "}" 2510 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];")); 2511 CHECK_EQ(5, result->Length()); 2512 CHECK(result->Get(v8::Integer::New(0))->IsString()); 2513 CHECK(result->Get(v8::Integer::New(1))->IsNumber()); 2514 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value()); 2515 CHECK(result->Get(v8::Integer::New(2))->IsNumber()); 2516 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value()); 2517 CHECK(result->Get(v8::Integer::New(3))->IsNull()); 2518 CHECK(result->Get(v8::Integer::New(4))->IsUndefined()); 2519} 2520 2521 2522THREADED_TEST(CatchZero) { 2523 v8::HandleScope scope; 2524 LocalContext context; 2525 v8::TryCatch try_catch; 2526 CHECK(!try_catch.HasCaught()); 2527 Script::Compile(v8_str("throw 10"))->Run(); 2528 CHECK(try_catch.HasCaught()); 2529 CHECK_EQ(10, try_catch.Exception()->Int32Value()); 2530 try_catch.Reset(); 2531 CHECK(!try_catch.HasCaught()); 2532 Script::Compile(v8_str("throw 0"))->Run(); 2533 CHECK(try_catch.HasCaught()); 2534 CHECK_EQ(0, try_catch.Exception()->Int32Value()); 2535} 2536 2537 2538THREADED_TEST(CatchExceptionFromWith) { 2539 v8::HandleScope scope; 2540 LocalContext context; 2541 v8::TryCatch try_catch; 2542 CHECK(!try_catch.HasCaught()); 2543 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run(); 2544 CHECK(try_catch.HasCaught()); 2545} 2546 2547 2548THREADED_TEST(Equality) { 2549 v8::HandleScope scope; 2550 LocalContext context; 2551 // Check that equality works at all before relying on CHECK_EQ 2552 CHECK(v8_str("a")->Equals(v8_str("a"))); 2553 CHECK(!v8_str("a")->Equals(v8_str("b"))); 2554 2555 CHECK_EQ(v8_str("a"), v8_str("a")); 2556 CHECK_NE(v8_str("a"), v8_str("b")); 2557 CHECK_EQ(v8_num(1), v8_num(1)); 2558 CHECK_EQ(v8_num(1.00), v8_num(1)); 2559 CHECK_NE(v8_num(1), v8_num(2)); 2560 2561 // Assume String is not symbol. 2562 CHECK(v8_str("a")->StrictEquals(v8_str("a"))); 2563 CHECK(!v8_str("a")->StrictEquals(v8_str("b"))); 2564 CHECK(!v8_str("5")->StrictEquals(v8_num(5))); 2565 CHECK(v8_num(1)->StrictEquals(v8_num(1))); 2566 CHECK(!v8_num(1)->StrictEquals(v8_num(2))); 2567 CHECK(v8_num(0)->StrictEquals(v8_num(-0))); 2568 Local<Value> not_a_number = v8_num(i::OS::nan_value()); 2569 CHECK(!not_a_number->StrictEquals(not_a_number)); 2570 CHECK(v8::False()->StrictEquals(v8::False())); 2571 CHECK(!v8::False()->StrictEquals(v8::Undefined())); 2572 2573 v8::Handle<v8::Object> obj = v8::Object::New(); 2574 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj); 2575 CHECK(alias->StrictEquals(obj)); 2576 alias.Dispose(); 2577} 2578 2579 2580THREADED_TEST(MultiRun) { 2581 v8::HandleScope scope; 2582 LocalContext context; 2583 Local<Script> script = Script::Compile(v8_str("x")); 2584 for (int i = 0; i < 10; i++) 2585 script->Run(); 2586} 2587 2588 2589static v8::Handle<Value> GetXValue(Local<String> name, 2590 const AccessorInfo& info) { 2591 ApiTestFuzzer::Fuzz(); 2592 CHECK_EQ(info.Data(), v8_str("donut")); 2593 CHECK_EQ(name, v8_str("x")); 2594 return name; 2595} 2596 2597 2598THREADED_TEST(SimplePropertyRead) { 2599 v8::HandleScope scope; 2600 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2601 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2602 LocalContext context; 2603 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2604 Local<Script> script = Script::Compile(v8_str("obj.x")); 2605 for (int i = 0; i < 10; i++) { 2606 Local<Value> result = script->Run(); 2607 CHECK_EQ(result, v8_str("x")); 2608 } 2609} 2610 2611THREADED_TEST(DefinePropertyOnAPIAccessor) { 2612 v8::HandleScope scope; 2613 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2614 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2615 LocalContext context; 2616 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2617 2618 // Uses getOwnPropertyDescriptor to check the configurable status 2619 Local<Script> script_desc 2620 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( " 2621 "obj, 'x');" 2622 "prop.configurable;")); 2623 Local<Value> result = script_desc->Run(); 2624 CHECK_EQ(result->BooleanValue(), true); 2625 2626 // Redefine get - but still configurable 2627 Local<Script> script_define 2628 = Script::Compile(v8_str("var desc = { get: function(){return 42; }," 2629 " configurable: true };" 2630 "Object.defineProperty(obj, 'x', desc);" 2631 "obj.x")); 2632 result = script_define->Run(); 2633 CHECK_EQ(result, v8_num(42)); 2634 2635 // Check that the accessor is still configurable 2636 result = script_desc->Run(); 2637 CHECK_EQ(result->BooleanValue(), true); 2638 2639 // Redefine to a non-configurable 2640 script_define 2641 = Script::Compile(v8_str("var desc = { get: function(){return 43; }," 2642 " configurable: false };" 2643 "Object.defineProperty(obj, 'x', desc);" 2644 "obj.x")); 2645 result = script_define->Run(); 2646 CHECK_EQ(result, v8_num(43)); 2647 result = script_desc->Run(); 2648 CHECK_EQ(result->BooleanValue(), false); 2649 2650 // Make sure that it is not possible to redefine again 2651 v8::TryCatch try_catch; 2652 result = script_define->Run(); 2653 CHECK(try_catch.HasCaught()); 2654 String::AsciiValue exception_value(try_catch.Exception()); 2655 CHECK_EQ(*exception_value, 2656 "TypeError: Cannot redefine property: defineProperty"); 2657} 2658 2659THREADED_TEST(DefinePropertyOnDefineGetterSetter) { 2660 v8::HandleScope scope; 2661 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2662 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2663 LocalContext context; 2664 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2665 2666 Local<Script> script_desc = Script::Compile(v8_str("var prop =" 2667 "Object.getOwnPropertyDescriptor( " 2668 "obj, 'x');" 2669 "prop.configurable;")); 2670 Local<Value> result = script_desc->Run(); 2671 CHECK_EQ(result->BooleanValue(), true); 2672 2673 Local<Script> script_define = 2674 Script::Compile(v8_str("var desc = {get: function(){return 42; }," 2675 " configurable: true };" 2676 "Object.defineProperty(obj, 'x', desc);" 2677 "obj.x")); 2678 result = script_define->Run(); 2679 CHECK_EQ(result, v8_num(42)); 2680 2681 2682 result = script_desc->Run(); 2683 CHECK_EQ(result->BooleanValue(), true); 2684 2685 2686 script_define = 2687 Script::Compile(v8_str("var desc = {get: function(){return 43; }," 2688 " configurable: false };" 2689 "Object.defineProperty(obj, 'x', desc);" 2690 "obj.x")); 2691 result = script_define->Run(); 2692 CHECK_EQ(result, v8_num(43)); 2693 result = script_desc->Run(); 2694 2695 CHECK_EQ(result->BooleanValue(), false); 2696 2697 v8::TryCatch try_catch; 2698 result = script_define->Run(); 2699 CHECK(try_catch.HasCaught()); 2700 String::AsciiValue exception_value(try_catch.Exception()); 2701 CHECK_EQ(*exception_value, 2702 "TypeError: Cannot redefine property: defineProperty"); 2703} 2704 2705 2706static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context, 2707 char const* name) { 2708 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name))); 2709} 2710 2711 2712THREADED_TEST(DefineAPIAccessorOnObject) { 2713 v8::HandleScope scope; 2714 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2715 LocalContext context; 2716 2717 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 2718 CompileRun("var obj2 = {};"); 2719 2720 CHECK(CompileRun("obj1.x")->IsUndefined()); 2721 CHECK(CompileRun("obj2.x")->IsUndefined()); 2722 2723 CHECK(GetGlobalProperty(&context, "obj1")-> 2724 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2725 2726 ExpectString("obj1.x", "x"); 2727 CHECK(CompileRun("obj2.x")->IsUndefined()); 2728 2729 CHECK(GetGlobalProperty(&context, "obj2")-> 2730 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2731 2732 ExpectString("obj1.x", "x"); 2733 ExpectString("obj2.x", "x"); 2734 2735 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2736 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2737 2738 CompileRun("Object.defineProperty(obj1, 'x'," 2739 "{ get: function() { return 'y'; }, configurable: true })"); 2740 2741 ExpectString("obj1.x", "y"); 2742 ExpectString("obj2.x", "x"); 2743 2744 CompileRun("Object.defineProperty(obj2, 'x'," 2745 "{ get: function() { return 'y'; }, configurable: true })"); 2746 2747 ExpectString("obj1.x", "y"); 2748 ExpectString("obj2.x", "y"); 2749 2750 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2751 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2752 2753 CHECK(GetGlobalProperty(&context, "obj1")-> 2754 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2755 CHECK(GetGlobalProperty(&context, "obj2")-> 2756 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2757 2758 ExpectString("obj1.x", "x"); 2759 ExpectString("obj2.x", "x"); 2760 2761 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2762 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2763 2764 // Define getters/setters, but now make them not configurable. 2765 CompileRun("Object.defineProperty(obj1, 'x'," 2766 "{ get: function() { return 'z'; }, configurable: false })"); 2767 CompileRun("Object.defineProperty(obj2, 'x'," 2768 "{ get: function() { return 'z'; }, configurable: false })"); 2769 2770 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2771 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2772 2773 ExpectString("obj1.x", "z"); 2774 ExpectString("obj2.x", "z"); 2775 2776 CHECK(!GetGlobalProperty(&context, "obj1")-> 2777 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2778 CHECK(!GetGlobalProperty(&context, "obj2")-> 2779 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2780 2781 ExpectString("obj1.x", "z"); 2782 ExpectString("obj2.x", "z"); 2783} 2784 2785 2786THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) { 2787 v8::HandleScope scope; 2788 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2789 LocalContext context; 2790 2791 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 2792 CompileRun("var obj2 = {};"); 2793 2794 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 2795 v8_str("x"), 2796 GetXValue, NULL, 2797 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 2798 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 2799 v8_str("x"), 2800 GetXValue, NULL, 2801 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 2802 2803 ExpectString("obj1.x", "x"); 2804 ExpectString("obj2.x", "x"); 2805 2806 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2807 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2808 2809 CHECK(!GetGlobalProperty(&context, "obj1")-> 2810 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2811 CHECK(!GetGlobalProperty(&context, "obj2")-> 2812 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2813 2814 { 2815 v8::TryCatch try_catch; 2816 CompileRun("Object.defineProperty(obj1, 'x'," 2817 "{get: function() { return 'func'; }})"); 2818 CHECK(try_catch.HasCaught()); 2819 String::AsciiValue exception_value(try_catch.Exception()); 2820 CHECK_EQ(*exception_value, 2821 "TypeError: Cannot redefine property: defineProperty"); 2822 } 2823 { 2824 v8::TryCatch try_catch; 2825 CompileRun("Object.defineProperty(obj2, 'x'," 2826 "{get: function() { return 'func'; }})"); 2827 CHECK(try_catch.HasCaught()); 2828 String::AsciiValue exception_value(try_catch.Exception()); 2829 CHECK_EQ(*exception_value, 2830 "TypeError: Cannot redefine property: defineProperty"); 2831 } 2832} 2833 2834 2835static v8::Handle<Value> Get239Value(Local<String> name, 2836 const AccessorInfo& info) { 2837 ApiTestFuzzer::Fuzz(); 2838 CHECK_EQ(info.Data(), v8_str("donut")); 2839 CHECK_EQ(name, v8_str("239")); 2840 return name; 2841} 2842 2843 2844THREADED_TEST(ElementAPIAccessor) { 2845 v8::HandleScope scope; 2846 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2847 LocalContext context; 2848 2849 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 2850 CompileRun("var obj2 = {};"); 2851 2852 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 2853 v8_str("239"), 2854 Get239Value, NULL, 2855 v8_str("donut"))); 2856 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 2857 v8_str("239"), 2858 Get239Value, NULL, 2859 v8_str("donut"))); 2860 2861 ExpectString("obj1[239]", "239"); 2862 ExpectString("obj2[239]", "239"); 2863 ExpectString("obj1['239']", "239"); 2864 ExpectString("obj2['239']", "239"); 2865} 2866 2867 2868v8::Persistent<Value> xValue; 2869 2870 2871static void SetXValue(Local<String> name, 2872 Local<Value> value, 2873 const AccessorInfo& info) { 2874 CHECK_EQ(value, v8_num(4)); 2875 CHECK_EQ(info.Data(), v8_str("donut")); 2876 CHECK_EQ(name, v8_str("x")); 2877 CHECK(xValue.IsEmpty()); 2878 xValue = v8::Persistent<Value>::New(value); 2879} 2880 2881 2882THREADED_TEST(SimplePropertyWrite) { 2883 v8::HandleScope scope; 2884 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2885 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut")); 2886 LocalContext context; 2887 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2888 Local<Script> script = Script::Compile(v8_str("obj.x = 4")); 2889 for (int i = 0; i < 10; i++) { 2890 CHECK(xValue.IsEmpty()); 2891 script->Run(); 2892 CHECK_EQ(v8_num(4), xValue); 2893 xValue.Dispose(); 2894 xValue = v8::Persistent<Value>(); 2895 } 2896} 2897 2898 2899static v8::Handle<Value> XPropertyGetter(Local<String> property, 2900 const AccessorInfo& info) { 2901 ApiTestFuzzer::Fuzz(); 2902 CHECK(info.Data()->IsUndefined()); 2903 return property; 2904} 2905 2906 2907THREADED_TEST(NamedInterceptorPropertyRead) { 2908 v8::HandleScope scope; 2909 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2910 templ->SetNamedPropertyHandler(XPropertyGetter); 2911 LocalContext context; 2912 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2913 Local<Script> script = Script::Compile(v8_str("obj.x")); 2914 for (int i = 0; i < 10; i++) { 2915 Local<Value> result = script->Run(); 2916 CHECK_EQ(result, v8_str("x")); 2917 } 2918} 2919 2920 2921THREADED_TEST(NamedInterceptorDictionaryIC) { 2922 v8::HandleScope scope; 2923 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2924 templ->SetNamedPropertyHandler(XPropertyGetter); 2925 LocalContext context; 2926 // Create an object with a named interceptor. 2927 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance()); 2928 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x")); 2929 for (int i = 0; i < 10; i++) { 2930 Local<Value> result = script->Run(); 2931 CHECK_EQ(result, v8_str("x")); 2932 } 2933 // Create a slow case object and a function accessing a property in 2934 // that slow case object (with dictionary probing in generated 2935 // code). Then force object with a named interceptor into slow-case, 2936 // pass it to the function, and check that the interceptor is called 2937 // instead of accessing the local property. 2938 Local<Value> result = 2939 CompileRun("function get_x(o) { return o.x; };" 2940 "var obj = { x : 42, y : 0 };" 2941 "delete obj.y;" 2942 "for (var i = 0; i < 10; i++) get_x(obj);" 2943 "interceptor_obj.x = 42;" 2944 "interceptor_obj.y = 10;" 2945 "delete interceptor_obj.y;" 2946 "get_x(interceptor_obj)"); 2947 CHECK_EQ(result, v8_str("x")); 2948} 2949 2950 2951THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) { 2952 v8::HandleScope scope; 2953 2954 v8::Persistent<Context> context1 = Context::New(); 2955 2956 context1->Enter(); 2957 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2958 templ->SetNamedPropertyHandler(XPropertyGetter); 2959 // Create an object with a named interceptor. 2960 v8::Local<v8::Object> object = templ->NewInstance(); 2961 context1->Global()->Set(v8_str("interceptor_obj"), object); 2962 2963 // Force the object into the slow case. 2964 CompileRun("interceptor_obj.y = 0;" 2965 "delete interceptor_obj.y;"); 2966 context1->Exit(); 2967 2968 { 2969 // Introduce the object into a different context. 2970 // Repeat named loads to exercise ICs. 2971 LocalContext context2; 2972 context2->Global()->Set(v8_str("interceptor_obj"), object); 2973 Local<Value> result = 2974 CompileRun("function get_x(o) { return o.x; }" 2975 "interceptor_obj.x = 42;" 2976 "for (var i=0; i != 10; i++) {" 2977 " get_x(interceptor_obj);" 2978 "}" 2979 "get_x(interceptor_obj)"); 2980 // Check that the interceptor was actually invoked. 2981 CHECK_EQ(result, v8_str("x")); 2982 } 2983 2984 // Return to the original context and force some object to the slow case 2985 // to cause the NormalizedMapCache to verify. 2986 context1->Enter(); 2987 CompileRun("var obj = { x : 0 }; delete obj.x;"); 2988 context1->Exit(); 2989 2990 context1.Dispose(); 2991} 2992 2993 2994static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property, 2995 const AccessorInfo& info) { 2996 // Set x on the prototype object and do not handle the get request. 2997 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype(); 2998 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23)); 2999 return v8::Handle<Value>(); 3000} 3001 3002 3003// This is a regression test for http://crbug.com/20104. Map 3004// transitions should not interfere with post interceptor lookup. 3005THREADED_TEST(NamedInterceptorMapTransitionRead) { 3006 v8::HandleScope scope; 3007 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(); 3008 Local<v8::ObjectTemplate> instance_template 3009 = function_template->InstanceTemplate(); 3010 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter); 3011 LocalContext context; 3012 context->Global()->Set(v8_str("F"), function_template->GetFunction()); 3013 // Create an instance of F and introduce a map transition for x. 3014 CompileRun("var o = new F(); o.x = 23;"); 3015 // Create an instance of F and invoke the getter. The result should be 23. 3016 Local<Value> result = CompileRun("o = new F(); o.x"); 3017 CHECK_EQ(result->Int32Value(), 23); 3018} 3019 3020 3021static v8::Handle<Value> IndexedPropertyGetter(uint32_t index, 3022 const AccessorInfo& info) { 3023 ApiTestFuzzer::Fuzz(); 3024 if (index == 37) { 3025 return v8::Handle<Value>(v8_num(625)); 3026 } 3027 return v8::Handle<Value>(); 3028} 3029 3030 3031static v8::Handle<Value> IndexedPropertySetter(uint32_t index, 3032 Local<Value> value, 3033 const AccessorInfo& info) { 3034 ApiTestFuzzer::Fuzz(); 3035 if (index == 39) { 3036 return value; 3037 } 3038 return v8::Handle<Value>(); 3039} 3040 3041 3042THREADED_TEST(IndexedInterceptorWithIndexedAccessor) { 3043 v8::HandleScope scope; 3044 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3045 templ->SetIndexedPropertyHandler(IndexedPropertyGetter, 3046 IndexedPropertySetter); 3047 LocalContext context; 3048 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3049 Local<Script> getter_script = Script::Compile(v8_str( 3050 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];")); 3051 Local<Script> setter_script = Script::Compile(v8_str( 3052 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});" 3053 "obj[17] = 23;" 3054 "obj.foo;")); 3055 Local<Script> interceptor_setter_script = Script::Compile(v8_str( 3056 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});" 3057 "obj[39] = 47;" 3058 "obj.foo;")); // This setter should not run, due to the interceptor. 3059 Local<Script> interceptor_getter_script = Script::Compile(v8_str( 3060 "obj[37];")); 3061 Local<Value> result = getter_script->Run(); 3062 CHECK_EQ(v8_num(5), result); 3063 result = setter_script->Run(); 3064 CHECK_EQ(v8_num(23), result); 3065 result = interceptor_setter_script->Run(); 3066 CHECK_EQ(v8_num(23), result); 3067 result = interceptor_getter_script->Run(); 3068 CHECK_EQ(v8_num(625), result); 3069} 3070 3071 3072static v8::Handle<Value> IdentityIndexedPropertyGetter( 3073 uint32_t index, 3074 const AccessorInfo& info) { 3075 return v8::Integer::NewFromUnsigned(index); 3076} 3077 3078 3079THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) { 3080 v8::HandleScope scope; 3081 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3082 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3083 3084 LocalContext context; 3085 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3086 3087 // Check fast object case. 3088 const char* fast_case_code = 3089 "Object.getOwnPropertyDescriptor(obj, 0).value.toString()"; 3090 ExpectString(fast_case_code, "0"); 3091 3092 // Check slow case. 3093 const char* slow_case_code = 3094 "obj.x = 1; delete obj.x;" 3095 "Object.getOwnPropertyDescriptor(obj, 1).value.toString()"; 3096 ExpectString(slow_case_code, "1"); 3097} 3098 3099 3100THREADED_TEST(IndexedInterceptorWithNoSetter) { 3101 v8::HandleScope scope; 3102 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3103 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3104 3105 LocalContext context; 3106 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3107 3108 const char* code = 3109 "try {" 3110 " obj[0] = 239;" 3111 " for (var i = 0; i < 100; i++) {" 3112 " var v = obj[0];" 3113 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;" 3114 " }" 3115 " 'PASSED'" 3116 "} catch(e) {" 3117 " e" 3118 "}"; 3119 ExpectString(code, "PASSED"); 3120} 3121 3122 3123THREADED_TEST(IndexedInterceptorWithAccessorCheck) { 3124 v8::HandleScope scope; 3125 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3126 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3127 3128 LocalContext context; 3129 Local<v8::Object> obj = templ->NewInstance(); 3130 obj->TurnOnAccessCheck(); 3131 context->Global()->Set(v8_str("obj"), obj); 3132 3133 const char* code = 3134 "try {" 3135 " for (var i = 0; i < 100; i++) {" 3136 " var v = obj[0];" 3137 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;" 3138 " }" 3139 " 'PASSED'" 3140 "} catch(e) {" 3141 " e" 3142 "}"; 3143 ExpectString(code, "PASSED"); 3144} 3145 3146 3147THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) { 3148 i::FLAG_allow_natives_syntax = true; 3149 v8::HandleScope scope; 3150 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3151 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3152 3153 LocalContext context; 3154 Local<v8::Object> obj = templ->NewInstance(); 3155 context->Global()->Set(v8_str("obj"), obj); 3156 3157 const char* code = 3158 "try {" 3159 " for (var i = 0; i < 100; i++) {" 3160 " var expected = i;" 3161 " if (i == 5) {" 3162 " %EnableAccessChecks(obj);" 3163 " expected = undefined;" 3164 " }" 3165 " var v = obj[i];" 3166 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3167 " if (i == 5) %DisableAccessChecks(obj);" 3168 " }" 3169 " 'PASSED'" 3170 "} catch(e) {" 3171 " e" 3172 "}"; 3173 ExpectString(code, "PASSED"); 3174} 3175 3176 3177THREADED_TEST(IndexedInterceptorWithDifferentIndices) { 3178 v8::HandleScope scope; 3179 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3180 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3181 3182 LocalContext context; 3183 Local<v8::Object> obj = templ->NewInstance(); 3184 context->Global()->Set(v8_str("obj"), obj); 3185 3186 const char* code = 3187 "try {" 3188 " for (var i = 0; i < 100; i++) {" 3189 " var v = obj[i];" 3190 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 3191 " }" 3192 " 'PASSED'" 3193 "} catch(e) {" 3194 " e" 3195 "}"; 3196 ExpectString(code, "PASSED"); 3197} 3198 3199 3200THREADED_TEST(IndexedInterceptorWithNegativeIndices) { 3201 v8::HandleScope scope; 3202 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3203 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3204 3205 LocalContext context; 3206 Local<v8::Object> obj = templ->NewInstance(); 3207 context->Global()->Set(v8_str("obj"), obj); 3208 3209 const char* code = 3210 "try {" 3211 " for (var i = 0; i < 100; i++) {" 3212 " var expected = i;" 3213 " var key = i;" 3214 " if (i == 25) {" 3215 " key = -1;" 3216 " expected = undefined;" 3217 " }" 3218 " if (i == 50) {" 3219 " /* probe minimal Smi number on 32-bit platforms */" 3220 " key = -(1 << 30);" 3221 " expected = undefined;" 3222 " }" 3223 " if (i == 75) {" 3224 " /* probe minimal Smi number on 64-bit platforms */" 3225 " key = 1 << 31;" 3226 " expected = undefined;" 3227 " }" 3228 " var v = obj[key];" 3229 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3230 " }" 3231 " 'PASSED'" 3232 "} catch(e) {" 3233 " e" 3234 "}"; 3235 ExpectString(code, "PASSED"); 3236} 3237 3238 3239THREADED_TEST(IndexedInterceptorWithNotSmiLookup) { 3240 v8::HandleScope scope; 3241 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3242 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3243 3244 LocalContext context; 3245 Local<v8::Object> obj = templ->NewInstance(); 3246 context->Global()->Set(v8_str("obj"), obj); 3247 3248 const char* code = 3249 "try {" 3250 " for (var i = 0; i < 100; i++) {" 3251 " var expected = i;" 3252 " var key = i;" 3253 " if (i == 50) {" 3254 " key = 'foobar';" 3255 " expected = undefined;" 3256 " }" 3257 " var v = obj[key];" 3258 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3259 " }" 3260 " 'PASSED'" 3261 "} catch(e) {" 3262 " e" 3263 "}"; 3264 ExpectString(code, "PASSED"); 3265} 3266 3267 3268THREADED_TEST(IndexedInterceptorGoingMegamorphic) { 3269 v8::HandleScope scope; 3270 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3271 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3272 3273 LocalContext context; 3274 Local<v8::Object> obj = templ->NewInstance(); 3275 context->Global()->Set(v8_str("obj"), obj); 3276 3277 const char* code = 3278 "var original = obj;" 3279 "try {" 3280 " for (var i = 0; i < 100; i++) {" 3281 " var expected = i;" 3282 " if (i == 50) {" 3283 " obj = {50: 'foobar'};" 3284 " expected = 'foobar';" 3285 " }" 3286 " var v = obj[i];" 3287 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3288 " if (i == 50) obj = original;" 3289 " }" 3290 " 'PASSED'" 3291 "} catch(e) {" 3292 " e" 3293 "}"; 3294 ExpectString(code, "PASSED"); 3295} 3296 3297 3298THREADED_TEST(IndexedInterceptorReceiverTurningSmi) { 3299 v8::HandleScope scope; 3300 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3301 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3302 3303 LocalContext context; 3304 Local<v8::Object> obj = templ->NewInstance(); 3305 context->Global()->Set(v8_str("obj"), obj); 3306 3307 const char* code = 3308 "var original = obj;" 3309 "try {" 3310 " for (var i = 0; i < 100; i++) {" 3311 " var expected = i;" 3312 " if (i == 5) {" 3313 " obj = 239;" 3314 " expected = undefined;" 3315 " }" 3316 " var v = obj[i];" 3317 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3318 " if (i == 5) obj = original;" 3319 " }" 3320 " 'PASSED'" 3321 "} catch(e) {" 3322 " e" 3323 "}"; 3324 ExpectString(code, "PASSED"); 3325} 3326 3327 3328THREADED_TEST(IndexedInterceptorOnProto) { 3329 v8::HandleScope scope; 3330 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3331 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3332 3333 LocalContext context; 3334 Local<v8::Object> obj = templ->NewInstance(); 3335 context->Global()->Set(v8_str("obj"), obj); 3336 3337 const char* code = 3338 "var o = {__proto__: obj};" 3339 "try {" 3340 " for (var i = 0; i < 100; i++) {" 3341 " var v = o[i];" 3342 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 3343 " }" 3344 " 'PASSED'" 3345 "} catch(e) {" 3346 " e" 3347 "}"; 3348 ExpectString(code, "PASSED"); 3349} 3350 3351 3352THREADED_TEST(MultiContexts) { 3353 v8::HandleScope scope; 3354 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New(); 3355 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler)); 3356 3357 Local<String> password = v8_str("Password"); 3358 3359 // Create an environment 3360 LocalContext context0(0, templ); 3361 context0->SetSecurityToken(password); 3362 v8::Handle<v8::Object> global0 = context0->Global(); 3363 global0->Set(v8_str("custom"), v8_num(1234)); 3364 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 3365 3366 // Create an independent environment 3367 LocalContext context1(0, templ); 3368 context1->SetSecurityToken(password); 3369 v8::Handle<v8::Object> global1 = context1->Global(); 3370 global1->Set(v8_str("custom"), v8_num(1234)); 3371 CHECK_NE(global0, global1); 3372 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 3373 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value()); 3374 3375 // Now create a new context with the old global 3376 LocalContext context2(0, templ, global1); 3377 context2->SetSecurityToken(password); 3378 v8::Handle<v8::Object> global2 = context2->Global(); 3379 CHECK_EQ(global1, global2); 3380 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value()); 3381 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value()); 3382} 3383 3384 3385THREADED_TEST(FunctionPrototypeAcrossContexts) { 3386 // Make sure that functions created by cloning boilerplates cannot 3387 // communicate through their __proto__ field. 3388 3389 v8::HandleScope scope; 3390 3391 LocalContext env0; 3392 v8::Handle<v8::Object> global0 = 3393 env0->Global(); 3394 v8::Handle<v8::Object> object0 = 3395 global0->Get(v8_str("Object")).As<v8::Object>(); 3396 v8::Handle<v8::Object> tostring0 = 3397 object0->Get(v8_str("toString")).As<v8::Object>(); 3398 v8::Handle<v8::Object> proto0 = 3399 tostring0->Get(v8_str("__proto__")).As<v8::Object>(); 3400 proto0->Set(v8_str("custom"), v8_num(1234)); 3401 3402 LocalContext env1; 3403 v8::Handle<v8::Object> global1 = 3404 env1->Global(); 3405 v8::Handle<v8::Object> object1 = 3406 global1->Get(v8_str("Object")).As<v8::Object>(); 3407 v8::Handle<v8::Object> tostring1 = 3408 object1->Get(v8_str("toString")).As<v8::Object>(); 3409 v8::Handle<v8::Object> proto1 = 3410 tostring1->Get(v8_str("__proto__")).As<v8::Object>(); 3411 CHECK(!proto1->Has(v8_str("custom"))); 3412} 3413 3414 3415THREADED_TEST(Regress892105) { 3416 // Make sure that object and array literals created by cloning 3417 // boilerplates cannot communicate through their __proto__ 3418 // field. This is rather difficult to check, but we try to add stuff 3419 // to Object.prototype and Array.prototype and create a new 3420 // environment. This should succeed. 3421 3422 v8::HandleScope scope; 3423 3424 Local<String> source = v8_str("Object.prototype.obj = 1234;" 3425 "Array.prototype.arr = 4567;" 3426 "8901"); 3427 3428 LocalContext env0; 3429 Local<Script> script0 = Script::Compile(source); 3430 CHECK_EQ(8901.0, script0->Run()->NumberValue()); 3431 3432 LocalContext env1; 3433 Local<Script> script1 = Script::Compile(source); 3434 CHECK_EQ(8901.0, script1->Run()->NumberValue()); 3435} 3436 3437 3438THREADED_TEST(UndetectableObject) { 3439 v8::HandleScope scope; 3440 LocalContext env; 3441 3442 Local<v8::FunctionTemplate> desc = 3443 v8::FunctionTemplate::New(0, v8::Handle<Value>()); 3444 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 3445 3446 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 3447 env->Global()->Set(v8_str("undetectable"), obj); 3448 3449 ExpectString("undetectable.toString()", "[object Object]"); 3450 ExpectString("typeof undetectable", "undefined"); 3451 ExpectString("typeof(undetectable)", "undefined"); 3452 ExpectBoolean("typeof undetectable == 'undefined'", true); 3453 ExpectBoolean("typeof undetectable == 'object'", false); 3454 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 3455 ExpectBoolean("!undetectable", true); 3456 3457 ExpectObject("true&&undetectable", obj); 3458 ExpectBoolean("false&&undetectable", false); 3459 ExpectBoolean("true||undetectable", true); 3460 ExpectObject("false||undetectable", obj); 3461 3462 ExpectObject("undetectable&&true", obj); 3463 ExpectObject("undetectable&&false", obj); 3464 ExpectBoolean("undetectable||true", true); 3465 ExpectBoolean("undetectable||false", false); 3466 3467 ExpectBoolean("undetectable==null", true); 3468 ExpectBoolean("null==undetectable", true); 3469 ExpectBoolean("undetectable==undefined", true); 3470 ExpectBoolean("undefined==undetectable", true); 3471 ExpectBoolean("undetectable==undetectable", true); 3472 3473 3474 ExpectBoolean("undetectable===null", false); 3475 ExpectBoolean("null===undetectable", false); 3476 ExpectBoolean("undetectable===undefined", false); 3477 ExpectBoolean("undefined===undetectable", false); 3478 ExpectBoolean("undetectable===undetectable", true); 3479} 3480 3481 3482 3483THREADED_TEST(ExtensibleOnUndetectable) { 3484 v8::HandleScope scope; 3485 LocalContext env; 3486 3487 Local<v8::FunctionTemplate> desc = 3488 v8::FunctionTemplate::New(0, v8::Handle<Value>()); 3489 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 3490 3491 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 3492 env->Global()->Set(v8_str("undetectable"), obj); 3493 3494 Local<String> source = v8_str("undetectable.x = 42;" 3495 "undetectable.x"); 3496 3497 Local<Script> script = Script::Compile(source); 3498 3499 CHECK_EQ(v8::Integer::New(42), script->Run()); 3500 3501 ExpectBoolean("Object.isExtensible(undetectable)", true); 3502 3503 source = v8_str("Object.preventExtensions(undetectable);"); 3504 script = Script::Compile(source); 3505 script->Run(); 3506 ExpectBoolean("Object.isExtensible(undetectable)", false); 3507 3508 source = v8_str("undetectable.y = 2000;"); 3509 script = Script::Compile(source); 3510 v8::TryCatch try_catch; 3511 Local<Value> result = script->Run(); 3512 CHECK(result.IsEmpty()); 3513 CHECK(try_catch.HasCaught()); 3514} 3515 3516 3517 3518THREADED_TEST(UndetectableString) { 3519 v8::HandleScope scope; 3520 LocalContext env; 3521 3522 Local<String> obj = String::NewUndetectable("foo"); 3523 env->Global()->Set(v8_str("undetectable"), obj); 3524 3525 ExpectString("undetectable", "foo"); 3526 ExpectString("typeof undetectable", "undefined"); 3527 ExpectString("typeof(undetectable)", "undefined"); 3528 ExpectBoolean("typeof undetectable == 'undefined'", true); 3529 ExpectBoolean("typeof undetectable == 'string'", false); 3530 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 3531 ExpectBoolean("!undetectable", true); 3532 3533 ExpectObject("true&&undetectable", obj); 3534 ExpectBoolean("false&&undetectable", false); 3535 ExpectBoolean("true||undetectable", true); 3536 ExpectObject("false||undetectable", obj); 3537 3538 ExpectObject("undetectable&&true", obj); 3539 ExpectObject("undetectable&&false", obj); 3540 ExpectBoolean("undetectable||true", true); 3541 ExpectBoolean("undetectable||false", false); 3542 3543 ExpectBoolean("undetectable==null", true); 3544 ExpectBoolean("null==undetectable", true); 3545 ExpectBoolean("undetectable==undefined", true); 3546 ExpectBoolean("undefined==undetectable", true); 3547 ExpectBoolean("undetectable==undetectable", true); 3548 3549 3550 ExpectBoolean("undetectable===null", false); 3551 ExpectBoolean("null===undetectable", false); 3552 ExpectBoolean("undetectable===undefined", false); 3553 ExpectBoolean("undefined===undetectable", false); 3554 ExpectBoolean("undetectable===undetectable", true); 3555} 3556 3557 3558template <typename T> static void USE(T) { } 3559 3560 3561// This test is not intended to be run, just type checked. 3562static void PersistentHandles() { 3563 USE(PersistentHandles); 3564 Local<String> str = v8_str("foo"); 3565 v8::Persistent<String> p_str = v8::Persistent<String>::New(str); 3566 USE(p_str); 3567 Local<Script> scr = Script::Compile(v8_str("")); 3568 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr); 3569 USE(p_scr); 3570 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3571 v8::Persistent<ObjectTemplate> p_templ = 3572 v8::Persistent<ObjectTemplate>::New(templ); 3573 USE(p_templ); 3574} 3575 3576 3577static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) { 3578 ApiTestFuzzer::Fuzz(); 3579 return v8::Undefined(); 3580} 3581 3582 3583THREADED_TEST(GlobalObjectTemplate) { 3584 v8::HandleScope handle_scope; 3585 Local<ObjectTemplate> global_template = ObjectTemplate::New(); 3586 global_template->Set(v8_str("JSNI_Log"), 3587 v8::FunctionTemplate::New(HandleLogDelegator)); 3588 v8::Persistent<Context> context = Context::New(0, global_template); 3589 Context::Scope context_scope(context); 3590 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run(); 3591 context.Dispose(); 3592} 3593 3594 3595static const char* kSimpleExtensionSource = 3596 "function Foo() {" 3597 " return 4;" 3598 "}"; 3599 3600 3601THREADED_TEST(SimpleExtensions) { 3602 v8::HandleScope handle_scope; 3603 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource)); 3604 const char* extension_names[] = { "simpletest" }; 3605 v8::ExtensionConfiguration extensions(1, extension_names); 3606 v8::Handle<Context> context = Context::New(&extensions); 3607 Context::Scope lock(context); 3608 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 3609 CHECK_EQ(result, v8::Integer::New(4)); 3610} 3611 3612 3613static const char* kEvalExtensionSource1 = 3614 "function UseEval1() {" 3615 " var x = 42;" 3616 " return eval('x');" 3617 "}"; 3618 3619 3620static const char* kEvalExtensionSource2 = 3621 "(function() {" 3622 " var x = 42;" 3623 " function e() {" 3624 " return eval('x');" 3625 " }" 3626 " this.UseEval2 = e;" 3627 "})()"; 3628 3629 3630THREADED_TEST(UseEvalFromExtension) { 3631 v8::HandleScope handle_scope; 3632 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1)); 3633 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2)); 3634 const char* extension_names[] = { "evaltest1", "evaltest2" }; 3635 v8::ExtensionConfiguration extensions(2, extension_names); 3636 v8::Handle<Context> context = Context::New(&extensions); 3637 Context::Scope lock(context); 3638 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run(); 3639 CHECK_EQ(result, v8::Integer::New(42)); 3640 result = Script::Compile(v8_str("UseEval2()"))->Run(); 3641 CHECK_EQ(result, v8::Integer::New(42)); 3642} 3643 3644 3645static const char* kWithExtensionSource1 = 3646 "function UseWith1() {" 3647 " var x = 42;" 3648 " with({x:87}) { return x; }" 3649 "}"; 3650 3651 3652 3653static const char* kWithExtensionSource2 = 3654 "(function() {" 3655 " var x = 42;" 3656 " function e() {" 3657 " with ({x:87}) { return x; }" 3658 " }" 3659 " this.UseWith2 = e;" 3660 "})()"; 3661 3662 3663THREADED_TEST(UseWithFromExtension) { 3664 v8::HandleScope handle_scope; 3665 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1)); 3666 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2)); 3667 const char* extension_names[] = { "withtest1", "withtest2" }; 3668 v8::ExtensionConfiguration extensions(2, extension_names); 3669 v8::Handle<Context> context = Context::New(&extensions); 3670 Context::Scope lock(context); 3671 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run(); 3672 CHECK_EQ(result, v8::Integer::New(87)); 3673 result = Script::Compile(v8_str("UseWith2()"))->Run(); 3674 CHECK_EQ(result, v8::Integer::New(87)); 3675} 3676 3677 3678THREADED_TEST(AutoExtensions) { 3679 v8::HandleScope handle_scope; 3680 Extension* extension = new Extension("autotest", kSimpleExtensionSource); 3681 extension->set_auto_enable(true); 3682 v8::RegisterExtension(extension); 3683 v8::Handle<Context> context = Context::New(); 3684 Context::Scope lock(context); 3685 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 3686 CHECK_EQ(result, v8::Integer::New(4)); 3687} 3688 3689 3690static const char* kSyntaxErrorInExtensionSource = 3691 "["; 3692 3693 3694// Test that a syntax error in an extension does not cause a fatal 3695// error but results in an empty context. 3696THREADED_TEST(SyntaxErrorExtensions) { 3697 v8::HandleScope handle_scope; 3698 v8::RegisterExtension(new Extension("syntaxerror", 3699 kSyntaxErrorInExtensionSource)); 3700 const char* extension_names[] = { "syntaxerror" }; 3701 v8::ExtensionConfiguration extensions(1, extension_names); 3702 v8::Handle<Context> context = Context::New(&extensions); 3703 CHECK(context.IsEmpty()); 3704} 3705 3706 3707static const char* kExceptionInExtensionSource = 3708 "throw 42"; 3709 3710 3711// Test that an exception when installing an extension does not cause 3712// a fatal error but results in an empty context. 3713THREADED_TEST(ExceptionExtensions) { 3714 v8::HandleScope handle_scope; 3715 v8::RegisterExtension(new Extension("exception", 3716 kExceptionInExtensionSource)); 3717 const char* extension_names[] = { "exception" }; 3718 v8::ExtensionConfiguration extensions(1, extension_names); 3719 v8::Handle<Context> context = Context::New(&extensions); 3720 CHECK(context.IsEmpty()); 3721} 3722 3723 3724static const char* kNativeCallInExtensionSource = 3725 "function call_runtime_last_index_of(x) {" 3726 " return %StringLastIndexOf(x, 'bob', 10);" 3727 "}"; 3728 3729 3730static const char* kNativeCallTest = 3731 "call_runtime_last_index_of('bobbobboellebobboellebobbob');"; 3732 3733// Test that a native runtime calls are supported in extensions. 3734THREADED_TEST(NativeCallInExtensions) { 3735 v8::HandleScope handle_scope; 3736 v8::RegisterExtension(new Extension("nativecall", 3737 kNativeCallInExtensionSource)); 3738 const char* extension_names[] = { "nativecall" }; 3739 v8::ExtensionConfiguration extensions(1, extension_names); 3740 v8::Handle<Context> context = Context::New(&extensions); 3741 Context::Scope lock(context); 3742 v8::Handle<Value> result = Script::Compile(v8_str(kNativeCallTest))->Run(); 3743 CHECK_EQ(result, v8::Integer::New(3)); 3744} 3745 3746 3747static void CheckDependencies(const char* name, const char* expected) { 3748 v8::HandleScope handle_scope; 3749 v8::ExtensionConfiguration config(1, &name); 3750 LocalContext context(&config); 3751 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded"))); 3752} 3753 3754 3755/* 3756 * Configuration: 3757 * 3758 * /-- B <--\ 3759 * A <- -- D <-- E 3760 * \-- C <--/ 3761 */ 3762THREADED_TEST(ExtensionDependency) { 3763 static const char* kEDeps[] = { "D" }; 3764 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps)); 3765 static const char* kDDeps[] = { "B", "C" }; 3766 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps)); 3767 static const char* kBCDeps[] = { "A" }; 3768 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps)); 3769 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps)); 3770 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';")); 3771 CheckDependencies("A", "undefinedA"); 3772 CheckDependencies("B", "undefinedAB"); 3773 CheckDependencies("C", "undefinedAC"); 3774 CheckDependencies("D", "undefinedABCD"); 3775 CheckDependencies("E", "undefinedABCDE"); 3776 v8::HandleScope handle_scope; 3777 static const char* exts[2] = { "C", "E" }; 3778 v8::ExtensionConfiguration config(2, exts); 3779 LocalContext context(&config); 3780 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded"))); 3781} 3782 3783 3784static const char* kExtensionTestScript = 3785 "native function A();" 3786 "native function B();" 3787 "native function C();" 3788 "function Foo(i) {" 3789 " if (i == 0) return A();" 3790 " if (i == 1) return B();" 3791 " if (i == 2) return C();" 3792 "}"; 3793 3794 3795static v8::Handle<Value> CallFun(const v8::Arguments& args) { 3796 ApiTestFuzzer::Fuzz(); 3797 if (args.IsConstructCall()) { 3798 args.This()->Set(v8_str("data"), args.Data()); 3799 return v8::Null(); 3800 } 3801 return args.Data(); 3802} 3803 3804 3805class FunctionExtension : public Extension { 3806 public: 3807 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { } 3808 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( 3809 v8::Handle<String> name); 3810}; 3811 3812 3813static int lookup_count = 0; 3814v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction( 3815 v8::Handle<String> name) { 3816 lookup_count++; 3817 if (name->Equals(v8_str("A"))) { 3818 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8)); 3819 } else if (name->Equals(v8_str("B"))) { 3820 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7)); 3821 } else if (name->Equals(v8_str("C"))) { 3822 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6)); 3823 } else { 3824 return v8::Handle<v8::FunctionTemplate>(); 3825 } 3826} 3827 3828 3829THREADED_TEST(FunctionLookup) { 3830 v8::RegisterExtension(new FunctionExtension()); 3831 v8::HandleScope handle_scope; 3832 static const char* exts[1] = { "functiontest" }; 3833 v8::ExtensionConfiguration config(1, exts); 3834 LocalContext context(&config); 3835 CHECK_EQ(3, lookup_count); 3836 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run()); 3837 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run()); 3838 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run()); 3839} 3840 3841 3842THREADED_TEST(NativeFunctionConstructCall) { 3843 v8::RegisterExtension(new FunctionExtension()); 3844 v8::HandleScope handle_scope; 3845 static const char* exts[1] = { "functiontest" }; 3846 v8::ExtensionConfiguration config(1, exts); 3847 LocalContext context(&config); 3848 for (int i = 0; i < 10; i++) { 3849 // Run a few times to ensure that allocation of objects doesn't 3850 // change behavior of a constructor function. 3851 CHECK_EQ(v8::Integer::New(8), 3852 Script::Compile(v8_str("(new A()).data"))->Run()); 3853 CHECK_EQ(v8::Integer::New(7), 3854 Script::Compile(v8_str("(new B()).data"))->Run()); 3855 CHECK_EQ(v8::Integer::New(6), 3856 Script::Compile(v8_str("(new C()).data"))->Run()); 3857 } 3858} 3859 3860 3861static const char* last_location; 3862static const char* last_message; 3863void StoringErrorCallback(const char* location, const char* message) { 3864 if (last_location == NULL) { 3865 last_location = location; 3866 last_message = message; 3867 } 3868} 3869 3870 3871// ErrorReporting creates a circular extensions configuration and 3872// tests that the fatal error handler gets called. This renders V8 3873// unusable and therefore this test cannot be run in parallel. 3874TEST(ErrorReporting) { 3875 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 3876 static const char* aDeps[] = { "B" }; 3877 v8::RegisterExtension(new Extension("A", "", 1, aDeps)); 3878 static const char* bDeps[] = { "A" }; 3879 v8::RegisterExtension(new Extension("B", "", 1, bDeps)); 3880 last_location = NULL; 3881 v8::ExtensionConfiguration config(1, bDeps); 3882 v8::Handle<Context> context = Context::New(&config); 3883 CHECK(context.IsEmpty()); 3884 CHECK_NE(last_location, NULL); 3885} 3886 3887 3888static const char* js_code_causing_huge_string_flattening = 3889 "var str = 'X';" 3890 "for (var i = 0; i < 30; i++) {" 3891 " str = str + str;" 3892 "}" 3893 "str.match(/X/);"; 3894 3895 3896void OOMCallback(const char* location, const char* message) { 3897 exit(0); 3898} 3899 3900 3901TEST(RegexpOutOfMemory) { 3902 // Execute a script that causes out of memory when flattening a string. 3903 v8::HandleScope scope; 3904 v8::V8::SetFatalErrorHandler(OOMCallback); 3905 LocalContext context; 3906 Local<Script> script = 3907 Script::Compile(String::New(js_code_causing_huge_string_flattening)); 3908 last_location = NULL; 3909 Local<Value> result = script->Run(); 3910 3911 CHECK(false); // Should not return. 3912} 3913 3914 3915static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message, 3916 v8::Handle<Value> data) { 3917 CHECK_EQ(v8::Undefined(), data); 3918 CHECK(message->GetScriptResourceName()->IsUndefined()); 3919 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName()); 3920 message->GetLineNumber(); 3921 message->GetSourceLine(); 3922} 3923 3924 3925THREADED_TEST(ErrorWithMissingScriptInfo) { 3926 v8::HandleScope scope; 3927 LocalContext context; 3928 v8::V8::AddMessageListener(MissingScriptInfoMessageListener); 3929 Script::Compile(v8_str("throw Error()"))->Run(); 3930 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener); 3931} 3932 3933 3934int global_index = 0; 3935 3936class Snorkel { 3937 public: 3938 Snorkel() { index_ = global_index++; } 3939 int index_; 3940}; 3941 3942class Whammy { 3943 public: 3944 Whammy() { 3945 cursor_ = 0; 3946 } 3947 ~Whammy() { 3948 script_.Dispose(); 3949 } 3950 v8::Handle<Script> getScript() { 3951 if (script_.IsEmpty()) 3952 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo")); 3953 return Local<Script>(*script_); 3954 } 3955 3956 public: 3957 static const int kObjectCount = 256; 3958 int cursor_; 3959 v8::Persistent<v8::Object> objects_[kObjectCount]; 3960 v8::Persistent<Script> script_; 3961}; 3962 3963static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) { 3964 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data); 3965 delete snorkel; 3966 obj.ClearWeak(); 3967} 3968 3969v8::Handle<Value> WhammyPropertyGetter(Local<String> name, 3970 const AccessorInfo& info) { 3971 Whammy* whammy = 3972 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); 3973 3974 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_]; 3975 3976 v8::Handle<v8::Object> obj = v8::Object::New(); 3977 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj); 3978 if (!prev.IsEmpty()) { 3979 prev->Set(v8_str("next"), obj); 3980 prev.MakeWeak(new Snorkel(), &HandleWeakReference); 3981 whammy->objects_[whammy->cursor_].Clear(); 3982 } 3983 whammy->objects_[whammy->cursor_] = global; 3984 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount; 3985 return whammy->getScript()->Run(); 3986} 3987 3988THREADED_TEST(WeakReference) { 3989 v8::HandleScope handle_scope; 3990 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New(); 3991 Whammy* whammy = new Whammy(); 3992 templ->SetNamedPropertyHandler(WhammyPropertyGetter, 3993 0, 0, 0, 0, 3994 v8::External::New(whammy)); 3995 const char* extension_list[] = { "v8/gc" }; 3996 v8::ExtensionConfiguration extensions(1, extension_list); 3997 v8::Persistent<Context> context = Context::New(&extensions); 3998 Context::Scope context_scope(context); 3999 4000 v8::Handle<v8::Object> interceptor = templ->NewInstance(); 4001 context->Global()->Set(v8_str("whammy"), interceptor); 4002 const char* code = 4003 "var last;" 4004 "for (var i = 0; i < 10000; i++) {" 4005 " var obj = whammy.length;" 4006 " if (last) last.next = obj;" 4007 " last = obj;" 4008 "}" 4009 "gc();" 4010 "4"; 4011 v8::Handle<Value> result = CompileRun(code); 4012 CHECK_EQ(4.0, result->NumberValue()); 4013 delete whammy; 4014 context.Dispose(); 4015} 4016 4017 4018static bool in_scavenge = false; 4019static int last = -1; 4020 4021static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) { 4022 CHECK_EQ(-1, last); 4023 last = 0; 4024 obj.Dispose(); 4025 obj.Clear(); 4026 in_scavenge = true; 4027 i::Heap::PerformScavenge(); 4028 in_scavenge = false; 4029 *(reinterpret_cast<bool*>(data)) = true; 4030} 4031 4032static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj, 4033 void* data) { 4034 CHECK_EQ(0, last); 4035 last = 1; 4036 *(reinterpret_cast<bool*>(data)) = in_scavenge; 4037 obj.Dispose(); 4038 obj.Clear(); 4039} 4040 4041THREADED_TEST(NoWeakRefCallbacksInScavenge) { 4042 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks. 4043 // Calling callbacks from scavenges is unsafe as objects held by those 4044 // handlers might have become strongly reachable, but scavenge doesn't 4045 // check that. 4046 v8::Persistent<Context> context = Context::New(); 4047 Context::Scope context_scope(context); 4048 4049 v8::Persistent<v8::Object> object_a; 4050 v8::Persistent<v8::Object> object_b; 4051 4052 { 4053 v8::HandleScope handle_scope; 4054 object_b = v8::Persistent<v8::Object>::New(v8::Object::New()); 4055 object_a = v8::Persistent<v8::Object>::New(v8::Object::New()); 4056 } 4057 4058 bool object_a_disposed = false; 4059 object_a.MakeWeak(&object_a_disposed, &ForceScavenge); 4060 bool released_in_scavenge = false; 4061 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge); 4062 4063 while (!object_a_disposed) { 4064 i::Heap::CollectAllGarbage(false); 4065 } 4066 CHECK(!released_in_scavenge); 4067} 4068 4069 4070v8::Handle<Function> args_fun; 4071 4072 4073static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) { 4074 ApiTestFuzzer::Fuzz(); 4075 CHECK_EQ(args_fun, args.Callee()); 4076 CHECK_EQ(3, args.Length()); 4077 CHECK_EQ(v8::Integer::New(1), args[0]); 4078 CHECK_EQ(v8::Integer::New(2), args[1]); 4079 CHECK_EQ(v8::Integer::New(3), args[2]); 4080 CHECK_EQ(v8::Undefined(), args[3]); 4081 v8::HandleScope scope; 4082 i::Heap::CollectAllGarbage(false); 4083 return v8::Undefined(); 4084} 4085 4086 4087THREADED_TEST(Arguments) { 4088 v8::HandleScope scope; 4089 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 4090 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback)); 4091 LocalContext context(NULL, global); 4092 args_fun = context->Global()->Get(v8_str("f")).As<Function>(); 4093 v8_compile("f(1, 2, 3)")->Run(); 4094} 4095 4096 4097static v8::Handle<Value> NoBlockGetterX(Local<String> name, 4098 const AccessorInfo&) { 4099 return v8::Handle<Value>(); 4100} 4101 4102 4103static v8::Handle<Value> NoBlockGetterI(uint32_t index, 4104 const AccessorInfo&) { 4105 return v8::Handle<Value>(); 4106} 4107 4108 4109static v8::Handle<v8::Boolean> PDeleter(Local<String> name, 4110 const AccessorInfo&) { 4111 if (!name->Equals(v8_str("foo"))) { 4112 return v8::Handle<v8::Boolean>(); // not intercepted 4113 } 4114 4115 return v8::False(); // intercepted, and don't delete the property 4116} 4117 4118 4119static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) { 4120 if (index != 2) { 4121 return v8::Handle<v8::Boolean>(); // not intercepted 4122 } 4123 4124 return v8::False(); // intercepted, and don't delete the property 4125} 4126 4127 4128THREADED_TEST(Deleter) { 4129 v8::HandleScope scope; 4130 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4131 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL); 4132 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL); 4133 LocalContext context; 4134 context->Global()->Set(v8_str("k"), obj->NewInstance()); 4135 CompileRun( 4136 "k.foo = 'foo';" 4137 "k.bar = 'bar';" 4138 "k[2] = 2;" 4139 "k[4] = 4;"); 4140 CHECK(v8_compile("delete k.foo")->Run()->IsFalse()); 4141 CHECK(v8_compile("delete k.bar")->Run()->IsTrue()); 4142 4143 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo")); 4144 CHECK(v8_compile("k.bar")->Run()->IsUndefined()); 4145 4146 CHECK(v8_compile("delete k[2]")->Run()->IsFalse()); 4147 CHECK(v8_compile("delete k[4]")->Run()->IsTrue()); 4148 4149 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2)); 4150 CHECK(v8_compile("k[4]")->Run()->IsUndefined()); 4151} 4152 4153 4154static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) { 4155 ApiTestFuzzer::Fuzz(); 4156 if (name->Equals(v8_str("foo")) || 4157 name->Equals(v8_str("bar")) || 4158 name->Equals(v8_str("baz"))) { 4159 return v8::Undefined(); 4160 } 4161 return v8::Handle<Value>(); 4162} 4163 4164 4165static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) { 4166 ApiTestFuzzer::Fuzz(); 4167 if (index == 0 || index == 1) return v8::Undefined(); 4168 return v8::Handle<Value>(); 4169} 4170 4171 4172static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) { 4173 ApiTestFuzzer::Fuzz(); 4174 v8::Handle<v8::Array> result = v8::Array::New(3); 4175 result->Set(v8::Integer::New(0), v8_str("foo")); 4176 result->Set(v8::Integer::New(1), v8_str("bar")); 4177 result->Set(v8::Integer::New(2), v8_str("baz")); 4178 return result; 4179} 4180 4181 4182static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) { 4183 ApiTestFuzzer::Fuzz(); 4184 v8::Handle<v8::Array> result = v8::Array::New(2); 4185 result->Set(v8::Integer::New(0), v8_str("0")); 4186 result->Set(v8::Integer::New(1), v8_str("1")); 4187 return result; 4188} 4189 4190 4191THREADED_TEST(Enumerators) { 4192 v8::HandleScope scope; 4193 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4194 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum); 4195 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum); 4196 LocalContext context; 4197 context->Global()->Set(v8_str("k"), obj->NewInstance()); 4198 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 4199 "k[10] = 0;" 4200 "k.a = 0;" 4201 "k[5] = 0;" 4202 "k.b = 0;" 4203 "k[4294967295] = 0;" 4204 "k.c = 0;" 4205 "k[4294967296] = 0;" 4206 "k.d = 0;" 4207 "k[140000] = 0;" 4208 "k.e = 0;" 4209 "k[30000000000] = 0;" 4210 "k.f = 0;" 4211 "var result = [];" 4212 "for (var prop in k) {" 4213 " result.push(prop);" 4214 "}" 4215 "result")); 4216 // Check that we get all the property names returned including the 4217 // ones from the enumerators in the right order: indexed properties 4218 // in numerical order, indexed interceptor properties, named 4219 // properties in insertion order, named interceptor properties. 4220 // This order is not mandated by the spec, so this test is just 4221 // documenting our behavior. 4222 CHECK_EQ(17, result->Length()); 4223 // Indexed properties in numerical order. 4224 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0))); 4225 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1))); 4226 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2))); 4227 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3))); 4228 // Indexed interceptor properties in the order they are returned 4229 // from the enumerator interceptor. 4230 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4))); 4231 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5))); 4232 // Named properties in insertion order. 4233 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6))); 4234 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7))); 4235 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8))); 4236 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9))); 4237 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10))); 4238 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11))); 4239 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12))); 4240 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13))); 4241 // Named interceptor properties. 4242 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14))); 4243 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15))); 4244 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16))); 4245} 4246 4247 4248int p_getter_count; 4249int p_getter_count2; 4250 4251 4252static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) { 4253 ApiTestFuzzer::Fuzz(); 4254 p_getter_count++; 4255 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 4256 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 4257 if (name->Equals(v8_str("p1"))) { 4258 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 4259 } else if (name->Equals(v8_str("p2"))) { 4260 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 4261 } else if (name->Equals(v8_str("p3"))) { 4262 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 4263 } else if (name->Equals(v8_str("p4"))) { 4264 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 4265 } 4266 return v8::Undefined(); 4267} 4268 4269 4270static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) { 4271 ApiTestFuzzer::Fuzz(); 4272 LocalContext context; 4273 context->Global()->Set(v8_str("o1"), obj->NewInstance()); 4274 CompileRun( 4275 "o1.__proto__ = { };" 4276 "var o2 = { __proto__: o1 };" 4277 "var o3 = { __proto__: o2 };" 4278 "var o4 = { __proto__: o3 };" 4279 "for (var i = 0; i < 10; i++) o4.p4;" 4280 "for (var i = 0; i < 10; i++) o3.p3;" 4281 "for (var i = 0; i < 10; i++) o2.p2;" 4282 "for (var i = 0; i < 10; i++) o1.p1;"); 4283} 4284 4285 4286static v8::Handle<Value> PGetter2(Local<String> name, 4287 const AccessorInfo& info) { 4288 ApiTestFuzzer::Fuzz(); 4289 p_getter_count2++; 4290 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 4291 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 4292 if (name->Equals(v8_str("p1"))) { 4293 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 4294 } else if (name->Equals(v8_str("p2"))) { 4295 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 4296 } else if (name->Equals(v8_str("p3"))) { 4297 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 4298 } else if (name->Equals(v8_str("p4"))) { 4299 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 4300 } 4301 return v8::Undefined(); 4302} 4303 4304 4305THREADED_TEST(GetterHolders) { 4306 v8::HandleScope scope; 4307 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4308 obj->SetAccessor(v8_str("p1"), PGetter); 4309 obj->SetAccessor(v8_str("p2"), PGetter); 4310 obj->SetAccessor(v8_str("p3"), PGetter); 4311 obj->SetAccessor(v8_str("p4"), PGetter); 4312 p_getter_count = 0; 4313 RunHolderTest(obj); 4314 CHECK_EQ(40, p_getter_count); 4315} 4316 4317 4318THREADED_TEST(PreInterceptorHolders) { 4319 v8::HandleScope scope; 4320 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4321 obj->SetNamedPropertyHandler(PGetter2); 4322 p_getter_count2 = 0; 4323 RunHolderTest(obj); 4324 CHECK_EQ(40, p_getter_count2); 4325} 4326 4327 4328THREADED_TEST(ObjectInstantiation) { 4329 v8::HandleScope scope; 4330 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 4331 templ->SetAccessor(v8_str("t"), PGetter2); 4332 LocalContext context; 4333 context->Global()->Set(v8_str("o"), templ->NewInstance()); 4334 for (int i = 0; i < 100; i++) { 4335 v8::HandleScope inner_scope; 4336 v8::Handle<v8::Object> obj = templ->NewInstance(); 4337 CHECK_NE(obj, context->Global()->Get(v8_str("o"))); 4338 context->Global()->Set(v8_str("o2"), obj); 4339 v8::Handle<Value> value = 4340 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run(); 4341 CHECK_EQ(v8::True(), value); 4342 context->Global()->Set(v8_str("o"), obj); 4343 } 4344} 4345 4346 4347THREADED_TEST(StringWrite) { 4348 v8::HandleScope scope; 4349 v8::Handle<String> str = v8_str("abcde"); 4350 4351 char buf[100]; 4352 int len; 4353 4354 memset(buf, 0x1, sizeof(buf)); 4355 len = str->WriteAscii(buf); 4356 CHECK_EQ(len, 5); 4357 CHECK_EQ(strncmp("abcde\0", buf, 6), 0); 4358 4359 memset(buf, 0x1, sizeof(buf)); 4360 len = str->WriteAscii(buf, 0, 4); 4361 CHECK_EQ(len, 4); 4362 CHECK_EQ(strncmp("abcd\1", buf, 5), 0); 4363 4364 memset(buf, 0x1, sizeof(buf)); 4365 len = str->WriteAscii(buf, 0, 5); 4366 CHECK_EQ(len, 5); 4367 CHECK_EQ(strncmp("abcde\1", buf, 6), 0); 4368 4369 memset(buf, 0x1, sizeof(buf)); 4370 len = str->WriteAscii(buf, 0, 6); 4371 CHECK_EQ(len, 5); 4372 CHECK_EQ(strncmp("abcde\0", buf, 6), 0); 4373 4374 memset(buf, 0x1, sizeof(buf)); 4375 len = str->WriteAscii(buf, 4, -1); 4376 CHECK_EQ(len, 1); 4377 CHECK_EQ(strncmp("e\0", buf, 2), 0); 4378 4379 memset(buf, 0x1, sizeof(buf)); 4380 len = str->WriteAscii(buf, 4, 6); 4381 CHECK_EQ(len, 1); 4382 CHECK_EQ(strncmp("e\0", buf, 2), 0); 4383 4384 memset(buf, 0x1, sizeof(buf)); 4385 len = str->WriteAscii(buf, 4, 1); 4386 CHECK_EQ(len, 1); 4387 CHECK_EQ(strncmp("e\1", buf, 2), 0); 4388} 4389 4390 4391THREADED_TEST(ToArrayIndex) { 4392 v8::HandleScope scope; 4393 LocalContext context; 4394 4395 v8::Handle<String> str = v8_str("42"); 4396 v8::Handle<v8::Uint32> index = str->ToArrayIndex(); 4397 CHECK(!index.IsEmpty()); 4398 CHECK_EQ(42.0, index->Uint32Value()); 4399 str = v8_str("42asdf"); 4400 index = str->ToArrayIndex(); 4401 CHECK(index.IsEmpty()); 4402 str = v8_str("-42"); 4403 index = str->ToArrayIndex(); 4404 CHECK(index.IsEmpty()); 4405 str = v8_str("4294967295"); 4406 index = str->ToArrayIndex(); 4407 CHECK(!index.IsEmpty()); 4408 CHECK_EQ(4294967295.0, index->Uint32Value()); 4409 v8::Handle<v8::Number> num = v8::Number::New(1); 4410 index = num->ToArrayIndex(); 4411 CHECK(!index.IsEmpty()); 4412 CHECK_EQ(1.0, index->Uint32Value()); 4413 num = v8::Number::New(-1); 4414 index = num->ToArrayIndex(); 4415 CHECK(index.IsEmpty()); 4416 v8::Handle<v8::Object> obj = v8::Object::New(); 4417 index = obj->ToArrayIndex(); 4418 CHECK(index.IsEmpty()); 4419} 4420 4421 4422THREADED_TEST(ErrorConstruction) { 4423 v8::HandleScope scope; 4424 LocalContext context; 4425 4426 v8::Handle<String> foo = v8_str("foo"); 4427 v8::Handle<String> message = v8_str("message"); 4428 v8::Handle<Value> range_error = v8::Exception::RangeError(foo); 4429 CHECK(range_error->IsObject()); 4430 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>(); 4431 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo)); 4432 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo); 4433 CHECK(reference_error->IsObject()); 4434 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo)); 4435 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo); 4436 CHECK(syntax_error->IsObject()); 4437 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo)); 4438 v8::Handle<Value> type_error = v8::Exception::TypeError(foo); 4439 CHECK(type_error->IsObject()); 4440 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo)); 4441 v8::Handle<Value> error = v8::Exception::Error(foo); 4442 CHECK(error->IsObject()); 4443 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo)); 4444} 4445 4446 4447static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) { 4448 ApiTestFuzzer::Fuzz(); 4449 return v8_num(10); 4450} 4451 4452 4453static void YSetter(Local<String> name, 4454 Local<Value> value, 4455 const AccessorInfo& info) { 4456 if (info.This()->Has(name)) { 4457 info.This()->Delete(name); 4458 } 4459 info.This()->Set(name, value); 4460} 4461 4462 4463THREADED_TEST(DeleteAccessor) { 4464 v8::HandleScope scope; 4465 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4466 obj->SetAccessor(v8_str("y"), YGetter, YSetter); 4467 LocalContext context; 4468 v8::Handle<v8::Object> holder = obj->NewInstance(); 4469 context->Global()->Set(v8_str("holder"), holder); 4470 v8::Handle<Value> result = CompileRun( 4471 "holder.y = 11; holder.y = 12; holder.y"); 4472 CHECK_EQ(12, result->Uint32Value()); 4473} 4474 4475 4476THREADED_TEST(TypeSwitch) { 4477 v8::HandleScope scope; 4478 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New(); 4479 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New(); 4480 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New(); 4481 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 }; 4482 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs); 4483 LocalContext context; 4484 v8::Handle<v8::Object> obj0 = v8::Object::New(); 4485 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance(); 4486 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance(); 4487 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance(); 4488 for (int i = 0; i < 10; i++) { 4489 CHECK_EQ(0, type_switch->match(obj0)); 4490 CHECK_EQ(1, type_switch->match(obj1)); 4491 CHECK_EQ(2, type_switch->match(obj2)); 4492 CHECK_EQ(3, type_switch->match(obj3)); 4493 CHECK_EQ(3, type_switch->match(obj3)); 4494 CHECK_EQ(2, type_switch->match(obj2)); 4495 CHECK_EQ(1, type_switch->match(obj1)); 4496 CHECK_EQ(0, type_switch->match(obj0)); 4497 } 4498} 4499 4500 4501// For use within the TestSecurityHandler() test. 4502static bool g_security_callback_result = false; 4503static bool NamedSecurityTestCallback(Local<v8::Object> global, 4504 Local<Value> name, 4505 v8::AccessType type, 4506 Local<Value> data) { 4507 // Always allow read access. 4508 if (type == v8::ACCESS_GET) 4509 return true; 4510 4511 // Sometimes allow other access. 4512 return g_security_callback_result; 4513} 4514 4515 4516static bool IndexedSecurityTestCallback(Local<v8::Object> global, 4517 uint32_t key, 4518 v8::AccessType type, 4519 Local<Value> data) { 4520 // Always allow read access. 4521 if (type == v8::ACCESS_GET) 4522 return true; 4523 4524 // Sometimes allow other access. 4525 return g_security_callback_result; 4526} 4527 4528 4529static int trouble_nesting = 0; 4530static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) { 4531 ApiTestFuzzer::Fuzz(); 4532 trouble_nesting++; 4533 4534 // Call a JS function that throws an uncaught exception. 4535 Local<v8::Object> arg_this = Context::GetCurrent()->Global(); 4536 Local<Value> trouble_callee = (trouble_nesting == 3) ? 4537 arg_this->Get(v8_str("trouble_callee")) : 4538 arg_this->Get(v8_str("trouble_caller")); 4539 CHECK(trouble_callee->IsFunction()); 4540 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL); 4541} 4542 4543 4544static int report_count = 0; 4545static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>, 4546 v8::Handle<Value>) { 4547 report_count++; 4548} 4549 4550 4551// Counts uncaught exceptions, but other tests running in parallel 4552// also have uncaught exceptions. 4553TEST(ApiUncaughtException) { 4554 report_count = 0; 4555 v8::HandleScope scope; 4556 LocalContext env; 4557 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener); 4558 4559 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 4560 v8::Local<v8::Object> global = env->Global(); 4561 global->Set(v8_str("trouble"), fun->GetFunction()); 4562 4563 Script::Compile(v8_str("function trouble_callee() {" 4564 " var x = null;" 4565 " return x.foo;" 4566 "};" 4567 "function trouble_caller() {" 4568 " trouble();" 4569 "};"))->Run(); 4570 Local<Value> trouble = global->Get(v8_str("trouble")); 4571 CHECK(trouble->IsFunction()); 4572 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee")); 4573 CHECK(trouble_callee->IsFunction()); 4574 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller")); 4575 CHECK(trouble_caller->IsFunction()); 4576 Function::Cast(*trouble_caller)->Call(global, 0, NULL); 4577 CHECK_EQ(1, report_count); 4578 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener); 4579} 4580 4581static const char* script_resource_name = "ExceptionInNativeScript.js"; 4582static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message, 4583 v8::Handle<Value>) { 4584 v8::Handle<v8::Value> name_val = message->GetScriptResourceName(); 4585 CHECK(!name_val.IsEmpty() && name_val->IsString()); 4586 v8::String::AsciiValue name(message->GetScriptResourceName()); 4587 CHECK_EQ(script_resource_name, *name); 4588 CHECK_EQ(3, message->GetLineNumber()); 4589 v8::String::AsciiValue source_line(message->GetSourceLine()); 4590 CHECK_EQ(" new o.foo();", *source_line); 4591} 4592 4593TEST(ExceptionInNativeScript) { 4594 v8::HandleScope scope; 4595 LocalContext env; 4596 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener); 4597 4598 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 4599 v8::Local<v8::Object> global = env->Global(); 4600 global->Set(v8_str("trouble"), fun->GetFunction()); 4601 4602 Script::Compile(v8_str("function trouble() {\n" 4603 " var o = {};\n" 4604 " new o.foo();\n" 4605 "};"), v8::String::New(script_resource_name))->Run(); 4606 Local<Value> trouble = global->Get(v8_str("trouble")); 4607 CHECK(trouble->IsFunction()); 4608 Function::Cast(*trouble)->Call(global, 0, NULL); 4609 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener); 4610} 4611 4612 4613TEST(CompilationErrorUsingTryCatchHandler) { 4614 v8::HandleScope scope; 4615 LocalContext env; 4616 v8::TryCatch try_catch; 4617 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile.")); 4618 CHECK_NE(NULL, *try_catch.Exception()); 4619 CHECK(try_catch.HasCaught()); 4620} 4621 4622 4623TEST(TryCatchFinallyUsingTryCatchHandler) { 4624 v8::HandleScope scope; 4625 LocalContext env; 4626 v8::TryCatch try_catch; 4627 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run(); 4628 CHECK(!try_catch.HasCaught()); 4629 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run(); 4630 CHECK(try_catch.HasCaught()); 4631 try_catch.Reset(); 4632 Script::Compile(v8_str("(function() {" 4633 "try { throw ''; } finally { return; }" 4634 "})()"))->Run(); 4635 CHECK(!try_catch.HasCaught()); 4636 Script::Compile(v8_str("(function()" 4637 " { try { throw ''; } finally { throw 0; }" 4638 "})()"))->Run(); 4639 CHECK(try_catch.HasCaught()); 4640} 4641 4642 4643// SecurityHandler can't be run twice 4644TEST(SecurityHandler) { 4645 v8::HandleScope scope0; 4646 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 4647 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback, 4648 IndexedSecurityTestCallback); 4649 // Create an environment 4650 v8::Persistent<Context> context0 = 4651 Context::New(NULL, global_template); 4652 context0->Enter(); 4653 4654 v8::Handle<v8::Object> global0 = context0->Global(); 4655 v8::Handle<Script> script0 = v8_compile("foo = 111"); 4656 script0->Run(); 4657 global0->Set(v8_str("0"), v8_num(999)); 4658 v8::Handle<Value> foo0 = global0->Get(v8_str("foo")); 4659 CHECK_EQ(111, foo0->Int32Value()); 4660 v8::Handle<Value> z0 = global0->Get(v8_str("0")); 4661 CHECK_EQ(999, z0->Int32Value()); 4662 4663 // Create another environment, should fail security checks. 4664 v8::HandleScope scope1; 4665 4666 v8::Persistent<Context> context1 = 4667 Context::New(NULL, global_template); 4668 context1->Enter(); 4669 4670 v8::Handle<v8::Object> global1 = context1->Global(); 4671 global1->Set(v8_str("othercontext"), global0); 4672 // This set will fail the security check. 4673 v8::Handle<Script> script1 = 4674 v8_compile("othercontext.foo = 222; othercontext[0] = 888;"); 4675 script1->Run(); 4676 // This read will pass the security check. 4677 v8::Handle<Value> foo1 = global0->Get(v8_str("foo")); 4678 CHECK_EQ(111, foo1->Int32Value()); 4679 // This read will pass the security check. 4680 v8::Handle<Value> z1 = global0->Get(v8_str("0")); 4681 CHECK_EQ(999, z1->Int32Value()); 4682 4683 // Create another environment, should pass security checks. 4684 { g_security_callback_result = true; // allow security handler to pass. 4685 v8::HandleScope scope2; 4686 LocalContext context2; 4687 v8::Handle<v8::Object> global2 = context2->Global(); 4688 global2->Set(v8_str("othercontext"), global0); 4689 v8::Handle<Script> script2 = 4690 v8_compile("othercontext.foo = 333; othercontext[0] = 888;"); 4691 script2->Run(); 4692 v8::Handle<Value> foo2 = global0->Get(v8_str("foo")); 4693 CHECK_EQ(333, foo2->Int32Value()); 4694 v8::Handle<Value> z2 = global0->Get(v8_str("0")); 4695 CHECK_EQ(888, z2->Int32Value()); 4696 } 4697 4698 context1->Exit(); 4699 context1.Dispose(); 4700 4701 context0->Exit(); 4702 context0.Dispose(); 4703} 4704 4705 4706THREADED_TEST(SecurityChecks) { 4707 v8::HandleScope handle_scope; 4708 LocalContext env1; 4709 v8::Persistent<Context> env2 = Context::New(); 4710 4711 Local<Value> foo = v8_str("foo"); 4712 Local<Value> bar = v8_str("bar"); 4713 4714 // Set to the same domain. 4715 env1->SetSecurityToken(foo); 4716 4717 // Create a function in env1. 4718 Script::Compile(v8_str("spy=function(){return spy;}"))->Run(); 4719 Local<Value> spy = env1->Global()->Get(v8_str("spy")); 4720 CHECK(spy->IsFunction()); 4721 4722 // Create another function accessing global objects. 4723 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run(); 4724 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2")); 4725 CHECK(spy2->IsFunction()); 4726 4727 // Switch to env2 in the same domain and invoke spy on env2. 4728 { 4729 env2->SetSecurityToken(foo); 4730 // Enter env2 4731 Context::Scope scope_env2(env2); 4732 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL); 4733 CHECK(result->IsFunction()); 4734 } 4735 4736 { 4737 env2->SetSecurityToken(bar); 4738 Context::Scope scope_env2(env2); 4739 4740 // Call cross_domain_call, it should throw an exception 4741 v8::TryCatch try_catch; 4742 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL); 4743 CHECK(try_catch.HasCaught()); 4744 } 4745 4746 env2.Dispose(); 4747} 4748 4749 4750// Regression test case for issue 1183439. 4751THREADED_TEST(SecurityChecksForPrototypeChain) { 4752 v8::HandleScope scope; 4753 LocalContext current; 4754 v8::Persistent<Context> other = Context::New(); 4755 4756 // Change context to be able to get to the Object function in the 4757 // other context without hitting the security checks. 4758 v8::Local<Value> other_object; 4759 { Context::Scope scope(other); 4760 other_object = other->Global()->Get(v8_str("Object")); 4761 other->Global()->Set(v8_num(42), v8_num(87)); 4762 } 4763 4764 current->Global()->Set(v8_str("other"), other->Global()); 4765 CHECK(v8_compile("other")->Run()->Equals(other->Global())); 4766 4767 // Make sure the security check fails here and we get an undefined 4768 // result instead of getting the Object function. Repeat in a loop 4769 // to make sure to exercise the IC code. 4770 v8::Local<Script> access_other0 = v8_compile("other.Object"); 4771 v8::Local<Script> access_other1 = v8_compile("other[42]"); 4772 for (int i = 0; i < 5; i++) { 4773 CHECK(!access_other0->Run()->Equals(other_object)); 4774 CHECK(access_other0->Run()->IsUndefined()); 4775 CHECK(!access_other1->Run()->Equals(v8_num(87))); 4776 CHECK(access_other1->Run()->IsUndefined()); 4777 } 4778 4779 // Create an object that has 'other' in its prototype chain and make 4780 // sure we cannot access the Object function indirectly through 4781 // that. Repeat in a loop to make sure to exercise the IC code. 4782 v8_compile("function F() { };" 4783 "F.prototype = other;" 4784 "var f = new F();")->Run(); 4785 v8::Local<Script> access_f0 = v8_compile("f.Object"); 4786 v8::Local<Script> access_f1 = v8_compile("f[42]"); 4787 for (int j = 0; j < 5; j++) { 4788 CHECK(!access_f0->Run()->Equals(other_object)); 4789 CHECK(access_f0->Run()->IsUndefined()); 4790 CHECK(!access_f1->Run()->Equals(v8_num(87))); 4791 CHECK(access_f1->Run()->IsUndefined()); 4792 } 4793 4794 // Now it gets hairy: Set the prototype for the other global object 4795 // to be the current global object. The prototype chain for 'f' now 4796 // goes through 'other' but ends up in the current global object. 4797 { Context::Scope scope(other); 4798 other->Global()->Set(v8_str("__proto__"), current->Global()); 4799 } 4800 // Set a named and an index property on the current global 4801 // object. To force the lookup to go through the other global object, 4802 // the properties must not exist in the other global object. 4803 current->Global()->Set(v8_str("foo"), v8_num(100)); 4804 current->Global()->Set(v8_num(99), v8_num(101)); 4805 // Try to read the properties from f and make sure that the access 4806 // gets stopped by the security checks on the other global object. 4807 Local<Script> access_f2 = v8_compile("f.foo"); 4808 Local<Script> access_f3 = v8_compile("f[99]"); 4809 for (int k = 0; k < 5; k++) { 4810 CHECK(!access_f2->Run()->Equals(v8_num(100))); 4811 CHECK(access_f2->Run()->IsUndefined()); 4812 CHECK(!access_f3->Run()->Equals(v8_num(101))); 4813 CHECK(access_f3->Run()->IsUndefined()); 4814 } 4815 other.Dispose(); 4816} 4817 4818 4819THREADED_TEST(CrossDomainDelete) { 4820 v8::HandleScope handle_scope; 4821 LocalContext env1; 4822 v8::Persistent<Context> env2 = Context::New(); 4823 4824 Local<Value> foo = v8_str("foo"); 4825 Local<Value> bar = v8_str("bar"); 4826 4827 // Set to the same domain. 4828 env1->SetSecurityToken(foo); 4829 env2->SetSecurityToken(foo); 4830 4831 env1->Global()->Set(v8_str("prop"), v8_num(3)); 4832 env2->Global()->Set(v8_str("env1"), env1->Global()); 4833 4834 // Change env2 to a different domain and delete env1.prop. 4835 env2->SetSecurityToken(bar); 4836 { 4837 Context::Scope scope_env2(env2); 4838 Local<Value> result = 4839 Script::Compile(v8_str("delete env1.prop"))->Run(); 4840 CHECK(result->IsFalse()); 4841 } 4842 4843 // Check that env1.prop still exists. 4844 Local<Value> v = env1->Global()->Get(v8_str("prop")); 4845 CHECK(v->IsNumber()); 4846 CHECK_EQ(3, v->Int32Value()); 4847 4848 env2.Dispose(); 4849} 4850 4851 4852THREADED_TEST(CrossDomainIsPropertyEnumerable) { 4853 v8::HandleScope handle_scope; 4854 LocalContext env1; 4855 v8::Persistent<Context> env2 = Context::New(); 4856 4857 Local<Value> foo = v8_str("foo"); 4858 Local<Value> bar = v8_str("bar"); 4859 4860 // Set to the same domain. 4861 env1->SetSecurityToken(foo); 4862 env2->SetSecurityToken(foo); 4863 4864 env1->Global()->Set(v8_str("prop"), v8_num(3)); 4865 env2->Global()->Set(v8_str("env1"), env1->Global()); 4866 4867 // env1.prop is enumerable in env2. 4868 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')"); 4869 { 4870 Context::Scope scope_env2(env2); 4871 Local<Value> result = Script::Compile(test)->Run(); 4872 CHECK(result->IsTrue()); 4873 } 4874 4875 // Change env2 to a different domain and test again. 4876 env2->SetSecurityToken(bar); 4877 { 4878 Context::Scope scope_env2(env2); 4879 Local<Value> result = Script::Compile(test)->Run(); 4880 CHECK(result->IsFalse()); 4881 } 4882 4883 env2.Dispose(); 4884} 4885 4886 4887THREADED_TEST(CrossDomainForIn) { 4888 v8::HandleScope handle_scope; 4889 LocalContext env1; 4890 v8::Persistent<Context> env2 = Context::New(); 4891 4892 Local<Value> foo = v8_str("foo"); 4893 Local<Value> bar = v8_str("bar"); 4894 4895 // Set to the same domain. 4896 env1->SetSecurityToken(foo); 4897 env2->SetSecurityToken(foo); 4898 4899 env1->Global()->Set(v8_str("prop"), v8_num(3)); 4900 env2->Global()->Set(v8_str("env1"), env1->Global()); 4901 4902 // Change env2 to a different domain and set env1's global object 4903 // as the __proto__ of an object in env2 and enumerate properties 4904 // in for-in. It shouldn't enumerate properties on env1's global 4905 // object. 4906 env2->SetSecurityToken(bar); 4907 { 4908 Context::Scope scope_env2(env2); 4909 Local<Value> result = 4910 CompileRun("(function(){var obj = {'__proto__':env1};" 4911 "for (var p in obj)" 4912 " if (p == 'prop') return false;" 4913 "return true;})()"); 4914 CHECK(result->IsTrue()); 4915 } 4916 env2.Dispose(); 4917} 4918 4919 4920TEST(ContextDetachGlobal) { 4921 v8::HandleScope handle_scope; 4922 LocalContext env1; 4923 v8::Persistent<Context> env2 = Context::New(); 4924 4925 Local<v8::Object> global1 = env1->Global(); 4926 4927 Local<Value> foo = v8_str("foo"); 4928 4929 // Set to the same domain. 4930 env1->SetSecurityToken(foo); 4931 env2->SetSecurityToken(foo); 4932 4933 // Enter env2 4934 env2->Enter(); 4935 4936 // Create a function in env2 and add a reference to it in env1. 4937 Local<v8::Object> global2 = env2->Global(); 4938 global2->Set(v8_str("prop"), v8::Integer::New(1)); 4939 CompileRun("function getProp() {return prop;}"); 4940 4941 env1->Global()->Set(v8_str("getProp"), 4942 global2->Get(v8_str("getProp"))); 4943 4944 // Detach env2's global, and reuse the global object of env2 4945 env2->Exit(); 4946 env2->DetachGlobal(); 4947 // env2 has a new global object. 4948 CHECK(!env2->Global()->Equals(global2)); 4949 4950 v8::Persistent<Context> env3 = 4951 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 4952 env3->SetSecurityToken(v8_str("bar")); 4953 env3->Enter(); 4954 4955 Local<v8::Object> global3 = env3->Global(); 4956 CHECK_EQ(global2, global3); 4957 CHECK(global3->Get(v8_str("prop"))->IsUndefined()); 4958 CHECK(global3->Get(v8_str("getProp"))->IsUndefined()); 4959 global3->Set(v8_str("prop"), v8::Integer::New(-1)); 4960 global3->Set(v8_str("prop2"), v8::Integer::New(2)); 4961 env3->Exit(); 4962 4963 // Call getProp in env1, and it should return the value 1 4964 { 4965 Local<Value> get_prop = global1->Get(v8_str("getProp")); 4966 CHECK(get_prop->IsFunction()); 4967 v8::TryCatch try_catch; 4968 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL); 4969 CHECK(!try_catch.HasCaught()); 4970 CHECK_EQ(1, r->Int32Value()); 4971 } 4972 4973 // Check that env3 is not accessible from env1 4974 { 4975 Local<Value> r = global3->Get(v8_str("prop2")); 4976 CHECK(r->IsUndefined()); 4977 } 4978 4979 env2.Dispose(); 4980 env3.Dispose(); 4981} 4982 4983 4984TEST(DetachAndReattachGlobal) { 4985 v8::HandleScope scope; 4986 LocalContext env1; 4987 4988 // Create second environment. 4989 v8::Persistent<Context> env2 = Context::New(); 4990 4991 Local<Value> foo = v8_str("foo"); 4992 4993 // Set same security token for env1 and env2. 4994 env1->SetSecurityToken(foo); 4995 env2->SetSecurityToken(foo); 4996 4997 // Create a property on the global object in env2. 4998 { 4999 v8::Context::Scope scope(env2); 5000 env2->Global()->Set(v8_str("p"), v8::Integer::New(42)); 5001 } 5002 5003 // Create a reference to env2 global from env1 global. 5004 env1->Global()->Set(v8_str("other"), env2->Global()); 5005 5006 // Check that we have access to other.p in env2 from env1. 5007 Local<Value> result = CompileRun("other.p"); 5008 CHECK(result->IsInt32()); 5009 CHECK_EQ(42, result->Int32Value()); 5010 5011 // Hold on to global from env2 and detach global from env2. 5012 Local<v8::Object> global2 = env2->Global(); 5013 env2->DetachGlobal(); 5014 5015 // Check that the global has been detached. No other.p property can 5016 // be found. 5017 result = CompileRun("other.p"); 5018 CHECK(result->IsUndefined()); 5019 5020 // Reuse global2 for env3. 5021 v8::Persistent<Context> env3 = 5022 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 5023 CHECK_EQ(global2, env3->Global()); 5024 5025 // Start by using the same security token for env3 as for env1 and env2. 5026 env3->SetSecurityToken(foo); 5027 5028 // Create a property on the global object in env3. 5029 { 5030 v8::Context::Scope scope(env3); 5031 env3->Global()->Set(v8_str("p"), v8::Integer::New(24)); 5032 } 5033 5034 // Check that other.p is now the property in env3 and that we have access. 5035 result = CompileRun("other.p"); 5036 CHECK(result->IsInt32()); 5037 CHECK_EQ(24, result->Int32Value()); 5038 5039 // Change security token for env3 to something different from env1 and env2. 5040 env3->SetSecurityToken(v8_str("bar")); 5041 5042 // Check that we do not have access to other.p in env1. |other| is now 5043 // the global object for env3 which has a different security token, 5044 // so access should be blocked. 5045 result = CompileRun("other.p"); 5046 CHECK(result->IsUndefined()); 5047 5048 // Detach the global for env3 and reattach it to env2. 5049 env3->DetachGlobal(); 5050 env2->ReattachGlobal(global2); 5051 5052 // Check that we have access to other.p again in env1. |other| is now 5053 // the global object for env2 which has the same security token as env1. 5054 result = CompileRun("other.p"); 5055 CHECK(result->IsInt32()); 5056 CHECK_EQ(42, result->Int32Value()); 5057 5058 env2.Dispose(); 5059 env3.Dispose(); 5060} 5061 5062 5063static bool NamedAccessBlocker(Local<v8::Object> global, 5064 Local<Value> name, 5065 v8::AccessType type, 5066 Local<Value> data) { 5067 return Context::GetCurrent()->Global()->Equals(global); 5068} 5069 5070 5071static bool IndexedAccessBlocker(Local<v8::Object> global, 5072 uint32_t key, 5073 v8::AccessType type, 5074 Local<Value> data) { 5075 return Context::GetCurrent()->Global()->Equals(global); 5076} 5077 5078 5079static int g_echo_value = -1; 5080static v8::Handle<Value> EchoGetter(Local<String> name, 5081 const AccessorInfo& info) { 5082 return v8_num(g_echo_value); 5083} 5084 5085 5086static void EchoSetter(Local<String> name, 5087 Local<Value> value, 5088 const AccessorInfo&) { 5089 if (value->IsNumber()) 5090 g_echo_value = value->Int32Value(); 5091} 5092 5093 5094static v8::Handle<Value> UnreachableGetter(Local<String> name, 5095 const AccessorInfo& info) { 5096 CHECK(false); // This function should not be called.. 5097 return v8::Undefined(); 5098} 5099 5100 5101static void UnreachableSetter(Local<String>, Local<Value>, 5102 const AccessorInfo&) { 5103 CHECK(false); // This function should nto be called. 5104} 5105 5106 5107THREADED_TEST(AccessControl) { 5108 v8::HandleScope handle_scope; 5109 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 5110 5111 global_template->SetAccessCheckCallbacks(NamedAccessBlocker, 5112 IndexedAccessBlocker); 5113 5114 // Add an accessor accessible by cross-domain JS code. 5115 global_template->SetAccessor( 5116 v8_str("accessible_prop"), 5117 EchoGetter, EchoSetter, 5118 v8::Handle<Value>(), 5119 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); 5120 5121 // Add an accessor that is not accessible by cross-domain JS code. 5122 global_template->SetAccessor(v8_str("blocked_prop"), 5123 UnreachableGetter, UnreachableSetter, 5124 v8::Handle<Value>(), 5125 v8::DEFAULT); 5126 5127 // Create an environment 5128 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 5129 context0->Enter(); 5130 5131 v8::Handle<v8::Object> global0 = context0->Global(); 5132 5133 v8::HandleScope scope1; 5134 5135 v8::Persistent<Context> context1 = Context::New(); 5136 context1->Enter(); 5137 5138 v8::Handle<v8::Object> global1 = context1->Global(); 5139 global1->Set(v8_str("other"), global0); 5140 5141 v8::Handle<Value> value; 5142 5143 // Access blocked property 5144 value = v8_compile("other.blocked_prop = 1")->Run(); 5145 value = v8_compile("other.blocked_prop")->Run(); 5146 CHECK(value->IsUndefined()); 5147 5148 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run(); 5149 CHECK(value->IsFalse()); 5150 5151 // Access accessible property 5152 value = v8_compile("other.accessible_prop = 3")->Run(); 5153 CHECK(value->IsNumber()); 5154 CHECK_EQ(3, value->Int32Value()); 5155 CHECK_EQ(3, g_echo_value); 5156 5157 value = v8_compile("other.accessible_prop")->Run(); 5158 CHECK(value->IsNumber()); 5159 CHECK_EQ(3, value->Int32Value()); 5160 5161 value = 5162 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run(); 5163 CHECK(value->IsTrue()); 5164 5165 // Enumeration doesn't enumerate accessors from inaccessible objects in 5166 // the prototype chain even if the accessors are in themselves accessible. 5167 Local<Value> result = 5168 CompileRun("(function(){var obj = {'__proto__':other};" 5169 "for (var p in obj)" 5170 " if (p == 'accessible_prop' || p == 'blocked_prop') {" 5171 " return false;" 5172 " }" 5173 "return true;})()"); 5174 CHECK(result->IsTrue()); 5175 5176 context1->Exit(); 5177 context0->Exit(); 5178 context1.Dispose(); 5179 context0.Dispose(); 5180} 5181 5182 5183static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global, 5184 Local<Value> name, 5185 v8::AccessType type, 5186 Local<Value> data) { 5187 return false; 5188} 5189 5190 5191static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global, 5192 uint32_t key, 5193 v8::AccessType type, 5194 Local<Value> data) { 5195 return false; 5196} 5197 5198 5199THREADED_TEST(AccessControlGetOwnPropertyNames) { 5200 v8::HandleScope handle_scope; 5201 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(); 5202 5203 obj_template->Set(v8_str("x"), v8::Integer::New(42)); 5204 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker, 5205 GetOwnPropertyNamesIndexedBlocker); 5206 5207 // Create an environment 5208 v8::Persistent<Context> context0 = Context::New(NULL, obj_template); 5209 context0->Enter(); 5210 5211 v8::Handle<v8::Object> global0 = context0->Global(); 5212 5213 v8::HandleScope scope1; 5214 5215 v8::Persistent<Context> context1 = Context::New(); 5216 context1->Enter(); 5217 5218 v8::Handle<v8::Object> global1 = context1->Global(); 5219 global1->Set(v8_str("other"), global0); 5220 global1->Set(v8_str("object"), obj_template->NewInstance()); 5221 5222 v8::Handle<Value> value; 5223 5224 // Attempt to get the property names of the other global object and 5225 // of an object that requires access checks. Accessing the other 5226 // global object should be blocked by access checks on the global 5227 // proxy object. Accessing the object that requires access checks 5228 // is blocked by the access checks on the object itself. 5229 value = CompileRun("Object.getOwnPropertyNames(other).length == 0"); 5230 CHECK(value->IsTrue()); 5231 5232 value = CompileRun("Object.getOwnPropertyNames(object).length == 0"); 5233 CHECK(value->IsTrue()); 5234 5235 context1->Exit(); 5236 context0->Exit(); 5237 context1.Dispose(); 5238 context0.Dispose(); 5239} 5240 5241 5242static v8::Handle<v8::Array> NamedPropertyEnumerator(const AccessorInfo& info) { 5243 v8::Handle<v8::Array> result = v8::Array::New(1); 5244 result->Set(0, v8_str("x")); 5245 return result; 5246} 5247 5248 5249THREADED_TEST(GetOwnPropertyNamesWithInterceptor) { 5250 v8::HandleScope handle_scope; 5251 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(); 5252 5253 obj_template->Set(v8_str("x"), v8::Integer::New(42)); 5254 obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL, 5255 NamedPropertyEnumerator); 5256 5257 LocalContext context; 5258 v8::Handle<v8::Object> global = context->Global(); 5259 global->Set(v8_str("object"), obj_template->NewInstance()); 5260 5261 v8::Handle<Value> value = 5262 CompileRun("Object.getOwnPropertyNames(object).join(',')"); 5263 CHECK_EQ(v8_str("x"), value); 5264} 5265 5266 5267static v8::Handle<Value> ConstTenGetter(Local<String> name, 5268 const AccessorInfo& info) { 5269 return v8_num(10); 5270} 5271 5272 5273THREADED_TEST(CrossDomainAccessors) { 5274 v8::HandleScope handle_scope; 5275 5276 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(); 5277 5278 v8::Handle<v8::ObjectTemplate> global_template = 5279 func_template->InstanceTemplate(); 5280 5281 v8::Handle<v8::ObjectTemplate> proto_template = 5282 func_template->PrototypeTemplate(); 5283 5284 // Add an accessor to proto that's accessible by cross-domain JS code. 5285 proto_template->SetAccessor(v8_str("accessible"), 5286 ConstTenGetter, 0, 5287 v8::Handle<Value>(), 5288 v8::ALL_CAN_READ); 5289 5290 // Add an accessor that is not accessible by cross-domain JS code. 5291 global_template->SetAccessor(v8_str("unreachable"), 5292 UnreachableGetter, 0, 5293 v8::Handle<Value>(), 5294 v8::DEFAULT); 5295 5296 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 5297 context0->Enter(); 5298 5299 Local<v8::Object> global = context0->Global(); 5300 // Add a normal property that shadows 'accessible' 5301 global->Set(v8_str("accessible"), v8_num(11)); 5302 5303 // Enter a new context. 5304 v8::HandleScope scope1; 5305 v8::Persistent<Context> context1 = Context::New(); 5306 context1->Enter(); 5307 5308 v8::Handle<v8::Object> global1 = context1->Global(); 5309 global1->Set(v8_str("other"), global); 5310 5311 // Should return 10, instead of 11 5312 v8::Handle<Value> value = v8_compile("other.accessible")->Run(); 5313 CHECK(value->IsNumber()); 5314 CHECK_EQ(10, value->Int32Value()); 5315 5316 value = v8_compile("other.unreachable")->Run(); 5317 CHECK(value->IsUndefined()); 5318 5319 context1->Exit(); 5320 context0->Exit(); 5321 context1.Dispose(); 5322 context0.Dispose(); 5323} 5324 5325 5326static int named_access_count = 0; 5327static int indexed_access_count = 0; 5328 5329static bool NamedAccessCounter(Local<v8::Object> global, 5330 Local<Value> name, 5331 v8::AccessType type, 5332 Local<Value> data) { 5333 named_access_count++; 5334 return true; 5335} 5336 5337 5338static bool IndexedAccessCounter(Local<v8::Object> global, 5339 uint32_t key, 5340 v8::AccessType type, 5341 Local<Value> data) { 5342 indexed_access_count++; 5343 return true; 5344} 5345 5346 5347// This one is too easily disturbed by other tests. 5348TEST(AccessControlIC) { 5349 named_access_count = 0; 5350 indexed_access_count = 0; 5351 5352 v8::HandleScope handle_scope; 5353 5354 // Create an environment. 5355 v8::Persistent<Context> context0 = Context::New(); 5356 context0->Enter(); 5357 5358 // Create an object that requires access-check functions to be 5359 // called for cross-domain access. 5360 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 5361 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 5362 IndexedAccessCounter); 5363 Local<v8::Object> object = object_template->NewInstance(); 5364 5365 v8::HandleScope scope1; 5366 5367 // Create another environment. 5368 v8::Persistent<Context> context1 = Context::New(); 5369 context1->Enter(); 5370 5371 // Make easy access to the object from the other environment. 5372 v8::Handle<v8::Object> global1 = context1->Global(); 5373 global1->Set(v8_str("obj"), object); 5374 5375 v8::Handle<Value> value; 5376 5377 // Check that the named access-control function is called every time. 5378 CompileRun("function testProp(obj) {" 5379 " for (var i = 0; i < 10; i++) obj.prop = 1;" 5380 " for (var j = 0; j < 10; j++) obj.prop;" 5381 " return obj.prop" 5382 "}"); 5383 value = CompileRun("testProp(obj)"); 5384 CHECK(value->IsNumber()); 5385 CHECK_EQ(1, value->Int32Value()); 5386 CHECK_EQ(21, named_access_count); 5387 5388 // Check that the named access-control function is called every time. 5389 CompileRun("var p = 'prop';" 5390 "function testKeyed(obj) {" 5391 " for (var i = 0; i < 10; i++) obj[p] = 1;" 5392 " for (var j = 0; j < 10; j++) obj[p];" 5393 " return obj[p];" 5394 "}"); 5395 // Use obj which requires access checks. No inline caching is used 5396 // in that case. 5397 value = CompileRun("testKeyed(obj)"); 5398 CHECK(value->IsNumber()); 5399 CHECK_EQ(1, value->Int32Value()); 5400 CHECK_EQ(42, named_access_count); 5401 // Force the inline caches into generic state and try again. 5402 CompileRun("testKeyed({ a: 0 })"); 5403 CompileRun("testKeyed({ b: 0 })"); 5404 value = CompileRun("testKeyed(obj)"); 5405 CHECK(value->IsNumber()); 5406 CHECK_EQ(1, value->Int32Value()); 5407 CHECK_EQ(63, named_access_count); 5408 5409 // Check that the indexed access-control function is called every time. 5410 CompileRun("function testIndexed(obj) {" 5411 " for (var i = 0; i < 10; i++) obj[0] = 1;" 5412 " for (var j = 0; j < 10; j++) obj[0];" 5413 " return obj[0]" 5414 "}"); 5415 value = CompileRun("testIndexed(obj)"); 5416 CHECK(value->IsNumber()); 5417 CHECK_EQ(1, value->Int32Value()); 5418 CHECK_EQ(21, indexed_access_count); 5419 // Force the inline caches into generic state. 5420 CompileRun("testIndexed(new Array(1))"); 5421 // Test that the indexed access check is called. 5422 value = CompileRun("testIndexed(obj)"); 5423 CHECK(value->IsNumber()); 5424 CHECK_EQ(1, value->Int32Value()); 5425 CHECK_EQ(42, indexed_access_count); 5426 5427 // Check that the named access check is called when invoking 5428 // functions on an object that requires access checks. 5429 CompileRun("obj.f = function() {}"); 5430 CompileRun("function testCallNormal(obj) {" 5431 " for (var i = 0; i < 10; i++) obj.f();" 5432 "}"); 5433 CompileRun("testCallNormal(obj)"); 5434 CHECK_EQ(74, named_access_count); 5435 5436 // Force obj into slow case. 5437 value = CompileRun("delete obj.prop"); 5438 CHECK(value->BooleanValue()); 5439 // Force inline caches into dictionary probing mode. 5440 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);"); 5441 // Test that the named access check is called. 5442 value = CompileRun("testProp(obj);"); 5443 CHECK(value->IsNumber()); 5444 CHECK_EQ(1, value->Int32Value()); 5445 CHECK_EQ(96, named_access_count); 5446 5447 // Force the call inline cache into dictionary probing mode. 5448 CompileRun("o.f = function() {}; testCallNormal(o)"); 5449 // Test that the named access check is still called for each 5450 // invocation of the function. 5451 value = CompileRun("testCallNormal(obj)"); 5452 CHECK_EQ(106, named_access_count); 5453 5454 context1->Exit(); 5455 context0->Exit(); 5456 context1.Dispose(); 5457 context0.Dispose(); 5458} 5459 5460 5461static bool NamedAccessFlatten(Local<v8::Object> global, 5462 Local<Value> name, 5463 v8::AccessType type, 5464 Local<Value> data) { 5465 char buf[100]; 5466 int len; 5467 5468 CHECK(name->IsString()); 5469 5470 memset(buf, 0x1, sizeof(buf)); 5471 len = name.As<String>()->WriteAscii(buf); 5472 CHECK_EQ(4, len); 5473 5474 uint16_t buf2[100]; 5475 5476 memset(buf, 0x1, sizeof(buf)); 5477 len = name.As<String>()->Write(buf2); 5478 CHECK_EQ(4, len); 5479 5480 return true; 5481} 5482 5483 5484static bool IndexedAccessFlatten(Local<v8::Object> global, 5485 uint32_t key, 5486 v8::AccessType type, 5487 Local<Value> data) { 5488 return true; 5489} 5490 5491 5492// Regression test. In access checks, operations that may cause 5493// garbage collection are not allowed. It used to be the case that 5494// using the Write operation on a string could cause a garbage 5495// collection due to flattening of the string. This is no longer the 5496// case. 5497THREADED_TEST(AccessControlFlatten) { 5498 named_access_count = 0; 5499 indexed_access_count = 0; 5500 5501 v8::HandleScope handle_scope; 5502 5503 // Create an environment. 5504 v8::Persistent<Context> context0 = Context::New(); 5505 context0->Enter(); 5506 5507 // Create an object that requires access-check functions to be 5508 // called for cross-domain access. 5509 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 5510 object_template->SetAccessCheckCallbacks(NamedAccessFlatten, 5511 IndexedAccessFlatten); 5512 Local<v8::Object> object = object_template->NewInstance(); 5513 5514 v8::HandleScope scope1; 5515 5516 // Create another environment. 5517 v8::Persistent<Context> context1 = Context::New(); 5518 context1->Enter(); 5519 5520 // Make easy access to the object from the other environment. 5521 v8::Handle<v8::Object> global1 = context1->Global(); 5522 global1->Set(v8_str("obj"), object); 5523 5524 v8::Handle<Value> value; 5525 5526 value = v8_compile("var p = 'as' + 'df';")->Run(); 5527 value = v8_compile("obj[p];")->Run(); 5528 5529 context1->Exit(); 5530 context0->Exit(); 5531 context1.Dispose(); 5532 context0.Dispose(); 5533} 5534 5535 5536static v8::Handle<Value> AccessControlNamedGetter( 5537 Local<String>, const AccessorInfo&) { 5538 return v8::Integer::New(42); 5539} 5540 5541 5542static v8::Handle<Value> AccessControlNamedSetter( 5543 Local<String>, Local<Value> value, const AccessorInfo&) { 5544 return value; 5545} 5546 5547 5548static v8::Handle<Value> AccessControlIndexedGetter( 5549 uint32_t index, 5550 const AccessorInfo& info) { 5551 return v8_num(42); 5552} 5553 5554 5555static v8::Handle<Value> AccessControlIndexedSetter( 5556 uint32_t, Local<Value> value, const AccessorInfo&) { 5557 return value; 5558} 5559 5560 5561THREADED_TEST(AccessControlInterceptorIC) { 5562 named_access_count = 0; 5563 indexed_access_count = 0; 5564 5565 v8::HandleScope handle_scope; 5566 5567 // Create an environment. 5568 v8::Persistent<Context> context0 = Context::New(); 5569 context0->Enter(); 5570 5571 // Create an object that requires access-check functions to be 5572 // called for cross-domain access. The object also has interceptors 5573 // interceptor. 5574 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 5575 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 5576 IndexedAccessCounter); 5577 object_template->SetNamedPropertyHandler(AccessControlNamedGetter, 5578 AccessControlNamedSetter); 5579 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter, 5580 AccessControlIndexedSetter); 5581 Local<v8::Object> object = object_template->NewInstance(); 5582 5583 v8::HandleScope scope1; 5584 5585 // Create another environment. 5586 v8::Persistent<Context> context1 = Context::New(); 5587 context1->Enter(); 5588 5589 // Make easy access to the object from the other environment. 5590 v8::Handle<v8::Object> global1 = context1->Global(); 5591 global1->Set(v8_str("obj"), object); 5592 5593 v8::Handle<Value> value; 5594 5595 // Check that the named access-control function is called every time 5596 // eventhough there is an interceptor on the object. 5597 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run(); 5598 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;" 5599 "obj.x")->Run(); 5600 CHECK(value->IsNumber()); 5601 CHECK_EQ(42, value->Int32Value()); 5602 CHECK_EQ(21, named_access_count); 5603 5604 value = v8_compile("var p = 'x';")->Run(); 5605 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run(); 5606 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];" 5607 "obj[p]")->Run(); 5608 CHECK(value->IsNumber()); 5609 CHECK_EQ(42, value->Int32Value()); 5610 CHECK_EQ(42, named_access_count); 5611 5612 // Check that the indexed access-control function is called every 5613 // time eventhough there is an interceptor on the object. 5614 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run(); 5615 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];" 5616 "obj[0]")->Run(); 5617 CHECK(value->IsNumber()); 5618 CHECK_EQ(42, value->Int32Value()); 5619 CHECK_EQ(21, indexed_access_count); 5620 5621 context1->Exit(); 5622 context0->Exit(); 5623 context1.Dispose(); 5624 context0.Dispose(); 5625} 5626 5627 5628THREADED_TEST(Version) { 5629 v8::V8::GetVersion(); 5630} 5631 5632 5633static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) { 5634 ApiTestFuzzer::Fuzz(); 5635 return v8_num(12); 5636} 5637 5638 5639THREADED_TEST(InstanceProperties) { 5640 v8::HandleScope handle_scope; 5641 LocalContext context; 5642 5643 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5644 Local<ObjectTemplate> instance = t->InstanceTemplate(); 5645 5646 instance->Set(v8_str("x"), v8_num(42)); 5647 instance->Set(v8_str("f"), 5648 v8::FunctionTemplate::New(InstanceFunctionCallback)); 5649 5650 Local<Value> o = t->GetFunction()->NewInstance(); 5651 5652 context->Global()->Set(v8_str("i"), o); 5653 Local<Value> value = Script::Compile(v8_str("i.x"))->Run(); 5654 CHECK_EQ(42, value->Int32Value()); 5655 5656 value = Script::Compile(v8_str("i.f()"))->Run(); 5657 CHECK_EQ(12, value->Int32Value()); 5658} 5659 5660 5661static v8::Handle<Value> 5662GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) { 5663 ApiTestFuzzer::Fuzz(); 5664 return v8::Handle<Value>(); 5665} 5666 5667 5668THREADED_TEST(GlobalObjectInstanceProperties) { 5669 v8::HandleScope handle_scope; 5670 5671 Local<Value> global_object; 5672 5673 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5674 t->InstanceTemplate()->SetNamedPropertyHandler( 5675 GlobalObjectInstancePropertiesGet); 5676 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 5677 instance_template->Set(v8_str("x"), v8_num(42)); 5678 instance_template->Set(v8_str("f"), 5679 v8::FunctionTemplate::New(InstanceFunctionCallback)); 5680 5681 { 5682 LocalContext env(NULL, instance_template); 5683 // Hold on to the global object so it can be used again in another 5684 // environment initialization. 5685 global_object = env->Global(); 5686 5687 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 5688 CHECK_EQ(42, value->Int32Value()); 5689 value = Script::Compile(v8_str("f()"))->Run(); 5690 CHECK_EQ(12, value->Int32Value()); 5691 } 5692 5693 { 5694 // Create new environment reusing the global object. 5695 LocalContext env(NULL, instance_template, global_object); 5696 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 5697 CHECK_EQ(42, value->Int32Value()); 5698 value = Script::Compile(v8_str("f()"))->Run(); 5699 CHECK_EQ(12, value->Int32Value()); 5700 } 5701} 5702 5703 5704static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) { 5705 ApiTestFuzzer::Fuzz(); 5706 return v8_num(42); 5707} 5708 5709 5710static int shadow_y; 5711static int shadow_y_setter_call_count; 5712static int shadow_y_getter_call_count; 5713 5714 5715static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) { 5716 shadow_y_setter_call_count++; 5717 shadow_y = 42; 5718} 5719 5720 5721static v8::Handle<Value> ShadowYGetter(Local<String> name, 5722 const AccessorInfo& info) { 5723 ApiTestFuzzer::Fuzz(); 5724 shadow_y_getter_call_count++; 5725 return v8_num(shadow_y); 5726} 5727 5728 5729static v8::Handle<Value> ShadowIndexedGet(uint32_t index, 5730 const AccessorInfo& info) { 5731 return v8::Handle<Value>(); 5732} 5733 5734 5735static v8::Handle<Value> ShadowNamedGet(Local<String> key, 5736 const AccessorInfo&) { 5737 return v8::Handle<Value>(); 5738} 5739 5740 5741THREADED_TEST(ShadowObject) { 5742 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0; 5743 v8::HandleScope handle_scope; 5744 5745 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New(); 5746 LocalContext context(NULL, global_template); 5747 5748 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5749 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet); 5750 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet); 5751 Local<ObjectTemplate> proto = t->PrototypeTemplate(); 5752 Local<ObjectTemplate> instance = t->InstanceTemplate(); 5753 5754 // Only allow calls of f on instances of t. 5755 Local<v8::Signature> signature = v8::Signature::New(t); 5756 proto->Set(v8_str("f"), 5757 v8::FunctionTemplate::New(ShadowFunctionCallback, 5758 Local<Value>(), 5759 signature)); 5760 proto->Set(v8_str("x"), v8_num(12)); 5761 5762 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter); 5763 5764 Local<Value> o = t->GetFunction()->NewInstance(); 5765 context->Global()->Set(v8_str("__proto__"), o); 5766 5767 Local<Value> value = 5768 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run(); 5769 CHECK(value->IsBoolean()); 5770 CHECK(!value->BooleanValue()); 5771 5772 value = Script::Compile(v8_str("x"))->Run(); 5773 CHECK_EQ(12, value->Int32Value()); 5774 5775 value = Script::Compile(v8_str("f()"))->Run(); 5776 CHECK_EQ(42, value->Int32Value()); 5777 5778 Script::Compile(v8_str("y = 42"))->Run(); 5779 CHECK_EQ(1, shadow_y_setter_call_count); 5780 value = Script::Compile(v8_str("y"))->Run(); 5781 CHECK_EQ(1, shadow_y_getter_call_count); 5782 CHECK_EQ(42, value->Int32Value()); 5783} 5784 5785 5786THREADED_TEST(HiddenPrototype) { 5787 v8::HandleScope handle_scope; 5788 LocalContext context; 5789 5790 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 5791 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 5792 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 5793 t1->SetHiddenPrototype(true); 5794 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 5795 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 5796 t2->SetHiddenPrototype(true); 5797 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 5798 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 5799 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 5800 5801 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 5802 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 5803 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 5804 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 5805 5806 // Setting the prototype on an object skips hidden prototypes. 5807 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5808 o0->Set(v8_str("__proto__"), o1); 5809 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5810 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5811 o0->Set(v8_str("__proto__"), o2); 5812 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5813 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5814 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5815 o0->Set(v8_str("__proto__"), o3); 5816 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5817 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5818 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5819 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 5820 5821 // Getting the prototype of o0 should get the first visible one 5822 // which is o3. Therefore, z should not be defined on the prototype 5823 // object. 5824 Local<Value> proto = o0->Get(v8_str("__proto__")); 5825 CHECK(proto->IsObject()); 5826 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined()); 5827} 5828 5829 5830THREADED_TEST(SetPrototype) { 5831 v8::HandleScope handle_scope; 5832 LocalContext context; 5833 5834 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 5835 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 5836 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 5837 t1->SetHiddenPrototype(true); 5838 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 5839 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 5840 t2->SetHiddenPrototype(true); 5841 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 5842 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 5843 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 5844 5845 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 5846 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 5847 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 5848 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 5849 5850 // Setting the prototype on an object does not skip hidden prototypes. 5851 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5852 CHECK(o0->SetPrototype(o1)); 5853 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5854 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5855 CHECK(o1->SetPrototype(o2)); 5856 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5857 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5858 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5859 CHECK(o2->SetPrototype(o3)); 5860 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5861 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5862 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5863 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 5864 5865 // Getting the prototype of o0 should get the first visible one 5866 // which is o3. Therefore, z should not be defined on the prototype 5867 // object. 5868 Local<Value> proto = o0->Get(v8_str("__proto__")); 5869 CHECK(proto->IsObject()); 5870 CHECK_EQ(proto.As<v8::Object>(), o3); 5871 5872 // However, Object::GetPrototype ignores hidden prototype. 5873 Local<Value> proto0 = o0->GetPrototype(); 5874 CHECK(proto0->IsObject()); 5875 CHECK_EQ(proto0.As<v8::Object>(), o1); 5876 5877 Local<Value> proto1 = o1->GetPrototype(); 5878 CHECK(proto1->IsObject()); 5879 CHECK_EQ(proto1.As<v8::Object>(), o2); 5880 5881 Local<Value> proto2 = o2->GetPrototype(); 5882 CHECK(proto2->IsObject()); 5883 CHECK_EQ(proto2.As<v8::Object>(), o3); 5884} 5885 5886 5887THREADED_TEST(SetPrototypeThrows) { 5888 v8::HandleScope handle_scope; 5889 LocalContext context; 5890 5891 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5892 5893 Local<v8::Object> o0 = t->GetFunction()->NewInstance(); 5894 Local<v8::Object> o1 = t->GetFunction()->NewInstance(); 5895 5896 CHECK(o0->SetPrototype(o1)); 5897 // If setting the prototype leads to the cycle, SetPrototype should 5898 // return false and keep VM in sane state. 5899 v8::TryCatch try_catch; 5900 CHECK(!o1->SetPrototype(o0)); 5901 CHECK(!try_catch.HasCaught()); 5902 ASSERT(!i::Top::has_pending_exception()); 5903 5904 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value()); 5905} 5906 5907 5908THREADED_TEST(GetterSetterExceptions) { 5909 v8::HandleScope handle_scope; 5910 LocalContext context; 5911 CompileRun( 5912 "function Foo() { };" 5913 "function Throw() { throw 5; };" 5914 "var x = { };" 5915 "x.__defineSetter__('set', Throw);" 5916 "x.__defineGetter__('get', Throw);"); 5917 Local<v8::Object> x = 5918 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x"))); 5919 v8::TryCatch try_catch; 5920 x->Set(v8_str("set"), v8::Integer::New(8)); 5921 x->Get(v8_str("get")); 5922 x->Set(v8_str("set"), v8::Integer::New(8)); 5923 x->Get(v8_str("get")); 5924 x->Set(v8_str("set"), v8::Integer::New(8)); 5925 x->Get(v8_str("get")); 5926 x->Set(v8_str("set"), v8::Integer::New(8)); 5927 x->Get(v8_str("get")); 5928} 5929 5930 5931THREADED_TEST(Constructor) { 5932 v8::HandleScope handle_scope; 5933 LocalContext context; 5934 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 5935 templ->SetClassName(v8_str("Fun")); 5936 Local<Function> cons = templ->GetFunction(); 5937 context->Global()->Set(v8_str("Fun"), cons); 5938 Local<v8::Object> inst = cons->NewInstance(); 5939 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst); 5940 Local<Value> value = CompileRun("(new Fun()).constructor === Fun"); 5941 CHECK(value->BooleanValue()); 5942} 5943 5944THREADED_TEST(FunctionDescriptorException) { 5945 v8::HandleScope handle_scope; 5946 LocalContext context; 5947 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 5948 templ->SetClassName(v8_str("Fun")); 5949 Local<Function> cons = templ->GetFunction(); 5950 context->Global()->Set(v8_str("Fun"), cons); 5951 Local<Value> value = CompileRun( 5952 "function test() {" 5953 " try {" 5954 " (new Fun()).blah()" 5955 " } catch (e) {" 5956 " var str = String(e);" 5957 " if (str.indexOf('TypeError') == -1) return 1;" 5958 " if (str.indexOf('[object Fun]') != -1) return 2;" 5959 " if (str.indexOf('#<a Fun>') == -1) return 3;" 5960 " return 0;" 5961 " }" 5962 " return 4;" 5963 "}" 5964 "test();"); 5965 CHECK_EQ(0, value->Int32Value()); 5966} 5967 5968 5969THREADED_TEST(EvalAliasedDynamic) { 5970 v8::HandleScope scope; 5971 LocalContext current; 5972 5973 // Tests where aliased eval can only be resolved dynamically. 5974 Local<Script> script = 5975 Script::Compile(v8_str("function f(x) { " 5976 " var foo = 2;" 5977 " with (x) { return eval('foo'); }" 5978 "}" 5979 "foo = 0;" 5980 "result1 = f(new Object());" 5981 "result2 = f(this);" 5982 "var x = new Object();" 5983 "x.eval = function(x) { return 1; };" 5984 "result3 = f(x);")); 5985 script->Run(); 5986 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value()); 5987 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value()); 5988 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value()); 5989 5990 v8::TryCatch try_catch; 5991 script = 5992 Script::Compile(v8_str("function f(x) { " 5993 " var bar = 2;" 5994 " with (x) { return eval('bar'); }" 5995 "}" 5996 "f(this)")); 5997 script->Run(); 5998 CHECK(try_catch.HasCaught()); 5999 try_catch.Reset(); 6000} 6001 6002 6003THREADED_TEST(CrossEval) { 6004 v8::HandleScope scope; 6005 LocalContext other; 6006 LocalContext current; 6007 6008 Local<String> token = v8_str("<security token>"); 6009 other->SetSecurityToken(token); 6010 current->SetSecurityToken(token); 6011 6012 // Setup reference from current to other. 6013 current->Global()->Set(v8_str("other"), other->Global()); 6014 6015 // Check that new variables are introduced in other context. 6016 Local<Script> script = 6017 Script::Compile(v8_str("other.eval('var foo = 1234')")); 6018 script->Run(); 6019 Local<Value> foo = other->Global()->Get(v8_str("foo")); 6020 CHECK_EQ(1234, foo->Int32Value()); 6021 CHECK(!current->Global()->Has(v8_str("foo"))); 6022 6023 // Check that writing to non-existing properties introduces them in 6024 // the other context. 6025 script = 6026 Script::Compile(v8_str("other.eval('na = 1234')")); 6027 script->Run(); 6028 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value()); 6029 CHECK(!current->Global()->Has(v8_str("na"))); 6030 6031 // Check that global variables in current context are not visible in other 6032 // context. 6033 v8::TryCatch try_catch; 6034 script = 6035 Script::Compile(v8_str("var bar = 42; other.eval('bar');")); 6036 Local<Value> result = script->Run(); 6037 CHECK(try_catch.HasCaught()); 6038 try_catch.Reset(); 6039 6040 // Check that local variables in current context are not visible in other 6041 // context. 6042 script = 6043 Script::Compile(v8_str("(function() { " 6044 " var baz = 87;" 6045 " return other.eval('baz');" 6046 "})();")); 6047 result = script->Run(); 6048 CHECK(try_catch.HasCaught()); 6049 try_catch.Reset(); 6050 6051 // Check that global variables in the other environment are visible 6052 // when evaluting code. 6053 other->Global()->Set(v8_str("bis"), v8_num(1234)); 6054 script = Script::Compile(v8_str("other.eval('bis')")); 6055 CHECK_EQ(1234, script->Run()->Int32Value()); 6056 CHECK(!try_catch.HasCaught()); 6057 6058 // Check that the 'this' pointer points to the global object evaluating 6059 // code. 6060 other->Global()->Set(v8_str("t"), other->Global()); 6061 script = Script::Compile(v8_str("other.eval('this == t')")); 6062 result = script->Run(); 6063 CHECK(result->IsTrue()); 6064 CHECK(!try_catch.HasCaught()); 6065 6066 // Check that variables introduced in with-statement are not visible in 6067 // other context. 6068 script = 6069 Script::Compile(v8_str("with({x:2}){other.eval('x')}")); 6070 result = script->Run(); 6071 CHECK(try_catch.HasCaught()); 6072 try_catch.Reset(); 6073 6074 // Check that you cannot use 'eval.call' with another object than the 6075 // current global object. 6076 script = 6077 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')")); 6078 result = script->Run(); 6079 CHECK(try_catch.HasCaught()); 6080} 6081 6082 6083// Test that calling eval in a context which has been detached from 6084// its global throws an exception. This behavior is consistent with 6085// other JavaScript implementations. 6086THREADED_TEST(EvalInDetachedGlobal) { 6087 v8::HandleScope scope; 6088 6089 v8::Persistent<Context> context0 = Context::New(); 6090 v8::Persistent<Context> context1 = Context::New(); 6091 6092 // Setup function in context0 that uses eval from context0. 6093 context0->Enter(); 6094 v8::Handle<v8::Value> fun = 6095 CompileRun("var x = 42;" 6096 "(function() {" 6097 " var e = eval;" 6098 " return function(s) { return e(s); }" 6099 "})()"); 6100 context0->Exit(); 6101 6102 // Put the function into context1 and call it before and after 6103 // detaching the global. Before detaching, the call succeeds and 6104 // after detaching and exception is thrown. 6105 context1->Enter(); 6106 context1->Global()->Set(v8_str("fun"), fun); 6107 v8::Handle<v8::Value> x_value = CompileRun("fun('x')"); 6108 CHECK_EQ(42, x_value->Int32Value()); 6109 context0->DetachGlobal(); 6110 v8::TryCatch catcher; 6111 x_value = CompileRun("fun('x')"); 6112 CHECK(x_value.IsEmpty()); 6113 CHECK(catcher.HasCaught()); 6114 context1->Exit(); 6115 6116 context1.Dispose(); 6117 context0.Dispose(); 6118} 6119 6120 6121THREADED_TEST(CrossLazyLoad) { 6122 v8::HandleScope scope; 6123 LocalContext other; 6124 LocalContext current; 6125 6126 Local<String> token = v8_str("<security token>"); 6127 other->SetSecurityToken(token); 6128 current->SetSecurityToken(token); 6129 6130 // Setup reference from current to other. 6131 current->Global()->Set(v8_str("other"), other->Global()); 6132 6133 // Trigger lazy loading in other context. 6134 Local<Script> script = 6135 Script::Compile(v8_str("other.eval('new Date(42)')")); 6136 Local<Value> value = script->Run(); 6137 CHECK_EQ(42.0, value->NumberValue()); 6138} 6139 6140 6141static v8::Handle<Value> call_as_function(const v8::Arguments& args) { 6142 ApiTestFuzzer::Fuzz(); 6143 if (args.IsConstructCall()) { 6144 if (args[0]->IsInt32()) { 6145 return v8_num(-args[0]->Int32Value()); 6146 } 6147 } 6148 6149 return args[0]; 6150} 6151 6152 6153// Test that a call handler can be set for objects which will allow 6154// non-function objects created through the API to be called as 6155// functions. 6156THREADED_TEST(CallAsFunction) { 6157 v8::HandleScope scope; 6158 LocalContext context; 6159 6160 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 6161 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 6162 instance_template->SetCallAsFunctionHandler(call_as_function); 6163 Local<v8::Object> instance = t->GetFunction()->NewInstance(); 6164 context->Global()->Set(v8_str("obj"), instance); 6165 v8::TryCatch try_catch; 6166 Local<Value> value; 6167 CHECK(!try_catch.HasCaught()); 6168 6169 value = CompileRun("obj(42)"); 6170 CHECK(!try_catch.HasCaught()); 6171 CHECK_EQ(42, value->Int32Value()); 6172 6173 value = CompileRun("(function(o){return o(49)})(obj)"); 6174 CHECK(!try_catch.HasCaught()); 6175 CHECK_EQ(49, value->Int32Value()); 6176 6177 // test special case of call as function 6178 value = CompileRun("[obj]['0'](45)"); 6179 CHECK(!try_catch.HasCaught()); 6180 CHECK_EQ(45, value->Int32Value()); 6181 6182 value = CompileRun("obj.call = Function.prototype.call;" 6183 "obj.call(null, 87)"); 6184 CHECK(!try_catch.HasCaught()); 6185 CHECK_EQ(87, value->Int32Value()); 6186 6187 // Regression tests for bug #1116356: Calling call through call/apply 6188 // must work for non-function receivers. 6189 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])"; 6190 value = CompileRun(apply_99); 6191 CHECK(!try_catch.HasCaught()); 6192 CHECK_EQ(99, value->Int32Value()); 6193 6194 const char* call_17 = "Function.prototype.call.call(obj, this, 17)"; 6195 value = CompileRun(call_17); 6196 CHECK(!try_catch.HasCaught()); 6197 CHECK_EQ(17, value->Int32Value()); 6198 6199 // Check that the call-as-function handler can be called through 6200 // new. 6201 value = CompileRun("new obj(43)"); 6202 CHECK(!try_catch.HasCaught()); 6203 CHECK_EQ(-43, value->Int32Value()); 6204} 6205 6206 6207static int CountHandles() { 6208 return v8::HandleScope::NumberOfHandles(); 6209} 6210 6211 6212static int Recurse(int depth, int iterations) { 6213 v8::HandleScope scope; 6214 if (depth == 0) return CountHandles(); 6215 for (int i = 0; i < iterations; i++) { 6216 Local<v8::Number> n = v8::Integer::New(42); 6217 } 6218 return Recurse(depth - 1, iterations); 6219} 6220 6221 6222THREADED_TEST(HandleIteration) { 6223 static const int kIterations = 500; 6224 static const int kNesting = 200; 6225 CHECK_EQ(0, CountHandles()); 6226 { 6227 v8::HandleScope scope1; 6228 CHECK_EQ(0, CountHandles()); 6229 for (int i = 0; i < kIterations; i++) { 6230 Local<v8::Number> n = v8::Integer::New(42); 6231 CHECK_EQ(i + 1, CountHandles()); 6232 } 6233 6234 CHECK_EQ(kIterations, CountHandles()); 6235 { 6236 v8::HandleScope scope2; 6237 for (int j = 0; j < kIterations; j++) { 6238 Local<v8::Number> n = v8::Integer::New(42); 6239 CHECK_EQ(j + 1 + kIterations, CountHandles()); 6240 } 6241 } 6242 CHECK_EQ(kIterations, CountHandles()); 6243 } 6244 CHECK_EQ(0, CountHandles()); 6245 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations)); 6246} 6247 6248 6249static v8::Handle<Value> InterceptorHasOwnPropertyGetter( 6250 Local<String> name, 6251 const AccessorInfo& info) { 6252 ApiTestFuzzer::Fuzz(); 6253 return v8::Handle<Value>(); 6254} 6255 6256 6257THREADED_TEST(InterceptorHasOwnProperty) { 6258 v8::HandleScope scope; 6259 LocalContext context; 6260 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6261 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 6262 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter); 6263 Local<Function> function = fun_templ->GetFunction(); 6264 context->Global()->Set(v8_str("constructor"), function); 6265 v8::Handle<Value> value = CompileRun( 6266 "var o = new constructor();" 6267 "o.hasOwnProperty('ostehaps');"); 6268 CHECK_EQ(false, value->BooleanValue()); 6269 value = CompileRun( 6270 "o.ostehaps = 42;" 6271 "o.hasOwnProperty('ostehaps');"); 6272 CHECK_EQ(true, value->BooleanValue()); 6273 value = CompileRun( 6274 "var p = new constructor();" 6275 "p.hasOwnProperty('ostehaps');"); 6276 CHECK_EQ(false, value->BooleanValue()); 6277} 6278 6279 6280static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC( 6281 Local<String> name, 6282 const AccessorInfo& info) { 6283 ApiTestFuzzer::Fuzz(); 6284 i::Heap::CollectAllGarbage(false); 6285 return v8::Handle<Value>(); 6286} 6287 6288 6289THREADED_TEST(InterceptorHasOwnPropertyCausingGC) { 6290 v8::HandleScope scope; 6291 LocalContext context; 6292 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6293 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 6294 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC); 6295 Local<Function> function = fun_templ->GetFunction(); 6296 context->Global()->Set(v8_str("constructor"), function); 6297 // Let's first make some stuff so we can be sure to get a good GC. 6298 CompileRun( 6299 "function makestr(size) {" 6300 " switch (size) {" 6301 " case 1: return 'f';" 6302 " case 2: return 'fo';" 6303 " case 3: return 'foo';" 6304 " }" 6305 " return makestr(size >> 1) + makestr((size + 1) >> 1);" 6306 "}" 6307 "var x = makestr(12345);" 6308 "x = makestr(31415);" 6309 "x = makestr(23456);"); 6310 v8::Handle<Value> value = CompileRun( 6311 "var o = new constructor();" 6312 "o.__proto__ = new String(x);" 6313 "o.hasOwnProperty('ostehaps');"); 6314 CHECK_EQ(false, value->BooleanValue()); 6315} 6316 6317 6318typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property, 6319 const AccessorInfo& info); 6320 6321 6322static void CheckInterceptorLoadIC(NamedPropertyGetter getter, 6323 const char* source, 6324 int expected) { 6325 v8::HandleScope scope; 6326 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6327 templ->SetNamedPropertyHandler(getter, 0, 0, 0, 0, v8_str("data")); 6328 LocalContext context; 6329 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6330 v8::Handle<Value> value = CompileRun(source); 6331 CHECK_EQ(expected, value->Int32Value()); 6332} 6333 6334 6335static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name, 6336 const AccessorInfo& info) { 6337 ApiTestFuzzer::Fuzz(); 6338 CHECK_EQ(v8_str("data"), info.Data()); 6339 CHECK_EQ(v8_str("x"), name); 6340 return v8::Integer::New(42); 6341} 6342 6343 6344// This test should hit the load IC for the interceptor case. 6345THREADED_TEST(InterceptorLoadIC) { 6346 CheckInterceptorLoadIC(InterceptorLoadICGetter, 6347 "var result = 0;" 6348 "for (var i = 0; i < 1000; i++) {" 6349 " result = o.x;" 6350 "}", 6351 42); 6352} 6353 6354 6355// Below go several tests which verify that JITing for various 6356// configurations of interceptor and explicit fields works fine 6357// (those cases are special cased to get better performance). 6358 6359static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name, 6360 const AccessorInfo& info) { 6361 ApiTestFuzzer::Fuzz(); 6362 return v8_str("x")->Equals(name) 6363 ? v8::Integer::New(42) : v8::Handle<v8::Value>(); 6364} 6365 6366 6367THREADED_TEST(InterceptorLoadICWithFieldOnHolder) { 6368 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6369 "var result = 0;" 6370 "o.y = 239;" 6371 "for (var i = 0; i < 1000; i++) {" 6372 " result = o.y;" 6373 "}", 6374 239); 6375} 6376 6377 6378THREADED_TEST(InterceptorLoadICWithSubstitutedProto) { 6379 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6380 "var result = 0;" 6381 "o.__proto__ = { 'y': 239 };" 6382 "for (var i = 0; i < 1000; i++) {" 6383 " result = o.y + o.x;" 6384 "}", 6385 239 + 42); 6386} 6387 6388 6389THREADED_TEST(InterceptorLoadICWithPropertyOnProto) { 6390 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6391 "var result = 0;" 6392 "o.__proto__.y = 239;" 6393 "for (var i = 0; i < 1000; i++) {" 6394 " result = o.y + o.x;" 6395 "}", 6396 239 + 42); 6397} 6398 6399 6400THREADED_TEST(InterceptorLoadICUndefined) { 6401 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6402 "var result = 0;" 6403 "for (var i = 0; i < 1000; i++) {" 6404 " result = (o.y == undefined) ? 239 : 42;" 6405 "}", 6406 239); 6407} 6408 6409 6410THREADED_TEST(InterceptorLoadICWithOverride) { 6411 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6412 "fst = new Object(); fst.__proto__ = o;" 6413 "snd = new Object(); snd.__proto__ = fst;" 6414 "var result1 = 0;" 6415 "for (var i = 0; i < 1000; i++) {" 6416 " result1 = snd.x;" 6417 "}" 6418 "fst.x = 239;" 6419 "var result = 0;" 6420 "for (var i = 0; i < 1000; i++) {" 6421 " result = snd.x;" 6422 "}" 6423 "result + result1", 6424 239 + 42); 6425} 6426 6427 6428// Test the case when we stored field into 6429// a stub, but interceptor produced value on its own. 6430THREADED_TEST(InterceptorLoadICFieldNotNeeded) { 6431 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6432 "proto = new Object();" 6433 "o.__proto__ = proto;" 6434 "proto.x = 239;" 6435 "for (var i = 0; i < 1000; i++) {" 6436 " o.x;" 6437 // Now it should be ICed and keep a reference to x defined on proto 6438 "}" 6439 "var result = 0;" 6440 "for (var i = 0; i < 1000; i++) {" 6441 " result += o.x;" 6442 "}" 6443 "result;", 6444 42 * 1000); 6445} 6446 6447 6448// Test the case when we stored field into 6449// a stub, but it got invalidated later on. 6450THREADED_TEST(InterceptorLoadICInvalidatedField) { 6451 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6452 "proto1 = new Object();" 6453 "proto2 = new Object();" 6454 "o.__proto__ = proto1;" 6455 "proto1.__proto__ = proto2;" 6456 "proto2.y = 239;" 6457 "for (var i = 0; i < 1000; i++) {" 6458 " o.y;" 6459 // Now it should be ICed and keep a reference to y defined on proto2 6460 "}" 6461 "proto1.y = 42;" 6462 "var result = 0;" 6463 "for (var i = 0; i < 1000; i++) {" 6464 " result += o.y;" 6465 "}" 6466 "result;", 6467 42 * 1000); 6468} 6469 6470 6471static int interceptor_load_not_handled_calls = 0; 6472static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name, 6473 const AccessorInfo& info) { 6474 ++interceptor_load_not_handled_calls; 6475 return v8::Handle<v8::Value>(); 6476} 6477 6478 6479// Test how post-interceptor lookups are done in the non-cacheable 6480// case: the interceptor should not be invoked during this lookup. 6481THREADED_TEST(InterceptorLoadICPostInterceptor) { 6482 interceptor_load_not_handled_calls = 0; 6483 CheckInterceptorLoadIC(InterceptorLoadNotHandled, 6484 "receiver = new Object();" 6485 "receiver.__proto__ = o;" 6486 "proto = new Object();" 6487 "/* Make proto a slow-case object. */" 6488 "for (var i = 0; i < 1000; i++) {" 6489 " proto[\"xxxxxxxx\" + i] = [];" 6490 "}" 6491 "proto.x = 17;" 6492 "o.__proto__ = proto;" 6493 "var result = 0;" 6494 "for (var i = 0; i < 1000; i++) {" 6495 " result += receiver.x;" 6496 "}" 6497 "result;", 6498 17 * 1000); 6499 CHECK_EQ(1000, interceptor_load_not_handled_calls); 6500} 6501 6502 6503// Test the case when we stored field into 6504// a stub, but it got invalidated later on due to override on 6505// global object which is between interceptor and fields' holders. 6506THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) { 6507 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6508 "o.__proto__ = this;" // set a global to be a proto of o. 6509 "this.__proto__.y = 239;" 6510 "for (var i = 0; i < 10; i++) {" 6511 " if (o.y != 239) throw 'oops: ' + o.y;" 6512 // Now it should be ICed and keep a reference to y defined on field_holder. 6513 "}" 6514 "this.y = 42;" // Assign on a global. 6515 "var result = 0;" 6516 "for (var i = 0; i < 10; i++) {" 6517 " result += o.y;" 6518 "}" 6519 "result;", 6520 42 * 10); 6521} 6522 6523 6524static void SetOnThis(Local<String> name, 6525 Local<Value> value, 6526 const AccessorInfo& info) { 6527 info.This()->ForceSet(name, value); 6528} 6529 6530 6531THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) { 6532 v8::HandleScope scope; 6533 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6534 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6535 templ->SetAccessor(v8_str("y"), Return239); 6536 LocalContext context; 6537 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6538 6539 // Check the case when receiver and interceptor's holder 6540 // are the same objects. 6541 v8::Handle<Value> value = CompileRun( 6542 "var result = 0;" 6543 "for (var i = 0; i < 7; i++) {" 6544 " result = o.y;" 6545 "}"); 6546 CHECK_EQ(239, value->Int32Value()); 6547 6548 // Check the case when interceptor's holder is in proto chain 6549 // of receiver. 6550 value = CompileRun( 6551 "r = { __proto__: o };" 6552 "var result = 0;" 6553 "for (var i = 0; i < 7; i++) {" 6554 " result = r.y;" 6555 "}"); 6556 CHECK_EQ(239, value->Int32Value()); 6557} 6558 6559 6560THREADED_TEST(InterceptorLoadICWithCallbackOnProto) { 6561 v8::HandleScope scope; 6562 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6563 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6564 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6565 templ_p->SetAccessor(v8_str("y"), Return239); 6566 6567 LocalContext context; 6568 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6569 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6570 6571 // Check the case when receiver and interceptor's holder 6572 // are the same objects. 6573 v8::Handle<Value> value = CompileRun( 6574 "o.__proto__ = p;" 6575 "var result = 0;" 6576 "for (var i = 0; i < 7; i++) {" 6577 " result = o.x + o.y;" 6578 "}"); 6579 CHECK_EQ(239 + 42, value->Int32Value()); 6580 6581 // Check the case when interceptor's holder is in proto chain 6582 // of receiver. 6583 value = CompileRun( 6584 "r = { __proto__: o };" 6585 "var result = 0;" 6586 "for (var i = 0; i < 7; i++) {" 6587 " result = r.x + r.y;" 6588 "}"); 6589 CHECK_EQ(239 + 42, value->Int32Value()); 6590} 6591 6592 6593THREADED_TEST(InterceptorLoadICForCallbackWithOverride) { 6594 v8::HandleScope scope; 6595 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6596 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6597 templ->SetAccessor(v8_str("y"), Return239); 6598 6599 LocalContext context; 6600 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6601 6602 v8::Handle<Value> value = CompileRun( 6603 "fst = new Object(); fst.__proto__ = o;" 6604 "snd = new Object(); snd.__proto__ = fst;" 6605 "var result1 = 0;" 6606 "for (var i = 0; i < 7; i++) {" 6607 " result1 = snd.x;" 6608 "}" 6609 "fst.x = 239;" 6610 "var result = 0;" 6611 "for (var i = 0; i < 7; i++) {" 6612 " result = snd.x;" 6613 "}" 6614 "result + result1"); 6615 CHECK_EQ(239 + 42, value->Int32Value()); 6616} 6617 6618 6619// Test the case when we stored callback into 6620// a stub, but interceptor produced value on its own. 6621THREADED_TEST(InterceptorLoadICCallbackNotNeeded) { 6622 v8::HandleScope scope; 6623 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6624 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6625 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6626 templ_p->SetAccessor(v8_str("y"), Return239); 6627 6628 LocalContext context; 6629 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6630 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6631 6632 v8::Handle<Value> value = CompileRun( 6633 "o.__proto__ = p;" 6634 "for (var i = 0; i < 7; i++) {" 6635 " o.x;" 6636 // Now it should be ICed and keep a reference to x defined on p 6637 "}" 6638 "var result = 0;" 6639 "for (var i = 0; i < 7; i++) {" 6640 " result += o.x;" 6641 "}" 6642 "result"); 6643 CHECK_EQ(42 * 7, value->Int32Value()); 6644} 6645 6646 6647// Test the case when we stored callback into 6648// a stub, but it got invalidated later on. 6649THREADED_TEST(InterceptorLoadICInvalidatedCallback) { 6650 v8::HandleScope scope; 6651 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6652 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6653 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6654 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 6655 6656 LocalContext context; 6657 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6658 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6659 6660 v8::Handle<Value> value = CompileRun( 6661 "inbetween = new Object();" 6662 "o.__proto__ = inbetween;" 6663 "inbetween.__proto__ = p;" 6664 "for (var i = 0; i < 10; i++) {" 6665 " o.y;" 6666 // Now it should be ICed and keep a reference to y defined on p 6667 "}" 6668 "inbetween.y = 42;" 6669 "var result = 0;" 6670 "for (var i = 0; i < 10; i++) {" 6671 " result += o.y;" 6672 "}" 6673 "result"); 6674 CHECK_EQ(42 * 10, value->Int32Value()); 6675} 6676 6677 6678// Test the case when we stored callback into 6679// a stub, but it got invalidated later on due to override on 6680// global object which is between interceptor and callbacks' holders. 6681THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) { 6682 v8::HandleScope scope; 6683 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6684 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6685 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6686 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 6687 6688 LocalContext context; 6689 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6690 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6691 6692 v8::Handle<Value> value = CompileRun( 6693 "o.__proto__ = this;" 6694 "this.__proto__ = p;" 6695 "for (var i = 0; i < 10; i++) {" 6696 " if (o.y != 239) throw 'oops: ' + o.y;" 6697 // Now it should be ICed and keep a reference to y defined on p 6698 "}" 6699 "this.y = 42;" 6700 "var result = 0;" 6701 "for (var i = 0; i < 10; i++) {" 6702 " result += o.y;" 6703 "}" 6704 "result"); 6705 CHECK_EQ(42 * 10, value->Int32Value()); 6706} 6707 6708 6709static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name, 6710 const AccessorInfo& info) { 6711 ApiTestFuzzer::Fuzz(); 6712 CHECK(v8_str("x")->Equals(name)); 6713 return v8::Integer::New(0); 6714} 6715 6716 6717THREADED_TEST(InterceptorReturningZero) { 6718 CheckInterceptorLoadIC(InterceptorLoadICGetter0, 6719 "o.x == undefined ? 1 : 0", 6720 0); 6721} 6722 6723 6724static v8::Handle<Value> InterceptorStoreICSetter( 6725 Local<String> key, Local<Value> value, const AccessorInfo&) { 6726 CHECK(v8_str("x")->Equals(key)); 6727 CHECK_EQ(42, value->Int32Value()); 6728 return value; 6729} 6730 6731 6732// This test should hit the store IC for the interceptor case. 6733THREADED_TEST(InterceptorStoreIC) { 6734 v8::HandleScope scope; 6735 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6736 templ->SetNamedPropertyHandler(InterceptorLoadICGetter, 6737 InterceptorStoreICSetter, 6738 0, 0, 0, v8_str("data")); 6739 LocalContext context; 6740 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6741 v8::Handle<Value> value = CompileRun( 6742 "for (var i = 0; i < 1000; i++) {" 6743 " o.x = 42;" 6744 "}"); 6745} 6746 6747 6748THREADED_TEST(InterceptorStoreICWithNoSetter) { 6749 v8::HandleScope scope; 6750 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6751 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6752 LocalContext context; 6753 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6754 v8::Handle<Value> value = CompileRun( 6755 "for (var i = 0; i < 1000; i++) {" 6756 " o.y = 239;" 6757 "}" 6758 "42 + o.y"); 6759 CHECK_EQ(239 + 42, value->Int32Value()); 6760} 6761 6762 6763 6764 6765v8::Handle<Value> call_ic_function; 6766v8::Handle<Value> call_ic_function2; 6767v8::Handle<Value> call_ic_function3; 6768 6769static v8::Handle<Value> InterceptorCallICGetter(Local<String> name, 6770 const AccessorInfo& info) { 6771 ApiTestFuzzer::Fuzz(); 6772 CHECK(v8_str("x")->Equals(name)); 6773 return call_ic_function; 6774} 6775 6776 6777// This test should hit the call IC for the interceptor case. 6778THREADED_TEST(InterceptorCallIC) { 6779 v8::HandleScope scope; 6780 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6781 templ->SetNamedPropertyHandler(InterceptorCallICGetter); 6782 LocalContext context; 6783 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6784 call_ic_function = 6785 v8_compile("function f(x) { return x + 1; }; f")->Run(); 6786 v8::Handle<Value> value = CompileRun( 6787 "var result = 0;" 6788 "for (var i = 0; i < 1000; i++) {" 6789 " result = o.x(41);" 6790 "}"); 6791 CHECK_EQ(42, value->Int32Value()); 6792} 6793 6794 6795// This test checks that if interceptor doesn't provide 6796// a value, we can fetch regular value. 6797THREADED_TEST(InterceptorCallICSeesOthers) { 6798 v8::HandleScope scope; 6799 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6800 templ->SetNamedPropertyHandler(NoBlockGetterX); 6801 LocalContext context; 6802 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6803 v8::Handle<Value> value = CompileRun( 6804 "o.x = function f(x) { return x + 1; };" 6805 "var result = 0;" 6806 "for (var i = 0; i < 7; i++) {" 6807 " result = o.x(41);" 6808 "}"); 6809 CHECK_EQ(42, value->Int32Value()); 6810} 6811 6812 6813static v8::Handle<Value> call_ic_function4; 6814static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name, 6815 const AccessorInfo& info) { 6816 ApiTestFuzzer::Fuzz(); 6817 CHECK(v8_str("x")->Equals(name)); 6818 return call_ic_function4; 6819} 6820 6821 6822// This test checks that if interceptor provides a function, 6823// even if we cached shadowed variant, interceptor's function 6824// is invoked 6825THREADED_TEST(InterceptorCallICCacheableNotNeeded) { 6826 v8::HandleScope scope; 6827 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6828 templ->SetNamedPropertyHandler(InterceptorCallICGetter4); 6829 LocalContext context; 6830 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6831 call_ic_function4 = 6832 v8_compile("function f(x) { return x - 1; }; f")->Run(); 6833 v8::Handle<Value> value = CompileRun( 6834 "o.__proto__.x = function(x) { return x + 1; };" 6835 "var result = 0;" 6836 "for (var i = 0; i < 1000; i++) {" 6837 " result = o.x(42);" 6838 "}"); 6839 CHECK_EQ(41, value->Int32Value()); 6840} 6841 6842 6843// Test the case when we stored cacheable lookup into 6844// a stub, but it got invalidated later on 6845THREADED_TEST(InterceptorCallICInvalidatedCacheable) { 6846 v8::HandleScope scope; 6847 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6848 templ->SetNamedPropertyHandler(NoBlockGetterX); 6849 LocalContext context; 6850 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6851 v8::Handle<Value> value = CompileRun( 6852 "proto1 = new Object();" 6853 "proto2 = new Object();" 6854 "o.__proto__ = proto1;" 6855 "proto1.__proto__ = proto2;" 6856 "proto2.y = function(x) { return x + 1; };" 6857 // Invoke it many times to compile a stub 6858 "for (var i = 0; i < 7; i++) {" 6859 " o.y(42);" 6860 "}" 6861 "proto1.y = function(x) { return x - 1; };" 6862 "var result = 0;" 6863 "for (var i = 0; i < 7; i++) {" 6864 " result += o.y(42);" 6865 "}"); 6866 CHECK_EQ(41 * 7, value->Int32Value()); 6867} 6868 6869 6870static v8::Handle<Value> call_ic_function5; 6871static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name, 6872 const AccessorInfo& info) { 6873 ApiTestFuzzer::Fuzz(); 6874 if (v8_str("x")->Equals(name)) 6875 return call_ic_function5; 6876 else 6877 return Local<Value>(); 6878} 6879 6880 6881// This test checks that if interceptor doesn't provide a function, 6882// cached constant function is used 6883THREADED_TEST(InterceptorCallICConstantFunctionUsed) { 6884 v8::HandleScope scope; 6885 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6886 templ->SetNamedPropertyHandler(NoBlockGetterX); 6887 LocalContext context; 6888 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6889 v8::Handle<Value> value = CompileRun( 6890 "function inc(x) { return x + 1; };" 6891 "inc(1);" 6892 "o.x = inc;" 6893 "var result = 0;" 6894 "for (var i = 0; i < 1000; i++) {" 6895 " result = o.x(42);" 6896 "}"); 6897 CHECK_EQ(43, value->Int32Value()); 6898} 6899 6900 6901// This test checks that if interceptor provides a function, 6902// even if we cached constant function, interceptor's function 6903// is invoked 6904THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) { 6905 v8::HandleScope scope; 6906 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6907 templ->SetNamedPropertyHandler(InterceptorCallICGetter5); 6908 LocalContext context; 6909 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6910 call_ic_function5 = 6911 v8_compile("function f(x) { return x - 1; }; f")->Run(); 6912 v8::Handle<Value> value = CompileRun( 6913 "function inc(x) { return x + 1; };" 6914 "inc(1);" 6915 "o.x = inc;" 6916 "var result = 0;" 6917 "for (var i = 0; i < 1000; i++) {" 6918 " result = o.x(42);" 6919 "}"); 6920 CHECK_EQ(41, value->Int32Value()); 6921} 6922 6923 6924// Test the case when we stored constant function into 6925// a stub, but it got invalidated later on 6926THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) { 6927 v8::HandleScope scope; 6928 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6929 templ->SetNamedPropertyHandler(NoBlockGetterX); 6930 LocalContext context; 6931 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6932 v8::Handle<Value> value = CompileRun( 6933 "function inc(x) { return x + 1; };" 6934 "inc(1);" 6935 "proto1 = new Object();" 6936 "proto2 = new Object();" 6937 "o.__proto__ = proto1;" 6938 "proto1.__proto__ = proto2;" 6939 "proto2.y = inc;" 6940 // Invoke it many times to compile a stub 6941 "for (var i = 0; i < 7; i++) {" 6942 " o.y(42);" 6943 "}" 6944 "proto1.y = function(x) { return x - 1; };" 6945 "var result = 0;" 6946 "for (var i = 0; i < 7; i++) {" 6947 " result += o.y(42);" 6948 "}"); 6949 CHECK_EQ(41 * 7, value->Int32Value()); 6950} 6951 6952 6953// Test the case when we stored constant function into 6954// a stub, but it got invalidated later on due to override on 6955// global object which is between interceptor and constant function' holders. 6956THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) { 6957 v8::HandleScope scope; 6958 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6959 templ->SetNamedPropertyHandler(NoBlockGetterX); 6960 LocalContext context; 6961 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6962 v8::Handle<Value> value = CompileRun( 6963 "function inc(x) { return x + 1; };" 6964 "inc(1);" 6965 "o.__proto__ = this;" 6966 "this.__proto__.y = inc;" 6967 // Invoke it many times to compile a stub 6968 "for (var i = 0; i < 7; i++) {" 6969 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);" 6970 "}" 6971 "this.y = function(x) { return x - 1; };" 6972 "var result = 0;" 6973 "for (var i = 0; i < 7; i++) {" 6974 " result += o.y(42);" 6975 "}"); 6976 CHECK_EQ(41 * 7, value->Int32Value()); 6977} 6978 6979 6980// Test the case when actual function to call sits on global object. 6981THREADED_TEST(InterceptorCallICCachedFromGlobal) { 6982 v8::HandleScope scope; 6983 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6984 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 6985 6986 LocalContext context; 6987 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6988 6989 v8::Handle<Value> value = CompileRun( 6990 "try {" 6991 " o.__proto__ = this;" 6992 " for (var i = 0; i < 10; i++) {" 6993 " var v = o.parseFloat('239');" 6994 " if (v != 239) throw v;" 6995 // Now it should be ICed and keep a reference to parseFloat. 6996 " }" 6997 " var result = 0;" 6998 " for (var i = 0; i < 10; i++) {" 6999 " result += o.parseFloat('239');" 7000 " }" 7001 " result" 7002 "} catch(e) {" 7003 " e" 7004 "};"); 7005 CHECK_EQ(239 * 10, value->Int32Value()); 7006} 7007 7008static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name, 7009 const AccessorInfo& info) { 7010 ApiTestFuzzer::Fuzz(); 7011 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data())); 7012 ++(*call_count); 7013 if ((*call_count) % 20 == 0) { 7014 i::Heap::CollectAllGarbage(true); 7015 } 7016 return v8::Handle<Value>(); 7017} 7018 7019static v8::Handle<Value> FastApiCallback_TrivialSignature( 7020 const v8::Arguments& args) { 7021 ApiTestFuzzer::Fuzz(); 7022 CHECK_EQ(args.This(), args.Holder()); 7023 CHECK(args.Data()->Equals(v8_str("method_data"))); 7024 return v8::Integer::New(args[0]->Int32Value() + 1); 7025} 7026 7027static v8::Handle<Value> FastApiCallback_SimpleSignature( 7028 const v8::Arguments& args) { 7029 ApiTestFuzzer::Fuzz(); 7030 CHECK_EQ(args.This()->GetPrototype(), args.Holder()); 7031 CHECK(args.Data()->Equals(v8_str("method_data"))); 7032 // Note, we're using HasRealNamedProperty instead of Has to avoid 7033 // invoking the interceptor again. 7034 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo"))); 7035 return v8::Integer::New(args[0]->Int32Value() + 1); 7036} 7037 7038// Helper to maximize the odds of object moving. 7039static void GenerateSomeGarbage() { 7040 CompileRun( 7041 "var garbage;" 7042 "for (var i = 0; i < 1000; i++) {" 7043 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];" 7044 "}" 7045 "garbage = undefined;"); 7046} 7047 7048THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) { 7049 int interceptor_call_count = 0; 7050 v8::HandleScope scope; 7051 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7052 v8::Handle<v8::FunctionTemplate> method_templ = 7053 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 7054 v8_str("method_data"), 7055 v8::Handle<v8::Signature>()); 7056 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7057 proto_templ->Set(v8_str("method"), method_templ); 7058 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7059 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 7060 NULL, NULL, NULL, NULL, 7061 v8::External::Wrap(&interceptor_call_count)); 7062 LocalContext context; 7063 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7064 GenerateSomeGarbage(); 7065 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7066 v8::Handle<Value> value = CompileRun( 7067 "var result = 0;" 7068 "for (var i = 0; i < 100; i++) {" 7069 " result = o.method(41);" 7070 "}"); 7071 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 7072 CHECK_EQ(100, interceptor_call_count); 7073} 7074 7075THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) { 7076 int interceptor_call_count = 0; 7077 v8::HandleScope scope; 7078 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7079 v8::Handle<v8::FunctionTemplate> method_templ = 7080 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7081 v8_str("method_data"), 7082 v8::Signature::New(fun_templ)); 7083 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7084 proto_templ->Set(v8_str("method"), method_templ); 7085 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7086 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 7087 NULL, NULL, NULL, NULL, 7088 v8::External::Wrap(&interceptor_call_count)); 7089 LocalContext context; 7090 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7091 GenerateSomeGarbage(); 7092 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7093 v8::Handle<Value> value = CompileRun( 7094 "o.foo = 17;" 7095 "var receiver = {};" 7096 "receiver.__proto__ = o;" 7097 "var result = 0;" 7098 "for (var i = 0; i < 100; i++) {" 7099 " result = receiver.method(41);" 7100 "}"); 7101 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 7102 CHECK_EQ(100, interceptor_call_count); 7103} 7104 7105THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) { 7106 int interceptor_call_count = 0; 7107 v8::HandleScope scope; 7108 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7109 v8::Handle<v8::FunctionTemplate> method_templ = 7110 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7111 v8_str("method_data"), 7112 v8::Signature::New(fun_templ)); 7113 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7114 proto_templ->Set(v8_str("method"), method_templ); 7115 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7116 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 7117 NULL, NULL, NULL, NULL, 7118 v8::External::Wrap(&interceptor_call_count)); 7119 LocalContext context; 7120 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7121 GenerateSomeGarbage(); 7122 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7123 v8::Handle<Value> value = CompileRun( 7124 "o.foo = 17;" 7125 "var receiver = {};" 7126 "receiver.__proto__ = o;" 7127 "var result = 0;" 7128 "var saved_result = 0;" 7129 "for (var i = 0; i < 100; i++) {" 7130 " result = receiver.method(41);" 7131 " if (i == 50) {" 7132 " saved_result = result;" 7133 " receiver = {method: function(x) { return x - 1 }};" 7134 " }" 7135 "}"); 7136 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 7137 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7138 CHECK_GE(interceptor_call_count, 50); 7139} 7140 7141THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) { 7142 int interceptor_call_count = 0; 7143 v8::HandleScope scope; 7144 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7145 v8::Handle<v8::FunctionTemplate> method_templ = 7146 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7147 v8_str("method_data"), 7148 v8::Signature::New(fun_templ)); 7149 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7150 proto_templ->Set(v8_str("method"), method_templ); 7151 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7152 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 7153 NULL, NULL, NULL, NULL, 7154 v8::External::Wrap(&interceptor_call_count)); 7155 LocalContext context; 7156 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7157 GenerateSomeGarbage(); 7158 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7159 v8::Handle<Value> value = CompileRun( 7160 "o.foo = 17;" 7161 "var receiver = {};" 7162 "receiver.__proto__ = o;" 7163 "var result = 0;" 7164 "var saved_result = 0;" 7165 "for (var i = 0; i < 100; i++) {" 7166 " result = receiver.method(41);" 7167 " if (i == 50) {" 7168 " saved_result = result;" 7169 " o.method = function(x) { return x - 1 };" 7170 " }" 7171 "}"); 7172 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 7173 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7174 CHECK_GE(interceptor_call_count, 50); 7175} 7176 7177THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) { 7178 int interceptor_call_count = 0; 7179 v8::HandleScope scope; 7180 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7181 v8::Handle<v8::FunctionTemplate> method_templ = 7182 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7183 v8_str("method_data"), 7184 v8::Signature::New(fun_templ)); 7185 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7186 proto_templ->Set(v8_str("method"), method_templ); 7187 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7188 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 7189 NULL, NULL, NULL, NULL, 7190 v8::External::Wrap(&interceptor_call_count)); 7191 LocalContext context; 7192 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7193 GenerateSomeGarbage(); 7194 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7195 v8::TryCatch try_catch; 7196 v8::Handle<Value> value = CompileRun( 7197 "o.foo = 17;" 7198 "var receiver = {};" 7199 "receiver.__proto__ = o;" 7200 "var result = 0;" 7201 "var saved_result = 0;" 7202 "for (var i = 0; i < 100; i++) {" 7203 " result = receiver.method(41);" 7204 " if (i == 50) {" 7205 " saved_result = result;" 7206 " receiver = 333;" 7207 " }" 7208 "}"); 7209 CHECK(try_catch.HasCaught()); 7210 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), 7211 try_catch.Exception()->ToString()); 7212 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7213 CHECK_GE(interceptor_call_count, 50); 7214} 7215 7216THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) { 7217 int interceptor_call_count = 0; 7218 v8::HandleScope scope; 7219 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7220 v8::Handle<v8::FunctionTemplate> method_templ = 7221 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7222 v8_str("method_data"), 7223 v8::Signature::New(fun_templ)); 7224 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7225 proto_templ->Set(v8_str("method"), method_templ); 7226 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7227 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 7228 NULL, NULL, NULL, NULL, 7229 v8::External::Wrap(&interceptor_call_count)); 7230 LocalContext context; 7231 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7232 GenerateSomeGarbage(); 7233 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7234 v8::TryCatch try_catch; 7235 v8::Handle<Value> value = CompileRun( 7236 "o.foo = 17;" 7237 "var receiver = {};" 7238 "receiver.__proto__ = o;" 7239 "var result = 0;" 7240 "var saved_result = 0;" 7241 "for (var i = 0; i < 100; i++) {" 7242 " result = receiver.method(41);" 7243 " if (i == 50) {" 7244 " saved_result = result;" 7245 " receiver = {method: receiver.method};" 7246 " }" 7247 "}"); 7248 CHECK(try_catch.HasCaught()); 7249 CHECK_EQ(v8_str("TypeError: Illegal invocation"), 7250 try_catch.Exception()->ToString()); 7251 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7252 CHECK_GE(interceptor_call_count, 50); 7253} 7254 7255THREADED_TEST(CallICFastApi_TrivialSignature) { 7256 v8::HandleScope scope; 7257 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7258 v8::Handle<v8::FunctionTemplate> method_templ = 7259 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 7260 v8_str("method_data"), 7261 v8::Handle<v8::Signature>()); 7262 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7263 proto_templ->Set(v8_str("method"), method_templ); 7264 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7265 LocalContext context; 7266 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7267 GenerateSomeGarbage(); 7268 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7269 v8::Handle<Value> value = CompileRun( 7270 "var result = 0;" 7271 "for (var i = 0; i < 100; i++) {" 7272 " result = o.method(41);" 7273 "}"); 7274 7275 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 7276} 7277 7278THREADED_TEST(CallICFastApi_SimpleSignature) { 7279 v8::HandleScope scope; 7280 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7281 v8::Handle<v8::FunctionTemplate> method_templ = 7282 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7283 v8_str("method_data"), 7284 v8::Signature::New(fun_templ)); 7285 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7286 proto_templ->Set(v8_str("method"), method_templ); 7287 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7288 LocalContext context; 7289 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7290 GenerateSomeGarbage(); 7291 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7292 v8::Handle<Value> value = CompileRun( 7293 "o.foo = 17;" 7294 "var receiver = {};" 7295 "receiver.__proto__ = o;" 7296 "var result = 0;" 7297 "for (var i = 0; i < 100; i++) {" 7298 " result = receiver.method(41);" 7299 "}"); 7300 7301 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 7302} 7303 7304THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) { 7305 v8::HandleScope scope; 7306 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7307 v8::Handle<v8::FunctionTemplate> method_templ = 7308 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7309 v8_str("method_data"), 7310 v8::Signature::New(fun_templ)); 7311 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7312 proto_templ->Set(v8_str("method"), method_templ); 7313 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7314 LocalContext context; 7315 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7316 GenerateSomeGarbage(); 7317 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7318 v8::Handle<Value> value = CompileRun( 7319 "o.foo = 17;" 7320 "var receiver = {};" 7321 "receiver.__proto__ = o;" 7322 "var result = 0;" 7323 "var saved_result = 0;" 7324 "for (var i = 0; i < 100; i++) {" 7325 " result = receiver.method(41);" 7326 " if (i == 50) {" 7327 " saved_result = result;" 7328 " receiver = {method: function(x) { return x - 1 }};" 7329 " }" 7330 "}"); 7331 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 7332 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7333} 7334 7335THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) { 7336 v8::HandleScope scope; 7337 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7338 v8::Handle<v8::FunctionTemplate> method_templ = 7339 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7340 v8_str("method_data"), 7341 v8::Signature::New(fun_templ)); 7342 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7343 proto_templ->Set(v8_str("method"), method_templ); 7344 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7345 LocalContext context; 7346 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7347 GenerateSomeGarbage(); 7348 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7349 v8::TryCatch try_catch; 7350 v8::Handle<Value> value = CompileRun( 7351 "o.foo = 17;" 7352 "var receiver = {};" 7353 "receiver.__proto__ = o;" 7354 "var result = 0;" 7355 "var saved_result = 0;" 7356 "for (var i = 0; i < 100; i++) {" 7357 " result = receiver.method(41);" 7358 " if (i == 50) {" 7359 " saved_result = result;" 7360 " receiver = 333;" 7361 " }" 7362 "}"); 7363 CHECK(try_catch.HasCaught()); 7364 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), 7365 try_catch.Exception()->ToString()); 7366 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7367} 7368 7369 7370v8::Handle<Value> keyed_call_ic_function; 7371 7372static v8::Handle<Value> InterceptorKeyedCallICGetter( 7373 Local<String> name, const AccessorInfo& info) { 7374 ApiTestFuzzer::Fuzz(); 7375 if (v8_str("x")->Equals(name)) { 7376 return keyed_call_ic_function; 7377 } 7378 return v8::Handle<Value>(); 7379} 7380 7381 7382// Test the case when we stored cacheable lookup into 7383// a stub, but the function name changed (to another cacheable function). 7384THREADED_TEST(InterceptorKeyedCallICKeyChange1) { 7385 v8::HandleScope scope; 7386 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7387 templ->SetNamedPropertyHandler(NoBlockGetterX); 7388 LocalContext context; 7389 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7390 v8::Handle<Value> value = CompileRun( 7391 "proto = new Object();" 7392 "proto.y = function(x) { return x + 1; };" 7393 "proto.z = function(x) { return x - 1; };" 7394 "o.__proto__ = proto;" 7395 "var result = 0;" 7396 "var method = 'y';" 7397 "for (var i = 0; i < 10; i++) {" 7398 " if (i == 5) { method = 'z'; };" 7399 " result += o[method](41);" 7400 "}"); 7401 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7402} 7403 7404 7405// Test the case when we stored cacheable lookup into 7406// a stub, but the function name changed (and the new function is present 7407// both before and after the interceptor in the prototype chain). 7408THREADED_TEST(InterceptorKeyedCallICKeyChange2) { 7409 v8::HandleScope scope; 7410 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7411 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter); 7412 LocalContext context; 7413 context->Global()->Set(v8_str("proto1"), templ->NewInstance()); 7414 keyed_call_ic_function = 7415 v8_compile("function f(x) { return x - 1; }; f")->Run(); 7416 v8::Handle<Value> value = CompileRun( 7417 "o = new Object();" 7418 "proto2 = new Object();" 7419 "o.y = function(x) { return x + 1; };" 7420 "proto2.y = function(x) { return x + 2; };" 7421 "o.__proto__ = proto1;" 7422 "proto1.__proto__ = proto2;" 7423 "var result = 0;" 7424 "var method = 'x';" 7425 "for (var i = 0; i < 10; i++) {" 7426 " if (i == 5) { method = 'y'; };" 7427 " result += o[method](41);" 7428 "}"); 7429 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7430} 7431 7432 7433// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit 7434// on the global object. 7435THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) { 7436 v8::HandleScope scope; 7437 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7438 templ->SetNamedPropertyHandler(NoBlockGetterX); 7439 LocalContext context; 7440 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7441 v8::Handle<Value> value = CompileRun( 7442 "function inc(x) { return x + 1; };" 7443 "inc(1);" 7444 "function dec(x) { return x - 1; };" 7445 "dec(1);" 7446 "o.__proto__ = this;" 7447 "this.__proto__.x = inc;" 7448 "this.__proto__.y = dec;" 7449 "var result = 0;" 7450 "var method = 'x';" 7451 "for (var i = 0; i < 10; i++) {" 7452 " if (i == 5) { method = 'y'; };" 7453 " result += o[method](41);" 7454 "}"); 7455 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7456} 7457 7458 7459// Test the case when actual function to call sits on global object. 7460THREADED_TEST(InterceptorKeyedCallICFromGlobal) { 7461 v8::HandleScope scope; 7462 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7463 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 7464 LocalContext context; 7465 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 7466 7467 v8::Handle<Value> value = CompileRun( 7468 "function len(x) { return x.length; };" 7469 "o.__proto__ = this;" 7470 "var m = 'parseFloat';" 7471 "var result = 0;" 7472 "for (var i = 0; i < 10; i++) {" 7473 " if (i == 5) {" 7474 " m = 'len';" 7475 " saved_result = result;" 7476 " };" 7477 " result = o[m]('239');" 7478 "}"); 7479 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value()); 7480 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7481} 7482 7483// Test the map transition before the interceptor. 7484THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) { 7485 v8::HandleScope scope; 7486 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7487 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 7488 LocalContext context; 7489 context->Global()->Set(v8_str("proto"), templ_o->NewInstance()); 7490 7491 v8::Handle<Value> value = CompileRun( 7492 "var o = new Object();" 7493 "o.__proto__ = proto;" 7494 "o.method = function(x) { return x + 1; };" 7495 "var m = 'method';" 7496 "var result = 0;" 7497 "for (var i = 0; i < 10; i++) {" 7498 " if (i == 5) { o.method = function(x) { return x - 1; }; };" 7499 " result += o[m](41);" 7500 "}"); 7501 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7502} 7503 7504 7505// Test the map transition after the interceptor. 7506THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) { 7507 v8::HandleScope scope; 7508 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7509 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 7510 LocalContext context; 7511 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 7512 7513 v8::Handle<Value> value = CompileRun( 7514 "var proto = new Object();" 7515 "o.__proto__ = proto;" 7516 "proto.method = function(x) { return x + 1; };" 7517 "var m = 'method';" 7518 "var result = 0;" 7519 "for (var i = 0; i < 10; i++) {" 7520 " if (i == 5) { proto.method = function(x) { return x - 1; }; };" 7521 " result += o[m](41);" 7522 "}"); 7523 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7524} 7525 7526 7527static int interceptor_call_count = 0; 7528 7529static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name, 7530 const AccessorInfo& info) { 7531 ApiTestFuzzer::Fuzz(); 7532 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) { 7533 return call_ic_function2; 7534 } 7535 return v8::Handle<Value>(); 7536} 7537 7538 7539// This test should hit load and call ICs for the interceptor case. 7540// Once in a while, the interceptor will reply that a property was not 7541// found in which case we should get a reference error. 7542THREADED_TEST(InterceptorICReferenceErrors) { 7543 v8::HandleScope scope; 7544 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7545 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter); 7546 LocalContext context(0, templ, v8::Handle<Value>()); 7547 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run(); 7548 v8::Handle<Value> value = CompileRun( 7549 "function f() {" 7550 " for (var i = 0; i < 1000; i++) {" 7551 " try { x; } catch(e) { return true; }" 7552 " }" 7553 " return false;" 7554 "};" 7555 "f();"); 7556 CHECK_EQ(true, value->BooleanValue()); 7557 interceptor_call_count = 0; 7558 value = CompileRun( 7559 "function g() {" 7560 " for (var i = 0; i < 1000; i++) {" 7561 " try { x(42); } catch(e) { return true; }" 7562 " }" 7563 " return false;" 7564 "};" 7565 "g();"); 7566 CHECK_EQ(true, value->BooleanValue()); 7567} 7568 7569 7570static int interceptor_ic_exception_get_count = 0; 7571 7572static v8::Handle<Value> InterceptorICExceptionGetter( 7573 Local<String> name, 7574 const AccessorInfo& info) { 7575 ApiTestFuzzer::Fuzz(); 7576 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) { 7577 return call_ic_function3; 7578 } 7579 if (interceptor_ic_exception_get_count == 20) { 7580 return v8::ThrowException(v8_num(42)); 7581 } 7582 // Do not handle get for properties other than x. 7583 return v8::Handle<Value>(); 7584} 7585 7586// Test interceptor load/call IC where the interceptor throws an 7587// exception once in a while. 7588THREADED_TEST(InterceptorICGetterExceptions) { 7589 interceptor_ic_exception_get_count = 0; 7590 v8::HandleScope scope; 7591 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7592 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter); 7593 LocalContext context(0, templ, v8::Handle<Value>()); 7594 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run(); 7595 v8::Handle<Value> value = CompileRun( 7596 "function f() {" 7597 " for (var i = 0; i < 100; i++) {" 7598 " try { x; } catch(e) { return true; }" 7599 " }" 7600 " return false;" 7601 "};" 7602 "f();"); 7603 CHECK_EQ(true, value->BooleanValue()); 7604 interceptor_ic_exception_get_count = 0; 7605 value = CompileRun( 7606 "function f() {" 7607 " for (var i = 0; i < 100; i++) {" 7608 " try { x(42); } catch(e) { return true; }" 7609 " }" 7610 " return false;" 7611 "};" 7612 "f();"); 7613 CHECK_EQ(true, value->BooleanValue()); 7614} 7615 7616 7617static int interceptor_ic_exception_set_count = 0; 7618 7619static v8::Handle<Value> InterceptorICExceptionSetter( 7620 Local<String> key, Local<Value> value, const AccessorInfo&) { 7621 ApiTestFuzzer::Fuzz(); 7622 if (++interceptor_ic_exception_set_count > 20) { 7623 return v8::ThrowException(v8_num(42)); 7624 } 7625 // Do not actually handle setting. 7626 return v8::Handle<Value>(); 7627} 7628 7629// Test interceptor store IC where the interceptor throws an exception 7630// once in a while. 7631THREADED_TEST(InterceptorICSetterExceptions) { 7632 interceptor_ic_exception_set_count = 0; 7633 v8::HandleScope scope; 7634 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7635 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter); 7636 LocalContext context(0, templ, v8::Handle<Value>()); 7637 v8::Handle<Value> value = CompileRun( 7638 "function f() {" 7639 " for (var i = 0; i < 100; i++) {" 7640 " try { x = 42; } catch(e) { return true; }" 7641 " }" 7642 " return false;" 7643 "};" 7644 "f();"); 7645 CHECK_EQ(true, value->BooleanValue()); 7646} 7647 7648 7649// Test that we ignore null interceptors. 7650THREADED_TEST(NullNamedInterceptor) { 7651 v8::HandleScope scope; 7652 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7653 templ->SetNamedPropertyHandler(0); 7654 LocalContext context; 7655 templ->Set("x", v8_num(42)); 7656 v8::Handle<v8::Object> obj = templ->NewInstance(); 7657 context->Global()->Set(v8_str("obj"), obj); 7658 v8::Handle<Value> value = CompileRun("obj.x"); 7659 CHECK(value->IsInt32()); 7660 CHECK_EQ(42, value->Int32Value()); 7661} 7662 7663 7664// Test that we ignore null interceptors. 7665THREADED_TEST(NullIndexedInterceptor) { 7666 v8::HandleScope scope; 7667 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7668 templ->SetIndexedPropertyHandler(0); 7669 LocalContext context; 7670 templ->Set("42", v8_num(42)); 7671 v8::Handle<v8::Object> obj = templ->NewInstance(); 7672 context->Global()->Set(v8_str("obj"), obj); 7673 v8::Handle<Value> value = CompileRun("obj[42]"); 7674 CHECK(value->IsInt32()); 7675 CHECK_EQ(42, value->Int32Value()); 7676} 7677 7678 7679THREADED_TEST(NamedPropertyHandlerGetterAttributes) { 7680 v8::HandleScope scope; 7681 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 7682 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter); 7683 LocalContext env; 7684 env->Global()->Set(v8_str("obj"), 7685 templ->GetFunction()->NewInstance()); 7686 ExpectTrue("obj.x === 42"); 7687 ExpectTrue("!obj.propertyIsEnumerable('x')"); 7688} 7689 7690 7691static v8::Handle<Value> ParentGetter(Local<String> name, 7692 const AccessorInfo& info) { 7693 ApiTestFuzzer::Fuzz(); 7694 return v8_num(1); 7695} 7696 7697 7698static v8::Handle<Value> ChildGetter(Local<String> name, 7699 const AccessorInfo& info) { 7700 ApiTestFuzzer::Fuzz(); 7701 return v8_num(42); 7702} 7703 7704 7705THREADED_TEST(Overriding) { 7706 v8::HandleScope scope; 7707 LocalContext context; 7708 7709 // Parent template. 7710 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New(); 7711 Local<ObjectTemplate> parent_instance_templ = 7712 parent_templ->InstanceTemplate(); 7713 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter); 7714 7715 // Template that inherits from the parent template. 7716 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New(); 7717 Local<ObjectTemplate> child_instance_templ = 7718 child_templ->InstanceTemplate(); 7719 child_templ->Inherit(parent_templ); 7720 // Override 'f'. The child version of 'f' should get called for child 7721 // instances. 7722 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter); 7723 // Add 'g' twice. The 'g' added last should get called for instances. 7724 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter); 7725 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter); 7726 7727 // Add 'h' as an accessor to the proto template with ReadOnly attributes 7728 // so 'h' can be shadowed on the instance object. 7729 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate(); 7730 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0, 7731 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 7732 7733 // Add 'i' as an accessor to the instance template with ReadOnly attributes 7734 // but the attribute does not have effect because it is duplicated with 7735 // NULL setter. 7736 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0, 7737 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 7738 7739 7740 7741 // Instantiate the child template. 7742 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance(); 7743 7744 // Check that the child function overrides the parent one. 7745 context->Global()->Set(v8_str("o"), instance); 7746 Local<Value> value = v8_compile("o.f")->Run(); 7747 // Check that the 'g' that was added last is hit. 7748 CHECK_EQ(42, value->Int32Value()); 7749 value = v8_compile("o.g")->Run(); 7750 CHECK_EQ(42, value->Int32Value()); 7751 7752 // Check 'h' can be shadowed. 7753 value = v8_compile("o.h = 3; o.h")->Run(); 7754 CHECK_EQ(3, value->Int32Value()); 7755 7756 // Check 'i' is cannot be shadowed or changed. 7757 value = v8_compile("o.i = 3; o.i")->Run(); 7758 CHECK_EQ(42, value->Int32Value()); 7759} 7760 7761 7762static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) { 7763 ApiTestFuzzer::Fuzz(); 7764 if (args.IsConstructCall()) { 7765 return v8::Boolean::New(true); 7766 } 7767 return v8::Boolean::New(false); 7768} 7769 7770 7771THREADED_TEST(IsConstructCall) { 7772 v8::HandleScope scope; 7773 7774 // Function template with call handler. 7775 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 7776 templ->SetCallHandler(IsConstructHandler); 7777 7778 LocalContext context; 7779 7780 context->Global()->Set(v8_str("f"), templ->GetFunction()); 7781 Local<Value> value = v8_compile("f()")->Run(); 7782 CHECK(!value->BooleanValue()); 7783 value = v8_compile("new f()")->Run(); 7784 CHECK(value->BooleanValue()); 7785} 7786 7787 7788THREADED_TEST(ObjectProtoToString) { 7789 v8::HandleScope scope; 7790 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 7791 templ->SetClassName(v8_str("MyClass")); 7792 7793 LocalContext context; 7794 7795 Local<String> customized_tostring = v8_str("customized toString"); 7796 7797 // Replace Object.prototype.toString 7798 v8_compile("Object.prototype.toString = function() {" 7799 " return 'customized toString';" 7800 "}")->Run(); 7801 7802 // Normal ToString call should call replaced Object.prototype.toString 7803 Local<v8::Object> instance = templ->GetFunction()->NewInstance(); 7804 Local<String> value = instance->ToString(); 7805 CHECK(value->IsString() && value->Equals(customized_tostring)); 7806 7807 // ObjectProtoToString should not call replace toString function. 7808 value = instance->ObjectProtoToString(); 7809 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]"))); 7810 7811 // Check global 7812 value = context->Global()->ObjectProtoToString(); 7813 CHECK(value->IsString() && value->Equals(v8_str("[object global]"))); 7814 7815 // Check ordinary object 7816 Local<Value> object = v8_compile("new Object()")->Run(); 7817 value = object.As<v8::Object>()->ObjectProtoToString(); 7818 CHECK(value->IsString() && value->Equals(v8_str("[object Object]"))); 7819} 7820 7821 7822THREADED_TEST(ObjectGetConstructorName) { 7823 v8::HandleScope scope; 7824 LocalContext context; 7825 v8_compile("function Parent() {};" 7826 "function Child() {};" 7827 "Child.prototype = new Parent();" 7828 "var outer = { inner: function() { } };" 7829 "var p = new Parent();" 7830 "var c = new Child();" 7831 "var x = new outer.inner();")->Run(); 7832 7833 Local<v8::Value> p = context->Global()->Get(v8_str("p")); 7834 CHECK(p->IsObject() && p->ToObject()->GetConstructorName()->Equals( 7835 v8_str("Parent"))); 7836 7837 Local<v8::Value> c = context->Global()->Get(v8_str("c")); 7838 CHECK(c->IsObject() && c->ToObject()->GetConstructorName()->Equals( 7839 v8_str("Child"))); 7840 7841 Local<v8::Value> x = context->Global()->Get(v8_str("x")); 7842 CHECK(x->IsObject() && x->ToObject()->GetConstructorName()->Equals( 7843 v8_str("outer.inner"))); 7844} 7845 7846 7847bool ApiTestFuzzer::fuzzing_ = false; 7848i::Semaphore* ApiTestFuzzer::all_tests_done_= 7849 i::OS::CreateSemaphore(0); 7850int ApiTestFuzzer::active_tests_; 7851int ApiTestFuzzer::tests_being_run_; 7852int ApiTestFuzzer::current_; 7853 7854 7855// We are in a callback and want to switch to another thread (if we 7856// are currently running the thread fuzzing test). 7857void ApiTestFuzzer::Fuzz() { 7858 if (!fuzzing_) return; 7859 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_; 7860 test->ContextSwitch(); 7861} 7862 7863 7864// Let the next thread go. Since it is also waiting on the V8 lock it may 7865// not start immediately. 7866bool ApiTestFuzzer::NextThread() { 7867 int test_position = GetNextTestNumber(); 7868 const char* test_name = RegisterThreadedTest::nth(current_)->name(); 7869 if (test_position == current_) { 7870 if (kLogThreading) 7871 printf("Stay with %s\n", test_name); 7872 return false; 7873 } 7874 if (kLogThreading) { 7875 printf("Switch from %s to %s\n", 7876 test_name, 7877 RegisterThreadedTest::nth(test_position)->name()); 7878 } 7879 current_ = test_position; 7880 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal(); 7881 return true; 7882} 7883 7884 7885void ApiTestFuzzer::Run() { 7886 // When it is our turn... 7887 gate_->Wait(); 7888 { 7889 // ... get the V8 lock and start running the test. 7890 v8::Locker locker; 7891 CallTest(); 7892 } 7893 // This test finished. 7894 active_ = false; 7895 active_tests_--; 7896 // If it was the last then signal that fact. 7897 if (active_tests_ == 0) { 7898 all_tests_done_->Signal(); 7899 } else { 7900 // Otherwise select a new test and start that. 7901 NextThread(); 7902 } 7903} 7904 7905 7906static unsigned linear_congruential_generator; 7907 7908 7909void ApiTestFuzzer::Setup(PartOfTest part) { 7910 linear_congruential_generator = i::FLAG_testing_prng_seed; 7911 fuzzing_ = true; 7912 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1); 7913 int end = (part == FIRST_PART) 7914 ? (RegisterThreadedTest::count() >> 1) 7915 : RegisterThreadedTest::count(); 7916 active_tests_ = tests_being_run_ = end - start; 7917 for (int i = 0; i < tests_being_run_; i++) { 7918 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start); 7919 } 7920 for (int i = 0; i < active_tests_; i++) { 7921 RegisterThreadedTest::nth(i)->fuzzer_->Start(); 7922 } 7923} 7924 7925 7926static void CallTestNumber(int test_number) { 7927 (RegisterThreadedTest::nth(test_number)->callback())(); 7928} 7929 7930 7931void ApiTestFuzzer::RunAllTests() { 7932 // Set off the first test. 7933 current_ = -1; 7934 NextThread(); 7935 // Wait till they are all done. 7936 all_tests_done_->Wait(); 7937} 7938 7939 7940int ApiTestFuzzer::GetNextTestNumber() { 7941 int next_test; 7942 do { 7943 next_test = (linear_congruential_generator >> 16) % tests_being_run_; 7944 linear_congruential_generator *= 1664525u; 7945 linear_congruential_generator += 1013904223u; 7946 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_); 7947 return next_test; 7948} 7949 7950 7951void ApiTestFuzzer::ContextSwitch() { 7952 // If the new thread is the same as the current thread there is nothing to do. 7953 if (NextThread()) { 7954 // Now it can start. 7955 v8::Unlocker unlocker; 7956 // Wait till someone starts us again. 7957 gate_->Wait(); 7958 // And we're off. 7959 } 7960} 7961 7962 7963void ApiTestFuzzer::TearDown() { 7964 fuzzing_ = false; 7965 for (int i = 0; i < RegisterThreadedTest::count(); i++) { 7966 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_; 7967 if (fuzzer != NULL) fuzzer->Join(); 7968 } 7969} 7970 7971 7972// Lets not be needlessly self-referential. 7973TEST(Threading) { 7974 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART); 7975 ApiTestFuzzer::RunAllTests(); 7976 ApiTestFuzzer::TearDown(); 7977} 7978 7979TEST(Threading2) { 7980 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART); 7981 ApiTestFuzzer::RunAllTests(); 7982 ApiTestFuzzer::TearDown(); 7983} 7984 7985 7986void ApiTestFuzzer::CallTest() { 7987 if (kLogThreading) 7988 printf("Start test %d\n", test_number_); 7989 CallTestNumber(test_number_); 7990 if (kLogThreading) 7991 printf("End test %d\n", test_number_); 7992} 7993 7994 7995static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) { 7996 CHECK(v8::Locker::IsLocked()); 7997 ApiTestFuzzer::Fuzz(); 7998 v8::Unlocker unlocker; 7999 const char* code = "throw 7;"; 8000 { 8001 v8::Locker nested_locker; 8002 v8::HandleScope scope; 8003 v8::Handle<Value> exception; 8004 { v8::TryCatch try_catch; 8005 v8::Handle<Value> value = CompileRun(code); 8006 CHECK(value.IsEmpty()); 8007 CHECK(try_catch.HasCaught()); 8008 // Make sure to wrap the exception in a new handle because 8009 // the handle returned from the TryCatch is destroyed 8010 // when the TryCatch is destroyed. 8011 exception = Local<Value>::New(try_catch.Exception()); 8012 } 8013 return v8::ThrowException(exception); 8014 } 8015} 8016 8017 8018static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) { 8019 CHECK(v8::Locker::IsLocked()); 8020 ApiTestFuzzer::Fuzz(); 8021 v8::Unlocker unlocker; 8022 const char* code = "throw 7;"; 8023 { 8024 v8::Locker nested_locker; 8025 v8::HandleScope scope; 8026 v8::Handle<Value> value = CompileRun(code); 8027 CHECK(value.IsEmpty()); 8028 return v8_str("foo"); 8029 } 8030} 8031 8032 8033// These are locking tests that don't need to be run again 8034// as part of the locking aggregation tests. 8035TEST(NestedLockers) { 8036 v8::Locker locker; 8037 CHECK(v8::Locker::IsLocked()); 8038 v8::HandleScope scope; 8039 LocalContext env; 8040 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS); 8041 Local<Function> fun = fun_templ->GetFunction(); 8042 env->Global()->Set(v8_str("throw_in_js"), fun); 8043 Local<Script> script = v8_compile("(function () {" 8044 " try {" 8045 " throw_in_js();" 8046 " return 42;" 8047 " } catch (e) {" 8048 " return e * 13;" 8049 " }" 8050 "})();"); 8051 CHECK_EQ(91, script->Run()->Int32Value()); 8052} 8053 8054 8055// These are locking tests that don't need to be run again 8056// as part of the locking aggregation tests. 8057TEST(NestedLockersNoTryCatch) { 8058 v8::Locker locker; 8059 v8::HandleScope scope; 8060 LocalContext env; 8061 Local<v8::FunctionTemplate> fun_templ = 8062 v8::FunctionTemplate::New(ThrowInJSNoCatch); 8063 Local<Function> fun = fun_templ->GetFunction(); 8064 env->Global()->Set(v8_str("throw_in_js"), fun); 8065 Local<Script> script = v8_compile("(function () {" 8066 " try {" 8067 " throw_in_js();" 8068 " return 42;" 8069 " } catch (e) {" 8070 " return e * 13;" 8071 " }" 8072 "})();"); 8073 CHECK_EQ(91, script->Run()->Int32Value()); 8074} 8075 8076 8077THREADED_TEST(RecursiveLocking) { 8078 v8::Locker locker; 8079 { 8080 v8::Locker locker2; 8081 CHECK(v8::Locker::IsLocked()); 8082 } 8083} 8084 8085 8086static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) { 8087 ApiTestFuzzer::Fuzz(); 8088 v8::Unlocker unlocker; 8089 return v8::Undefined(); 8090} 8091 8092 8093THREADED_TEST(LockUnlockLock) { 8094 { 8095 v8::Locker locker; 8096 v8::HandleScope scope; 8097 LocalContext env; 8098 Local<v8::FunctionTemplate> fun_templ = 8099 v8::FunctionTemplate::New(UnlockForAMoment); 8100 Local<Function> fun = fun_templ->GetFunction(); 8101 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 8102 Local<Script> script = v8_compile("(function () {" 8103 " unlock_for_a_moment();" 8104 " return 42;" 8105 "})();"); 8106 CHECK_EQ(42, script->Run()->Int32Value()); 8107 } 8108 { 8109 v8::Locker locker; 8110 v8::HandleScope scope; 8111 LocalContext env; 8112 Local<v8::FunctionTemplate> fun_templ = 8113 v8::FunctionTemplate::New(UnlockForAMoment); 8114 Local<Function> fun = fun_templ->GetFunction(); 8115 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 8116 Local<Script> script = v8_compile("(function () {" 8117 " unlock_for_a_moment();" 8118 " return 42;" 8119 "})();"); 8120 CHECK_EQ(42, script->Run()->Int32Value()); 8121 } 8122} 8123 8124 8125static int GetGlobalObjectsCount() { 8126 int count = 0; 8127 i::HeapIterator it; 8128 for (i::HeapObject* object = it.next(); object != NULL; object = it.next()) 8129 if (object->IsJSGlobalObject()) count++; 8130 return count; 8131} 8132 8133 8134static void CheckSurvivingGlobalObjectsCount(int expected) { 8135 // We need to collect all garbage twice to be sure that everything 8136 // has been collected. This is because inline caches are cleared in 8137 // the first garbage collection but some of the maps have already 8138 // been marked at that point. Therefore some of the maps are not 8139 // collected until the second garbage collection. 8140 i::Heap::CollectAllGarbage(false); 8141 i::Heap::CollectAllGarbage(false); 8142 int count = GetGlobalObjectsCount(); 8143#ifdef DEBUG 8144 if (count != expected) i::Heap::TracePathToGlobal(); 8145#endif 8146 CHECK_EQ(expected, count); 8147} 8148 8149 8150TEST(DontLeakGlobalObjects) { 8151 // Regression test for issues 1139850 and 1174891. 8152 8153 v8::V8::Initialize(); 8154 8155 for (int i = 0; i < 5; i++) { 8156 { v8::HandleScope scope; 8157 LocalContext context; 8158 } 8159 CheckSurvivingGlobalObjectsCount(0); 8160 8161 { v8::HandleScope scope; 8162 LocalContext context; 8163 v8_compile("Date")->Run(); 8164 } 8165 CheckSurvivingGlobalObjectsCount(0); 8166 8167 { v8::HandleScope scope; 8168 LocalContext context; 8169 v8_compile("/aaa/")->Run(); 8170 } 8171 CheckSurvivingGlobalObjectsCount(0); 8172 8173 { v8::HandleScope scope; 8174 const char* extension_list[] = { "v8/gc" }; 8175 v8::ExtensionConfiguration extensions(1, extension_list); 8176 LocalContext context(&extensions); 8177 v8_compile("gc();")->Run(); 8178 } 8179 CheckSurvivingGlobalObjectsCount(0); 8180 } 8181} 8182 8183 8184v8::Persistent<v8::Object> some_object; 8185v8::Persistent<v8::Object> bad_handle; 8186 8187void NewPersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) { 8188 v8::HandleScope scope; 8189 bad_handle = v8::Persistent<v8::Object>::New(some_object); 8190 handle.Dispose(); 8191} 8192 8193 8194THREADED_TEST(NewPersistentHandleFromWeakCallback) { 8195 LocalContext context; 8196 8197 v8::Persistent<v8::Object> handle1, handle2; 8198 { 8199 v8::HandleScope scope; 8200 some_object = v8::Persistent<v8::Object>::New(v8::Object::New()); 8201 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8202 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8203 } 8204 // Note: order is implementation dependent alas: currently 8205 // global handle nodes are processed by PostGarbageCollectionProcessing 8206 // in reverse allocation order, so if second allocated handle is deleted, 8207 // weak callback of the first handle would be able to 'reallocate' it. 8208 handle1.MakeWeak(NULL, NewPersistentHandleCallback); 8209 handle2.Dispose(); 8210 i::Heap::CollectAllGarbage(false); 8211} 8212 8213 8214v8::Persistent<v8::Object> to_be_disposed; 8215 8216void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) { 8217 to_be_disposed.Dispose(); 8218 i::Heap::CollectAllGarbage(false); 8219 handle.Dispose(); 8220} 8221 8222 8223THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) { 8224 LocalContext context; 8225 8226 v8::Persistent<v8::Object> handle1, handle2; 8227 { 8228 v8::HandleScope scope; 8229 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8230 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8231 } 8232 handle1.MakeWeak(NULL, DisposeAndForceGcCallback); 8233 to_be_disposed = handle2; 8234 i::Heap::CollectAllGarbage(false); 8235} 8236 8237void DisposingCallback(v8::Persistent<v8::Value> handle, void*) { 8238 handle.Dispose(); 8239} 8240 8241void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) { 8242 v8::HandleScope scope; 8243 v8::Persistent<v8::Object>::New(v8::Object::New()); 8244 handle.Dispose(); 8245} 8246 8247 8248THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) { 8249 LocalContext context; 8250 8251 v8::Persistent<v8::Object> handle1, handle2, handle3; 8252 { 8253 v8::HandleScope scope; 8254 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8255 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8256 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8257 } 8258 handle2.MakeWeak(NULL, DisposingCallback); 8259 handle3.MakeWeak(NULL, HandleCreatingCallback); 8260 i::Heap::CollectAllGarbage(false); 8261} 8262 8263 8264THREADED_TEST(CheckForCrossContextObjectLiterals) { 8265 v8::V8::Initialize(); 8266 8267 const int nof = 2; 8268 const char* sources[nof] = { 8269 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }", 8270 "Object()" 8271 }; 8272 8273 for (int i = 0; i < nof; i++) { 8274 const char* source = sources[i]; 8275 { v8::HandleScope scope; 8276 LocalContext context; 8277 CompileRun(source); 8278 } 8279 { v8::HandleScope scope; 8280 LocalContext context; 8281 CompileRun(source); 8282 } 8283 } 8284} 8285 8286 8287static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) { 8288 v8::HandleScope inner; 8289 env->Enter(); 8290 v8::Handle<Value> three = v8_num(3); 8291 v8::Handle<Value> value = inner.Close(three); 8292 env->Exit(); 8293 return value; 8294} 8295 8296 8297THREADED_TEST(NestedHandleScopeAndContexts) { 8298 v8::HandleScope outer; 8299 v8::Persistent<Context> env = Context::New(); 8300 env->Enter(); 8301 v8::Handle<Value> value = NestedScope(env); 8302 v8::Handle<String> str = value->ToString(); 8303 env->Exit(); 8304 env.Dispose(); 8305} 8306 8307 8308THREADED_TEST(ExternalAllocatedMemory) { 8309 v8::HandleScope outer; 8310 v8::Persistent<Context> env = Context::New(); 8311 const int kSize = 1024*1024; 8312 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize); 8313 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0); 8314} 8315 8316 8317THREADED_TEST(DisposeEnteredContext) { 8318 v8::HandleScope scope; 8319 LocalContext outer; 8320 { v8::Persistent<v8::Context> inner = v8::Context::New(); 8321 inner->Enter(); 8322 inner.Dispose(); 8323 inner.Clear(); 8324 inner->Exit(); 8325 } 8326} 8327 8328 8329// Regression test for issue 54, object templates with internal fields 8330// but no accessors or interceptors did not get their internal field 8331// count set on instances. 8332THREADED_TEST(Regress54) { 8333 v8::HandleScope outer; 8334 LocalContext context; 8335 static v8::Persistent<v8::ObjectTemplate> templ; 8336 if (templ.IsEmpty()) { 8337 v8::HandleScope inner; 8338 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New(); 8339 local->SetInternalFieldCount(1); 8340 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local)); 8341 } 8342 v8::Handle<v8::Object> result = templ->NewInstance(); 8343 CHECK_EQ(1, result->InternalFieldCount()); 8344} 8345 8346 8347// If part of the threaded tests, this test makes ThreadingTest fail 8348// on mac. 8349TEST(CatchStackOverflow) { 8350 v8::HandleScope scope; 8351 LocalContext context; 8352 v8::TryCatch try_catch; 8353 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New( 8354 "function f() {" 8355 " return f();" 8356 "}" 8357 "" 8358 "f();")); 8359 v8::Handle<v8::Value> result = script->Run(); 8360 CHECK(result.IsEmpty()); 8361} 8362 8363 8364static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script, 8365 const char* resource_name, 8366 int line_offset) { 8367 v8::HandleScope scope; 8368 v8::TryCatch try_catch; 8369 v8::Handle<v8::Value> result = script->Run(); 8370 CHECK(result.IsEmpty()); 8371 CHECK(try_catch.HasCaught()); 8372 v8::Handle<v8::Message> message = try_catch.Message(); 8373 CHECK(!message.IsEmpty()); 8374 CHECK_EQ(10 + line_offset, message->GetLineNumber()); 8375 CHECK_EQ(91, message->GetStartPosition()); 8376 CHECK_EQ(92, message->GetEndPosition()); 8377 CHECK_EQ(2, message->GetStartColumn()); 8378 CHECK_EQ(3, message->GetEndColumn()); 8379 v8::String::AsciiValue line(message->GetSourceLine()); 8380 CHECK_EQ(" throw 'nirk';", *line); 8381 v8::String::AsciiValue name(message->GetScriptResourceName()); 8382 CHECK_EQ(resource_name, *name); 8383} 8384 8385 8386THREADED_TEST(TryCatchSourceInfo) { 8387 v8::HandleScope scope; 8388 LocalContext context; 8389 v8::Handle<v8::String> source = v8::String::New( 8390 "function Foo() {\n" 8391 " return Bar();\n" 8392 "}\n" 8393 "\n" 8394 "function Bar() {\n" 8395 " return Baz();\n" 8396 "}\n" 8397 "\n" 8398 "function Baz() {\n" 8399 " throw 'nirk';\n" 8400 "}\n" 8401 "\n" 8402 "Foo();\n"); 8403 8404 const char* resource_name; 8405 v8::Handle<v8::Script> script; 8406 resource_name = "test.js"; 8407 script = v8::Script::Compile(source, v8::String::New(resource_name)); 8408 CheckTryCatchSourceInfo(script, resource_name, 0); 8409 8410 resource_name = "test1.js"; 8411 v8::ScriptOrigin origin1(v8::String::New(resource_name)); 8412 script = v8::Script::Compile(source, &origin1); 8413 CheckTryCatchSourceInfo(script, resource_name, 0); 8414 8415 resource_name = "test2.js"; 8416 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7)); 8417 script = v8::Script::Compile(source, &origin2); 8418 CheckTryCatchSourceInfo(script, resource_name, 7); 8419} 8420 8421 8422THREADED_TEST(CompilationCache) { 8423 v8::HandleScope scope; 8424 LocalContext context; 8425 v8::Handle<v8::String> source0 = v8::String::New("1234"); 8426 v8::Handle<v8::String> source1 = v8::String::New("1234"); 8427 v8::Handle<v8::Script> script0 = 8428 v8::Script::Compile(source0, v8::String::New("test.js")); 8429 v8::Handle<v8::Script> script1 = 8430 v8::Script::Compile(source1, v8::String::New("test.js")); 8431 v8::Handle<v8::Script> script2 = 8432 v8::Script::Compile(source0); // different origin 8433 CHECK_EQ(1234, script0->Run()->Int32Value()); 8434 CHECK_EQ(1234, script1->Run()->Int32Value()); 8435 CHECK_EQ(1234, script2->Run()->Int32Value()); 8436} 8437 8438 8439static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) { 8440 ApiTestFuzzer::Fuzz(); 8441 return v8_num(42); 8442} 8443 8444 8445THREADED_TEST(CallbackFunctionName) { 8446 v8::HandleScope scope; 8447 LocalContext context; 8448 Local<ObjectTemplate> t = ObjectTemplate::New(); 8449 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback)); 8450 context->Global()->Set(v8_str("obj"), t->NewInstance()); 8451 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name"); 8452 CHECK(value->IsString()); 8453 v8::String::AsciiValue name(value); 8454 CHECK_EQ("asdf", *name); 8455} 8456 8457 8458THREADED_TEST(DateAccess) { 8459 v8::HandleScope scope; 8460 LocalContext context; 8461 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0); 8462 CHECK(date->IsDate()); 8463 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue()); 8464} 8465 8466 8467void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) { 8468 v8::Handle<v8::Object> obj = val.As<v8::Object>(); 8469 v8::Handle<v8::Array> props = obj->GetPropertyNames(); 8470 CHECK_EQ(elmc, props->Length()); 8471 for (int i = 0; i < elmc; i++) { 8472 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i))); 8473 CHECK_EQ(elmv[i], *elm); 8474 } 8475} 8476 8477 8478THREADED_TEST(PropertyEnumeration) { 8479 v8::HandleScope scope; 8480 LocalContext context; 8481 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New( 8482 "var result = [];" 8483 "result[0] = {};" 8484 "result[1] = {a: 1, b: 2};" 8485 "result[2] = [1, 2, 3];" 8486 "var proto = {x: 1, y: 2, z: 3};" 8487 "var x = { __proto__: proto, w: 0, z: 1 };" 8488 "result[3] = x;" 8489 "result;"))->Run(); 8490 v8::Handle<v8::Array> elms = obj.As<v8::Array>(); 8491 CHECK_EQ(4, elms->Length()); 8492 int elmc0 = 0; 8493 const char** elmv0 = NULL; 8494 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0); 8495 int elmc1 = 2; 8496 const char* elmv1[] = {"a", "b"}; 8497 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1); 8498 int elmc2 = 3; 8499 const char* elmv2[] = {"0", "1", "2"}; 8500 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2); 8501 int elmc3 = 4; 8502 const char* elmv3[] = {"w", "z", "x", "y"}; 8503 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3); 8504} 8505 8506 8507static bool NamedSetAccessBlocker(Local<v8::Object> obj, 8508 Local<Value> name, 8509 v8::AccessType type, 8510 Local<Value> data) { 8511 return type != v8::ACCESS_SET; 8512} 8513 8514 8515static bool IndexedSetAccessBlocker(Local<v8::Object> obj, 8516 uint32_t key, 8517 v8::AccessType type, 8518 Local<Value> data) { 8519 return type != v8::ACCESS_SET; 8520} 8521 8522 8523THREADED_TEST(DisableAccessChecksWhileConfiguring) { 8524 v8::HandleScope scope; 8525 LocalContext context; 8526 Local<ObjectTemplate> templ = ObjectTemplate::New(); 8527 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker, 8528 IndexedSetAccessBlocker); 8529 templ->Set(v8_str("x"), v8::True()); 8530 Local<v8::Object> instance = templ->NewInstance(); 8531 context->Global()->Set(v8_str("obj"), instance); 8532 Local<Value> value = CompileRun("obj.x"); 8533 CHECK(value->BooleanValue()); 8534} 8535 8536 8537static bool NamedGetAccessBlocker(Local<v8::Object> obj, 8538 Local<Value> name, 8539 v8::AccessType type, 8540 Local<Value> data) { 8541 return false; 8542} 8543 8544 8545static bool IndexedGetAccessBlocker(Local<v8::Object> obj, 8546 uint32_t key, 8547 v8::AccessType type, 8548 Local<Value> data) { 8549 return false; 8550} 8551 8552 8553 8554THREADED_TEST(AccessChecksReenabledCorrectly) { 8555 v8::HandleScope scope; 8556 LocalContext context; 8557 Local<ObjectTemplate> templ = ObjectTemplate::New(); 8558 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker, 8559 IndexedGetAccessBlocker); 8560 templ->Set(v8_str("a"), v8_str("a")); 8561 // Add more than 8 (see kMaxFastProperties) properties 8562 // so that the constructor will force copying map. 8563 // Cannot sprintf, gcc complains unsafety. 8564 char buf[4]; 8565 for (char i = '0'; i <= '9' ; i++) { 8566 buf[0] = i; 8567 for (char j = '0'; j <= '9'; j++) { 8568 buf[1] = j; 8569 for (char k = '0'; k <= '9'; k++) { 8570 buf[2] = k; 8571 buf[3] = 0; 8572 templ->Set(v8_str(buf), v8::Number::New(k)); 8573 } 8574 } 8575 } 8576 8577 Local<v8::Object> instance_1 = templ->NewInstance(); 8578 context->Global()->Set(v8_str("obj_1"), instance_1); 8579 8580 Local<Value> value_1 = CompileRun("obj_1.a"); 8581 CHECK(value_1->IsUndefined()); 8582 8583 Local<v8::Object> instance_2 = templ->NewInstance(); 8584 context->Global()->Set(v8_str("obj_2"), instance_2); 8585 8586 Local<Value> value_2 = CompileRun("obj_2.a"); 8587 CHECK(value_2->IsUndefined()); 8588} 8589 8590 8591// This tests that access check information remains on the global 8592// object template when creating contexts. 8593THREADED_TEST(AccessControlRepeatedContextCreation) { 8594 v8::HandleScope handle_scope; 8595 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 8596 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker, 8597 IndexedSetAccessBlocker); 8598 i::Handle<i::ObjectTemplateInfo> internal_template = 8599 v8::Utils::OpenHandle(*global_template); 8600 CHECK(!internal_template->constructor()->IsUndefined()); 8601 i::Handle<i::FunctionTemplateInfo> constructor( 8602 i::FunctionTemplateInfo::cast(internal_template->constructor())); 8603 CHECK(!constructor->access_check_info()->IsUndefined()); 8604 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 8605 CHECK(!constructor->access_check_info()->IsUndefined()); 8606} 8607 8608 8609THREADED_TEST(TurnOnAccessCheck) { 8610 v8::HandleScope handle_scope; 8611 8612 // Create an environment with access check to the global object disabled by 8613 // default. 8614 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 8615 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker, 8616 IndexedGetAccessBlocker, 8617 v8::Handle<v8::Value>(), 8618 false); 8619 v8::Persistent<Context> context = Context::New(NULL, global_template); 8620 Context::Scope context_scope(context); 8621 8622 // Set up a property and a number of functions. 8623 context->Global()->Set(v8_str("a"), v8_num(1)); 8624 CompileRun("function f1() {return a;}" 8625 "function f2() {return a;}" 8626 "function g1() {return h();}" 8627 "function g2() {return h();}" 8628 "function h() {return 1;}"); 8629 Local<Function> f1 = 8630 Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); 8631 Local<Function> f2 = 8632 Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); 8633 Local<Function> g1 = 8634 Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); 8635 Local<Function> g2 = 8636 Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); 8637 Local<Function> h = 8638 Local<Function>::Cast(context->Global()->Get(v8_str("h"))); 8639 8640 // Get the global object. 8641 v8::Handle<v8::Object> global = context->Global(); 8642 8643 // Call f1 one time and f2 a number of times. This will ensure that f1 still 8644 // uses the runtime system to retreive property a whereas f2 uses global load 8645 // inline cache. 8646 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1))); 8647 for (int i = 0; i < 4; i++) { 8648 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1))); 8649 } 8650 8651 // Same for g1 and g2. 8652 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1))); 8653 for (int i = 0; i < 4; i++) { 8654 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1))); 8655 } 8656 8657 // Detach the global and turn on access check. 8658 context->DetachGlobal(); 8659 context->Global()->TurnOnAccessCheck(); 8660 8661 // Failing access check to property get results in undefined. 8662 CHECK(f1->Call(global, 0, NULL)->IsUndefined()); 8663 CHECK(f2->Call(global, 0, NULL)->IsUndefined()); 8664 8665 // Failing access check to function call results in exception. 8666 CHECK(g1->Call(global, 0, NULL).IsEmpty()); 8667 CHECK(g2->Call(global, 0, NULL).IsEmpty()); 8668 8669 // No failing access check when just returning a constant. 8670 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1))); 8671} 8672 8673 8674// This test verifies that pre-compilation (aka preparsing) can be called 8675// without initializing the whole VM. Thus we cannot run this test in a 8676// multi-threaded setup. 8677TEST(PreCompile) { 8678 // TODO(155): This test would break without the initialization of V8. This is 8679 // a workaround for now to make this test not fail. 8680 v8::V8::Initialize(); 8681 const char* script = "function foo(a) { return a+1; }"; 8682 v8::ScriptData* sd = 8683 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8684 CHECK_NE(sd->Length(), 0); 8685 CHECK_NE(sd->Data(), NULL); 8686 CHECK(!sd->HasError()); 8687 delete sd; 8688} 8689 8690 8691TEST(PreCompileWithError) { 8692 v8::V8::Initialize(); 8693 const char* script = "function foo(a) { return 1 * * 2; }"; 8694 v8::ScriptData* sd = 8695 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8696 CHECK(sd->HasError()); 8697 delete sd; 8698} 8699 8700 8701TEST(Regress31661) { 8702 v8::V8::Initialize(); 8703 const char* script = " The Definintive Guide"; 8704 v8::ScriptData* sd = 8705 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8706 CHECK(sd->HasError()); 8707 delete sd; 8708} 8709 8710 8711// Tests that ScriptData can be serialized and deserialized. 8712TEST(PreCompileSerialization) { 8713 v8::V8::Initialize(); 8714 const char* script = "function foo(a) { return a+1; }"; 8715 v8::ScriptData* sd = 8716 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8717 8718 // Serialize. 8719 int serialized_data_length = sd->Length(); 8720 char* serialized_data = i::NewArray<char>(serialized_data_length); 8721 memcpy(serialized_data, sd->Data(), serialized_data_length); 8722 8723 // Deserialize. 8724 v8::ScriptData* deserialized_sd = 8725 v8::ScriptData::New(serialized_data, serialized_data_length); 8726 8727 // Verify that the original is the same as the deserialized. 8728 CHECK_EQ(sd->Length(), deserialized_sd->Length()); 8729 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length())); 8730 CHECK_EQ(sd->HasError(), deserialized_sd->HasError()); 8731 8732 delete sd; 8733 delete deserialized_sd; 8734} 8735 8736 8737// Attempts to deserialize bad data. 8738TEST(PreCompileDeserializationError) { 8739 v8::V8::Initialize(); 8740 const char* data = "DONT CARE"; 8741 int invalid_size = 3; 8742 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size); 8743 8744 CHECK_EQ(0, sd->Length()); 8745 8746 delete sd; 8747} 8748 8749 8750// Attempts to deserialize bad data. 8751TEST(PreCompileInvalidPreparseDataError) { 8752 v8::V8::Initialize(); 8753 v8::HandleScope scope; 8754 LocalContext context; 8755 8756 const char* script = "function foo(){ return 5;}\n" 8757 "function bar(){ return 6 + 7;} foo();"; 8758 v8::ScriptData* sd = 8759 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8760 CHECK(!sd->HasError()); 8761 // ScriptDataImpl private implementation details 8762 const int kHeaderSize = i::PreparseDataConstants::kHeaderSize; 8763 const int kFunctionEntrySize = i::FunctionEntry::kSize; 8764 const int kFunctionEntryStartOffset = 0; 8765 const int kFunctionEntryEndOffset = 1; 8766 unsigned* sd_data = 8767 reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data())); 8768 8769 // Overwrite function bar's end position with 0. 8770 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0; 8771 v8::TryCatch try_catch; 8772 8773 Local<String> source = String::New(script); 8774 Local<Script> compiled_script = Script::New(source, NULL, sd); 8775 CHECK(try_catch.HasCaught()); 8776 String::AsciiValue exception_value(try_catch.Message()->Get()); 8777 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar", 8778 *exception_value); 8779 8780 try_catch.Reset(); 8781 // Overwrite function bar's start position with 200. The function entry 8782 // will not be found when searching for it by position. 8783 sd = v8::ScriptData::PreCompile(script, i::StrLength(script)); 8784 sd_data = reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data())); 8785 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] = 8786 200; 8787 compiled_script = Script::New(source, NULL, sd); 8788 CHECK(try_catch.HasCaught()); 8789 String::AsciiValue second_exception_value(try_catch.Message()->Get()); 8790 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar", 8791 *second_exception_value); 8792 8793 delete sd; 8794} 8795 8796 8797// Verifies that the Handle<String> and const char* versions of the API produce 8798// the same results (at least for one trivial case). 8799TEST(PreCompileAPIVariationsAreSame) { 8800 v8::V8::Initialize(); 8801 v8::HandleScope scope; 8802 8803 const char* cstring = "function foo(a) { return a+1; }"; 8804 8805 v8::ScriptData* sd_from_cstring = 8806 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring)); 8807 8808 TestAsciiResource* resource = new TestAsciiResource(cstring); 8809 v8::ScriptData* sd_from_external_string = v8::ScriptData::PreCompile( 8810 v8::String::NewExternal(resource)); 8811 8812 v8::ScriptData* sd_from_string = v8::ScriptData::PreCompile( 8813 v8::String::New(cstring)); 8814 8815 CHECK_EQ(sd_from_cstring->Length(), sd_from_external_string->Length()); 8816 CHECK_EQ(0, memcmp(sd_from_cstring->Data(), 8817 sd_from_external_string->Data(), 8818 sd_from_cstring->Length())); 8819 8820 CHECK_EQ(sd_from_cstring->Length(), sd_from_string->Length()); 8821 CHECK_EQ(0, memcmp(sd_from_cstring->Data(), 8822 sd_from_string->Data(), 8823 sd_from_cstring->Length())); 8824 8825 8826 delete sd_from_cstring; 8827 delete sd_from_external_string; 8828 delete sd_from_string; 8829} 8830 8831 8832// This tests that we do not allow dictionary load/call inline caches 8833// to use functions that have not yet been compiled. The potential 8834// problem of loading a function that has not yet been compiled can 8835// arise because we share code between contexts via the compilation 8836// cache. 8837THREADED_TEST(DictionaryICLoadedFunction) { 8838 v8::HandleScope scope; 8839 // Test LoadIC. 8840 for (int i = 0; i < 2; i++) { 8841 LocalContext context; 8842 context->Global()->Set(v8_str("tmp"), v8::True()); 8843 context->Global()->Delete(v8_str("tmp")); 8844 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');"); 8845 } 8846 // Test CallIC. 8847 for (int i = 0; i < 2; i++) { 8848 LocalContext context; 8849 context->Global()->Set(v8_str("tmp"), v8::True()); 8850 context->Global()->Delete(v8_str("tmp")); 8851 CompileRun("for (var j = 0; j < 10; j++) RegExp('')"); 8852 } 8853} 8854 8855 8856// Test that cross-context new calls use the context of the callee to 8857// create the new JavaScript object. 8858THREADED_TEST(CrossContextNew) { 8859 v8::HandleScope scope; 8860 v8::Persistent<Context> context0 = Context::New(); 8861 v8::Persistent<Context> context1 = Context::New(); 8862 8863 // Allow cross-domain access. 8864 Local<String> token = v8_str("<security token>"); 8865 context0->SetSecurityToken(token); 8866 context1->SetSecurityToken(token); 8867 8868 // Set an 'x' property on the Object prototype and define a 8869 // constructor function in context0. 8870 context0->Enter(); 8871 CompileRun("Object.prototype.x = 42; function C() {};"); 8872 context0->Exit(); 8873 8874 // Call the constructor function from context0 and check that the 8875 // result has the 'x' property. 8876 context1->Enter(); 8877 context1->Global()->Set(v8_str("other"), context0->Global()); 8878 Local<Value> value = CompileRun("var instance = new other.C(); instance.x"); 8879 CHECK(value->IsInt32()); 8880 CHECK_EQ(42, value->Int32Value()); 8881 context1->Exit(); 8882 8883 // Dispose the contexts to allow them to be garbage collected. 8884 context0.Dispose(); 8885 context1.Dispose(); 8886} 8887 8888 8889class RegExpInterruptTest { 8890 public: 8891 RegExpInterruptTest() : block_(NULL) {} 8892 ~RegExpInterruptTest() { delete block_; } 8893 void RunTest() { 8894 block_ = i::OS::CreateSemaphore(0); 8895 gc_count_ = 0; 8896 gc_during_regexp_ = 0; 8897 regexp_success_ = false; 8898 gc_success_ = false; 8899 GCThread gc_thread(this); 8900 gc_thread.Start(); 8901 v8::Locker::StartPreemption(1); 8902 8903 LongRunningRegExp(); 8904 { 8905 v8::Unlocker unlock; 8906 gc_thread.Join(); 8907 } 8908 v8::Locker::StopPreemption(); 8909 CHECK(regexp_success_); 8910 CHECK(gc_success_); 8911 } 8912 private: 8913 // Number of garbage collections required. 8914 static const int kRequiredGCs = 5; 8915 8916 class GCThread : public i::Thread { 8917 public: 8918 explicit GCThread(RegExpInterruptTest* test) 8919 : test_(test) {} 8920 virtual void Run() { 8921 test_->CollectGarbage(); 8922 } 8923 private: 8924 RegExpInterruptTest* test_; 8925 }; 8926 8927 void CollectGarbage() { 8928 block_->Wait(); 8929 while (gc_during_regexp_ < kRequiredGCs) { 8930 { 8931 v8::Locker lock; 8932 // TODO(lrn): Perhaps create some garbage before collecting. 8933 i::Heap::CollectAllGarbage(false); 8934 gc_count_++; 8935 } 8936 i::OS::Sleep(1); 8937 } 8938 gc_success_ = true; 8939 } 8940 8941 void LongRunningRegExp() { 8942 block_->Signal(); // Enable garbage collection thread on next preemption. 8943 int rounds = 0; 8944 while (gc_during_regexp_ < kRequiredGCs) { 8945 int gc_before = gc_count_; 8946 { 8947 // Match 15-30 "a"'s against 14 and a "b". 8948 const char* c_source = 8949 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 8950 ".exec('aaaaaaaaaaaaaaab') === null"; 8951 Local<String> source = String::New(c_source); 8952 Local<Script> script = Script::Compile(source); 8953 Local<Value> result = script->Run(); 8954 if (!result->BooleanValue()) { 8955 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit. 8956 return; 8957 } 8958 } 8959 { 8960 // Match 15-30 "a"'s against 15 and a "b". 8961 const char* c_source = 8962 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 8963 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'"; 8964 Local<String> source = String::New(c_source); 8965 Local<Script> script = Script::Compile(source); 8966 Local<Value> result = script->Run(); 8967 if (!result->BooleanValue()) { 8968 gc_during_regexp_ = kRequiredGCs; 8969 return; 8970 } 8971 } 8972 int gc_after = gc_count_; 8973 gc_during_regexp_ += gc_after - gc_before; 8974 rounds++; 8975 i::OS::Sleep(1); 8976 } 8977 regexp_success_ = true; 8978 } 8979 8980 i::Semaphore* block_; 8981 int gc_count_; 8982 int gc_during_regexp_; 8983 bool regexp_success_; 8984 bool gc_success_; 8985}; 8986 8987 8988// Test that a regular expression execution can be interrupted and 8989// survive a garbage collection. 8990TEST(RegExpInterruption) { 8991 v8::Locker lock; 8992 v8::V8::Initialize(); 8993 v8::HandleScope scope; 8994 Local<Context> local_env; 8995 { 8996 LocalContext env; 8997 local_env = env.local(); 8998 } 8999 9000 // Local context should still be live. 9001 CHECK(!local_env.IsEmpty()); 9002 local_env->Enter(); 9003 9004 // Should complete without problems. 9005 RegExpInterruptTest().RunTest(); 9006 9007 local_env->Exit(); 9008} 9009 9010 9011class ApplyInterruptTest { 9012 public: 9013 ApplyInterruptTest() : block_(NULL) {} 9014 ~ApplyInterruptTest() { delete block_; } 9015 void RunTest() { 9016 block_ = i::OS::CreateSemaphore(0); 9017 gc_count_ = 0; 9018 gc_during_apply_ = 0; 9019 apply_success_ = false; 9020 gc_success_ = false; 9021 GCThread gc_thread(this); 9022 gc_thread.Start(); 9023 v8::Locker::StartPreemption(1); 9024 9025 LongRunningApply(); 9026 { 9027 v8::Unlocker unlock; 9028 gc_thread.Join(); 9029 } 9030 v8::Locker::StopPreemption(); 9031 CHECK(apply_success_); 9032 CHECK(gc_success_); 9033 } 9034 private: 9035 // Number of garbage collections required. 9036 static const int kRequiredGCs = 2; 9037 9038 class GCThread : public i::Thread { 9039 public: 9040 explicit GCThread(ApplyInterruptTest* test) 9041 : test_(test) {} 9042 virtual void Run() { 9043 test_->CollectGarbage(); 9044 } 9045 private: 9046 ApplyInterruptTest* test_; 9047 }; 9048 9049 void CollectGarbage() { 9050 block_->Wait(); 9051 while (gc_during_apply_ < kRequiredGCs) { 9052 { 9053 v8::Locker lock; 9054 i::Heap::CollectAllGarbage(false); 9055 gc_count_++; 9056 } 9057 i::OS::Sleep(1); 9058 } 9059 gc_success_ = true; 9060 } 9061 9062 void LongRunningApply() { 9063 block_->Signal(); 9064 int rounds = 0; 9065 while (gc_during_apply_ < kRequiredGCs) { 9066 int gc_before = gc_count_; 9067 { 9068 const char* c_source = 9069 "function do_very_little(bar) {" 9070 " this.foo = bar;" 9071 "}" 9072 "for (var i = 0; i < 100000; i++) {" 9073 " do_very_little.apply(this, ['bar']);" 9074 "}"; 9075 Local<String> source = String::New(c_source); 9076 Local<Script> script = Script::Compile(source); 9077 Local<Value> result = script->Run(); 9078 // Check that no exception was thrown. 9079 CHECK(!result.IsEmpty()); 9080 } 9081 int gc_after = gc_count_; 9082 gc_during_apply_ += gc_after - gc_before; 9083 rounds++; 9084 } 9085 apply_success_ = true; 9086 } 9087 9088 i::Semaphore* block_; 9089 int gc_count_; 9090 int gc_during_apply_; 9091 bool apply_success_; 9092 bool gc_success_; 9093}; 9094 9095 9096// Test that nothing bad happens if we get a preemption just when we were 9097// about to do an apply(). 9098TEST(ApplyInterruption) { 9099 v8::Locker lock; 9100 v8::V8::Initialize(); 9101 v8::HandleScope scope; 9102 Local<Context> local_env; 9103 { 9104 LocalContext env; 9105 local_env = env.local(); 9106 } 9107 9108 // Local context should still be live. 9109 CHECK(!local_env.IsEmpty()); 9110 local_env->Enter(); 9111 9112 // Should complete without problems. 9113 ApplyInterruptTest().RunTest(); 9114 9115 local_env->Exit(); 9116} 9117 9118 9119// Verify that we can clone an object 9120TEST(ObjectClone) { 9121 v8::HandleScope scope; 9122 LocalContext env; 9123 9124 const char* sample = 9125 "var rv = {};" \ 9126 "rv.alpha = 'hello';" \ 9127 "rv.beta = 123;" \ 9128 "rv;"; 9129 9130 // Create an object, verify basics. 9131 Local<Value> val = CompileRun(sample); 9132 CHECK(val->IsObject()); 9133 Local<v8::Object> obj = val.As<v8::Object>(); 9134 obj->Set(v8_str("gamma"), v8_str("cloneme")); 9135 9136 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha"))); 9137 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 9138 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma"))); 9139 9140 // Clone it. 9141 Local<v8::Object> clone = obj->Clone(); 9142 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha"))); 9143 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta"))); 9144 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma"))); 9145 9146 // Set a property on the clone, verify each object. 9147 clone->Set(v8_str("beta"), v8::Integer::New(456)); 9148 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 9149 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta"))); 9150} 9151 9152 9153class AsciiVectorResource : public v8::String::ExternalAsciiStringResource { 9154 public: 9155 explicit AsciiVectorResource(i::Vector<const char> vector) 9156 : data_(vector) {} 9157 virtual ~AsciiVectorResource() {} 9158 virtual size_t length() const { return data_.length(); } 9159 virtual const char* data() const { return data_.start(); } 9160 private: 9161 i::Vector<const char> data_; 9162}; 9163 9164 9165class UC16VectorResource : public v8::String::ExternalStringResource { 9166 public: 9167 explicit UC16VectorResource(i::Vector<const i::uc16> vector) 9168 : data_(vector) {} 9169 virtual ~UC16VectorResource() {} 9170 virtual size_t length() const { return data_.length(); } 9171 virtual const i::uc16* data() const { return data_.start(); } 9172 private: 9173 i::Vector<const i::uc16> data_; 9174}; 9175 9176 9177static void MorphAString(i::String* string, 9178 AsciiVectorResource* ascii_resource, 9179 UC16VectorResource* uc16_resource) { 9180 CHECK(i::StringShape(string).IsExternal()); 9181 if (string->IsAsciiRepresentation()) { 9182 // Check old map is not symbol or long. 9183 CHECK(string->map() == i::Heap::external_ascii_string_map()); 9184 // Morph external string to be TwoByte string. 9185 string->set_map(i::Heap::external_string_map()); 9186 i::ExternalTwoByteString* morphed = 9187 i::ExternalTwoByteString::cast(string); 9188 morphed->set_resource(uc16_resource); 9189 } else { 9190 // Check old map is not symbol or long. 9191 CHECK(string->map() == i::Heap::external_string_map()); 9192 // Morph external string to be ASCII string. 9193 string->set_map(i::Heap::external_ascii_string_map()); 9194 i::ExternalAsciiString* morphed = 9195 i::ExternalAsciiString::cast(string); 9196 morphed->set_resource(ascii_resource); 9197 } 9198} 9199 9200 9201// Test that we can still flatten a string if the components it is built up 9202// from have been turned into 16 bit strings in the mean time. 9203THREADED_TEST(MorphCompositeStringTest) { 9204 const char* c_string = "Now is the time for all good men" 9205 " to come to the aid of the party"; 9206 uint16_t* two_byte_string = AsciiToTwoByteString(c_string); 9207 { 9208 v8::HandleScope scope; 9209 LocalContext env; 9210 AsciiVectorResource ascii_resource( 9211 i::Vector<const char>(c_string, i::StrLength(c_string))); 9212 UC16VectorResource uc16_resource( 9213 i::Vector<const uint16_t>(two_byte_string, 9214 i::StrLength(c_string))); 9215 9216 Local<String> lhs(v8::Utils::ToLocal( 9217 i::Factory::NewExternalStringFromAscii(&ascii_resource))); 9218 Local<String> rhs(v8::Utils::ToLocal( 9219 i::Factory::NewExternalStringFromAscii(&ascii_resource))); 9220 9221 env->Global()->Set(v8_str("lhs"), lhs); 9222 env->Global()->Set(v8_str("rhs"), rhs); 9223 9224 CompileRun( 9225 "var cons = lhs + rhs;" 9226 "var slice = lhs.substring(1, lhs.length - 1);" 9227 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);"); 9228 9229 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource); 9230 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource); 9231 9232 // Now do some stuff to make sure the strings are flattened, etc. 9233 CompileRun( 9234 "/[^a-z]/.test(cons);" 9235 "/[^a-z]/.test(slice);" 9236 "/[^a-z]/.test(slice_on_cons);"); 9237 const char* expected_cons = 9238 "Now is the time for all good men to come to the aid of the party" 9239 "Now is the time for all good men to come to the aid of the party"; 9240 const char* expected_slice = 9241 "ow is the time for all good men to come to the aid of the part"; 9242 const char* expected_slice_on_cons = 9243 "ow is the time for all good men to come to the aid of the party" 9244 "Now is the time for all good men to come to the aid of the part"; 9245 CHECK_EQ(String::New(expected_cons), 9246 env->Global()->Get(v8_str("cons"))); 9247 CHECK_EQ(String::New(expected_slice), 9248 env->Global()->Get(v8_str("slice"))); 9249 CHECK_EQ(String::New(expected_slice_on_cons), 9250 env->Global()->Get(v8_str("slice_on_cons"))); 9251 } 9252 i::DeleteArray(two_byte_string); 9253} 9254 9255 9256TEST(CompileExternalTwoByteSource) { 9257 v8::HandleScope scope; 9258 LocalContext context; 9259 9260 // This is a very short list of sources, which currently is to check for a 9261 // regression caused by r2703. 9262 const char* ascii_sources[] = { 9263 "0.5", 9264 "-0.5", // This mainly testes PushBack in the Scanner. 9265 "--0.5", // This mainly testes PushBack in the Scanner. 9266 NULL 9267 }; 9268 9269 // Compile the sources as external two byte strings. 9270 for (int i = 0; ascii_sources[i] != NULL; i++) { 9271 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]); 9272 UC16VectorResource uc16_resource( 9273 i::Vector<const uint16_t>(two_byte_string, 9274 i::StrLength(ascii_sources[i]))); 9275 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource); 9276 v8::Script::Compile(source); 9277 i::DeleteArray(two_byte_string); 9278 } 9279} 9280 9281 9282class RegExpStringModificationTest { 9283 public: 9284 RegExpStringModificationTest() 9285 : block_(i::OS::CreateSemaphore(0)), 9286 morphs_(0), 9287 morphs_during_regexp_(0), 9288 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)), 9289 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {} 9290 ~RegExpStringModificationTest() { delete block_; } 9291 void RunTest() { 9292 regexp_success_ = false; 9293 morph_success_ = false; 9294 9295 // Initialize the contents of two_byte_content_ to be a uc16 representation 9296 // of "aaaaaaaaaaaaaab". 9297 for (int i = 0; i < 14; i++) { 9298 two_byte_content_[i] = 'a'; 9299 } 9300 two_byte_content_[14] = 'b'; 9301 9302 // Create the input string for the regexp - the one we are going to change 9303 // properties of. 9304 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_); 9305 9306 // Inject the input as a global variable. 9307 i::Handle<i::String> input_name = 9308 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5)); 9309 i::Top::global_context()->global()->SetProperty(*input_name, 9310 *input_, 9311 NONE)->ToObjectChecked(); 9312 9313 9314 MorphThread morph_thread(this); 9315 morph_thread.Start(); 9316 v8::Locker::StartPreemption(1); 9317 LongRunningRegExp(); 9318 { 9319 v8::Unlocker unlock; 9320 morph_thread.Join(); 9321 } 9322 v8::Locker::StopPreemption(); 9323 CHECK(regexp_success_); 9324 CHECK(morph_success_); 9325 } 9326 private: 9327 9328 // Number of string modifications required. 9329 static const int kRequiredModifications = 5; 9330 static const int kMaxModifications = 100; 9331 9332 class MorphThread : public i::Thread { 9333 public: 9334 explicit MorphThread(RegExpStringModificationTest* test) 9335 : test_(test) {} 9336 virtual void Run() { 9337 test_->MorphString(); 9338 } 9339 private: 9340 RegExpStringModificationTest* test_; 9341 }; 9342 9343 void MorphString() { 9344 block_->Wait(); 9345 while (morphs_during_regexp_ < kRequiredModifications && 9346 morphs_ < kMaxModifications) { 9347 { 9348 v8::Locker lock; 9349 // Swap string between ascii and two-byte representation. 9350 i::String* string = *input_; 9351 MorphAString(string, &ascii_resource_, &uc16_resource_); 9352 morphs_++; 9353 } 9354 i::OS::Sleep(1); 9355 } 9356 morph_success_ = true; 9357 } 9358 9359 void LongRunningRegExp() { 9360 block_->Signal(); // Enable morphing thread on next preemption. 9361 while (morphs_during_regexp_ < kRequiredModifications && 9362 morphs_ < kMaxModifications) { 9363 int morphs_before = morphs_; 9364 { 9365 v8::HandleScope scope; 9366 // Match 15-30 "a"'s against 14 and a "b". 9367 const char* c_source = 9368 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 9369 ".exec(input) === null"; 9370 Local<String> source = String::New(c_source); 9371 Local<Script> script = Script::Compile(source); 9372 Local<Value> result = script->Run(); 9373 CHECK(result->IsTrue()); 9374 } 9375 int morphs_after = morphs_; 9376 morphs_during_regexp_ += morphs_after - morphs_before; 9377 } 9378 regexp_success_ = true; 9379 } 9380 9381 i::uc16 two_byte_content_[15]; 9382 i::Semaphore* block_; 9383 int morphs_; 9384 int morphs_during_regexp_; 9385 bool regexp_success_; 9386 bool morph_success_; 9387 i::Handle<i::String> input_; 9388 AsciiVectorResource ascii_resource_; 9389 UC16VectorResource uc16_resource_; 9390}; 9391 9392 9393// Test that a regular expression execution can be interrupted and 9394// the string changed without failing. 9395TEST(RegExpStringModification) { 9396 v8::Locker lock; 9397 v8::V8::Initialize(); 9398 v8::HandleScope scope; 9399 Local<Context> local_env; 9400 { 9401 LocalContext env; 9402 local_env = env.local(); 9403 } 9404 9405 // Local context should still be live. 9406 CHECK(!local_env.IsEmpty()); 9407 local_env->Enter(); 9408 9409 // Should complete without problems. 9410 RegExpStringModificationTest().RunTest(); 9411 9412 local_env->Exit(); 9413} 9414 9415 9416// Test that we can set a property on the global object even if there 9417// is a read-only property in the prototype chain. 9418TEST(ReadOnlyPropertyInGlobalProto) { 9419 v8::HandleScope scope; 9420 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9421 LocalContext context(0, templ); 9422 v8::Handle<v8::Object> global = context->Global(); 9423 v8::Handle<v8::Object> global_proto = 9424 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__"))); 9425 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly); 9426 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly); 9427 // Check without 'eval' or 'with'. 9428 v8::Handle<v8::Value> res = 9429 CompileRun("function f() { x = 42; return x; }; f()"); 9430 // Check with 'eval'. 9431 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()"); 9432 CHECK_EQ(v8::Integer::New(42), res); 9433 // Check with 'with'. 9434 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()"); 9435 CHECK_EQ(v8::Integer::New(42), res); 9436} 9437 9438static int force_set_set_count = 0; 9439static int force_set_get_count = 0; 9440bool pass_on_get = false; 9441 9442static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name, 9443 const v8::AccessorInfo& info) { 9444 force_set_get_count++; 9445 if (pass_on_get) { 9446 return v8::Handle<v8::Value>(); 9447 } else { 9448 return v8::Int32::New(3); 9449 } 9450} 9451 9452static void ForceSetSetter(v8::Local<v8::String> name, 9453 v8::Local<v8::Value> value, 9454 const v8::AccessorInfo& info) { 9455 force_set_set_count++; 9456} 9457 9458static v8::Handle<v8::Value> ForceSetInterceptSetter( 9459 v8::Local<v8::String> name, 9460 v8::Local<v8::Value> value, 9461 const v8::AccessorInfo& info) { 9462 force_set_set_count++; 9463 return v8::Undefined(); 9464} 9465 9466TEST(ForceSet) { 9467 force_set_get_count = 0; 9468 force_set_set_count = 0; 9469 pass_on_get = false; 9470 9471 v8::HandleScope scope; 9472 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9473 v8::Handle<v8::String> access_property = v8::String::New("a"); 9474 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter); 9475 LocalContext context(NULL, templ); 9476 v8::Handle<v8::Object> global = context->Global(); 9477 9478 // Ordinary properties 9479 v8::Handle<v8::String> simple_property = v8::String::New("p"); 9480 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly); 9481 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9482 // This should fail because the property is read-only 9483 global->Set(simple_property, v8::Int32::New(5)); 9484 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9485 // This should succeed even though the property is read-only 9486 global->ForceSet(simple_property, v8::Int32::New(6)); 9487 CHECK_EQ(6, global->Get(simple_property)->Int32Value()); 9488 9489 // Accessors 9490 CHECK_EQ(0, force_set_set_count); 9491 CHECK_EQ(0, force_set_get_count); 9492 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 9493 // CHECK_EQ the property shouldn't override it, just call the setter 9494 // which in this case does nothing. 9495 global->Set(access_property, v8::Int32::New(7)); 9496 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 9497 CHECK_EQ(1, force_set_set_count); 9498 CHECK_EQ(2, force_set_get_count); 9499 // Forcing the property to be set should override the accessor without 9500 // calling it 9501 global->ForceSet(access_property, v8::Int32::New(8)); 9502 CHECK_EQ(8, global->Get(access_property)->Int32Value()); 9503 CHECK_EQ(1, force_set_set_count); 9504 CHECK_EQ(2, force_set_get_count); 9505} 9506 9507TEST(ForceSetWithInterceptor) { 9508 force_set_get_count = 0; 9509 force_set_set_count = 0; 9510 pass_on_get = false; 9511 9512 v8::HandleScope scope; 9513 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9514 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter); 9515 LocalContext context(NULL, templ); 9516 v8::Handle<v8::Object> global = context->Global(); 9517 9518 v8::Handle<v8::String> some_property = v8::String::New("a"); 9519 CHECK_EQ(0, force_set_set_count); 9520 CHECK_EQ(0, force_set_get_count); 9521 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 9522 // Setting the property shouldn't override it, just call the setter 9523 // which in this case does nothing. 9524 global->Set(some_property, v8::Int32::New(7)); 9525 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 9526 CHECK_EQ(1, force_set_set_count); 9527 CHECK_EQ(2, force_set_get_count); 9528 // Getting the property when the interceptor returns an empty handle 9529 // should yield undefined, since the property isn't present on the 9530 // object itself yet. 9531 pass_on_get = true; 9532 CHECK(global->Get(some_property)->IsUndefined()); 9533 CHECK_EQ(1, force_set_set_count); 9534 CHECK_EQ(3, force_set_get_count); 9535 // Forcing the property to be set should cause the value to be 9536 // set locally without calling the interceptor. 9537 global->ForceSet(some_property, v8::Int32::New(8)); 9538 CHECK_EQ(8, global->Get(some_property)->Int32Value()); 9539 CHECK_EQ(1, force_set_set_count); 9540 CHECK_EQ(4, force_set_get_count); 9541 // Reenabling the interceptor should cause it to take precedence over 9542 // the property 9543 pass_on_get = false; 9544 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 9545 CHECK_EQ(1, force_set_set_count); 9546 CHECK_EQ(5, force_set_get_count); 9547 // The interceptor should also work for other properties 9548 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value()); 9549 CHECK_EQ(1, force_set_set_count); 9550 CHECK_EQ(6, force_set_get_count); 9551} 9552 9553 9554THREADED_TEST(ForceDelete) { 9555 v8::HandleScope scope; 9556 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9557 LocalContext context(NULL, templ); 9558 v8::Handle<v8::Object> global = context->Global(); 9559 9560 // Ordinary properties 9561 v8::Handle<v8::String> simple_property = v8::String::New("p"); 9562 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete); 9563 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9564 // This should fail because the property is dont-delete. 9565 CHECK(!global->Delete(simple_property)); 9566 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9567 // This should succeed even though the property is dont-delete. 9568 CHECK(global->ForceDelete(simple_property)); 9569 CHECK(global->Get(simple_property)->IsUndefined()); 9570} 9571 9572 9573static int force_delete_interceptor_count = 0; 9574static bool pass_on_delete = false; 9575 9576 9577static v8::Handle<v8::Boolean> ForceDeleteDeleter( 9578 v8::Local<v8::String> name, 9579 const v8::AccessorInfo& info) { 9580 force_delete_interceptor_count++; 9581 if (pass_on_delete) { 9582 return v8::Handle<v8::Boolean>(); 9583 } else { 9584 return v8::True(); 9585 } 9586} 9587 9588 9589THREADED_TEST(ForceDeleteWithInterceptor) { 9590 force_delete_interceptor_count = 0; 9591 pass_on_delete = false; 9592 9593 v8::HandleScope scope; 9594 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9595 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter); 9596 LocalContext context(NULL, templ); 9597 v8::Handle<v8::Object> global = context->Global(); 9598 9599 v8::Handle<v8::String> some_property = v8::String::New("a"); 9600 global->Set(some_property, v8::Integer::New(42), v8::DontDelete); 9601 9602 // Deleting a property should get intercepted and nothing should 9603 // happen. 9604 CHECK_EQ(0, force_delete_interceptor_count); 9605 CHECK(global->Delete(some_property)); 9606 CHECK_EQ(1, force_delete_interceptor_count); 9607 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 9608 // Deleting the property when the interceptor returns an empty 9609 // handle should not delete the property since it is DontDelete. 9610 pass_on_delete = true; 9611 CHECK(!global->Delete(some_property)); 9612 CHECK_EQ(2, force_delete_interceptor_count); 9613 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 9614 // Forcing the property to be deleted should delete the value 9615 // without calling the interceptor. 9616 CHECK(global->ForceDelete(some_property)); 9617 CHECK(global->Get(some_property)->IsUndefined()); 9618 CHECK_EQ(2, force_delete_interceptor_count); 9619} 9620 9621 9622// Make sure that forcing a delete invalidates any IC stubs, so we 9623// don't read the hole value. 9624THREADED_TEST(ForceDeleteIC) { 9625 v8::HandleScope scope; 9626 LocalContext context; 9627 // Create a DontDelete variable on the global object. 9628 CompileRun("this.__proto__ = { foo: 'horse' };" 9629 "var foo = 'fish';" 9630 "function f() { return foo.length; }"); 9631 // Initialize the IC for foo in f. 9632 CompileRun("for (var i = 0; i < 4; i++) f();"); 9633 // Make sure the value of foo is correct before the deletion. 9634 CHECK_EQ(4, CompileRun("f()")->Int32Value()); 9635 // Force the deletion of foo. 9636 CHECK(context->Global()->ForceDelete(v8_str("foo"))); 9637 // Make sure the value for foo is read from the prototype, and that 9638 // we don't get in trouble with reading the deleted cell value 9639 // sentinel. 9640 CHECK_EQ(5, CompileRun("f()")->Int32Value()); 9641} 9642 9643 9644v8::Persistent<Context> calling_context0; 9645v8::Persistent<Context> calling_context1; 9646v8::Persistent<Context> calling_context2; 9647 9648 9649// Check that the call to the callback is initiated in 9650// calling_context2, the directly calling context is calling_context1 9651// and the callback itself is in calling_context0. 9652static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) { 9653 ApiTestFuzzer::Fuzz(); 9654 CHECK(Context::GetCurrent() == calling_context0); 9655 CHECK(Context::GetCalling() == calling_context1); 9656 CHECK(Context::GetEntered() == calling_context2); 9657 return v8::Integer::New(42); 9658} 9659 9660 9661THREADED_TEST(GetCallingContext) { 9662 v8::HandleScope scope; 9663 9664 calling_context0 = Context::New(); 9665 calling_context1 = Context::New(); 9666 calling_context2 = Context::New(); 9667 9668 // Allow cross-domain access. 9669 Local<String> token = v8_str("<security token>"); 9670 calling_context0->SetSecurityToken(token); 9671 calling_context1->SetSecurityToken(token); 9672 calling_context2->SetSecurityToken(token); 9673 9674 // Create an object with a C++ callback in context0. 9675 calling_context0->Enter(); 9676 Local<v8::FunctionTemplate> callback_templ = 9677 v8::FunctionTemplate::New(GetCallingContextCallback); 9678 calling_context0->Global()->Set(v8_str("callback"), 9679 callback_templ->GetFunction()); 9680 calling_context0->Exit(); 9681 9682 // Expose context0 in context1 and setup a function that calls the 9683 // callback function. 9684 calling_context1->Enter(); 9685 calling_context1->Global()->Set(v8_str("context0"), 9686 calling_context0->Global()); 9687 CompileRun("function f() { context0.callback() }"); 9688 calling_context1->Exit(); 9689 9690 // Expose context1 in context2 and call the callback function in 9691 // context0 indirectly through f in context1. 9692 calling_context2->Enter(); 9693 calling_context2->Global()->Set(v8_str("context1"), 9694 calling_context1->Global()); 9695 CompileRun("context1.f()"); 9696 calling_context2->Exit(); 9697 9698 // Dispose the contexts to allow them to be garbage collected. 9699 calling_context0.Dispose(); 9700 calling_context1.Dispose(); 9701 calling_context2.Dispose(); 9702 calling_context0.Clear(); 9703 calling_context1.Clear(); 9704 calling_context2.Clear(); 9705} 9706 9707 9708// Check that a variable declaration with no explicit initialization 9709// value does not shadow an existing property in the prototype chain. 9710// 9711// This is consistent with Firefox and Safari. 9712// 9713// See http://crbug.com/12548. 9714THREADED_TEST(InitGlobalVarInProtoChain) { 9715 v8::HandleScope scope; 9716 LocalContext context; 9717 // Introduce a variable in the prototype chain. 9718 CompileRun("__proto__.x = 42"); 9719 v8::Handle<v8::Value> result = CompileRun("var x; x"); 9720 CHECK(!result->IsUndefined()); 9721 CHECK_EQ(42, result->Int32Value()); 9722} 9723 9724 9725// Regression test for issue 398. 9726// If a function is added to an object, creating a constant function 9727// field, and the result is cloned, replacing the constant function on the 9728// original should not affect the clone. 9729// See http://code.google.com/p/v8/issues/detail?id=398 9730THREADED_TEST(ReplaceConstantFunction) { 9731 v8::HandleScope scope; 9732 LocalContext context; 9733 v8::Handle<v8::Object> obj = v8::Object::New(); 9734 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 9735 v8::Handle<v8::String> foo_string = v8::String::New("foo"); 9736 obj->Set(foo_string, func_templ->GetFunction()); 9737 v8::Handle<v8::Object> obj_clone = obj->Clone(); 9738 obj_clone->Set(foo_string, v8::String::New("Hello")); 9739 CHECK(!obj->Get(foo_string)->IsUndefined()); 9740} 9741 9742 9743// Regression test for http://crbug.com/16276. 9744THREADED_TEST(Regress16276) { 9745 v8::HandleScope scope; 9746 LocalContext context; 9747 // Force the IC in f to be a dictionary load IC. 9748 CompileRun("function f(obj) { return obj.x; }\n" 9749 "var obj = { x: { foo: 42 }, y: 87 };\n" 9750 "var x = obj.x;\n" 9751 "delete obj.y;\n" 9752 "for (var i = 0; i < 5; i++) f(obj);"); 9753 // Detach the global object to make 'this' refer directly to the 9754 // global object (not the proxy), and make sure that the dictionary 9755 // load IC doesn't mess up loading directly from the global object. 9756 context->DetachGlobal(); 9757 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value()); 9758} 9759 9760 9761THREADED_TEST(PixelArray) { 9762 v8::HandleScope scope; 9763 LocalContext context; 9764 const int kElementCount = 260; 9765 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); 9766 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount, 9767 pixel_data); 9768 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9769 for (int i = 0; i < kElementCount; i++) { 9770 pixels->set(i, i % 256); 9771 } 9772 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9773 for (int i = 0; i < kElementCount; i++) { 9774 CHECK_EQ(i % 256, pixels->get(i)); 9775 CHECK_EQ(i % 256, pixel_data[i]); 9776 } 9777 9778 v8::Handle<v8::Object> obj = v8::Object::New(); 9779 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 9780 // Set the elements to be the pixels. 9781 // jsobj->set_elements(*pixels); 9782 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount); 9783 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 9784 obj->Set(v8_str("field"), v8::Int32::New(1503)); 9785 context->Global()->Set(v8_str("pixels"), obj); 9786 v8::Handle<v8::Value> result = CompileRun("pixels.field"); 9787 CHECK_EQ(1503, result->Int32Value()); 9788 result = CompileRun("pixels[1]"); 9789 CHECK_EQ(1, result->Int32Value()); 9790 9791 result = CompileRun("var sum = 0;" 9792 "for (var i = 0; i < 8; i++) {" 9793 " sum += pixels[i] = pixels[i] = -i;" 9794 "}" 9795 "sum;"); 9796 CHECK_EQ(-28, result->Int32Value()); 9797 9798 result = CompileRun("var sum = 0;" 9799 "for (var i = 0; i < 8; i++) {" 9800 " sum += pixels[i] = pixels[i] = 0;" 9801 "}" 9802 "sum;"); 9803 CHECK_EQ(0, result->Int32Value()); 9804 9805 result = CompileRun("var sum = 0;" 9806 "for (var i = 0; i < 8; i++) {" 9807 " sum += pixels[i] = pixels[i] = 255;" 9808 "}" 9809 "sum;"); 9810 CHECK_EQ(8 * 255, result->Int32Value()); 9811 9812 result = CompileRun("var sum = 0;" 9813 "for (var i = 0; i < 8; i++) {" 9814 " sum += pixels[i] = pixels[i] = 256 + i;" 9815 "}" 9816 "sum;"); 9817 CHECK_EQ(2076, result->Int32Value()); 9818 9819 result = CompileRun("var sum = 0;" 9820 "for (var i = 0; i < 8; i++) {" 9821 " sum += pixels[i] = pixels[i] = i;" 9822 "}" 9823 "sum;"); 9824 CHECK_EQ(28, result->Int32Value()); 9825 9826 result = CompileRun("var sum = 0;" 9827 "for (var i = 0; i < 8; i++) {" 9828 " sum += pixels[i];" 9829 "}" 9830 "sum;"); 9831 CHECK_EQ(28, result->Int32Value()); 9832 9833 i::Handle<i::Smi> value(i::Smi::FromInt(2)); 9834 i::SetElement(jsobj, 1, value); 9835 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 9836 *value.location() = i::Smi::FromInt(256); 9837 i::SetElement(jsobj, 1, value); 9838 CHECK_EQ(255, 9839 i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 9840 *value.location() = i::Smi::FromInt(-1); 9841 i::SetElement(jsobj, 1, value); 9842 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 9843 9844 result = CompileRun("for (var i = 0; i < 8; i++) {" 9845 " pixels[i] = (i * 65) - 109;" 9846 "}" 9847 "pixels[1] + pixels[6];"); 9848 CHECK_EQ(255, result->Int32Value()); 9849 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value()); 9850 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 9851 CHECK_EQ(21, 9852 i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value()); 9853 CHECK_EQ(86, 9854 i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value()); 9855 CHECK_EQ(151, 9856 i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value()); 9857 CHECK_EQ(216, 9858 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 9859 CHECK_EQ(255, 9860 i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value()); 9861 CHECK_EQ(255, 9862 i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value()); 9863 result = CompileRun("var sum = 0;" 9864 "for (var i = 0; i < 8; i++) {" 9865 " sum += pixels[i];" 9866 "}" 9867 "sum;"); 9868 CHECK_EQ(984, result->Int32Value()); 9869 9870 result = CompileRun("for (var i = 0; i < 8; i++) {" 9871 " pixels[i] = (i * 1.1);" 9872 "}" 9873 "pixels[1] + pixels[6];"); 9874 CHECK_EQ(8, result->Int32Value()); 9875 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value()); 9876 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 9877 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value()); 9878 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value()); 9879 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value()); 9880 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 9881 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value()); 9882 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value()); 9883 9884 result = CompileRun("for (var i = 0; i < 8; i++) {" 9885 " pixels[7] = undefined;" 9886 "}" 9887 "pixels[7];"); 9888 CHECK_EQ(0, result->Int32Value()); 9889 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value()); 9890 9891 result = CompileRun("for (var i = 0; i < 8; i++) {" 9892 " pixels[6] = '2.3';" 9893 "}" 9894 "pixels[6];"); 9895 CHECK_EQ(2, result->Int32Value()); 9896 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value()); 9897 9898 result = CompileRun("for (var i = 0; i < 8; i++) {" 9899 " pixels[5] = NaN;" 9900 "}" 9901 "pixels[5];"); 9902 CHECK_EQ(0, result->Int32Value()); 9903 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 9904 9905 result = CompileRun("for (var i = 0; i < 8; i++) {" 9906 " pixels[8] = Infinity;" 9907 "}" 9908 "pixels[8];"); 9909 CHECK_EQ(255, result->Int32Value()); 9910 CHECK_EQ(255, 9911 i::Smi::cast(jsobj->GetElement(8)->ToObjectChecked())->value()); 9912 9913 result = CompileRun("for (var i = 0; i < 8; i++) {" 9914 " pixels[9] = -Infinity;" 9915 "}" 9916 "pixels[9];"); 9917 CHECK_EQ(0, result->Int32Value()); 9918 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9)->ToObjectChecked())->value()); 9919 9920 result = CompileRun("pixels[3] = 33;" 9921 "delete pixels[3];" 9922 "pixels[3];"); 9923 CHECK_EQ(33, result->Int32Value()); 9924 9925 result = CompileRun("pixels[0] = 10; pixels[1] = 11;" 9926 "pixels[2] = 12; pixels[3] = 13;" 9927 "pixels.__defineGetter__('2'," 9928 "function() { return 120; });" 9929 "pixels[2];"); 9930 CHECK_EQ(12, result->Int32Value()); 9931 9932 result = CompileRun("var js_array = new Array(40);" 9933 "js_array[0] = 77;" 9934 "js_array;"); 9935 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 9936 9937 result = CompileRun("pixels[1] = 23;" 9938 "pixels.__proto__ = [];" 9939 "js_array.__proto__ = pixels;" 9940 "js_array.concat(pixels);"); 9941 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 9942 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 9943 9944 result = CompileRun("pixels[1] = 23;"); 9945 CHECK_EQ(23, result->Int32Value()); 9946 9947 // Test for index greater than 255. Regression test for: 9948 // http://code.google.com/p/chromium/issues/detail?id=26337. 9949 result = CompileRun("pixels[256] = 255;"); 9950 CHECK_EQ(255, result->Int32Value()); 9951 result = CompileRun("var i = 0;" 9952 "for (var j = 0; j < 8; j++) { i = pixels[256]; }" 9953 "i"); 9954 CHECK_EQ(255, result->Int32Value()); 9955 9956 free(pixel_data); 9957} 9958 9959 9960THREADED_TEST(PixelArrayInfo) { 9961 v8::HandleScope scope; 9962 LocalContext context; 9963 for (int size = 0; size < 100; size += 10) { 9964 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size)); 9965 v8::Handle<v8::Object> obj = v8::Object::New(); 9966 obj->SetIndexedPropertiesToPixelData(pixel_data, size); 9967 CHECK(obj->HasIndexedPropertiesInPixelData()); 9968 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData()); 9969 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength()); 9970 free(pixel_data); 9971 } 9972} 9973 9974 9975static int ExternalArrayElementSize(v8::ExternalArrayType array_type) { 9976 switch (array_type) { 9977 case v8::kExternalByteArray: 9978 case v8::kExternalUnsignedByteArray: 9979 return 1; 9980 break; 9981 case v8::kExternalShortArray: 9982 case v8::kExternalUnsignedShortArray: 9983 return 2; 9984 break; 9985 case v8::kExternalIntArray: 9986 case v8::kExternalUnsignedIntArray: 9987 case v8::kExternalFloatArray: 9988 return 4; 9989 break; 9990 default: 9991 UNREACHABLE(); 9992 return -1; 9993 } 9994 UNREACHABLE(); 9995 return -1; 9996} 9997 9998 9999template <class ExternalArrayClass, class ElementType> 10000static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, 10001 int64_t low, 10002 int64_t high) { 10003 v8::HandleScope scope; 10004 LocalContext context; 10005 const int kElementCount = 40; 10006 int element_size = ExternalArrayElementSize(array_type); 10007 ElementType* array_data = 10008 static_cast<ElementType*>(malloc(kElementCount * element_size)); 10009 i::Handle<ExternalArrayClass> array = 10010 i::Handle<ExternalArrayClass>::cast( 10011 i::Factory::NewExternalArray(kElementCount, array_type, array_data)); 10012 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 10013 for (int i = 0; i < kElementCount; i++) { 10014 array->set(i, static_cast<ElementType>(i)); 10015 } 10016 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 10017 for (int i = 0; i < kElementCount; i++) { 10018 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i))); 10019 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i])); 10020 } 10021 10022 v8::Handle<v8::Object> obj = v8::Object::New(); 10023 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 10024 // Set the elements to be the external array. 10025 obj->SetIndexedPropertiesToExternalArrayData(array_data, 10026 array_type, 10027 kElementCount); 10028 CHECK_EQ( 10029 1, static_cast<int>(jsobj->GetElement(1)->ToObjectChecked()->Number())); 10030 obj->Set(v8_str("field"), v8::Int32::New(1503)); 10031 context->Global()->Set(v8_str("ext_array"), obj); 10032 v8::Handle<v8::Value> result = CompileRun("ext_array.field"); 10033 CHECK_EQ(1503, result->Int32Value()); 10034 result = CompileRun("ext_array[1]"); 10035 CHECK_EQ(1, result->Int32Value()); 10036 10037 // Check pass through of assigned smis 10038 result = CompileRun("var sum = 0;" 10039 "for (var i = 0; i < 8; i++) {" 10040 " sum += ext_array[i] = ext_array[i] = -i;" 10041 "}" 10042 "sum;"); 10043 CHECK_EQ(-28, result->Int32Value()); 10044 10045 // Check assigned smis 10046 result = CompileRun("for (var i = 0; i < 8; i++) {" 10047 " ext_array[i] = i;" 10048 "}" 10049 "var sum = 0;" 10050 "for (var i = 0; i < 8; i++) {" 10051 " sum += ext_array[i];" 10052 "}" 10053 "sum;"); 10054 CHECK_EQ(28, result->Int32Value()); 10055 10056 // Check assigned smis in reverse order 10057 result = CompileRun("for (var i = 8; --i >= 0; ) {" 10058 " ext_array[i] = i;" 10059 "}" 10060 "var sum = 0;" 10061 "for (var i = 0; i < 8; i++) {" 10062 " sum += ext_array[i];" 10063 "}" 10064 "sum;"); 10065 CHECK_EQ(28, result->Int32Value()); 10066 10067 // Check pass through of assigned HeapNumbers 10068 result = CompileRun("var sum = 0;" 10069 "for (var i = 0; i < 16; i+=2) {" 10070 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);" 10071 "}" 10072 "sum;"); 10073 CHECK_EQ(-28, result->Int32Value()); 10074 10075 // Check assigned HeapNumbers 10076 result = CompileRun("for (var i = 0; i < 16; i+=2) {" 10077 " ext_array[i] = (i * 0.5);" 10078 "}" 10079 "var sum = 0;" 10080 "for (var i = 0; i < 16; i+=2) {" 10081 " sum += ext_array[i];" 10082 "}" 10083 "sum;"); 10084 CHECK_EQ(28, result->Int32Value()); 10085 10086 // Check assigned HeapNumbers in reverse order 10087 result = CompileRun("for (var i = 14; i >= 0; i-=2) {" 10088 " ext_array[i] = (i * 0.5);" 10089 "}" 10090 "var sum = 0;" 10091 "for (var i = 0; i < 16; i+=2) {" 10092 " sum += ext_array[i];" 10093 "}" 10094 "sum;"); 10095 CHECK_EQ(28, result->Int32Value()); 10096 10097 i::ScopedVector<char> test_buf(1024); 10098 10099 // Check legal boundary conditions. 10100 // The repeated loads and stores ensure the ICs are exercised. 10101 const char* boundary_program = 10102 "var res = 0;" 10103 "for (var i = 0; i < 16; i++) {" 10104 " ext_array[i] = %lld;" 10105 " if (i > 8) {" 10106 " res = ext_array[i];" 10107 " }" 10108 "}" 10109 "res;"; 10110 i::OS::SNPrintF(test_buf, 10111 boundary_program, 10112 low); 10113 result = CompileRun(test_buf.start()); 10114 CHECK_EQ(low, result->IntegerValue()); 10115 10116 i::OS::SNPrintF(test_buf, 10117 boundary_program, 10118 high); 10119 result = CompileRun(test_buf.start()); 10120 CHECK_EQ(high, result->IntegerValue()); 10121 10122 // Check misprediction of type in IC. 10123 result = CompileRun("var tmp_array = ext_array;" 10124 "var sum = 0;" 10125 "for (var i = 0; i < 8; i++) {" 10126 " tmp_array[i] = i;" 10127 " sum += tmp_array[i];" 10128 " if (i == 4) {" 10129 " tmp_array = {};" 10130 " }" 10131 "}" 10132 "sum;"); 10133 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 10134 CHECK_EQ(28, result->Int32Value()); 10135 10136 // Make sure out-of-range loads do not throw. 10137 i::OS::SNPrintF(test_buf, 10138 "var caught_exception = false;" 10139 "try {" 10140 " ext_array[%d];" 10141 "} catch (e) {" 10142 " caught_exception = true;" 10143 "}" 10144 "caught_exception;", 10145 kElementCount); 10146 result = CompileRun(test_buf.start()); 10147 CHECK_EQ(false, result->BooleanValue()); 10148 10149 // Make sure out-of-range stores do not throw. 10150 i::OS::SNPrintF(test_buf, 10151 "var caught_exception = false;" 10152 "try {" 10153 " ext_array[%d] = 1;" 10154 "} catch (e) {" 10155 " caught_exception = true;" 10156 "}" 10157 "caught_exception;", 10158 kElementCount); 10159 result = CompileRun(test_buf.start()); 10160 CHECK_EQ(false, result->BooleanValue()); 10161 10162 // Check other boundary conditions, values and operations. 10163 result = CompileRun("for (var i = 0; i < 8; i++) {" 10164 " ext_array[7] = undefined;" 10165 "}" 10166 "ext_array[7];"); 10167 CHECK_EQ(0, result->Int32Value()); 10168 CHECK_EQ( 10169 0, static_cast<int>(jsobj->GetElement(7)->ToObjectChecked()->Number())); 10170 10171 result = CompileRun("for (var i = 0; i < 8; i++) {" 10172 " ext_array[6] = '2.3';" 10173 "}" 10174 "ext_array[6];"); 10175 CHECK_EQ(2, result->Int32Value()); 10176 CHECK_EQ( 10177 2, static_cast<int>(jsobj->GetElement(6)->ToObjectChecked()->Number())); 10178 10179 if (array_type != v8::kExternalFloatArray) { 10180 // Though the specification doesn't state it, be explicit about 10181 // converting NaNs and +/-Infinity to zero. 10182 result = CompileRun("for (var i = 0; i < 8; i++) {" 10183 " ext_array[i] = 5;" 10184 "}" 10185 "for (var i = 0; i < 8; i++) {" 10186 " ext_array[i] = NaN;" 10187 "}" 10188 "ext_array[5];"); 10189 CHECK_EQ(0, result->Int32Value()); 10190 CHECK_EQ(0, 10191 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 10192 10193 result = CompileRun("for (var i = 0; i < 8; i++) {" 10194 " ext_array[i] = 5;" 10195 "}" 10196 "for (var i = 0; i < 8; i++) {" 10197 " ext_array[i] = Infinity;" 10198 "}" 10199 "ext_array[5];"); 10200 CHECK_EQ(0, result->Int32Value()); 10201 CHECK_EQ(0, 10202 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 10203 10204 result = CompileRun("for (var i = 0; i < 8; i++) {" 10205 " ext_array[i] = 5;" 10206 "}" 10207 "for (var i = 0; i < 8; i++) {" 10208 " ext_array[i] = -Infinity;" 10209 "}" 10210 "ext_array[5];"); 10211 CHECK_EQ(0, result->Int32Value()); 10212 CHECK_EQ(0, 10213 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 10214 } 10215 10216 result = CompileRun("ext_array[3] = 33;" 10217 "delete ext_array[3];" 10218 "ext_array[3];"); 10219 CHECK_EQ(33, result->Int32Value()); 10220 10221 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;" 10222 "ext_array[2] = 12; ext_array[3] = 13;" 10223 "ext_array.__defineGetter__('2'," 10224 "function() { return 120; });" 10225 "ext_array[2];"); 10226 CHECK_EQ(12, result->Int32Value()); 10227 10228 result = CompileRun("var js_array = new Array(40);" 10229 "js_array[0] = 77;" 10230 "js_array;"); 10231 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 10232 10233 result = CompileRun("ext_array[1] = 23;" 10234 "ext_array.__proto__ = [];" 10235 "js_array.__proto__ = ext_array;" 10236 "js_array.concat(ext_array);"); 10237 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 10238 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 10239 10240 result = CompileRun("ext_array[1] = 23;"); 10241 CHECK_EQ(23, result->Int32Value()); 10242 10243 // Test more complex manipulations which cause eax to contain values 10244 // that won't be completely overwritten by loads from the arrays. 10245 // This catches bugs in the instructions used for the KeyedLoadIC 10246 // for byte and word types. 10247 { 10248 const int kXSize = 300; 10249 const int kYSize = 300; 10250 const int kLargeElementCount = kXSize * kYSize * 4; 10251 ElementType* large_array_data = 10252 static_cast<ElementType*>(malloc(kLargeElementCount * element_size)); 10253 i::Handle<ExternalArrayClass> large_array = 10254 i::Handle<ExternalArrayClass>::cast( 10255 i::Factory::NewExternalArray(kLargeElementCount, 10256 array_type, 10257 array_data)); 10258 v8::Handle<v8::Object> large_obj = v8::Object::New(); 10259 // Set the elements to be the external array. 10260 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data, 10261 array_type, 10262 kLargeElementCount); 10263 context->Global()->Set(v8_str("large_array"), large_obj); 10264 // Initialize contents of a few rows. 10265 for (int x = 0; x < 300; x++) { 10266 int row = 0; 10267 int offset = row * 300 * 4; 10268 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 10269 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 10270 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 10271 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 10272 row = 150; 10273 offset = row * 300 * 4; 10274 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 10275 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 10276 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 10277 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 10278 row = 298; 10279 offset = row * 300 * 4; 10280 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 10281 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 10282 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 10283 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 10284 } 10285 // The goal of the code below is to make "offset" large enough 10286 // that the computation of the index (which goes into eax) has 10287 // high bits set which will not be overwritten by a byte or short 10288 // load. 10289 result = CompileRun("var failed = false;" 10290 "var offset = 0;" 10291 "for (var i = 0; i < 300; i++) {" 10292 " if (large_array[4 * i] != 127 ||" 10293 " large_array[4 * i + 1] != 0 ||" 10294 " large_array[4 * i + 2] != 0 ||" 10295 " large_array[4 * i + 3] != 127) {" 10296 " failed = true;" 10297 " }" 10298 "}" 10299 "offset = 150 * 300 * 4;" 10300 "for (var i = 0; i < 300; i++) {" 10301 " if (large_array[offset + 4 * i] != 127 ||" 10302 " large_array[offset + 4 * i + 1] != 0 ||" 10303 " large_array[offset + 4 * i + 2] != 0 ||" 10304 " large_array[offset + 4 * i + 3] != 127) {" 10305 " failed = true;" 10306 " }" 10307 "}" 10308 "offset = 298 * 300 * 4;" 10309 "for (var i = 0; i < 300; i++) {" 10310 " if (large_array[offset + 4 * i] != 127 ||" 10311 " large_array[offset + 4 * i + 1] != 0 ||" 10312 " large_array[offset + 4 * i + 2] != 0 ||" 10313 " large_array[offset + 4 * i + 3] != 127) {" 10314 " failed = true;" 10315 " }" 10316 "}" 10317 "!failed;"); 10318 CHECK_EQ(true, result->BooleanValue()); 10319 free(large_array_data); 10320 } 10321 10322 free(array_data); 10323} 10324 10325 10326THREADED_TEST(ExternalByteArray) { 10327 ExternalArrayTestHelper<i::ExternalByteArray, int8_t>( 10328 v8::kExternalByteArray, 10329 -128, 10330 127); 10331} 10332 10333 10334THREADED_TEST(ExternalUnsignedByteArray) { 10335 ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>( 10336 v8::kExternalUnsignedByteArray, 10337 0, 10338 255); 10339} 10340 10341 10342THREADED_TEST(ExternalShortArray) { 10343 ExternalArrayTestHelper<i::ExternalShortArray, int16_t>( 10344 v8::kExternalShortArray, 10345 -32768, 10346 32767); 10347} 10348 10349 10350THREADED_TEST(ExternalUnsignedShortArray) { 10351 ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>( 10352 v8::kExternalUnsignedShortArray, 10353 0, 10354 65535); 10355} 10356 10357 10358THREADED_TEST(ExternalIntArray) { 10359 ExternalArrayTestHelper<i::ExternalIntArray, int32_t>( 10360 v8::kExternalIntArray, 10361 INT_MIN, // -2147483648 10362 INT_MAX); // 2147483647 10363} 10364 10365 10366THREADED_TEST(ExternalUnsignedIntArray) { 10367 ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>( 10368 v8::kExternalUnsignedIntArray, 10369 0, 10370 UINT_MAX); // 4294967295 10371} 10372 10373 10374THREADED_TEST(ExternalFloatArray) { 10375 ExternalArrayTestHelper<i::ExternalFloatArray, float>( 10376 v8::kExternalFloatArray, 10377 -500, 10378 500); 10379} 10380 10381 10382THREADED_TEST(ExternalArrays) { 10383 TestExternalByteArray(); 10384 TestExternalUnsignedByteArray(); 10385 TestExternalShortArray(); 10386 TestExternalUnsignedShortArray(); 10387 TestExternalIntArray(); 10388 TestExternalUnsignedIntArray(); 10389 TestExternalFloatArray(); 10390} 10391 10392 10393void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) { 10394 v8::HandleScope scope; 10395 LocalContext context; 10396 for (int size = 0; size < 100; size += 10) { 10397 int element_size = ExternalArrayElementSize(array_type); 10398 void* external_data = malloc(size * element_size); 10399 v8::Handle<v8::Object> obj = v8::Object::New(); 10400 obj->SetIndexedPropertiesToExternalArrayData( 10401 external_data, array_type, size); 10402 CHECK(obj->HasIndexedPropertiesInExternalArrayData()); 10403 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData()); 10404 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType()); 10405 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength()); 10406 free(external_data); 10407 } 10408} 10409 10410 10411THREADED_TEST(ExternalArrayInfo) { 10412 ExternalArrayInfoTestHelper(v8::kExternalByteArray); 10413 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray); 10414 ExternalArrayInfoTestHelper(v8::kExternalShortArray); 10415 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray); 10416 ExternalArrayInfoTestHelper(v8::kExternalIntArray); 10417 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray); 10418 ExternalArrayInfoTestHelper(v8::kExternalFloatArray); 10419} 10420 10421 10422THREADED_TEST(ScriptContextDependence) { 10423 v8::HandleScope scope; 10424 LocalContext c1; 10425 const char *source = "foo"; 10426 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source)); 10427 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source)); 10428 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100)); 10429 CHECK_EQ(dep->Run()->Int32Value(), 100); 10430 CHECK_EQ(indep->Run()->Int32Value(), 100); 10431 LocalContext c2; 10432 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101)); 10433 CHECK_EQ(dep->Run()->Int32Value(), 100); 10434 CHECK_EQ(indep->Run()->Int32Value(), 101); 10435} 10436 10437 10438THREADED_TEST(StackTrace) { 10439 v8::HandleScope scope; 10440 LocalContext context; 10441 v8::TryCatch try_catch; 10442 const char *source = "function foo() { FAIL.FAIL; }; foo();"; 10443 v8::Handle<v8::String> src = v8::String::New(source); 10444 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test"); 10445 v8::Script::New(src, origin)->Run(); 10446 CHECK(try_catch.HasCaught()); 10447 v8::String::Utf8Value stack(try_catch.StackTrace()); 10448 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL); 10449} 10450 10451 10452// Checks that a StackFrame has certain expected values. 10453void checkStackFrame(const char* expected_script_name, 10454 const char* expected_func_name, int expected_line_number, 10455 int expected_column, bool is_eval, bool is_constructor, 10456 v8::Handle<v8::StackFrame> frame) { 10457 v8::HandleScope scope; 10458 v8::String::Utf8Value func_name(frame->GetFunctionName()); 10459 v8::String::Utf8Value script_name(frame->GetScriptName()); 10460 if (*script_name == NULL) { 10461 // The situation where there is no associated script, like for evals. 10462 CHECK(expected_script_name == NULL); 10463 } else { 10464 CHECK(strstr(*script_name, expected_script_name) != NULL); 10465 } 10466 CHECK(strstr(*func_name, expected_func_name) != NULL); 10467 CHECK_EQ(expected_line_number, frame->GetLineNumber()); 10468 CHECK_EQ(expected_column, frame->GetColumn()); 10469 CHECK_EQ(is_eval, frame->IsEval()); 10470 CHECK_EQ(is_constructor, frame->IsConstructor()); 10471} 10472 10473 10474v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) { 10475 v8::HandleScope scope; 10476 const char* origin = "capture-stack-trace-test"; 10477 const int kOverviewTest = 1; 10478 const int kDetailedTest = 2; 10479 10480 ASSERT(args.Length() == 1); 10481 10482 int testGroup = args[0]->Int32Value(); 10483 if (testGroup == kOverviewTest) { 10484 v8::Handle<v8::StackTrace> stackTrace = 10485 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview); 10486 CHECK_EQ(4, stackTrace->GetFrameCount()); 10487 checkStackFrame(origin, "bar", 2, 10, false, false, 10488 stackTrace->GetFrame(0)); 10489 checkStackFrame(origin, "foo", 6, 3, false, false, 10490 stackTrace->GetFrame(1)); 10491 checkStackFrame(NULL, "", 1, 1, false, false, 10492 stackTrace->GetFrame(2)); 10493 // The last frame is an anonymous function that has the initial call. 10494 checkStackFrame(origin, "", 8, 7, false, false, 10495 stackTrace->GetFrame(3)); 10496 10497 CHECK(stackTrace->AsArray()->IsArray()); 10498 } else if (testGroup == kDetailedTest) { 10499 v8::Handle<v8::StackTrace> stackTrace = 10500 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); 10501 CHECK_EQ(4, stackTrace->GetFrameCount()); 10502 checkStackFrame(origin, "bat", 4, 22, false, false, 10503 stackTrace->GetFrame(0)); 10504 checkStackFrame(origin, "baz", 8, 3, false, true, 10505 stackTrace->GetFrame(1)); 10506#ifdef ENABLE_DEBUGGER_SUPPORT 10507 bool is_eval = true; 10508#else // ENABLE_DEBUGGER_SUPPORT 10509 bool is_eval = false; 10510#endif // ENABLE_DEBUGGER_SUPPORT 10511 10512 checkStackFrame(NULL, "", 1, 1, is_eval, false, 10513 stackTrace->GetFrame(2)); 10514 // The last frame is an anonymous function that has the initial call to foo. 10515 checkStackFrame(origin, "", 10, 1, false, false, 10516 stackTrace->GetFrame(3)); 10517 10518 CHECK(stackTrace->AsArray()->IsArray()); 10519 } 10520 return v8::Undefined(); 10521} 10522 10523 10524// Tests the C++ StackTrace API. 10525THREADED_TEST(CaptureStackTrace) { 10526 v8::HandleScope scope; 10527 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test"); 10528 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10529 templ->Set(v8_str("AnalyzeStackInNativeCode"), 10530 v8::FunctionTemplate::New(AnalyzeStackInNativeCode)); 10531 LocalContext context(0, templ); 10532 10533 // Test getting OVERVIEW information. Should ignore information that is not 10534 // script name, function name, line number, and column offset. 10535 const char *overview_source = 10536 "function bar() {\n" 10537 " var y; AnalyzeStackInNativeCode(1);\n" 10538 "}\n" 10539 "function foo() {\n" 10540 "\n" 10541 " bar();\n" 10542 "}\n" 10543 "var x;eval('new foo();');"; 10544 v8::Handle<v8::String> overview_src = v8::String::New(overview_source); 10545 v8::Handle<Value> overview_result = 10546 v8::Script::New(overview_src, origin)->Run(); 10547 ASSERT(!overview_result.IsEmpty()); 10548 ASSERT(overview_result->IsObject()); 10549 10550 // Test getting DETAILED information. 10551 const char *detailed_source = 10552 "function bat() {AnalyzeStackInNativeCode(2);\n" 10553 "}\n" 10554 "\n" 10555 "function baz() {\n" 10556 " bat();\n" 10557 "}\n" 10558 "eval('new baz();');"; 10559 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source); 10560 // Make the script using a non-zero line and column offset. 10561 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3); 10562 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5); 10563 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset); 10564 v8::Handle<v8::Script> detailed_script( 10565 v8::Script::New(detailed_src, &detailed_origin)); 10566 v8::Handle<Value> detailed_result = detailed_script->Run(); 10567 ASSERT(!detailed_result.IsEmpty()); 10568 ASSERT(detailed_result->IsObject()); 10569} 10570 10571 10572static void StackTraceForUncaughtExceptionListener( 10573 v8::Handle<v8::Message> message, 10574 v8::Handle<Value>) { 10575 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace(); 10576 CHECK_EQ(2, stack_trace->GetFrameCount()); 10577 checkStackFrame("origin", "foo", 2, 3, false, false, 10578 stack_trace->GetFrame(0)); 10579 checkStackFrame("origin", "bar", 5, 3, false, false, 10580 stack_trace->GetFrame(1)); 10581} 10582 10583TEST(CaptureStackTraceForUncaughtException) { 10584 report_count = 0; 10585 v8::HandleScope scope; 10586 LocalContext env; 10587 v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener); 10588 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); 10589 10590 Script::Compile(v8_str("function foo() {\n" 10591 " throw 1;\n" 10592 "};\n" 10593 "function bar() {\n" 10594 " foo();\n" 10595 "};"), 10596 v8_str("origin"))->Run(); 10597 v8::Local<v8::Object> global = env->Global(); 10598 Local<Value> trouble = global->Get(v8_str("bar")); 10599 CHECK(trouble->IsFunction()); 10600 Function::Cast(*trouble)->Call(global, 0, NULL); 10601 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); 10602 v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener); 10603} 10604 10605 10606v8::Handle<Value> AnalyzeStackOfEvalWithSourceURL(const v8::Arguments& args) { 10607 v8::HandleScope scope; 10608 v8::Handle<v8::StackTrace> stackTrace = 10609 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); 10610 CHECK_EQ(5, stackTrace->GetFrameCount()); 10611 v8::Handle<v8::String> url = v8_str("eval_url"); 10612 for (int i = 0; i < 3; i++) { 10613 v8::Handle<v8::String> name = 10614 stackTrace->GetFrame(i)->GetScriptNameOrSourceURL(); 10615 CHECK(!name.IsEmpty()); 10616 CHECK_EQ(url, name); 10617 } 10618 return v8::Undefined(); 10619} 10620 10621 10622TEST(SourceURLInStackTrace) { 10623 v8::HandleScope scope; 10624 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10625 templ->Set(v8_str("AnalyzeStackOfEvalWithSourceURL"), 10626 v8::FunctionTemplate::New(AnalyzeStackOfEvalWithSourceURL)); 10627 LocalContext context(0, templ); 10628 10629 const char *source = 10630 "function outer() {\n" 10631 "function bar() {\n" 10632 " AnalyzeStackOfEvalWithSourceURL();\n" 10633 "}\n" 10634 "function foo() {\n" 10635 "\n" 10636 " bar();\n" 10637 "}\n" 10638 "foo();\n" 10639 "}\n" 10640 "eval('(' + outer +')()//@ sourceURL=eval_url');"; 10641 CHECK(CompileRun(source)->IsUndefined()); 10642} 10643 10644 10645// Test that idle notification can be handled and eventually returns true. 10646THREADED_TEST(IdleNotification) { 10647 bool rv = false; 10648 for (int i = 0; i < 100; i++) { 10649 rv = v8::V8::IdleNotification(); 10650 if (rv) 10651 break; 10652 } 10653 CHECK(rv == true); 10654} 10655 10656 10657static uint32_t* stack_limit; 10658 10659static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) { 10660 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::real_climit()); 10661 return v8::Undefined(); 10662} 10663 10664 10665// Uses the address of a local variable to determine the stack top now. 10666// Given a size, returns an address that is that far from the current 10667// top of stack. 10668static uint32_t* ComputeStackLimit(uint32_t size) { 10669 uint32_t* answer = &size - (size / sizeof(size)); 10670 // If the size is very large and the stack is very near the bottom of 10671 // memory then the calculation above may wrap around and give an address 10672 // that is above the (downwards-growing) stack. In that case we return 10673 // a very low address. 10674 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size)); 10675 return answer; 10676} 10677 10678 10679TEST(SetResourceConstraints) { 10680 static const int K = 1024; 10681 uint32_t* set_limit = ComputeStackLimit(128 * K); 10682 10683 // Set stack limit. 10684 v8::ResourceConstraints constraints; 10685 constraints.set_stack_limit(set_limit); 10686 CHECK(v8::SetResourceConstraints(&constraints)); 10687 10688 // Execute a script. 10689 v8::HandleScope scope; 10690 LocalContext env; 10691 Local<v8::FunctionTemplate> fun_templ = 10692 v8::FunctionTemplate::New(GetStackLimitCallback); 10693 Local<Function> fun = fun_templ->GetFunction(); 10694 env->Global()->Set(v8_str("get_stack_limit"), fun); 10695 CompileRun("get_stack_limit();"); 10696 10697 CHECK(stack_limit == set_limit); 10698} 10699 10700 10701TEST(SetResourceConstraintsInThread) { 10702 uint32_t* set_limit; 10703 { 10704 v8::Locker locker; 10705 static const int K = 1024; 10706 set_limit = ComputeStackLimit(128 * K); 10707 10708 // Set stack limit. 10709 v8::ResourceConstraints constraints; 10710 constraints.set_stack_limit(set_limit); 10711 CHECK(v8::SetResourceConstraints(&constraints)); 10712 10713 // Execute a script. 10714 v8::HandleScope scope; 10715 LocalContext env; 10716 Local<v8::FunctionTemplate> fun_templ = 10717 v8::FunctionTemplate::New(GetStackLimitCallback); 10718 Local<Function> fun = fun_templ->GetFunction(); 10719 env->Global()->Set(v8_str("get_stack_limit"), fun); 10720 CompileRun("get_stack_limit();"); 10721 10722 CHECK(stack_limit == set_limit); 10723 } 10724 { 10725 v8::Locker locker; 10726 CHECK(stack_limit == set_limit); 10727 } 10728} 10729 10730 10731THREADED_TEST(GetHeapStatistics) { 10732 v8::HandleScope scope; 10733 LocalContext c1; 10734 v8::HeapStatistics heap_statistics; 10735 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0); 10736 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0); 10737 v8::V8::GetHeapStatistics(&heap_statistics); 10738 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0); 10739 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0); 10740} 10741 10742 10743static double DoubleFromBits(uint64_t value) { 10744 double target; 10745#ifdef BIG_ENDIAN_FLOATING_POINT 10746 const int kIntSize = 4; 10747 // Somebody swapped the lower and higher half of doubles. 10748 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); 10749 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); 10750#else 10751 memcpy(&target, &value, sizeof(target)); 10752#endif 10753 return target; 10754} 10755 10756 10757static uint64_t DoubleToBits(double value) { 10758 uint64_t target; 10759#ifdef BIG_ENDIAN_FLOATING_POINT 10760 const int kIntSize = 4; 10761 // Somebody swapped the lower and higher half of doubles. 10762 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); 10763 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); 10764#else 10765 memcpy(&target, &value, sizeof(target)); 10766#endif 10767 return target; 10768} 10769 10770 10771static double DoubleToDateTime(double input) { 10772 double date_limit = 864e13; 10773 if (IsNaN(input) || input < -date_limit || input > date_limit) { 10774 return i::OS::nan_value(); 10775 } 10776 return (input < 0) ? -(floor(-input)) : floor(input); 10777} 10778 10779// We don't have a consistent way to write 64-bit constants syntactically, so we 10780// split them into two 32-bit constants and combine them programmatically. 10781static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) { 10782 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits); 10783} 10784 10785 10786THREADED_TEST(QuietSignalingNaNs) { 10787 v8::HandleScope scope; 10788 LocalContext context; 10789 v8::TryCatch try_catch; 10790 10791 // Special double values. 10792 double snan = DoubleFromBits(0x7ff00000, 0x00000001); 10793 double qnan = DoubleFromBits(0x7ff80000, 0x00000000); 10794 double infinity = DoubleFromBits(0x7ff00000, 0x00000000); 10795 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu); 10796 double min_normal = DoubleFromBits(0x00100000, 0x00000000); 10797 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu); 10798 double min_denormal = DoubleFromBits(0x00000000, 0x00000001); 10799 10800 // Date values are capped at +/-100000000 days (times 864e5 ms per day) 10801 // on either side of the epoch. 10802 double date_limit = 864e13; 10803 10804 double test_values[] = { 10805 snan, 10806 qnan, 10807 infinity, 10808 max_normal, 10809 date_limit + 1, 10810 date_limit, 10811 min_normal, 10812 max_denormal, 10813 min_denormal, 10814 0, 10815 -0, 10816 -min_denormal, 10817 -max_denormal, 10818 -min_normal, 10819 -date_limit, 10820 -date_limit - 1, 10821 -max_normal, 10822 -infinity, 10823 -qnan, 10824 -snan 10825 }; 10826 int num_test_values = 20; 10827 10828 for (int i = 0; i < num_test_values; i++) { 10829 double test_value = test_values[i]; 10830 10831 // Check that Number::New preserves non-NaNs and quiets SNaNs. 10832 v8::Handle<v8::Value> number = v8::Number::New(test_value); 10833 double stored_number = number->NumberValue(); 10834 if (!IsNaN(test_value)) { 10835 CHECK_EQ(test_value, stored_number); 10836 } else { 10837 uint64_t stored_bits = DoubleToBits(stored_number); 10838 // Check if quiet nan (bits 51..62 all set). 10839 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 10840 } 10841 10842 // Check that Date::New preserves non-NaNs in the date range and 10843 // quiets SNaNs. 10844 v8::Handle<v8::Value> date = v8::Date::New(test_value); 10845 double expected_stored_date = DoubleToDateTime(test_value); 10846 double stored_date = date->NumberValue(); 10847 if (!IsNaN(expected_stored_date)) { 10848 CHECK_EQ(expected_stored_date, stored_date); 10849 } else { 10850 uint64_t stored_bits = DoubleToBits(stored_date); 10851 // Check if quiet nan (bits 51..62 all set). 10852 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 10853 } 10854 } 10855} 10856 10857 10858static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) { 10859 v8::HandleScope scope; 10860 v8::TryCatch tc; 10861 v8::Handle<v8::String> str = args[0]->ToString(); 10862 if (tc.HasCaught()) 10863 return tc.ReThrow(); 10864 return v8::Undefined(); 10865} 10866 10867 10868// Test that an exception can be propagated down through a spaghetti 10869// stack using ReThrow. 10870THREADED_TEST(SpaghettiStackReThrow) { 10871 v8::HandleScope scope; 10872 LocalContext context; 10873 context->Global()->Set( 10874 v8::String::New("s"), 10875 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction()); 10876 v8::TryCatch try_catch; 10877 CompileRun( 10878 "var i = 0;" 10879 "var o = {" 10880 " toString: function () {" 10881 " if (i == 10) {" 10882 " throw 'Hey!';" 10883 " } else {" 10884 " i++;" 10885 " return s(o);" 10886 " }" 10887 " }" 10888 "};" 10889 "s(o);"); 10890 CHECK(try_catch.HasCaught()); 10891 v8::String::Utf8Value value(try_catch.Exception()); 10892 CHECK_EQ(0, strcmp(*value, "Hey!")); 10893} 10894 10895 10896TEST(Regress528) { 10897 v8::V8::Initialize(); 10898 10899 v8::HandleScope scope; 10900 v8::Persistent<Context> context; 10901 v8::Persistent<Context> other_context; 10902 int gc_count; 10903 10904 // Create a context used to keep the code from aging in the compilation 10905 // cache. 10906 other_context = Context::New(); 10907 10908 // Context-dependent context data creates reference from the compilation 10909 // cache to the global object. 10910 const char* source_simple = "1"; 10911 context = Context::New(); 10912 { 10913 v8::HandleScope scope; 10914 10915 context->Enter(); 10916 Local<v8::String> obj = v8::String::New(""); 10917 context->SetData(obj); 10918 CompileRun(source_simple); 10919 context->Exit(); 10920 } 10921 context.Dispose(); 10922 for (gc_count = 1; gc_count < 10; gc_count++) { 10923 other_context->Enter(); 10924 CompileRun(source_simple); 10925 other_context->Exit(); 10926 i::Heap::CollectAllGarbage(false); 10927 if (GetGlobalObjectsCount() == 1) break; 10928 } 10929 CHECK_GE(2, gc_count); 10930 CHECK_EQ(1, GetGlobalObjectsCount()); 10931 10932 // Eval in a function creates reference from the compilation cache to the 10933 // global object. 10934 const char* source_eval = "function f(){eval('1')}; f()"; 10935 context = Context::New(); 10936 { 10937 v8::HandleScope scope; 10938 10939 context->Enter(); 10940 CompileRun(source_eval); 10941 context->Exit(); 10942 } 10943 context.Dispose(); 10944 for (gc_count = 1; gc_count < 10; gc_count++) { 10945 other_context->Enter(); 10946 CompileRun(source_eval); 10947 other_context->Exit(); 10948 i::Heap::CollectAllGarbage(false); 10949 if (GetGlobalObjectsCount() == 1) break; 10950 } 10951 CHECK_GE(2, gc_count); 10952 CHECK_EQ(1, GetGlobalObjectsCount()); 10953 10954 // Looking up the line number for an exception creates reference from the 10955 // compilation cache to the global object. 10956 const char* source_exception = "function f(){throw 1;} f()"; 10957 context = Context::New(); 10958 { 10959 v8::HandleScope scope; 10960 10961 context->Enter(); 10962 v8::TryCatch try_catch; 10963 CompileRun(source_exception); 10964 CHECK(try_catch.HasCaught()); 10965 v8::Handle<v8::Message> message = try_catch.Message(); 10966 CHECK(!message.IsEmpty()); 10967 CHECK_EQ(1, message->GetLineNumber()); 10968 context->Exit(); 10969 } 10970 context.Dispose(); 10971 for (gc_count = 1; gc_count < 10; gc_count++) { 10972 other_context->Enter(); 10973 CompileRun(source_exception); 10974 other_context->Exit(); 10975 i::Heap::CollectAllGarbage(false); 10976 if (GetGlobalObjectsCount() == 1) break; 10977 } 10978 CHECK_GE(2, gc_count); 10979 CHECK_EQ(1, GetGlobalObjectsCount()); 10980 10981 other_context.Dispose(); 10982} 10983 10984 10985THREADED_TEST(ScriptOrigin) { 10986 v8::HandleScope scope; 10987 LocalContext env; 10988 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 10989 v8::Handle<v8::String> script = v8::String::New( 10990 "function f() {}\n\nfunction g() {}"); 10991 v8::Script::Compile(script, &origin)->Run(); 10992 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 10993 env->Global()->Get(v8::String::New("f"))); 10994 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 10995 env->Global()->Get(v8::String::New("g"))); 10996 10997 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin(); 10998 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName())); 10999 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value()); 11000 11001 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin(); 11002 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName())); 11003 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value()); 11004} 11005 11006 11007THREADED_TEST(ScriptLineNumber) { 11008 v8::HandleScope scope; 11009 LocalContext env; 11010 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 11011 v8::Handle<v8::String> script = v8::String::New( 11012 "function f() {}\n\nfunction g() {}"); 11013 v8::Script::Compile(script, &origin)->Run(); 11014 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 11015 env->Global()->Get(v8::String::New("f"))); 11016 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 11017 env->Global()->Get(v8::String::New("g"))); 11018 CHECK_EQ(0, f->GetScriptLineNumber()); 11019 CHECK_EQ(2, g->GetScriptLineNumber()); 11020} 11021 11022 11023static v8::Handle<Value> GetterWhichReturns42(Local<String> name, 11024 const AccessorInfo& info) { 11025 return v8_num(42); 11026} 11027 11028 11029static void SetterWhichSetsYOnThisTo23(Local<String> name, 11030 Local<Value> value, 11031 const AccessorInfo& info) { 11032 info.This()->Set(v8_str("y"), v8_num(23)); 11033} 11034 11035 11036TEST(SetterOnConstructorPrototype) { 11037 v8::HandleScope scope; 11038 Local<ObjectTemplate> templ = ObjectTemplate::New(); 11039 templ->SetAccessor(v8_str("x"), 11040 GetterWhichReturns42, 11041 SetterWhichSetsYOnThisTo23); 11042 LocalContext context; 11043 context->Global()->Set(v8_str("P"), templ->NewInstance()); 11044 CompileRun("function C1() {" 11045 " this.x = 23;" 11046 "};" 11047 "C1.prototype = P;" 11048 "function C2() {" 11049 " this.x = 23" 11050 "};" 11051 "C2.prototype = { };" 11052 "C2.prototype.__proto__ = P;"); 11053 11054 v8::Local<v8::Script> script; 11055 script = v8::Script::Compile(v8_str("new C1();")); 11056 for (int i = 0; i < 10; i++) { 11057 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 11058 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); 11059 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); 11060 } 11061 11062 script = v8::Script::Compile(v8_str("new C2();")); 11063 for (int i = 0; i < 10; i++) { 11064 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 11065 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value()); 11066 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value()); 11067 } 11068} 11069 11070 11071static v8::Handle<Value> NamedPropertyGetterWhichReturns42( 11072 Local<String> name, const AccessorInfo& info) { 11073 return v8_num(42); 11074} 11075 11076 11077static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23( 11078 Local<String> name, Local<Value> value, const AccessorInfo& info) { 11079 if (name->Equals(v8_str("x"))) { 11080 info.This()->Set(v8_str("y"), v8_num(23)); 11081 } 11082 return v8::Handle<Value>(); 11083} 11084 11085 11086THREADED_TEST(InterceptorOnConstructorPrototype) { 11087 v8::HandleScope scope; 11088 Local<ObjectTemplate> templ = ObjectTemplate::New(); 11089 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42, 11090 NamedPropertySetterWhichSetsYOnThisTo23); 11091 LocalContext context; 11092 context->Global()->Set(v8_str("P"), templ->NewInstance()); 11093 CompileRun("function C1() {" 11094 " this.x = 23;" 11095 "};" 11096 "C1.prototype = P;" 11097 "function C2() {" 11098 " this.x = 23" 11099 "};" 11100 "C2.prototype = { };" 11101 "C2.prototype.__proto__ = P;"); 11102 11103 v8::Local<v8::Script> script; 11104 script = v8::Script::Compile(v8_str("new C1();")); 11105 for (int i = 0; i < 10; i++) { 11106 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 11107 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); 11108 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); 11109 } 11110 11111 script = v8::Script::Compile(v8_str("new C2();")); 11112 for (int i = 0; i < 10; i++) { 11113 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 11114 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value()); 11115 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value()); 11116 } 11117} 11118 11119 11120TEST(Bug618) { 11121 const char* source = "function C1() {" 11122 " this.x = 23;" 11123 "};" 11124 "C1.prototype = P;"; 11125 11126 v8::HandleScope scope; 11127 LocalContext context; 11128 v8::Local<v8::Script> script; 11129 11130 // Use a simple object as prototype. 11131 v8::Local<v8::Object> prototype = v8::Object::New(); 11132 prototype->Set(v8_str("y"), v8_num(42)); 11133 context->Global()->Set(v8_str("P"), prototype); 11134 11135 // This compile will add the code to the compilation cache. 11136 CompileRun(source); 11137 11138 script = v8::Script::Compile(v8_str("new C1();")); 11139 // Allow enough iterations for the inobject slack tracking logic 11140 // to finalize instance size and install the fast construct stub. 11141 for (int i = 0; i < 256; i++) { 11142 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 11143 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); 11144 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); 11145 } 11146 11147 // Use an API object with accessors as prototype. 11148 Local<ObjectTemplate> templ = ObjectTemplate::New(); 11149 templ->SetAccessor(v8_str("x"), 11150 GetterWhichReturns42, 11151 SetterWhichSetsYOnThisTo23); 11152 context->Global()->Set(v8_str("P"), templ->NewInstance()); 11153 11154 // This compile will get the code from the compilation cache. 11155 CompileRun(source); 11156 11157 script = v8::Script::Compile(v8_str("new C1();")); 11158 for (int i = 0; i < 10; i++) { 11159 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 11160 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); 11161 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); 11162 } 11163} 11164 11165int prologue_call_count = 0; 11166int epilogue_call_count = 0; 11167int prologue_call_count_second = 0; 11168int epilogue_call_count_second = 0; 11169 11170void PrologueCallback(v8::GCType, v8::GCCallbackFlags) { 11171 ++prologue_call_count; 11172} 11173 11174void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) { 11175 ++epilogue_call_count; 11176} 11177 11178void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { 11179 ++prologue_call_count_second; 11180} 11181 11182void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { 11183 ++epilogue_call_count_second; 11184} 11185 11186TEST(GCCallbacks) { 11187 LocalContext context; 11188 11189 v8::V8::AddGCPrologueCallback(PrologueCallback); 11190 v8::V8::AddGCEpilogueCallback(EpilogueCallback); 11191 CHECK_EQ(0, prologue_call_count); 11192 CHECK_EQ(0, epilogue_call_count); 11193 i::Heap::CollectAllGarbage(false); 11194 CHECK_EQ(1, prologue_call_count); 11195 CHECK_EQ(1, epilogue_call_count); 11196 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond); 11197 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond); 11198 i::Heap::CollectAllGarbage(false); 11199 CHECK_EQ(2, prologue_call_count); 11200 CHECK_EQ(2, epilogue_call_count); 11201 CHECK_EQ(1, prologue_call_count_second); 11202 CHECK_EQ(1, epilogue_call_count_second); 11203 v8::V8::RemoveGCPrologueCallback(PrologueCallback); 11204 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback); 11205 i::Heap::CollectAllGarbage(false); 11206 CHECK_EQ(2, prologue_call_count); 11207 CHECK_EQ(2, epilogue_call_count); 11208 CHECK_EQ(2, prologue_call_count_second); 11209 CHECK_EQ(2, epilogue_call_count_second); 11210 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond); 11211 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond); 11212 i::Heap::CollectAllGarbage(false); 11213 CHECK_EQ(2, prologue_call_count); 11214 CHECK_EQ(2, epilogue_call_count); 11215 CHECK_EQ(2, prologue_call_count_second); 11216 CHECK_EQ(2, epilogue_call_count_second); 11217} 11218 11219 11220THREADED_TEST(AddToJSFunctionResultCache) { 11221 i::FLAG_allow_natives_syntax = true; 11222 v8::HandleScope scope; 11223 11224 LocalContext context; 11225 11226 const char* code = 11227 "(function() {" 11228 " var key0 = 'a';" 11229 " var key1 = 'b';" 11230 " var r0 = %_GetFromCache(0, key0);" 11231 " var r1 = %_GetFromCache(0, key1);" 11232 " var r0_ = %_GetFromCache(0, key0);" 11233 " if (r0 !== r0_)" 11234 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;" 11235 " var r1_ = %_GetFromCache(0, key1);" 11236 " if (r1 !== r1_)" 11237 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;" 11238 " return 'PASSED';" 11239 "})()"; 11240 i::Heap::ClearJSFunctionResultCaches(); 11241 ExpectString(code, "PASSED"); 11242} 11243 11244 11245static const int k0CacheSize = 16; 11246 11247THREADED_TEST(FillJSFunctionResultCache) { 11248 i::FLAG_allow_natives_syntax = true; 11249 v8::HandleScope scope; 11250 11251 LocalContext context; 11252 11253 const char* code = 11254 "(function() {" 11255 " var k = 'a';" 11256 " var r = %_GetFromCache(0, k);" 11257 " for (var i = 0; i < 16; i++) {" 11258 " %_GetFromCache(0, 'a' + i);" 11259 " };" 11260 " if (r === %_GetFromCache(0, k))" 11261 " return 'FAILED: k0CacheSize is too small';" 11262 " return 'PASSED';" 11263 "})()"; 11264 i::Heap::ClearJSFunctionResultCaches(); 11265 ExpectString(code, "PASSED"); 11266} 11267 11268 11269THREADED_TEST(RoundRobinGetFromCache) { 11270 i::FLAG_allow_natives_syntax = true; 11271 v8::HandleScope scope; 11272 11273 LocalContext context; 11274 11275 const char* code = 11276 "(function() {" 11277 " var keys = [];" 11278 " for (var i = 0; i < 16; i++) keys.push(i);" 11279 " var values = [];" 11280 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" 11281 " for (var i = 0; i < 16; i++) {" 11282 " var v = %_GetFromCache(0, keys[i]);" 11283 " if (v !== values[i])" 11284 " return 'Wrong value for ' + " 11285 " keys[i] + ': ' + v + ' vs. ' + values[i];" 11286 " };" 11287 " return 'PASSED';" 11288 "})()"; 11289 i::Heap::ClearJSFunctionResultCaches(); 11290 ExpectString(code, "PASSED"); 11291} 11292 11293 11294THREADED_TEST(ReverseGetFromCache) { 11295 i::FLAG_allow_natives_syntax = true; 11296 v8::HandleScope scope; 11297 11298 LocalContext context; 11299 11300 const char* code = 11301 "(function() {" 11302 " var keys = [];" 11303 " for (var i = 0; i < 16; i++) keys.push(i);" 11304 " var values = [];" 11305 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" 11306 " for (var i = 15; i >= 16; i--) {" 11307 " var v = %_GetFromCache(0, keys[i]);" 11308 " if (v !== values[i])" 11309 " return 'Wrong value for ' + " 11310 " keys[i] + ': ' + v + ' vs. ' + values[i];" 11311 " };" 11312 " return 'PASSED';" 11313 "})()"; 11314 i::Heap::ClearJSFunctionResultCaches(); 11315 ExpectString(code, "PASSED"); 11316} 11317 11318 11319THREADED_TEST(TestEviction) { 11320 i::FLAG_allow_natives_syntax = true; 11321 v8::HandleScope scope; 11322 11323 LocalContext context; 11324 11325 const char* code = 11326 "(function() {" 11327 " for (var i = 0; i < 2*16; i++) {" 11328 " %_GetFromCache(0, 'a' + i);" 11329 " };" 11330 " return 'PASSED';" 11331 "})()"; 11332 i::Heap::ClearJSFunctionResultCaches(); 11333 ExpectString(code, "PASSED"); 11334} 11335 11336 11337THREADED_TEST(TwoByteStringInAsciiCons) { 11338 // See Chromium issue 47824. 11339 v8::HandleScope scope; 11340 11341 LocalContext context; 11342 const char* init_code = 11343 "var str1 = 'abelspendabel';" 11344 "var str2 = str1 + str1 + str1;" 11345 "str2;"; 11346 Local<Value> result = CompileRun(init_code); 11347 11348 CHECK(result->IsString()); 11349 i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result)); 11350 int length = string->length(); 11351 CHECK(string->IsAsciiRepresentation()); 11352 11353 FlattenString(string); 11354 i::Handle<i::String> flat_string = FlattenGetString(string); 11355 11356 CHECK(string->IsAsciiRepresentation()); 11357 CHECK(flat_string->IsAsciiRepresentation()); 11358 11359 // Create external resource. 11360 uint16_t* uc16_buffer = new uint16_t[length + 1]; 11361 11362 i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length); 11363 uc16_buffer[length] = 0; 11364 11365 TestResource resource(uc16_buffer); 11366 11367 flat_string->MakeExternal(&resource); 11368 11369 CHECK(flat_string->IsTwoByteRepresentation()); 11370 11371 // At this point, we should have a Cons string which is flat and ASCII, 11372 // with a first half that is a two-byte string (although it only contains 11373 // ASCII characters). This is a valid sequence of steps, and it can happen 11374 // in real pages. 11375 11376 CHECK(string->IsAsciiRepresentation()); 11377 i::ConsString* cons = i::ConsString::cast(*string); 11378 CHECK_EQ(0, cons->second()->length()); 11379 CHECK(cons->first()->IsTwoByteRepresentation()); 11380 11381 // Check that some string operations work. 11382 11383 // Atom RegExp. 11384 Local<Value> reresult = CompileRun("str2.match(/abel/g).length;"); 11385 CHECK_EQ(6, reresult->Int32Value()); 11386 11387 // Nonatom RegExp. 11388 reresult = CompileRun("str2.match(/abe./g).length;"); 11389 CHECK_EQ(6, reresult->Int32Value()); 11390 11391 reresult = CompileRun("str2.search(/bel/g);"); 11392 CHECK_EQ(1, reresult->Int32Value()); 11393 11394 reresult = CompileRun("str2.search(/be./g);"); 11395 CHECK_EQ(1, reresult->Int32Value()); 11396 11397 ExpectTrue("/bel/g.test(str2);"); 11398 11399 ExpectTrue("/be./g.test(str2);"); 11400 11401 reresult = CompileRun("/bel/g.exec(str2);"); 11402 CHECK(!reresult->IsNull()); 11403 11404 reresult = CompileRun("/be./g.exec(str2);"); 11405 CHECK(!reresult->IsNull()); 11406 11407 ExpectString("str2.substring(2, 10);", "elspenda"); 11408 11409 ExpectString("str2.substring(2, 20);", "elspendabelabelspe"); 11410 11411 ExpectString("str2.charAt(2);", "e"); 11412 11413 reresult = CompileRun("str2.charCodeAt(2);"); 11414 CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value()); 11415} 11416 11417 11418// Failed access check callback that performs a GC on each invocation. 11419void FailedAccessCheckCallbackGC(Local<v8::Object> target, 11420 v8::AccessType type, 11421 Local<v8::Value> data) { 11422 i::Heap::CollectAllGarbage(true); 11423} 11424 11425 11426TEST(GCInFailedAccessCheckCallback) { 11427 // Install a failed access check callback that performs a GC on each 11428 // invocation. Then force the callback to be called from va 11429 11430 v8::V8::Initialize(); 11431 v8::V8::SetFailedAccessCheckCallbackFunction(&FailedAccessCheckCallbackGC); 11432 11433 v8::HandleScope scope; 11434 11435 // Create an ObjectTemplate for global objects and install access 11436 // check callbacks that will block access. 11437 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 11438 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker, 11439 IndexedGetAccessBlocker, 11440 v8::Handle<v8::Value>(), 11441 false); 11442 11443 // Create a context and set an x property on it's global object. 11444 LocalContext context0(NULL, global_template); 11445 context0->Global()->Set(v8_str("x"), v8_num(42)); 11446 v8::Handle<v8::Object> global0 = context0->Global(); 11447 11448 // Create a context with a different security token so that the 11449 // failed access check callback will be called on each access. 11450 LocalContext context1(NULL, global_template); 11451 context1->Global()->Set(v8_str("other"), global0); 11452 11453 // Get property with failed access check. 11454 ExpectUndefined("other.x"); 11455 11456 // Get element with failed access check. 11457 ExpectUndefined("other[0]"); 11458 11459 // Set property with failed access check. 11460 v8::Handle<v8::Value> result = CompileRun("other.x = new Object()"); 11461 CHECK(result->IsObject()); 11462 11463 // Set element with failed access check. 11464 result = CompileRun("other[0] = new Object()"); 11465 CHECK(result->IsObject()); 11466 11467 // Get property attribute with failed access check. 11468 ExpectFalse("\'x\' in other"); 11469 11470 // Get property attribute for element with failed access check. 11471 ExpectFalse("0 in other"); 11472 11473 // Delete property. 11474 ExpectFalse("delete other.x"); 11475 11476 // Delete element. 11477 CHECK_EQ(false, global0->Delete(0)); 11478 11479 // DefineAccessor. 11480 CHECK_EQ(false, 11481 global0->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("x"))); 11482 11483 // Define JavaScript accessor. 11484 ExpectUndefined("Object.prototype.__defineGetter__.call(" 11485 " other, \'x\', function() { return 42; })"); 11486 11487 // LookupAccessor. 11488 ExpectUndefined("Object.prototype.__lookupGetter__.call(" 11489 " other, \'x\')"); 11490 11491 // HasLocalElement. 11492 ExpectFalse("Object.prototype.hasOwnProperty.call(other, \'0\')"); 11493 11494 CHECK_EQ(false, global0->HasRealIndexedProperty(0)); 11495 CHECK_EQ(false, global0->HasRealNamedProperty(v8_str("x"))); 11496 CHECK_EQ(false, global0->HasRealNamedCallbackProperty(v8_str("x"))); 11497 11498 // Reset the failed access check callback so it does not influence 11499 // the other tests. 11500 v8::V8::SetFailedAccessCheckCallbackFunction(NULL); 11501} 11502 11503 11504TEST(StringCheckMultipleContexts) { 11505 const char* code = 11506 "(function() { return \"a\".charAt(0); })()"; 11507 11508 { 11509 // Run the code twice in the first context to initialize the call IC. 11510 v8::HandleScope scope; 11511 LocalContext context1; 11512 ExpectString(code, "a"); 11513 ExpectString(code, "a"); 11514 } 11515 11516 { 11517 // Change the String.prototype in the second context and check 11518 // that the right function gets called. 11519 v8::HandleScope scope; 11520 LocalContext context2; 11521 CompileRun("String.prototype.charAt = function() { return \"not a\"; }"); 11522 ExpectString(code, "not a"); 11523 } 11524} 11525 11526 11527TEST(NumberCheckMultipleContexts) { 11528 const char* code = 11529 "(function() { return (42).toString(); })()"; 11530 11531 { 11532 // Run the code twice in the first context to initialize the call IC. 11533 v8::HandleScope scope; 11534 LocalContext context1; 11535 ExpectString(code, "42"); 11536 ExpectString(code, "42"); 11537 } 11538 11539 { 11540 // Change the Number.prototype in the second context and check 11541 // that the right function gets called. 11542 v8::HandleScope scope; 11543 LocalContext context2; 11544 CompileRun("Number.prototype.toString = function() { return \"not 42\"; }"); 11545 ExpectString(code, "not 42"); 11546 } 11547} 11548 11549 11550TEST(BooleanCheckMultipleContexts) { 11551 const char* code = 11552 "(function() { return true.toString(); })()"; 11553 11554 { 11555 // Run the code twice in the first context to initialize the call IC. 11556 v8::HandleScope scope; 11557 LocalContext context1; 11558 ExpectString(code, "true"); 11559 ExpectString(code, "true"); 11560 } 11561 11562 { 11563 // Change the Boolean.prototype in the second context and check 11564 // that the right function gets called. 11565 v8::HandleScope scope; 11566 LocalContext context2; 11567 CompileRun("Boolean.prototype.toString = function() { return \"\"; }"); 11568 ExpectString(code, ""); 11569 } 11570} 11571 11572 11573TEST(DontDeleteCellLoadIC) { 11574 const char* function_code = 11575 "function readCell() { while (true) { return cell; } }"; 11576 11577 { 11578 // Run the code twice in the first context to initialize the load 11579 // IC for a don't delete cell. 11580 v8::HandleScope scope; 11581 LocalContext context1; 11582 CompileRun("var cell = \"first\";"); 11583 ExpectBoolean("delete cell", false); 11584 CompileRun(function_code); 11585 ExpectString("readCell()", "first"); 11586 ExpectString("readCell()", "first"); 11587 } 11588 11589 { 11590 // Use a deletable cell in the second context. 11591 v8::HandleScope scope; 11592 LocalContext context2; 11593 CompileRun("cell = \"second\";"); 11594 CompileRun(function_code); 11595 ExpectString("readCell()", "second"); 11596 ExpectBoolean("delete cell", true); 11597 ExpectString("(function() {" 11598 " try {" 11599 " return readCell();" 11600 " } catch(e) {" 11601 " return e.toString();" 11602 " }" 11603 "})()", 11604 "ReferenceError: cell is not defined"); 11605 CompileRun("cell = \"new_second\";"); 11606 i::Heap::CollectAllGarbage(true); 11607 ExpectString("readCell()", "new_second"); 11608 ExpectString("readCell()", "new_second"); 11609 } 11610} 11611 11612 11613TEST(DontDeleteCellLoadICForceDelete) { 11614 const char* function_code = 11615 "function readCell() { while (true) { return cell; } }"; 11616 11617 // Run the code twice to initialize the load IC for a don't delete 11618 // cell. 11619 v8::HandleScope scope; 11620 LocalContext context; 11621 CompileRun("var cell = \"value\";"); 11622 ExpectBoolean("delete cell", false); 11623 CompileRun(function_code); 11624 ExpectString("readCell()", "value"); 11625 ExpectString("readCell()", "value"); 11626 11627 // Delete the cell using the API and check the inlined code works 11628 // correctly. 11629 CHECK(context->Global()->ForceDelete(v8_str("cell"))); 11630 ExpectString("(function() {" 11631 " try {" 11632 " return readCell();" 11633 " } catch(e) {" 11634 " return e.toString();" 11635 " }" 11636 "})()", 11637 "ReferenceError: cell is not defined"); 11638} 11639 11640 11641TEST(DontDeleteCellLoadICAPI) { 11642 const char* function_code = 11643 "function readCell() { while (true) { return cell; } }"; 11644 11645 // Run the code twice to initialize the load IC for a don't delete 11646 // cell created using the API. 11647 v8::HandleScope scope; 11648 LocalContext context; 11649 context->Global()->Set(v8_str("cell"), v8_str("value"), v8::DontDelete); 11650 ExpectBoolean("delete cell", false); 11651 CompileRun(function_code); 11652 ExpectString("readCell()", "value"); 11653 ExpectString("readCell()", "value"); 11654 11655 // Delete the cell using the API and check the inlined code works 11656 // correctly. 11657 CHECK(context->Global()->ForceDelete(v8_str("cell"))); 11658 ExpectString("(function() {" 11659 " try {" 11660 " return readCell();" 11661 " } catch(e) {" 11662 " return e.toString();" 11663 " }" 11664 "})()", 11665 "ReferenceError: cell is not defined"); 11666} 11667 11668 11669TEST(GlobalLoadICGC) { 11670 const char* function_code = 11671 "function readCell() { while (true) { return cell; } }"; 11672 11673 // Check inline load code for a don't delete cell is cleared during 11674 // GC. 11675 { 11676 v8::HandleScope scope; 11677 LocalContext context; 11678 CompileRun("var cell = \"value\";"); 11679 ExpectBoolean("delete cell", false); 11680 CompileRun(function_code); 11681 ExpectString("readCell()", "value"); 11682 ExpectString("readCell()", "value"); 11683 } 11684 { 11685 v8::HandleScope scope; 11686 LocalContext context2; 11687 // Hold the code object in the second context. 11688 CompileRun(function_code); 11689 CheckSurvivingGlobalObjectsCount(1); 11690 } 11691 11692 // Check inline load code for a deletable cell is cleared during GC. 11693 { 11694 v8::HandleScope scope; 11695 LocalContext context; 11696 CompileRun("cell = \"value\";"); 11697 CompileRun(function_code); 11698 ExpectString("readCell()", "value"); 11699 ExpectString("readCell()", "value"); 11700 } 11701 { 11702 v8::HandleScope scope; 11703 LocalContext context2; 11704 // Hold the code object in the second context. 11705 CompileRun(function_code); 11706 CheckSurvivingGlobalObjectsCount(1); 11707 } 11708} 11709 11710 11711TEST(RegExp) { 11712 v8::HandleScope scope; 11713 LocalContext context; 11714 11715 v8::Handle<v8::RegExp> re = v8::RegExp::New(v8_str("foo"), v8::RegExp::kNone); 11716 CHECK(re->IsRegExp()); 11717 CHECK(re->GetSource()->Equals(v8_str("foo"))); 11718 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone); 11719 11720 re = v8::RegExp::New(v8_str("bar"), 11721 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | 11722 v8::RegExp::kGlobal)); 11723 CHECK(re->IsRegExp()); 11724 CHECK(re->GetSource()->Equals(v8_str("bar"))); 11725 CHECK_EQ(static_cast<int>(re->GetFlags()), 11726 v8::RegExp::kIgnoreCase | v8::RegExp::kGlobal); 11727 11728 re = v8::RegExp::New(v8_str("baz"), 11729 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | 11730 v8::RegExp::kMultiline)); 11731 CHECK(re->IsRegExp()); 11732 CHECK(re->GetSource()->Equals(v8_str("baz"))); 11733 CHECK_EQ(static_cast<int>(re->GetFlags()), 11734 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline); 11735 11736 re = CompileRun("/quux/").As<v8::RegExp>(); 11737 CHECK(re->IsRegExp()); 11738 CHECK(re->GetSource()->Equals(v8_str("quux"))); 11739 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone); 11740 11741 re = CompileRun("/quux/gm").As<v8::RegExp>(); 11742 CHECK(re->IsRegExp()); 11743 CHECK(re->GetSource()->Equals(v8_str("quux"))); 11744 CHECK_EQ(static_cast<int>(re->GetFlags()), 11745 v8::RegExp::kGlobal | v8::RegExp::kMultiline); 11746 11747 // Override the RegExp constructor and check the API constructor 11748 // still works. 11749 CompileRun("RegExp = function() {}"); 11750 11751 re = v8::RegExp::New(v8_str("foobar"), v8::RegExp::kNone); 11752 CHECK(re->IsRegExp()); 11753 CHECK(re->GetSource()->Equals(v8_str("foobar"))); 11754 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone); 11755 11756 re = v8::RegExp::New(v8_str("foobarbaz"), 11757 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | 11758 v8::RegExp::kMultiline)); 11759 CHECK(re->IsRegExp()); 11760 CHECK(re->GetSource()->Equals(v8_str("foobarbaz"))); 11761 CHECK_EQ(static_cast<int>(re->GetFlags()), 11762 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline); 11763 11764 context->Global()->Set(v8_str("re"), re); 11765 ExpectTrue("re.test('FoobarbaZ')"); 11766 11767 v8::TryCatch try_catch; 11768 re = v8::RegExp::New(v8_str("foo["), v8::RegExp::kNone); 11769 CHECK(re.IsEmpty()); 11770 CHECK(try_catch.HasCaught()); 11771 context->Global()->Set(v8_str("ex"), try_catch.Exception()); 11772 ExpectTrue("ex instanceof SyntaxError"); 11773} 11774 11775 11776static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, 11777 const v8::AccessorInfo& info ) { 11778 return v8_str("42!"); 11779} 11780 11781 11782static v8::Handle<v8::Array> Enumerator(const v8::AccessorInfo& info) { 11783 v8::Handle<v8::Array> result = v8::Array::New(); 11784 result->Set(0, v8_str("universalAnswer")); 11785 return result; 11786} 11787 11788 11789TEST(NamedEnumeratorAndForIn) { 11790 v8::HandleScope handle_scope; 11791 LocalContext context; 11792 v8::Context::Scope context_scope(context.local()); 11793 11794 v8::Handle<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New(); 11795 tmpl->SetNamedPropertyHandler(Getter, NULL, NULL, NULL, Enumerator); 11796 context->Global()->Set(v8_str("o"), tmpl->NewInstance()); 11797 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 11798 "var result = []; for (var k in o) result.push(k); result")); 11799 CHECK_EQ(1, result->Length()); 11800 CHECK_EQ(v8_str("universalAnswer"), result->Get(0)); 11801} 11802