test-api.cc revision 80d68eab642096c1a48b6474d6ec33064b0ad1f5
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 41static const bool kLogThreading = true; 42 43static bool IsNaN(double x) { 44#ifdef WIN32 45 return _isnan(x); 46#else 47 return isnan(x); 48#endif 49} 50 51using ::v8::ObjectTemplate; 52using ::v8::Value; 53using ::v8::Context; 54using ::v8::Local; 55using ::v8::String; 56using ::v8::Script; 57using ::v8::Function; 58using ::v8::AccessorInfo; 59using ::v8::Extension; 60 61namespace i = ::i; 62 63 64static void ExpectString(const char* code, const char* expected) { 65 Local<Value> result = CompileRun(code); 66 CHECK(result->IsString()); 67 String::AsciiValue ascii(result); 68 CHECK_EQ(expected, *ascii); 69} 70 71 72static void ExpectBoolean(const char* code, bool expected) { 73 Local<Value> result = CompileRun(code); 74 CHECK(result->IsBoolean()); 75 CHECK_EQ(expected, result->BooleanValue()); 76} 77 78 79static void ExpectTrue(const char* code) { 80 ExpectBoolean(code, true); 81} 82 83 84static void ExpectFalse(const char* code) { 85 ExpectBoolean(code, false); 86} 87 88 89static void ExpectObject(const char* code, Local<Value> expected) { 90 Local<Value> result = CompileRun(code); 91 CHECK(result->Equals(expected)); 92} 93 94 95static void ExpectUndefined(const char* code) { 96 Local<Value> result = CompileRun(code); 97 CHECK(result->IsUndefined()); 98} 99 100 101static int signature_callback_count; 102static v8::Handle<Value> IncrementingSignatureCallback( 103 const v8::Arguments& args) { 104 ApiTestFuzzer::Fuzz(); 105 signature_callback_count++; 106 v8::Handle<v8::Array> result = v8::Array::New(args.Length()); 107 for (int i = 0; i < args.Length(); i++) 108 result->Set(v8::Integer::New(i), args[i]); 109 return result; 110} 111 112 113static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) { 114 ApiTestFuzzer::Fuzz(); 115 v8::Handle<v8::Array> result = v8::Array::New(args.Length()); 116 for (int i = 0; i < args.Length(); i++) { 117 result->Set(v8::Integer::New(i), args[i]); 118 } 119 return result; 120} 121 122 123THREADED_TEST(Handles) { 124 v8::HandleScope scope; 125 Local<Context> local_env; 126 { 127 LocalContext env; 128 local_env = env.local(); 129 } 130 131 // Local context should still be live. 132 CHECK(!local_env.IsEmpty()); 133 local_env->Enter(); 134 135 v8::Handle<v8::Primitive> undef = v8::Undefined(); 136 CHECK(!undef.IsEmpty()); 137 CHECK(undef->IsUndefined()); 138 139 const char* c_source = "1 + 2 + 3"; 140 Local<String> source = String::New(c_source); 141 Local<Script> script = Script::Compile(source); 142 CHECK_EQ(6, script->Run()->Int32Value()); 143 144 local_env->Exit(); 145} 146 147 148THREADED_TEST(ReceiverSignature) { 149 v8::HandleScope scope; 150 LocalContext env; 151 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); 152 v8::Handle<v8::Signature> sig = v8::Signature::New(fun); 153 fun->PrototypeTemplate()->Set( 154 v8_str("m"), 155 v8::FunctionTemplate::New(IncrementingSignatureCallback, 156 v8::Handle<Value>(), 157 sig)); 158 env->Global()->Set(v8_str("Fun"), fun->GetFunction()); 159 signature_callback_count = 0; 160 CompileRun( 161 "var o = new Fun();" 162 "o.m();"); 163 CHECK_EQ(1, signature_callback_count); 164 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New(); 165 sub_fun->Inherit(fun); 166 env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction()); 167 CompileRun( 168 "var o = new SubFun();" 169 "o.m();"); 170 CHECK_EQ(2, signature_callback_count); 171 172 v8::TryCatch try_catch; 173 CompileRun( 174 "var o = { };" 175 "o.m = Fun.prototype.m;" 176 "o.m();"); 177 CHECK_EQ(2, signature_callback_count); 178 CHECK(try_catch.HasCaught()); 179 try_catch.Reset(); 180 v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New(); 181 sub_fun->Inherit(fun); 182 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction()); 183 CompileRun( 184 "var o = new UnrelFun();" 185 "o.m = Fun.prototype.m;" 186 "o.m();"); 187 CHECK_EQ(2, signature_callback_count); 188 CHECK(try_catch.HasCaught()); 189} 190 191 192 193 194THREADED_TEST(ArgumentSignature) { 195 v8::HandleScope scope; 196 LocalContext env; 197 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New(); 198 cons->SetClassName(v8_str("Cons")); 199 v8::Handle<v8::Signature> sig = 200 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons); 201 v8::Handle<v8::FunctionTemplate> fun = 202 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig); 203 env->Global()->Set(v8_str("Cons"), cons->GetFunction()); 204 env->Global()->Set(v8_str("Fun1"), fun->GetFunction()); 205 206 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';"); 207 CHECK(value1->IsTrue()); 208 209 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';"); 210 CHECK(value2->IsTrue()); 211 212 v8::Handle<Value> value3 = CompileRun("Fun1() == '';"); 213 CHECK(value3->IsTrue()); 214 215 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New(); 216 cons1->SetClassName(v8_str("Cons1")); 217 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New(); 218 cons2->SetClassName(v8_str("Cons2")); 219 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New(); 220 cons3->SetClassName(v8_str("Cons3")); 221 222 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 }; 223 v8::Handle<v8::Signature> wsig = 224 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args); 225 v8::Handle<v8::FunctionTemplate> fun2 = 226 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig); 227 228 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction()); 229 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction()); 230 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction()); 231 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction()); 232 v8::Handle<Value> value4 = CompileRun( 233 "Fun2(new Cons1(), new Cons2(), new Cons3()) ==" 234 "'[object Cons1],[object Cons2],[object Cons3]'"); 235 CHECK(value4->IsTrue()); 236 237 v8::Handle<Value> value5 = CompileRun( 238 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'"); 239 CHECK(value5->IsTrue()); 240 241 v8::Handle<Value> value6 = CompileRun( 242 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'"); 243 CHECK(value6->IsTrue()); 244 245 v8::Handle<Value> value7 = CompileRun( 246 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == " 247 "'[object Cons1],[object Cons2],[object Cons3],d';"); 248 CHECK(value7->IsTrue()); 249 250 v8::Handle<Value> value8 = CompileRun( 251 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'"); 252 CHECK(value8->IsTrue()); 253} 254 255 256THREADED_TEST(HulIgennem) { 257 v8::HandleScope scope; 258 LocalContext env; 259 v8::Handle<v8::Primitive> undef = v8::Undefined(); 260 Local<String> undef_str = undef->ToString(); 261 char* value = i::NewArray<char>(undef_str->Length() + 1); 262 undef_str->WriteAscii(value); 263 CHECK_EQ(0, strcmp(value, "undefined")); 264 i::DeleteArray(value); 265} 266 267 268THREADED_TEST(Access) { 269 v8::HandleScope scope; 270 LocalContext env; 271 Local<v8::Object> obj = v8::Object::New(); 272 Local<Value> foo_before = obj->Get(v8_str("foo")); 273 CHECK(foo_before->IsUndefined()); 274 Local<String> bar_str = v8_str("bar"); 275 obj->Set(v8_str("foo"), bar_str); 276 Local<Value> foo_after = obj->Get(v8_str("foo")); 277 CHECK(!foo_after->IsUndefined()); 278 CHECK(foo_after->IsString()); 279 CHECK_EQ(bar_str, foo_after); 280} 281 282 283THREADED_TEST(AccessElement) { 284 v8::HandleScope scope; 285 LocalContext env; 286 Local<v8::Object> obj = v8::Object::New(); 287 Local<Value> before = obj->Get(1); 288 CHECK(before->IsUndefined()); 289 Local<String> bar_str = v8_str("bar"); 290 obj->Set(1, bar_str); 291 Local<Value> after = obj->Get(1); 292 CHECK(!after->IsUndefined()); 293 CHECK(after->IsString()); 294 CHECK_EQ(bar_str, after); 295 296 Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>(); 297 CHECK_EQ(v8_str("a"), value->Get(0)); 298 CHECK_EQ(v8_str("b"), value->Get(1)); 299} 300 301 302THREADED_TEST(Script) { 303 v8::HandleScope scope; 304 LocalContext env; 305 const char* c_source = "1 + 2 + 3"; 306 Local<String> source = String::New(c_source); 307 Local<Script> script = Script::Compile(source); 308 CHECK_EQ(6, script->Run()->Int32Value()); 309} 310 311 312static uint16_t* AsciiToTwoByteString(const char* source) { 313 int array_length = i::StrLength(source) + 1; 314 uint16_t* converted = i::NewArray<uint16_t>(array_length); 315 for (int i = 0; i < array_length; i++) converted[i] = source[i]; 316 return converted; 317} 318 319 320class TestResource: public String::ExternalStringResource { 321 public: 322 static int dispose_count; 323 324 explicit TestResource(uint16_t* data) 325 : data_(data), length_(0) { 326 while (data[length_]) ++length_; 327 } 328 329 ~TestResource() { 330 i::DeleteArray(data_); 331 ++dispose_count; 332 } 333 334 const uint16_t* data() const { 335 return data_; 336 } 337 338 size_t length() const { 339 return length_; 340 } 341 private: 342 uint16_t* data_; 343 size_t length_; 344}; 345 346 347int TestResource::dispose_count = 0; 348 349 350class TestAsciiResource: public String::ExternalAsciiStringResource { 351 public: 352 static int dispose_count; 353 354 explicit TestAsciiResource(const char* data) 355 : data_(data), 356 length_(strlen(data)) { } 357 358 ~TestAsciiResource() { 359 i::DeleteArray(data_); 360 ++dispose_count; 361 } 362 363 const char* data() const { 364 return data_; 365 } 366 367 size_t length() const { 368 return length_; 369 } 370 private: 371 const char* data_; 372 size_t length_; 373}; 374 375 376int TestAsciiResource::dispose_count = 0; 377 378 379THREADED_TEST(ScriptUsingStringResource) { 380 TestResource::dispose_count = 0; 381 const char* c_source = "1 + 2 * 3"; 382 uint16_t* two_byte_source = AsciiToTwoByteString(c_source); 383 { 384 v8::HandleScope scope; 385 LocalContext env; 386 TestResource* resource = new TestResource(two_byte_source); 387 Local<String> source = String::NewExternal(resource); 388 Local<Script> script = Script::Compile(source); 389 Local<Value> value = script->Run(); 390 CHECK(value->IsNumber()); 391 CHECK_EQ(7, value->Int32Value()); 392 CHECK(source->IsExternal()); 393 CHECK_EQ(resource, 394 static_cast<TestResource*>(source->GetExternalStringResource())); 395 i::Heap::CollectAllGarbage(false); 396 CHECK_EQ(0, TestResource::dispose_count); 397 } 398 i::CompilationCache::Clear(); 399 i::Heap::CollectAllGarbage(false); 400 CHECK_EQ(1, TestResource::dispose_count); 401} 402 403 404THREADED_TEST(ScriptUsingAsciiStringResource) { 405 TestAsciiResource::dispose_count = 0; 406 const char* c_source = "1 + 2 * 3"; 407 { 408 v8::HandleScope scope; 409 LocalContext env; 410 Local<String> source = 411 String::NewExternal(new TestAsciiResource(i::StrDup(c_source))); 412 Local<Script> script = Script::Compile(source); 413 Local<Value> value = script->Run(); 414 CHECK(value->IsNumber()); 415 CHECK_EQ(7, value->Int32Value()); 416 i::Heap::CollectAllGarbage(false); 417 CHECK_EQ(0, TestAsciiResource::dispose_count); 418 } 419 i::CompilationCache::Clear(); 420 i::Heap::CollectAllGarbage(false); 421 CHECK_EQ(1, TestAsciiResource::dispose_count); 422} 423 424 425THREADED_TEST(ScriptMakingExternalString) { 426 TestResource::dispose_count = 0; 427 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3"); 428 { 429 v8::HandleScope scope; 430 LocalContext env; 431 Local<String> source = String::New(two_byte_source); 432 // Trigger GCs so that the newly allocated string moves to old gen. 433 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 434 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 435 bool success = source->MakeExternal(new TestResource(two_byte_source)); 436 CHECK(success); 437 Local<Script> script = Script::Compile(source); 438 Local<Value> value = script->Run(); 439 CHECK(value->IsNumber()); 440 CHECK_EQ(7, value->Int32Value()); 441 i::Heap::CollectAllGarbage(false); 442 CHECK_EQ(0, TestResource::dispose_count); 443 } 444 i::CompilationCache::Clear(); 445 i::Heap::CollectAllGarbage(false); 446 CHECK_EQ(1, TestResource::dispose_count); 447} 448 449 450THREADED_TEST(ScriptMakingExternalAsciiString) { 451 TestAsciiResource::dispose_count = 0; 452 const char* c_source = "1 + 2 * 3"; 453 { 454 v8::HandleScope scope; 455 LocalContext env; 456 Local<String> source = v8_str(c_source); 457 // Trigger GCs so that the newly allocated string moves to old gen. 458 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 459 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 460 bool success = source->MakeExternal( 461 new TestAsciiResource(i::StrDup(c_source))); 462 CHECK(success); 463 Local<Script> script = Script::Compile(source); 464 Local<Value> value = script->Run(); 465 CHECK(value->IsNumber()); 466 CHECK_EQ(7, value->Int32Value()); 467 i::Heap::CollectAllGarbage(false); 468 CHECK_EQ(0, TestAsciiResource::dispose_count); 469 } 470 i::CompilationCache::Clear(); 471 i::Heap::CollectAllGarbage(false); 472 CHECK_EQ(1, TestAsciiResource::dispose_count); 473} 474 475 476TEST(MakingExternalStringConditions) { 477 v8::HandleScope scope; 478 LocalContext env; 479 480 // Free some space in the new space so that we can check freshness. 481 i::Heap::CollectGarbage(0, i::NEW_SPACE); 482 i::Heap::CollectGarbage(0, i::NEW_SPACE); 483 484 uint16_t* two_byte_string = AsciiToTwoByteString("small"); 485 Local<String> small_string = String::New(two_byte_string); 486 i::DeleteArray(two_byte_string); 487 488 // We should refuse to externalize newly created small string. 489 CHECK(!small_string->CanMakeExternal()); 490 // Trigger GCs so that the newly allocated string moves to old gen. 491 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 492 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 493 // Old space strings should be accepted. 494 CHECK(small_string->CanMakeExternal()); 495 496 two_byte_string = AsciiToTwoByteString("small 2"); 497 small_string = String::New(two_byte_string); 498 i::DeleteArray(two_byte_string); 499 500 // We should refuse externalizing newly created small string. 501 CHECK(!small_string->CanMakeExternal()); 502 for (int i = 0; i < 100; i++) { 503 String::Value value(small_string); 504 } 505 // Frequently used strings should be accepted. 506 CHECK(small_string->CanMakeExternal()); 507 508 const int buf_size = 10 * 1024; 509 char* buf = i::NewArray<char>(buf_size); 510 memset(buf, 'a', buf_size); 511 buf[buf_size - 1] = '\0'; 512 513 two_byte_string = AsciiToTwoByteString(buf); 514 Local<String> large_string = String::New(two_byte_string); 515 i::DeleteArray(buf); 516 i::DeleteArray(two_byte_string); 517 // Large strings should be immediately accepted. 518 CHECK(large_string->CanMakeExternal()); 519} 520 521 522TEST(MakingExternalAsciiStringConditions) { 523 v8::HandleScope scope; 524 LocalContext env; 525 526 // Free some space in the new space so that we can check freshness. 527 i::Heap::CollectGarbage(0, i::NEW_SPACE); 528 i::Heap::CollectGarbage(0, i::NEW_SPACE); 529 530 Local<String> small_string = String::New("small"); 531 // We should refuse to externalize newly created small string. 532 CHECK(!small_string->CanMakeExternal()); 533 // Trigger GCs so that the newly allocated string moves to old gen. 534 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 535 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 536 // Old space strings should be accepted. 537 CHECK(small_string->CanMakeExternal()); 538 539 small_string = String::New("small 2"); 540 // We should refuse externalizing newly created small string. 541 CHECK(!small_string->CanMakeExternal()); 542 for (int i = 0; i < 100; i++) { 543 String::Value value(small_string); 544 } 545 // Frequently used strings should be accepted. 546 CHECK(small_string->CanMakeExternal()); 547 548 const int buf_size = 10 * 1024; 549 char* buf = i::NewArray<char>(buf_size); 550 memset(buf, 'a', buf_size); 551 buf[buf_size - 1] = '\0'; 552 Local<String> large_string = String::New(buf); 553 i::DeleteArray(buf); 554 // Large strings should be immediately accepted. 555 CHECK(large_string->CanMakeExternal()); 556} 557 558 559THREADED_TEST(UsingExternalString) { 560 { 561 v8::HandleScope scope; 562 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 563 Local<String> string = 564 String::NewExternal(new TestResource(two_byte_string)); 565 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 566 // Trigger GCs so that the newly allocated string moves to old gen. 567 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 568 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 569 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring); 570 CHECK(isymbol->IsSymbol()); 571 } 572 i::Heap::CollectAllGarbage(false); 573 i::Heap::CollectAllGarbage(false); 574} 575 576 577THREADED_TEST(UsingExternalAsciiString) { 578 { 579 v8::HandleScope scope; 580 const char* one_byte_string = "test string"; 581 Local<String> string = String::NewExternal( 582 new TestAsciiResource(i::StrDup(one_byte_string))); 583 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 584 // Trigger GCs so that the newly allocated string moves to old gen. 585 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 586 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 587 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring); 588 CHECK(isymbol->IsSymbol()); 589 } 590 i::Heap::CollectAllGarbage(false); 591 i::Heap::CollectAllGarbage(false); 592} 593 594 595THREADED_TEST(ScavengeExternalString) { 596 TestResource::dispose_count = 0; 597 bool in_new_space = false; 598 { 599 v8::HandleScope scope; 600 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 601 Local<String> string = 602 String::NewExternal(new TestResource(two_byte_string)); 603 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 604 i::Heap::CollectGarbage(0, i::NEW_SPACE); 605 in_new_space = i::Heap::InNewSpace(*istring); 606 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring)); 607 CHECK_EQ(0, TestResource::dispose_count); 608 } 609 i::Heap::CollectGarbage(0, in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); 610 CHECK_EQ(1, TestResource::dispose_count); 611} 612 613 614THREADED_TEST(ScavengeExternalAsciiString) { 615 TestAsciiResource::dispose_count = 0; 616 bool in_new_space = false; 617 { 618 v8::HandleScope scope; 619 const char* one_byte_string = "test string"; 620 Local<String> string = String::NewExternal( 621 new TestAsciiResource(i::StrDup(one_byte_string))); 622 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 623 i::Heap::CollectGarbage(0, i::NEW_SPACE); 624 in_new_space = i::Heap::InNewSpace(*istring); 625 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring)); 626 CHECK_EQ(0, TestAsciiResource::dispose_count); 627 } 628 i::Heap::CollectGarbage(0, in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); 629 CHECK_EQ(1, TestAsciiResource::dispose_count); 630} 631 632 633class TestAsciiResourceWithDisposeControl: public TestAsciiResource { 634 public: 635 static int dispose_calls; 636 637 TestAsciiResourceWithDisposeControl(const char* data, bool dispose) 638 : TestAsciiResource(data), 639 dispose_(dispose) { } 640 641 void Dispose() { 642 ++dispose_calls; 643 if (dispose_) delete this; 644 } 645 private: 646 bool dispose_; 647}; 648 649 650int TestAsciiResourceWithDisposeControl::dispose_calls = 0; 651 652 653TEST(ExternalStringWithDisposeHandling) { 654 const char* c_source = "1 + 2 * 3"; 655 656 // Use a stack allocated external string resource allocated object. 657 TestAsciiResource::dispose_count = 0; 658 TestAsciiResourceWithDisposeControl::dispose_calls = 0; 659 TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false); 660 { 661 v8::HandleScope scope; 662 LocalContext env; 663 Local<String> source = String::NewExternal(&res_stack); 664 Local<Script> script = Script::Compile(source); 665 Local<Value> value = script->Run(); 666 CHECK(value->IsNumber()); 667 CHECK_EQ(7, value->Int32Value()); 668 i::Heap::CollectAllGarbage(false); 669 CHECK_EQ(0, TestAsciiResource::dispose_count); 670 } 671 i::CompilationCache::Clear(); 672 i::Heap::CollectAllGarbage(false); 673 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); 674 CHECK_EQ(0, TestAsciiResource::dispose_count); 675 676 // Use a heap allocated external string resource allocated object. 677 TestAsciiResource::dispose_count = 0; 678 TestAsciiResourceWithDisposeControl::dispose_calls = 0; 679 TestAsciiResource* res_heap = 680 new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true); 681 { 682 v8::HandleScope scope; 683 LocalContext env; 684 Local<String> source = String::NewExternal(res_heap); 685 Local<Script> script = Script::Compile(source); 686 Local<Value> value = script->Run(); 687 CHECK(value->IsNumber()); 688 CHECK_EQ(7, value->Int32Value()); 689 i::Heap::CollectAllGarbage(false); 690 CHECK_EQ(0, TestAsciiResource::dispose_count); 691 } 692 i::CompilationCache::Clear(); 693 i::Heap::CollectAllGarbage(false); 694 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); 695 CHECK_EQ(1, TestAsciiResource::dispose_count); 696} 697 698 699THREADED_TEST(StringConcat) { 700 { 701 v8::HandleScope scope; 702 LocalContext env; 703 const char* one_byte_string_1 = "function a_times_t"; 704 const char* two_byte_string_1 = "wo_plus_b(a, b) {return "; 705 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + "; 706 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + "; 707 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 708 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 709 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);"; 710 Local<String> left = v8_str(one_byte_string_1); 711 712 uint16_t* two_byte_source = AsciiToTwoByteString(two_byte_string_1); 713 Local<String> right = String::New(two_byte_source); 714 i::DeleteArray(two_byte_source); 715 716 Local<String> source = String::Concat(left, right); 717 right = String::NewExternal( 718 new TestAsciiResource(i::StrDup(one_byte_extern_1))); 719 source = String::Concat(source, right); 720 right = String::NewExternal( 721 new TestResource(AsciiToTwoByteString(two_byte_extern_1))); 722 source = String::Concat(source, right); 723 right = v8_str(one_byte_string_2); 724 source = String::Concat(source, right); 725 726 two_byte_source = AsciiToTwoByteString(two_byte_string_2); 727 right = String::New(two_byte_source); 728 i::DeleteArray(two_byte_source); 729 730 source = String::Concat(source, right); 731 right = String::NewExternal( 732 new TestResource(AsciiToTwoByteString(two_byte_extern_2))); 733 source = String::Concat(source, right); 734 Local<Script> script = Script::Compile(source); 735 Local<Value> value = script->Run(); 736 CHECK(value->IsNumber()); 737 CHECK_EQ(68, value->Int32Value()); 738 } 739 i::CompilationCache::Clear(); 740 i::Heap::CollectAllGarbage(false); 741 i::Heap::CollectAllGarbage(false); 742} 743 744 745THREADED_TEST(GlobalProperties) { 746 v8::HandleScope scope; 747 LocalContext env; 748 v8::Handle<v8::Object> global = env->Global(); 749 global->Set(v8_str("pi"), v8_num(3.1415926)); 750 Local<Value> pi = global->Get(v8_str("pi")); 751 CHECK_EQ(3.1415926, pi->NumberValue()); 752} 753 754 755static v8::Handle<Value> handle_call(const v8::Arguments& args) { 756 ApiTestFuzzer::Fuzz(); 757 return v8_num(102); 758} 759 760 761static v8::Handle<Value> construct_call(const v8::Arguments& args) { 762 ApiTestFuzzer::Fuzz(); 763 args.This()->Set(v8_str("x"), v8_num(1)); 764 args.This()->Set(v8_str("y"), v8_num(2)); 765 return args.This(); 766} 767 768THREADED_TEST(FunctionTemplate) { 769 v8::HandleScope scope; 770 LocalContext env; 771 { 772 Local<v8::FunctionTemplate> fun_templ = 773 v8::FunctionTemplate::New(handle_call); 774 Local<Function> fun = fun_templ->GetFunction(); 775 env->Global()->Set(v8_str("obj"), fun); 776 Local<Script> script = v8_compile("obj()"); 777 CHECK_EQ(102, script->Run()->Int32Value()); 778 } 779 // Use SetCallHandler to initialize a function template, should work like the 780 // previous one. 781 { 782 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 783 fun_templ->SetCallHandler(handle_call); 784 Local<Function> fun = fun_templ->GetFunction(); 785 env->Global()->Set(v8_str("obj"), fun); 786 Local<Script> script = v8_compile("obj()"); 787 CHECK_EQ(102, script->Run()->Int32Value()); 788 } 789 // Test constructor calls. 790 { 791 Local<v8::FunctionTemplate> fun_templ = 792 v8::FunctionTemplate::New(construct_call); 793 fun_templ->SetClassName(v8_str("funky")); 794 Local<Function> fun = fun_templ->GetFunction(); 795 env->Global()->Set(v8_str("obj"), fun); 796 Local<Script> script = v8_compile("var s = new obj(); s.x"); 797 CHECK_EQ(1, script->Run()->Int32Value()); 798 799 Local<Value> result = v8_compile("(new obj()).toString()")->Run(); 800 CHECK_EQ(v8_str("[object funky]"), result); 801 } 802} 803 804 805THREADED_TEST(FindInstanceInPrototypeChain) { 806 v8::HandleScope scope; 807 LocalContext env; 808 809 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New(); 810 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New(); 811 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New(); 812 derived->Inherit(base); 813 814 Local<v8::Function> base_function = base->GetFunction(); 815 Local<v8::Function> derived_function = derived->GetFunction(); 816 Local<v8::Function> other_function = other->GetFunction(); 817 818 Local<v8::Object> base_instance = base_function->NewInstance(); 819 Local<v8::Object> derived_instance = derived_function->NewInstance(); 820 Local<v8::Object> derived_instance2 = derived_function->NewInstance(); 821 Local<v8::Object> other_instance = other_function->NewInstance(); 822 derived_instance2->Set(v8_str("__proto__"), derived_instance); 823 other_instance->Set(v8_str("__proto__"), derived_instance2); 824 825 // base_instance is only an instance of base. 826 CHECK_EQ(base_instance, 827 base_instance->FindInstanceInPrototypeChain(base)); 828 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty()); 829 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 830 831 // derived_instance is an instance of base and derived. 832 CHECK_EQ(derived_instance, 833 derived_instance->FindInstanceInPrototypeChain(base)); 834 CHECK_EQ(derived_instance, 835 derived_instance->FindInstanceInPrototypeChain(derived)); 836 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 837 838 // other_instance is an instance of other and its immediate 839 // prototype derived_instance2 is an instance of base and derived. 840 // Note, derived_instance is an instance of base and derived too, 841 // but it comes after derived_instance2 in the prototype chain of 842 // other_instance. 843 CHECK_EQ(derived_instance2, 844 other_instance->FindInstanceInPrototypeChain(base)); 845 CHECK_EQ(derived_instance2, 846 other_instance->FindInstanceInPrototypeChain(derived)); 847 CHECK_EQ(other_instance, 848 other_instance->FindInstanceInPrototypeChain(other)); 849} 850 851 852THREADED_TEST(TinyInteger) { 853 v8::HandleScope scope; 854 LocalContext env; 855 int32_t value = 239; 856 Local<v8::Integer> value_obj = v8::Integer::New(value); 857 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 858} 859 860 861THREADED_TEST(BigSmiInteger) { 862 v8::HandleScope scope; 863 LocalContext env; 864 int32_t value = i::Smi::kMaxValue; 865 // We cannot add one to a Smi::kMaxValue without wrapping. 866 if (i::kSmiValueSize < 32) { 867 CHECK(i::Smi::IsValid(value)); 868 CHECK(!i::Smi::IsValid(value + 1)); 869 Local<v8::Integer> value_obj = v8::Integer::New(value); 870 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 871 } 872} 873 874 875THREADED_TEST(BigInteger) { 876 v8::HandleScope scope; 877 LocalContext env; 878 // We cannot add one to a Smi::kMaxValue without wrapping. 879 if (i::kSmiValueSize < 32) { 880 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1. 881 // The code will not be run in that case, due to the "if" guard. 882 int32_t value = 883 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1); 884 CHECK(value > i::Smi::kMaxValue); 885 CHECK(!i::Smi::IsValid(value)); 886 Local<v8::Integer> value_obj = v8::Integer::New(value); 887 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 888 } 889} 890 891 892THREADED_TEST(TinyUnsignedInteger) { 893 v8::HandleScope scope; 894 LocalContext env; 895 uint32_t value = 239; 896 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 897 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 898} 899 900 901THREADED_TEST(BigUnsignedSmiInteger) { 902 v8::HandleScope scope; 903 LocalContext env; 904 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue); 905 CHECK(i::Smi::IsValid(value)); 906 CHECK(!i::Smi::IsValid(value + 1)); 907 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 908 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 909} 910 911 912THREADED_TEST(BigUnsignedInteger) { 913 v8::HandleScope scope; 914 LocalContext env; 915 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1; 916 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue)); 917 CHECK(!i::Smi::IsValid(value)); 918 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 919 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 920} 921 922 923THREADED_TEST(OutOfSignedRangeUnsignedInteger) { 924 v8::HandleScope scope; 925 LocalContext env; 926 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1; 927 uint32_t value = INT32_MAX_AS_UINT + 1; 928 CHECK(value > INT32_MAX_AS_UINT); // No overflow. 929 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 930 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 931} 932 933 934THREADED_TEST(Number) { 935 v8::HandleScope scope; 936 LocalContext env; 937 double PI = 3.1415926; 938 Local<v8::Number> pi_obj = v8::Number::New(PI); 939 CHECK_EQ(PI, pi_obj->NumberValue()); 940} 941 942 943THREADED_TEST(ToNumber) { 944 v8::HandleScope scope; 945 LocalContext env; 946 Local<String> str = v8_str("3.1415926"); 947 CHECK_EQ(3.1415926, str->NumberValue()); 948 v8::Handle<v8::Boolean> t = v8::True(); 949 CHECK_EQ(1.0, t->NumberValue()); 950 v8::Handle<v8::Boolean> f = v8::False(); 951 CHECK_EQ(0.0, f->NumberValue()); 952} 953 954 955THREADED_TEST(Date) { 956 v8::HandleScope scope; 957 LocalContext env; 958 double PI = 3.1415926; 959 Local<Value> date_obj = v8::Date::New(PI); 960 CHECK_EQ(3.0, date_obj->NumberValue()); 961} 962 963 964THREADED_TEST(Boolean) { 965 v8::HandleScope scope; 966 LocalContext env; 967 v8::Handle<v8::Boolean> t = v8::True(); 968 CHECK(t->Value()); 969 v8::Handle<v8::Boolean> f = v8::False(); 970 CHECK(!f->Value()); 971 v8::Handle<v8::Primitive> u = v8::Undefined(); 972 CHECK(!u->BooleanValue()); 973 v8::Handle<v8::Primitive> n = v8::Null(); 974 CHECK(!n->BooleanValue()); 975 v8::Handle<String> str1 = v8_str(""); 976 CHECK(!str1->BooleanValue()); 977 v8::Handle<String> str2 = v8_str("x"); 978 CHECK(str2->BooleanValue()); 979 CHECK(!v8::Number::New(0)->BooleanValue()); 980 CHECK(v8::Number::New(-1)->BooleanValue()); 981 CHECK(v8::Number::New(1)->BooleanValue()); 982 CHECK(v8::Number::New(42)->BooleanValue()); 983 CHECK(!v8_compile("NaN")->Run()->BooleanValue()); 984} 985 986 987static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) { 988 ApiTestFuzzer::Fuzz(); 989 return v8_num(13.4); 990} 991 992 993static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) { 994 ApiTestFuzzer::Fuzz(); 995 return v8_num(876); 996} 997 998 999THREADED_TEST(GlobalPrototype) { 1000 v8::HandleScope scope; 1001 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 1002 func_templ->PrototypeTemplate()->Set( 1003 "dummy", 1004 v8::FunctionTemplate::New(DummyCallHandler)); 1005 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate(); 1006 templ->Set("x", v8_num(200)); 1007 templ->SetAccessor(v8_str("m"), GetM); 1008 LocalContext env(0, templ); 1009 v8::Handle<v8::Object> obj = env->Global(); 1010 v8::Handle<Script> script = v8_compile("dummy()"); 1011 v8::Handle<Value> result = script->Run(); 1012 CHECK_EQ(13.4, result->NumberValue()); 1013 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value()); 1014 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value()); 1015} 1016 1017 1018THREADED_TEST(ObjectTemplate) { 1019 v8::HandleScope scope; 1020 Local<ObjectTemplate> templ1 = ObjectTemplate::New(); 1021 templ1->Set("x", v8_num(10)); 1022 templ1->Set("y", v8_num(13)); 1023 LocalContext env; 1024 Local<v8::Object> instance1 = templ1->NewInstance(); 1025 env->Global()->Set(v8_str("p"), instance1); 1026 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue()); 1027 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue()); 1028 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); 1029 fun->PrototypeTemplate()->Set("nirk", v8_num(123)); 1030 Local<ObjectTemplate> templ2 = fun->InstanceTemplate(); 1031 templ2->Set("a", v8_num(12)); 1032 templ2->Set("b", templ1); 1033 Local<v8::Object> instance2 = templ2->NewInstance(); 1034 env->Global()->Set(v8_str("q"), instance2); 1035 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue()); 1036 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue()); 1037 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue()); 1038 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue()); 1039} 1040 1041 1042static v8::Handle<Value> GetFlabby(const v8::Arguments& args) { 1043 ApiTestFuzzer::Fuzz(); 1044 return v8_num(17.2); 1045} 1046 1047 1048static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) { 1049 ApiTestFuzzer::Fuzz(); 1050 return v8_num(15.2); 1051} 1052 1053 1054THREADED_TEST(DescriptorInheritance) { 1055 v8::HandleScope scope; 1056 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New(); 1057 super->PrototypeTemplate()->Set("flabby", 1058 v8::FunctionTemplate::New(GetFlabby)); 1059 super->PrototypeTemplate()->Set("PI", v8_num(3.14)); 1060 1061 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd); 1062 1063 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New(); 1064 base1->Inherit(super); 1065 base1->PrototypeTemplate()->Set("v1", v8_num(20.1)); 1066 1067 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New(); 1068 base2->Inherit(super); 1069 base2->PrototypeTemplate()->Set("v2", v8_num(10.1)); 1070 1071 LocalContext env; 1072 1073 env->Global()->Set(v8_str("s"), super->GetFunction()); 1074 env->Global()->Set(v8_str("base1"), base1->GetFunction()); 1075 env->Global()->Set(v8_str("base2"), base2->GetFunction()); 1076 1077 // Checks right __proto__ chain. 1078 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue()); 1079 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue()); 1080 1081 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue()); 1082 1083 // Instance accessor should not be visible on function object or its prototype 1084 CHECK(CompileRun("s.knurd == undefined")->BooleanValue()); 1085 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue()); 1086 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue()); 1087 1088 env->Global()->Set(v8_str("obj"), 1089 base1->GetFunction()->NewInstance()); 1090 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue()); 1091 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue()); 1092 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue()); 1093 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue()); 1094 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue()); 1095 1096 env->Global()->Set(v8_str("obj2"), 1097 base2->GetFunction()->NewInstance()); 1098 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue()); 1099 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue()); 1100 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue()); 1101 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue()); 1102 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue()); 1103 1104 // base1 and base2 cannot cross reference to each's prototype 1105 CHECK(v8_compile("obj.v2")->Run()->IsUndefined()); 1106 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined()); 1107} 1108 1109 1110int echo_named_call_count; 1111 1112 1113static v8::Handle<Value> EchoNamedProperty(Local<String> name, 1114 const AccessorInfo& info) { 1115 ApiTestFuzzer::Fuzz(); 1116 CHECK_EQ(v8_str("data"), info.Data()); 1117 echo_named_call_count++; 1118 return name; 1119} 1120 1121 1122THREADED_TEST(NamedPropertyHandlerGetter) { 1123 echo_named_call_count = 0; 1124 v8::HandleScope scope; 1125 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1126 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty, 1127 0, 0, 0, 0, 1128 v8_str("data")); 1129 LocalContext env; 1130 env->Global()->Set(v8_str("obj"), 1131 templ->GetFunction()->NewInstance()); 1132 CHECK_EQ(echo_named_call_count, 0); 1133 v8_compile("obj.x")->Run(); 1134 CHECK_EQ(echo_named_call_count, 1); 1135 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;"; 1136 v8::Handle<Value> str = CompileRun(code); 1137 String::AsciiValue value(str); 1138 CHECK_EQ(*value, "oddlepoddle"); 1139 // Check default behavior 1140 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10); 1141 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue()); 1142 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue()); 1143} 1144 1145 1146int echo_indexed_call_count = 0; 1147 1148 1149static v8::Handle<Value> EchoIndexedProperty(uint32_t index, 1150 const AccessorInfo& info) { 1151 ApiTestFuzzer::Fuzz(); 1152 CHECK_EQ(v8_num(637), info.Data()); 1153 echo_indexed_call_count++; 1154 return v8_num(index); 1155} 1156 1157 1158THREADED_TEST(IndexedPropertyHandlerGetter) { 1159 v8::HandleScope scope; 1160 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1161 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty, 1162 0, 0, 0, 0, 1163 v8_num(637)); 1164 LocalContext env; 1165 env->Global()->Set(v8_str("obj"), 1166 templ->GetFunction()->NewInstance()); 1167 Local<Script> script = v8_compile("obj[900]"); 1168 CHECK_EQ(script->Run()->Int32Value(), 900); 1169} 1170 1171 1172v8::Handle<v8::Object> bottom; 1173 1174static v8::Handle<Value> CheckThisIndexedPropertyHandler( 1175 uint32_t index, 1176 const AccessorInfo& info) { 1177 ApiTestFuzzer::Fuzz(); 1178 CHECK(info.This()->Equals(bottom)); 1179 return v8::Handle<Value>(); 1180} 1181 1182static v8::Handle<Value> CheckThisNamedPropertyHandler( 1183 Local<String> name, 1184 const AccessorInfo& info) { 1185 ApiTestFuzzer::Fuzz(); 1186 CHECK(info.This()->Equals(bottom)); 1187 return v8::Handle<Value>(); 1188} 1189 1190 1191v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index, 1192 Local<Value> value, 1193 const AccessorInfo& info) { 1194 ApiTestFuzzer::Fuzz(); 1195 CHECK(info.This()->Equals(bottom)); 1196 return v8::Handle<Value>(); 1197} 1198 1199 1200v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property, 1201 Local<Value> value, 1202 const AccessorInfo& info) { 1203 ApiTestFuzzer::Fuzz(); 1204 CHECK(info.This()->Equals(bottom)); 1205 return v8::Handle<Value>(); 1206} 1207 1208v8::Handle<v8::Integer> CheckThisIndexedPropertyQuery( 1209 uint32_t index, 1210 const AccessorInfo& info) { 1211 ApiTestFuzzer::Fuzz(); 1212 CHECK(info.This()->Equals(bottom)); 1213 return v8::Handle<v8::Integer>(); 1214} 1215 1216 1217v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property, 1218 const AccessorInfo& info) { 1219 ApiTestFuzzer::Fuzz(); 1220 CHECK(info.This()->Equals(bottom)); 1221 return v8::Handle<v8::Integer>(); 1222} 1223 1224 1225v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter( 1226 uint32_t index, 1227 const AccessorInfo& info) { 1228 ApiTestFuzzer::Fuzz(); 1229 CHECK(info.This()->Equals(bottom)); 1230 return v8::Handle<v8::Boolean>(); 1231} 1232 1233 1234v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter( 1235 Local<String> property, 1236 const AccessorInfo& info) { 1237 ApiTestFuzzer::Fuzz(); 1238 CHECK(info.This()->Equals(bottom)); 1239 return v8::Handle<v8::Boolean>(); 1240} 1241 1242 1243v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator( 1244 const AccessorInfo& info) { 1245 ApiTestFuzzer::Fuzz(); 1246 CHECK(info.This()->Equals(bottom)); 1247 return v8::Handle<v8::Array>(); 1248} 1249 1250 1251v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator( 1252 const AccessorInfo& info) { 1253 ApiTestFuzzer::Fuzz(); 1254 CHECK(info.This()->Equals(bottom)); 1255 return v8::Handle<v8::Array>(); 1256} 1257 1258 1259THREADED_TEST(PropertyHandlerInPrototype) { 1260 v8::HandleScope scope; 1261 LocalContext env; 1262 1263 // Set up a prototype chain with three interceptors. 1264 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1265 templ->InstanceTemplate()->SetIndexedPropertyHandler( 1266 CheckThisIndexedPropertyHandler, 1267 CheckThisIndexedPropertySetter, 1268 CheckThisIndexedPropertyQuery, 1269 CheckThisIndexedPropertyDeleter, 1270 CheckThisIndexedPropertyEnumerator); 1271 1272 templ->InstanceTemplate()->SetNamedPropertyHandler( 1273 CheckThisNamedPropertyHandler, 1274 CheckThisNamedPropertySetter, 1275 CheckThisNamedPropertyQuery, 1276 CheckThisNamedPropertyDeleter, 1277 CheckThisNamedPropertyEnumerator); 1278 1279 bottom = templ->GetFunction()->NewInstance(); 1280 Local<v8::Object> top = templ->GetFunction()->NewInstance(); 1281 Local<v8::Object> middle = templ->GetFunction()->NewInstance(); 1282 1283 bottom->Set(v8_str("__proto__"), middle); 1284 middle->Set(v8_str("__proto__"), top); 1285 env->Global()->Set(v8_str("obj"), bottom); 1286 1287 // Indexed and named get. 1288 Script::Compile(v8_str("obj[0]"))->Run(); 1289 Script::Compile(v8_str("obj.x"))->Run(); 1290 1291 // Indexed and named set. 1292 Script::Compile(v8_str("obj[1] = 42"))->Run(); 1293 Script::Compile(v8_str("obj.y = 42"))->Run(); 1294 1295 // Indexed and named query. 1296 Script::Compile(v8_str("0 in obj"))->Run(); 1297 Script::Compile(v8_str("'x' in obj"))->Run(); 1298 1299 // Indexed and named deleter. 1300 Script::Compile(v8_str("delete obj[0]"))->Run(); 1301 Script::Compile(v8_str("delete obj.x"))->Run(); 1302 1303 // Enumerators. 1304 Script::Compile(v8_str("for (var p in obj) ;"))->Run(); 1305} 1306 1307 1308static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key, 1309 const AccessorInfo& info) { 1310 ApiTestFuzzer::Fuzz(); 1311 if (v8_str("pre")->Equals(key)) { 1312 return v8_str("PrePropertyHandler: pre"); 1313 } 1314 return v8::Handle<String>(); 1315} 1316 1317 1318static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key, 1319 const AccessorInfo&) { 1320 if (v8_str("pre")->Equals(key)) { 1321 return v8::Integer::New(v8::None); 1322 } 1323 1324 return v8::Handle<v8::Integer>(); // do not intercept the call 1325} 1326 1327 1328THREADED_TEST(PrePropertyHandler) { 1329 v8::HandleScope scope; 1330 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(); 1331 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet, 1332 0, 1333 PrePropertyHandlerQuery); 1334 LocalContext env(NULL, desc->InstanceTemplate()); 1335 Script::Compile(v8_str( 1336 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run(); 1337 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run(); 1338 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre); 1339 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run(); 1340 CHECK_EQ(v8_str("Object: on"), result_on); 1341 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run(); 1342 CHECK(result_post.IsEmpty()); 1343} 1344 1345 1346THREADED_TEST(UndefinedIsNotEnumerable) { 1347 v8::HandleScope scope; 1348 LocalContext env; 1349 v8::Handle<Value> result = Script::Compile(v8_str( 1350 "this.propertyIsEnumerable(undefined)"))->Run(); 1351 CHECK(result->IsFalse()); 1352} 1353 1354 1355v8::Handle<Script> call_recursively_script; 1356static const int kTargetRecursionDepth = 200; // near maximum 1357 1358 1359static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) { 1360 ApiTestFuzzer::Fuzz(); 1361 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1362 if (depth == kTargetRecursionDepth) return v8::Undefined(); 1363 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1364 return call_recursively_script->Run(); 1365} 1366 1367 1368static v8::Handle<Value> CallFunctionRecursivelyCall( 1369 const v8::Arguments& args) { 1370 ApiTestFuzzer::Fuzz(); 1371 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1372 if (depth == kTargetRecursionDepth) { 1373 printf("[depth = %d]\n", depth); 1374 return v8::Undefined(); 1375 } 1376 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1377 v8::Handle<Value> function = 1378 args.This()->Get(v8_str("callFunctionRecursively")); 1379 return function.As<Function>()->Call(args.This(), 0, NULL); 1380} 1381 1382 1383THREADED_TEST(DeepCrossLanguageRecursion) { 1384 v8::HandleScope scope; 1385 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 1386 global->Set(v8_str("callScriptRecursively"), 1387 v8::FunctionTemplate::New(CallScriptRecursivelyCall)); 1388 global->Set(v8_str("callFunctionRecursively"), 1389 v8::FunctionTemplate::New(CallFunctionRecursivelyCall)); 1390 LocalContext env(NULL, global); 1391 1392 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1393 call_recursively_script = v8_compile("callScriptRecursively()"); 1394 v8::Handle<Value> result = call_recursively_script->Run(); 1395 call_recursively_script = v8::Handle<Script>(); 1396 1397 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1398 Script::Compile(v8_str("callFunctionRecursively()"))->Run(); 1399} 1400 1401 1402static v8::Handle<Value> 1403 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) { 1404 ApiTestFuzzer::Fuzz(); 1405 return v8::ThrowException(key); 1406} 1407 1408 1409static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key, 1410 Local<Value>, 1411 const AccessorInfo&) { 1412 v8::ThrowException(key); 1413 return v8::Undefined(); // not the same as v8::Handle<v8::Value>() 1414} 1415 1416 1417THREADED_TEST(CallbackExceptionRegression) { 1418 v8::HandleScope scope; 1419 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 1420 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet, 1421 ThrowingPropertyHandlerSet); 1422 LocalContext env; 1423 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 1424 v8::Handle<Value> otto = Script::Compile(v8_str( 1425 "try { with (obj) { otto; } } catch (e) { e; }"))->Run(); 1426 CHECK_EQ(v8_str("otto"), otto); 1427 v8::Handle<Value> netto = Script::Compile(v8_str( 1428 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run(); 1429 CHECK_EQ(v8_str("netto"), netto); 1430} 1431 1432 1433THREADED_TEST(FunctionPrototype) { 1434 v8::HandleScope scope; 1435 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New(); 1436 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321)); 1437 LocalContext env; 1438 env->Global()->Set(v8_str("Foo"), Foo->GetFunction()); 1439 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak")); 1440 CHECK_EQ(script->Run()->Int32Value(), 321); 1441} 1442 1443 1444THREADED_TEST(InternalFields) { 1445 v8::HandleScope scope; 1446 LocalContext env; 1447 1448 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1449 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1450 instance_templ->SetInternalFieldCount(1); 1451 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1452 CHECK_EQ(1, obj->InternalFieldCount()); 1453 CHECK(obj->GetInternalField(0)->IsUndefined()); 1454 obj->SetInternalField(0, v8_num(17)); 1455 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value()); 1456} 1457 1458 1459THREADED_TEST(GlobalObjectInternalFields) { 1460 v8::HandleScope scope; 1461 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 1462 global_template->SetInternalFieldCount(1); 1463 LocalContext env(NULL, global_template); 1464 v8::Handle<v8::Object> global_proxy = env->Global(); 1465 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); 1466 CHECK_EQ(1, global->InternalFieldCount()); 1467 CHECK(global->GetInternalField(0)->IsUndefined()); 1468 global->SetInternalField(0, v8_num(17)); 1469 CHECK_EQ(17, global->GetInternalField(0)->Int32Value()); 1470} 1471 1472 1473THREADED_TEST(InternalFieldsNativePointers) { 1474 v8::HandleScope scope; 1475 LocalContext env; 1476 1477 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1478 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1479 instance_templ->SetInternalFieldCount(1); 1480 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1481 CHECK_EQ(1, obj->InternalFieldCount()); 1482 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1483 1484 char* data = new char[100]; 1485 1486 void* aligned = data; 1487 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1); 1488 void* unaligned = data + 1; 1489 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1); 1490 1491 // Check reading and writing aligned pointers. 1492 obj->SetPointerInInternalField(0, aligned); 1493 i::Heap::CollectAllGarbage(false); 1494 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1495 1496 // Check reading and writing unaligned pointers. 1497 obj->SetPointerInInternalField(0, unaligned); 1498 i::Heap::CollectAllGarbage(false); 1499 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1500 1501 delete[] data; 1502} 1503 1504 1505THREADED_TEST(InternalFieldsNativePointersAndExternal) { 1506 v8::HandleScope scope; 1507 LocalContext env; 1508 1509 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1510 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1511 instance_templ->SetInternalFieldCount(1); 1512 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1513 CHECK_EQ(1, obj->InternalFieldCount()); 1514 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1515 1516 char* data = new char[100]; 1517 1518 void* aligned = data; 1519 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1); 1520 void* unaligned = data + 1; 1521 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1); 1522 1523 obj->SetPointerInInternalField(0, aligned); 1524 i::Heap::CollectAllGarbage(false); 1525 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0))); 1526 1527 obj->SetPointerInInternalField(0, unaligned); 1528 i::Heap::CollectAllGarbage(false); 1529 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0))); 1530 1531 obj->SetInternalField(0, v8::External::Wrap(aligned)); 1532 i::Heap::CollectAllGarbage(false); 1533 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1534 1535 obj->SetInternalField(0, v8::External::Wrap(unaligned)); 1536 i::Heap::CollectAllGarbage(false); 1537 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1538 1539 delete[] data; 1540} 1541 1542 1543THREADED_TEST(IdentityHash) { 1544 v8::HandleScope scope; 1545 LocalContext env; 1546 1547 // Ensure that the test starts with an fresh heap to test whether the hash 1548 // code is based on the address. 1549 i::Heap::CollectAllGarbage(false); 1550 Local<v8::Object> obj = v8::Object::New(); 1551 int hash = obj->GetIdentityHash(); 1552 int hash1 = obj->GetIdentityHash(); 1553 CHECK_EQ(hash, hash1); 1554 int hash2 = v8::Object::New()->GetIdentityHash(); 1555 // Since the identity hash is essentially a random number two consecutive 1556 // objects should not be assigned the same hash code. If the test below fails 1557 // the random number generator should be evaluated. 1558 CHECK_NE(hash, hash2); 1559 i::Heap::CollectAllGarbage(false); 1560 int hash3 = v8::Object::New()->GetIdentityHash(); 1561 // Make sure that the identity hash is not based on the initial address of 1562 // the object alone. If the test below fails the random number generator 1563 // should be evaluated. 1564 CHECK_NE(hash, hash3); 1565 int hash4 = obj->GetIdentityHash(); 1566 CHECK_EQ(hash, hash4); 1567} 1568 1569 1570THREADED_TEST(HiddenProperties) { 1571 v8::HandleScope scope; 1572 LocalContext env; 1573 1574 v8::Local<v8::Object> obj = v8::Object::New(); 1575 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 1576 v8::Local<v8::String> empty = v8_str(""); 1577 v8::Local<v8::String> prop_name = v8_str("prop_name"); 1578 1579 i::Heap::CollectAllGarbage(false); 1580 1581 // Make sure delete of a non-existent hidden value works 1582 CHECK(obj->DeleteHiddenValue(key)); 1583 1584 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503))); 1585 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value()); 1586 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002))); 1587 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1588 1589 i::Heap::CollectAllGarbage(false); 1590 1591 // Make sure we do not find the hidden property. 1592 CHECK(!obj->Has(empty)); 1593 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1594 CHECK(obj->Get(empty)->IsUndefined()); 1595 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1596 CHECK(obj->Set(empty, v8::Integer::New(2003))); 1597 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1598 CHECK_EQ(2003, obj->Get(empty)->Int32Value()); 1599 1600 i::Heap::CollectAllGarbage(false); 1601 1602 // Add another property and delete it afterwards to force the object in 1603 // slow case. 1604 CHECK(obj->Set(prop_name, v8::Integer::New(2008))); 1605 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1606 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value()); 1607 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1608 CHECK(obj->Delete(prop_name)); 1609 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1610 1611 i::Heap::CollectAllGarbage(false); 1612 1613 CHECK(obj->DeleteHiddenValue(key)); 1614 CHECK(obj->GetHiddenValue(key).IsEmpty()); 1615} 1616 1617 1618static bool interceptor_for_hidden_properties_called; 1619static v8::Handle<Value> InterceptorForHiddenProperties( 1620 Local<String> name, const AccessorInfo& info) { 1621 interceptor_for_hidden_properties_called = true; 1622 return v8::Handle<Value>(); 1623} 1624 1625 1626THREADED_TEST(HiddenPropertiesWithInterceptors) { 1627 v8::HandleScope scope; 1628 LocalContext context; 1629 1630 interceptor_for_hidden_properties_called = false; 1631 1632 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 1633 1634 // Associate an interceptor with an object and start setting hidden values. 1635 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 1636 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 1637 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties); 1638 Local<v8::Function> function = fun_templ->GetFunction(); 1639 Local<v8::Object> obj = function->NewInstance(); 1640 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302))); 1641 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value()); 1642 CHECK(!interceptor_for_hidden_properties_called); 1643} 1644 1645 1646THREADED_TEST(External) { 1647 v8::HandleScope scope; 1648 int x = 3; 1649 Local<v8::External> ext = v8::External::New(&x); 1650 LocalContext env; 1651 env->Global()->Set(v8_str("ext"), ext); 1652 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run(); 1653 v8::Handle<v8::External> reext = reext_obj.As<v8::External>(); 1654 int* ptr = static_cast<int*>(reext->Value()); 1655 CHECK_EQ(x, 3); 1656 *ptr = 10; 1657 CHECK_EQ(x, 10); 1658 1659 // Make sure unaligned pointers are wrapped properly. 1660 char* data = i::StrDup("0123456789"); 1661 Local<v8::Value> zero = v8::External::Wrap(&data[0]); 1662 Local<v8::Value> one = v8::External::Wrap(&data[1]); 1663 Local<v8::Value> two = v8::External::Wrap(&data[2]); 1664 Local<v8::Value> three = v8::External::Wrap(&data[3]); 1665 1666 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero)); 1667 CHECK_EQ('0', *char_ptr); 1668 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one)); 1669 CHECK_EQ('1', *char_ptr); 1670 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two)); 1671 CHECK_EQ('2', *char_ptr); 1672 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three)); 1673 CHECK_EQ('3', *char_ptr); 1674 i::DeleteArray(data); 1675} 1676 1677 1678THREADED_TEST(GlobalHandle) { 1679 v8::Persistent<String> global; 1680 { 1681 v8::HandleScope scope; 1682 Local<String> str = v8_str("str"); 1683 global = v8::Persistent<String>::New(str); 1684 } 1685 CHECK_EQ(global->Length(), 3); 1686 global.Dispose(); 1687} 1688 1689 1690THREADED_TEST(ScriptException) { 1691 v8::HandleScope scope; 1692 LocalContext env; 1693 Local<Script> script = Script::Compile(v8_str("throw 'panama!';")); 1694 v8::TryCatch try_catch; 1695 Local<Value> result = script->Run(); 1696 CHECK(result.IsEmpty()); 1697 CHECK(try_catch.HasCaught()); 1698 String::AsciiValue exception_value(try_catch.Exception()); 1699 CHECK_EQ(*exception_value, "panama!"); 1700} 1701 1702 1703bool message_received; 1704 1705 1706static void check_message(v8::Handle<v8::Message> message, 1707 v8::Handle<Value> data) { 1708 CHECK_EQ(5.76, data->NumberValue()); 1709 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue()); 1710 CHECK_EQ(7.56, message->GetScriptData()->NumberValue()); 1711 message_received = true; 1712} 1713 1714 1715THREADED_TEST(MessageHandlerData) { 1716 message_received = false; 1717 v8::HandleScope scope; 1718 CHECK(!message_received); 1719 v8::V8::AddMessageListener(check_message, v8_num(5.76)); 1720 LocalContext context; 1721 v8::ScriptOrigin origin = 1722 v8::ScriptOrigin(v8_str("6.75")); 1723 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"), 1724 &origin); 1725 script->SetData(v8_str("7.56")); 1726 script->Run(); 1727 CHECK(message_received); 1728 // clear out the message listener 1729 v8::V8::RemoveMessageListeners(check_message); 1730} 1731 1732 1733THREADED_TEST(GetSetProperty) { 1734 v8::HandleScope scope; 1735 LocalContext context; 1736 context->Global()->Set(v8_str("foo"), v8_num(14)); 1737 context->Global()->Set(v8_str("12"), v8_num(92)); 1738 context->Global()->Set(v8::Integer::New(16), v8_num(32)); 1739 context->Global()->Set(v8_num(13), v8_num(56)); 1740 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run(); 1741 CHECK_EQ(14, foo->Int32Value()); 1742 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run(); 1743 CHECK_EQ(92, twelve->Int32Value()); 1744 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run(); 1745 CHECK_EQ(32, sixteen->Int32Value()); 1746 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run(); 1747 CHECK_EQ(56, thirteen->Int32Value()); 1748 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value()); 1749 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value()); 1750 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value()); 1751 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value()); 1752 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value()); 1753 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value()); 1754 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value()); 1755 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value()); 1756 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value()); 1757} 1758 1759 1760THREADED_TEST(PropertyAttributes) { 1761 v8::HandleScope scope; 1762 LocalContext context; 1763 // read-only 1764 Local<String> prop = v8_str("read_only"); 1765 context->Global()->Set(prop, v8_num(7), v8::ReadOnly); 1766 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1767 Script::Compile(v8_str("read_only = 9"))->Run(); 1768 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1769 context->Global()->Set(prop, v8_num(10)); 1770 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1771 // dont-delete 1772 prop = v8_str("dont_delete"); 1773 context->Global()->Set(prop, v8_num(13), v8::DontDelete); 1774 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 1775 Script::Compile(v8_str("delete dont_delete"))->Run(); 1776 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 1777} 1778 1779 1780THREADED_TEST(Array) { 1781 v8::HandleScope scope; 1782 LocalContext context; 1783 Local<v8::Array> array = v8::Array::New(); 1784 CHECK_EQ(0, array->Length()); 1785 CHECK(array->Get(0)->IsUndefined()); 1786 CHECK(!array->Has(0)); 1787 CHECK(array->Get(100)->IsUndefined()); 1788 CHECK(!array->Has(100)); 1789 array->Set(2, v8_num(7)); 1790 CHECK_EQ(3, array->Length()); 1791 CHECK(!array->Has(0)); 1792 CHECK(!array->Has(1)); 1793 CHECK(array->Has(2)); 1794 CHECK_EQ(7, array->Get(2)->Int32Value()); 1795 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run(); 1796 Local<v8::Array> arr = obj.As<v8::Array>(); 1797 CHECK_EQ(3, arr->Length()); 1798 CHECK_EQ(1, arr->Get(0)->Int32Value()); 1799 CHECK_EQ(2, arr->Get(1)->Int32Value()); 1800 CHECK_EQ(3, arr->Get(2)->Int32Value()); 1801} 1802 1803 1804v8::Handle<Value> HandleF(const v8::Arguments& args) { 1805 v8::HandleScope scope; 1806 ApiTestFuzzer::Fuzz(); 1807 Local<v8::Array> result = v8::Array::New(args.Length()); 1808 for (int i = 0; i < args.Length(); i++) 1809 result->Set(i, args[i]); 1810 return scope.Close(result); 1811} 1812 1813 1814THREADED_TEST(Vector) { 1815 v8::HandleScope scope; 1816 Local<ObjectTemplate> global = ObjectTemplate::New(); 1817 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF)); 1818 LocalContext context(0, global); 1819 1820 const char* fun = "f()"; 1821 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>(); 1822 CHECK_EQ(0, a0->Length()); 1823 1824 const char* fun2 = "f(11)"; 1825 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>(); 1826 CHECK_EQ(1, a1->Length()); 1827 CHECK_EQ(11, a1->Get(0)->Int32Value()); 1828 1829 const char* fun3 = "f(12, 13)"; 1830 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>(); 1831 CHECK_EQ(2, a2->Length()); 1832 CHECK_EQ(12, a2->Get(0)->Int32Value()); 1833 CHECK_EQ(13, a2->Get(1)->Int32Value()); 1834 1835 const char* fun4 = "f(14, 15, 16)"; 1836 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>(); 1837 CHECK_EQ(3, a3->Length()); 1838 CHECK_EQ(14, a3->Get(0)->Int32Value()); 1839 CHECK_EQ(15, a3->Get(1)->Int32Value()); 1840 CHECK_EQ(16, a3->Get(2)->Int32Value()); 1841 1842 const char* fun5 = "f(17, 18, 19, 20)"; 1843 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>(); 1844 CHECK_EQ(4, a4->Length()); 1845 CHECK_EQ(17, a4->Get(0)->Int32Value()); 1846 CHECK_EQ(18, a4->Get(1)->Int32Value()); 1847 CHECK_EQ(19, a4->Get(2)->Int32Value()); 1848 CHECK_EQ(20, a4->Get(3)->Int32Value()); 1849} 1850 1851 1852THREADED_TEST(FunctionCall) { 1853 v8::HandleScope scope; 1854 LocalContext context; 1855 CompileRun( 1856 "function Foo() {" 1857 " var result = [];" 1858 " for (var i = 0; i < arguments.length; i++) {" 1859 " result.push(arguments[i]);" 1860 " }" 1861 " return result;" 1862 "}"); 1863 Local<Function> Foo = 1864 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 1865 1866 v8::Handle<Value>* args0 = NULL; 1867 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0)); 1868 CHECK_EQ(0, a0->Length()); 1869 1870 v8::Handle<Value> args1[] = { v8_num(1.1) }; 1871 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1)); 1872 CHECK_EQ(1, a1->Length()); 1873 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 1874 1875 v8::Handle<Value> args2[] = { v8_num(2.2), 1876 v8_num(3.3) }; 1877 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2)); 1878 CHECK_EQ(2, a2->Length()); 1879 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 1880 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 1881 1882 v8::Handle<Value> args3[] = { v8_num(4.4), 1883 v8_num(5.5), 1884 v8_num(6.6) }; 1885 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3)); 1886 CHECK_EQ(3, a3->Length()); 1887 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 1888 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 1889 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 1890 1891 v8::Handle<Value> args4[] = { v8_num(7.7), 1892 v8_num(8.8), 1893 v8_num(9.9), 1894 v8_num(10.11) }; 1895 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4)); 1896 CHECK_EQ(4, a4->Length()); 1897 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 1898 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 1899 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 1900 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 1901} 1902 1903 1904static const char* js_code_causing_out_of_memory = 1905 "var a = new Array(); while(true) a.push(a);"; 1906 1907 1908// These tests run for a long time and prevent us from running tests 1909// that come after them so they cannot run in parallel. 1910TEST(OutOfMemory) { 1911 // It's not possible to read a snapshot into a heap with different dimensions. 1912 if (i::Snapshot::IsEnabled()) return; 1913 // Set heap limits. 1914 static const int K = 1024; 1915 v8::ResourceConstraints constraints; 1916 constraints.set_max_young_space_size(256 * K); 1917 constraints.set_max_old_space_size(4 * K * K); 1918 v8::SetResourceConstraints(&constraints); 1919 1920 // Execute a script that causes out of memory. 1921 v8::HandleScope scope; 1922 LocalContext context; 1923 v8::V8::IgnoreOutOfMemoryException(); 1924 Local<Script> script = 1925 Script::Compile(String::New(js_code_causing_out_of_memory)); 1926 Local<Value> result = script->Run(); 1927 1928 // Check for out of memory state. 1929 CHECK(result.IsEmpty()); 1930 CHECK(context->HasOutOfMemoryException()); 1931} 1932 1933 1934v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) { 1935 ApiTestFuzzer::Fuzz(); 1936 1937 v8::HandleScope scope; 1938 LocalContext context; 1939 Local<Script> script = 1940 Script::Compile(String::New(js_code_causing_out_of_memory)); 1941 Local<Value> result = script->Run(); 1942 1943 // Check for out of memory state. 1944 CHECK(result.IsEmpty()); 1945 CHECK(context->HasOutOfMemoryException()); 1946 1947 return result; 1948} 1949 1950 1951TEST(OutOfMemoryNested) { 1952 // It's not possible to read a snapshot into a heap with different dimensions. 1953 if (i::Snapshot::IsEnabled()) return; 1954 // Set heap limits. 1955 static const int K = 1024; 1956 v8::ResourceConstraints constraints; 1957 constraints.set_max_young_space_size(256 * K); 1958 constraints.set_max_old_space_size(4 * K * K); 1959 v8::SetResourceConstraints(&constraints); 1960 1961 v8::HandleScope scope; 1962 Local<ObjectTemplate> templ = ObjectTemplate::New(); 1963 templ->Set(v8_str("ProvokeOutOfMemory"), 1964 v8::FunctionTemplate::New(ProvokeOutOfMemory)); 1965 LocalContext context(0, templ); 1966 v8::V8::IgnoreOutOfMemoryException(); 1967 Local<Value> result = CompileRun( 1968 "var thrown = false;" 1969 "try {" 1970 " ProvokeOutOfMemory();" 1971 "} catch (e) {" 1972 " thrown = true;" 1973 "}"); 1974 // Check for out of memory state. 1975 CHECK(result.IsEmpty()); 1976 CHECK(context->HasOutOfMemoryException()); 1977} 1978 1979 1980TEST(HugeConsStringOutOfMemory) { 1981 // It's not possible to read a snapshot into a heap with different dimensions. 1982 if (i::Snapshot::IsEnabled()) return; 1983 v8::HandleScope scope; 1984 LocalContext context; 1985 // Set heap limits. 1986 static const int K = 1024; 1987 v8::ResourceConstraints constraints; 1988 constraints.set_max_young_space_size(256 * K); 1989 constraints.set_max_old_space_size(2 * K * K); 1990 v8::SetResourceConstraints(&constraints); 1991 1992 // Execute a script that causes out of memory. 1993 v8::V8::IgnoreOutOfMemoryException(); 1994 1995 // Build huge string. This should fail with out of memory exception. 1996 Local<Value> result = CompileRun( 1997 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();" 1998 "for (var i = 0; i < 22; i++) { str = str + str; }"); 1999 2000 // Check for out of memory state. 2001 CHECK(result.IsEmpty()); 2002 CHECK(context->HasOutOfMemoryException()); 2003} 2004 2005 2006THREADED_TEST(ConstructCall) { 2007 v8::HandleScope scope; 2008 LocalContext context; 2009 CompileRun( 2010 "function Foo() {" 2011 " var result = [];" 2012 " for (var i = 0; i < arguments.length; i++) {" 2013 " result.push(arguments[i]);" 2014 " }" 2015 " return result;" 2016 "}"); 2017 Local<Function> Foo = 2018 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 2019 2020 v8::Handle<Value>* args0 = NULL; 2021 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0)); 2022 CHECK_EQ(0, a0->Length()); 2023 2024 v8::Handle<Value> args1[] = { v8_num(1.1) }; 2025 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1)); 2026 CHECK_EQ(1, a1->Length()); 2027 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 2028 2029 v8::Handle<Value> args2[] = { v8_num(2.2), 2030 v8_num(3.3) }; 2031 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2)); 2032 CHECK_EQ(2, a2->Length()); 2033 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 2034 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 2035 2036 v8::Handle<Value> args3[] = { v8_num(4.4), 2037 v8_num(5.5), 2038 v8_num(6.6) }; 2039 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3)); 2040 CHECK_EQ(3, a3->Length()); 2041 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 2042 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 2043 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 2044 2045 v8::Handle<Value> args4[] = { v8_num(7.7), 2046 v8_num(8.8), 2047 v8_num(9.9), 2048 v8_num(10.11) }; 2049 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4)); 2050 CHECK_EQ(4, a4->Length()); 2051 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 2052 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 2053 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 2054 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 2055} 2056 2057 2058static void CheckUncle(v8::TryCatch* try_catch) { 2059 CHECK(try_catch->HasCaught()); 2060 String::AsciiValue str_value(try_catch->Exception()); 2061 CHECK_EQ(*str_value, "uncle?"); 2062 try_catch->Reset(); 2063} 2064 2065 2066THREADED_TEST(ConversionNumber) { 2067 v8::HandleScope scope; 2068 LocalContext env; 2069 // Very large number. 2070 CompileRun("var obj = Math.pow(2,32) * 1237;"); 2071 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2072 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value()); 2073 CHECK_EQ(0, obj->ToInt32()->Value()); 2074 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned. 2075 // Large number. 2076 CompileRun("var obj = -1234567890123;"); 2077 obj = env->Global()->Get(v8_str("obj")); 2078 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value()); 2079 CHECK_EQ(-1912276171, obj->ToInt32()->Value()); 2080 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT 2081 // Small positive integer. 2082 CompileRun("var obj = 42;"); 2083 obj = env->Global()->Get(v8_str("obj")); 2084 CHECK_EQ(42.0, obj->ToNumber()->Value()); 2085 CHECK_EQ(42, obj->ToInt32()->Value()); 2086 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 2087 // Negative integer. 2088 CompileRun("var obj = -37;"); 2089 obj = env->Global()->Get(v8_str("obj")); 2090 CHECK_EQ(-37.0, obj->ToNumber()->Value()); 2091 CHECK_EQ(-37, obj->ToInt32()->Value()); 2092 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT 2093 // Positive non-int32 integer. 2094 CompileRun("var obj = 0x81234567;"); 2095 obj = env->Global()->Get(v8_str("obj")); 2096 CHECK_EQ(2166572391.0, obj->ToNumber()->Value()); 2097 CHECK_EQ(-2128394905, obj->ToInt32()->Value()); 2098 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT 2099 // Fraction. 2100 CompileRun("var obj = 42.3;"); 2101 obj = env->Global()->Get(v8_str("obj")); 2102 CHECK_EQ(42.3, obj->ToNumber()->Value()); 2103 CHECK_EQ(42, obj->ToInt32()->Value()); 2104 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 2105 // Large negative fraction. 2106 CompileRun("var obj = -5726623061.75;"); 2107 obj = env->Global()->Get(v8_str("obj")); 2108 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value()); 2109 CHECK_EQ(-1431655765, obj->ToInt32()->Value()); 2110 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT 2111} 2112 2113 2114THREADED_TEST(isNumberType) { 2115 v8::HandleScope scope; 2116 LocalContext env; 2117 // Very large number. 2118 CompileRun("var obj = Math.pow(2,32) * 1237;"); 2119 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2120 CHECK(!obj->IsInt32()); 2121 CHECK(!obj->IsUint32()); 2122 // Large negative number. 2123 CompileRun("var obj = -1234567890123;"); 2124 obj = env->Global()->Get(v8_str("obj")); 2125 CHECK(!obj->IsInt32()); 2126 CHECK(!obj->IsUint32()); 2127 // Small positive integer. 2128 CompileRun("var obj = 42;"); 2129 obj = env->Global()->Get(v8_str("obj")); 2130 CHECK(obj->IsInt32()); 2131 CHECK(obj->IsUint32()); 2132 // Negative integer. 2133 CompileRun("var obj = -37;"); 2134 obj = env->Global()->Get(v8_str("obj")); 2135 CHECK(obj->IsInt32()); 2136 CHECK(!obj->IsUint32()); 2137 // Positive non-int32 integer. 2138 CompileRun("var obj = 0x81234567;"); 2139 obj = env->Global()->Get(v8_str("obj")); 2140 CHECK(!obj->IsInt32()); 2141 CHECK(obj->IsUint32()); 2142 // Fraction. 2143 CompileRun("var obj = 42.3;"); 2144 obj = env->Global()->Get(v8_str("obj")); 2145 CHECK(!obj->IsInt32()); 2146 CHECK(!obj->IsUint32()); 2147 // Large negative fraction. 2148 CompileRun("var obj = -5726623061.75;"); 2149 obj = env->Global()->Get(v8_str("obj")); 2150 CHECK(!obj->IsInt32()); 2151 CHECK(!obj->IsUint32()); 2152} 2153 2154 2155THREADED_TEST(ConversionException) { 2156 v8::HandleScope scope; 2157 LocalContext env; 2158 CompileRun( 2159 "function TestClass() { };" 2160 "TestClass.prototype.toString = function () { throw 'uncle?'; };" 2161 "var obj = new TestClass();"); 2162 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2163 2164 v8::TryCatch try_catch; 2165 2166 Local<Value> to_string_result = obj->ToString(); 2167 CHECK(to_string_result.IsEmpty()); 2168 CheckUncle(&try_catch); 2169 2170 Local<Value> to_number_result = obj->ToNumber(); 2171 CHECK(to_number_result.IsEmpty()); 2172 CheckUncle(&try_catch); 2173 2174 Local<Value> to_integer_result = obj->ToInteger(); 2175 CHECK(to_integer_result.IsEmpty()); 2176 CheckUncle(&try_catch); 2177 2178 Local<Value> to_uint32_result = obj->ToUint32(); 2179 CHECK(to_uint32_result.IsEmpty()); 2180 CheckUncle(&try_catch); 2181 2182 Local<Value> to_int32_result = obj->ToInt32(); 2183 CHECK(to_int32_result.IsEmpty()); 2184 CheckUncle(&try_catch); 2185 2186 Local<Value> to_object_result = v8::Undefined()->ToObject(); 2187 CHECK(to_object_result.IsEmpty()); 2188 CHECK(try_catch.HasCaught()); 2189 try_catch.Reset(); 2190 2191 int32_t int32_value = obj->Int32Value(); 2192 CHECK_EQ(0, int32_value); 2193 CheckUncle(&try_catch); 2194 2195 uint32_t uint32_value = obj->Uint32Value(); 2196 CHECK_EQ(0, uint32_value); 2197 CheckUncle(&try_catch); 2198 2199 double number_value = obj->NumberValue(); 2200 CHECK_NE(0, IsNaN(number_value)); 2201 CheckUncle(&try_catch); 2202 2203 int64_t integer_value = obj->IntegerValue(); 2204 CHECK_EQ(0.0, static_cast<double>(integer_value)); 2205 CheckUncle(&try_catch); 2206} 2207 2208 2209v8::Handle<Value> ThrowFromC(const v8::Arguments& args) { 2210 ApiTestFuzzer::Fuzz(); 2211 return v8::ThrowException(v8_str("konto")); 2212} 2213 2214 2215v8::Handle<Value> CCatcher(const v8::Arguments& args) { 2216 if (args.Length() < 1) return v8::Boolean::New(false); 2217 v8::HandleScope scope; 2218 v8::TryCatch try_catch; 2219 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run(); 2220 CHECK(!try_catch.HasCaught() || result.IsEmpty()); 2221 return v8::Boolean::New(try_catch.HasCaught()); 2222} 2223 2224 2225THREADED_TEST(APICatch) { 2226 v8::HandleScope scope; 2227 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2228 templ->Set(v8_str("ThrowFromC"), 2229 v8::FunctionTemplate::New(ThrowFromC)); 2230 LocalContext context(0, templ); 2231 CompileRun( 2232 "var thrown = false;" 2233 "try {" 2234 " ThrowFromC();" 2235 "} catch (e) {" 2236 " thrown = true;" 2237 "}"); 2238 Local<Value> thrown = context->Global()->Get(v8_str("thrown")); 2239 CHECK(thrown->BooleanValue()); 2240} 2241 2242 2243THREADED_TEST(APIThrowTryCatch) { 2244 v8::HandleScope scope; 2245 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2246 templ->Set(v8_str("ThrowFromC"), 2247 v8::FunctionTemplate::New(ThrowFromC)); 2248 LocalContext context(0, templ); 2249 v8::TryCatch try_catch; 2250 CompileRun("ThrowFromC();"); 2251 CHECK(try_catch.HasCaught()); 2252} 2253 2254 2255// Test that a try-finally block doesn't shadow a try-catch block 2256// when setting up an external handler. 2257// 2258// BUG(271): Some of the exception propagation does not work on the 2259// ARM simulator because the simulator separates the C++ stack and the 2260// JS stack. This test therefore fails on the simulator. The test is 2261// not threaded to allow the threading tests to run on the simulator. 2262TEST(TryCatchInTryFinally) { 2263 v8::HandleScope scope; 2264 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2265 templ->Set(v8_str("CCatcher"), 2266 v8::FunctionTemplate::New(CCatcher)); 2267 LocalContext context(0, templ); 2268 Local<Value> result = CompileRun("try {" 2269 " try {" 2270 " CCatcher('throw 7;');" 2271 " } finally {" 2272 " }" 2273 "} catch (e) {" 2274 "}"); 2275 CHECK(result->IsTrue()); 2276} 2277 2278 2279static void receive_message(v8::Handle<v8::Message> message, 2280 v8::Handle<v8::Value> data) { 2281 message->Get(); 2282 message_received = true; 2283} 2284 2285 2286TEST(APIThrowMessage) { 2287 message_received = false; 2288 v8::HandleScope scope; 2289 v8::V8::AddMessageListener(receive_message); 2290 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2291 templ->Set(v8_str("ThrowFromC"), 2292 v8::FunctionTemplate::New(ThrowFromC)); 2293 LocalContext context(0, templ); 2294 CompileRun("ThrowFromC();"); 2295 CHECK(message_received); 2296 v8::V8::RemoveMessageListeners(check_message); 2297} 2298 2299 2300TEST(APIThrowMessageAndVerboseTryCatch) { 2301 message_received = false; 2302 v8::HandleScope scope; 2303 v8::V8::AddMessageListener(receive_message); 2304 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2305 templ->Set(v8_str("ThrowFromC"), 2306 v8::FunctionTemplate::New(ThrowFromC)); 2307 LocalContext context(0, templ); 2308 v8::TryCatch try_catch; 2309 try_catch.SetVerbose(true); 2310 Local<Value> result = CompileRun("ThrowFromC();"); 2311 CHECK(try_catch.HasCaught()); 2312 CHECK(result.IsEmpty()); 2313 CHECK(message_received); 2314 v8::V8::RemoveMessageListeners(check_message); 2315} 2316 2317 2318THREADED_TEST(ExternalScriptException) { 2319 v8::HandleScope scope; 2320 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2321 templ->Set(v8_str("ThrowFromC"), 2322 v8::FunctionTemplate::New(ThrowFromC)); 2323 LocalContext context(0, templ); 2324 2325 v8::TryCatch try_catch; 2326 Local<Script> script 2327 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';")); 2328 Local<Value> result = script->Run(); 2329 CHECK(result.IsEmpty()); 2330 CHECK(try_catch.HasCaught()); 2331 String::AsciiValue exception_value(try_catch.Exception()); 2332 CHECK_EQ("konto", *exception_value); 2333} 2334 2335 2336 2337v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) { 2338 ApiTestFuzzer::Fuzz(); 2339 CHECK_EQ(4, args.Length()); 2340 int count = args[0]->Int32Value(); 2341 int cInterval = args[2]->Int32Value(); 2342 if (count == 0) { 2343 return v8::ThrowException(v8_str("FromC")); 2344 } else { 2345 Local<v8::Object> global = Context::GetCurrent()->Global(); 2346 Local<Value> fun = global->Get(v8_str("JSThrowCountDown")); 2347 v8::Handle<Value> argv[] = { v8_num(count - 1), 2348 args[1], 2349 args[2], 2350 args[3] }; 2351 if (count % cInterval == 0) { 2352 v8::TryCatch try_catch; 2353 Local<Value> result = fun.As<Function>()->Call(global, 4, argv); 2354 int expected = args[3]->Int32Value(); 2355 if (try_catch.HasCaught()) { 2356 CHECK_EQ(expected, count); 2357 CHECK(result.IsEmpty()); 2358 CHECK(!i::Top::has_scheduled_exception()); 2359 } else { 2360 CHECK_NE(expected, count); 2361 } 2362 return result; 2363 } else { 2364 return fun.As<Function>()->Call(global, 4, argv); 2365 } 2366 } 2367} 2368 2369 2370v8::Handle<Value> JSCheck(const v8::Arguments& args) { 2371 ApiTestFuzzer::Fuzz(); 2372 CHECK_EQ(3, args.Length()); 2373 bool equality = args[0]->BooleanValue(); 2374 int count = args[1]->Int32Value(); 2375 int expected = args[2]->Int32Value(); 2376 if (equality) { 2377 CHECK_EQ(count, expected); 2378 } else { 2379 CHECK_NE(count, expected); 2380 } 2381 return v8::Undefined(); 2382} 2383 2384 2385THREADED_TEST(EvalInTryFinally) { 2386 v8::HandleScope scope; 2387 LocalContext context; 2388 v8::TryCatch try_catch; 2389 CompileRun("(function() {" 2390 " try {" 2391 " eval('asldkf (*&^&*^');" 2392 " } finally {" 2393 " return;" 2394 " }" 2395 "})()"); 2396 CHECK(!try_catch.HasCaught()); 2397} 2398 2399 2400// This test works by making a stack of alternating JavaScript and C 2401// activations. These activations set up exception handlers with regular 2402// intervals, one interval for C activations and another for JavaScript 2403// activations. When enough activations have been created an exception is 2404// thrown and we check that the right activation catches the exception and that 2405// no other activations do. The right activation is always the topmost one with 2406// a handler, regardless of whether it is in JavaScript or C. 2407// 2408// The notation used to describe a test case looks like this: 2409// 2410// *JS[4] *C[3] @JS[2] C[1] JS[0] 2411// 2412// Each entry is an activation, either JS or C. The index is the count at that 2413// level. Stars identify activations with exception handlers, the @ identifies 2414// the exception handler that should catch the exception. 2415// 2416// BUG(271): Some of the exception propagation does not work on the 2417// ARM simulator because the simulator separates the C++ stack and the 2418// JS stack. This test therefore fails on the simulator. The test is 2419// not threaded to allow the threading tests to run on the simulator. 2420TEST(ExceptionOrder) { 2421 v8::HandleScope scope; 2422 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2423 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck)); 2424 templ->Set(v8_str("CThrowCountDown"), 2425 v8::FunctionTemplate::New(CThrowCountDown)); 2426 LocalContext context(0, templ); 2427 CompileRun( 2428 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {" 2429 " if (count == 0) throw 'FromJS';" 2430 " if (count % jsInterval == 0) {" 2431 " try {" 2432 " var value = CThrowCountDown(count - 1," 2433 " jsInterval," 2434 " cInterval," 2435 " expected);" 2436 " check(false, count, expected);" 2437 " return value;" 2438 " } catch (e) {" 2439 " check(true, count, expected);" 2440 " }" 2441 " } else {" 2442 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);" 2443 " }" 2444 "}"); 2445 Local<Function> fun = 2446 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown"))); 2447 2448 const int argc = 4; 2449 // count jsInterval cInterval expected 2450 2451 // *JS[4] *C[3] @JS[2] C[1] JS[0] 2452 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) }; 2453 fun->Call(fun, argc, a0); 2454 2455 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0] 2456 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) }; 2457 fun->Call(fun, argc, a1); 2458 2459 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0] 2460 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) }; 2461 fun->Call(fun, argc, a2); 2462 2463 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0] 2464 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) }; 2465 fun->Call(fun, argc, a3); 2466 2467 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0] 2468 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) }; 2469 fun->Call(fun, argc, a4); 2470 2471 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0] 2472 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) }; 2473 fun->Call(fun, argc, a5); 2474} 2475 2476 2477v8::Handle<Value> ThrowValue(const v8::Arguments& args) { 2478 ApiTestFuzzer::Fuzz(); 2479 CHECK_EQ(1, args.Length()); 2480 return v8::ThrowException(args[0]); 2481} 2482 2483 2484THREADED_TEST(ThrowValues) { 2485 v8::HandleScope scope; 2486 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2487 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue)); 2488 LocalContext context(0, templ); 2489 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 2490 "function Run(obj) {" 2491 " try {" 2492 " Throw(obj);" 2493 " } catch (e) {" 2494 " return e;" 2495 " }" 2496 " return 'no exception';" 2497 "}" 2498 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];")); 2499 CHECK_EQ(5, result->Length()); 2500 CHECK(result->Get(v8::Integer::New(0))->IsString()); 2501 CHECK(result->Get(v8::Integer::New(1))->IsNumber()); 2502 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value()); 2503 CHECK(result->Get(v8::Integer::New(2))->IsNumber()); 2504 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value()); 2505 CHECK(result->Get(v8::Integer::New(3))->IsNull()); 2506 CHECK(result->Get(v8::Integer::New(4))->IsUndefined()); 2507} 2508 2509 2510THREADED_TEST(CatchZero) { 2511 v8::HandleScope scope; 2512 LocalContext context; 2513 v8::TryCatch try_catch; 2514 CHECK(!try_catch.HasCaught()); 2515 Script::Compile(v8_str("throw 10"))->Run(); 2516 CHECK(try_catch.HasCaught()); 2517 CHECK_EQ(10, try_catch.Exception()->Int32Value()); 2518 try_catch.Reset(); 2519 CHECK(!try_catch.HasCaught()); 2520 Script::Compile(v8_str("throw 0"))->Run(); 2521 CHECK(try_catch.HasCaught()); 2522 CHECK_EQ(0, try_catch.Exception()->Int32Value()); 2523} 2524 2525 2526THREADED_TEST(CatchExceptionFromWith) { 2527 v8::HandleScope scope; 2528 LocalContext context; 2529 v8::TryCatch try_catch; 2530 CHECK(!try_catch.HasCaught()); 2531 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run(); 2532 CHECK(try_catch.HasCaught()); 2533} 2534 2535 2536THREADED_TEST(Equality) { 2537 v8::HandleScope scope; 2538 LocalContext context; 2539 // Check that equality works at all before relying on CHECK_EQ 2540 CHECK(v8_str("a")->Equals(v8_str("a"))); 2541 CHECK(!v8_str("a")->Equals(v8_str("b"))); 2542 2543 CHECK_EQ(v8_str("a"), v8_str("a")); 2544 CHECK_NE(v8_str("a"), v8_str("b")); 2545 CHECK_EQ(v8_num(1), v8_num(1)); 2546 CHECK_EQ(v8_num(1.00), v8_num(1)); 2547 CHECK_NE(v8_num(1), v8_num(2)); 2548 2549 // Assume String is not symbol. 2550 CHECK(v8_str("a")->StrictEquals(v8_str("a"))); 2551 CHECK(!v8_str("a")->StrictEquals(v8_str("b"))); 2552 CHECK(!v8_str("5")->StrictEquals(v8_num(5))); 2553 CHECK(v8_num(1)->StrictEquals(v8_num(1))); 2554 CHECK(!v8_num(1)->StrictEquals(v8_num(2))); 2555 CHECK(v8_num(0)->StrictEquals(v8_num(-0))); 2556 Local<Value> not_a_number = v8_num(i::OS::nan_value()); 2557 CHECK(!not_a_number->StrictEquals(not_a_number)); 2558 CHECK(v8::False()->StrictEquals(v8::False())); 2559 CHECK(!v8::False()->StrictEquals(v8::Undefined())); 2560 2561 v8::Handle<v8::Object> obj = v8::Object::New(); 2562 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj); 2563 CHECK(alias->StrictEquals(obj)); 2564 alias.Dispose(); 2565} 2566 2567 2568THREADED_TEST(MultiRun) { 2569 v8::HandleScope scope; 2570 LocalContext context; 2571 Local<Script> script = Script::Compile(v8_str("x")); 2572 for (int i = 0; i < 10; i++) 2573 script->Run(); 2574} 2575 2576 2577static v8::Handle<Value> GetXValue(Local<String> name, 2578 const AccessorInfo& info) { 2579 ApiTestFuzzer::Fuzz(); 2580 CHECK_EQ(info.Data(), v8_str("donut")); 2581 CHECK_EQ(name, v8_str("x")); 2582 return name; 2583} 2584 2585 2586THREADED_TEST(SimplePropertyRead) { 2587 v8::HandleScope scope; 2588 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2589 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2590 LocalContext context; 2591 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2592 Local<Script> script = Script::Compile(v8_str("obj.x")); 2593 for (int i = 0; i < 10; i++) { 2594 Local<Value> result = script->Run(); 2595 CHECK_EQ(result, v8_str("x")); 2596 } 2597} 2598 2599THREADED_TEST(DefinePropertyOnAPIAccessor) { 2600 v8::HandleScope scope; 2601 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2602 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2603 LocalContext context; 2604 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2605 2606 // Uses getOwnPropertyDescriptor to check the configurable status 2607 Local<Script> script_desc 2608 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( " 2609 "obj, 'x');" 2610 "prop.configurable;")); 2611 Local<Value> result = script_desc->Run(); 2612 CHECK_EQ(result->BooleanValue(), true); 2613 2614 // Redefine get - but still configurable 2615 Local<Script> script_define 2616 = Script::Compile(v8_str("var desc = { get: function(){return 42; }," 2617 " configurable: true };" 2618 "Object.defineProperty(obj, 'x', desc);" 2619 "obj.x")); 2620 result = script_define->Run(); 2621 CHECK_EQ(result, v8_num(42)); 2622 2623 // Check that the accessor is still configurable 2624 result = script_desc->Run(); 2625 CHECK_EQ(result->BooleanValue(), true); 2626 2627 // Redefine to a non-configurable 2628 script_define 2629 = Script::Compile(v8_str("var desc = { get: function(){return 43; }," 2630 " configurable: false };" 2631 "Object.defineProperty(obj, 'x', desc);" 2632 "obj.x")); 2633 result = script_define->Run(); 2634 CHECK_EQ(result, v8_num(43)); 2635 result = script_desc->Run(); 2636 CHECK_EQ(result->BooleanValue(), false); 2637 2638 // Make sure that it is not possible to redefine again 2639 v8::TryCatch try_catch; 2640 result = script_define->Run(); 2641 CHECK(try_catch.HasCaught()); 2642 String::AsciiValue exception_value(try_catch.Exception()); 2643 CHECK_EQ(*exception_value, 2644 "TypeError: Cannot redefine property: defineProperty"); 2645} 2646 2647THREADED_TEST(DefinePropertyOnDefineGetterSetter) { 2648 v8::HandleScope scope; 2649 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2650 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2651 LocalContext context; 2652 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2653 2654 Local<Script> script_desc = Script::Compile(v8_str("var prop =" 2655 "Object.getOwnPropertyDescriptor( " 2656 "obj, 'x');" 2657 "prop.configurable;")); 2658 Local<Value> result = script_desc->Run(); 2659 CHECK_EQ(result->BooleanValue(), true); 2660 2661 Local<Script> script_define = 2662 Script::Compile(v8_str("var desc = {get: function(){return 42; }," 2663 " configurable: true };" 2664 "Object.defineProperty(obj, 'x', desc);" 2665 "obj.x")); 2666 result = script_define->Run(); 2667 CHECK_EQ(result, v8_num(42)); 2668 2669 2670 result = script_desc->Run(); 2671 CHECK_EQ(result->BooleanValue(), true); 2672 2673 2674 script_define = 2675 Script::Compile(v8_str("var desc = {get: function(){return 43; }," 2676 " configurable: false };" 2677 "Object.defineProperty(obj, 'x', desc);" 2678 "obj.x")); 2679 result = script_define->Run(); 2680 CHECK_EQ(result, v8_num(43)); 2681 result = script_desc->Run(); 2682 2683 CHECK_EQ(result->BooleanValue(), false); 2684 2685 v8::TryCatch try_catch; 2686 result = script_define->Run(); 2687 CHECK(try_catch.HasCaught()); 2688 String::AsciiValue exception_value(try_catch.Exception()); 2689 CHECK_EQ(*exception_value, 2690 "TypeError: Cannot redefine property: defineProperty"); 2691} 2692 2693 2694static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context, 2695 char const* name) { 2696 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name))); 2697} 2698 2699 2700THREADED_TEST(DefineAPIAccessorOnObject) { 2701 v8::HandleScope scope; 2702 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2703 LocalContext context; 2704 2705 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 2706 CompileRun("var obj2 = {};"); 2707 2708 CHECK(CompileRun("obj1.x")->IsUndefined()); 2709 CHECK(CompileRun("obj2.x")->IsUndefined()); 2710 2711 CHECK(GetGlobalProperty(&context, "obj1")-> 2712 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2713 2714 ExpectString("obj1.x", "x"); 2715 CHECK(CompileRun("obj2.x")->IsUndefined()); 2716 2717 CHECK(GetGlobalProperty(&context, "obj2")-> 2718 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2719 2720 ExpectString("obj1.x", "x"); 2721 ExpectString("obj2.x", "x"); 2722 2723 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2724 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2725 2726 CompileRun("Object.defineProperty(obj1, 'x'," 2727 "{ get: function() { return 'y'; }, configurable: true })"); 2728 2729 ExpectString("obj1.x", "y"); 2730 ExpectString("obj2.x", "x"); 2731 2732 CompileRun("Object.defineProperty(obj2, 'x'," 2733 "{ get: function() { return 'y'; }, configurable: true })"); 2734 2735 ExpectString("obj1.x", "y"); 2736 ExpectString("obj2.x", "y"); 2737 2738 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2739 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2740 2741 CHECK(GetGlobalProperty(&context, "obj1")-> 2742 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2743 CHECK(GetGlobalProperty(&context, "obj2")-> 2744 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2745 2746 ExpectString("obj1.x", "x"); 2747 ExpectString("obj2.x", "x"); 2748 2749 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2750 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2751 2752 // Define getters/setters, but now make them not configurable. 2753 CompileRun("Object.defineProperty(obj1, 'x'," 2754 "{ get: function() { return 'z'; }, configurable: false })"); 2755 CompileRun("Object.defineProperty(obj2, 'x'," 2756 "{ get: function() { return 'z'; }, configurable: false })"); 2757 2758 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2759 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2760 2761 ExpectString("obj1.x", "z"); 2762 ExpectString("obj2.x", "z"); 2763 2764 CHECK(!GetGlobalProperty(&context, "obj1")-> 2765 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2766 CHECK(!GetGlobalProperty(&context, "obj2")-> 2767 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2768 2769 ExpectString("obj1.x", "z"); 2770 ExpectString("obj2.x", "z"); 2771} 2772 2773 2774THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) { 2775 v8::HandleScope scope; 2776 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2777 LocalContext context; 2778 2779 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 2780 CompileRun("var obj2 = {};"); 2781 2782 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 2783 v8_str("x"), 2784 GetXValue, NULL, 2785 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 2786 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 2787 v8_str("x"), 2788 GetXValue, NULL, 2789 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 2790 2791 ExpectString("obj1.x", "x"); 2792 ExpectString("obj2.x", "x"); 2793 2794 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2795 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2796 2797 CHECK(!GetGlobalProperty(&context, "obj1")-> 2798 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2799 CHECK(!GetGlobalProperty(&context, "obj2")-> 2800 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2801 2802 { 2803 v8::TryCatch try_catch; 2804 CompileRun("Object.defineProperty(obj1, 'x'," 2805 "{get: function() { return 'func'; }})"); 2806 CHECK(try_catch.HasCaught()); 2807 String::AsciiValue exception_value(try_catch.Exception()); 2808 CHECK_EQ(*exception_value, 2809 "TypeError: Cannot redefine property: defineProperty"); 2810 } 2811 { 2812 v8::TryCatch try_catch; 2813 CompileRun("Object.defineProperty(obj2, 'x'," 2814 "{get: function() { return 'func'; }})"); 2815 CHECK(try_catch.HasCaught()); 2816 String::AsciiValue exception_value(try_catch.Exception()); 2817 CHECK_EQ(*exception_value, 2818 "TypeError: Cannot redefine property: defineProperty"); 2819 } 2820} 2821 2822 2823static v8::Handle<Value> Get239Value(Local<String> name, 2824 const AccessorInfo& info) { 2825 ApiTestFuzzer::Fuzz(); 2826 CHECK_EQ(info.Data(), v8_str("donut")); 2827 CHECK_EQ(name, v8_str("239")); 2828 return name; 2829} 2830 2831 2832THREADED_TEST(ElementAPIAccessor) { 2833 v8::HandleScope scope; 2834 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2835 LocalContext context; 2836 2837 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 2838 CompileRun("var obj2 = {};"); 2839 2840 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 2841 v8_str("239"), 2842 Get239Value, NULL, 2843 v8_str("donut"))); 2844 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 2845 v8_str("239"), 2846 Get239Value, NULL, 2847 v8_str("donut"))); 2848 2849 ExpectString("obj1[239]", "239"); 2850 ExpectString("obj2[239]", "239"); 2851 ExpectString("obj1['239']", "239"); 2852 ExpectString("obj2['239']", "239"); 2853} 2854 2855 2856v8::Persistent<Value> xValue; 2857 2858 2859static void SetXValue(Local<String> name, 2860 Local<Value> value, 2861 const AccessorInfo& info) { 2862 CHECK_EQ(value, v8_num(4)); 2863 CHECK_EQ(info.Data(), v8_str("donut")); 2864 CHECK_EQ(name, v8_str("x")); 2865 CHECK(xValue.IsEmpty()); 2866 xValue = v8::Persistent<Value>::New(value); 2867} 2868 2869 2870THREADED_TEST(SimplePropertyWrite) { 2871 v8::HandleScope scope; 2872 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2873 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut")); 2874 LocalContext context; 2875 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2876 Local<Script> script = Script::Compile(v8_str("obj.x = 4")); 2877 for (int i = 0; i < 10; i++) { 2878 CHECK(xValue.IsEmpty()); 2879 script->Run(); 2880 CHECK_EQ(v8_num(4), xValue); 2881 xValue.Dispose(); 2882 xValue = v8::Persistent<Value>(); 2883 } 2884} 2885 2886 2887static v8::Handle<Value> XPropertyGetter(Local<String> property, 2888 const AccessorInfo& info) { 2889 ApiTestFuzzer::Fuzz(); 2890 CHECK(info.Data()->IsUndefined()); 2891 return property; 2892} 2893 2894 2895THREADED_TEST(NamedInterceptorPropertyRead) { 2896 v8::HandleScope scope; 2897 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2898 templ->SetNamedPropertyHandler(XPropertyGetter); 2899 LocalContext context; 2900 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2901 Local<Script> script = Script::Compile(v8_str("obj.x")); 2902 for (int i = 0; i < 10; i++) { 2903 Local<Value> result = script->Run(); 2904 CHECK_EQ(result, v8_str("x")); 2905 } 2906} 2907 2908 2909THREADED_TEST(NamedInterceptorDictionaryIC) { 2910 v8::HandleScope scope; 2911 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2912 templ->SetNamedPropertyHandler(XPropertyGetter); 2913 LocalContext context; 2914 // Create an object with a named interceptor. 2915 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance()); 2916 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x")); 2917 for (int i = 0; i < 10; i++) { 2918 Local<Value> result = script->Run(); 2919 CHECK_EQ(result, v8_str("x")); 2920 } 2921 // Create a slow case object and a function accessing a property in 2922 // that slow case object (with dictionary probing in generated 2923 // code). Then force object with a named interceptor into slow-case, 2924 // pass it to the function, and check that the interceptor is called 2925 // instead of accessing the local property. 2926 Local<Value> result = 2927 CompileRun("function get_x(o) { return o.x; };" 2928 "var obj = { x : 42, y : 0 };" 2929 "delete obj.y;" 2930 "for (var i = 0; i < 10; i++) get_x(obj);" 2931 "interceptor_obj.x = 42;" 2932 "interceptor_obj.y = 10;" 2933 "delete interceptor_obj.y;" 2934 "get_x(interceptor_obj)"); 2935 CHECK_EQ(result, v8_str("x")); 2936} 2937 2938 2939static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property, 2940 const AccessorInfo& info) { 2941 // Set x on the prototype object and do not handle the get request. 2942 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype(); 2943 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23)); 2944 return v8::Handle<Value>(); 2945} 2946 2947 2948// This is a regression test for http://crbug.com/20104. Map 2949// transitions should not interfere with post interceptor lookup. 2950THREADED_TEST(NamedInterceptorMapTransitionRead) { 2951 v8::HandleScope scope; 2952 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(); 2953 Local<v8::ObjectTemplate> instance_template 2954 = function_template->InstanceTemplate(); 2955 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter); 2956 LocalContext context; 2957 context->Global()->Set(v8_str("F"), function_template->GetFunction()); 2958 // Create an instance of F and introduce a map transition for x. 2959 CompileRun("var o = new F(); o.x = 23;"); 2960 // Create an instance of F and invoke the getter. The result should be 23. 2961 Local<Value> result = CompileRun("o = new F(); o.x"); 2962 CHECK_EQ(result->Int32Value(), 23); 2963} 2964 2965 2966static v8::Handle<Value> IndexedPropertyGetter(uint32_t index, 2967 const AccessorInfo& info) { 2968 ApiTestFuzzer::Fuzz(); 2969 if (index == 37) { 2970 return v8::Handle<Value>(v8_num(625)); 2971 } 2972 return v8::Handle<Value>(); 2973} 2974 2975 2976static v8::Handle<Value> IndexedPropertySetter(uint32_t index, 2977 Local<Value> value, 2978 const AccessorInfo& info) { 2979 ApiTestFuzzer::Fuzz(); 2980 if (index == 39) { 2981 return value; 2982 } 2983 return v8::Handle<Value>(); 2984} 2985 2986 2987THREADED_TEST(IndexedInterceptorWithIndexedAccessor) { 2988 v8::HandleScope scope; 2989 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2990 templ->SetIndexedPropertyHandler(IndexedPropertyGetter, 2991 IndexedPropertySetter); 2992 LocalContext context; 2993 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2994 Local<Script> getter_script = Script::Compile(v8_str( 2995 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];")); 2996 Local<Script> setter_script = Script::Compile(v8_str( 2997 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});" 2998 "obj[17] = 23;" 2999 "obj.foo;")); 3000 Local<Script> interceptor_setter_script = Script::Compile(v8_str( 3001 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});" 3002 "obj[39] = 47;" 3003 "obj.foo;")); // This setter should not run, due to the interceptor. 3004 Local<Script> interceptor_getter_script = Script::Compile(v8_str( 3005 "obj[37];")); 3006 Local<Value> result = getter_script->Run(); 3007 CHECK_EQ(v8_num(5), result); 3008 result = setter_script->Run(); 3009 CHECK_EQ(v8_num(23), result); 3010 result = interceptor_setter_script->Run(); 3011 CHECK_EQ(v8_num(23), result); 3012 result = interceptor_getter_script->Run(); 3013 CHECK_EQ(v8_num(625), result); 3014} 3015 3016 3017static v8::Handle<Value> IdentityIndexedPropertyGetter( 3018 uint32_t index, 3019 const AccessorInfo& info) { 3020 return v8::Integer::New(index); 3021} 3022 3023 3024THREADED_TEST(IndexedInterceptorWithNoSetter) { 3025 v8::HandleScope scope; 3026 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3027 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3028 3029 LocalContext context; 3030 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3031 3032 const char* code = 3033 "try {" 3034 " obj[0] = 239;" 3035 " for (var i = 0; i < 100; i++) {" 3036 " var v = obj[0];" 3037 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;" 3038 " }" 3039 " 'PASSED'" 3040 "} catch(e) {" 3041 " e" 3042 "}"; 3043 ExpectString(code, "PASSED"); 3044} 3045 3046 3047THREADED_TEST(IndexedInterceptorWithAccessorCheck) { 3048 v8::HandleScope scope; 3049 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3050 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3051 3052 LocalContext context; 3053 Local<v8::Object> obj = templ->NewInstance(); 3054 obj->TurnOnAccessCheck(); 3055 context->Global()->Set(v8_str("obj"), obj); 3056 3057 const char* code = 3058 "try {" 3059 " for (var i = 0; i < 100; i++) {" 3060 " var v = obj[0];" 3061 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;" 3062 " }" 3063 " 'PASSED'" 3064 "} catch(e) {" 3065 " e" 3066 "}"; 3067 ExpectString(code, "PASSED"); 3068} 3069 3070 3071THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) { 3072 i::FLAG_allow_natives_syntax = true; 3073 v8::HandleScope scope; 3074 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3075 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3076 3077 LocalContext context; 3078 Local<v8::Object> obj = templ->NewInstance(); 3079 context->Global()->Set(v8_str("obj"), obj); 3080 3081 const char* code = 3082 "try {" 3083 " for (var i = 0; i < 100; i++) {" 3084 " var expected = i;" 3085 " if (i == 5) {" 3086 " %EnableAccessChecks(obj);" 3087 " expected = undefined;" 3088 " }" 3089 " var v = obj[i];" 3090 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3091 " if (i == 5) %DisableAccessChecks(obj);" 3092 " }" 3093 " 'PASSED'" 3094 "} catch(e) {" 3095 " e" 3096 "}"; 3097 ExpectString(code, "PASSED"); 3098} 3099 3100 3101THREADED_TEST(IndexedInterceptorWithDifferentIndices) { 3102 v8::HandleScope scope; 3103 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3104 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3105 3106 LocalContext context; 3107 Local<v8::Object> obj = templ->NewInstance(); 3108 context->Global()->Set(v8_str("obj"), obj); 3109 3110 const char* code = 3111 "try {" 3112 " for (var i = 0; i < 100; i++) {" 3113 " var v = obj[i];" 3114 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 3115 " }" 3116 " 'PASSED'" 3117 "} catch(e) {" 3118 " e" 3119 "}"; 3120 ExpectString(code, "PASSED"); 3121} 3122 3123 3124THREADED_TEST(IndexedInterceptorWithNotSmiLookup) { 3125 v8::HandleScope scope; 3126 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3127 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3128 3129 LocalContext context; 3130 Local<v8::Object> obj = templ->NewInstance(); 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 expected = i;" 3137 " if (i == 50) {" 3138 " i = 'foobar';" 3139 " expected = undefined;" 3140 " }" 3141 " var v = obj[i];" 3142 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3143 " }" 3144 " 'PASSED'" 3145 "} catch(e) {" 3146 " e" 3147 "}"; 3148 ExpectString(code, "PASSED"); 3149} 3150 3151 3152THREADED_TEST(IndexedInterceptorGoingMegamorphic) { 3153 v8::HandleScope scope; 3154 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3155 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3156 3157 LocalContext context; 3158 Local<v8::Object> obj = templ->NewInstance(); 3159 context->Global()->Set(v8_str("obj"), obj); 3160 3161 const char* code = 3162 "var original = obj;" 3163 "try {" 3164 " for (var i = 0; i < 100; i++) {" 3165 " var expected = i;" 3166 " if (i == 50) {" 3167 " obj = {50: 'foobar'};" 3168 " expected = 'foobar';" 3169 " }" 3170 " var v = obj[i];" 3171 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3172 " if (i == 50) obj = original;" 3173 " }" 3174 " 'PASSED'" 3175 "} catch(e) {" 3176 " e" 3177 "}"; 3178 ExpectString(code, "PASSED"); 3179} 3180 3181 3182THREADED_TEST(IndexedInterceptorReceiverTurningSmi) { 3183 v8::HandleScope scope; 3184 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3185 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3186 3187 LocalContext context; 3188 Local<v8::Object> obj = templ->NewInstance(); 3189 context->Global()->Set(v8_str("obj"), obj); 3190 3191 const char* code = 3192 "var original = obj;" 3193 "try {" 3194 " for (var i = 0; i < 100; i++) {" 3195 " var expected = i;" 3196 " if (i == 5) {" 3197 " obj = 239;" 3198 " expected = undefined;" 3199 " }" 3200 " var v = obj[i];" 3201 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3202 " if (i == 5) obj = original;" 3203 " }" 3204 " 'PASSED'" 3205 "} catch(e) {" 3206 " e" 3207 "}"; 3208 ExpectString(code, "PASSED"); 3209} 3210 3211 3212THREADED_TEST(IndexedInterceptorOnProto) { 3213 v8::HandleScope scope; 3214 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3215 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3216 3217 LocalContext context; 3218 Local<v8::Object> obj = templ->NewInstance(); 3219 context->Global()->Set(v8_str("obj"), obj); 3220 3221 const char* code = 3222 "var o = {__proto__: obj};" 3223 "try {" 3224 " for (var i = 0; i < 100; i++) {" 3225 " var v = o[i];" 3226 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 3227 " }" 3228 " 'PASSED'" 3229 "} catch(e) {" 3230 " e" 3231 "}"; 3232 ExpectString(code, "PASSED"); 3233} 3234 3235 3236THREADED_TEST(MultiContexts) { 3237 v8::HandleScope scope; 3238 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New(); 3239 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler)); 3240 3241 Local<String> password = v8_str("Password"); 3242 3243 // Create an environment 3244 LocalContext context0(0, templ); 3245 context0->SetSecurityToken(password); 3246 v8::Handle<v8::Object> global0 = context0->Global(); 3247 global0->Set(v8_str("custom"), v8_num(1234)); 3248 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 3249 3250 // Create an independent environment 3251 LocalContext context1(0, templ); 3252 context1->SetSecurityToken(password); 3253 v8::Handle<v8::Object> global1 = context1->Global(); 3254 global1->Set(v8_str("custom"), v8_num(1234)); 3255 CHECK_NE(global0, global1); 3256 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 3257 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value()); 3258 3259 // Now create a new context with the old global 3260 LocalContext context2(0, templ, global1); 3261 context2->SetSecurityToken(password); 3262 v8::Handle<v8::Object> global2 = context2->Global(); 3263 CHECK_EQ(global1, global2); 3264 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value()); 3265 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value()); 3266} 3267 3268 3269THREADED_TEST(FunctionPrototypeAcrossContexts) { 3270 // Make sure that functions created by cloning boilerplates cannot 3271 // communicate through their __proto__ field. 3272 3273 v8::HandleScope scope; 3274 3275 LocalContext env0; 3276 v8::Handle<v8::Object> global0 = 3277 env0->Global(); 3278 v8::Handle<v8::Object> object0 = 3279 global0->Get(v8_str("Object")).As<v8::Object>(); 3280 v8::Handle<v8::Object> tostring0 = 3281 object0->Get(v8_str("toString")).As<v8::Object>(); 3282 v8::Handle<v8::Object> proto0 = 3283 tostring0->Get(v8_str("__proto__")).As<v8::Object>(); 3284 proto0->Set(v8_str("custom"), v8_num(1234)); 3285 3286 LocalContext env1; 3287 v8::Handle<v8::Object> global1 = 3288 env1->Global(); 3289 v8::Handle<v8::Object> object1 = 3290 global1->Get(v8_str("Object")).As<v8::Object>(); 3291 v8::Handle<v8::Object> tostring1 = 3292 object1->Get(v8_str("toString")).As<v8::Object>(); 3293 v8::Handle<v8::Object> proto1 = 3294 tostring1->Get(v8_str("__proto__")).As<v8::Object>(); 3295 CHECK(!proto1->Has(v8_str("custom"))); 3296} 3297 3298 3299THREADED_TEST(Regress892105) { 3300 // Make sure that object and array literals created by cloning 3301 // boilerplates cannot communicate through their __proto__ 3302 // field. This is rather difficult to check, but we try to add stuff 3303 // to Object.prototype and Array.prototype and create a new 3304 // environment. This should succeed. 3305 3306 v8::HandleScope scope; 3307 3308 Local<String> source = v8_str("Object.prototype.obj = 1234;" 3309 "Array.prototype.arr = 4567;" 3310 "8901"); 3311 3312 LocalContext env0; 3313 Local<Script> script0 = Script::Compile(source); 3314 CHECK_EQ(8901.0, script0->Run()->NumberValue()); 3315 3316 LocalContext env1; 3317 Local<Script> script1 = Script::Compile(source); 3318 CHECK_EQ(8901.0, script1->Run()->NumberValue()); 3319} 3320 3321 3322THREADED_TEST(UndetectableObject) { 3323 v8::HandleScope scope; 3324 LocalContext env; 3325 3326 Local<v8::FunctionTemplate> desc = 3327 v8::FunctionTemplate::New(0, v8::Handle<Value>()); 3328 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 3329 3330 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 3331 env->Global()->Set(v8_str("undetectable"), obj); 3332 3333 ExpectString("undetectable.toString()", "[object Object]"); 3334 ExpectString("typeof undetectable", "undefined"); 3335 ExpectString("typeof(undetectable)", "undefined"); 3336 ExpectBoolean("typeof undetectable == 'undefined'", true); 3337 ExpectBoolean("typeof undetectable == 'object'", false); 3338 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 3339 ExpectBoolean("!undetectable", true); 3340 3341 ExpectObject("true&&undetectable", obj); 3342 ExpectBoolean("false&&undetectable", false); 3343 ExpectBoolean("true||undetectable", true); 3344 ExpectObject("false||undetectable", obj); 3345 3346 ExpectObject("undetectable&&true", obj); 3347 ExpectObject("undetectable&&false", obj); 3348 ExpectBoolean("undetectable||true", true); 3349 ExpectBoolean("undetectable||false", false); 3350 3351 ExpectBoolean("undetectable==null", true); 3352 ExpectBoolean("null==undetectable", true); 3353 ExpectBoolean("undetectable==undefined", true); 3354 ExpectBoolean("undefined==undetectable", true); 3355 ExpectBoolean("undetectable==undetectable", true); 3356 3357 3358 ExpectBoolean("undetectable===null", false); 3359 ExpectBoolean("null===undetectable", false); 3360 ExpectBoolean("undetectable===undefined", false); 3361 ExpectBoolean("undefined===undetectable", false); 3362 ExpectBoolean("undetectable===undetectable", true); 3363} 3364 3365 3366 3367THREADED_TEST(ExtensibleOnUndetectable) { 3368 v8::HandleScope scope; 3369 LocalContext env; 3370 3371 Local<v8::FunctionTemplate> desc = 3372 v8::FunctionTemplate::New(0, v8::Handle<Value>()); 3373 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 3374 3375 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 3376 env->Global()->Set(v8_str("undetectable"), obj); 3377 3378 Local<String> source = v8_str("undetectable.x = 42;" 3379 "undetectable.x"); 3380 3381 Local<Script> script = Script::Compile(source); 3382 3383 CHECK_EQ(v8::Integer::New(42), script->Run()); 3384 3385 ExpectBoolean("Object.isExtensible(undetectable)", true); 3386 3387 source = v8_str("Object.preventExtensions(undetectable);"); 3388 script = Script::Compile(source); 3389 script->Run(); 3390 ExpectBoolean("Object.isExtensible(undetectable)", false); 3391 3392 source = v8_str("undetectable.y = 2000;"); 3393 script = Script::Compile(source); 3394 v8::TryCatch try_catch; 3395 Local<Value> result = script->Run(); 3396 CHECK(result.IsEmpty()); 3397 CHECK(try_catch.HasCaught()); 3398} 3399 3400 3401 3402THREADED_TEST(UndetectableString) { 3403 v8::HandleScope scope; 3404 LocalContext env; 3405 3406 Local<String> obj = String::NewUndetectable("foo"); 3407 env->Global()->Set(v8_str("undetectable"), obj); 3408 3409 ExpectString("undetectable", "foo"); 3410 ExpectString("typeof undetectable", "undefined"); 3411 ExpectString("typeof(undetectable)", "undefined"); 3412 ExpectBoolean("typeof undetectable == 'undefined'", true); 3413 ExpectBoolean("typeof undetectable == 'string'", false); 3414 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 3415 ExpectBoolean("!undetectable", true); 3416 3417 ExpectObject("true&&undetectable", obj); 3418 ExpectBoolean("false&&undetectable", false); 3419 ExpectBoolean("true||undetectable", true); 3420 ExpectObject("false||undetectable", obj); 3421 3422 ExpectObject("undetectable&&true", obj); 3423 ExpectObject("undetectable&&false", obj); 3424 ExpectBoolean("undetectable||true", true); 3425 ExpectBoolean("undetectable||false", false); 3426 3427 ExpectBoolean("undetectable==null", true); 3428 ExpectBoolean("null==undetectable", true); 3429 ExpectBoolean("undetectable==undefined", true); 3430 ExpectBoolean("undefined==undetectable", true); 3431 ExpectBoolean("undetectable==undetectable", true); 3432 3433 3434 ExpectBoolean("undetectable===null", false); 3435 ExpectBoolean("null===undetectable", false); 3436 ExpectBoolean("undetectable===undefined", false); 3437 ExpectBoolean("undefined===undetectable", false); 3438 ExpectBoolean("undetectable===undetectable", true); 3439} 3440 3441 3442template <typename T> static void USE(T) { } 3443 3444 3445// This test is not intended to be run, just type checked. 3446static void PersistentHandles() { 3447 USE(PersistentHandles); 3448 Local<String> str = v8_str("foo"); 3449 v8::Persistent<String> p_str = v8::Persistent<String>::New(str); 3450 USE(p_str); 3451 Local<Script> scr = Script::Compile(v8_str("")); 3452 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr); 3453 USE(p_scr); 3454 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3455 v8::Persistent<ObjectTemplate> p_templ = 3456 v8::Persistent<ObjectTemplate>::New(templ); 3457 USE(p_templ); 3458} 3459 3460 3461static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) { 3462 ApiTestFuzzer::Fuzz(); 3463 return v8::Undefined(); 3464} 3465 3466 3467THREADED_TEST(GlobalObjectTemplate) { 3468 v8::HandleScope handle_scope; 3469 Local<ObjectTemplate> global_template = ObjectTemplate::New(); 3470 global_template->Set(v8_str("JSNI_Log"), 3471 v8::FunctionTemplate::New(HandleLogDelegator)); 3472 v8::Persistent<Context> context = Context::New(0, global_template); 3473 Context::Scope context_scope(context); 3474 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run(); 3475 context.Dispose(); 3476} 3477 3478 3479static const char* kSimpleExtensionSource = 3480 "function Foo() {" 3481 " return 4;" 3482 "}"; 3483 3484 3485THREADED_TEST(SimpleExtensions) { 3486 v8::HandleScope handle_scope; 3487 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource)); 3488 const char* extension_names[] = { "simpletest" }; 3489 v8::ExtensionConfiguration extensions(1, extension_names); 3490 v8::Handle<Context> context = Context::New(&extensions); 3491 Context::Scope lock(context); 3492 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 3493 CHECK_EQ(result, v8::Integer::New(4)); 3494} 3495 3496 3497static const char* kEvalExtensionSource1 = 3498 "function UseEval1() {" 3499 " var x = 42;" 3500 " return eval('x');" 3501 "}"; 3502 3503 3504static const char* kEvalExtensionSource2 = 3505 "(function() {" 3506 " var x = 42;" 3507 " function e() {" 3508 " return eval('x');" 3509 " }" 3510 " this.UseEval2 = e;" 3511 "})()"; 3512 3513 3514THREADED_TEST(UseEvalFromExtension) { 3515 v8::HandleScope handle_scope; 3516 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1)); 3517 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2)); 3518 const char* extension_names[] = { "evaltest1", "evaltest2" }; 3519 v8::ExtensionConfiguration extensions(2, extension_names); 3520 v8::Handle<Context> context = Context::New(&extensions); 3521 Context::Scope lock(context); 3522 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run(); 3523 CHECK_EQ(result, v8::Integer::New(42)); 3524 result = Script::Compile(v8_str("UseEval2()"))->Run(); 3525 CHECK_EQ(result, v8::Integer::New(42)); 3526} 3527 3528 3529static const char* kWithExtensionSource1 = 3530 "function UseWith1() {" 3531 " var x = 42;" 3532 " with({x:87}) { return x; }" 3533 "}"; 3534 3535 3536 3537static const char* kWithExtensionSource2 = 3538 "(function() {" 3539 " var x = 42;" 3540 " function e() {" 3541 " with ({x:87}) { return x; }" 3542 " }" 3543 " this.UseWith2 = e;" 3544 "})()"; 3545 3546 3547THREADED_TEST(UseWithFromExtension) { 3548 v8::HandleScope handle_scope; 3549 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1)); 3550 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2)); 3551 const char* extension_names[] = { "withtest1", "withtest2" }; 3552 v8::ExtensionConfiguration extensions(2, extension_names); 3553 v8::Handle<Context> context = Context::New(&extensions); 3554 Context::Scope lock(context); 3555 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run(); 3556 CHECK_EQ(result, v8::Integer::New(87)); 3557 result = Script::Compile(v8_str("UseWith2()"))->Run(); 3558 CHECK_EQ(result, v8::Integer::New(87)); 3559} 3560 3561 3562THREADED_TEST(AutoExtensions) { 3563 v8::HandleScope handle_scope; 3564 Extension* extension = new Extension("autotest", kSimpleExtensionSource); 3565 extension->set_auto_enable(true); 3566 v8::RegisterExtension(extension); 3567 v8::Handle<Context> context = Context::New(); 3568 Context::Scope lock(context); 3569 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 3570 CHECK_EQ(result, v8::Integer::New(4)); 3571} 3572 3573 3574static const char* kSyntaxErrorInExtensionSource = 3575 "["; 3576 3577 3578// Test that a syntax error in an extension does not cause a fatal 3579// error but results in an empty context. 3580THREADED_TEST(SyntaxErrorExtensions) { 3581 v8::HandleScope handle_scope; 3582 v8::RegisterExtension(new Extension("syntaxerror", 3583 kSyntaxErrorInExtensionSource)); 3584 const char* extension_names[] = { "syntaxerror" }; 3585 v8::ExtensionConfiguration extensions(1, extension_names); 3586 v8::Handle<Context> context = Context::New(&extensions); 3587 CHECK(context.IsEmpty()); 3588} 3589 3590 3591static const char* kExceptionInExtensionSource = 3592 "throw 42"; 3593 3594 3595// Test that an exception when installing an extension does not cause 3596// a fatal error but results in an empty context. 3597THREADED_TEST(ExceptionExtensions) { 3598 v8::HandleScope handle_scope; 3599 v8::RegisterExtension(new Extension("exception", 3600 kExceptionInExtensionSource)); 3601 const char* extension_names[] = { "exception" }; 3602 v8::ExtensionConfiguration extensions(1, extension_names); 3603 v8::Handle<Context> context = Context::New(&extensions); 3604 CHECK(context.IsEmpty()); 3605} 3606 3607 3608static void CheckDependencies(const char* name, const char* expected) { 3609 v8::HandleScope handle_scope; 3610 v8::ExtensionConfiguration config(1, &name); 3611 LocalContext context(&config); 3612 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded"))); 3613} 3614 3615 3616/* 3617 * Configuration: 3618 * 3619 * /-- B <--\ 3620 * A <- -- D <-- E 3621 * \-- C <--/ 3622 */ 3623THREADED_TEST(ExtensionDependency) { 3624 static const char* kEDeps[] = { "D" }; 3625 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps)); 3626 static const char* kDDeps[] = { "B", "C" }; 3627 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps)); 3628 static const char* kBCDeps[] = { "A" }; 3629 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps)); 3630 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps)); 3631 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';")); 3632 CheckDependencies("A", "undefinedA"); 3633 CheckDependencies("B", "undefinedAB"); 3634 CheckDependencies("C", "undefinedAC"); 3635 CheckDependencies("D", "undefinedABCD"); 3636 CheckDependencies("E", "undefinedABCDE"); 3637 v8::HandleScope handle_scope; 3638 static const char* exts[2] = { "C", "E" }; 3639 v8::ExtensionConfiguration config(2, exts); 3640 LocalContext context(&config); 3641 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded"))); 3642} 3643 3644 3645static const char* kExtensionTestScript = 3646 "native function A();" 3647 "native function B();" 3648 "native function C();" 3649 "function Foo(i) {" 3650 " if (i == 0) return A();" 3651 " if (i == 1) return B();" 3652 " if (i == 2) return C();" 3653 "}"; 3654 3655 3656static v8::Handle<Value> CallFun(const v8::Arguments& args) { 3657 ApiTestFuzzer::Fuzz(); 3658 if (args.IsConstructCall()) { 3659 args.This()->Set(v8_str("data"), args.Data()); 3660 return v8::Null(); 3661 } 3662 return args.Data(); 3663} 3664 3665 3666class FunctionExtension : public Extension { 3667 public: 3668 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { } 3669 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( 3670 v8::Handle<String> name); 3671}; 3672 3673 3674static int lookup_count = 0; 3675v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction( 3676 v8::Handle<String> name) { 3677 lookup_count++; 3678 if (name->Equals(v8_str("A"))) { 3679 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8)); 3680 } else if (name->Equals(v8_str("B"))) { 3681 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7)); 3682 } else if (name->Equals(v8_str("C"))) { 3683 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6)); 3684 } else { 3685 return v8::Handle<v8::FunctionTemplate>(); 3686 } 3687} 3688 3689 3690THREADED_TEST(FunctionLookup) { 3691 v8::RegisterExtension(new FunctionExtension()); 3692 v8::HandleScope handle_scope; 3693 static const char* exts[1] = { "functiontest" }; 3694 v8::ExtensionConfiguration config(1, exts); 3695 LocalContext context(&config); 3696 CHECK_EQ(3, lookup_count); 3697 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run()); 3698 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run()); 3699 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run()); 3700} 3701 3702 3703THREADED_TEST(NativeFunctionConstructCall) { 3704 v8::RegisterExtension(new FunctionExtension()); 3705 v8::HandleScope handle_scope; 3706 static const char* exts[1] = { "functiontest" }; 3707 v8::ExtensionConfiguration config(1, exts); 3708 LocalContext context(&config); 3709 for (int i = 0; i < 10; i++) { 3710 // Run a few times to ensure that allocation of objects doesn't 3711 // change behavior of a constructor function. 3712 CHECK_EQ(v8::Integer::New(8), 3713 Script::Compile(v8_str("(new A()).data"))->Run()); 3714 CHECK_EQ(v8::Integer::New(7), 3715 Script::Compile(v8_str("(new B()).data"))->Run()); 3716 CHECK_EQ(v8::Integer::New(6), 3717 Script::Compile(v8_str("(new C()).data"))->Run()); 3718 } 3719} 3720 3721 3722static const char* last_location; 3723static const char* last_message; 3724void StoringErrorCallback(const char* location, const char* message) { 3725 if (last_location == NULL) { 3726 last_location = location; 3727 last_message = message; 3728 } 3729} 3730 3731 3732// ErrorReporting creates a circular extensions configuration and 3733// tests that the fatal error handler gets called. This renders V8 3734// unusable and therefore this test cannot be run in parallel. 3735TEST(ErrorReporting) { 3736 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 3737 static const char* aDeps[] = { "B" }; 3738 v8::RegisterExtension(new Extension("A", "", 1, aDeps)); 3739 static const char* bDeps[] = { "A" }; 3740 v8::RegisterExtension(new Extension("B", "", 1, bDeps)); 3741 last_location = NULL; 3742 v8::ExtensionConfiguration config(1, bDeps); 3743 v8::Handle<Context> context = Context::New(&config); 3744 CHECK(context.IsEmpty()); 3745 CHECK_NE(last_location, NULL); 3746} 3747 3748 3749static const char* js_code_causing_huge_string_flattening = 3750 "var str = 'X';" 3751 "for (var i = 0; i < 30; i++) {" 3752 " str = str + str;" 3753 "}" 3754 "str.match(/X/);"; 3755 3756 3757void OOMCallback(const char* location, const char* message) { 3758 exit(0); 3759} 3760 3761 3762TEST(RegexpOutOfMemory) { 3763 // Execute a script that causes out of memory when flattening a string. 3764 v8::HandleScope scope; 3765 v8::V8::SetFatalErrorHandler(OOMCallback); 3766 LocalContext context; 3767 Local<Script> script = 3768 Script::Compile(String::New(js_code_causing_huge_string_flattening)); 3769 last_location = NULL; 3770 Local<Value> result = script->Run(); 3771 3772 CHECK(false); // Should not return. 3773} 3774 3775 3776static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message, 3777 v8::Handle<Value> data) { 3778 CHECK_EQ(v8::Undefined(), data); 3779 CHECK(message->GetScriptResourceName()->IsUndefined()); 3780 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName()); 3781 message->GetLineNumber(); 3782 message->GetSourceLine(); 3783} 3784 3785 3786THREADED_TEST(ErrorWithMissingScriptInfo) { 3787 v8::HandleScope scope; 3788 LocalContext context; 3789 v8::V8::AddMessageListener(MissingScriptInfoMessageListener); 3790 Script::Compile(v8_str("throw Error()"))->Run(); 3791 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener); 3792} 3793 3794 3795int global_index = 0; 3796 3797class Snorkel { 3798 public: 3799 Snorkel() { index_ = global_index++; } 3800 int index_; 3801}; 3802 3803class Whammy { 3804 public: 3805 Whammy() { 3806 cursor_ = 0; 3807 } 3808 ~Whammy() { 3809 script_.Dispose(); 3810 } 3811 v8::Handle<Script> getScript() { 3812 if (script_.IsEmpty()) 3813 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo")); 3814 return Local<Script>(*script_); 3815 } 3816 3817 public: 3818 static const int kObjectCount = 256; 3819 int cursor_; 3820 v8::Persistent<v8::Object> objects_[kObjectCount]; 3821 v8::Persistent<Script> script_; 3822}; 3823 3824static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) { 3825 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data); 3826 delete snorkel; 3827 obj.ClearWeak(); 3828} 3829 3830v8::Handle<Value> WhammyPropertyGetter(Local<String> name, 3831 const AccessorInfo& info) { 3832 Whammy* whammy = 3833 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); 3834 3835 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_]; 3836 3837 v8::Handle<v8::Object> obj = v8::Object::New(); 3838 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj); 3839 if (!prev.IsEmpty()) { 3840 prev->Set(v8_str("next"), obj); 3841 prev.MakeWeak(new Snorkel(), &HandleWeakReference); 3842 whammy->objects_[whammy->cursor_].Clear(); 3843 } 3844 whammy->objects_[whammy->cursor_] = global; 3845 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount; 3846 return whammy->getScript()->Run(); 3847} 3848 3849THREADED_TEST(WeakReference) { 3850 v8::HandleScope handle_scope; 3851 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New(); 3852 Whammy* whammy = new Whammy(); 3853 templ->SetNamedPropertyHandler(WhammyPropertyGetter, 3854 0, 0, 0, 0, 3855 v8::External::New(whammy)); 3856 const char* extension_list[] = { "v8/gc" }; 3857 v8::ExtensionConfiguration extensions(1, extension_list); 3858 v8::Persistent<Context> context = Context::New(&extensions); 3859 Context::Scope context_scope(context); 3860 3861 v8::Handle<v8::Object> interceptor = templ->NewInstance(); 3862 context->Global()->Set(v8_str("whammy"), interceptor); 3863 const char* code = 3864 "var last;" 3865 "for (var i = 0; i < 10000; i++) {" 3866 " var obj = whammy.length;" 3867 " if (last) last.next = obj;" 3868 " last = obj;" 3869 "}" 3870 "gc();" 3871 "4"; 3872 v8::Handle<Value> result = CompileRun(code); 3873 CHECK_EQ(4.0, result->NumberValue()); 3874 delete whammy; 3875 context.Dispose(); 3876} 3877 3878 3879static bool in_scavenge = false; 3880static int last = -1; 3881 3882static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) { 3883 CHECK_EQ(-1, last); 3884 last = 0; 3885 obj.Dispose(); 3886 obj.Clear(); 3887 in_scavenge = true; 3888 i::Heap::PerformScavenge(); 3889 in_scavenge = false; 3890 *(reinterpret_cast<bool*>(data)) = true; 3891} 3892 3893static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj, 3894 void* data) { 3895 CHECK_EQ(0, last); 3896 last = 1; 3897 *(reinterpret_cast<bool*>(data)) = in_scavenge; 3898 obj.Dispose(); 3899 obj.Clear(); 3900} 3901 3902THREADED_TEST(NoWeakRefCallbacksInScavenge) { 3903 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks. 3904 // Calling callbacks from scavenges is unsafe as objects held by those 3905 // handlers might have become strongly reachable, but scavenge doesn't 3906 // check that. 3907 v8::Persistent<Context> context = Context::New(); 3908 Context::Scope context_scope(context); 3909 3910 v8::Persistent<v8::Object> object_a; 3911 v8::Persistent<v8::Object> object_b; 3912 3913 { 3914 v8::HandleScope handle_scope; 3915 object_b = v8::Persistent<v8::Object>::New(v8::Object::New()); 3916 object_a = v8::Persistent<v8::Object>::New(v8::Object::New()); 3917 } 3918 3919 bool object_a_disposed = false; 3920 object_a.MakeWeak(&object_a_disposed, &ForceScavenge); 3921 bool released_in_scavenge = false; 3922 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge); 3923 3924 while (!object_a_disposed) { 3925 i::Heap::CollectAllGarbage(false); 3926 } 3927 CHECK(!released_in_scavenge); 3928} 3929 3930 3931v8::Handle<Function> args_fun; 3932 3933 3934static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) { 3935 ApiTestFuzzer::Fuzz(); 3936 CHECK_EQ(args_fun, args.Callee()); 3937 CHECK_EQ(3, args.Length()); 3938 CHECK_EQ(v8::Integer::New(1), args[0]); 3939 CHECK_EQ(v8::Integer::New(2), args[1]); 3940 CHECK_EQ(v8::Integer::New(3), args[2]); 3941 CHECK_EQ(v8::Undefined(), args[3]); 3942 v8::HandleScope scope; 3943 i::Heap::CollectAllGarbage(false); 3944 return v8::Undefined(); 3945} 3946 3947 3948THREADED_TEST(Arguments) { 3949 v8::HandleScope scope; 3950 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 3951 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback)); 3952 LocalContext context(NULL, global); 3953 args_fun = context->Global()->Get(v8_str("f")).As<Function>(); 3954 v8_compile("f(1, 2, 3)")->Run(); 3955} 3956 3957 3958static v8::Handle<Value> NoBlockGetterX(Local<String> name, 3959 const AccessorInfo&) { 3960 return v8::Handle<Value>(); 3961} 3962 3963 3964static v8::Handle<Value> NoBlockGetterI(uint32_t index, 3965 const AccessorInfo&) { 3966 return v8::Handle<Value>(); 3967} 3968 3969 3970static v8::Handle<v8::Boolean> PDeleter(Local<String> name, 3971 const AccessorInfo&) { 3972 if (!name->Equals(v8_str("foo"))) { 3973 return v8::Handle<v8::Boolean>(); // not intercepted 3974 } 3975 3976 return v8::False(); // intercepted, and don't delete the property 3977} 3978 3979 3980static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) { 3981 if (index != 2) { 3982 return v8::Handle<v8::Boolean>(); // not intercepted 3983 } 3984 3985 return v8::False(); // intercepted, and don't delete the property 3986} 3987 3988 3989THREADED_TEST(Deleter) { 3990 v8::HandleScope scope; 3991 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 3992 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL); 3993 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL); 3994 LocalContext context; 3995 context->Global()->Set(v8_str("k"), obj->NewInstance()); 3996 CompileRun( 3997 "k.foo = 'foo';" 3998 "k.bar = 'bar';" 3999 "k[2] = 2;" 4000 "k[4] = 4;"); 4001 CHECK(v8_compile("delete k.foo")->Run()->IsFalse()); 4002 CHECK(v8_compile("delete k.bar")->Run()->IsTrue()); 4003 4004 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo")); 4005 CHECK(v8_compile("k.bar")->Run()->IsUndefined()); 4006 4007 CHECK(v8_compile("delete k[2]")->Run()->IsFalse()); 4008 CHECK(v8_compile("delete k[4]")->Run()->IsTrue()); 4009 4010 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2)); 4011 CHECK(v8_compile("k[4]")->Run()->IsUndefined()); 4012} 4013 4014 4015static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) { 4016 ApiTestFuzzer::Fuzz(); 4017 if (name->Equals(v8_str("foo")) || 4018 name->Equals(v8_str("bar")) || 4019 name->Equals(v8_str("baz"))) { 4020 return v8::Undefined(); 4021 } 4022 return v8::Handle<Value>(); 4023} 4024 4025 4026static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) { 4027 ApiTestFuzzer::Fuzz(); 4028 if (index == 0 || index == 1) return v8::Undefined(); 4029 return v8::Handle<Value>(); 4030} 4031 4032 4033static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) { 4034 ApiTestFuzzer::Fuzz(); 4035 v8::Handle<v8::Array> result = v8::Array::New(3); 4036 result->Set(v8::Integer::New(0), v8_str("foo")); 4037 result->Set(v8::Integer::New(1), v8_str("bar")); 4038 result->Set(v8::Integer::New(2), v8_str("baz")); 4039 return result; 4040} 4041 4042 4043static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) { 4044 ApiTestFuzzer::Fuzz(); 4045 v8::Handle<v8::Array> result = v8::Array::New(2); 4046 result->Set(v8::Integer::New(0), v8_str("0")); 4047 result->Set(v8::Integer::New(1), v8_str("1")); 4048 return result; 4049} 4050 4051 4052THREADED_TEST(Enumerators) { 4053 v8::HandleScope scope; 4054 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4055 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum); 4056 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum); 4057 LocalContext context; 4058 context->Global()->Set(v8_str("k"), obj->NewInstance()); 4059 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 4060 "k[10] = 0;" 4061 "k.a = 0;" 4062 "k[5] = 0;" 4063 "k.b = 0;" 4064 "k[4294967295] = 0;" 4065 "k.c = 0;" 4066 "k[4294967296] = 0;" 4067 "k.d = 0;" 4068 "k[140000] = 0;" 4069 "k.e = 0;" 4070 "k[30000000000] = 0;" 4071 "k.f = 0;" 4072 "var result = [];" 4073 "for (var prop in k) {" 4074 " result.push(prop);" 4075 "}" 4076 "result")); 4077 // Check that we get all the property names returned including the 4078 // ones from the enumerators in the right order: indexed properties 4079 // in numerical order, indexed interceptor properties, named 4080 // properties in insertion order, named interceptor properties. 4081 // This order is not mandated by the spec, so this test is just 4082 // documenting our behavior. 4083 CHECK_EQ(17, result->Length()); 4084 // Indexed properties in numerical order. 4085 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0))); 4086 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1))); 4087 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2))); 4088 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3))); 4089 // Indexed interceptor properties in the order they are returned 4090 // from the enumerator interceptor. 4091 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4))); 4092 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5))); 4093 // Named properties in insertion order. 4094 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6))); 4095 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7))); 4096 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8))); 4097 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9))); 4098 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10))); 4099 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11))); 4100 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12))); 4101 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13))); 4102 // Named interceptor properties. 4103 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14))); 4104 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15))); 4105 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16))); 4106} 4107 4108 4109int p_getter_count; 4110int p_getter_count2; 4111 4112 4113static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) { 4114 ApiTestFuzzer::Fuzz(); 4115 p_getter_count++; 4116 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 4117 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 4118 if (name->Equals(v8_str("p1"))) { 4119 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 4120 } else if (name->Equals(v8_str("p2"))) { 4121 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 4122 } else if (name->Equals(v8_str("p3"))) { 4123 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 4124 } else if (name->Equals(v8_str("p4"))) { 4125 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 4126 } 4127 return v8::Undefined(); 4128} 4129 4130 4131static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) { 4132 ApiTestFuzzer::Fuzz(); 4133 LocalContext context; 4134 context->Global()->Set(v8_str("o1"), obj->NewInstance()); 4135 CompileRun( 4136 "o1.__proto__ = { };" 4137 "var o2 = { __proto__: o1 };" 4138 "var o3 = { __proto__: o2 };" 4139 "var o4 = { __proto__: o3 };" 4140 "for (var i = 0; i < 10; i++) o4.p4;" 4141 "for (var i = 0; i < 10; i++) o3.p3;" 4142 "for (var i = 0; i < 10; i++) o2.p2;" 4143 "for (var i = 0; i < 10; i++) o1.p1;"); 4144} 4145 4146 4147static v8::Handle<Value> PGetter2(Local<String> name, 4148 const AccessorInfo& info) { 4149 ApiTestFuzzer::Fuzz(); 4150 p_getter_count2++; 4151 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 4152 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 4153 if (name->Equals(v8_str("p1"))) { 4154 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 4155 } else if (name->Equals(v8_str("p2"))) { 4156 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 4157 } else if (name->Equals(v8_str("p3"))) { 4158 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 4159 } else if (name->Equals(v8_str("p4"))) { 4160 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 4161 } 4162 return v8::Undefined(); 4163} 4164 4165 4166THREADED_TEST(GetterHolders) { 4167 v8::HandleScope scope; 4168 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4169 obj->SetAccessor(v8_str("p1"), PGetter); 4170 obj->SetAccessor(v8_str("p2"), PGetter); 4171 obj->SetAccessor(v8_str("p3"), PGetter); 4172 obj->SetAccessor(v8_str("p4"), PGetter); 4173 p_getter_count = 0; 4174 RunHolderTest(obj); 4175 CHECK_EQ(40, p_getter_count); 4176} 4177 4178 4179THREADED_TEST(PreInterceptorHolders) { 4180 v8::HandleScope scope; 4181 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4182 obj->SetNamedPropertyHandler(PGetter2); 4183 p_getter_count2 = 0; 4184 RunHolderTest(obj); 4185 CHECK_EQ(40, p_getter_count2); 4186} 4187 4188 4189THREADED_TEST(ObjectInstantiation) { 4190 v8::HandleScope scope; 4191 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 4192 templ->SetAccessor(v8_str("t"), PGetter2); 4193 LocalContext context; 4194 context->Global()->Set(v8_str("o"), templ->NewInstance()); 4195 for (int i = 0; i < 100; i++) { 4196 v8::HandleScope inner_scope; 4197 v8::Handle<v8::Object> obj = templ->NewInstance(); 4198 CHECK_NE(obj, context->Global()->Get(v8_str("o"))); 4199 context->Global()->Set(v8_str("o2"), obj); 4200 v8::Handle<Value> value = 4201 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run(); 4202 CHECK_EQ(v8::True(), value); 4203 context->Global()->Set(v8_str("o"), obj); 4204 } 4205} 4206 4207 4208THREADED_TEST(StringWrite) { 4209 v8::HandleScope scope; 4210 v8::Handle<String> str = v8_str("abcde"); 4211 4212 char buf[100]; 4213 int len; 4214 4215 memset(buf, 0x1, sizeof(buf)); 4216 len = str->WriteAscii(buf); 4217 CHECK_EQ(len, 5); 4218 CHECK_EQ(strncmp("abcde\0", buf, 6), 0); 4219 4220 memset(buf, 0x1, sizeof(buf)); 4221 len = str->WriteAscii(buf, 0, 4); 4222 CHECK_EQ(len, 4); 4223 CHECK_EQ(strncmp("abcd\1", buf, 5), 0); 4224 4225 memset(buf, 0x1, sizeof(buf)); 4226 len = str->WriteAscii(buf, 0, 5); 4227 CHECK_EQ(len, 5); 4228 CHECK_EQ(strncmp("abcde\1", buf, 6), 0); 4229 4230 memset(buf, 0x1, sizeof(buf)); 4231 len = str->WriteAscii(buf, 0, 6); 4232 CHECK_EQ(len, 5); 4233 CHECK_EQ(strncmp("abcde\0", buf, 6), 0); 4234 4235 memset(buf, 0x1, sizeof(buf)); 4236 len = str->WriteAscii(buf, 4, -1); 4237 CHECK_EQ(len, 1); 4238 CHECK_EQ(strncmp("e\0", buf, 2), 0); 4239 4240 memset(buf, 0x1, sizeof(buf)); 4241 len = str->WriteAscii(buf, 4, 6); 4242 CHECK_EQ(len, 1); 4243 CHECK_EQ(strncmp("e\0", buf, 2), 0); 4244 4245 memset(buf, 0x1, sizeof(buf)); 4246 len = str->WriteAscii(buf, 4, 1); 4247 CHECK_EQ(len, 1); 4248 CHECK_EQ(strncmp("e\1", buf, 2), 0); 4249} 4250 4251 4252THREADED_TEST(ToArrayIndex) { 4253 v8::HandleScope scope; 4254 LocalContext context; 4255 4256 v8::Handle<String> str = v8_str("42"); 4257 v8::Handle<v8::Uint32> index = str->ToArrayIndex(); 4258 CHECK(!index.IsEmpty()); 4259 CHECK_EQ(42.0, index->Uint32Value()); 4260 str = v8_str("42asdf"); 4261 index = str->ToArrayIndex(); 4262 CHECK(index.IsEmpty()); 4263 str = v8_str("-42"); 4264 index = str->ToArrayIndex(); 4265 CHECK(index.IsEmpty()); 4266 str = v8_str("4294967295"); 4267 index = str->ToArrayIndex(); 4268 CHECK(!index.IsEmpty()); 4269 CHECK_EQ(4294967295.0, index->Uint32Value()); 4270 v8::Handle<v8::Number> num = v8::Number::New(1); 4271 index = num->ToArrayIndex(); 4272 CHECK(!index.IsEmpty()); 4273 CHECK_EQ(1.0, index->Uint32Value()); 4274 num = v8::Number::New(-1); 4275 index = num->ToArrayIndex(); 4276 CHECK(index.IsEmpty()); 4277 v8::Handle<v8::Object> obj = v8::Object::New(); 4278 index = obj->ToArrayIndex(); 4279 CHECK(index.IsEmpty()); 4280} 4281 4282 4283THREADED_TEST(ErrorConstruction) { 4284 v8::HandleScope scope; 4285 LocalContext context; 4286 4287 v8::Handle<String> foo = v8_str("foo"); 4288 v8::Handle<String> message = v8_str("message"); 4289 v8::Handle<Value> range_error = v8::Exception::RangeError(foo); 4290 CHECK(range_error->IsObject()); 4291 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>(); 4292 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo)); 4293 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo); 4294 CHECK(reference_error->IsObject()); 4295 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo)); 4296 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo); 4297 CHECK(syntax_error->IsObject()); 4298 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo)); 4299 v8::Handle<Value> type_error = v8::Exception::TypeError(foo); 4300 CHECK(type_error->IsObject()); 4301 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo)); 4302 v8::Handle<Value> error = v8::Exception::Error(foo); 4303 CHECK(error->IsObject()); 4304 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo)); 4305} 4306 4307 4308static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) { 4309 ApiTestFuzzer::Fuzz(); 4310 return v8_num(10); 4311} 4312 4313 4314static void YSetter(Local<String> name, 4315 Local<Value> value, 4316 const AccessorInfo& info) { 4317 if (info.This()->Has(name)) { 4318 info.This()->Delete(name); 4319 } 4320 info.This()->Set(name, value); 4321} 4322 4323 4324THREADED_TEST(DeleteAccessor) { 4325 v8::HandleScope scope; 4326 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4327 obj->SetAccessor(v8_str("y"), YGetter, YSetter); 4328 LocalContext context; 4329 v8::Handle<v8::Object> holder = obj->NewInstance(); 4330 context->Global()->Set(v8_str("holder"), holder); 4331 v8::Handle<Value> result = CompileRun( 4332 "holder.y = 11; holder.y = 12; holder.y"); 4333 CHECK_EQ(12, result->Uint32Value()); 4334} 4335 4336 4337THREADED_TEST(TypeSwitch) { 4338 v8::HandleScope scope; 4339 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New(); 4340 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New(); 4341 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New(); 4342 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 }; 4343 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs); 4344 LocalContext context; 4345 v8::Handle<v8::Object> obj0 = v8::Object::New(); 4346 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance(); 4347 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance(); 4348 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance(); 4349 for (int i = 0; i < 10; i++) { 4350 CHECK_EQ(0, type_switch->match(obj0)); 4351 CHECK_EQ(1, type_switch->match(obj1)); 4352 CHECK_EQ(2, type_switch->match(obj2)); 4353 CHECK_EQ(3, type_switch->match(obj3)); 4354 CHECK_EQ(3, type_switch->match(obj3)); 4355 CHECK_EQ(2, type_switch->match(obj2)); 4356 CHECK_EQ(1, type_switch->match(obj1)); 4357 CHECK_EQ(0, type_switch->match(obj0)); 4358 } 4359} 4360 4361 4362// For use within the TestSecurityHandler() test. 4363static bool g_security_callback_result = false; 4364static bool NamedSecurityTestCallback(Local<v8::Object> global, 4365 Local<Value> name, 4366 v8::AccessType type, 4367 Local<Value> data) { 4368 // Always allow read access. 4369 if (type == v8::ACCESS_GET) 4370 return true; 4371 4372 // Sometimes allow other access. 4373 return g_security_callback_result; 4374} 4375 4376 4377static bool IndexedSecurityTestCallback(Local<v8::Object> global, 4378 uint32_t key, 4379 v8::AccessType type, 4380 Local<Value> data) { 4381 // Always allow read access. 4382 if (type == v8::ACCESS_GET) 4383 return true; 4384 4385 // Sometimes allow other access. 4386 return g_security_callback_result; 4387} 4388 4389 4390static int trouble_nesting = 0; 4391static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) { 4392 ApiTestFuzzer::Fuzz(); 4393 trouble_nesting++; 4394 4395 // Call a JS function that throws an uncaught exception. 4396 Local<v8::Object> arg_this = Context::GetCurrent()->Global(); 4397 Local<Value> trouble_callee = (trouble_nesting == 3) ? 4398 arg_this->Get(v8_str("trouble_callee")) : 4399 arg_this->Get(v8_str("trouble_caller")); 4400 CHECK(trouble_callee->IsFunction()); 4401 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL); 4402} 4403 4404 4405static int report_count = 0; 4406static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>, 4407 v8::Handle<Value>) { 4408 report_count++; 4409} 4410 4411 4412// Counts uncaught exceptions, but other tests running in parallel 4413// also have uncaught exceptions. 4414TEST(ApiUncaughtException) { 4415 report_count = 0; 4416 v8::HandleScope scope; 4417 LocalContext env; 4418 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener); 4419 4420 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 4421 v8::Local<v8::Object> global = env->Global(); 4422 global->Set(v8_str("trouble"), fun->GetFunction()); 4423 4424 Script::Compile(v8_str("function trouble_callee() {" 4425 " var x = null;" 4426 " return x.foo;" 4427 "};" 4428 "function trouble_caller() {" 4429 " trouble();" 4430 "};"))->Run(); 4431 Local<Value> trouble = global->Get(v8_str("trouble")); 4432 CHECK(trouble->IsFunction()); 4433 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee")); 4434 CHECK(trouble_callee->IsFunction()); 4435 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller")); 4436 CHECK(trouble_caller->IsFunction()); 4437 Function::Cast(*trouble_caller)->Call(global, 0, NULL); 4438 CHECK_EQ(1, report_count); 4439 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener); 4440} 4441 4442static const char* script_resource_name = "ExceptionInNativeScript.js"; 4443static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message, 4444 v8::Handle<Value>) { 4445 v8::Handle<v8::Value> name_val = message->GetScriptResourceName(); 4446 CHECK(!name_val.IsEmpty() && name_val->IsString()); 4447 v8::String::AsciiValue name(message->GetScriptResourceName()); 4448 CHECK_EQ(script_resource_name, *name); 4449 CHECK_EQ(3, message->GetLineNumber()); 4450 v8::String::AsciiValue source_line(message->GetSourceLine()); 4451 CHECK_EQ(" new o.foo();", *source_line); 4452} 4453 4454TEST(ExceptionInNativeScript) { 4455 v8::HandleScope scope; 4456 LocalContext env; 4457 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener); 4458 4459 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 4460 v8::Local<v8::Object> global = env->Global(); 4461 global->Set(v8_str("trouble"), fun->GetFunction()); 4462 4463 Script::Compile(v8_str("function trouble() {\n" 4464 " var o = {};\n" 4465 " new o.foo();\n" 4466 "};"), v8::String::New(script_resource_name))->Run(); 4467 Local<Value> trouble = global->Get(v8_str("trouble")); 4468 CHECK(trouble->IsFunction()); 4469 Function::Cast(*trouble)->Call(global, 0, NULL); 4470 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener); 4471} 4472 4473 4474TEST(CompilationErrorUsingTryCatchHandler) { 4475 v8::HandleScope scope; 4476 LocalContext env; 4477 v8::TryCatch try_catch; 4478 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile.")); 4479 CHECK_NE(NULL, *try_catch.Exception()); 4480 CHECK(try_catch.HasCaught()); 4481} 4482 4483 4484TEST(TryCatchFinallyUsingTryCatchHandler) { 4485 v8::HandleScope scope; 4486 LocalContext env; 4487 v8::TryCatch try_catch; 4488 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run(); 4489 CHECK(!try_catch.HasCaught()); 4490 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run(); 4491 CHECK(try_catch.HasCaught()); 4492 try_catch.Reset(); 4493 Script::Compile(v8_str("(function() {" 4494 "try { throw ''; } finally { return; }" 4495 "})()"))->Run(); 4496 CHECK(!try_catch.HasCaught()); 4497 Script::Compile(v8_str("(function()" 4498 " { try { throw ''; } finally { throw 0; }" 4499 "})()"))->Run(); 4500 CHECK(try_catch.HasCaught()); 4501} 4502 4503 4504// SecurityHandler can't be run twice 4505TEST(SecurityHandler) { 4506 v8::HandleScope scope0; 4507 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 4508 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback, 4509 IndexedSecurityTestCallback); 4510 // Create an environment 4511 v8::Persistent<Context> context0 = 4512 Context::New(NULL, global_template); 4513 context0->Enter(); 4514 4515 v8::Handle<v8::Object> global0 = context0->Global(); 4516 v8::Handle<Script> script0 = v8_compile("foo = 111"); 4517 script0->Run(); 4518 global0->Set(v8_str("0"), v8_num(999)); 4519 v8::Handle<Value> foo0 = global0->Get(v8_str("foo")); 4520 CHECK_EQ(111, foo0->Int32Value()); 4521 v8::Handle<Value> z0 = global0->Get(v8_str("0")); 4522 CHECK_EQ(999, z0->Int32Value()); 4523 4524 // Create another environment, should fail security checks. 4525 v8::HandleScope scope1; 4526 4527 v8::Persistent<Context> context1 = 4528 Context::New(NULL, global_template); 4529 context1->Enter(); 4530 4531 v8::Handle<v8::Object> global1 = context1->Global(); 4532 global1->Set(v8_str("othercontext"), global0); 4533 // This set will fail the security check. 4534 v8::Handle<Script> script1 = 4535 v8_compile("othercontext.foo = 222; othercontext[0] = 888;"); 4536 script1->Run(); 4537 // This read will pass the security check. 4538 v8::Handle<Value> foo1 = global0->Get(v8_str("foo")); 4539 CHECK_EQ(111, foo1->Int32Value()); 4540 // This read will pass the security check. 4541 v8::Handle<Value> z1 = global0->Get(v8_str("0")); 4542 CHECK_EQ(999, z1->Int32Value()); 4543 4544 // Create another environment, should pass security checks. 4545 { g_security_callback_result = true; // allow security handler to pass. 4546 v8::HandleScope scope2; 4547 LocalContext context2; 4548 v8::Handle<v8::Object> global2 = context2->Global(); 4549 global2->Set(v8_str("othercontext"), global0); 4550 v8::Handle<Script> script2 = 4551 v8_compile("othercontext.foo = 333; othercontext[0] = 888;"); 4552 script2->Run(); 4553 v8::Handle<Value> foo2 = global0->Get(v8_str("foo")); 4554 CHECK_EQ(333, foo2->Int32Value()); 4555 v8::Handle<Value> z2 = global0->Get(v8_str("0")); 4556 CHECK_EQ(888, z2->Int32Value()); 4557 } 4558 4559 context1->Exit(); 4560 context1.Dispose(); 4561 4562 context0->Exit(); 4563 context0.Dispose(); 4564} 4565 4566 4567THREADED_TEST(SecurityChecks) { 4568 v8::HandleScope handle_scope; 4569 LocalContext env1; 4570 v8::Persistent<Context> env2 = Context::New(); 4571 4572 Local<Value> foo = v8_str("foo"); 4573 Local<Value> bar = v8_str("bar"); 4574 4575 // Set to the same domain. 4576 env1->SetSecurityToken(foo); 4577 4578 // Create a function in env1. 4579 Script::Compile(v8_str("spy=function(){return spy;}"))->Run(); 4580 Local<Value> spy = env1->Global()->Get(v8_str("spy")); 4581 CHECK(spy->IsFunction()); 4582 4583 // Create another function accessing global objects. 4584 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run(); 4585 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2")); 4586 CHECK(spy2->IsFunction()); 4587 4588 // Switch to env2 in the same domain and invoke spy on env2. 4589 { 4590 env2->SetSecurityToken(foo); 4591 // Enter env2 4592 Context::Scope scope_env2(env2); 4593 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL); 4594 CHECK(result->IsFunction()); 4595 } 4596 4597 { 4598 env2->SetSecurityToken(bar); 4599 Context::Scope scope_env2(env2); 4600 4601 // Call cross_domain_call, it should throw an exception 4602 v8::TryCatch try_catch; 4603 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL); 4604 CHECK(try_catch.HasCaught()); 4605 } 4606 4607 env2.Dispose(); 4608} 4609 4610 4611// Regression test case for issue 1183439. 4612THREADED_TEST(SecurityChecksForPrototypeChain) { 4613 v8::HandleScope scope; 4614 LocalContext current; 4615 v8::Persistent<Context> other = Context::New(); 4616 4617 // Change context to be able to get to the Object function in the 4618 // other context without hitting the security checks. 4619 v8::Local<Value> other_object; 4620 { Context::Scope scope(other); 4621 other_object = other->Global()->Get(v8_str("Object")); 4622 other->Global()->Set(v8_num(42), v8_num(87)); 4623 } 4624 4625 current->Global()->Set(v8_str("other"), other->Global()); 4626 CHECK(v8_compile("other")->Run()->Equals(other->Global())); 4627 4628 // Make sure the security check fails here and we get an undefined 4629 // result instead of getting the Object function. Repeat in a loop 4630 // to make sure to exercise the IC code. 4631 v8::Local<Script> access_other0 = v8_compile("other.Object"); 4632 v8::Local<Script> access_other1 = v8_compile("other[42]"); 4633 for (int i = 0; i < 5; i++) { 4634 CHECK(!access_other0->Run()->Equals(other_object)); 4635 CHECK(access_other0->Run()->IsUndefined()); 4636 CHECK(!access_other1->Run()->Equals(v8_num(87))); 4637 CHECK(access_other1->Run()->IsUndefined()); 4638 } 4639 4640 // Create an object that has 'other' in its prototype chain and make 4641 // sure we cannot access the Object function indirectly through 4642 // that. Repeat in a loop to make sure to exercise the IC code. 4643 v8_compile("function F() { };" 4644 "F.prototype = other;" 4645 "var f = new F();")->Run(); 4646 v8::Local<Script> access_f0 = v8_compile("f.Object"); 4647 v8::Local<Script> access_f1 = v8_compile("f[42]"); 4648 for (int j = 0; j < 5; j++) { 4649 CHECK(!access_f0->Run()->Equals(other_object)); 4650 CHECK(access_f0->Run()->IsUndefined()); 4651 CHECK(!access_f1->Run()->Equals(v8_num(87))); 4652 CHECK(access_f1->Run()->IsUndefined()); 4653 } 4654 4655 // Now it gets hairy: Set the prototype for the other global object 4656 // to be the current global object. The prototype chain for 'f' now 4657 // goes through 'other' but ends up in the current global object. 4658 { Context::Scope scope(other); 4659 other->Global()->Set(v8_str("__proto__"), current->Global()); 4660 } 4661 // Set a named and an index property on the current global 4662 // object. To force the lookup to go through the other global object, 4663 // the properties must not exist in the other global object. 4664 current->Global()->Set(v8_str("foo"), v8_num(100)); 4665 current->Global()->Set(v8_num(99), v8_num(101)); 4666 // Try to read the properties from f and make sure that the access 4667 // gets stopped by the security checks on the other global object. 4668 Local<Script> access_f2 = v8_compile("f.foo"); 4669 Local<Script> access_f3 = v8_compile("f[99]"); 4670 for (int k = 0; k < 5; k++) { 4671 CHECK(!access_f2->Run()->Equals(v8_num(100))); 4672 CHECK(access_f2->Run()->IsUndefined()); 4673 CHECK(!access_f3->Run()->Equals(v8_num(101))); 4674 CHECK(access_f3->Run()->IsUndefined()); 4675 } 4676 other.Dispose(); 4677} 4678 4679 4680THREADED_TEST(CrossDomainDelete) { 4681 v8::HandleScope handle_scope; 4682 LocalContext env1; 4683 v8::Persistent<Context> env2 = Context::New(); 4684 4685 Local<Value> foo = v8_str("foo"); 4686 Local<Value> bar = v8_str("bar"); 4687 4688 // Set to the same domain. 4689 env1->SetSecurityToken(foo); 4690 env2->SetSecurityToken(foo); 4691 4692 env1->Global()->Set(v8_str("prop"), v8_num(3)); 4693 env2->Global()->Set(v8_str("env1"), env1->Global()); 4694 4695 // Change env2 to a different domain and delete env1.prop. 4696 env2->SetSecurityToken(bar); 4697 { 4698 Context::Scope scope_env2(env2); 4699 Local<Value> result = 4700 Script::Compile(v8_str("delete env1.prop"))->Run(); 4701 CHECK(result->IsFalse()); 4702 } 4703 4704 // Check that env1.prop still exists. 4705 Local<Value> v = env1->Global()->Get(v8_str("prop")); 4706 CHECK(v->IsNumber()); 4707 CHECK_EQ(3, v->Int32Value()); 4708 4709 env2.Dispose(); 4710} 4711 4712 4713THREADED_TEST(CrossDomainIsPropertyEnumerable) { 4714 v8::HandleScope handle_scope; 4715 LocalContext env1; 4716 v8::Persistent<Context> env2 = Context::New(); 4717 4718 Local<Value> foo = v8_str("foo"); 4719 Local<Value> bar = v8_str("bar"); 4720 4721 // Set to the same domain. 4722 env1->SetSecurityToken(foo); 4723 env2->SetSecurityToken(foo); 4724 4725 env1->Global()->Set(v8_str("prop"), v8_num(3)); 4726 env2->Global()->Set(v8_str("env1"), env1->Global()); 4727 4728 // env1.prop is enumerable in env2. 4729 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')"); 4730 { 4731 Context::Scope scope_env2(env2); 4732 Local<Value> result = Script::Compile(test)->Run(); 4733 CHECK(result->IsTrue()); 4734 } 4735 4736 // Change env2 to a different domain and test again. 4737 env2->SetSecurityToken(bar); 4738 { 4739 Context::Scope scope_env2(env2); 4740 Local<Value> result = Script::Compile(test)->Run(); 4741 CHECK(result->IsFalse()); 4742 } 4743 4744 env2.Dispose(); 4745} 4746 4747 4748THREADED_TEST(CrossDomainForIn) { 4749 v8::HandleScope handle_scope; 4750 LocalContext env1; 4751 v8::Persistent<Context> env2 = Context::New(); 4752 4753 Local<Value> foo = v8_str("foo"); 4754 Local<Value> bar = v8_str("bar"); 4755 4756 // Set to the same domain. 4757 env1->SetSecurityToken(foo); 4758 env2->SetSecurityToken(foo); 4759 4760 env1->Global()->Set(v8_str("prop"), v8_num(3)); 4761 env2->Global()->Set(v8_str("env1"), env1->Global()); 4762 4763 // Change env2 to a different domain and set env1's global object 4764 // as the __proto__ of an object in env2 and enumerate properties 4765 // in for-in. It shouldn't enumerate properties on env1's global 4766 // object. 4767 env2->SetSecurityToken(bar); 4768 { 4769 Context::Scope scope_env2(env2); 4770 Local<Value> result = 4771 CompileRun("(function(){var obj = {'__proto__':env1};" 4772 "for (var p in obj)" 4773 " if (p == 'prop') return false;" 4774 "return true;})()"); 4775 CHECK(result->IsTrue()); 4776 } 4777 env2.Dispose(); 4778} 4779 4780 4781TEST(ContextDetachGlobal) { 4782 v8::HandleScope handle_scope; 4783 LocalContext env1; 4784 v8::Persistent<Context> env2 = Context::New(); 4785 4786 Local<v8::Object> global1 = env1->Global(); 4787 4788 Local<Value> foo = v8_str("foo"); 4789 4790 // Set to the same domain. 4791 env1->SetSecurityToken(foo); 4792 env2->SetSecurityToken(foo); 4793 4794 // Enter env2 4795 env2->Enter(); 4796 4797 // Create a function in env2 and add a reference to it in env1. 4798 Local<v8::Object> global2 = env2->Global(); 4799 global2->Set(v8_str("prop"), v8::Integer::New(1)); 4800 CompileRun("function getProp() {return prop;}"); 4801 4802 env1->Global()->Set(v8_str("getProp"), 4803 global2->Get(v8_str("getProp"))); 4804 4805 // Detach env2's global, and reuse the global object of env2 4806 env2->Exit(); 4807 env2->DetachGlobal(); 4808 // env2 has a new global object. 4809 CHECK(!env2->Global()->Equals(global2)); 4810 4811 v8::Persistent<Context> env3 = 4812 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 4813 env3->SetSecurityToken(v8_str("bar")); 4814 env3->Enter(); 4815 4816 Local<v8::Object> global3 = env3->Global(); 4817 CHECK_EQ(global2, global3); 4818 CHECK(global3->Get(v8_str("prop"))->IsUndefined()); 4819 CHECK(global3->Get(v8_str("getProp"))->IsUndefined()); 4820 global3->Set(v8_str("prop"), v8::Integer::New(-1)); 4821 global3->Set(v8_str("prop2"), v8::Integer::New(2)); 4822 env3->Exit(); 4823 4824 // Call getProp in env1, and it should return the value 1 4825 { 4826 Local<Value> get_prop = global1->Get(v8_str("getProp")); 4827 CHECK(get_prop->IsFunction()); 4828 v8::TryCatch try_catch; 4829 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL); 4830 CHECK(!try_catch.HasCaught()); 4831 CHECK_EQ(1, r->Int32Value()); 4832 } 4833 4834 // Check that env3 is not accessible from env1 4835 { 4836 Local<Value> r = global3->Get(v8_str("prop2")); 4837 CHECK(r->IsUndefined()); 4838 } 4839 4840 env2.Dispose(); 4841 env3.Dispose(); 4842} 4843 4844 4845TEST(DetachAndReattachGlobal) { 4846 v8::HandleScope scope; 4847 LocalContext env1; 4848 4849 // Create second environment. 4850 v8::Persistent<Context> env2 = Context::New(); 4851 4852 Local<Value> foo = v8_str("foo"); 4853 4854 // Set same security token for env1 and env2. 4855 env1->SetSecurityToken(foo); 4856 env2->SetSecurityToken(foo); 4857 4858 // Create a property on the global object in env2. 4859 { 4860 v8::Context::Scope scope(env2); 4861 env2->Global()->Set(v8_str("p"), v8::Integer::New(42)); 4862 } 4863 4864 // Create a reference to env2 global from env1 global. 4865 env1->Global()->Set(v8_str("other"), env2->Global()); 4866 4867 // Check that we have access to other.p in env2 from env1. 4868 Local<Value> result = CompileRun("other.p"); 4869 CHECK(result->IsInt32()); 4870 CHECK_EQ(42, result->Int32Value()); 4871 4872 // Hold on to global from env2 and detach global from env2. 4873 Local<v8::Object> global2 = env2->Global(); 4874 env2->DetachGlobal(); 4875 4876 // Check that the global has been detached. No other.p property can 4877 // be found. 4878 result = CompileRun("other.p"); 4879 CHECK(result->IsUndefined()); 4880 4881 // Reuse global2 for env3. 4882 v8::Persistent<Context> env3 = 4883 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 4884 CHECK_EQ(global2, env3->Global()); 4885 4886 // Start by using the same security token for env3 as for env1 and env2. 4887 env3->SetSecurityToken(foo); 4888 4889 // Create a property on the global object in env3. 4890 { 4891 v8::Context::Scope scope(env3); 4892 env3->Global()->Set(v8_str("p"), v8::Integer::New(24)); 4893 } 4894 4895 // Check that other.p is now the property in env3 and that we have access. 4896 result = CompileRun("other.p"); 4897 CHECK(result->IsInt32()); 4898 CHECK_EQ(24, result->Int32Value()); 4899 4900 // Change security token for env3 to something different from env1 and env2. 4901 env3->SetSecurityToken(v8_str("bar")); 4902 4903 // Check that we do not have access to other.p in env1. |other| is now 4904 // the global object for env3 which has a different security token, 4905 // so access should be blocked. 4906 result = CompileRun("other.p"); 4907 CHECK(result->IsUndefined()); 4908 4909 // Detach the global for env3 and reattach it to env2. 4910 env3->DetachGlobal(); 4911 env2->ReattachGlobal(global2); 4912 4913 // Check that we have access to other.p again in env1. |other| is now 4914 // the global object for env2 which has the same security token as env1. 4915 result = CompileRun("other.p"); 4916 CHECK(result->IsInt32()); 4917 CHECK_EQ(42, result->Int32Value()); 4918 4919 env2.Dispose(); 4920 env3.Dispose(); 4921} 4922 4923 4924static bool NamedAccessBlocker(Local<v8::Object> global, 4925 Local<Value> name, 4926 v8::AccessType type, 4927 Local<Value> data) { 4928 return Context::GetCurrent()->Global()->Equals(global); 4929} 4930 4931 4932static bool IndexedAccessBlocker(Local<v8::Object> global, 4933 uint32_t key, 4934 v8::AccessType type, 4935 Local<Value> data) { 4936 return Context::GetCurrent()->Global()->Equals(global); 4937} 4938 4939 4940static int g_echo_value = -1; 4941static v8::Handle<Value> EchoGetter(Local<String> name, 4942 const AccessorInfo& info) { 4943 return v8_num(g_echo_value); 4944} 4945 4946 4947static void EchoSetter(Local<String> name, 4948 Local<Value> value, 4949 const AccessorInfo&) { 4950 if (value->IsNumber()) 4951 g_echo_value = value->Int32Value(); 4952} 4953 4954 4955static v8::Handle<Value> UnreachableGetter(Local<String> name, 4956 const AccessorInfo& info) { 4957 CHECK(false); // This function should not be called.. 4958 return v8::Undefined(); 4959} 4960 4961 4962static void UnreachableSetter(Local<String>, Local<Value>, 4963 const AccessorInfo&) { 4964 CHECK(false); // This function should nto be called. 4965} 4966 4967 4968THREADED_TEST(AccessControl) { 4969 v8::HandleScope handle_scope; 4970 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 4971 4972 global_template->SetAccessCheckCallbacks(NamedAccessBlocker, 4973 IndexedAccessBlocker); 4974 4975 // Add an accessor accessible by cross-domain JS code. 4976 global_template->SetAccessor( 4977 v8_str("accessible_prop"), 4978 EchoGetter, EchoSetter, 4979 v8::Handle<Value>(), 4980 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); 4981 4982 // Add an accessor that is not accessible by cross-domain JS code. 4983 global_template->SetAccessor(v8_str("blocked_prop"), 4984 UnreachableGetter, UnreachableSetter, 4985 v8::Handle<Value>(), 4986 v8::DEFAULT); 4987 4988 // Create an environment 4989 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 4990 context0->Enter(); 4991 4992 v8::Handle<v8::Object> global0 = context0->Global(); 4993 4994 v8::HandleScope scope1; 4995 4996 v8::Persistent<Context> context1 = Context::New(); 4997 context1->Enter(); 4998 4999 v8::Handle<v8::Object> global1 = context1->Global(); 5000 global1->Set(v8_str("other"), global0); 5001 5002 v8::Handle<Value> value; 5003 5004 // Access blocked property 5005 value = v8_compile("other.blocked_prop = 1")->Run(); 5006 value = v8_compile("other.blocked_prop")->Run(); 5007 CHECK(value->IsUndefined()); 5008 5009 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run(); 5010 CHECK(value->IsFalse()); 5011 5012 // Access accessible property 5013 value = v8_compile("other.accessible_prop = 3")->Run(); 5014 CHECK(value->IsNumber()); 5015 CHECK_EQ(3, value->Int32Value()); 5016 CHECK_EQ(3, g_echo_value); 5017 5018 value = v8_compile("other.accessible_prop")->Run(); 5019 CHECK(value->IsNumber()); 5020 CHECK_EQ(3, value->Int32Value()); 5021 5022 value = 5023 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run(); 5024 CHECK(value->IsTrue()); 5025 5026 // Enumeration doesn't enumerate accessors from inaccessible objects in 5027 // the prototype chain even if the accessors are in themselves accessible. 5028 Local<Value> result = 5029 CompileRun("(function(){var obj = {'__proto__':other};" 5030 "for (var p in obj)" 5031 " if (p == 'accessible_prop' || p == 'blocked_prop') {" 5032 " return false;" 5033 " }" 5034 "return true;})()"); 5035 CHECK(result->IsTrue()); 5036 5037 context1->Exit(); 5038 context0->Exit(); 5039 context1.Dispose(); 5040 context0.Dispose(); 5041} 5042 5043 5044static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global, 5045 Local<Value> name, 5046 v8::AccessType type, 5047 Local<Value> data) { 5048 return false; 5049} 5050 5051 5052static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global, 5053 uint32_t key, 5054 v8::AccessType type, 5055 Local<Value> data) { 5056 return false; 5057} 5058 5059 5060THREADED_TEST(AccessControlGetOwnPropertyNames) { 5061 v8::HandleScope handle_scope; 5062 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(); 5063 5064 obj_template->Set(v8_str("x"), v8::Integer::New(42)); 5065 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker, 5066 GetOwnPropertyNamesIndexedBlocker); 5067 5068 // Create an environment 5069 v8::Persistent<Context> context0 = Context::New(NULL, obj_template); 5070 context0->Enter(); 5071 5072 v8::Handle<v8::Object> global0 = context0->Global(); 5073 5074 v8::HandleScope scope1; 5075 5076 v8::Persistent<Context> context1 = Context::New(); 5077 context1->Enter(); 5078 5079 v8::Handle<v8::Object> global1 = context1->Global(); 5080 global1->Set(v8_str("other"), global0); 5081 global1->Set(v8_str("object"), obj_template->NewInstance()); 5082 5083 v8::Handle<Value> value; 5084 5085 // Attempt to get the property names of the other global object and 5086 // of an object that requires access checks. Accessing the other 5087 // global object should be blocked by access checks on the global 5088 // proxy object. Accessing the object that requires access checks 5089 // is blocked by the access checks on the object itself. 5090 value = CompileRun("Object.getOwnPropertyNames(other).length == 0"); 5091 CHECK(value->IsTrue()); 5092 5093 value = CompileRun("Object.getOwnPropertyNames(object).length == 0"); 5094 CHECK(value->IsTrue()); 5095 5096 context1->Exit(); 5097 context0->Exit(); 5098 context1.Dispose(); 5099 context0.Dispose(); 5100} 5101 5102 5103static v8::Handle<v8::Array> NamedPropertyEnumerator(const AccessorInfo& info) { 5104 v8::Handle<v8::Array> result = v8::Array::New(1); 5105 result->Set(0, v8_str("x")); 5106 return result; 5107} 5108 5109 5110THREADED_TEST(GetOwnPropertyNamesWithInterceptor) { 5111 v8::HandleScope handle_scope; 5112 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(); 5113 5114 obj_template->Set(v8_str("x"), v8::Integer::New(42)); 5115 obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL, 5116 NamedPropertyEnumerator); 5117 5118 LocalContext context; 5119 v8::Handle<v8::Object> global = context->Global(); 5120 global->Set(v8_str("object"), obj_template->NewInstance()); 5121 5122 v8::Handle<Value> value = 5123 CompileRun("Object.getOwnPropertyNames(object).join(',')"); 5124 CHECK_EQ(v8_str("x"), value); 5125} 5126 5127 5128static v8::Handle<Value> ConstTenGetter(Local<String> name, 5129 const AccessorInfo& info) { 5130 return v8_num(10); 5131} 5132 5133 5134THREADED_TEST(CrossDomainAccessors) { 5135 v8::HandleScope handle_scope; 5136 5137 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(); 5138 5139 v8::Handle<v8::ObjectTemplate> global_template = 5140 func_template->InstanceTemplate(); 5141 5142 v8::Handle<v8::ObjectTemplate> proto_template = 5143 func_template->PrototypeTemplate(); 5144 5145 // Add an accessor to proto that's accessible by cross-domain JS code. 5146 proto_template->SetAccessor(v8_str("accessible"), 5147 ConstTenGetter, 0, 5148 v8::Handle<Value>(), 5149 v8::ALL_CAN_READ); 5150 5151 // Add an accessor that is not accessible by cross-domain JS code. 5152 global_template->SetAccessor(v8_str("unreachable"), 5153 UnreachableGetter, 0, 5154 v8::Handle<Value>(), 5155 v8::DEFAULT); 5156 5157 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 5158 context0->Enter(); 5159 5160 Local<v8::Object> global = context0->Global(); 5161 // Add a normal property that shadows 'accessible' 5162 global->Set(v8_str("accessible"), v8_num(11)); 5163 5164 // Enter a new context. 5165 v8::HandleScope scope1; 5166 v8::Persistent<Context> context1 = Context::New(); 5167 context1->Enter(); 5168 5169 v8::Handle<v8::Object> global1 = context1->Global(); 5170 global1->Set(v8_str("other"), global); 5171 5172 // Should return 10, instead of 11 5173 v8::Handle<Value> value = v8_compile("other.accessible")->Run(); 5174 CHECK(value->IsNumber()); 5175 CHECK_EQ(10, value->Int32Value()); 5176 5177 value = v8_compile("other.unreachable")->Run(); 5178 CHECK(value->IsUndefined()); 5179 5180 context1->Exit(); 5181 context0->Exit(); 5182 context1.Dispose(); 5183 context0.Dispose(); 5184} 5185 5186 5187static int named_access_count = 0; 5188static int indexed_access_count = 0; 5189 5190static bool NamedAccessCounter(Local<v8::Object> global, 5191 Local<Value> name, 5192 v8::AccessType type, 5193 Local<Value> data) { 5194 named_access_count++; 5195 return true; 5196} 5197 5198 5199static bool IndexedAccessCounter(Local<v8::Object> global, 5200 uint32_t key, 5201 v8::AccessType type, 5202 Local<Value> data) { 5203 indexed_access_count++; 5204 return true; 5205} 5206 5207 5208// This one is too easily disturbed by other tests. 5209TEST(AccessControlIC) { 5210 named_access_count = 0; 5211 indexed_access_count = 0; 5212 5213 v8::HandleScope handle_scope; 5214 5215 // Create an environment. 5216 v8::Persistent<Context> context0 = Context::New(); 5217 context0->Enter(); 5218 5219 // Create an object that requires access-check functions to be 5220 // called for cross-domain access. 5221 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 5222 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 5223 IndexedAccessCounter); 5224 Local<v8::Object> object = object_template->NewInstance(); 5225 5226 v8::HandleScope scope1; 5227 5228 // Create another environment. 5229 v8::Persistent<Context> context1 = Context::New(); 5230 context1->Enter(); 5231 5232 // Make easy access to the object from the other environment. 5233 v8::Handle<v8::Object> global1 = context1->Global(); 5234 global1->Set(v8_str("obj"), object); 5235 5236 v8::Handle<Value> value; 5237 5238 // Check that the named access-control function is called every time. 5239 CompileRun("function testProp(obj) {" 5240 " for (var i = 0; i < 10; i++) obj.prop = 1;" 5241 " for (var j = 0; j < 10; j++) obj.prop;" 5242 " return obj.prop" 5243 "}"); 5244 value = CompileRun("testProp(obj)"); 5245 CHECK(value->IsNumber()); 5246 CHECK_EQ(1, value->Int32Value()); 5247 CHECK_EQ(21, named_access_count); 5248 5249 // Check that the named access-control function is called every time. 5250 CompileRun("var p = 'prop';" 5251 "function testKeyed(obj) {" 5252 " for (var i = 0; i < 10; i++) obj[p] = 1;" 5253 " for (var j = 0; j < 10; j++) obj[p];" 5254 " return obj[p];" 5255 "}"); 5256 // Use obj which requires access checks. No inline caching is used 5257 // in that case. 5258 value = CompileRun("testKeyed(obj)"); 5259 CHECK(value->IsNumber()); 5260 CHECK_EQ(1, value->Int32Value()); 5261 CHECK_EQ(42, named_access_count); 5262 // Force the inline caches into generic state and try again. 5263 CompileRun("testKeyed({ a: 0 })"); 5264 CompileRun("testKeyed({ b: 0 })"); 5265 value = CompileRun("testKeyed(obj)"); 5266 CHECK(value->IsNumber()); 5267 CHECK_EQ(1, value->Int32Value()); 5268 CHECK_EQ(63, named_access_count); 5269 5270 // Check that the indexed access-control function is called every time. 5271 CompileRun("function testIndexed(obj) {" 5272 " for (var i = 0; i < 10; i++) obj[0] = 1;" 5273 " for (var j = 0; j < 10; j++) obj[0];" 5274 " return obj[0]" 5275 "}"); 5276 value = CompileRun("testIndexed(obj)"); 5277 CHECK(value->IsNumber()); 5278 CHECK_EQ(1, value->Int32Value()); 5279 CHECK_EQ(21, indexed_access_count); 5280 // Force the inline caches into generic state. 5281 CompileRun("testIndexed(new Array(1))"); 5282 // Test that the indexed access check is called. 5283 value = CompileRun("testIndexed(obj)"); 5284 CHECK(value->IsNumber()); 5285 CHECK_EQ(1, value->Int32Value()); 5286 CHECK_EQ(42, indexed_access_count); 5287 5288 // Check that the named access check is called when invoking 5289 // functions on an object that requires access checks. 5290 CompileRun("obj.f = function() {}"); 5291 CompileRun("function testCallNormal(obj) {" 5292 " for (var i = 0; i < 10; i++) obj.f();" 5293 "}"); 5294 CompileRun("testCallNormal(obj)"); 5295 CHECK_EQ(74, named_access_count); 5296 5297 // Force obj into slow case. 5298 value = CompileRun("delete obj.prop"); 5299 CHECK(value->BooleanValue()); 5300 // Force inline caches into dictionary probing mode. 5301 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);"); 5302 // Test that the named access check is called. 5303 value = CompileRun("testProp(obj);"); 5304 CHECK(value->IsNumber()); 5305 CHECK_EQ(1, value->Int32Value()); 5306 CHECK_EQ(96, named_access_count); 5307 5308 // Force the call inline cache into dictionary probing mode. 5309 CompileRun("o.f = function() {}; testCallNormal(o)"); 5310 // Test that the named access check is still called for each 5311 // invocation of the function. 5312 value = CompileRun("testCallNormal(obj)"); 5313 CHECK_EQ(106, named_access_count); 5314 5315 context1->Exit(); 5316 context0->Exit(); 5317 context1.Dispose(); 5318 context0.Dispose(); 5319} 5320 5321 5322static bool NamedAccessFlatten(Local<v8::Object> global, 5323 Local<Value> name, 5324 v8::AccessType type, 5325 Local<Value> data) { 5326 char buf[100]; 5327 int len; 5328 5329 CHECK(name->IsString()); 5330 5331 memset(buf, 0x1, sizeof(buf)); 5332 len = name.As<String>()->WriteAscii(buf); 5333 CHECK_EQ(4, len); 5334 5335 uint16_t buf2[100]; 5336 5337 memset(buf, 0x1, sizeof(buf)); 5338 len = name.As<String>()->Write(buf2); 5339 CHECK_EQ(4, len); 5340 5341 return true; 5342} 5343 5344 5345static bool IndexedAccessFlatten(Local<v8::Object> global, 5346 uint32_t key, 5347 v8::AccessType type, 5348 Local<Value> data) { 5349 return true; 5350} 5351 5352 5353// Regression test. In access checks, operations that may cause 5354// garbage collection are not allowed. It used to be the case that 5355// using the Write operation on a string could cause a garbage 5356// collection due to flattening of the string. This is no longer the 5357// case. 5358THREADED_TEST(AccessControlFlatten) { 5359 named_access_count = 0; 5360 indexed_access_count = 0; 5361 5362 v8::HandleScope handle_scope; 5363 5364 // Create an environment. 5365 v8::Persistent<Context> context0 = Context::New(); 5366 context0->Enter(); 5367 5368 // Create an object that requires access-check functions to be 5369 // called for cross-domain access. 5370 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 5371 object_template->SetAccessCheckCallbacks(NamedAccessFlatten, 5372 IndexedAccessFlatten); 5373 Local<v8::Object> object = object_template->NewInstance(); 5374 5375 v8::HandleScope scope1; 5376 5377 // Create another environment. 5378 v8::Persistent<Context> context1 = Context::New(); 5379 context1->Enter(); 5380 5381 // Make easy access to the object from the other environment. 5382 v8::Handle<v8::Object> global1 = context1->Global(); 5383 global1->Set(v8_str("obj"), object); 5384 5385 v8::Handle<Value> value; 5386 5387 value = v8_compile("var p = 'as' + 'df';")->Run(); 5388 value = v8_compile("obj[p];")->Run(); 5389 5390 context1->Exit(); 5391 context0->Exit(); 5392 context1.Dispose(); 5393 context0.Dispose(); 5394} 5395 5396 5397static v8::Handle<Value> AccessControlNamedGetter( 5398 Local<String>, const AccessorInfo&) { 5399 return v8::Integer::New(42); 5400} 5401 5402 5403static v8::Handle<Value> AccessControlNamedSetter( 5404 Local<String>, Local<Value> value, const AccessorInfo&) { 5405 return value; 5406} 5407 5408 5409static v8::Handle<Value> AccessControlIndexedGetter( 5410 uint32_t index, 5411 const AccessorInfo& info) { 5412 return v8_num(42); 5413} 5414 5415 5416static v8::Handle<Value> AccessControlIndexedSetter( 5417 uint32_t, Local<Value> value, const AccessorInfo&) { 5418 return value; 5419} 5420 5421 5422THREADED_TEST(AccessControlInterceptorIC) { 5423 named_access_count = 0; 5424 indexed_access_count = 0; 5425 5426 v8::HandleScope handle_scope; 5427 5428 // Create an environment. 5429 v8::Persistent<Context> context0 = Context::New(); 5430 context0->Enter(); 5431 5432 // Create an object that requires access-check functions to be 5433 // called for cross-domain access. The object also has interceptors 5434 // interceptor. 5435 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 5436 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 5437 IndexedAccessCounter); 5438 object_template->SetNamedPropertyHandler(AccessControlNamedGetter, 5439 AccessControlNamedSetter); 5440 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter, 5441 AccessControlIndexedSetter); 5442 Local<v8::Object> object = object_template->NewInstance(); 5443 5444 v8::HandleScope scope1; 5445 5446 // Create another environment. 5447 v8::Persistent<Context> context1 = Context::New(); 5448 context1->Enter(); 5449 5450 // Make easy access to the object from the other environment. 5451 v8::Handle<v8::Object> global1 = context1->Global(); 5452 global1->Set(v8_str("obj"), object); 5453 5454 v8::Handle<Value> value; 5455 5456 // Check that the named access-control function is called every time 5457 // eventhough there is an interceptor on the object. 5458 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run(); 5459 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;" 5460 "obj.x")->Run(); 5461 CHECK(value->IsNumber()); 5462 CHECK_EQ(42, value->Int32Value()); 5463 CHECK_EQ(21, named_access_count); 5464 5465 value = v8_compile("var p = 'x';")->Run(); 5466 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run(); 5467 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];" 5468 "obj[p]")->Run(); 5469 CHECK(value->IsNumber()); 5470 CHECK_EQ(42, value->Int32Value()); 5471 CHECK_EQ(42, named_access_count); 5472 5473 // Check that the indexed access-control function is called every 5474 // time eventhough there is an interceptor on the object. 5475 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run(); 5476 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];" 5477 "obj[0]")->Run(); 5478 CHECK(value->IsNumber()); 5479 CHECK_EQ(42, value->Int32Value()); 5480 CHECK_EQ(21, indexed_access_count); 5481 5482 context1->Exit(); 5483 context0->Exit(); 5484 context1.Dispose(); 5485 context0.Dispose(); 5486} 5487 5488 5489THREADED_TEST(Version) { 5490 v8::V8::GetVersion(); 5491} 5492 5493 5494static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) { 5495 ApiTestFuzzer::Fuzz(); 5496 return v8_num(12); 5497} 5498 5499 5500THREADED_TEST(InstanceProperties) { 5501 v8::HandleScope handle_scope; 5502 LocalContext context; 5503 5504 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5505 Local<ObjectTemplate> instance = t->InstanceTemplate(); 5506 5507 instance->Set(v8_str("x"), v8_num(42)); 5508 instance->Set(v8_str("f"), 5509 v8::FunctionTemplate::New(InstanceFunctionCallback)); 5510 5511 Local<Value> o = t->GetFunction()->NewInstance(); 5512 5513 context->Global()->Set(v8_str("i"), o); 5514 Local<Value> value = Script::Compile(v8_str("i.x"))->Run(); 5515 CHECK_EQ(42, value->Int32Value()); 5516 5517 value = Script::Compile(v8_str("i.f()"))->Run(); 5518 CHECK_EQ(12, value->Int32Value()); 5519} 5520 5521 5522static v8::Handle<Value> 5523GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) { 5524 ApiTestFuzzer::Fuzz(); 5525 return v8::Handle<Value>(); 5526} 5527 5528 5529THREADED_TEST(GlobalObjectInstanceProperties) { 5530 v8::HandleScope handle_scope; 5531 5532 Local<Value> global_object; 5533 5534 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5535 t->InstanceTemplate()->SetNamedPropertyHandler( 5536 GlobalObjectInstancePropertiesGet); 5537 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 5538 instance_template->Set(v8_str("x"), v8_num(42)); 5539 instance_template->Set(v8_str("f"), 5540 v8::FunctionTemplate::New(InstanceFunctionCallback)); 5541 5542 { 5543 LocalContext env(NULL, instance_template); 5544 // Hold on to the global object so it can be used again in another 5545 // environment initialization. 5546 global_object = env->Global(); 5547 5548 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 5549 CHECK_EQ(42, value->Int32Value()); 5550 value = Script::Compile(v8_str("f()"))->Run(); 5551 CHECK_EQ(12, value->Int32Value()); 5552 } 5553 5554 { 5555 // Create new environment reusing the global object. 5556 LocalContext env(NULL, instance_template, global_object); 5557 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 5558 CHECK_EQ(42, value->Int32Value()); 5559 value = Script::Compile(v8_str("f()"))->Run(); 5560 CHECK_EQ(12, value->Int32Value()); 5561 } 5562} 5563 5564 5565static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) { 5566 ApiTestFuzzer::Fuzz(); 5567 return v8_num(42); 5568} 5569 5570 5571static int shadow_y; 5572static int shadow_y_setter_call_count; 5573static int shadow_y_getter_call_count; 5574 5575 5576static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) { 5577 shadow_y_setter_call_count++; 5578 shadow_y = 42; 5579} 5580 5581 5582static v8::Handle<Value> ShadowYGetter(Local<String> name, 5583 const AccessorInfo& info) { 5584 ApiTestFuzzer::Fuzz(); 5585 shadow_y_getter_call_count++; 5586 return v8_num(shadow_y); 5587} 5588 5589 5590static v8::Handle<Value> ShadowIndexedGet(uint32_t index, 5591 const AccessorInfo& info) { 5592 return v8::Handle<Value>(); 5593} 5594 5595 5596static v8::Handle<Value> ShadowNamedGet(Local<String> key, 5597 const AccessorInfo&) { 5598 return v8::Handle<Value>(); 5599} 5600 5601 5602THREADED_TEST(ShadowObject) { 5603 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0; 5604 v8::HandleScope handle_scope; 5605 5606 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New(); 5607 LocalContext context(NULL, global_template); 5608 5609 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5610 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet); 5611 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet); 5612 Local<ObjectTemplate> proto = t->PrototypeTemplate(); 5613 Local<ObjectTemplate> instance = t->InstanceTemplate(); 5614 5615 // Only allow calls of f on instances of t. 5616 Local<v8::Signature> signature = v8::Signature::New(t); 5617 proto->Set(v8_str("f"), 5618 v8::FunctionTemplate::New(ShadowFunctionCallback, 5619 Local<Value>(), 5620 signature)); 5621 proto->Set(v8_str("x"), v8_num(12)); 5622 5623 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter); 5624 5625 Local<Value> o = t->GetFunction()->NewInstance(); 5626 context->Global()->Set(v8_str("__proto__"), o); 5627 5628 Local<Value> value = 5629 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run(); 5630 CHECK(value->IsBoolean()); 5631 CHECK(!value->BooleanValue()); 5632 5633 value = Script::Compile(v8_str("x"))->Run(); 5634 CHECK_EQ(12, value->Int32Value()); 5635 5636 value = Script::Compile(v8_str("f()"))->Run(); 5637 CHECK_EQ(42, value->Int32Value()); 5638 5639 Script::Compile(v8_str("y = 42"))->Run(); 5640 CHECK_EQ(1, shadow_y_setter_call_count); 5641 value = Script::Compile(v8_str("y"))->Run(); 5642 CHECK_EQ(1, shadow_y_getter_call_count); 5643 CHECK_EQ(42, value->Int32Value()); 5644} 5645 5646 5647THREADED_TEST(HiddenPrototype) { 5648 v8::HandleScope handle_scope; 5649 LocalContext context; 5650 5651 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 5652 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 5653 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 5654 t1->SetHiddenPrototype(true); 5655 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 5656 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 5657 t2->SetHiddenPrototype(true); 5658 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 5659 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 5660 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 5661 5662 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 5663 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 5664 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 5665 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 5666 5667 // Setting the prototype on an object skips hidden prototypes. 5668 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5669 o0->Set(v8_str("__proto__"), o1); 5670 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5671 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5672 o0->Set(v8_str("__proto__"), o2); 5673 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5674 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5675 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5676 o0->Set(v8_str("__proto__"), o3); 5677 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5678 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5679 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5680 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 5681 5682 // Getting the prototype of o0 should get the first visible one 5683 // which is o3. Therefore, z should not be defined on the prototype 5684 // object. 5685 Local<Value> proto = o0->Get(v8_str("__proto__")); 5686 CHECK(proto->IsObject()); 5687 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined()); 5688} 5689 5690 5691THREADED_TEST(SetPrototype) { 5692 v8::HandleScope handle_scope; 5693 LocalContext context; 5694 5695 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 5696 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 5697 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 5698 t1->SetHiddenPrototype(true); 5699 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 5700 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 5701 t2->SetHiddenPrototype(true); 5702 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 5703 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 5704 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 5705 5706 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 5707 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 5708 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 5709 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 5710 5711 // Setting the prototype on an object does not skip hidden prototypes. 5712 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5713 CHECK(o0->SetPrototype(o1)); 5714 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5715 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5716 CHECK(o1->SetPrototype(o2)); 5717 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5718 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5719 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5720 CHECK(o2->SetPrototype(o3)); 5721 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5722 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5723 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5724 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 5725 5726 // Getting the prototype of o0 should get the first visible one 5727 // which is o3. Therefore, z should not be defined on the prototype 5728 // object. 5729 Local<Value> proto = o0->Get(v8_str("__proto__")); 5730 CHECK(proto->IsObject()); 5731 CHECK_EQ(proto.As<v8::Object>(), o3); 5732 5733 // However, Object::GetPrototype ignores hidden prototype. 5734 Local<Value> proto0 = o0->GetPrototype(); 5735 CHECK(proto0->IsObject()); 5736 CHECK_EQ(proto0.As<v8::Object>(), o1); 5737 5738 Local<Value> proto1 = o1->GetPrototype(); 5739 CHECK(proto1->IsObject()); 5740 CHECK_EQ(proto1.As<v8::Object>(), o2); 5741 5742 Local<Value> proto2 = o2->GetPrototype(); 5743 CHECK(proto2->IsObject()); 5744 CHECK_EQ(proto2.As<v8::Object>(), o3); 5745} 5746 5747 5748THREADED_TEST(SetPrototypeThrows) { 5749 v8::HandleScope handle_scope; 5750 LocalContext context; 5751 5752 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5753 5754 Local<v8::Object> o0 = t->GetFunction()->NewInstance(); 5755 Local<v8::Object> o1 = t->GetFunction()->NewInstance(); 5756 5757 CHECK(o0->SetPrototype(o1)); 5758 // If setting the prototype leads to the cycle, SetPrototype should 5759 // return false and keep VM in sane state. 5760 v8::TryCatch try_catch; 5761 CHECK(!o1->SetPrototype(o0)); 5762 CHECK(!try_catch.HasCaught()); 5763 ASSERT(!i::Top::has_pending_exception()); 5764 5765 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value()); 5766} 5767 5768 5769THREADED_TEST(GetterSetterExceptions) { 5770 v8::HandleScope handle_scope; 5771 LocalContext context; 5772 CompileRun( 5773 "function Foo() { };" 5774 "function Throw() { throw 5; };" 5775 "var x = { };" 5776 "x.__defineSetter__('set', Throw);" 5777 "x.__defineGetter__('get', Throw);"); 5778 Local<v8::Object> x = 5779 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x"))); 5780 v8::TryCatch try_catch; 5781 x->Set(v8_str("set"), v8::Integer::New(8)); 5782 x->Get(v8_str("get")); 5783 x->Set(v8_str("set"), v8::Integer::New(8)); 5784 x->Get(v8_str("get")); 5785 x->Set(v8_str("set"), v8::Integer::New(8)); 5786 x->Get(v8_str("get")); 5787 x->Set(v8_str("set"), v8::Integer::New(8)); 5788 x->Get(v8_str("get")); 5789} 5790 5791 5792THREADED_TEST(Constructor) { 5793 v8::HandleScope handle_scope; 5794 LocalContext context; 5795 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 5796 templ->SetClassName(v8_str("Fun")); 5797 Local<Function> cons = templ->GetFunction(); 5798 context->Global()->Set(v8_str("Fun"), cons); 5799 Local<v8::Object> inst = cons->NewInstance(); 5800 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst); 5801 Local<Value> value = CompileRun("(new Fun()).constructor === Fun"); 5802 CHECK(value->BooleanValue()); 5803} 5804 5805THREADED_TEST(FunctionDescriptorException) { 5806 v8::HandleScope handle_scope; 5807 LocalContext context; 5808 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 5809 templ->SetClassName(v8_str("Fun")); 5810 Local<Function> cons = templ->GetFunction(); 5811 context->Global()->Set(v8_str("Fun"), cons); 5812 Local<Value> value = CompileRun( 5813 "function test() {" 5814 " try {" 5815 " (new Fun()).blah()" 5816 " } catch (e) {" 5817 " var str = String(e);" 5818 " if (str.indexOf('TypeError') == -1) return 1;" 5819 " if (str.indexOf('[object Fun]') != -1) return 2;" 5820 " if (str.indexOf('#<a Fun>') == -1) return 3;" 5821 " return 0;" 5822 " }" 5823 " return 4;" 5824 "}" 5825 "test();"); 5826 CHECK_EQ(0, value->Int32Value()); 5827} 5828 5829 5830THREADED_TEST(EvalAliasedDynamic) { 5831 v8::HandleScope scope; 5832 LocalContext current; 5833 5834 // Tests where aliased eval can only be resolved dynamically. 5835 Local<Script> script = 5836 Script::Compile(v8_str("function f(x) { " 5837 " var foo = 2;" 5838 " with (x) { return eval('foo'); }" 5839 "}" 5840 "foo = 0;" 5841 "result1 = f(new Object());" 5842 "result2 = f(this);" 5843 "var x = new Object();" 5844 "x.eval = function(x) { return 1; };" 5845 "result3 = f(x);")); 5846 script->Run(); 5847 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value()); 5848 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value()); 5849 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value()); 5850 5851 v8::TryCatch try_catch; 5852 script = 5853 Script::Compile(v8_str("function f(x) { " 5854 " var bar = 2;" 5855 " with (x) { return eval('bar'); }" 5856 "}" 5857 "f(this)")); 5858 script->Run(); 5859 CHECK(try_catch.HasCaught()); 5860 try_catch.Reset(); 5861} 5862 5863 5864THREADED_TEST(CrossEval) { 5865 v8::HandleScope scope; 5866 LocalContext other; 5867 LocalContext current; 5868 5869 Local<String> token = v8_str("<security token>"); 5870 other->SetSecurityToken(token); 5871 current->SetSecurityToken(token); 5872 5873 // Setup reference from current to other. 5874 current->Global()->Set(v8_str("other"), other->Global()); 5875 5876 // Check that new variables are introduced in other context. 5877 Local<Script> script = 5878 Script::Compile(v8_str("other.eval('var foo = 1234')")); 5879 script->Run(); 5880 Local<Value> foo = other->Global()->Get(v8_str("foo")); 5881 CHECK_EQ(1234, foo->Int32Value()); 5882 CHECK(!current->Global()->Has(v8_str("foo"))); 5883 5884 // Check that writing to non-existing properties introduces them in 5885 // the other context. 5886 script = 5887 Script::Compile(v8_str("other.eval('na = 1234')")); 5888 script->Run(); 5889 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value()); 5890 CHECK(!current->Global()->Has(v8_str("na"))); 5891 5892 // Check that global variables in current context are not visible in other 5893 // context. 5894 v8::TryCatch try_catch; 5895 script = 5896 Script::Compile(v8_str("var bar = 42; other.eval('bar');")); 5897 Local<Value> result = script->Run(); 5898 CHECK(try_catch.HasCaught()); 5899 try_catch.Reset(); 5900 5901 // Check that local variables in current context are not visible in other 5902 // context. 5903 script = 5904 Script::Compile(v8_str("(function() { " 5905 " var baz = 87;" 5906 " return other.eval('baz');" 5907 "})();")); 5908 result = script->Run(); 5909 CHECK(try_catch.HasCaught()); 5910 try_catch.Reset(); 5911 5912 // Check that global variables in the other environment are visible 5913 // when evaluting code. 5914 other->Global()->Set(v8_str("bis"), v8_num(1234)); 5915 script = Script::Compile(v8_str("other.eval('bis')")); 5916 CHECK_EQ(1234, script->Run()->Int32Value()); 5917 CHECK(!try_catch.HasCaught()); 5918 5919 // Check that the 'this' pointer points to the global object evaluating 5920 // code. 5921 other->Global()->Set(v8_str("t"), other->Global()); 5922 script = Script::Compile(v8_str("other.eval('this == t')")); 5923 result = script->Run(); 5924 CHECK(result->IsTrue()); 5925 CHECK(!try_catch.HasCaught()); 5926 5927 // Check that variables introduced in with-statement are not visible in 5928 // other context. 5929 script = 5930 Script::Compile(v8_str("with({x:2}){other.eval('x')}")); 5931 result = script->Run(); 5932 CHECK(try_catch.HasCaught()); 5933 try_catch.Reset(); 5934 5935 // Check that you cannot use 'eval.call' with another object than the 5936 // current global object. 5937 script = 5938 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')")); 5939 result = script->Run(); 5940 CHECK(try_catch.HasCaught()); 5941} 5942 5943 5944// Test that calling eval in a context which has been detached from 5945// its global throws an exception. This behavior is consistent with 5946// other JavaScript implementations. 5947THREADED_TEST(EvalInDetachedGlobal) { 5948 v8::HandleScope scope; 5949 5950 v8::Persistent<Context> context0 = Context::New(); 5951 v8::Persistent<Context> context1 = Context::New(); 5952 5953 // Setup function in context0 that uses eval from context0. 5954 context0->Enter(); 5955 v8::Handle<v8::Value> fun = 5956 CompileRun("var x = 42;" 5957 "(function() {" 5958 " var e = eval;" 5959 " return function(s) { return e(s); }" 5960 "})()"); 5961 context0->Exit(); 5962 5963 // Put the function into context1 and call it before and after 5964 // detaching the global. Before detaching, the call succeeds and 5965 // after detaching and exception is thrown. 5966 context1->Enter(); 5967 context1->Global()->Set(v8_str("fun"), fun); 5968 v8::Handle<v8::Value> x_value = CompileRun("fun('x')"); 5969 CHECK_EQ(42, x_value->Int32Value()); 5970 context0->DetachGlobal(); 5971 v8::TryCatch catcher; 5972 x_value = CompileRun("fun('x')"); 5973 CHECK(x_value.IsEmpty()); 5974 CHECK(catcher.HasCaught()); 5975 context1->Exit(); 5976 5977 context1.Dispose(); 5978 context0.Dispose(); 5979} 5980 5981 5982THREADED_TEST(CrossLazyLoad) { 5983 v8::HandleScope scope; 5984 LocalContext other; 5985 LocalContext current; 5986 5987 Local<String> token = v8_str("<security token>"); 5988 other->SetSecurityToken(token); 5989 current->SetSecurityToken(token); 5990 5991 // Setup reference from current to other. 5992 current->Global()->Set(v8_str("other"), other->Global()); 5993 5994 // Trigger lazy loading in other context. 5995 Local<Script> script = 5996 Script::Compile(v8_str("other.eval('new Date(42)')")); 5997 Local<Value> value = script->Run(); 5998 CHECK_EQ(42.0, value->NumberValue()); 5999} 6000 6001 6002static v8::Handle<Value> call_as_function(const v8::Arguments& args) { 6003 ApiTestFuzzer::Fuzz(); 6004 if (args.IsConstructCall()) { 6005 if (args[0]->IsInt32()) { 6006 return v8_num(-args[0]->Int32Value()); 6007 } 6008 } 6009 6010 return args[0]; 6011} 6012 6013 6014// Test that a call handler can be set for objects which will allow 6015// non-function objects created through the API to be called as 6016// functions. 6017THREADED_TEST(CallAsFunction) { 6018 v8::HandleScope scope; 6019 LocalContext context; 6020 6021 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 6022 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 6023 instance_template->SetCallAsFunctionHandler(call_as_function); 6024 Local<v8::Object> instance = t->GetFunction()->NewInstance(); 6025 context->Global()->Set(v8_str("obj"), instance); 6026 v8::TryCatch try_catch; 6027 Local<Value> value; 6028 CHECK(!try_catch.HasCaught()); 6029 6030 value = CompileRun("obj(42)"); 6031 CHECK(!try_catch.HasCaught()); 6032 CHECK_EQ(42, value->Int32Value()); 6033 6034 value = CompileRun("(function(o){return o(49)})(obj)"); 6035 CHECK(!try_catch.HasCaught()); 6036 CHECK_EQ(49, value->Int32Value()); 6037 6038 // test special case of call as function 6039 value = CompileRun("[obj]['0'](45)"); 6040 CHECK(!try_catch.HasCaught()); 6041 CHECK_EQ(45, value->Int32Value()); 6042 6043 value = CompileRun("obj.call = Function.prototype.call;" 6044 "obj.call(null, 87)"); 6045 CHECK(!try_catch.HasCaught()); 6046 CHECK_EQ(87, value->Int32Value()); 6047 6048 // Regression tests for bug #1116356: Calling call through call/apply 6049 // must work for non-function receivers. 6050 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])"; 6051 value = CompileRun(apply_99); 6052 CHECK(!try_catch.HasCaught()); 6053 CHECK_EQ(99, value->Int32Value()); 6054 6055 const char* call_17 = "Function.prototype.call.call(obj, this, 17)"; 6056 value = CompileRun(call_17); 6057 CHECK(!try_catch.HasCaught()); 6058 CHECK_EQ(17, value->Int32Value()); 6059 6060 // Check that the call-as-function handler can be called through 6061 // new. 6062 value = CompileRun("new obj(43)"); 6063 CHECK(!try_catch.HasCaught()); 6064 CHECK_EQ(-43, value->Int32Value()); 6065} 6066 6067 6068static int CountHandles() { 6069 return v8::HandleScope::NumberOfHandles(); 6070} 6071 6072 6073static int Recurse(int depth, int iterations) { 6074 v8::HandleScope scope; 6075 if (depth == 0) return CountHandles(); 6076 for (int i = 0; i < iterations; i++) { 6077 Local<v8::Number> n = v8::Integer::New(42); 6078 } 6079 return Recurse(depth - 1, iterations); 6080} 6081 6082 6083THREADED_TEST(HandleIteration) { 6084 static const int kIterations = 500; 6085 static const int kNesting = 200; 6086 CHECK_EQ(0, CountHandles()); 6087 { 6088 v8::HandleScope scope1; 6089 CHECK_EQ(0, CountHandles()); 6090 for (int i = 0; i < kIterations; i++) { 6091 Local<v8::Number> n = v8::Integer::New(42); 6092 CHECK_EQ(i + 1, CountHandles()); 6093 } 6094 6095 CHECK_EQ(kIterations, CountHandles()); 6096 { 6097 v8::HandleScope scope2; 6098 for (int j = 0; j < kIterations; j++) { 6099 Local<v8::Number> n = v8::Integer::New(42); 6100 CHECK_EQ(j + 1 + kIterations, CountHandles()); 6101 } 6102 } 6103 CHECK_EQ(kIterations, CountHandles()); 6104 } 6105 CHECK_EQ(0, CountHandles()); 6106 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations)); 6107} 6108 6109 6110static v8::Handle<Value> InterceptorHasOwnPropertyGetter( 6111 Local<String> name, 6112 const AccessorInfo& info) { 6113 ApiTestFuzzer::Fuzz(); 6114 return v8::Handle<Value>(); 6115} 6116 6117 6118THREADED_TEST(InterceptorHasOwnProperty) { 6119 v8::HandleScope scope; 6120 LocalContext context; 6121 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6122 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 6123 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter); 6124 Local<Function> function = fun_templ->GetFunction(); 6125 context->Global()->Set(v8_str("constructor"), function); 6126 v8::Handle<Value> value = CompileRun( 6127 "var o = new constructor();" 6128 "o.hasOwnProperty('ostehaps');"); 6129 CHECK_EQ(false, value->BooleanValue()); 6130 value = CompileRun( 6131 "o.ostehaps = 42;" 6132 "o.hasOwnProperty('ostehaps');"); 6133 CHECK_EQ(true, value->BooleanValue()); 6134 value = CompileRun( 6135 "var p = new constructor();" 6136 "p.hasOwnProperty('ostehaps');"); 6137 CHECK_EQ(false, value->BooleanValue()); 6138} 6139 6140 6141static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC( 6142 Local<String> name, 6143 const AccessorInfo& info) { 6144 ApiTestFuzzer::Fuzz(); 6145 i::Heap::CollectAllGarbage(false); 6146 return v8::Handle<Value>(); 6147} 6148 6149 6150THREADED_TEST(InterceptorHasOwnPropertyCausingGC) { 6151 v8::HandleScope scope; 6152 LocalContext context; 6153 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6154 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 6155 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC); 6156 Local<Function> function = fun_templ->GetFunction(); 6157 context->Global()->Set(v8_str("constructor"), function); 6158 // Let's first make some stuff so we can be sure to get a good GC. 6159 CompileRun( 6160 "function makestr(size) {" 6161 " switch (size) {" 6162 " case 1: return 'f';" 6163 " case 2: return 'fo';" 6164 " case 3: return 'foo';" 6165 " }" 6166 " return makestr(size >> 1) + makestr((size + 1) >> 1);" 6167 "}" 6168 "var x = makestr(12345);" 6169 "x = makestr(31415);" 6170 "x = makestr(23456);"); 6171 v8::Handle<Value> value = CompileRun( 6172 "var o = new constructor();" 6173 "o.__proto__ = new String(x);" 6174 "o.hasOwnProperty('ostehaps');"); 6175 CHECK_EQ(false, value->BooleanValue()); 6176} 6177 6178 6179typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property, 6180 const AccessorInfo& info); 6181 6182 6183static void CheckInterceptorLoadIC(NamedPropertyGetter getter, 6184 const char* source, 6185 int expected) { 6186 v8::HandleScope scope; 6187 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6188 templ->SetNamedPropertyHandler(getter); 6189 LocalContext context; 6190 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6191 v8::Handle<Value> value = CompileRun(source); 6192 CHECK_EQ(expected, value->Int32Value()); 6193} 6194 6195 6196static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name, 6197 const AccessorInfo& info) { 6198 ApiTestFuzzer::Fuzz(); 6199 CHECK(v8_str("x")->Equals(name)); 6200 return v8::Integer::New(42); 6201} 6202 6203 6204// This test should hit the load IC for the interceptor case. 6205THREADED_TEST(InterceptorLoadIC) { 6206 CheckInterceptorLoadIC(InterceptorLoadICGetter, 6207 "var result = 0;" 6208 "for (var i = 0; i < 1000; i++) {" 6209 " result = o.x;" 6210 "}", 6211 42); 6212} 6213 6214 6215// Below go several tests which verify that JITing for various 6216// configurations of interceptor and explicit fields works fine 6217// (those cases are special cased to get better performance). 6218 6219static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name, 6220 const AccessorInfo& info) { 6221 ApiTestFuzzer::Fuzz(); 6222 return v8_str("x")->Equals(name) 6223 ? v8::Integer::New(42) : v8::Handle<v8::Value>(); 6224} 6225 6226 6227THREADED_TEST(InterceptorLoadICWithFieldOnHolder) { 6228 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6229 "var result = 0;" 6230 "o.y = 239;" 6231 "for (var i = 0; i < 1000; i++) {" 6232 " result = o.y;" 6233 "}", 6234 239); 6235} 6236 6237 6238THREADED_TEST(InterceptorLoadICWithSubstitutedProto) { 6239 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6240 "var result = 0;" 6241 "o.__proto__ = { 'y': 239 };" 6242 "for (var i = 0; i < 1000; i++) {" 6243 " result = o.y + o.x;" 6244 "}", 6245 239 + 42); 6246} 6247 6248 6249THREADED_TEST(InterceptorLoadICWithPropertyOnProto) { 6250 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6251 "var result = 0;" 6252 "o.__proto__.y = 239;" 6253 "for (var i = 0; i < 1000; i++) {" 6254 " result = o.y + o.x;" 6255 "}", 6256 239 + 42); 6257} 6258 6259 6260THREADED_TEST(InterceptorLoadICUndefined) { 6261 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6262 "var result = 0;" 6263 "for (var i = 0; i < 1000; i++) {" 6264 " result = (o.y == undefined) ? 239 : 42;" 6265 "}", 6266 239); 6267} 6268 6269 6270THREADED_TEST(InterceptorLoadICWithOverride) { 6271 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6272 "fst = new Object(); fst.__proto__ = o;" 6273 "snd = new Object(); snd.__proto__ = fst;" 6274 "var result1 = 0;" 6275 "for (var i = 0; i < 1000; i++) {" 6276 " result1 = snd.x;" 6277 "}" 6278 "fst.x = 239;" 6279 "var result = 0;" 6280 "for (var i = 0; i < 1000; i++) {" 6281 " result = snd.x;" 6282 "}" 6283 "result + result1", 6284 239 + 42); 6285} 6286 6287 6288// Test the case when we stored field into 6289// a stub, but interceptor produced value on its own. 6290THREADED_TEST(InterceptorLoadICFieldNotNeeded) { 6291 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6292 "proto = new Object();" 6293 "o.__proto__ = proto;" 6294 "proto.x = 239;" 6295 "for (var i = 0; i < 1000; i++) {" 6296 " o.x;" 6297 // Now it should be ICed and keep a reference to x defined on proto 6298 "}" 6299 "var result = 0;" 6300 "for (var i = 0; i < 1000; i++) {" 6301 " result += o.x;" 6302 "}" 6303 "result;", 6304 42 * 1000); 6305} 6306 6307 6308// Test the case when we stored field into 6309// a stub, but it got invalidated later on. 6310THREADED_TEST(InterceptorLoadICInvalidatedField) { 6311 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6312 "proto1 = new Object();" 6313 "proto2 = new Object();" 6314 "o.__proto__ = proto1;" 6315 "proto1.__proto__ = proto2;" 6316 "proto2.y = 239;" 6317 "for (var i = 0; i < 1000; i++) {" 6318 " o.y;" 6319 // Now it should be ICed and keep a reference to y defined on proto2 6320 "}" 6321 "proto1.y = 42;" 6322 "var result = 0;" 6323 "for (var i = 0; i < 1000; i++) {" 6324 " result += o.y;" 6325 "}" 6326 "result;", 6327 42 * 1000); 6328} 6329 6330 6331static int interceptor_load_not_handled_calls = 0; 6332static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name, 6333 const AccessorInfo& info) { 6334 ++interceptor_load_not_handled_calls; 6335 return v8::Handle<v8::Value>(); 6336} 6337 6338 6339// Test how post-interceptor lookups are done in the non-cacheable 6340// case: the interceptor should not be invoked during this lookup. 6341THREADED_TEST(InterceptorLoadICPostInterceptor) { 6342 interceptor_load_not_handled_calls = 0; 6343 CheckInterceptorLoadIC(InterceptorLoadNotHandled, 6344 "receiver = new Object();" 6345 "receiver.__proto__ = o;" 6346 "proto = new Object();" 6347 "/* Make proto a slow-case object. */" 6348 "for (var i = 0; i < 1000; i++) {" 6349 " proto[\"xxxxxxxx\" + i] = [];" 6350 "}" 6351 "proto.x = 17;" 6352 "o.__proto__ = proto;" 6353 "var result = 0;" 6354 "for (var i = 0; i < 1000; i++) {" 6355 " result += receiver.x;" 6356 "}" 6357 "result;", 6358 17 * 1000); 6359 CHECK_EQ(1000, interceptor_load_not_handled_calls); 6360} 6361 6362 6363// Test the case when we stored field into 6364// a stub, but it got invalidated later on due to override on 6365// global object which is between interceptor and fields' holders. 6366THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) { 6367 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6368 "o.__proto__ = this;" // set a global to be a proto of o. 6369 "this.__proto__.y = 239;" 6370 "for (var i = 0; i < 10; i++) {" 6371 " if (o.y != 239) throw 'oops: ' + o.y;" 6372 // Now it should be ICed and keep a reference to y defined on field_holder. 6373 "}" 6374 "this.y = 42;" // Assign on a global. 6375 "var result = 0;" 6376 "for (var i = 0; i < 10; i++) {" 6377 " result += o.y;" 6378 "}" 6379 "result;", 6380 42 * 10); 6381} 6382 6383 6384static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) { 6385 ApiTestFuzzer::Fuzz(); 6386 return v8_num(239); 6387} 6388 6389 6390static void SetOnThis(Local<String> name, 6391 Local<Value> value, 6392 const AccessorInfo& info) { 6393 info.This()->ForceSet(name, value); 6394} 6395 6396 6397THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) { 6398 v8::HandleScope scope; 6399 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6400 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6401 templ->SetAccessor(v8_str("y"), Return239); 6402 LocalContext context; 6403 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6404 6405 // Check the case when receiver and interceptor's holder 6406 // are the same objects. 6407 v8::Handle<Value> value = CompileRun( 6408 "var result = 0;" 6409 "for (var i = 0; i < 7; i++) {" 6410 " result = o.y;" 6411 "}"); 6412 CHECK_EQ(239, value->Int32Value()); 6413 6414 // Check the case when interceptor's holder is in proto chain 6415 // of receiver. 6416 value = CompileRun( 6417 "r = { __proto__: o };" 6418 "var result = 0;" 6419 "for (var i = 0; i < 7; i++) {" 6420 " result = r.y;" 6421 "}"); 6422 CHECK_EQ(239, value->Int32Value()); 6423} 6424 6425 6426THREADED_TEST(InterceptorLoadICWithCallbackOnProto) { 6427 v8::HandleScope scope; 6428 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6429 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6430 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6431 templ_p->SetAccessor(v8_str("y"), Return239); 6432 6433 LocalContext context; 6434 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6435 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6436 6437 // Check the case when receiver and interceptor's holder 6438 // are the same objects. 6439 v8::Handle<Value> value = CompileRun( 6440 "o.__proto__ = p;" 6441 "var result = 0;" 6442 "for (var i = 0; i < 7; i++) {" 6443 " result = o.x + o.y;" 6444 "}"); 6445 CHECK_EQ(239 + 42, value->Int32Value()); 6446 6447 // Check the case when interceptor's holder is in proto chain 6448 // of receiver. 6449 value = CompileRun( 6450 "r = { __proto__: o };" 6451 "var result = 0;" 6452 "for (var i = 0; i < 7; i++) {" 6453 " result = r.x + r.y;" 6454 "}"); 6455 CHECK_EQ(239 + 42, value->Int32Value()); 6456} 6457 6458 6459THREADED_TEST(InterceptorLoadICForCallbackWithOverride) { 6460 v8::HandleScope scope; 6461 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6462 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6463 templ->SetAccessor(v8_str("y"), Return239); 6464 6465 LocalContext context; 6466 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6467 6468 v8::Handle<Value> value = CompileRun( 6469 "fst = new Object(); fst.__proto__ = o;" 6470 "snd = new Object(); snd.__proto__ = fst;" 6471 "var result1 = 0;" 6472 "for (var i = 0; i < 7; i++) {" 6473 " result1 = snd.x;" 6474 "}" 6475 "fst.x = 239;" 6476 "var result = 0;" 6477 "for (var i = 0; i < 7; i++) {" 6478 " result = snd.x;" 6479 "}" 6480 "result + result1"); 6481 CHECK_EQ(239 + 42, value->Int32Value()); 6482} 6483 6484 6485// Test the case when we stored callback into 6486// a stub, but interceptor produced value on its own. 6487THREADED_TEST(InterceptorLoadICCallbackNotNeeded) { 6488 v8::HandleScope scope; 6489 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6490 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6491 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6492 templ_p->SetAccessor(v8_str("y"), Return239); 6493 6494 LocalContext context; 6495 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6496 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6497 6498 v8::Handle<Value> value = CompileRun( 6499 "o.__proto__ = p;" 6500 "for (var i = 0; i < 7; i++) {" 6501 " o.x;" 6502 // Now it should be ICed and keep a reference to x defined on p 6503 "}" 6504 "var result = 0;" 6505 "for (var i = 0; i < 7; i++) {" 6506 " result += o.x;" 6507 "}" 6508 "result"); 6509 CHECK_EQ(42 * 7, value->Int32Value()); 6510} 6511 6512 6513// Test the case when we stored callback into 6514// a stub, but it got invalidated later on. 6515THREADED_TEST(InterceptorLoadICInvalidatedCallback) { 6516 v8::HandleScope scope; 6517 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6518 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6519 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6520 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 6521 6522 LocalContext context; 6523 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6524 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6525 6526 v8::Handle<Value> value = CompileRun( 6527 "inbetween = new Object();" 6528 "o.__proto__ = inbetween;" 6529 "inbetween.__proto__ = p;" 6530 "for (var i = 0; i < 10; i++) {" 6531 " o.y;" 6532 // Now it should be ICed and keep a reference to y defined on p 6533 "}" 6534 "inbetween.y = 42;" 6535 "var result = 0;" 6536 "for (var i = 0; i < 10; i++) {" 6537 " result += o.y;" 6538 "}" 6539 "result"); 6540 CHECK_EQ(42 * 10, value->Int32Value()); 6541} 6542 6543 6544// Test the case when we stored callback into 6545// a stub, but it got invalidated later on due to override on 6546// global object which is between interceptor and callbacks' holders. 6547THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) { 6548 v8::HandleScope scope; 6549 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6550 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6551 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6552 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 6553 6554 LocalContext context; 6555 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6556 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6557 6558 v8::Handle<Value> value = CompileRun( 6559 "o.__proto__ = this;" 6560 "this.__proto__ = p;" 6561 "for (var i = 0; i < 10; i++) {" 6562 " if (o.y != 239) throw 'oops: ' + o.y;" 6563 // Now it should be ICed and keep a reference to y defined on p 6564 "}" 6565 "this.y = 42;" 6566 "var result = 0;" 6567 "for (var i = 0; i < 10; i++) {" 6568 " result += o.y;" 6569 "}" 6570 "result"); 6571 CHECK_EQ(42 * 10, value->Int32Value()); 6572} 6573 6574 6575static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name, 6576 const AccessorInfo& info) { 6577 ApiTestFuzzer::Fuzz(); 6578 CHECK(v8_str("x")->Equals(name)); 6579 return v8::Integer::New(0); 6580} 6581 6582 6583THREADED_TEST(InterceptorReturningZero) { 6584 CheckInterceptorLoadIC(InterceptorLoadICGetter0, 6585 "o.x == undefined ? 1 : 0", 6586 0); 6587} 6588 6589 6590static v8::Handle<Value> InterceptorStoreICSetter( 6591 Local<String> key, Local<Value> value, const AccessorInfo&) { 6592 CHECK(v8_str("x")->Equals(key)); 6593 CHECK_EQ(42, value->Int32Value()); 6594 return value; 6595} 6596 6597 6598// This test should hit the store IC for the interceptor case. 6599THREADED_TEST(InterceptorStoreIC) { 6600 v8::HandleScope scope; 6601 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6602 templ->SetNamedPropertyHandler(InterceptorLoadICGetter, 6603 InterceptorStoreICSetter); 6604 LocalContext context; 6605 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6606 v8::Handle<Value> value = CompileRun( 6607 "for (var i = 0; i < 1000; i++) {" 6608 " o.x = 42;" 6609 "}"); 6610} 6611 6612 6613THREADED_TEST(InterceptorStoreICWithNoSetter) { 6614 v8::HandleScope scope; 6615 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6616 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6617 LocalContext context; 6618 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6619 v8::Handle<Value> value = CompileRun( 6620 "for (var i = 0; i < 1000; i++) {" 6621 " o.y = 239;" 6622 "}" 6623 "42 + o.y"); 6624 CHECK_EQ(239 + 42, value->Int32Value()); 6625} 6626 6627 6628 6629 6630v8::Handle<Value> call_ic_function; 6631v8::Handle<Value> call_ic_function2; 6632v8::Handle<Value> call_ic_function3; 6633 6634static v8::Handle<Value> InterceptorCallICGetter(Local<String> name, 6635 const AccessorInfo& info) { 6636 ApiTestFuzzer::Fuzz(); 6637 CHECK(v8_str("x")->Equals(name)); 6638 return call_ic_function; 6639} 6640 6641 6642// This test should hit the call IC for the interceptor case. 6643THREADED_TEST(InterceptorCallIC) { 6644 v8::HandleScope scope; 6645 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6646 templ->SetNamedPropertyHandler(InterceptorCallICGetter); 6647 LocalContext context; 6648 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6649 call_ic_function = 6650 v8_compile("function f(x) { return x + 1; }; f")->Run(); 6651 v8::Handle<Value> value = CompileRun( 6652 "var result = 0;" 6653 "for (var i = 0; i < 1000; i++) {" 6654 " result = o.x(41);" 6655 "}"); 6656 CHECK_EQ(42, value->Int32Value()); 6657} 6658 6659 6660// This test checks that if interceptor doesn't provide 6661// a value, we can fetch regular value. 6662THREADED_TEST(InterceptorCallICSeesOthers) { 6663 v8::HandleScope scope; 6664 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6665 templ->SetNamedPropertyHandler(NoBlockGetterX); 6666 LocalContext context; 6667 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6668 v8::Handle<Value> value = CompileRun( 6669 "o.x = function f(x) { return x + 1; };" 6670 "var result = 0;" 6671 "for (var i = 0; i < 7; i++) {" 6672 " result = o.x(41);" 6673 "}"); 6674 CHECK_EQ(42, value->Int32Value()); 6675} 6676 6677 6678static v8::Handle<Value> call_ic_function4; 6679static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name, 6680 const AccessorInfo& info) { 6681 ApiTestFuzzer::Fuzz(); 6682 CHECK(v8_str("x")->Equals(name)); 6683 return call_ic_function4; 6684} 6685 6686 6687// This test checks that if interceptor provides a function, 6688// even if we cached shadowed variant, interceptor's function 6689// is invoked 6690THREADED_TEST(InterceptorCallICCacheableNotNeeded) { 6691 v8::HandleScope scope; 6692 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6693 templ->SetNamedPropertyHandler(InterceptorCallICGetter4); 6694 LocalContext context; 6695 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6696 call_ic_function4 = 6697 v8_compile("function f(x) { return x - 1; }; f")->Run(); 6698 v8::Handle<Value> value = CompileRun( 6699 "o.__proto__.x = function(x) { return x + 1; };" 6700 "var result = 0;" 6701 "for (var i = 0; i < 1000; i++) {" 6702 " result = o.x(42);" 6703 "}"); 6704 CHECK_EQ(41, value->Int32Value()); 6705} 6706 6707 6708// Test the case when we stored cacheable lookup into 6709// a stub, but it got invalidated later on 6710THREADED_TEST(InterceptorCallICInvalidatedCacheable) { 6711 v8::HandleScope scope; 6712 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6713 templ->SetNamedPropertyHandler(NoBlockGetterX); 6714 LocalContext context; 6715 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6716 v8::Handle<Value> value = CompileRun( 6717 "proto1 = new Object();" 6718 "proto2 = new Object();" 6719 "o.__proto__ = proto1;" 6720 "proto1.__proto__ = proto2;" 6721 "proto2.y = function(x) { return x + 1; };" 6722 // Invoke it many times to compile a stub 6723 "for (var i = 0; i < 7; i++) {" 6724 " o.y(42);" 6725 "}" 6726 "proto1.y = function(x) { return x - 1; };" 6727 "var result = 0;" 6728 "for (var i = 0; i < 7; i++) {" 6729 " result += o.y(42);" 6730 "}"); 6731 CHECK_EQ(41 * 7, value->Int32Value()); 6732} 6733 6734 6735static v8::Handle<Value> call_ic_function5; 6736static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name, 6737 const AccessorInfo& info) { 6738 ApiTestFuzzer::Fuzz(); 6739 if (v8_str("x")->Equals(name)) 6740 return call_ic_function5; 6741 else 6742 return Local<Value>(); 6743} 6744 6745 6746// This test checks that if interceptor doesn't provide a function, 6747// cached constant function is used 6748THREADED_TEST(InterceptorCallICConstantFunctionUsed) { 6749 v8::HandleScope scope; 6750 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6751 templ->SetNamedPropertyHandler(NoBlockGetterX); 6752 LocalContext context; 6753 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6754 v8::Handle<Value> value = CompileRun( 6755 "function inc(x) { return x + 1; };" 6756 "inc(1);" 6757 "o.x = inc;" 6758 "var result = 0;" 6759 "for (var i = 0; i < 1000; i++) {" 6760 " result = o.x(42);" 6761 "}"); 6762 CHECK_EQ(43, value->Int32Value()); 6763} 6764 6765 6766// This test checks that if interceptor provides a function, 6767// even if we cached constant function, interceptor's function 6768// is invoked 6769THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) { 6770 v8::HandleScope scope; 6771 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6772 templ->SetNamedPropertyHandler(InterceptorCallICGetter5); 6773 LocalContext context; 6774 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6775 call_ic_function5 = 6776 v8_compile("function f(x) { return x - 1; }; f")->Run(); 6777 v8::Handle<Value> value = CompileRun( 6778 "function inc(x) { return x + 1; };" 6779 "inc(1);" 6780 "o.x = inc;" 6781 "var result = 0;" 6782 "for (var i = 0; i < 1000; i++) {" 6783 " result = o.x(42);" 6784 "}"); 6785 CHECK_EQ(41, value->Int32Value()); 6786} 6787 6788 6789// Test the case when we stored constant function into 6790// a stub, but it got invalidated later on 6791THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) { 6792 v8::HandleScope scope; 6793 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6794 templ->SetNamedPropertyHandler(NoBlockGetterX); 6795 LocalContext context; 6796 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6797 v8::Handle<Value> value = CompileRun( 6798 "function inc(x) { return x + 1; };" 6799 "inc(1);" 6800 "proto1 = new Object();" 6801 "proto2 = new Object();" 6802 "o.__proto__ = proto1;" 6803 "proto1.__proto__ = proto2;" 6804 "proto2.y = inc;" 6805 // Invoke it many times to compile a stub 6806 "for (var i = 0; i < 7; i++) {" 6807 " o.y(42);" 6808 "}" 6809 "proto1.y = function(x) { return x - 1; };" 6810 "var result = 0;" 6811 "for (var i = 0; i < 7; i++) {" 6812 " result += o.y(42);" 6813 "}"); 6814 CHECK_EQ(41 * 7, value->Int32Value()); 6815} 6816 6817 6818// Test the case when we stored constant function into 6819// a stub, but it got invalidated later on due to override on 6820// global object which is between interceptor and constant function' holders. 6821THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) { 6822 v8::HandleScope scope; 6823 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6824 templ->SetNamedPropertyHandler(NoBlockGetterX); 6825 LocalContext context; 6826 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6827 v8::Handle<Value> value = CompileRun( 6828 "function inc(x) { return x + 1; };" 6829 "inc(1);" 6830 "o.__proto__ = this;" 6831 "this.__proto__.y = inc;" 6832 // Invoke it many times to compile a stub 6833 "for (var i = 0; i < 7; i++) {" 6834 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);" 6835 "}" 6836 "this.y = function(x) { return x - 1; };" 6837 "var result = 0;" 6838 "for (var i = 0; i < 7; i++) {" 6839 " result += o.y(42);" 6840 "}"); 6841 CHECK_EQ(41 * 7, value->Int32Value()); 6842} 6843 6844 6845// Test the case when actual function to call sits on global object. 6846THREADED_TEST(InterceptorCallICCachedFromGlobal) { 6847 v8::HandleScope scope; 6848 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6849 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 6850 6851 LocalContext context; 6852 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6853 6854 v8::Handle<Value> value = CompileRun( 6855 "try {" 6856 " o.__proto__ = this;" 6857 " for (var i = 0; i < 10; i++) {" 6858 " var v = o.parseFloat('239');" 6859 " if (v != 239) throw v;" 6860 // Now it should be ICed and keep a reference to parseFloat. 6861 " }" 6862 " var result = 0;" 6863 " for (var i = 0; i < 10; i++) {" 6864 " result += o.parseFloat('239');" 6865 " }" 6866 " result" 6867 "} catch(e) {" 6868 " e" 6869 "};"); 6870 CHECK_EQ(239 * 10, value->Int32Value()); 6871} 6872 6873static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name, 6874 const AccessorInfo& info) { 6875 ApiTestFuzzer::Fuzz(); 6876 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data())); 6877 ++(*call_count); 6878 if ((*call_count) % 20 == 0) { 6879 i::Heap::CollectAllGarbage(true); 6880 } 6881 return v8::Handle<Value>(); 6882} 6883 6884static v8::Handle<Value> FastApiCallback_TrivialSignature( 6885 const v8::Arguments& args) { 6886 ApiTestFuzzer::Fuzz(); 6887 CHECK_EQ(args.This(), args.Holder()); 6888 CHECK(args.Data()->Equals(v8_str("method_data"))); 6889 return v8::Integer::New(args[0]->Int32Value() + 1); 6890} 6891 6892static v8::Handle<Value> FastApiCallback_SimpleSignature( 6893 const v8::Arguments& args) { 6894 ApiTestFuzzer::Fuzz(); 6895 CHECK_EQ(args.This()->GetPrototype(), args.Holder()); 6896 CHECK(args.Data()->Equals(v8_str("method_data"))); 6897 // Note, we're using HasRealNamedProperty instead of Has to avoid 6898 // invoking the interceptor again. 6899 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo"))); 6900 return v8::Integer::New(args[0]->Int32Value() + 1); 6901} 6902 6903// Helper to maximize the odds of object moving. 6904static void GenerateSomeGarbage() { 6905 CompileRun( 6906 "var garbage;" 6907 "for (var i = 0; i < 1000; i++) {" 6908 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];" 6909 "}" 6910 "garbage = undefined;"); 6911} 6912 6913THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) { 6914 int interceptor_call_count = 0; 6915 v8::HandleScope scope; 6916 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6917 v8::Handle<v8::FunctionTemplate> method_templ = 6918 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 6919 v8_str("method_data"), 6920 v8::Handle<v8::Signature>()); 6921 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6922 proto_templ->Set(v8_str("method"), method_templ); 6923 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6924 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6925 NULL, NULL, NULL, NULL, 6926 v8::External::Wrap(&interceptor_call_count)); 6927 LocalContext context; 6928 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6929 GenerateSomeGarbage(); 6930 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6931 v8::Handle<Value> value = CompileRun( 6932 "var result = 0;" 6933 "for (var i = 0; i < 100; i++) {" 6934 " result = o.method(41);" 6935 "}"); 6936 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 6937 CHECK_EQ(100, interceptor_call_count); 6938} 6939 6940THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) { 6941 int interceptor_call_count = 0; 6942 v8::HandleScope scope; 6943 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6944 v8::Handle<v8::FunctionTemplate> method_templ = 6945 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6946 v8_str("method_data"), 6947 v8::Signature::New(fun_templ)); 6948 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6949 proto_templ->Set(v8_str("method"), method_templ); 6950 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6951 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6952 NULL, NULL, NULL, NULL, 6953 v8::External::Wrap(&interceptor_call_count)); 6954 LocalContext context; 6955 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6956 GenerateSomeGarbage(); 6957 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6958 v8::Handle<Value> value = CompileRun( 6959 "o.foo = 17;" 6960 "var receiver = {};" 6961 "receiver.__proto__ = o;" 6962 "var result = 0;" 6963 "for (var i = 0; i < 100; i++) {" 6964 " result = receiver.method(41);" 6965 "}"); 6966 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 6967 CHECK_EQ(100, interceptor_call_count); 6968} 6969 6970THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) { 6971 int interceptor_call_count = 0; 6972 v8::HandleScope scope; 6973 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6974 v8::Handle<v8::FunctionTemplate> method_templ = 6975 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6976 v8_str("method_data"), 6977 v8::Signature::New(fun_templ)); 6978 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6979 proto_templ->Set(v8_str("method"), method_templ); 6980 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6981 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6982 NULL, NULL, NULL, NULL, 6983 v8::External::Wrap(&interceptor_call_count)); 6984 LocalContext context; 6985 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6986 GenerateSomeGarbage(); 6987 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6988 v8::Handle<Value> value = CompileRun( 6989 "o.foo = 17;" 6990 "var receiver = {};" 6991 "receiver.__proto__ = o;" 6992 "var result = 0;" 6993 "var saved_result = 0;" 6994 "for (var i = 0; i < 100; i++) {" 6995 " result = receiver.method(41);" 6996 " if (i == 50) {" 6997 " saved_result = result;" 6998 " receiver = {method: function(x) { return x - 1 }};" 6999 " }" 7000 "}"); 7001 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 7002 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7003 CHECK_GE(interceptor_call_count, 50); 7004} 7005 7006THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) { 7007 int interceptor_call_count = 0; 7008 v8::HandleScope scope; 7009 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7010 v8::Handle<v8::FunctionTemplate> method_templ = 7011 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7012 v8_str("method_data"), 7013 v8::Signature::New(fun_templ)); 7014 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7015 proto_templ->Set(v8_str("method"), method_templ); 7016 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7017 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 7018 NULL, NULL, NULL, NULL, 7019 v8::External::Wrap(&interceptor_call_count)); 7020 LocalContext context; 7021 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7022 GenerateSomeGarbage(); 7023 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7024 v8::Handle<Value> value = CompileRun( 7025 "o.foo = 17;" 7026 "var receiver = {};" 7027 "receiver.__proto__ = o;" 7028 "var result = 0;" 7029 "var saved_result = 0;" 7030 "for (var i = 0; i < 100; i++) {" 7031 " result = receiver.method(41);" 7032 " if (i == 50) {" 7033 " saved_result = result;" 7034 " o.method = function(x) { return x - 1 };" 7035 " }" 7036 "}"); 7037 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 7038 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7039 CHECK_GE(interceptor_call_count, 50); 7040} 7041 7042THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) { 7043 int interceptor_call_count = 0; 7044 v8::HandleScope scope; 7045 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7046 v8::Handle<v8::FunctionTemplate> method_templ = 7047 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7048 v8_str("method_data"), 7049 v8::Signature::New(fun_templ)); 7050 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7051 proto_templ->Set(v8_str("method"), method_templ); 7052 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7053 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 7054 NULL, NULL, NULL, NULL, 7055 v8::External::Wrap(&interceptor_call_count)); 7056 LocalContext context; 7057 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7058 GenerateSomeGarbage(); 7059 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7060 v8::TryCatch try_catch; 7061 v8::Handle<Value> value = CompileRun( 7062 "o.foo = 17;" 7063 "var receiver = {};" 7064 "receiver.__proto__ = o;" 7065 "var result = 0;" 7066 "var saved_result = 0;" 7067 "for (var i = 0; i < 100; i++) {" 7068 " result = receiver.method(41);" 7069 " if (i == 50) {" 7070 " saved_result = result;" 7071 " receiver = 333;" 7072 " }" 7073 "}"); 7074 CHECK(try_catch.HasCaught()); 7075 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), 7076 try_catch.Exception()->ToString()); 7077 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7078 CHECK_GE(interceptor_call_count, 50); 7079} 7080 7081THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) { 7082 int interceptor_call_count = 0; 7083 v8::HandleScope scope; 7084 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7085 v8::Handle<v8::FunctionTemplate> method_templ = 7086 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7087 v8_str("method_data"), 7088 v8::Signature::New(fun_templ)); 7089 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7090 proto_templ->Set(v8_str("method"), method_templ); 7091 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7092 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 7093 NULL, NULL, NULL, NULL, 7094 v8::External::Wrap(&interceptor_call_count)); 7095 LocalContext context; 7096 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7097 GenerateSomeGarbage(); 7098 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7099 v8::TryCatch try_catch; 7100 v8::Handle<Value> value = CompileRun( 7101 "o.foo = 17;" 7102 "var receiver = {};" 7103 "receiver.__proto__ = o;" 7104 "var result = 0;" 7105 "var saved_result = 0;" 7106 "for (var i = 0; i < 100; i++) {" 7107 " result = receiver.method(41);" 7108 " if (i == 50) {" 7109 " saved_result = result;" 7110 " receiver = {method: receiver.method};" 7111 " }" 7112 "}"); 7113 CHECK(try_catch.HasCaught()); 7114 CHECK_EQ(v8_str("TypeError: Illegal invocation"), 7115 try_catch.Exception()->ToString()); 7116 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7117 CHECK_GE(interceptor_call_count, 50); 7118} 7119 7120THREADED_TEST(CallICFastApi_TrivialSignature) { 7121 v8::HandleScope scope; 7122 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7123 v8::Handle<v8::FunctionTemplate> method_templ = 7124 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 7125 v8_str("method_data"), 7126 v8::Handle<v8::Signature>()); 7127 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7128 proto_templ->Set(v8_str("method"), method_templ); 7129 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7130 LocalContext context; 7131 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7132 GenerateSomeGarbage(); 7133 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7134 v8::Handle<Value> value = CompileRun( 7135 "var result = 0;" 7136 "for (var i = 0; i < 100; i++) {" 7137 " result = o.method(41);" 7138 "}"); 7139 7140 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 7141} 7142 7143THREADED_TEST(CallICFastApi_SimpleSignature) { 7144 v8::HandleScope scope; 7145 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7146 v8::Handle<v8::FunctionTemplate> method_templ = 7147 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7148 v8_str("method_data"), 7149 v8::Signature::New(fun_templ)); 7150 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7151 proto_templ->Set(v8_str("method"), method_templ); 7152 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7153 LocalContext context; 7154 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7155 GenerateSomeGarbage(); 7156 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7157 v8::Handle<Value> value = CompileRun( 7158 "o.foo = 17;" 7159 "var receiver = {};" 7160 "receiver.__proto__ = o;" 7161 "var result = 0;" 7162 "for (var i = 0; i < 100; i++) {" 7163 " result = receiver.method(41);" 7164 "}"); 7165 7166 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 7167} 7168 7169THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) { 7170 v8::HandleScope scope; 7171 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7172 v8::Handle<v8::FunctionTemplate> method_templ = 7173 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7174 v8_str("method_data"), 7175 v8::Signature::New(fun_templ)); 7176 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7177 proto_templ->Set(v8_str("method"), method_templ); 7178 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7179 LocalContext context; 7180 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7181 GenerateSomeGarbage(); 7182 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7183 v8::Handle<Value> value = CompileRun( 7184 "o.foo = 17;" 7185 "var receiver = {};" 7186 "receiver.__proto__ = o;" 7187 "var result = 0;" 7188 "var saved_result = 0;" 7189 "for (var i = 0; i < 100; i++) {" 7190 " result = receiver.method(41);" 7191 " if (i == 50) {" 7192 " saved_result = result;" 7193 " receiver = {method: function(x) { return x - 1 }};" 7194 " }" 7195 "}"); 7196 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 7197 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7198} 7199 7200THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) { 7201 v8::HandleScope scope; 7202 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7203 v8::Handle<v8::FunctionTemplate> method_templ = 7204 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7205 v8_str("method_data"), 7206 v8::Signature::New(fun_templ)); 7207 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7208 proto_templ->Set(v8_str("method"), method_templ); 7209 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7210 LocalContext context; 7211 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7212 GenerateSomeGarbage(); 7213 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7214 v8::TryCatch try_catch; 7215 v8::Handle<Value> value = CompileRun( 7216 "o.foo = 17;" 7217 "var receiver = {};" 7218 "receiver.__proto__ = o;" 7219 "var result = 0;" 7220 "var saved_result = 0;" 7221 "for (var i = 0; i < 100; i++) {" 7222 " result = receiver.method(41);" 7223 " if (i == 50) {" 7224 " saved_result = result;" 7225 " receiver = 333;" 7226 " }" 7227 "}"); 7228 CHECK(try_catch.HasCaught()); 7229 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), 7230 try_catch.Exception()->ToString()); 7231 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7232} 7233 7234 7235v8::Handle<Value> keyed_call_ic_function; 7236 7237static v8::Handle<Value> InterceptorKeyedCallICGetter( 7238 Local<String> name, const AccessorInfo& info) { 7239 ApiTestFuzzer::Fuzz(); 7240 if (v8_str("x")->Equals(name)) { 7241 return keyed_call_ic_function; 7242 } 7243 return v8::Handle<Value>(); 7244} 7245 7246 7247// Test the case when we stored cacheable lookup into 7248// a stub, but the function name changed (to another cacheable function). 7249THREADED_TEST(InterceptorKeyedCallICKeyChange1) { 7250 v8::HandleScope scope; 7251 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7252 templ->SetNamedPropertyHandler(NoBlockGetterX); 7253 LocalContext context; 7254 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7255 v8::Handle<Value> value = CompileRun( 7256 "proto = new Object();" 7257 "proto.y = function(x) { return x + 1; };" 7258 "proto.z = function(x) { return x - 1; };" 7259 "o.__proto__ = proto;" 7260 "var result = 0;" 7261 "var method = 'y';" 7262 "for (var i = 0; i < 10; i++) {" 7263 " if (i == 5) { method = 'z'; };" 7264 " result += o[method](41);" 7265 "}"); 7266 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7267} 7268 7269 7270// Test the case when we stored cacheable lookup into 7271// a stub, but the function name changed (and the new function is present 7272// both before and after the interceptor in the prototype chain). 7273THREADED_TEST(InterceptorKeyedCallICKeyChange2) { 7274 v8::HandleScope scope; 7275 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7276 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter); 7277 LocalContext context; 7278 context->Global()->Set(v8_str("proto1"), templ->NewInstance()); 7279 keyed_call_ic_function = 7280 v8_compile("function f(x) { return x - 1; }; f")->Run(); 7281 v8::Handle<Value> value = CompileRun( 7282 "o = new Object();" 7283 "proto2 = new Object();" 7284 "o.y = function(x) { return x + 1; };" 7285 "proto2.y = function(x) { return x + 2; };" 7286 "o.__proto__ = proto1;" 7287 "proto1.__proto__ = proto2;" 7288 "var result = 0;" 7289 "var method = 'x';" 7290 "for (var i = 0; i < 10; i++) {" 7291 " if (i == 5) { method = 'y'; };" 7292 " result += o[method](41);" 7293 "}"); 7294 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7295} 7296 7297 7298// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit 7299// on the global object. 7300THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) { 7301 v8::HandleScope scope; 7302 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7303 templ->SetNamedPropertyHandler(NoBlockGetterX); 7304 LocalContext context; 7305 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7306 v8::Handle<Value> value = CompileRun( 7307 "function inc(x) { return x + 1; };" 7308 "inc(1);" 7309 "function dec(x) { return x - 1; };" 7310 "dec(1);" 7311 "o.__proto__ = this;" 7312 "this.__proto__.x = inc;" 7313 "this.__proto__.y = dec;" 7314 "var result = 0;" 7315 "var method = 'x';" 7316 "for (var i = 0; i < 10; i++) {" 7317 " if (i == 5) { method = 'y'; };" 7318 " result += o[method](41);" 7319 "}"); 7320 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7321} 7322 7323 7324// Test the case when actual function to call sits on global object. 7325THREADED_TEST(InterceptorKeyedCallICFromGlobal) { 7326 v8::HandleScope scope; 7327 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7328 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 7329 LocalContext context; 7330 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 7331 7332 v8::Handle<Value> value = CompileRun( 7333 "function len(x) { return x.length; };" 7334 "o.__proto__ = this;" 7335 "var m = 'parseFloat';" 7336 "var result = 0;" 7337 "for (var i = 0; i < 10; i++) {" 7338 " if (i == 5) {" 7339 " m = 'len';" 7340 " saved_result = result;" 7341 " };" 7342 " result = o[m]('239');" 7343 "}"); 7344 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value()); 7345 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7346} 7347 7348// Test the map transition before the interceptor. 7349THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) { 7350 v8::HandleScope scope; 7351 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7352 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 7353 LocalContext context; 7354 context->Global()->Set(v8_str("proto"), templ_o->NewInstance()); 7355 7356 v8::Handle<Value> value = CompileRun( 7357 "var o = new Object();" 7358 "o.__proto__ = proto;" 7359 "o.method = function(x) { return x + 1; };" 7360 "var m = 'method';" 7361 "var result = 0;" 7362 "for (var i = 0; i < 10; i++) {" 7363 " if (i == 5) { o.method = function(x) { return x - 1; }; };" 7364 " result += o[m](41);" 7365 "}"); 7366 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7367} 7368 7369 7370// Test the map transition after the interceptor. 7371THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) { 7372 v8::HandleScope scope; 7373 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7374 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 7375 LocalContext context; 7376 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 7377 7378 v8::Handle<Value> value = CompileRun( 7379 "var proto = new Object();" 7380 "o.__proto__ = proto;" 7381 "proto.method = function(x) { return x + 1; };" 7382 "var m = 'method';" 7383 "var result = 0;" 7384 "for (var i = 0; i < 10; i++) {" 7385 " if (i == 5) { proto.method = function(x) { return x - 1; }; };" 7386 " result += o[m](41);" 7387 "}"); 7388 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7389} 7390 7391 7392static int interceptor_call_count = 0; 7393 7394static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name, 7395 const AccessorInfo& info) { 7396 ApiTestFuzzer::Fuzz(); 7397 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) { 7398 return call_ic_function2; 7399 } 7400 return v8::Handle<Value>(); 7401} 7402 7403 7404// This test should hit load and call ICs for the interceptor case. 7405// Once in a while, the interceptor will reply that a property was not 7406// found in which case we should get a reference error. 7407THREADED_TEST(InterceptorICReferenceErrors) { 7408 v8::HandleScope scope; 7409 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7410 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter); 7411 LocalContext context(0, templ, v8::Handle<Value>()); 7412 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run(); 7413 v8::Handle<Value> value = CompileRun( 7414 "function f() {" 7415 " for (var i = 0; i < 1000; i++) {" 7416 " try { x; } catch(e) { return true; }" 7417 " }" 7418 " return false;" 7419 "};" 7420 "f();"); 7421 CHECK_EQ(true, value->BooleanValue()); 7422 interceptor_call_count = 0; 7423 value = CompileRun( 7424 "function g() {" 7425 " for (var i = 0; i < 1000; i++) {" 7426 " try { x(42); } catch(e) { return true; }" 7427 " }" 7428 " return false;" 7429 "};" 7430 "g();"); 7431 CHECK_EQ(true, value->BooleanValue()); 7432} 7433 7434 7435static int interceptor_ic_exception_get_count = 0; 7436 7437static v8::Handle<Value> InterceptorICExceptionGetter( 7438 Local<String> name, 7439 const AccessorInfo& info) { 7440 ApiTestFuzzer::Fuzz(); 7441 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) { 7442 return call_ic_function3; 7443 } 7444 if (interceptor_ic_exception_get_count == 20) { 7445 return v8::ThrowException(v8_num(42)); 7446 } 7447 // Do not handle get for properties other than x. 7448 return v8::Handle<Value>(); 7449} 7450 7451// Test interceptor load/call IC where the interceptor throws an 7452// exception once in a while. 7453THREADED_TEST(InterceptorICGetterExceptions) { 7454 interceptor_ic_exception_get_count = 0; 7455 v8::HandleScope scope; 7456 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7457 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter); 7458 LocalContext context(0, templ, v8::Handle<Value>()); 7459 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run(); 7460 v8::Handle<Value> value = CompileRun( 7461 "function f() {" 7462 " for (var i = 0; i < 100; i++) {" 7463 " try { x; } catch(e) { return true; }" 7464 " }" 7465 " return false;" 7466 "};" 7467 "f();"); 7468 CHECK_EQ(true, value->BooleanValue()); 7469 interceptor_ic_exception_get_count = 0; 7470 value = CompileRun( 7471 "function f() {" 7472 " for (var i = 0; i < 100; i++) {" 7473 " try { x(42); } catch(e) { return true; }" 7474 " }" 7475 " return false;" 7476 "};" 7477 "f();"); 7478 CHECK_EQ(true, value->BooleanValue()); 7479} 7480 7481 7482static int interceptor_ic_exception_set_count = 0; 7483 7484static v8::Handle<Value> InterceptorICExceptionSetter( 7485 Local<String> key, Local<Value> value, const AccessorInfo&) { 7486 ApiTestFuzzer::Fuzz(); 7487 if (++interceptor_ic_exception_set_count > 20) { 7488 return v8::ThrowException(v8_num(42)); 7489 } 7490 // Do not actually handle setting. 7491 return v8::Handle<Value>(); 7492} 7493 7494// Test interceptor store IC where the interceptor throws an exception 7495// once in a while. 7496THREADED_TEST(InterceptorICSetterExceptions) { 7497 interceptor_ic_exception_set_count = 0; 7498 v8::HandleScope scope; 7499 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7500 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter); 7501 LocalContext context(0, templ, v8::Handle<Value>()); 7502 v8::Handle<Value> value = CompileRun( 7503 "function f() {" 7504 " for (var i = 0; i < 100; i++) {" 7505 " try { x = 42; } catch(e) { return true; }" 7506 " }" 7507 " return false;" 7508 "};" 7509 "f();"); 7510 CHECK_EQ(true, value->BooleanValue()); 7511} 7512 7513 7514// Test that we ignore null interceptors. 7515THREADED_TEST(NullNamedInterceptor) { 7516 v8::HandleScope scope; 7517 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7518 templ->SetNamedPropertyHandler(0); 7519 LocalContext context; 7520 templ->Set("x", v8_num(42)); 7521 v8::Handle<v8::Object> obj = templ->NewInstance(); 7522 context->Global()->Set(v8_str("obj"), obj); 7523 v8::Handle<Value> value = CompileRun("obj.x"); 7524 CHECK(value->IsInt32()); 7525 CHECK_EQ(42, value->Int32Value()); 7526} 7527 7528 7529// Test that we ignore null interceptors. 7530THREADED_TEST(NullIndexedInterceptor) { 7531 v8::HandleScope scope; 7532 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7533 templ->SetIndexedPropertyHandler(0); 7534 LocalContext context; 7535 templ->Set("42", v8_num(42)); 7536 v8::Handle<v8::Object> obj = templ->NewInstance(); 7537 context->Global()->Set(v8_str("obj"), obj); 7538 v8::Handle<Value> value = CompileRun("obj[42]"); 7539 CHECK(value->IsInt32()); 7540 CHECK_EQ(42, value->Int32Value()); 7541} 7542 7543 7544THREADED_TEST(NamedPropertyHandlerGetterAttributes) { 7545 v8::HandleScope scope; 7546 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 7547 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter); 7548 LocalContext env; 7549 env->Global()->Set(v8_str("obj"), 7550 templ->GetFunction()->NewInstance()); 7551 ExpectTrue("obj.x === 42"); 7552 ExpectTrue("!obj.propertyIsEnumerable('x')"); 7553} 7554 7555 7556static v8::Handle<Value> ParentGetter(Local<String> name, 7557 const AccessorInfo& info) { 7558 ApiTestFuzzer::Fuzz(); 7559 return v8_num(1); 7560} 7561 7562 7563static v8::Handle<Value> ChildGetter(Local<String> name, 7564 const AccessorInfo& info) { 7565 ApiTestFuzzer::Fuzz(); 7566 return v8_num(42); 7567} 7568 7569 7570THREADED_TEST(Overriding) { 7571 v8::HandleScope scope; 7572 LocalContext context; 7573 7574 // Parent template. 7575 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New(); 7576 Local<ObjectTemplate> parent_instance_templ = 7577 parent_templ->InstanceTemplate(); 7578 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter); 7579 7580 // Template that inherits from the parent template. 7581 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New(); 7582 Local<ObjectTemplate> child_instance_templ = 7583 child_templ->InstanceTemplate(); 7584 child_templ->Inherit(parent_templ); 7585 // Override 'f'. The child version of 'f' should get called for child 7586 // instances. 7587 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter); 7588 // Add 'g' twice. The 'g' added last should get called for instances. 7589 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter); 7590 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter); 7591 7592 // Add 'h' as an accessor to the proto template with ReadOnly attributes 7593 // so 'h' can be shadowed on the instance object. 7594 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate(); 7595 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0, 7596 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 7597 7598 // Add 'i' as an accessor to the instance template with ReadOnly attributes 7599 // but the attribute does not have effect because it is duplicated with 7600 // NULL setter. 7601 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0, 7602 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 7603 7604 7605 7606 // Instantiate the child template. 7607 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance(); 7608 7609 // Check that the child function overrides the parent one. 7610 context->Global()->Set(v8_str("o"), instance); 7611 Local<Value> value = v8_compile("o.f")->Run(); 7612 // Check that the 'g' that was added last is hit. 7613 CHECK_EQ(42, value->Int32Value()); 7614 value = v8_compile("o.g")->Run(); 7615 CHECK_EQ(42, value->Int32Value()); 7616 7617 // Check 'h' can be shadowed. 7618 value = v8_compile("o.h = 3; o.h")->Run(); 7619 CHECK_EQ(3, value->Int32Value()); 7620 7621 // Check 'i' is cannot be shadowed or changed. 7622 value = v8_compile("o.i = 3; o.i")->Run(); 7623 CHECK_EQ(42, value->Int32Value()); 7624} 7625 7626 7627static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) { 7628 ApiTestFuzzer::Fuzz(); 7629 if (args.IsConstructCall()) { 7630 return v8::Boolean::New(true); 7631 } 7632 return v8::Boolean::New(false); 7633} 7634 7635 7636THREADED_TEST(IsConstructCall) { 7637 v8::HandleScope scope; 7638 7639 // Function template with call handler. 7640 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 7641 templ->SetCallHandler(IsConstructHandler); 7642 7643 LocalContext context; 7644 7645 context->Global()->Set(v8_str("f"), templ->GetFunction()); 7646 Local<Value> value = v8_compile("f()")->Run(); 7647 CHECK(!value->BooleanValue()); 7648 value = v8_compile("new f()")->Run(); 7649 CHECK(value->BooleanValue()); 7650} 7651 7652 7653THREADED_TEST(ObjectProtoToString) { 7654 v8::HandleScope scope; 7655 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 7656 templ->SetClassName(v8_str("MyClass")); 7657 7658 LocalContext context; 7659 7660 Local<String> customized_tostring = v8_str("customized toString"); 7661 7662 // Replace Object.prototype.toString 7663 v8_compile("Object.prototype.toString = function() {" 7664 " return 'customized toString';" 7665 "}")->Run(); 7666 7667 // Normal ToString call should call replaced Object.prototype.toString 7668 Local<v8::Object> instance = templ->GetFunction()->NewInstance(); 7669 Local<String> value = instance->ToString(); 7670 CHECK(value->IsString() && value->Equals(customized_tostring)); 7671 7672 // ObjectProtoToString should not call replace toString function. 7673 value = instance->ObjectProtoToString(); 7674 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]"))); 7675 7676 // Check global 7677 value = context->Global()->ObjectProtoToString(); 7678 CHECK(value->IsString() && value->Equals(v8_str("[object global]"))); 7679 7680 // Check ordinary object 7681 Local<Value> object = v8_compile("new Object()")->Run(); 7682 value = object.As<v8::Object>()->ObjectProtoToString(); 7683 CHECK(value->IsString() && value->Equals(v8_str("[object Object]"))); 7684} 7685 7686 7687bool ApiTestFuzzer::fuzzing_ = false; 7688i::Semaphore* ApiTestFuzzer::all_tests_done_= 7689 i::OS::CreateSemaphore(0); 7690int ApiTestFuzzer::active_tests_; 7691int ApiTestFuzzer::tests_being_run_; 7692int ApiTestFuzzer::current_; 7693 7694 7695// We are in a callback and want to switch to another thread (if we 7696// are currently running the thread fuzzing test). 7697void ApiTestFuzzer::Fuzz() { 7698 if (!fuzzing_) return; 7699 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_; 7700 test->ContextSwitch(); 7701} 7702 7703 7704// Let the next thread go. Since it is also waiting on the V8 lock it may 7705// not start immediately. 7706bool ApiTestFuzzer::NextThread() { 7707 int test_position = GetNextTestNumber(); 7708 const char* test_name = RegisterThreadedTest::nth(current_)->name(); 7709 if (test_position == current_) { 7710 if (kLogThreading) 7711 printf("Stay with %s\n", test_name); 7712 return false; 7713 } 7714 if (kLogThreading) { 7715 printf("Switch from %s to %s\n", 7716 test_name, 7717 RegisterThreadedTest::nth(test_position)->name()); 7718 } 7719 current_ = test_position; 7720 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal(); 7721 return true; 7722} 7723 7724 7725void ApiTestFuzzer::Run() { 7726 // When it is our turn... 7727 gate_->Wait(); 7728 { 7729 // ... get the V8 lock and start running the test. 7730 v8::Locker locker; 7731 CallTest(); 7732 } 7733 // This test finished. 7734 active_ = false; 7735 active_tests_--; 7736 // If it was the last then signal that fact. 7737 if (active_tests_ == 0) { 7738 all_tests_done_->Signal(); 7739 } else { 7740 // Otherwise select a new test and start that. 7741 NextThread(); 7742 } 7743} 7744 7745 7746static unsigned linear_congruential_generator; 7747 7748 7749void ApiTestFuzzer::Setup(PartOfTest part) { 7750 linear_congruential_generator = i::FLAG_testing_prng_seed; 7751 fuzzing_ = true; 7752 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1); 7753 int end = (part == FIRST_PART) 7754 ? (RegisterThreadedTest::count() >> 1) 7755 : RegisterThreadedTest::count(); 7756 active_tests_ = tests_being_run_ = end - start; 7757 for (int i = 0; i < tests_being_run_; i++) { 7758 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start); 7759 } 7760 for (int i = 0; i < active_tests_; i++) { 7761 RegisterThreadedTest::nth(i)->fuzzer_->Start(); 7762 } 7763} 7764 7765 7766static void CallTestNumber(int test_number) { 7767 (RegisterThreadedTest::nth(test_number)->callback())(); 7768} 7769 7770 7771void ApiTestFuzzer::RunAllTests() { 7772 // Set off the first test. 7773 current_ = -1; 7774 NextThread(); 7775 // Wait till they are all done. 7776 all_tests_done_->Wait(); 7777} 7778 7779 7780int ApiTestFuzzer::GetNextTestNumber() { 7781 int next_test; 7782 do { 7783 next_test = (linear_congruential_generator >> 16) % tests_being_run_; 7784 linear_congruential_generator *= 1664525u; 7785 linear_congruential_generator += 1013904223u; 7786 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_); 7787 return next_test; 7788} 7789 7790 7791void ApiTestFuzzer::ContextSwitch() { 7792 // If the new thread is the same as the current thread there is nothing to do. 7793 if (NextThread()) { 7794 // Now it can start. 7795 v8::Unlocker unlocker; 7796 // Wait till someone starts us again. 7797 gate_->Wait(); 7798 // And we're off. 7799 } 7800} 7801 7802 7803void ApiTestFuzzer::TearDown() { 7804 fuzzing_ = false; 7805 for (int i = 0; i < RegisterThreadedTest::count(); i++) { 7806 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_; 7807 if (fuzzer != NULL) fuzzer->Join(); 7808 } 7809} 7810 7811 7812// Lets not be needlessly self-referential. 7813TEST(Threading) { 7814 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART); 7815 ApiTestFuzzer::RunAllTests(); 7816 ApiTestFuzzer::TearDown(); 7817} 7818 7819TEST(Threading2) { 7820 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART); 7821 ApiTestFuzzer::RunAllTests(); 7822 ApiTestFuzzer::TearDown(); 7823} 7824 7825 7826void ApiTestFuzzer::CallTest() { 7827 if (kLogThreading) 7828 printf("Start test %d\n", test_number_); 7829 CallTestNumber(test_number_); 7830 if (kLogThreading) 7831 printf("End test %d\n", test_number_); 7832} 7833 7834 7835static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) { 7836 CHECK(v8::Locker::IsLocked()); 7837 ApiTestFuzzer::Fuzz(); 7838 v8::Unlocker unlocker; 7839 const char* code = "throw 7;"; 7840 { 7841 v8::Locker nested_locker; 7842 v8::HandleScope scope; 7843 v8::Handle<Value> exception; 7844 { v8::TryCatch try_catch; 7845 v8::Handle<Value> value = CompileRun(code); 7846 CHECK(value.IsEmpty()); 7847 CHECK(try_catch.HasCaught()); 7848 // Make sure to wrap the exception in a new handle because 7849 // the handle returned from the TryCatch is destroyed 7850 // when the TryCatch is destroyed. 7851 exception = Local<Value>::New(try_catch.Exception()); 7852 } 7853 return v8::ThrowException(exception); 7854 } 7855} 7856 7857 7858static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) { 7859 CHECK(v8::Locker::IsLocked()); 7860 ApiTestFuzzer::Fuzz(); 7861 v8::Unlocker unlocker; 7862 const char* code = "throw 7;"; 7863 { 7864 v8::Locker nested_locker; 7865 v8::HandleScope scope; 7866 v8::Handle<Value> value = CompileRun(code); 7867 CHECK(value.IsEmpty()); 7868 return v8_str("foo"); 7869 } 7870} 7871 7872 7873// These are locking tests that don't need to be run again 7874// as part of the locking aggregation tests. 7875TEST(NestedLockers) { 7876 v8::Locker locker; 7877 CHECK(v8::Locker::IsLocked()); 7878 v8::HandleScope scope; 7879 LocalContext env; 7880 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS); 7881 Local<Function> fun = fun_templ->GetFunction(); 7882 env->Global()->Set(v8_str("throw_in_js"), fun); 7883 Local<Script> script = v8_compile("(function () {" 7884 " try {" 7885 " throw_in_js();" 7886 " return 42;" 7887 " } catch (e) {" 7888 " return e * 13;" 7889 " }" 7890 "})();"); 7891 CHECK_EQ(91, script->Run()->Int32Value()); 7892} 7893 7894 7895// These are locking tests that don't need to be run again 7896// as part of the locking aggregation tests. 7897TEST(NestedLockersNoTryCatch) { 7898 v8::Locker locker; 7899 v8::HandleScope scope; 7900 LocalContext env; 7901 Local<v8::FunctionTemplate> fun_templ = 7902 v8::FunctionTemplate::New(ThrowInJSNoCatch); 7903 Local<Function> fun = fun_templ->GetFunction(); 7904 env->Global()->Set(v8_str("throw_in_js"), fun); 7905 Local<Script> script = v8_compile("(function () {" 7906 " try {" 7907 " throw_in_js();" 7908 " return 42;" 7909 " } catch (e) {" 7910 " return e * 13;" 7911 " }" 7912 "})();"); 7913 CHECK_EQ(91, script->Run()->Int32Value()); 7914} 7915 7916 7917THREADED_TEST(RecursiveLocking) { 7918 v8::Locker locker; 7919 { 7920 v8::Locker locker2; 7921 CHECK(v8::Locker::IsLocked()); 7922 } 7923} 7924 7925 7926static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) { 7927 ApiTestFuzzer::Fuzz(); 7928 v8::Unlocker unlocker; 7929 return v8::Undefined(); 7930} 7931 7932 7933THREADED_TEST(LockUnlockLock) { 7934 { 7935 v8::Locker locker; 7936 v8::HandleScope scope; 7937 LocalContext env; 7938 Local<v8::FunctionTemplate> fun_templ = 7939 v8::FunctionTemplate::New(UnlockForAMoment); 7940 Local<Function> fun = fun_templ->GetFunction(); 7941 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 7942 Local<Script> script = v8_compile("(function () {" 7943 " unlock_for_a_moment();" 7944 " return 42;" 7945 "})();"); 7946 CHECK_EQ(42, script->Run()->Int32Value()); 7947 } 7948 { 7949 v8::Locker locker; 7950 v8::HandleScope scope; 7951 LocalContext env; 7952 Local<v8::FunctionTemplate> fun_templ = 7953 v8::FunctionTemplate::New(UnlockForAMoment); 7954 Local<Function> fun = fun_templ->GetFunction(); 7955 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 7956 Local<Script> script = v8_compile("(function () {" 7957 " unlock_for_a_moment();" 7958 " return 42;" 7959 "})();"); 7960 CHECK_EQ(42, script->Run()->Int32Value()); 7961 } 7962} 7963 7964 7965static int GetGlobalObjectsCount() { 7966 int count = 0; 7967 i::HeapIterator it; 7968 for (i::HeapObject* object = it.next(); object != NULL; object = it.next()) 7969 if (object->IsJSGlobalObject()) count++; 7970 return count; 7971} 7972 7973 7974static int GetSurvivingGlobalObjectsCount() { 7975 // We need to collect all garbage twice to be sure that everything 7976 // has been collected. This is because inline caches are cleared in 7977 // the first garbage collection but some of the maps have already 7978 // been marked at that point. Therefore some of the maps are not 7979 // collected until the second garbage collection. 7980 i::Heap::CollectAllGarbage(false); 7981 i::Heap::CollectAllGarbage(false); 7982 int count = GetGlobalObjectsCount(); 7983#ifdef DEBUG 7984 if (count > 0) i::Heap::TracePathToGlobal(); 7985#endif 7986 return count; 7987} 7988 7989 7990TEST(DontLeakGlobalObjects) { 7991 // Regression test for issues 1139850 and 1174891. 7992 7993 v8::V8::Initialize(); 7994 7995 int count = GetSurvivingGlobalObjectsCount(); 7996 7997 for (int i = 0; i < 5; i++) { 7998 { v8::HandleScope scope; 7999 LocalContext context; 8000 } 8001 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 8002 8003 { v8::HandleScope scope; 8004 LocalContext context; 8005 v8_compile("Date")->Run(); 8006 } 8007 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 8008 8009 { v8::HandleScope scope; 8010 LocalContext context; 8011 v8_compile("/aaa/")->Run(); 8012 } 8013 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 8014 8015 { v8::HandleScope scope; 8016 const char* extension_list[] = { "v8/gc" }; 8017 v8::ExtensionConfiguration extensions(1, extension_list); 8018 LocalContext context(&extensions); 8019 v8_compile("gc();")->Run(); 8020 } 8021 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 8022 } 8023} 8024 8025 8026v8::Persistent<v8::Object> some_object; 8027v8::Persistent<v8::Object> bad_handle; 8028 8029void NewPersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) { 8030 v8::HandleScope scope; 8031 bad_handle = v8::Persistent<v8::Object>::New(some_object); 8032 handle.Dispose(); 8033} 8034 8035 8036THREADED_TEST(NewPersistentHandleFromWeakCallback) { 8037 LocalContext context; 8038 8039 v8::Persistent<v8::Object> handle1, handle2; 8040 { 8041 v8::HandleScope scope; 8042 some_object = v8::Persistent<v8::Object>::New(v8::Object::New()); 8043 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8044 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8045 } 8046 // Note: order is implementation dependent alas: currently 8047 // global handle nodes are processed by PostGarbageCollectionProcessing 8048 // in reverse allocation order, so if second allocated handle is deleted, 8049 // weak callback of the first handle would be able to 'reallocate' it. 8050 handle1.MakeWeak(NULL, NewPersistentHandleCallback); 8051 handle2.Dispose(); 8052 i::Heap::CollectAllGarbage(false); 8053} 8054 8055 8056v8::Persistent<v8::Object> to_be_disposed; 8057 8058void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) { 8059 to_be_disposed.Dispose(); 8060 i::Heap::CollectAllGarbage(false); 8061 handle.Dispose(); 8062} 8063 8064 8065THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) { 8066 LocalContext context; 8067 8068 v8::Persistent<v8::Object> handle1, handle2; 8069 { 8070 v8::HandleScope scope; 8071 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8072 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8073 } 8074 handle1.MakeWeak(NULL, DisposeAndForceGcCallback); 8075 to_be_disposed = handle2; 8076 i::Heap::CollectAllGarbage(false); 8077} 8078 8079void DisposingCallback(v8::Persistent<v8::Value> handle, void*) { 8080 handle.Dispose(); 8081} 8082 8083void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) { 8084 v8::HandleScope scope; 8085 v8::Persistent<v8::Object>::New(v8::Object::New()); 8086 handle.Dispose(); 8087} 8088 8089 8090THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) { 8091 LocalContext context; 8092 8093 v8::Persistent<v8::Object> handle1, handle2, handle3; 8094 { 8095 v8::HandleScope scope; 8096 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8097 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8098 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8099 } 8100 handle2.MakeWeak(NULL, DisposingCallback); 8101 handle3.MakeWeak(NULL, HandleCreatingCallback); 8102 i::Heap::CollectAllGarbage(false); 8103} 8104 8105 8106THREADED_TEST(CheckForCrossContextObjectLiterals) { 8107 v8::V8::Initialize(); 8108 8109 const int nof = 2; 8110 const char* sources[nof] = { 8111 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }", 8112 "Object()" 8113 }; 8114 8115 for (int i = 0; i < nof; i++) { 8116 const char* source = sources[i]; 8117 { v8::HandleScope scope; 8118 LocalContext context; 8119 CompileRun(source); 8120 } 8121 { v8::HandleScope scope; 8122 LocalContext context; 8123 CompileRun(source); 8124 } 8125 } 8126} 8127 8128 8129static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) { 8130 v8::HandleScope inner; 8131 env->Enter(); 8132 v8::Handle<Value> three = v8_num(3); 8133 v8::Handle<Value> value = inner.Close(three); 8134 env->Exit(); 8135 return value; 8136} 8137 8138 8139THREADED_TEST(NestedHandleScopeAndContexts) { 8140 v8::HandleScope outer; 8141 v8::Persistent<Context> env = Context::New(); 8142 env->Enter(); 8143 v8::Handle<Value> value = NestedScope(env); 8144 v8::Handle<String> str = value->ToString(); 8145 env->Exit(); 8146 env.Dispose(); 8147} 8148 8149 8150THREADED_TEST(ExternalAllocatedMemory) { 8151 v8::HandleScope outer; 8152 v8::Persistent<Context> env = Context::New(); 8153 const int kSize = 1024*1024; 8154 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize); 8155 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0); 8156} 8157 8158 8159THREADED_TEST(DisposeEnteredContext) { 8160 v8::HandleScope scope; 8161 LocalContext outer; 8162 { v8::Persistent<v8::Context> inner = v8::Context::New(); 8163 inner->Enter(); 8164 inner.Dispose(); 8165 inner.Clear(); 8166 inner->Exit(); 8167 } 8168} 8169 8170 8171// Regression test for issue 54, object templates with internal fields 8172// but no accessors or interceptors did not get their internal field 8173// count set on instances. 8174THREADED_TEST(Regress54) { 8175 v8::HandleScope outer; 8176 LocalContext context; 8177 static v8::Persistent<v8::ObjectTemplate> templ; 8178 if (templ.IsEmpty()) { 8179 v8::HandleScope inner; 8180 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New(); 8181 local->SetInternalFieldCount(1); 8182 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local)); 8183 } 8184 v8::Handle<v8::Object> result = templ->NewInstance(); 8185 CHECK_EQ(1, result->InternalFieldCount()); 8186} 8187 8188 8189// If part of the threaded tests, this test makes ThreadingTest fail 8190// on mac. 8191TEST(CatchStackOverflow) { 8192 v8::HandleScope scope; 8193 LocalContext context; 8194 v8::TryCatch try_catch; 8195 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New( 8196 "function f() {" 8197 " return f();" 8198 "}" 8199 "" 8200 "f();")); 8201 v8::Handle<v8::Value> result = script->Run(); 8202 CHECK(result.IsEmpty()); 8203} 8204 8205 8206static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script, 8207 const char* resource_name, 8208 int line_offset) { 8209 v8::HandleScope scope; 8210 v8::TryCatch try_catch; 8211 v8::Handle<v8::Value> result = script->Run(); 8212 CHECK(result.IsEmpty()); 8213 CHECK(try_catch.HasCaught()); 8214 v8::Handle<v8::Message> message = try_catch.Message(); 8215 CHECK(!message.IsEmpty()); 8216 CHECK_EQ(10 + line_offset, message->GetLineNumber()); 8217 CHECK_EQ(91, message->GetStartPosition()); 8218 CHECK_EQ(92, message->GetEndPosition()); 8219 CHECK_EQ(2, message->GetStartColumn()); 8220 CHECK_EQ(3, message->GetEndColumn()); 8221 v8::String::AsciiValue line(message->GetSourceLine()); 8222 CHECK_EQ(" throw 'nirk';", *line); 8223 v8::String::AsciiValue name(message->GetScriptResourceName()); 8224 CHECK_EQ(resource_name, *name); 8225} 8226 8227 8228THREADED_TEST(TryCatchSourceInfo) { 8229 v8::HandleScope scope; 8230 LocalContext context; 8231 v8::Handle<v8::String> source = v8::String::New( 8232 "function Foo() {\n" 8233 " return Bar();\n" 8234 "}\n" 8235 "\n" 8236 "function Bar() {\n" 8237 " return Baz();\n" 8238 "}\n" 8239 "\n" 8240 "function Baz() {\n" 8241 " throw 'nirk';\n" 8242 "}\n" 8243 "\n" 8244 "Foo();\n"); 8245 8246 const char* resource_name; 8247 v8::Handle<v8::Script> script; 8248 resource_name = "test.js"; 8249 script = v8::Script::Compile(source, v8::String::New(resource_name)); 8250 CheckTryCatchSourceInfo(script, resource_name, 0); 8251 8252 resource_name = "test1.js"; 8253 v8::ScriptOrigin origin1(v8::String::New(resource_name)); 8254 script = v8::Script::Compile(source, &origin1); 8255 CheckTryCatchSourceInfo(script, resource_name, 0); 8256 8257 resource_name = "test2.js"; 8258 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7)); 8259 script = v8::Script::Compile(source, &origin2); 8260 CheckTryCatchSourceInfo(script, resource_name, 7); 8261} 8262 8263 8264THREADED_TEST(CompilationCache) { 8265 v8::HandleScope scope; 8266 LocalContext context; 8267 v8::Handle<v8::String> source0 = v8::String::New("1234"); 8268 v8::Handle<v8::String> source1 = v8::String::New("1234"); 8269 v8::Handle<v8::Script> script0 = 8270 v8::Script::Compile(source0, v8::String::New("test.js")); 8271 v8::Handle<v8::Script> script1 = 8272 v8::Script::Compile(source1, v8::String::New("test.js")); 8273 v8::Handle<v8::Script> script2 = 8274 v8::Script::Compile(source0); // different origin 8275 CHECK_EQ(1234, script0->Run()->Int32Value()); 8276 CHECK_EQ(1234, script1->Run()->Int32Value()); 8277 CHECK_EQ(1234, script2->Run()->Int32Value()); 8278} 8279 8280 8281static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) { 8282 ApiTestFuzzer::Fuzz(); 8283 return v8_num(42); 8284} 8285 8286 8287THREADED_TEST(CallbackFunctionName) { 8288 v8::HandleScope scope; 8289 LocalContext context; 8290 Local<ObjectTemplate> t = ObjectTemplate::New(); 8291 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback)); 8292 context->Global()->Set(v8_str("obj"), t->NewInstance()); 8293 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name"); 8294 CHECK(value->IsString()); 8295 v8::String::AsciiValue name(value); 8296 CHECK_EQ("asdf", *name); 8297} 8298 8299 8300THREADED_TEST(DateAccess) { 8301 v8::HandleScope scope; 8302 LocalContext context; 8303 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0); 8304 CHECK(date->IsDate()); 8305 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue()); 8306} 8307 8308 8309void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) { 8310 v8::Handle<v8::Object> obj = val.As<v8::Object>(); 8311 v8::Handle<v8::Array> props = obj->GetPropertyNames(); 8312 CHECK_EQ(elmc, props->Length()); 8313 for (int i = 0; i < elmc; i++) { 8314 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i))); 8315 CHECK_EQ(elmv[i], *elm); 8316 } 8317} 8318 8319 8320THREADED_TEST(PropertyEnumeration) { 8321 v8::HandleScope scope; 8322 LocalContext context; 8323 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New( 8324 "var result = [];" 8325 "result[0] = {};" 8326 "result[1] = {a: 1, b: 2};" 8327 "result[2] = [1, 2, 3];" 8328 "var proto = {x: 1, y: 2, z: 3};" 8329 "var x = { __proto__: proto, w: 0, z: 1 };" 8330 "result[3] = x;" 8331 "result;"))->Run(); 8332 v8::Handle<v8::Array> elms = obj.As<v8::Array>(); 8333 CHECK_EQ(4, elms->Length()); 8334 int elmc0 = 0; 8335 const char** elmv0 = NULL; 8336 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0); 8337 int elmc1 = 2; 8338 const char* elmv1[] = {"a", "b"}; 8339 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1); 8340 int elmc2 = 3; 8341 const char* elmv2[] = {"0", "1", "2"}; 8342 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2); 8343 int elmc3 = 4; 8344 const char* elmv3[] = {"w", "z", "x", "y"}; 8345 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3); 8346} 8347 8348 8349static bool NamedSetAccessBlocker(Local<v8::Object> obj, 8350 Local<Value> name, 8351 v8::AccessType type, 8352 Local<Value> data) { 8353 return type != v8::ACCESS_SET; 8354} 8355 8356 8357static bool IndexedSetAccessBlocker(Local<v8::Object> obj, 8358 uint32_t key, 8359 v8::AccessType type, 8360 Local<Value> data) { 8361 return type != v8::ACCESS_SET; 8362} 8363 8364 8365THREADED_TEST(DisableAccessChecksWhileConfiguring) { 8366 v8::HandleScope scope; 8367 LocalContext context; 8368 Local<ObjectTemplate> templ = ObjectTemplate::New(); 8369 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker, 8370 IndexedSetAccessBlocker); 8371 templ->Set(v8_str("x"), v8::True()); 8372 Local<v8::Object> instance = templ->NewInstance(); 8373 context->Global()->Set(v8_str("obj"), instance); 8374 Local<Value> value = CompileRun("obj.x"); 8375 CHECK(value->BooleanValue()); 8376} 8377 8378 8379static bool NamedGetAccessBlocker(Local<v8::Object> obj, 8380 Local<Value> name, 8381 v8::AccessType type, 8382 Local<Value> data) { 8383 return false; 8384} 8385 8386 8387static bool IndexedGetAccessBlocker(Local<v8::Object> obj, 8388 uint32_t key, 8389 v8::AccessType type, 8390 Local<Value> data) { 8391 return false; 8392} 8393 8394 8395 8396THREADED_TEST(AccessChecksReenabledCorrectly) { 8397 v8::HandleScope scope; 8398 LocalContext context; 8399 Local<ObjectTemplate> templ = ObjectTemplate::New(); 8400 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker, 8401 IndexedGetAccessBlocker); 8402 templ->Set(v8_str("a"), v8_str("a")); 8403 // Add more than 8 (see kMaxFastProperties) properties 8404 // so that the constructor will force copying map. 8405 // Cannot sprintf, gcc complains unsafety. 8406 char buf[4]; 8407 for (char i = '0'; i <= '9' ; i++) { 8408 buf[0] = i; 8409 for (char j = '0'; j <= '9'; j++) { 8410 buf[1] = j; 8411 for (char k = '0'; k <= '9'; k++) { 8412 buf[2] = k; 8413 buf[3] = 0; 8414 templ->Set(v8_str(buf), v8::Number::New(k)); 8415 } 8416 } 8417 } 8418 8419 Local<v8::Object> instance_1 = templ->NewInstance(); 8420 context->Global()->Set(v8_str("obj_1"), instance_1); 8421 8422 Local<Value> value_1 = CompileRun("obj_1.a"); 8423 CHECK(value_1->IsUndefined()); 8424 8425 Local<v8::Object> instance_2 = templ->NewInstance(); 8426 context->Global()->Set(v8_str("obj_2"), instance_2); 8427 8428 Local<Value> value_2 = CompileRun("obj_2.a"); 8429 CHECK(value_2->IsUndefined()); 8430} 8431 8432 8433// This tests that access check information remains on the global 8434// object template when creating contexts. 8435THREADED_TEST(AccessControlRepeatedContextCreation) { 8436 v8::HandleScope handle_scope; 8437 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 8438 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker, 8439 IndexedSetAccessBlocker); 8440 i::Handle<i::ObjectTemplateInfo> internal_template = 8441 v8::Utils::OpenHandle(*global_template); 8442 CHECK(!internal_template->constructor()->IsUndefined()); 8443 i::Handle<i::FunctionTemplateInfo> constructor( 8444 i::FunctionTemplateInfo::cast(internal_template->constructor())); 8445 CHECK(!constructor->access_check_info()->IsUndefined()); 8446 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 8447 CHECK(!constructor->access_check_info()->IsUndefined()); 8448} 8449 8450 8451THREADED_TEST(TurnOnAccessCheck) { 8452 v8::HandleScope handle_scope; 8453 8454 // Create an environment with access check to the global object disabled by 8455 // default. 8456 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 8457 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker, 8458 IndexedGetAccessBlocker, 8459 v8::Handle<v8::Value>(), 8460 false); 8461 v8::Persistent<Context> context = Context::New(NULL, global_template); 8462 Context::Scope context_scope(context); 8463 8464 // Set up a property and a number of functions. 8465 context->Global()->Set(v8_str("a"), v8_num(1)); 8466 CompileRun("function f1() {return a;}" 8467 "function f2() {return a;}" 8468 "function g1() {return h();}" 8469 "function g2() {return h();}" 8470 "function h() {return 1;}"); 8471 Local<Function> f1 = 8472 Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); 8473 Local<Function> f2 = 8474 Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); 8475 Local<Function> g1 = 8476 Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); 8477 Local<Function> g2 = 8478 Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); 8479 Local<Function> h = 8480 Local<Function>::Cast(context->Global()->Get(v8_str("h"))); 8481 8482 // Get the global object. 8483 v8::Handle<v8::Object> global = context->Global(); 8484 8485 // Call f1 one time and f2 a number of times. This will ensure that f1 still 8486 // uses the runtime system to retreive property a whereas f2 uses global load 8487 // inline cache. 8488 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1))); 8489 for (int i = 0; i < 4; i++) { 8490 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1))); 8491 } 8492 8493 // Same for g1 and g2. 8494 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1))); 8495 for (int i = 0; i < 4; i++) { 8496 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1))); 8497 } 8498 8499 // Detach the global and turn on access check. 8500 context->DetachGlobal(); 8501 context->Global()->TurnOnAccessCheck(); 8502 8503 // Failing access check to property get results in undefined. 8504 CHECK(f1->Call(global, 0, NULL)->IsUndefined()); 8505 CHECK(f2->Call(global, 0, NULL)->IsUndefined()); 8506 8507 // Failing access check to function call results in exception. 8508 CHECK(g1->Call(global, 0, NULL).IsEmpty()); 8509 CHECK(g2->Call(global, 0, NULL).IsEmpty()); 8510 8511 // No failing access check when just returning a constant. 8512 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1))); 8513} 8514 8515 8516// This test verifies that pre-compilation (aka preparsing) can be called 8517// without initializing the whole VM. Thus we cannot run this test in a 8518// multi-threaded setup. 8519TEST(PreCompile) { 8520 // TODO(155): This test would break without the initialization of V8. This is 8521 // a workaround for now to make this test not fail. 8522 v8::V8::Initialize(); 8523 const char* script = "function foo(a) { return a+1; }"; 8524 v8::ScriptData* sd = 8525 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8526 CHECK_NE(sd->Length(), 0); 8527 CHECK_NE(sd->Data(), NULL); 8528 CHECK(!sd->HasError()); 8529 delete sd; 8530} 8531 8532 8533TEST(PreCompileWithError) { 8534 v8::V8::Initialize(); 8535 const char* script = "function foo(a) { return 1 * * 2; }"; 8536 v8::ScriptData* sd = 8537 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8538 CHECK(sd->HasError()); 8539 delete sd; 8540} 8541 8542 8543TEST(Regress31661) { 8544 v8::V8::Initialize(); 8545 const char* script = " The Definintive Guide"; 8546 v8::ScriptData* sd = 8547 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8548 CHECK(sd->HasError()); 8549 delete sd; 8550} 8551 8552 8553// Tests that ScriptData can be serialized and deserialized. 8554TEST(PreCompileSerialization) { 8555 v8::V8::Initialize(); 8556 const char* script = "function foo(a) { return a+1; }"; 8557 v8::ScriptData* sd = 8558 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8559 8560 // Serialize. 8561 int serialized_data_length = sd->Length(); 8562 char* serialized_data = i::NewArray<char>(serialized_data_length); 8563 memcpy(serialized_data, sd->Data(), serialized_data_length); 8564 8565 // Deserialize. 8566 v8::ScriptData* deserialized_sd = 8567 v8::ScriptData::New(serialized_data, serialized_data_length); 8568 8569 // Verify that the original is the same as the deserialized. 8570 CHECK_EQ(sd->Length(), deserialized_sd->Length()); 8571 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length())); 8572 CHECK_EQ(sd->HasError(), deserialized_sd->HasError()); 8573 8574 delete sd; 8575 delete deserialized_sd; 8576} 8577 8578 8579// Attempts to deserialize bad data. 8580TEST(PreCompileDeserializationError) { 8581 v8::V8::Initialize(); 8582 const char* data = "DONT CARE"; 8583 int invalid_size = 3; 8584 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size); 8585 8586 CHECK_EQ(0, sd->Length()); 8587 8588 delete sd; 8589} 8590 8591 8592// Attempts to deserialize bad data. 8593TEST(PreCompileInvalidPreparseDataError) { 8594 v8::V8::Initialize(); 8595 v8::HandleScope scope; 8596 LocalContext context; 8597 8598 const char* script = "function foo(){ return 5;}\n" 8599 "function bar(){ return 6 + 7;} foo();"; 8600 v8::ScriptData* sd = 8601 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8602 CHECK(!sd->HasError()); 8603 // ScriptDataImpl private implementation details 8604 const int kUnsignedSize = sizeof(unsigned); 8605 const int kHeaderSize = 4; 8606 const int kFunctionEntrySize = 5; 8607 const int kFunctionEntryStartOffset = 0; 8608 const int kFunctionEntryEndOffset = 1; 8609 unsigned* sd_data = 8610 reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data())); 8611 CHECK_EQ(sd->Length(), 8612 (kHeaderSize + 2 * kFunctionEntrySize) * kUnsignedSize); 8613 8614 // Overwrite function bar's end position with 0. 8615 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0; 8616 v8::TryCatch try_catch; 8617 8618 Local<String> source = String::New(script); 8619 Local<Script> compiled_script = Script::New(source, NULL, sd); 8620 CHECK(try_catch.HasCaught()); 8621 String::AsciiValue exception_value(try_catch.Message()->Get()); 8622 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar", 8623 *exception_value); 8624 8625 try_catch.Reset(); 8626 // Overwrite function bar's start position with 200. The function entry 8627 // will not be found when searching for it by position. 8628 sd = v8::ScriptData::PreCompile(script, i::StrLength(script)); 8629 sd_data = reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data())); 8630 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] = 8631 200; 8632 compiled_script = Script::New(source, NULL, sd); 8633 CHECK(try_catch.HasCaught()); 8634 String::AsciiValue second_exception_value(try_catch.Message()->Get()); 8635 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar", 8636 *second_exception_value); 8637 8638 delete sd; 8639} 8640 8641 8642// Verifies that the Handle<String> and const char* versions of the API produce 8643// the same results (at least for one trivial case). 8644TEST(PreCompileAPIVariationsAreSame) { 8645 v8::V8::Initialize(); 8646 v8::HandleScope scope; 8647 8648 const char* cstring = "function foo(a) { return a+1; }"; 8649 8650 v8::ScriptData* sd_from_cstring = 8651 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring)); 8652 8653 TestAsciiResource* resource = new TestAsciiResource(cstring); 8654 v8::ScriptData* sd_from_external_string = v8::ScriptData::PreCompile( 8655 v8::String::NewExternal(resource)); 8656 8657 v8::ScriptData* sd_from_string = v8::ScriptData::PreCompile( 8658 v8::String::New(cstring)); 8659 8660 CHECK_EQ(sd_from_cstring->Length(), sd_from_external_string->Length()); 8661 CHECK_EQ(0, memcmp(sd_from_cstring->Data(), 8662 sd_from_external_string->Data(), 8663 sd_from_cstring->Length())); 8664 8665 CHECK_EQ(sd_from_cstring->Length(), sd_from_string->Length()); 8666 CHECK_EQ(0, memcmp(sd_from_cstring->Data(), 8667 sd_from_string->Data(), 8668 sd_from_cstring->Length())); 8669 8670 8671 delete sd_from_cstring; 8672 delete sd_from_external_string; 8673 delete sd_from_string; 8674} 8675 8676 8677// This tests that we do not allow dictionary load/call inline caches 8678// to use functions that have not yet been compiled. The potential 8679// problem of loading a function that has not yet been compiled can 8680// arise because we share code between contexts via the compilation 8681// cache. 8682THREADED_TEST(DictionaryICLoadedFunction) { 8683 v8::HandleScope scope; 8684 // Test LoadIC. 8685 for (int i = 0; i < 2; i++) { 8686 LocalContext context; 8687 context->Global()->Set(v8_str("tmp"), v8::True()); 8688 context->Global()->Delete(v8_str("tmp")); 8689 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');"); 8690 } 8691 // Test CallIC. 8692 for (int i = 0; i < 2; i++) { 8693 LocalContext context; 8694 context->Global()->Set(v8_str("tmp"), v8::True()); 8695 context->Global()->Delete(v8_str("tmp")); 8696 CompileRun("for (var j = 0; j < 10; j++) RegExp('')"); 8697 } 8698} 8699 8700 8701// Test that cross-context new calls use the context of the callee to 8702// create the new JavaScript object. 8703THREADED_TEST(CrossContextNew) { 8704 v8::HandleScope scope; 8705 v8::Persistent<Context> context0 = Context::New(); 8706 v8::Persistent<Context> context1 = Context::New(); 8707 8708 // Allow cross-domain access. 8709 Local<String> token = v8_str("<security token>"); 8710 context0->SetSecurityToken(token); 8711 context1->SetSecurityToken(token); 8712 8713 // Set an 'x' property on the Object prototype and define a 8714 // constructor function in context0. 8715 context0->Enter(); 8716 CompileRun("Object.prototype.x = 42; function C() {};"); 8717 context0->Exit(); 8718 8719 // Call the constructor function from context0 and check that the 8720 // result has the 'x' property. 8721 context1->Enter(); 8722 context1->Global()->Set(v8_str("other"), context0->Global()); 8723 Local<Value> value = CompileRun("var instance = new other.C(); instance.x"); 8724 CHECK(value->IsInt32()); 8725 CHECK_EQ(42, value->Int32Value()); 8726 context1->Exit(); 8727 8728 // Dispose the contexts to allow them to be garbage collected. 8729 context0.Dispose(); 8730 context1.Dispose(); 8731} 8732 8733 8734class RegExpInterruptTest { 8735 public: 8736 RegExpInterruptTest() : block_(NULL) {} 8737 ~RegExpInterruptTest() { delete block_; } 8738 void RunTest() { 8739 block_ = i::OS::CreateSemaphore(0); 8740 gc_count_ = 0; 8741 gc_during_regexp_ = 0; 8742 regexp_success_ = false; 8743 gc_success_ = false; 8744 GCThread gc_thread(this); 8745 gc_thread.Start(); 8746 v8::Locker::StartPreemption(1); 8747 8748 LongRunningRegExp(); 8749 { 8750 v8::Unlocker unlock; 8751 gc_thread.Join(); 8752 } 8753 v8::Locker::StopPreemption(); 8754 CHECK(regexp_success_); 8755 CHECK(gc_success_); 8756 } 8757 private: 8758 // Number of garbage collections required. 8759 static const int kRequiredGCs = 5; 8760 8761 class GCThread : public i::Thread { 8762 public: 8763 explicit GCThread(RegExpInterruptTest* test) 8764 : test_(test) {} 8765 virtual void Run() { 8766 test_->CollectGarbage(); 8767 } 8768 private: 8769 RegExpInterruptTest* test_; 8770 }; 8771 8772 void CollectGarbage() { 8773 block_->Wait(); 8774 while (gc_during_regexp_ < kRequiredGCs) { 8775 { 8776 v8::Locker lock; 8777 // TODO(lrn): Perhaps create some garbage before collecting. 8778 i::Heap::CollectAllGarbage(false); 8779 gc_count_++; 8780 } 8781 i::OS::Sleep(1); 8782 } 8783 gc_success_ = true; 8784 } 8785 8786 void LongRunningRegExp() { 8787 block_->Signal(); // Enable garbage collection thread on next preemption. 8788 int rounds = 0; 8789 while (gc_during_regexp_ < kRequiredGCs) { 8790 int gc_before = gc_count_; 8791 { 8792 // Match 15-30 "a"'s against 14 and a "b". 8793 const char* c_source = 8794 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 8795 ".exec('aaaaaaaaaaaaaaab') === null"; 8796 Local<String> source = String::New(c_source); 8797 Local<Script> script = Script::Compile(source); 8798 Local<Value> result = script->Run(); 8799 if (!result->BooleanValue()) { 8800 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit. 8801 return; 8802 } 8803 } 8804 { 8805 // Match 15-30 "a"'s against 15 and a "b". 8806 const char* c_source = 8807 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 8808 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'"; 8809 Local<String> source = String::New(c_source); 8810 Local<Script> script = Script::Compile(source); 8811 Local<Value> result = script->Run(); 8812 if (!result->BooleanValue()) { 8813 gc_during_regexp_ = kRequiredGCs; 8814 return; 8815 } 8816 } 8817 int gc_after = gc_count_; 8818 gc_during_regexp_ += gc_after - gc_before; 8819 rounds++; 8820 i::OS::Sleep(1); 8821 } 8822 regexp_success_ = true; 8823 } 8824 8825 i::Semaphore* block_; 8826 int gc_count_; 8827 int gc_during_regexp_; 8828 bool regexp_success_; 8829 bool gc_success_; 8830}; 8831 8832 8833// Test that a regular expression execution can be interrupted and 8834// survive a garbage collection. 8835TEST(RegExpInterruption) { 8836 v8::Locker lock; 8837 v8::V8::Initialize(); 8838 v8::HandleScope scope; 8839 Local<Context> local_env; 8840 { 8841 LocalContext env; 8842 local_env = env.local(); 8843 } 8844 8845 // Local context should still be live. 8846 CHECK(!local_env.IsEmpty()); 8847 local_env->Enter(); 8848 8849 // Should complete without problems. 8850 RegExpInterruptTest().RunTest(); 8851 8852 local_env->Exit(); 8853} 8854 8855 8856class ApplyInterruptTest { 8857 public: 8858 ApplyInterruptTest() : block_(NULL) {} 8859 ~ApplyInterruptTest() { delete block_; } 8860 void RunTest() { 8861 block_ = i::OS::CreateSemaphore(0); 8862 gc_count_ = 0; 8863 gc_during_apply_ = 0; 8864 apply_success_ = false; 8865 gc_success_ = false; 8866 GCThread gc_thread(this); 8867 gc_thread.Start(); 8868 v8::Locker::StartPreemption(1); 8869 8870 LongRunningApply(); 8871 { 8872 v8::Unlocker unlock; 8873 gc_thread.Join(); 8874 } 8875 v8::Locker::StopPreemption(); 8876 CHECK(apply_success_); 8877 CHECK(gc_success_); 8878 } 8879 private: 8880 // Number of garbage collections required. 8881 static const int kRequiredGCs = 2; 8882 8883 class GCThread : public i::Thread { 8884 public: 8885 explicit GCThread(ApplyInterruptTest* test) 8886 : test_(test) {} 8887 virtual void Run() { 8888 test_->CollectGarbage(); 8889 } 8890 private: 8891 ApplyInterruptTest* test_; 8892 }; 8893 8894 void CollectGarbage() { 8895 block_->Wait(); 8896 while (gc_during_apply_ < kRequiredGCs) { 8897 { 8898 v8::Locker lock; 8899 i::Heap::CollectAllGarbage(false); 8900 gc_count_++; 8901 } 8902 i::OS::Sleep(1); 8903 } 8904 gc_success_ = true; 8905 } 8906 8907 void LongRunningApply() { 8908 block_->Signal(); 8909 int rounds = 0; 8910 while (gc_during_apply_ < kRequiredGCs) { 8911 int gc_before = gc_count_; 8912 { 8913 const char* c_source = 8914 "function do_very_little(bar) {" 8915 " this.foo = bar;" 8916 "}" 8917 "for (var i = 0; i < 100000; i++) {" 8918 " do_very_little.apply(this, ['bar']);" 8919 "}"; 8920 Local<String> source = String::New(c_source); 8921 Local<Script> script = Script::Compile(source); 8922 Local<Value> result = script->Run(); 8923 // Check that no exception was thrown. 8924 CHECK(!result.IsEmpty()); 8925 } 8926 int gc_after = gc_count_; 8927 gc_during_apply_ += gc_after - gc_before; 8928 rounds++; 8929 } 8930 apply_success_ = true; 8931 } 8932 8933 i::Semaphore* block_; 8934 int gc_count_; 8935 int gc_during_apply_; 8936 bool apply_success_; 8937 bool gc_success_; 8938}; 8939 8940 8941// Test that nothing bad happens if we get a preemption just when we were 8942// about to do an apply(). 8943TEST(ApplyInterruption) { 8944 v8::Locker lock; 8945 v8::V8::Initialize(); 8946 v8::HandleScope scope; 8947 Local<Context> local_env; 8948 { 8949 LocalContext env; 8950 local_env = env.local(); 8951 } 8952 8953 // Local context should still be live. 8954 CHECK(!local_env.IsEmpty()); 8955 local_env->Enter(); 8956 8957 // Should complete without problems. 8958 ApplyInterruptTest().RunTest(); 8959 8960 local_env->Exit(); 8961} 8962 8963 8964// Verify that we can clone an object 8965TEST(ObjectClone) { 8966 v8::HandleScope scope; 8967 LocalContext env; 8968 8969 const char* sample = 8970 "var rv = {};" \ 8971 "rv.alpha = 'hello';" \ 8972 "rv.beta = 123;" \ 8973 "rv;"; 8974 8975 // Create an object, verify basics. 8976 Local<Value> val = CompileRun(sample); 8977 CHECK(val->IsObject()); 8978 Local<v8::Object> obj = val.As<v8::Object>(); 8979 obj->Set(v8_str("gamma"), v8_str("cloneme")); 8980 8981 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha"))); 8982 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 8983 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma"))); 8984 8985 // Clone it. 8986 Local<v8::Object> clone = obj->Clone(); 8987 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha"))); 8988 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta"))); 8989 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma"))); 8990 8991 // Set a property on the clone, verify each object. 8992 clone->Set(v8_str("beta"), v8::Integer::New(456)); 8993 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 8994 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta"))); 8995} 8996 8997 8998class AsciiVectorResource : public v8::String::ExternalAsciiStringResource { 8999 public: 9000 explicit AsciiVectorResource(i::Vector<const char> vector) 9001 : data_(vector) {} 9002 virtual ~AsciiVectorResource() {} 9003 virtual size_t length() const { return data_.length(); } 9004 virtual const char* data() const { return data_.start(); } 9005 private: 9006 i::Vector<const char> data_; 9007}; 9008 9009 9010class UC16VectorResource : public v8::String::ExternalStringResource { 9011 public: 9012 explicit UC16VectorResource(i::Vector<const i::uc16> vector) 9013 : data_(vector) {} 9014 virtual ~UC16VectorResource() {} 9015 virtual size_t length() const { return data_.length(); } 9016 virtual const i::uc16* data() const { return data_.start(); } 9017 private: 9018 i::Vector<const i::uc16> data_; 9019}; 9020 9021 9022static void MorphAString(i::String* string, 9023 AsciiVectorResource* ascii_resource, 9024 UC16VectorResource* uc16_resource) { 9025 CHECK(i::StringShape(string).IsExternal()); 9026 if (string->IsAsciiRepresentation()) { 9027 // Check old map is not symbol or long. 9028 CHECK(string->map() == i::Heap::external_ascii_string_map()); 9029 // Morph external string to be TwoByte string. 9030 string->set_map(i::Heap::external_string_map()); 9031 i::ExternalTwoByteString* morphed = 9032 i::ExternalTwoByteString::cast(string); 9033 morphed->set_resource(uc16_resource); 9034 } else { 9035 // Check old map is not symbol or long. 9036 CHECK(string->map() == i::Heap::external_string_map()); 9037 // Morph external string to be ASCII string. 9038 string->set_map(i::Heap::external_ascii_string_map()); 9039 i::ExternalAsciiString* morphed = 9040 i::ExternalAsciiString::cast(string); 9041 morphed->set_resource(ascii_resource); 9042 } 9043} 9044 9045 9046// Test that we can still flatten a string if the components it is built up 9047// from have been turned into 16 bit strings in the mean time. 9048THREADED_TEST(MorphCompositeStringTest) { 9049 const char* c_string = "Now is the time for all good men" 9050 " to come to the aid of the party"; 9051 uint16_t* two_byte_string = AsciiToTwoByteString(c_string); 9052 { 9053 v8::HandleScope scope; 9054 LocalContext env; 9055 AsciiVectorResource ascii_resource( 9056 i::Vector<const char>(c_string, i::StrLength(c_string))); 9057 UC16VectorResource uc16_resource( 9058 i::Vector<const uint16_t>(two_byte_string, 9059 i::StrLength(c_string))); 9060 9061 Local<String> lhs(v8::Utils::ToLocal( 9062 i::Factory::NewExternalStringFromAscii(&ascii_resource))); 9063 Local<String> rhs(v8::Utils::ToLocal( 9064 i::Factory::NewExternalStringFromAscii(&ascii_resource))); 9065 9066 env->Global()->Set(v8_str("lhs"), lhs); 9067 env->Global()->Set(v8_str("rhs"), rhs); 9068 9069 CompileRun( 9070 "var cons = lhs + rhs;" 9071 "var slice = lhs.substring(1, lhs.length - 1);" 9072 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);"); 9073 9074 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource); 9075 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource); 9076 9077 // Now do some stuff to make sure the strings are flattened, etc. 9078 CompileRun( 9079 "/[^a-z]/.test(cons);" 9080 "/[^a-z]/.test(slice);" 9081 "/[^a-z]/.test(slice_on_cons);"); 9082 const char* expected_cons = 9083 "Now is the time for all good men to come to the aid of the party" 9084 "Now is the time for all good men to come to the aid of the party"; 9085 const char* expected_slice = 9086 "ow is the time for all good men to come to the aid of the part"; 9087 const char* expected_slice_on_cons = 9088 "ow is the time for all good men to come to the aid of the party" 9089 "Now is the time for all good men to come to the aid of the part"; 9090 CHECK_EQ(String::New(expected_cons), 9091 env->Global()->Get(v8_str("cons"))); 9092 CHECK_EQ(String::New(expected_slice), 9093 env->Global()->Get(v8_str("slice"))); 9094 CHECK_EQ(String::New(expected_slice_on_cons), 9095 env->Global()->Get(v8_str("slice_on_cons"))); 9096 } 9097 i::DeleteArray(two_byte_string); 9098} 9099 9100 9101TEST(CompileExternalTwoByteSource) { 9102 v8::HandleScope scope; 9103 LocalContext context; 9104 9105 // This is a very short list of sources, which currently is to check for a 9106 // regression caused by r2703. 9107 const char* ascii_sources[] = { 9108 "0.5", 9109 "-0.5", // This mainly testes PushBack in the Scanner. 9110 "--0.5", // This mainly testes PushBack in the Scanner. 9111 NULL 9112 }; 9113 9114 // Compile the sources as external two byte strings. 9115 for (int i = 0; ascii_sources[i] != NULL; i++) { 9116 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]); 9117 UC16VectorResource uc16_resource( 9118 i::Vector<const uint16_t>(two_byte_string, 9119 i::StrLength(ascii_sources[i]))); 9120 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource); 9121 v8::Script::Compile(source); 9122 i::DeleteArray(two_byte_string); 9123 } 9124} 9125 9126 9127class RegExpStringModificationTest { 9128 public: 9129 RegExpStringModificationTest() 9130 : block_(i::OS::CreateSemaphore(0)), 9131 morphs_(0), 9132 morphs_during_regexp_(0), 9133 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)), 9134 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {} 9135 ~RegExpStringModificationTest() { delete block_; } 9136 void RunTest() { 9137 regexp_success_ = false; 9138 morph_success_ = false; 9139 9140 // Initialize the contents of two_byte_content_ to be a uc16 representation 9141 // of "aaaaaaaaaaaaaab". 9142 for (int i = 0; i < 14; i++) { 9143 two_byte_content_[i] = 'a'; 9144 } 9145 two_byte_content_[14] = 'b'; 9146 9147 // Create the input string for the regexp - the one we are going to change 9148 // properties of. 9149 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_); 9150 9151 // Inject the input as a global variable. 9152 i::Handle<i::String> input_name = 9153 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5)); 9154 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE); 9155 9156 9157 MorphThread morph_thread(this); 9158 morph_thread.Start(); 9159 v8::Locker::StartPreemption(1); 9160 LongRunningRegExp(); 9161 { 9162 v8::Unlocker unlock; 9163 morph_thread.Join(); 9164 } 9165 v8::Locker::StopPreemption(); 9166 CHECK(regexp_success_); 9167 CHECK(morph_success_); 9168 } 9169 private: 9170 9171 // Number of string modifications required. 9172 static const int kRequiredModifications = 5; 9173 static const int kMaxModifications = 100; 9174 9175 class MorphThread : public i::Thread { 9176 public: 9177 explicit MorphThread(RegExpStringModificationTest* test) 9178 : test_(test) {} 9179 virtual void Run() { 9180 test_->MorphString(); 9181 } 9182 private: 9183 RegExpStringModificationTest* test_; 9184 }; 9185 9186 void MorphString() { 9187 block_->Wait(); 9188 while (morphs_during_regexp_ < kRequiredModifications && 9189 morphs_ < kMaxModifications) { 9190 { 9191 v8::Locker lock; 9192 // Swap string between ascii and two-byte representation. 9193 i::String* string = *input_; 9194 MorphAString(string, &ascii_resource_, &uc16_resource_); 9195 morphs_++; 9196 } 9197 i::OS::Sleep(1); 9198 } 9199 morph_success_ = true; 9200 } 9201 9202 void LongRunningRegExp() { 9203 block_->Signal(); // Enable morphing thread on next preemption. 9204 while (morphs_during_regexp_ < kRequiredModifications && 9205 morphs_ < kMaxModifications) { 9206 int morphs_before = morphs_; 9207 { 9208 v8::HandleScope scope; 9209 // Match 15-30 "a"'s against 14 and a "b". 9210 const char* c_source = 9211 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 9212 ".exec(input) === null"; 9213 Local<String> source = String::New(c_source); 9214 Local<Script> script = Script::Compile(source); 9215 Local<Value> result = script->Run(); 9216 CHECK(result->IsTrue()); 9217 } 9218 int morphs_after = morphs_; 9219 morphs_during_regexp_ += morphs_after - morphs_before; 9220 } 9221 regexp_success_ = true; 9222 } 9223 9224 i::uc16 two_byte_content_[15]; 9225 i::Semaphore* block_; 9226 int morphs_; 9227 int morphs_during_regexp_; 9228 bool regexp_success_; 9229 bool morph_success_; 9230 i::Handle<i::String> input_; 9231 AsciiVectorResource ascii_resource_; 9232 UC16VectorResource uc16_resource_; 9233}; 9234 9235 9236// Test that a regular expression execution can be interrupted and 9237// the string changed without failing. 9238TEST(RegExpStringModification) { 9239 v8::Locker lock; 9240 v8::V8::Initialize(); 9241 v8::HandleScope scope; 9242 Local<Context> local_env; 9243 { 9244 LocalContext env; 9245 local_env = env.local(); 9246 } 9247 9248 // Local context should still be live. 9249 CHECK(!local_env.IsEmpty()); 9250 local_env->Enter(); 9251 9252 // Should complete without problems. 9253 RegExpStringModificationTest().RunTest(); 9254 9255 local_env->Exit(); 9256} 9257 9258 9259// Test that we can set a property on the global object even if there 9260// is a read-only property in the prototype chain. 9261TEST(ReadOnlyPropertyInGlobalProto) { 9262 v8::HandleScope scope; 9263 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9264 LocalContext context(0, templ); 9265 v8::Handle<v8::Object> global = context->Global(); 9266 v8::Handle<v8::Object> global_proto = 9267 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__"))); 9268 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly); 9269 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly); 9270 // Check without 'eval' or 'with'. 9271 v8::Handle<v8::Value> res = 9272 CompileRun("function f() { x = 42; return x; }; f()"); 9273 // Check with 'eval'. 9274 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()"); 9275 CHECK_EQ(v8::Integer::New(42), res); 9276 // Check with 'with'. 9277 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()"); 9278 CHECK_EQ(v8::Integer::New(42), res); 9279} 9280 9281static int force_set_set_count = 0; 9282static int force_set_get_count = 0; 9283bool pass_on_get = false; 9284 9285static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name, 9286 const v8::AccessorInfo& info) { 9287 force_set_get_count++; 9288 if (pass_on_get) { 9289 return v8::Handle<v8::Value>(); 9290 } else { 9291 return v8::Int32::New(3); 9292 } 9293} 9294 9295static void ForceSetSetter(v8::Local<v8::String> name, 9296 v8::Local<v8::Value> value, 9297 const v8::AccessorInfo& info) { 9298 force_set_set_count++; 9299} 9300 9301static v8::Handle<v8::Value> ForceSetInterceptSetter( 9302 v8::Local<v8::String> name, 9303 v8::Local<v8::Value> value, 9304 const v8::AccessorInfo& info) { 9305 force_set_set_count++; 9306 return v8::Undefined(); 9307} 9308 9309TEST(ForceSet) { 9310 force_set_get_count = 0; 9311 force_set_set_count = 0; 9312 pass_on_get = false; 9313 9314 v8::HandleScope scope; 9315 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9316 v8::Handle<v8::String> access_property = v8::String::New("a"); 9317 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter); 9318 LocalContext context(NULL, templ); 9319 v8::Handle<v8::Object> global = context->Global(); 9320 9321 // Ordinary properties 9322 v8::Handle<v8::String> simple_property = v8::String::New("p"); 9323 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly); 9324 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9325 // This should fail because the property is read-only 9326 global->Set(simple_property, v8::Int32::New(5)); 9327 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9328 // This should succeed even though the property is read-only 9329 global->ForceSet(simple_property, v8::Int32::New(6)); 9330 CHECK_EQ(6, global->Get(simple_property)->Int32Value()); 9331 9332 // Accessors 9333 CHECK_EQ(0, force_set_set_count); 9334 CHECK_EQ(0, force_set_get_count); 9335 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 9336 // CHECK_EQ the property shouldn't override it, just call the setter 9337 // which in this case does nothing. 9338 global->Set(access_property, v8::Int32::New(7)); 9339 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 9340 CHECK_EQ(1, force_set_set_count); 9341 CHECK_EQ(2, force_set_get_count); 9342 // Forcing the property to be set should override the accessor without 9343 // calling it 9344 global->ForceSet(access_property, v8::Int32::New(8)); 9345 CHECK_EQ(8, global->Get(access_property)->Int32Value()); 9346 CHECK_EQ(1, force_set_set_count); 9347 CHECK_EQ(2, force_set_get_count); 9348} 9349 9350TEST(ForceSetWithInterceptor) { 9351 force_set_get_count = 0; 9352 force_set_set_count = 0; 9353 pass_on_get = false; 9354 9355 v8::HandleScope scope; 9356 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9357 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter); 9358 LocalContext context(NULL, templ); 9359 v8::Handle<v8::Object> global = context->Global(); 9360 9361 v8::Handle<v8::String> some_property = v8::String::New("a"); 9362 CHECK_EQ(0, force_set_set_count); 9363 CHECK_EQ(0, force_set_get_count); 9364 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 9365 // Setting the property shouldn't override it, just call the setter 9366 // which in this case does nothing. 9367 global->Set(some_property, v8::Int32::New(7)); 9368 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 9369 CHECK_EQ(1, force_set_set_count); 9370 CHECK_EQ(2, force_set_get_count); 9371 // Getting the property when the interceptor returns an empty handle 9372 // should yield undefined, since the property isn't present on the 9373 // object itself yet. 9374 pass_on_get = true; 9375 CHECK(global->Get(some_property)->IsUndefined()); 9376 CHECK_EQ(1, force_set_set_count); 9377 CHECK_EQ(3, force_set_get_count); 9378 // Forcing the property to be set should cause the value to be 9379 // set locally without calling the interceptor. 9380 global->ForceSet(some_property, v8::Int32::New(8)); 9381 CHECK_EQ(8, global->Get(some_property)->Int32Value()); 9382 CHECK_EQ(1, force_set_set_count); 9383 CHECK_EQ(4, force_set_get_count); 9384 // Reenabling the interceptor should cause it to take precedence over 9385 // the property 9386 pass_on_get = false; 9387 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 9388 CHECK_EQ(1, force_set_set_count); 9389 CHECK_EQ(5, force_set_get_count); 9390 // The interceptor should also work for other properties 9391 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value()); 9392 CHECK_EQ(1, force_set_set_count); 9393 CHECK_EQ(6, force_set_get_count); 9394} 9395 9396 9397THREADED_TEST(ForceDelete) { 9398 v8::HandleScope scope; 9399 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9400 LocalContext context(NULL, templ); 9401 v8::Handle<v8::Object> global = context->Global(); 9402 9403 // Ordinary properties 9404 v8::Handle<v8::String> simple_property = v8::String::New("p"); 9405 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete); 9406 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9407 // This should fail because the property is dont-delete. 9408 CHECK(!global->Delete(simple_property)); 9409 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9410 // This should succeed even though the property is dont-delete. 9411 CHECK(global->ForceDelete(simple_property)); 9412 CHECK(global->Get(simple_property)->IsUndefined()); 9413} 9414 9415 9416static int force_delete_interceptor_count = 0; 9417static bool pass_on_delete = false; 9418 9419 9420static v8::Handle<v8::Boolean> ForceDeleteDeleter( 9421 v8::Local<v8::String> name, 9422 const v8::AccessorInfo& info) { 9423 force_delete_interceptor_count++; 9424 if (pass_on_delete) { 9425 return v8::Handle<v8::Boolean>(); 9426 } else { 9427 return v8::True(); 9428 } 9429} 9430 9431 9432THREADED_TEST(ForceDeleteWithInterceptor) { 9433 force_delete_interceptor_count = 0; 9434 pass_on_delete = false; 9435 9436 v8::HandleScope scope; 9437 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9438 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter); 9439 LocalContext context(NULL, templ); 9440 v8::Handle<v8::Object> global = context->Global(); 9441 9442 v8::Handle<v8::String> some_property = v8::String::New("a"); 9443 global->Set(some_property, v8::Integer::New(42), v8::DontDelete); 9444 9445 // Deleting a property should get intercepted and nothing should 9446 // happen. 9447 CHECK_EQ(0, force_delete_interceptor_count); 9448 CHECK(global->Delete(some_property)); 9449 CHECK_EQ(1, force_delete_interceptor_count); 9450 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 9451 // Deleting the property when the interceptor returns an empty 9452 // handle should not delete the property since it is DontDelete. 9453 pass_on_delete = true; 9454 CHECK(!global->Delete(some_property)); 9455 CHECK_EQ(2, force_delete_interceptor_count); 9456 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 9457 // Forcing the property to be deleted should delete the value 9458 // without calling the interceptor. 9459 CHECK(global->ForceDelete(some_property)); 9460 CHECK(global->Get(some_property)->IsUndefined()); 9461 CHECK_EQ(2, force_delete_interceptor_count); 9462} 9463 9464 9465// Make sure that forcing a delete invalidates any IC stubs, so we 9466// don't read the hole value. 9467THREADED_TEST(ForceDeleteIC) { 9468 v8::HandleScope scope; 9469 LocalContext context; 9470 // Create a DontDelete variable on the global object. 9471 CompileRun("this.__proto__ = { foo: 'horse' };" 9472 "var foo = 'fish';" 9473 "function f() { return foo.length; }"); 9474 // Initialize the IC for foo in f. 9475 CompileRun("for (var i = 0; i < 4; i++) f();"); 9476 // Make sure the value of foo is correct before the deletion. 9477 CHECK_EQ(4, CompileRun("f()")->Int32Value()); 9478 // Force the deletion of foo. 9479 CHECK(context->Global()->ForceDelete(v8_str("foo"))); 9480 // Make sure the value for foo is read from the prototype, and that 9481 // we don't get in trouble with reading the deleted cell value 9482 // sentinel. 9483 CHECK_EQ(5, CompileRun("f()")->Int32Value()); 9484} 9485 9486 9487v8::Persistent<Context> calling_context0; 9488v8::Persistent<Context> calling_context1; 9489v8::Persistent<Context> calling_context2; 9490 9491 9492// Check that the call to the callback is initiated in 9493// calling_context2, the directly calling context is calling_context1 9494// and the callback itself is in calling_context0. 9495static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) { 9496 ApiTestFuzzer::Fuzz(); 9497 CHECK(Context::GetCurrent() == calling_context0); 9498 CHECK(Context::GetCalling() == calling_context1); 9499 CHECK(Context::GetEntered() == calling_context2); 9500 return v8::Integer::New(42); 9501} 9502 9503 9504THREADED_TEST(GetCallingContext) { 9505 v8::HandleScope scope; 9506 9507 calling_context0 = Context::New(); 9508 calling_context1 = Context::New(); 9509 calling_context2 = Context::New(); 9510 9511 // Allow cross-domain access. 9512 Local<String> token = v8_str("<security token>"); 9513 calling_context0->SetSecurityToken(token); 9514 calling_context1->SetSecurityToken(token); 9515 calling_context2->SetSecurityToken(token); 9516 9517 // Create an object with a C++ callback in context0. 9518 calling_context0->Enter(); 9519 Local<v8::FunctionTemplate> callback_templ = 9520 v8::FunctionTemplate::New(GetCallingContextCallback); 9521 calling_context0->Global()->Set(v8_str("callback"), 9522 callback_templ->GetFunction()); 9523 calling_context0->Exit(); 9524 9525 // Expose context0 in context1 and setup a function that calls the 9526 // callback function. 9527 calling_context1->Enter(); 9528 calling_context1->Global()->Set(v8_str("context0"), 9529 calling_context0->Global()); 9530 CompileRun("function f() { context0.callback() }"); 9531 calling_context1->Exit(); 9532 9533 // Expose context1 in context2 and call the callback function in 9534 // context0 indirectly through f in context1. 9535 calling_context2->Enter(); 9536 calling_context2->Global()->Set(v8_str("context1"), 9537 calling_context1->Global()); 9538 CompileRun("context1.f()"); 9539 calling_context2->Exit(); 9540 9541 // Dispose the contexts to allow them to be garbage collected. 9542 calling_context0.Dispose(); 9543 calling_context1.Dispose(); 9544 calling_context2.Dispose(); 9545 calling_context0.Clear(); 9546 calling_context1.Clear(); 9547 calling_context2.Clear(); 9548} 9549 9550 9551// Check that a variable declaration with no explicit initialization 9552// value does not shadow an existing property in the prototype chain. 9553// 9554// This is consistent with Firefox and Safari. 9555// 9556// See http://crbug.com/12548. 9557THREADED_TEST(InitGlobalVarInProtoChain) { 9558 v8::HandleScope scope; 9559 LocalContext context; 9560 // Introduce a variable in the prototype chain. 9561 CompileRun("__proto__.x = 42"); 9562 v8::Handle<v8::Value> result = CompileRun("var x; x"); 9563 CHECK(!result->IsUndefined()); 9564 CHECK_EQ(42, result->Int32Value()); 9565} 9566 9567 9568// Regression test for issue 398. 9569// If a function is added to an object, creating a constant function 9570// field, and the result is cloned, replacing the constant function on the 9571// original should not affect the clone. 9572// See http://code.google.com/p/v8/issues/detail?id=398 9573THREADED_TEST(ReplaceConstantFunction) { 9574 v8::HandleScope scope; 9575 LocalContext context; 9576 v8::Handle<v8::Object> obj = v8::Object::New(); 9577 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 9578 v8::Handle<v8::String> foo_string = v8::String::New("foo"); 9579 obj->Set(foo_string, func_templ->GetFunction()); 9580 v8::Handle<v8::Object> obj_clone = obj->Clone(); 9581 obj_clone->Set(foo_string, v8::String::New("Hello")); 9582 CHECK(!obj->Get(foo_string)->IsUndefined()); 9583} 9584 9585 9586// Regression test for http://crbug.com/16276. 9587THREADED_TEST(Regress16276) { 9588 v8::HandleScope scope; 9589 LocalContext context; 9590 // Force the IC in f to be a dictionary load IC. 9591 CompileRun("function f(obj) { return obj.x; }\n" 9592 "var obj = { x: { foo: 42 }, y: 87 };\n" 9593 "var x = obj.x;\n" 9594 "delete obj.y;\n" 9595 "for (var i = 0; i < 5; i++) f(obj);"); 9596 // Detach the global object to make 'this' refer directly to the 9597 // global object (not the proxy), and make sure that the dictionary 9598 // load IC doesn't mess up loading directly from the global object. 9599 context->DetachGlobal(); 9600 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value()); 9601} 9602 9603 9604THREADED_TEST(PixelArray) { 9605 v8::HandleScope scope; 9606 LocalContext context; 9607 const int kElementCount = 260; 9608 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); 9609 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount, 9610 pixel_data); 9611 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9612 for (int i = 0; i < kElementCount; i++) { 9613 pixels->set(i, i % 256); 9614 } 9615 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9616 for (int i = 0; i < kElementCount; i++) { 9617 CHECK_EQ(i % 256, pixels->get(i)); 9618 CHECK_EQ(i % 256, pixel_data[i]); 9619 } 9620 9621 v8::Handle<v8::Object> obj = v8::Object::New(); 9622 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 9623 // Set the elements to be the pixels. 9624 // jsobj->set_elements(*pixels); 9625 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount); 9626 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value()); 9627 obj->Set(v8_str("field"), v8::Int32::New(1503)); 9628 context->Global()->Set(v8_str("pixels"), obj); 9629 v8::Handle<v8::Value> result = CompileRun("pixels.field"); 9630 CHECK_EQ(1503, result->Int32Value()); 9631 result = CompileRun("pixels[1]"); 9632 CHECK_EQ(1, result->Int32Value()); 9633 9634 result = CompileRun("var sum = 0;" 9635 "for (var i = 0; i < 8; i++) {" 9636 " sum += pixels[i] = pixels[i] = -i;" 9637 "}" 9638 "sum;"); 9639 CHECK_EQ(-28, result->Int32Value()); 9640 9641 result = CompileRun("var sum = 0;" 9642 "for (var i = 0; i < 8; i++) {" 9643 " sum += pixels[i] = pixels[i] = 0;" 9644 "}" 9645 "sum;"); 9646 CHECK_EQ(0, result->Int32Value()); 9647 9648 result = CompileRun("var sum = 0;" 9649 "for (var i = 0; i < 8; i++) {" 9650 " sum += pixels[i] = pixels[i] = 255;" 9651 "}" 9652 "sum;"); 9653 CHECK_EQ(8 * 255, result->Int32Value()); 9654 9655 result = CompileRun("var sum = 0;" 9656 "for (var i = 0; i < 8; i++) {" 9657 " sum += pixels[i] = pixels[i] = 256 + i;" 9658 "}" 9659 "sum;"); 9660 CHECK_EQ(2076, result->Int32Value()); 9661 9662 result = CompileRun("var sum = 0;" 9663 "for (var i = 0; i < 8; i++) {" 9664 " sum += pixels[i] = pixels[i] = i;" 9665 "}" 9666 "sum;"); 9667 CHECK_EQ(28, result->Int32Value()); 9668 9669 result = CompileRun("var sum = 0;" 9670 "for (var i = 0; i < 8; i++) {" 9671 " sum += pixels[i];" 9672 "}" 9673 "sum;"); 9674 CHECK_EQ(28, result->Int32Value()); 9675 9676 i::Handle<i::Smi> value(i::Smi::FromInt(2)); 9677 i::SetElement(jsobj, 1, value); 9678 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value()); 9679 *value.location() = i::Smi::FromInt(256); 9680 i::SetElement(jsobj, 1, value); 9681 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value()); 9682 *value.location() = i::Smi::FromInt(-1); 9683 i::SetElement(jsobj, 1, value); 9684 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value()); 9685 9686 result = CompileRun("for (var i = 0; i < 8; i++) {" 9687 " pixels[i] = (i * 65) - 109;" 9688 "}" 9689 "pixels[1] + pixels[6];"); 9690 CHECK_EQ(255, result->Int32Value()); 9691 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value()); 9692 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value()); 9693 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value()); 9694 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value()); 9695 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value()); 9696 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value()); 9697 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value()); 9698 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value()); 9699 result = CompileRun("var sum = 0;" 9700 "for (var i = 0; i < 8; i++) {" 9701 " sum += pixels[i];" 9702 "}" 9703 "sum;"); 9704 CHECK_EQ(984, result->Int32Value()); 9705 9706 result = CompileRun("for (var i = 0; i < 8; i++) {" 9707 " pixels[i] = (i * 1.1);" 9708 "}" 9709 "pixels[1] + pixels[6];"); 9710 CHECK_EQ(8, result->Int32Value()); 9711 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value()); 9712 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value()); 9713 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value()); 9714 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value()); 9715 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value()); 9716 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value()); 9717 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value()); 9718 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value()); 9719 9720 result = CompileRun("for (var i = 0; i < 8; i++) {" 9721 " pixels[7] = undefined;" 9722 "}" 9723 "pixels[7];"); 9724 CHECK_EQ(0, result->Int32Value()); 9725 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value()); 9726 9727 result = CompileRun("for (var i = 0; i < 8; i++) {" 9728 " pixels[6] = '2.3';" 9729 "}" 9730 "pixels[6];"); 9731 CHECK_EQ(2, result->Int32Value()); 9732 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value()); 9733 9734 result = CompileRun("for (var i = 0; i < 8; i++) {" 9735 " pixels[5] = NaN;" 9736 "}" 9737 "pixels[5];"); 9738 CHECK_EQ(0, result->Int32Value()); 9739 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 9740 9741 result = CompileRun("for (var i = 0; i < 8; i++) {" 9742 " pixels[8] = Infinity;" 9743 "}" 9744 "pixels[8];"); 9745 CHECK_EQ(255, result->Int32Value()); 9746 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value()); 9747 9748 result = CompileRun("for (var i = 0; i < 8; i++) {" 9749 " pixels[9] = -Infinity;" 9750 "}" 9751 "pixels[9];"); 9752 CHECK_EQ(0, result->Int32Value()); 9753 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value()); 9754 9755 result = CompileRun("pixels[3] = 33;" 9756 "delete pixels[3];" 9757 "pixels[3];"); 9758 CHECK_EQ(33, result->Int32Value()); 9759 9760 result = CompileRun("pixels[0] = 10; pixels[1] = 11;" 9761 "pixels[2] = 12; pixels[3] = 13;" 9762 "pixels.__defineGetter__('2'," 9763 "function() { return 120; });" 9764 "pixels[2];"); 9765 CHECK_EQ(12, result->Int32Value()); 9766 9767 result = CompileRun("var js_array = new Array(40);" 9768 "js_array[0] = 77;" 9769 "js_array;"); 9770 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 9771 9772 result = CompileRun("pixels[1] = 23;" 9773 "pixels.__proto__ = [];" 9774 "js_array.__proto__ = pixels;" 9775 "js_array.concat(pixels);"); 9776 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 9777 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 9778 9779 result = CompileRun("pixels[1] = 23;"); 9780 CHECK_EQ(23, result->Int32Value()); 9781 9782 // Test for index greater than 255. Regression test for: 9783 // http://code.google.com/p/chromium/issues/detail?id=26337. 9784 result = CompileRun("pixels[256] = 255;"); 9785 CHECK_EQ(255, result->Int32Value()); 9786 result = CompileRun("var i = 0;" 9787 "for (var j = 0; j < 8; j++) { i = pixels[256]; }" 9788 "i"); 9789 CHECK_EQ(255, result->Int32Value()); 9790 9791 free(pixel_data); 9792} 9793 9794 9795THREADED_TEST(PixelArrayInfo) { 9796 v8::HandleScope scope; 9797 LocalContext context; 9798 for (int size = 0; size < 100; size += 10) { 9799 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size)); 9800 v8::Handle<v8::Object> obj = v8::Object::New(); 9801 obj->SetIndexedPropertiesToPixelData(pixel_data, size); 9802 CHECK(obj->HasIndexedPropertiesInPixelData()); 9803 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData()); 9804 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength()); 9805 free(pixel_data); 9806 } 9807} 9808 9809 9810static int ExternalArrayElementSize(v8::ExternalArrayType array_type) { 9811 switch (array_type) { 9812 case v8::kExternalByteArray: 9813 case v8::kExternalUnsignedByteArray: 9814 return 1; 9815 break; 9816 case v8::kExternalShortArray: 9817 case v8::kExternalUnsignedShortArray: 9818 return 2; 9819 break; 9820 case v8::kExternalIntArray: 9821 case v8::kExternalUnsignedIntArray: 9822 case v8::kExternalFloatArray: 9823 return 4; 9824 break; 9825 default: 9826 UNREACHABLE(); 9827 return -1; 9828 } 9829 UNREACHABLE(); 9830 return -1; 9831} 9832 9833 9834template <class ExternalArrayClass, class ElementType> 9835static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, 9836 int64_t low, 9837 int64_t high) { 9838 v8::HandleScope scope; 9839 LocalContext context; 9840 const int kElementCount = 40; 9841 int element_size = ExternalArrayElementSize(array_type); 9842 ElementType* array_data = 9843 static_cast<ElementType*>(malloc(kElementCount * element_size)); 9844 i::Handle<ExternalArrayClass> array = 9845 i::Handle<ExternalArrayClass>::cast( 9846 i::Factory::NewExternalArray(kElementCount, array_type, array_data)); 9847 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9848 for (int i = 0; i < kElementCount; i++) { 9849 array->set(i, static_cast<ElementType>(i)); 9850 } 9851 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9852 for (int i = 0; i < kElementCount; i++) { 9853 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i))); 9854 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i])); 9855 } 9856 9857 v8::Handle<v8::Object> obj = v8::Object::New(); 9858 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 9859 // Set the elements to be the external array. 9860 obj->SetIndexedPropertiesToExternalArrayData(array_data, 9861 array_type, 9862 kElementCount); 9863 CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number())); 9864 obj->Set(v8_str("field"), v8::Int32::New(1503)); 9865 context->Global()->Set(v8_str("ext_array"), obj); 9866 v8::Handle<v8::Value> result = CompileRun("ext_array.field"); 9867 CHECK_EQ(1503, result->Int32Value()); 9868 result = CompileRun("ext_array[1]"); 9869 CHECK_EQ(1, result->Int32Value()); 9870 9871 // Check pass through of assigned smis 9872 result = CompileRun("var sum = 0;" 9873 "for (var i = 0; i < 8; i++) {" 9874 " sum += ext_array[i] = ext_array[i] = -i;" 9875 "}" 9876 "sum;"); 9877 CHECK_EQ(-28, result->Int32Value()); 9878 9879 // Check assigned smis 9880 result = CompileRun("for (var i = 0; i < 8; i++) {" 9881 " ext_array[i] = i;" 9882 "}" 9883 "var sum = 0;" 9884 "for (var i = 0; i < 8; i++) {" 9885 " sum += ext_array[i];" 9886 "}" 9887 "sum;"); 9888 CHECK_EQ(28, result->Int32Value()); 9889 9890 // Check assigned smis in reverse order 9891 result = CompileRun("for (var i = 8; --i >= 0; ) {" 9892 " ext_array[i] = i;" 9893 "}" 9894 "var sum = 0;" 9895 "for (var i = 0; i < 8; i++) {" 9896 " sum += ext_array[i];" 9897 "}" 9898 "sum;"); 9899 CHECK_EQ(28, result->Int32Value()); 9900 9901 // Check pass through of assigned HeapNumbers 9902 result = CompileRun("var sum = 0;" 9903 "for (var i = 0; i < 16; i+=2) {" 9904 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);" 9905 "}" 9906 "sum;"); 9907 CHECK_EQ(-28, result->Int32Value()); 9908 9909 // Check assigned HeapNumbers 9910 result = CompileRun("for (var i = 0; i < 16; i+=2) {" 9911 " ext_array[i] = (i * 0.5);" 9912 "}" 9913 "var sum = 0;" 9914 "for (var i = 0; i < 16; i+=2) {" 9915 " sum += ext_array[i];" 9916 "}" 9917 "sum;"); 9918 CHECK_EQ(28, result->Int32Value()); 9919 9920 // Check assigned HeapNumbers in reverse order 9921 result = CompileRun("for (var i = 14; i >= 0; i-=2) {" 9922 " ext_array[i] = (i * 0.5);" 9923 "}" 9924 "var sum = 0;" 9925 "for (var i = 0; i < 16; i+=2) {" 9926 " sum += ext_array[i];" 9927 "}" 9928 "sum;"); 9929 CHECK_EQ(28, result->Int32Value()); 9930 9931 i::ScopedVector<char> test_buf(1024); 9932 9933 // Check legal boundary conditions. 9934 // The repeated loads and stores ensure the ICs are exercised. 9935 const char* boundary_program = 9936 "var res = 0;" 9937 "for (var i = 0; i < 16; i++) {" 9938 " ext_array[i] = %lld;" 9939 " if (i > 8) {" 9940 " res = ext_array[i];" 9941 " }" 9942 "}" 9943 "res;"; 9944 i::OS::SNPrintF(test_buf, 9945 boundary_program, 9946 low); 9947 result = CompileRun(test_buf.start()); 9948 CHECK_EQ(low, result->IntegerValue()); 9949 9950 i::OS::SNPrintF(test_buf, 9951 boundary_program, 9952 high); 9953 result = CompileRun(test_buf.start()); 9954 CHECK_EQ(high, result->IntegerValue()); 9955 9956 // Check misprediction of type in IC. 9957 result = CompileRun("var tmp_array = ext_array;" 9958 "var sum = 0;" 9959 "for (var i = 0; i < 8; i++) {" 9960 " tmp_array[i] = i;" 9961 " sum += tmp_array[i];" 9962 " if (i == 4) {" 9963 " tmp_array = {};" 9964 " }" 9965 "}" 9966 "sum;"); 9967 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9968 CHECK_EQ(28, result->Int32Value()); 9969 9970 // Make sure out-of-range loads do not throw. 9971 i::OS::SNPrintF(test_buf, 9972 "var caught_exception = false;" 9973 "try {" 9974 " ext_array[%d];" 9975 "} catch (e) {" 9976 " caught_exception = true;" 9977 "}" 9978 "caught_exception;", 9979 kElementCount); 9980 result = CompileRun(test_buf.start()); 9981 CHECK_EQ(false, result->BooleanValue()); 9982 9983 // Make sure out-of-range stores do not throw. 9984 i::OS::SNPrintF(test_buf, 9985 "var caught_exception = false;" 9986 "try {" 9987 " ext_array[%d] = 1;" 9988 "} catch (e) {" 9989 " caught_exception = true;" 9990 "}" 9991 "caught_exception;", 9992 kElementCount); 9993 result = CompileRun(test_buf.start()); 9994 CHECK_EQ(false, result->BooleanValue()); 9995 9996 // Check other boundary conditions, values and operations. 9997 result = CompileRun("for (var i = 0; i < 8; i++) {" 9998 " ext_array[7] = undefined;" 9999 "}" 10000 "ext_array[7];"); 10001 CHECK_EQ(0, result->Int32Value()); 10002 CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number())); 10003 10004 result = CompileRun("for (var i = 0; i < 8; i++) {" 10005 " ext_array[6] = '2.3';" 10006 "}" 10007 "ext_array[6];"); 10008 CHECK_EQ(2, result->Int32Value()); 10009 CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number())); 10010 10011 if (array_type != v8::kExternalFloatArray) { 10012 // Though the specification doesn't state it, be explicit about 10013 // converting NaNs and +/-Infinity to zero. 10014 result = CompileRun("for (var i = 0; i < 8; i++) {" 10015 " ext_array[i] = 5;" 10016 "}" 10017 "for (var i = 0; i < 8; i++) {" 10018 " ext_array[i] = NaN;" 10019 "}" 10020 "ext_array[5];"); 10021 CHECK_EQ(0, result->Int32Value()); 10022 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 10023 10024 result = CompileRun("for (var i = 0; i < 8; i++) {" 10025 " ext_array[i] = 5;" 10026 "}" 10027 "for (var i = 0; i < 8; i++) {" 10028 " ext_array[i] = Infinity;" 10029 "}" 10030 "ext_array[5];"); 10031 CHECK_EQ(0, result->Int32Value()); 10032 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 10033 10034 result = CompileRun("for (var i = 0; i < 8; i++) {" 10035 " ext_array[i] = 5;" 10036 "}" 10037 "for (var i = 0; i < 8; i++) {" 10038 " ext_array[i] = -Infinity;" 10039 "}" 10040 "ext_array[5];"); 10041 CHECK_EQ(0, result->Int32Value()); 10042 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 10043 } 10044 10045 result = CompileRun("ext_array[3] = 33;" 10046 "delete ext_array[3];" 10047 "ext_array[3];"); 10048 CHECK_EQ(33, result->Int32Value()); 10049 10050 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;" 10051 "ext_array[2] = 12; ext_array[3] = 13;" 10052 "ext_array.__defineGetter__('2'," 10053 "function() { return 120; });" 10054 "ext_array[2];"); 10055 CHECK_EQ(12, result->Int32Value()); 10056 10057 result = CompileRun("var js_array = new Array(40);" 10058 "js_array[0] = 77;" 10059 "js_array;"); 10060 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 10061 10062 result = CompileRun("ext_array[1] = 23;" 10063 "ext_array.__proto__ = [];" 10064 "js_array.__proto__ = ext_array;" 10065 "js_array.concat(ext_array);"); 10066 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 10067 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 10068 10069 result = CompileRun("ext_array[1] = 23;"); 10070 CHECK_EQ(23, result->Int32Value()); 10071 10072 // Test more complex manipulations which cause eax to contain values 10073 // that won't be completely overwritten by loads from the arrays. 10074 // This catches bugs in the instructions used for the KeyedLoadIC 10075 // for byte and word types. 10076 { 10077 const int kXSize = 300; 10078 const int kYSize = 300; 10079 const int kLargeElementCount = kXSize * kYSize * 4; 10080 ElementType* large_array_data = 10081 static_cast<ElementType*>(malloc(kLargeElementCount * element_size)); 10082 i::Handle<ExternalArrayClass> large_array = 10083 i::Handle<ExternalArrayClass>::cast( 10084 i::Factory::NewExternalArray(kLargeElementCount, 10085 array_type, 10086 array_data)); 10087 v8::Handle<v8::Object> large_obj = v8::Object::New(); 10088 // Set the elements to be the external array. 10089 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data, 10090 array_type, 10091 kLargeElementCount); 10092 context->Global()->Set(v8_str("large_array"), large_obj); 10093 // Initialize contents of a few rows. 10094 for (int x = 0; x < 300; x++) { 10095 int row = 0; 10096 int offset = row * 300 * 4; 10097 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 10098 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 10099 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 10100 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 10101 row = 150; 10102 offset = row * 300 * 4; 10103 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 10104 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 10105 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 10106 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 10107 row = 298; 10108 offset = row * 300 * 4; 10109 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 10110 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 10111 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 10112 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 10113 } 10114 // The goal of the code below is to make "offset" large enough 10115 // that the computation of the index (which goes into eax) has 10116 // high bits set which will not be overwritten by a byte or short 10117 // load. 10118 result = CompileRun("var failed = false;" 10119 "var offset = 0;" 10120 "for (var i = 0; i < 300; i++) {" 10121 " if (large_array[4 * i] != 127 ||" 10122 " large_array[4 * i + 1] != 0 ||" 10123 " large_array[4 * i + 2] != 0 ||" 10124 " large_array[4 * i + 3] != 127) {" 10125 " failed = true;" 10126 " }" 10127 "}" 10128 "offset = 150 * 300 * 4;" 10129 "for (var i = 0; i < 300; i++) {" 10130 " if (large_array[offset + 4 * i] != 127 ||" 10131 " large_array[offset + 4 * i + 1] != 0 ||" 10132 " large_array[offset + 4 * i + 2] != 0 ||" 10133 " large_array[offset + 4 * i + 3] != 127) {" 10134 " failed = true;" 10135 " }" 10136 "}" 10137 "offset = 298 * 300 * 4;" 10138 "for (var i = 0; i < 300; i++) {" 10139 " if (large_array[offset + 4 * i] != 127 ||" 10140 " large_array[offset + 4 * i + 1] != 0 ||" 10141 " large_array[offset + 4 * i + 2] != 0 ||" 10142 " large_array[offset + 4 * i + 3] != 127) {" 10143 " failed = true;" 10144 " }" 10145 "}" 10146 "!failed;"); 10147 CHECK_EQ(true, result->BooleanValue()); 10148 free(large_array_data); 10149 } 10150 10151 free(array_data); 10152} 10153 10154 10155THREADED_TEST(ExternalByteArray) { 10156 ExternalArrayTestHelper<i::ExternalByteArray, int8_t>( 10157 v8::kExternalByteArray, 10158 -128, 10159 127); 10160} 10161 10162 10163THREADED_TEST(ExternalUnsignedByteArray) { 10164 ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>( 10165 v8::kExternalUnsignedByteArray, 10166 0, 10167 255); 10168} 10169 10170 10171THREADED_TEST(ExternalShortArray) { 10172 ExternalArrayTestHelper<i::ExternalShortArray, int16_t>( 10173 v8::kExternalShortArray, 10174 -32768, 10175 32767); 10176} 10177 10178 10179THREADED_TEST(ExternalUnsignedShortArray) { 10180 ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>( 10181 v8::kExternalUnsignedShortArray, 10182 0, 10183 65535); 10184} 10185 10186 10187THREADED_TEST(ExternalIntArray) { 10188 ExternalArrayTestHelper<i::ExternalIntArray, int32_t>( 10189 v8::kExternalIntArray, 10190 INT_MIN, // -2147483648 10191 INT_MAX); // 2147483647 10192} 10193 10194 10195THREADED_TEST(ExternalUnsignedIntArray) { 10196 ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>( 10197 v8::kExternalUnsignedIntArray, 10198 0, 10199 UINT_MAX); // 4294967295 10200} 10201 10202 10203THREADED_TEST(ExternalFloatArray) { 10204 ExternalArrayTestHelper<i::ExternalFloatArray, float>( 10205 v8::kExternalFloatArray, 10206 -500, 10207 500); 10208} 10209 10210 10211THREADED_TEST(ExternalArrays) { 10212 TestExternalByteArray(); 10213 TestExternalUnsignedByteArray(); 10214 TestExternalShortArray(); 10215 TestExternalUnsignedShortArray(); 10216 TestExternalIntArray(); 10217 TestExternalUnsignedIntArray(); 10218 TestExternalFloatArray(); 10219} 10220 10221 10222void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) { 10223 v8::HandleScope scope; 10224 LocalContext context; 10225 for (int size = 0; size < 100; size += 10) { 10226 int element_size = ExternalArrayElementSize(array_type); 10227 void* external_data = malloc(size * element_size); 10228 v8::Handle<v8::Object> obj = v8::Object::New(); 10229 obj->SetIndexedPropertiesToExternalArrayData( 10230 external_data, array_type, size); 10231 CHECK(obj->HasIndexedPropertiesInExternalArrayData()); 10232 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData()); 10233 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType()); 10234 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength()); 10235 free(external_data); 10236 } 10237} 10238 10239 10240THREADED_TEST(ExternalArrayInfo) { 10241 ExternalArrayInfoTestHelper(v8::kExternalByteArray); 10242 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray); 10243 ExternalArrayInfoTestHelper(v8::kExternalShortArray); 10244 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray); 10245 ExternalArrayInfoTestHelper(v8::kExternalIntArray); 10246 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray); 10247 ExternalArrayInfoTestHelper(v8::kExternalFloatArray); 10248} 10249 10250 10251THREADED_TEST(ScriptContextDependence) { 10252 v8::HandleScope scope; 10253 LocalContext c1; 10254 const char *source = "foo"; 10255 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source)); 10256 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source)); 10257 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100)); 10258 CHECK_EQ(dep->Run()->Int32Value(), 100); 10259 CHECK_EQ(indep->Run()->Int32Value(), 100); 10260 LocalContext c2; 10261 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101)); 10262 CHECK_EQ(dep->Run()->Int32Value(), 100); 10263 CHECK_EQ(indep->Run()->Int32Value(), 101); 10264} 10265 10266 10267THREADED_TEST(StackTrace) { 10268 v8::HandleScope scope; 10269 LocalContext context; 10270 v8::TryCatch try_catch; 10271 const char *source = "function foo() { FAIL.FAIL; }; foo();"; 10272 v8::Handle<v8::String> src = v8::String::New(source); 10273 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test"); 10274 v8::Script::New(src, origin)->Run(); 10275 CHECK(try_catch.HasCaught()); 10276 v8::String::Utf8Value stack(try_catch.StackTrace()); 10277 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL); 10278} 10279 10280 10281// Checks that a StackFrame has certain expected values. 10282void checkStackFrame(const char* expected_script_name, 10283 const char* expected_func_name, int expected_line_number, 10284 int expected_column, bool is_eval, bool is_constructor, 10285 v8::Handle<v8::StackFrame> frame) { 10286 v8::HandleScope scope; 10287 v8::String::Utf8Value func_name(frame->GetFunctionName()); 10288 v8::String::Utf8Value script_name(frame->GetScriptName()); 10289 if (*script_name == NULL) { 10290 // The situation where there is no associated script, like for evals. 10291 CHECK(expected_script_name == NULL); 10292 } else { 10293 CHECK(strstr(*script_name, expected_script_name) != NULL); 10294 } 10295 CHECK(strstr(*func_name, expected_func_name) != NULL); 10296 CHECK_EQ(expected_line_number, frame->GetLineNumber()); 10297 CHECK_EQ(expected_column, frame->GetColumn()); 10298 CHECK_EQ(is_eval, frame->IsEval()); 10299 CHECK_EQ(is_constructor, frame->IsConstructor()); 10300} 10301 10302 10303v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) { 10304 v8::HandleScope scope; 10305 const char* origin = "capture-stack-trace-test"; 10306 const int kOverviewTest = 1; 10307 const int kDetailedTest = 2; 10308 10309 ASSERT(args.Length() == 1); 10310 10311 int testGroup = args[0]->Int32Value(); 10312 if (testGroup == kOverviewTest) { 10313 v8::Handle<v8::StackTrace> stackTrace = 10314 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview); 10315 CHECK_EQ(4, stackTrace->GetFrameCount()); 10316 checkStackFrame(origin, "bar", 2, 10, false, false, 10317 stackTrace->GetFrame(0)); 10318 checkStackFrame(origin, "foo", 6, 3, false, false, 10319 stackTrace->GetFrame(1)); 10320 checkStackFrame(NULL, "", 1, 1, false, false, 10321 stackTrace->GetFrame(2)); 10322 // The last frame is an anonymous function that has the initial call. 10323 checkStackFrame(origin, "", 8, 7, false, false, 10324 stackTrace->GetFrame(3)); 10325 10326 CHECK(stackTrace->AsArray()->IsArray()); 10327 } else if (testGroup == kDetailedTest) { 10328 v8::Handle<v8::StackTrace> stackTrace = 10329 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); 10330 CHECK_EQ(4, stackTrace->GetFrameCount()); 10331 checkStackFrame(origin, "bat", 4, 22, false, false, 10332 stackTrace->GetFrame(0)); 10333 checkStackFrame(origin, "baz", 8, 3, false, true, 10334 stackTrace->GetFrame(1)); 10335#ifdef ENABLE_DEBUGGER_SUPPORT 10336 bool is_eval = true; 10337#else // ENABLE_DEBUGGER_SUPPORT 10338 bool is_eval = false; 10339#endif // ENABLE_DEBUGGER_SUPPORT 10340 10341 checkStackFrame(NULL, "", 1, 1, is_eval, false, 10342 stackTrace->GetFrame(2)); 10343 // The last frame is an anonymous function that has the initial call to foo. 10344 checkStackFrame(origin, "", 10, 1, false, false, 10345 stackTrace->GetFrame(3)); 10346 10347 CHECK(stackTrace->AsArray()->IsArray()); 10348 } 10349 return v8::Undefined(); 10350} 10351 10352 10353// Tests the C++ StackTrace API. 10354THREADED_TEST(CaptureStackTrace) { 10355 v8::HandleScope scope; 10356 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test"); 10357 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10358 templ->Set(v8_str("AnalyzeStackInNativeCode"), 10359 v8::FunctionTemplate::New(AnalyzeStackInNativeCode)); 10360 LocalContext context(0, templ); 10361 10362 // Test getting OVERVIEW information. Should ignore information that is not 10363 // script name, function name, line number, and column offset. 10364 const char *overview_source = 10365 "function bar() {\n" 10366 " var y; AnalyzeStackInNativeCode(1);\n" 10367 "}\n" 10368 "function foo() {\n" 10369 "\n" 10370 " bar();\n" 10371 "}\n" 10372 "var x;eval('new foo();');"; 10373 v8::Handle<v8::String> overview_src = v8::String::New(overview_source); 10374 v8::Handle<Value> overview_result = 10375 v8::Script::New(overview_src, origin)->Run(); 10376 ASSERT(!overview_result.IsEmpty()); 10377 ASSERT(overview_result->IsObject()); 10378 10379 // Test getting DETAILED information. 10380 const char *detailed_source = 10381 "function bat() {AnalyzeStackInNativeCode(2);\n" 10382 "}\n" 10383 "\n" 10384 "function baz() {\n" 10385 " bat();\n" 10386 "}\n" 10387 "eval('new baz();');"; 10388 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source); 10389 // Make the script using a non-zero line and column offset. 10390 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3); 10391 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5); 10392 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset); 10393 v8::Handle<v8::Script> detailed_script( 10394 v8::Script::New(detailed_src, &detailed_origin)); 10395 v8::Handle<Value> detailed_result = detailed_script->Run(); 10396 ASSERT(!detailed_result.IsEmpty()); 10397 ASSERT(detailed_result->IsObject()); 10398} 10399 10400 10401static void StackTraceForUncaughtExceptionListener( 10402 v8::Handle<v8::Message> message, 10403 v8::Handle<Value>) { 10404 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace(); 10405 CHECK_EQ(2, stack_trace->GetFrameCount()); 10406 checkStackFrame("origin", "foo", 2, 3, false, false, 10407 stack_trace->GetFrame(0)); 10408 checkStackFrame("origin", "bar", 5, 3, false, false, 10409 stack_trace->GetFrame(1)); 10410} 10411 10412TEST(CaptureStackTraceForUncaughtException) { 10413 report_count = 0; 10414 v8::HandleScope scope; 10415 LocalContext env; 10416 v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener); 10417 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); 10418 10419 Script::Compile(v8_str("function foo() {\n" 10420 " throw 1;\n" 10421 "};\n" 10422 "function bar() {\n" 10423 " foo();\n" 10424 "};"), 10425 v8_str("origin"))->Run(); 10426 v8::Local<v8::Object> global = env->Global(); 10427 Local<Value> trouble = global->Get(v8_str("bar")); 10428 CHECK(trouble->IsFunction()); 10429 Function::Cast(*trouble)->Call(global, 0, NULL); 10430 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); 10431 v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener); 10432} 10433 10434 10435// Test that idle notification can be handled and eventually returns true. 10436THREADED_TEST(IdleNotification) { 10437 bool rv = false; 10438 for (int i = 0; i < 100; i++) { 10439 rv = v8::V8::IdleNotification(); 10440 if (rv) 10441 break; 10442 } 10443 CHECK(rv == true); 10444} 10445 10446 10447static uint32_t* stack_limit; 10448 10449static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) { 10450 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit()); 10451 return v8::Undefined(); 10452} 10453 10454 10455// Uses the address of a local variable to determine the stack top now. 10456// Given a size, returns an address that is that far from the current 10457// top of stack. 10458static uint32_t* ComputeStackLimit(uint32_t size) { 10459 uint32_t* answer = &size - (size / sizeof(size)); 10460 // If the size is very large and the stack is very near the bottom of 10461 // memory then the calculation above may wrap around and give an address 10462 // that is above the (downwards-growing) stack. In that case we return 10463 // a very low address. 10464 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size)); 10465 return answer; 10466} 10467 10468 10469TEST(SetResourceConstraints) { 10470 static const int K = 1024; 10471 uint32_t* set_limit = ComputeStackLimit(128 * K); 10472 10473 // Set stack limit. 10474 v8::ResourceConstraints constraints; 10475 constraints.set_stack_limit(set_limit); 10476 CHECK(v8::SetResourceConstraints(&constraints)); 10477 10478 // Execute a script. 10479 v8::HandleScope scope; 10480 LocalContext env; 10481 Local<v8::FunctionTemplate> fun_templ = 10482 v8::FunctionTemplate::New(GetStackLimitCallback); 10483 Local<Function> fun = fun_templ->GetFunction(); 10484 env->Global()->Set(v8_str("get_stack_limit"), fun); 10485 CompileRun("get_stack_limit();"); 10486 10487 CHECK(stack_limit == set_limit); 10488} 10489 10490 10491TEST(SetResourceConstraintsInThread) { 10492 uint32_t* set_limit; 10493 { 10494 v8::Locker locker; 10495 static const int K = 1024; 10496 set_limit = ComputeStackLimit(128 * K); 10497 10498 // Set stack limit. 10499 v8::ResourceConstraints constraints; 10500 constraints.set_stack_limit(set_limit); 10501 CHECK(v8::SetResourceConstraints(&constraints)); 10502 10503 // Execute a script. 10504 v8::HandleScope scope; 10505 LocalContext env; 10506 Local<v8::FunctionTemplate> fun_templ = 10507 v8::FunctionTemplate::New(GetStackLimitCallback); 10508 Local<Function> fun = fun_templ->GetFunction(); 10509 env->Global()->Set(v8_str("get_stack_limit"), fun); 10510 CompileRun("get_stack_limit();"); 10511 10512 CHECK(stack_limit == set_limit); 10513 } 10514 { 10515 v8::Locker locker; 10516 CHECK(stack_limit == set_limit); 10517 } 10518} 10519 10520 10521THREADED_TEST(GetHeapStatistics) { 10522 v8::HandleScope scope; 10523 LocalContext c1; 10524 v8::HeapStatistics heap_statistics; 10525 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0); 10526 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0); 10527 v8::V8::GetHeapStatistics(&heap_statistics); 10528 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0); 10529 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0); 10530} 10531 10532 10533static double DoubleFromBits(uint64_t value) { 10534 double target; 10535#ifdef BIG_ENDIAN_FLOATING_POINT 10536 const int kIntSize = 4; 10537 // Somebody swapped the lower and higher half of doubles. 10538 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); 10539 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); 10540#else 10541 memcpy(&target, &value, sizeof(target)); 10542#endif 10543 return target; 10544} 10545 10546 10547static uint64_t DoubleToBits(double value) { 10548 uint64_t target; 10549#ifdef BIG_ENDIAN_FLOATING_POINT 10550 const int kIntSize = 4; 10551 // Somebody swapped the lower and higher half of doubles. 10552 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); 10553 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); 10554#else 10555 memcpy(&target, &value, sizeof(target)); 10556#endif 10557 return target; 10558} 10559 10560 10561static double DoubleToDateTime(double input) { 10562 double date_limit = 864e13; 10563 if (IsNaN(input) || input < -date_limit || input > date_limit) { 10564 return i::OS::nan_value(); 10565 } 10566 return (input < 0) ? -(floor(-input)) : floor(input); 10567} 10568 10569// We don't have a consistent way to write 64-bit constants syntactically, so we 10570// split them into two 32-bit constants and combine them programmatically. 10571static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) { 10572 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits); 10573} 10574 10575 10576THREADED_TEST(QuietSignalingNaNs) { 10577 v8::HandleScope scope; 10578 LocalContext context; 10579 v8::TryCatch try_catch; 10580 10581 // Special double values. 10582 double snan = DoubleFromBits(0x7ff00000, 0x00000001); 10583 double qnan = DoubleFromBits(0x7ff80000, 0x00000000); 10584 double infinity = DoubleFromBits(0x7ff00000, 0x00000000); 10585 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu); 10586 double min_normal = DoubleFromBits(0x00100000, 0x00000000); 10587 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu); 10588 double min_denormal = DoubleFromBits(0x00000000, 0x00000001); 10589 10590 // Date values are capped at +/-100000000 days (times 864e5 ms per day) 10591 // on either side of the epoch. 10592 double date_limit = 864e13; 10593 10594 double test_values[] = { 10595 snan, 10596 qnan, 10597 infinity, 10598 max_normal, 10599 date_limit + 1, 10600 date_limit, 10601 min_normal, 10602 max_denormal, 10603 min_denormal, 10604 0, 10605 -0, 10606 -min_denormal, 10607 -max_denormal, 10608 -min_normal, 10609 -date_limit, 10610 -date_limit - 1, 10611 -max_normal, 10612 -infinity, 10613 -qnan, 10614 -snan 10615 }; 10616 int num_test_values = 20; 10617 10618 for (int i = 0; i < num_test_values; i++) { 10619 double test_value = test_values[i]; 10620 10621 // Check that Number::New preserves non-NaNs and quiets SNaNs. 10622 v8::Handle<v8::Value> number = v8::Number::New(test_value); 10623 double stored_number = number->NumberValue(); 10624 if (!IsNaN(test_value)) { 10625 CHECK_EQ(test_value, stored_number); 10626 } else { 10627 uint64_t stored_bits = DoubleToBits(stored_number); 10628 // Check if quiet nan (bits 51..62 all set). 10629 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 10630 } 10631 10632 // Check that Date::New preserves non-NaNs in the date range and 10633 // quiets SNaNs. 10634 v8::Handle<v8::Value> date = v8::Date::New(test_value); 10635 double expected_stored_date = DoubleToDateTime(test_value); 10636 double stored_date = date->NumberValue(); 10637 if (!IsNaN(expected_stored_date)) { 10638 CHECK_EQ(expected_stored_date, stored_date); 10639 } else { 10640 uint64_t stored_bits = DoubleToBits(stored_date); 10641 // Check if quiet nan (bits 51..62 all set). 10642 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 10643 } 10644 } 10645} 10646 10647 10648static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) { 10649 v8::HandleScope scope; 10650 v8::TryCatch tc; 10651 v8::Handle<v8::String> str = args[0]->ToString(); 10652 if (tc.HasCaught()) 10653 return tc.ReThrow(); 10654 return v8::Undefined(); 10655} 10656 10657 10658// Test that an exception can be propagated down through a spaghetti 10659// stack using ReThrow. 10660THREADED_TEST(SpaghettiStackReThrow) { 10661 v8::HandleScope scope; 10662 LocalContext context; 10663 context->Global()->Set( 10664 v8::String::New("s"), 10665 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction()); 10666 v8::TryCatch try_catch; 10667 CompileRun( 10668 "var i = 0;" 10669 "var o = {" 10670 " toString: function () {" 10671 " if (i == 10) {" 10672 " throw 'Hey!';" 10673 " } else {" 10674 " i++;" 10675 " return s(o);" 10676 " }" 10677 " }" 10678 "};" 10679 "s(o);"); 10680 CHECK(try_catch.HasCaught()); 10681 v8::String::Utf8Value value(try_catch.Exception()); 10682 CHECK_EQ(0, strcmp(*value, "Hey!")); 10683} 10684 10685 10686TEST(Regress528) { 10687 v8::V8::Initialize(); 10688 10689 v8::HandleScope scope; 10690 v8::Persistent<Context> context; 10691 v8::Persistent<Context> other_context; 10692 int gc_count; 10693 10694 // Create a context used to keep the code from aging in the compilation 10695 // cache. 10696 other_context = Context::New(); 10697 10698 // Context-dependent context data creates reference from the compilation 10699 // cache to the global object. 10700 const char* source_simple = "1"; 10701 context = Context::New(); 10702 { 10703 v8::HandleScope scope; 10704 10705 context->Enter(); 10706 Local<v8::String> obj = v8::String::New(""); 10707 context->SetData(obj); 10708 CompileRun(source_simple); 10709 context->Exit(); 10710 } 10711 context.Dispose(); 10712 for (gc_count = 1; gc_count < 10; gc_count++) { 10713 other_context->Enter(); 10714 CompileRun(source_simple); 10715 other_context->Exit(); 10716 i::Heap::CollectAllGarbage(false); 10717 if (GetGlobalObjectsCount() == 1) break; 10718 } 10719 CHECK_GE(2, gc_count); 10720 CHECK_EQ(1, GetGlobalObjectsCount()); 10721 10722 // Eval in a function creates reference from the compilation cache to the 10723 // global object. 10724 const char* source_eval = "function f(){eval('1')}; f()"; 10725 context = Context::New(); 10726 { 10727 v8::HandleScope scope; 10728 10729 context->Enter(); 10730 CompileRun(source_eval); 10731 context->Exit(); 10732 } 10733 context.Dispose(); 10734 for (gc_count = 1; gc_count < 10; gc_count++) { 10735 other_context->Enter(); 10736 CompileRun(source_eval); 10737 other_context->Exit(); 10738 i::Heap::CollectAllGarbage(false); 10739 if (GetGlobalObjectsCount() == 1) break; 10740 } 10741 CHECK_GE(2, gc_count); 10742 CHECK_EQ(1, GetGlobalObjectsCount()); 10743 10744 // Looking up the line number for an exception creates reference from the 10745 // compilation cache to the global object. 10746 const char* source_exception = "function f(){throw 1;} f()"; 10747 context = Context::New(); 10748 { 10749 v8::HandleScope scope; 10750 10751 context->Enter(); 10752 v8::TryCatch try_catch; 10753 CompileRun(source_exception); 10754 CHECK(try_catch.HasCaught()); 10755 v8::Handle<v8::Message> message = try_catch.Message(); 10756 CHECK(!message.IsEmpty()); 10757 CHECK_EQ(1, message->GetLineNumber()); 10758 context->Exit(); 10759 } 10760 context.Dispose(); 10761 for (gc_count = 1; gc_count < 10; gc_count++) { 10762 other_context->Enter(); 10763 CompileRun(source_exception); 10764 other_context->Exit(); 10765 i::Heap::CollectAllGarbage(false); 10766 if (GetGlobalObjectsCount() == 1) break; 10767 } 10768 CHECK_GE(2, gc_count); 10769 CHECK_EQ(1, GetGlobalObjectsCount()); 10770 10771 other_context.Dispose(); 10772} 10773 10774 10775THREADED_TEST(ScriptOrigin) { 10776 v8::HandleScope scope; 10777 LocalContext env; 10778 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 10779 v8::Handle<v8::String> script = v8::String::New( 10780 "function f() {}\n\nfunction g() {}"); 10781 v8::Script::Compile(script, &origin)->Run(); 10782 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 10783 env->Global()->Get(v8::String::New("f"))); 10784 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 10785 env->Global()->Get(v8::String::New("g"))); 10786 10787 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin(); 10788 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName())); 10789 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value()); 10790 10791 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin(); 10792 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName())); 10793 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value()); 10794} 10795 10796 10797THREADED_TEST(ScriptLineNumber) { 10798 v8::HandleScope scope; 10799 LocalContext env; 10800 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 10801 v8::Handle<v8::String> script = v8::String::New( 10802 "function f() {}\n\nfunction g() {}"); 10803 v8::Script::Compile(script, &origin)->Run(); 10804 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 10805 env->Global()->Get(v8::String::New("f"))); 10806 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 10807 env->Global()->Get(v8::String::New("g"))); 10808 CHECK_EQ(0, f->GetScriptLineNumber()); 10809 CHECK_EQ(2, g->GetScriptLineNumber()); 10810} 10811 10812 10813static v8::Handle<Value> GetterWhichReturns42(Local<String> name, 10814 const AccessorInfo& info) { 10815 return v8_num(42); 10816} 10817 10818 10819static void SetterWhichSetsYOnThisTo23(Local<String> name, 10820 Local<Value> value, 10821 const AccessorInfo& info) { 10822 info.This()->Set(v8_str("y"), v8_num(23)); 10823} 10824 10825 10826TEST(SetterOnConstructorPrototype) { 10827 v8::HandleScope scope; 10828 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10829 templ->SetAccessor(v8_str("x"), 10830 GetterWhichReturns42, 10831 SetterWhichSetsYOnThisTo23); 10832 LocalContext context; 10833 context->Global()->Set(v8_str("P"), templ->NewInstance()); 10834 CompileRun("function C1() {" 10835 " this.x = 23;" 10836 "};" 10837 "C1.prototype = P;" 10838 "function C2() {" 10839 " this.x = 23" 10840 "};" 10841 "C2.prototype = { };" 10842 "C2.prototype.__proto__ = P;"); 10843 10844 v8::Local<v8::Script> script; 10845 script = v8::Script::Compile(v8_str("new C1();")); 10846 for (int i = 0; i < 10; i++) { 10847 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 10848 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); 10849 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); 10850 } 10851 10852 script = v8::Script::Compile(v8_str("new C2();")); 10853 for (int i = 0; i < 10; i++) { 10854 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 10855 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value()); 10856 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value()); 10857 } 10858} 10859 10860 10861static v8::Handle<Value> NamedPropertyGetterWhichReturns42( 10862 Local<String> name, const AccessorInfo& info) { 10863 return v8_num(42); 10864} 10865 10866 10867static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23( 10868 Local<String> name, Local<Value> value, const AccessorInfo& info) { 10869 if (name->Equals(v8_str("x"))) { 10870 info.This()->Set(v8_str("y"), v8_num(23)); 10871 } 10872 return v8::Handle<Value>(); 10873} 10874 10875 10876THREADED_TEST(InterceptorOnConstructorPrototype) { 10877 v8::HandleScope scope; 10878 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10879 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42, 10880 NamedPropertySetterWhichSetsYOnThisTo23); 10881 LocalContext context; 10882 context->Global()->Set(v8_str("P"), templ->NewInstance()); 10883 CompileRun("function C1() {" 10884 " this.x = 23;" 10885 "};" 10886 "C1.prototype = P;" 10887 "function C2() {" 10888 " this.x = 23" 10889 "};" 10890 "C2.prototype = { };" 10891 "C2.prototype.__proto__ = P;"); 10892 10893 v8::Local<v8::Script> script; 10894 script = v8::Script::Compile(v8_str("new C1();")); 10895 for (int i = 0; i < 10; i++) { 10896 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 10897 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); 10898 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); 10899 } 10900 10901 script = v8::Script::Compile(v8_str("new C2();")); 10902 for (int i = 0; i < 10; i++) { 10903 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 10904 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value()); 10905 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value()); 10906 } 10907} 10908 10909 10910TEST(Bug618) { 10911 const char* source = "function C1() {" 10912 " this.x = 23;" 10913 "};" 10914 "C1.prototype = P;"; 10915 10916 v8::HandleScope scope; 10917 LocalContext context; 10918 v8::Local<v8::Script> script; 10919 10920 // Use a simple object as prototype. 10921 v8::Local<v8::Object> prototype = v8::Object::New(); 10922 prototype->Set(v8_str("y"), v8_num(42)); 10923 context->Global()->Set(v8_str("P"), prototype); 10924 10925 // This compile will add the code to the compilation cache. 10926 CompileRun(source); 10927 10928 script = v8::Script::Compile(v8_str("new C1();")); 10929 for (int i = 0; i < 10; i++) { 10930 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 10931 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); 10932 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); 10933 } 10934 10935 // Use an API object with accessors as prototype. 10936 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10937 templ->SetAccessor(v8_str("x"), 10938 GetterWhichReturns42, 10939 SetterWhichSetsYOnThisTo23); 10940 context->Global()->Set(v8_str("P"), templ->NewInstance()); 10941 10942 // This compile will get the code from the compilation cache. 10943 CompileRun(source); 10944 10945 script = v8::Script::Compile(v8_str("new C1();")); 10946 for (int i = 0; i < 10; i++) { 10947 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 10948 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); 10949 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); 10950 } 10951} 10952 10953int prologue_call_count = 0; 10954int epilogue_call_count = 0; 10955int prologue_call_count_second = 0; 10956int epilogue_call_count_second = 0; 10957 10958void PrologueCallback(v8::GCType, v8::GCCallbackFlags) { 10959 ++prologue_call_count; 10960} 10961 10962void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) { 10963 ++epilogue_call_count; 10964} 10965 10966void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { 10967 ++prologue_call_count_second; 10968} 10969 10970void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { 10971 ++epilogue_call_count_second; 10972} 10973 10974TEST(GCCallbacks) { 10975 LocalContext context; 10976 10977 v8::V8::AddGCPrologueCallback(PrologueCallback); 10978 v8::V8::AddGCEpilogueCallback(EpilogueCallback); 10979 CHECK_EQ(0, prologue_call_count); 10980 CHECK_EQ(0, epilogue_call_count); 10981 i::Heap::CollectAllGarbage(false); 10982 CHECK_EQ(1, prologue_call_count); 10983 CHECK_EQ(1, epilogue_call_count); 10984 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond); 10985 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond); 10986 i::Heap::CollectAllGarbage(false); 10987 CHECK_EQ(2, prologue_call_count); 10988 CHECK_EQ(2, epilogue_call_count); 10989 CHECK_EQ(1, prologue_call_count_second); 10990 CHECK_EQ(1, epilogue_call_count_second); 10991 v8::V8::RemoveGCPrologueCallback(PrologueCallback); 10992 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback); 10993 i::Heap::CollectAllGarbage(false); 10994 CHECK_EQ(2, prologue_call_count); 10995 CHECK_EQ(2, epilogue_call_count); 10996 CHECK_EQ(2, prologue_call_count_second); 10997 CHECK_EQ(2, epilogue_call_count_second); 10998 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond); 10999 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond); 11000 i::Heap::CollectAllGarbage(false); 11001 CHECK_EQ(2, prologue_call_count); 11002 CHECK_EQ(2, epilogue_call_count); 11003 CHECK_EQ(2, prologue_call_count_second); 11004 CHECK_EQ(2, epilogue_call_count_second); 11005} 11006 11007 11008THREADED_TEST(AddToJSFunctionResultCache) { 11009 i::FLAG_allow_natives_syntax = true; 11010 v8::HandleScope scope; 11011 11012 LocalContext context; 11013 11014 const char* code = 11015 "(function() {" 11016 " var key0 = 'a';" 11017 " var key1 = 'b';" 11018 " var r0 = %_GetFromCache(0, key0);" 11019 " var r1 = %_GetFromCache(0, key1);" 11020 " var r0_ = %_GetFromCache(0, key0);" 11021 " if (r0 !== r0_)" 11022 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;" 11023 " var r1_ = %_GetFromCache(0, key1);" 11024 " if (r1 !== r1_)" 11025 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;" 11026 " return 'PASSED';" 11027 "})()"; 11028 i::Heap::ClearJSFunctionResultCaches(); 11029 ExpectString(code, "PASSED"); 11030} 11031 11032 11033static const int k0CacheSize = 16; 11034 11035THREADED_TEST(FillJSFunctionResultCache) { 11036 i::FLAG_allow_natives_syntax = true; 11037 v8::HandleScope scope; 11038 11039 LocalContext context; 11040 11041 const char* code = 11042 "(function() {" 11043 " var k = 'a';" 11044 " var r = %_GetFromCache(0, k);" 11045 " for (var i = 0; i < 16; i++) {" 11046 " %_GetFromCache(0, 'a' + i);" 11047 " };" 11048 " if (r === %_GetFromCache(0, k))" 11049 " return 'FAILED: k0CacheSize is too small';" 11050 " return 'PASSED';" 11051 "})()"; 11052 i::Heap::ClearJSFunctionResultCaches(); 11053 ExpectString(code, "PASSED"); 11054} 11055 11056 11057THREADED_TEST(RoundRobinGetFromCache) { 11058 i::FLAG_allow_natives_syntax = true; 11059 v8::HandleScope scope; 11060 11061 LocalContext context; 11062 11063 const char* code = 11064 "(function() {" 11065 " var keys = [];" 11066 " for (var i = 0; i < 16; i++) keys.push(i);" 11067 " var values = [];" 11068 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" 11069 " for (var i = 0; i < 16; i++) {" 11070 " var v = %_GetFromCache(0, keys[i]);" 11071 " if (v !== values[i])" 11072 " return 'Wrong value for ' + " 11073 " keys[i] + ': ' + v + ' vs. ' + values[i];" 11074 " };" 11075 " return 'PASSED';" 11076 "})()"; 11077 i::Heap::ClearJSFunctionResultCaches(); 11078 ExpectString(code, "PASSED"); 11079} 11080 11081 11082THREADED_TEST(ReverseGetFromCache) { 11083 i::FLAG_allow_natives_syntax = true; 11084 v8::HandleScope scope; 11085 11086 LocalContext context; 11087 11088 const char* code = 11089 "(function() {" 11090 " var keys = [];" 11091 " for (var i = 0; i < 16; i++) keys.push(i);" 11092 " var values = [];" 11093 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" 11094 " for (var i = 15; i >= 16; i--) {" 11095 " var v = %_GetFromCache(0, keys[i]);" 11096 " if (v !== values[i])" 11097 " return 'Wrong value for ' + " 11098 " keys[i] + ': ' + v + ' vs. ' + values[i];" 11099 " };" 11100 " return 'PASSED';" 11101 "})()"; 11102 i::Heap::ClearJSFunctionResultCaches(); 11103 ExpectString(code, "PASSED"); 11104} 11105 11106 11107THREADED_TEST(TestEviction) { 11108 i::FLAG_allow_natives_syntax = true; 11109 v8::HandleScope scope; 11110 11111 LocalContext context; 11112 11113 const char* code = 11114 "(function() {" 11115 " for (var i = 0; i < 2*16; i++) {" 11116 " %_GetFromCache(0, 'a' + i);" 11117 " };" 11118 " return 'PASSED';" 11119 "})()"; 11120 i::Heap::ClearJSFunctionResultCaches(); 11121 ExpectString(code, "PASSED"); 11122} 11123 11124 11125THREADED_TEST(TwoByteStringInAsciiCons) { 11126 // See Chromium issue 47824. 11127 v8::HandleScope scope; 11128 11129 LocalContext context; 11130 const char* init_code = 11131 "var str1 = 'abelspendabel';" 11132 "var str2 = str1 + str1 + str1;" 11133 "str2;"; 11134 Local<Value> result = CompileRun(init_code); 11135 11136 CHECK(result->IsString()); 11137 i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result)); 11138 int length = string->length(); 11139 CHECK(string->IsAsciiRepresentation()); 11140 11141 FlattenString(string); 11142 i::Handle<i::String> flat_string = FlattenGetString(string); 11143 11144 CHECK(string->IsAsciiRepresentation()); 11145 CHECK(flat_string->IsAsciiRepresentation()); 11146 11147 // Create external resource. 11148 uint16_t* uc16_buffer = new uint16_t[length + 1]; 11149 11150 i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length); 11151 uc16_buffer[length] = 0; 11152 11153 TestResource resource(uc16_buffer); 11154 11155 flat_string->MakeExternal(&resource); 11156 11157 CHECK(flat_string->IsTwoByteRepresentation()); 11158 11159 // At this point, we should have a Cons string which is flat and ASCII, 11160 // with a first half that is a two-byte string (although it only contains 11161 // ASCII characters). This is a valid sequence of steps, and it can happen 11162 // in real pages. 11163 11164 CHECK(string->IsAsciiRepresentation()); 11165 i::ConsString* cons = i::ConsString::cast(*string); 11166 CHECK_EQ(0, cons->second()->length()); 11167 CHECK(cons->first()->IsTwoByteRepresentation()); 11168 11169 // Check that some string operations work. 11170 11171 // Atom RegExp. 11172 Local<Value> reresult = CompileRun("str2.match(/abel/g).length;"); 11173 CHECK_EQ(6, reresult->Int32Value()); 11174 11175 // Nonatom RegExp. 11176 reresult = CompileRun("str2.match(/abe./g).length;"); 11177 CHECK_EQ(6, reresult->Int32Value()); 11178 11179 reresult = CompileRun("str2.search(/bel/g);"); 11180 CHECK_EQ(1, reresult->Int32Value()); 11181 11182 reresult = CompileRun("str2.search(/be./g);"); 11183 CHECK_EQ(1, reresult->Int32Value()); 11184 11185 ExpectTrue("/bel/g.test(str2);"); 11186 11187 ExpectTrue("/be./g.test(str2);"); 11188 11189 reresult = CompileRun("/bel/g.exec(str2);"); 11190 CHECK(!reresult->IsNull()); 11191 11192 reresult = CompileRun("/be./g.exec(str2);"); 11193 CHECK(!reresult->IsNull()); 11194 11195 ExpectString("str2.substring(2, 10);", "elspenda"); 11196 11197 ExpectString("str2.substring(2, 20);", "elspendabelabelspe"); 11198 11199 ExpectString("str2.charAt(2);", "e"); 11200 11201 reresult = CompileRun("str2.charCodeAt(2);"); 11202 CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value()); 11203} 11204 11205 11206// Failed access check callback that performs a GC on each invocation. 11207void FailedAccessCheckCallbackGC(Local<v8::Object> target, 11208 v8::AccessType type, 11209 Local<v8::Value> data) { 11210 i::Heap::CollectAllGarbage(true); 11211} 11212 11213 11214TEST(GCInFailedAccessCheckCallback) { 11215 // Install a failed access check callback that performs a GC on each 11216 // invocation. Then force the callback to be called from va 11217 11218 v8::V8::Initialize(); 11219 v8::V8::SetFailedAccessCheckCallbackFunction(&FailedAccessCheckCallbackGC); 11220 11221 v8::HandleScope scope; 11222 11223 // Create an ObjectTemplate for global objects and install access 11224 // check callbacks that will block access. 11225 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 11226 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker, 11227 IndexedGetAccessBlocker, 11228 v8::Handle<v8::Value>(), 11229 false); 11230 11231 // Create a context and set an x property on it's global object. 11232 LocalContext context0(NULL, global_template); 11233 context0->Global()->Set(v8_str("x"), v8_num(42)); 11234 v8::Handle<v8::Object> global0 = context0->Global(); 11235 11236 // Create a context with a different security token so that the 11237 // failed access check callback will be called on each access. 11238 LocalContext context1(NULL, global_template); 11239 context1->Global()->Set(v8_str("other"), global0); 11240 11241 // Get property with failed access check. 11242 ExpectUndefined("other.x"); 11243 11244 // Get element with failed access check. 11245 ExpectUndefined("other[0]"); 11246 11247 // Set property with failed access check. 11248 v8::Handle<v8::Value> result = CompileRun("other.x = new Object()"); 11249 CHECK(result->IsObject()); 11250 11251 // Set element with failed access check. 11252 result = CompileRun("other[0] = new Object()"); 11253 CHECK(result->IsObject()); 11254 11255 // Get property attribute with failed access check. 11256 ExpectFalse("\'x\' in other"); 11257 11258 // Get property attribute for element with failed access check. 11259 ExpectFalse("0 in other"); 11260 11261 // Delete property. 11262 ExpectFalse("delete other.x"); 11263 11264 // Delete element. 11265 CHECK_EQ(false, global0->Delete(0)); 11266 11267 // DefineAccessor. 11268 CHECK_EQ(false, 11269 global0->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("x"))); 11270 11271 // Define JavaScript accessor. 11272 ExpectUndefined("Object.prototype.__defineGetter__.call(" 11273 " other, \'x\', function() { return 42; })"); 11274 11275 // LookupAccessor. 11276 ExpectUndefined("Object.prototype.__lookupGetter__.call(" 11277 " other, \'x\')"); 11278 11279 // HasLocalElement. 11280 ExpectFalse("Object.prototype.hasOwnProperty.call(other, \'0\')"); 11281 11282 CHECK_EQ(false, global0->HasRealIndexedProperty(0)); 11283 CHECK_EQ(false, global0->HasRealNamedProperty(v8_str("x"))); 11284 CHECK_EQ(false, global0->HasRealNamedCallbackProperty(v8_str("x"))); 11285 11286 // Reset the failed access check callback so it does not influence 11287 // the other tests. 11288 v8::V8::SetFailedAccessCheckCallbackFunction(NULL); 11289} 11290