test-api.cc revision b8e0da25ee8efac3bb05cd6b2730aafbd96119f4
1// Copyright 2007-2009 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28#include <limits.h> 29 30#include "v8.h" 31 32#include "api.h" 33#include "compilation-cache.h" 34#include "execution.h" 35#include "snapshot.h" 36#include "platform.h" 37#include "top.h" 38#include "utils.h" 39#include "cctest.h" 40#include "parser.h" 41#include "unicode-inl.h" 42 43static const bool kLogThreading = true; 44 45static bool IsNaN(double x) { 46#ifdef WIN32 47 return _isnan(x); 48#else 49 return isnan(x); 50#endif 51} 52 53using ::v8::ObjectTemplate; 54using ::v8::Value; 55using ::v8::Context; 56using ::v8::Local; 57using ::v8::String; 58using ::v8::Script; 59using ::v8::Function; 60using ::v8::AccessorInfo; 61using ::v8::Extension; 62 63namespace i = ::i; 64 65 66static void ExpectString(const char* code, const char* expected) { 67 Local<Value> result = CompileRun(code); 68 CHECK(result->IsString()); 69 String::AsciiValue ascii(result); 70 CHECK_EQ(expected, *ascii); 71} 72 73 74static void ExpectBoolean(const char* code, bool expected) { 75 Local<Value> result = CompileRun(code); 76 CHECK(result->IsBoolean()); 77 CHECK_EQ(expected, result->BooleanValue()); 78} 79 80 81static void ExpectTrue(const char* code) { 82 ExpectBoolean(code, true); 83} 84 85 86static void ExpectFalse(const char* code) { 87 ExpectBoolean(code, false); 88} 89 90 91static void ExpectObject(const char* code, Local<Value> expected) { 92 Local<Value> result = CompileRun(code); 93 CHECK(result->Equals(expected)); 94} 95 96 97static void ExpectUndefined(const char* code) { 98 Local<Value> result = CompileRun(code); 99 CHECK(result->IsUndefined()); 100} 101 102 103static int signature_callback_count; 104static v8::Handle<Value> IncrementingSignatureCallback( 105 const v8::Arguments& args) { 106 ApiTestFuzzer::Fuzz(); 107 signature_callback_count++; 108 v8::Handle<v8::Array> result = v8::Array::New(args.Length()); 109 for (int i = 0; i < args.Length(); i++) 110 result->Set(v8::Integer::New(i), args[i]); 111 return result; 112} 113 114 115static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) { 116 ApiTestFuzzer::Fuzz(); 117 v8::Handle<v8::Array> result = v8::Array::New(args.Length()); 118 for (int i = 0; i < args.Length(); i++) { 119 result->Set(v8::Integer::New(i), args[i]); 120 } 121 return result; 122} 123 124 125THREADED_TEST(Handles) { 126 v8::HandleScope scope; 127 Local<Context> local_env; 128 { 129 LocalContext env; 130 local_env = env.local(); 131 } 132 133 // Local context should still be live. 134 CHECK(!local_env.IsEmpty()); 135 local_env->Enter(); 136 137 v8::Handle<v8::Primitive> undef = v8::Undefined(); 138 CHECK(!undef.IsEmpty()); 139 CHECK(undef->IsUndefined()); 140 141 const char* c_source = "1 + 2 + 3"; 142 Local<String> source = String::New(c_source); 143 Local<Script> script = Script::Compile(source); 144 CHECK_EQ(6, script->Run()->Int32Value()); 145 146 local_env->Exit(); 147} 148 149 150THREADED_TEST(ReceiverSignature) { 151 v8::HandleScope scope; 152 LocalContext env; 153 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); 154 v8::Handle<v8::Signature> sig = v8::Signature::New(fun); 155 fun->PrototypeTemplate()->Set( 156 v8_str("m"), 157 v8::FunctionTemplate::New(IncrementingSignatureCallback, 158 v8::Handle<Value>(), 159 sig)); 160 env->Global()->Set(v8_str("Fun"), fun->GetFunction()); 161 signature_callback_count = 0; 162 CompileRun( 163 "var o = new Fun();" 164 "o.m();"); 165 CHECK_EQ(1, signature_callback_count); 166 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New(); 167 sub_fun->Inherit(fun); 168 env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction()); 169 CompileRun( 170 "var o = new SubFun();" 171 "o.m();"); 172 CHECK_EQ(2, signature_callback_count); 173 174 v8::TryCatch try_catch; 175 CompileRun( 176 "var o = { };" 177 "o.m = Fun.prototype.m;" 178 "o.m();"); 179 CHECK_EQ(2, signature_callback_count); 180 CHECK(try_catch.HasCaught()); 181 try_catch.Reset(); 182 v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New(); 183 sub_fun->Inherit(fun); 184 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction()); 185 CompileRun( 186 "var o = new UnrelFun();" 187 "o.m = Fun.prototype.m;" 188 "o.m();"); 189 CHECK_EQ(2, signature_callback_count); 190 CHECK(try_catch.HasCaught()); 191} 192 193 194 195 196THREADED_TEST(ArgumentSignature) { 197 v8::HandleScope scope; 198 LocalContext env; 199 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New(); 200 cons->SetClassName(v8_str("Cons")); 201 v8::Handle<v8::Signature> sig = 202 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons); 203 v8::Handle<v8::FunctionTemplate> fun = 204 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig); 205 env->Global()->Set(v8_str("Cons"), cons->GetFunction()); 206 env->Global()->Set(v8_str("Fun1"), fun->GetFunction()); 207 208 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';"); 209 CHECK(value1->IsTrue()); 210 211 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';"); 212 CHECK(value2->IsTrue()); 213 214 v8::Handle<Value> value3 = CompileRun("Fun1() == '';"); 215 CHECK(value3->IsTrue()); 216 217 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New(); 218 cons1->SetClassName(v8_str("Cons1")); 219 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New(); 220 cons2->SetClassName(v8_str("Cons2")); 221 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New(); 222 cons3->SetClassName(v8_str("Cons3")); 223 224 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 }; 225 v8::Handle<v8::Signature> wsig = 226 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args); 227 v8::Handle<v8::FunctionTemplate> fun2 = 228 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig); 229 230 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction()); 231 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction()); 232 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction()); 233 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction()); 234 v8::Handle<Value> value4 = CompileRun( 235 "Fun2(new Cons1(), new Cons2(), new Cons3()) ==" 236 "'[object Cons1],[object Cons2],[object Cons3]'"); 237 CHECK(value4->IsTrue()); 238 239 v8::Handle<Value> value5 = CompileRun( 240 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'"); 241 CHECK(value5->IsTrue()); 242 243 v8::Handle<Value> value6 = CompileRun( 244 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'"); 245 CHECK(value6->IsTrue()); 246 247 v8::Handle<Value> value7 = CompileRun( 248 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == " 249 "'[object Cons1],[object Cons2],[object Cons3],d';"); 250 CHECK(value7->IsTrue()); 251 252 v8::Handle<Value> value8 = CompileRun( 253 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'"); 254 CHECK(value8->IsTrue()); 255} 256 257 258THREADED_TEST(HulIgennem) { 259 v8::HandleScope scope; 260 LocalContext env; 261 v8::Handle<v8::Primitive> undef = v8::Undefined(); 262 Local<String> undef_str = undef->ToString(); 263 char* value = i::NewArray<char>(undef_str->Length() + 1); 264 undef_str->WriteAscii(value); 265 CHECK_EQ(0, strcmp(value, "undefined")); 266 i::DeleteArray(value); 267} 268 269 270THREADED_TEST(Access) { 271 v8::HandleScope scope; 272 LocalContext env; 273 Local<v8::Object> obj = v8::Object::New(); 274 Local<Value> foo_before = obj->Get(v8_str("foo")); 275 CHECK(foo_before->IsUndefined()); 276 Local<String> bar_str = v8_str("bar"); 277 obj->Set(v8_str("foo"), bar_str); 278 Local<Value> foo_after = obj->Get(v8_str("foo")); 279 CHECK(!foo_after->IsUndefined()); 280 CHECK(foo_after->IsString()); 281 CHECK_EQ(bar_str, foo_after); 282} 283 284 285THREADED_TEST(AccessElement) { 286 v8::HandleScope scope; 287 LocalContext env; 288 Local<v8::Object> obj = v8::Object::New(); 289 Local<Value> before = obj->Get(1); 290 CHECK(before->IsUndefined()); 291 Local<String> bar_str = v8_str("bar"); 292 obj->Set(1, bar_str); 293 Local<Value> after = obj->Get(1); 294 CHECK(!after->IsUndefined()); 295 CHECK(after->IsString()); 296 CHECK_EQ(bar_str, after); 297 298 Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>(); 299 CHECK_EQ(v8_str("a"), value->Get(0)); 300 CHECK_EQ(v8_str("b"), value->Get(1)); 301} 302 303 304THREADED_TEST(Script) { 305 v8::HandleScope scope; 306 LocalContext env; 307 const char* c_source = "1 + 2 + 3"; 308 Local<String> source = String::New(c_source); 309 Local<Script> script = Script::Compile(source); 310 CHECK_EQ(6, script->Run()->Int32Value()); 311} 312 313 314static uint16_t* AsciiToTwoByteString(const char* source) { 315 int array_length = i::StrLength(source) + 1; 316 uint16_t* converted = i::NewArray<uint16_t>(array_length); 317 for (int i = 0; i < array_length; i++) converted[i] = source[i]; 318 return converted; 319} 320 321 322class TestResource: public String::ExternalStringResource { 323 public: 324 static int dispose_count; 325 326 explicit TestResource(uint16_t* data) 327 : data_(data), length_(0) { 328 while (data[length_]) ++length_; 329 } 330 331 ~TestResource() { 332 i::DeleteArray(data_); 333 ++dispose_count; 334 } 335 336 const uint16_t* data() const { 337 return data_; 338 } 339 340 size_t length() const { 341 return length_; 342 } 343 private: 344 uint16_t* data_; 345 size_t length_; 346}; 347 348 349int TestResource::dispose_count = 0; 350 351 352class TestAsciiResource: public String::ExternalAsciiStringResource { 353 public: 354 static int dispose_count; 355 356 explicit TestAsciiResource(const char* data) 357 : data_(data), 358 length_(strlen(data)) { } 359 360 ~TestAsciiResource() { 361 i::DeleteArray(data_); 362 ++dispose_count; 363 } 364 365 const char* data() const { 366 return data_; 367 } 368 369 size_t length() const { 370 return length_; 371 } 372 private: 373 const char* data_; 374 size_t length_; 375}; 376 377 378int TestAsciiResource::dispose_count = 0; 379 380 381THREADED_TEST(ScriptUsingStringResource) { 382 TestResource::dispose_count = 0; 383 const char* c_source = "1 + 2 * 3"; 384 uint16_t* two_byte_source = AsciiToTwoByteString(c_source); 385 { 386 v8::HandleScope scope; 387 LocalContext env; 388 TestResource* resource = new TestResource(two_byte_source); 389 Local<String> source = String::NewExternal(resource); 390 Local<Script> script = Script::Compile(source); 391 Local<Value> value = script->Run(); 392 CHECK(value->IsNumber()); 393 CHECK_EQ(7, value->Int32Value()); 394 CHECK(source->IsExternal()); 395 CHECK_EQ(resource, 396 static_cast<TestResource*>(source->GetExternalStringResource())); 397 i::Heap::CollectAllGarbage(false); 398 CHECK_EQ(0, TestResource::dispose_count); 399 } 400 i::CompilationCache::Clear(); 401 i::Heap::CollectAllGarbage(false); 402 CHECK_EQ(1, TestResource::dispose_count); 403} 404 405 406THREADED_TEST(ScriptUsingAsciiStringResource) { 407 TestAsciiResource::dispose_count = 0; 408 const char* c_source = "1 + 2 * 3"; 409 { 410 v8::HandleScope scope; 411 LocalContext env; 412 Local<String> source = 413 String::NewExternal(new TestAsciiResource(i::StrDup(c_source))); 414 Local<Script> script = Script::Compile(source); 415 Local<Value> value = script->Run(); 416 CHECK(value->IsNumber()); 417 CHECK_EQ(7, value->Int32Value()); 418 i::Heap::CollectAllGarbage(false); 419 CHECK_EQ(0, TestAsciiResource::dispose_count); 420 } 421 i::CompilationCache::Clear(); 422 i::Heap::CollectAllGarbage(false); 423 CHECK_EQ(1, TestAsciiResource::dispose_count); 424} 425 426 427THREADED_TEST(ScriptMakingExternalString) { 428 TestResource::dispose_count = 0; 429 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3"); 430 { 431 v8::HandleScope scope; 432 LocalContext env; 433 Local<String> source = String::New(two_byte_source); 434 // Trigger GCs so that the newly allocated string moves to old gen. 435 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now 436 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now 437 bool success = source->MakeExternal(new TestResource(two_byte_source)); 438 CHECK(success); 439 Local<Script> script = Script::Compile(source); 440 Local<Value> value = script->Run(); 441 CHECK(value->IsNumber()); 442 CHECK_EQ(7, value->Int32Value()); 443 i::Heap::CollectAllGarbage(false); 444 CHECK_EQ(0, TestResource::dispose_count); 445 } 446 i::CompilationCache::Clear(); 447 i::Heap::CollectAllGarbage(false); 448 CHECK_EQ(1, TestResource::dispose_count); 449} 450 451 452THREADED_TEST(ScriptMakingExternalAsciiString) { 453 TestAsciiResource::dispose_count = 0; 454 const char* c_source = "1 + 2 * 3"; 455 { 456 v8::HandleScope scope; 457 LocalContext env; 458 Local<String> source = v8_str(c_source); 459 // Trigger GCs so that the newly allocated string moves to old gen. 460 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now 461 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now 462 bool success = source->MakeExternal( 463 new TestAsciiResource(i::StrDup(c_source))); 464 CHECK(success); 465 Local<Script> script = Script::Compile(source); 466 Local<Value> value = script->Run(); 467 CHECK(value->IsNumber()); 468 CHECK_EQ(7, value->Int32Value()); 469 i::Heap::CollectAllGarbage(false); 470 CHECK_EQ(0, TestAsciiResource::dispose_count); 471 } 472 i::CompilationCache::Clear(); 473 i::Heap::CollectAllGarbage(false); 474 CHECK_EQ(1, TestAsciiResource::dispose_count); 475} 476 477 478TEST(MakingExternalStringConditions) { 479 v8::HandleScope scope; 480 LocalContext env; 481 482 // Free some space in the new space so that we can check freshness. 483 i::Heap::CollectGarbage(i::NEW_SPACE); 484 i::Heap::CollectGarbage(i::NEW_SPACE); 485 486 uint16_t* two_byte_string = AsciiToTwoByteString("small"); 487 Local<String> small_string = String::New(two_byte_string); 488 i::DeleteArray(two_byte_string); 489 490 // We should refuse to externalize newly created small string. 491 CHECK(!small_string->CanMakeExternal()); 492 // Trigger GCs so that the newly allocated string moves to old gen. 493 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now 494 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now 495 // Old space strings should be accepted. 496 CHECK(small_string->CanMakeExternal()); 497 498 two_byte_string = AsciiToTwoByteString("small 2"); 499 small_string = String::New(two_byte_string); 500 i::DeleteArray(two_byte_string); 501 502 // We should refuse externalizing newly created small string. 503 CHECK(!small_string->CanMakeExternal()); 504 for (int i = 0; i < 100; i++) { 505 String::Value value(small_string); 506 } 507 // Frequently used strings should be accepted. 508 CHECK(small_string->CanMakeExternal()); 509 510 const int buf_size = 10 * 1024; 511 char* buf = i::NewArray<char>(buf_size); 512 memset(buf, 'a', buf_size); 513 buf[buf_size - 1] = '\0'; 514 515 two_byte_string = AsciiToTwoByteString(buf); 516 Local<String> large_string = String::New(two_byte_string); 517 i::DeleteArray(buf); 518 i::DeleteArray(two_byte_string); 519 // Large strings should be immediately accepted. 520 CHECK(large_string->CanMakeExternal()); 521} 522 523 524TEST(MakingExternalAsciiStringConditions) { 525 v8::HandleScope scope; 526 LocalContext env; 527 528 // Free some space in the new space so that we can check freshness. 529 i::Heap::CollectGarbage(i::NEW_SPACE); 530 i::Heap::CollectGarbage(i::NEW_SPACE); 531 532 Local<String> small_string = String::New("small"); 533 // We should refuse to externalize newly created small string. 534 CHECK(!small_string->CanMakeExternal()); 535 // Trigger GCs so that the newly allocated string moves to old gen. 536 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now 537 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now 538 // Old space strings should be accepted. 539 CHECK(small_string->CanMakeExternal()); 540 541 small_string = String::New("small 2"); 542 // We should refuse externalizing newly created small string. 543 CHECK(!small_string->CanMakeExternal()); 544 for (int i = 0; i < 100; i++) { 545 String::Value value(small_string); 546 } 547 // Frequently used strings should be accepted. 548 CHECK(small_string->CanMakeExternal()); 549 550 const int buf_size = 10 * 1024; 551 char* buf = i::NewArray<char>(buf_size); 552 memset(buf, 'a', buf_size); 553 buf[buf_size - 1] = '\0'; 554 Local<String> large_string = String::New(buf); 555 i::DeleteArray(buf); 556 // Large strings should be immediately accepted. 557 CHECK(large_string->CanMakeExternal()); 558} 559 560 561THREADED_TEST(UsingExternalString) { 562 { 563 v8::HandleScope scope; 564 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 565 Local<String> string = 566 String::NewExternal(new TestResource(two_byte_string)); 567 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 568 // Trigger GCs so that the newly allocated string moves to old gen. 569 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now 570 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now 571 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring); 572 CHECK(isymbol->IsSymbol()); 573 } 574 i::Heap::CollectAllGarbage(false); 575 i::Heap::CollectAllGarbage(false); 576} 577 578 579THREADED_TEST(UsingExternalAsciiString) { 580 { 581 v8::HandleScope scope; 582 const char* one_byte_string = "test string"; 583 Local<String> string = String::NewExternal( 584 new TestAsciiResource(i::StrDup(one_byte_string))); 585 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 586 // Trigger GCs so that the newly allocated string moves to old gen. 587 i::Heap::CollectGarbage(i::NEW_SPACE); // in survivor space now 588 i::Heap::CollectGarbage(i::NEW_SPACE); // in old gen now 589 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring); 590 CHECK(isymbol->IsSymbol()); 591 } 592 i::Heap::CollectAllGarbage(false); 593 i::Heap::CollectAllGarbage(false); 594} 595 596 597THREADED_TEST(ScavengeExternalString) { 598 TestResource::dispose_count = 0; 599 bool in_new_space = false; 600 { 601 v8::HandleScope scope; 602 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 603 Local<String> string = 604 String::NewExternal(new TestResource(two_byte_string)); 605 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 606 i::Heap::CollectGarbage(i::NEW_SPACE); 607 in_new_space = i::Heap::InNewSpace(*istring); 608 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring)); 609 CHECK_EQ(0, TestResource::dispose_count); 610 } 611 i::Heap::CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); 612 CHECK_EQ(1, TestResource::dispose_count); 613} 614 615 616THREADED_TEST(ScavengeExternalAsciiString) { 617 TestAsciiResource::dispose_count = 0; 618 bool in_new_space = false; 619 { 620 v8::HandleScope scope; 621 const char* one_byte_string = "test string"; 622 Local<String> string = String::NewExternal( 623 new TestAsciiResource(i::StrDup(one_byte_string))); 624 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 625 i::Heap::CollectGarbage(i::NEW_SPACE); 626 in_new_space = i::Heap::InNewSpace(*istring); 627 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring)); 628 CHECK_EQ(0, TestAsciiResource::dispose_count); 629 } 630 i::Heap::CollectGarbage(in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); 631 CHECK_EQ(1, TestAsciiResource::dispose_count); 632} 633 634 635class TestAsciiResourceWithDisposeControl: public TestAsciiResource { 636 public: 637 static int dispose_calls; 638 639 TestAsciiResourceWithDisposeControl(const char* data, bool dispose) 640 : TestAsciiResource(data), 641 dispose_(dispose) { } 642 643 void Dispose() { 644 ++dispose_calls; 645 if (dispose_) delete this; 646 } 647 private: 648 bool dispose_; 649}; 650 651 652int TestAsciiResourceWithDisposeControl::dispose_calls = 0; 653 654 655TEST(ExternalStringWithDisposeHandling) { 656 const char* c_source = "1 + 2 * 3"; 657 658 // Use a stack allocated external string resource allocated object. 659 TestAsciiResource::dispose_count = 0; 660 TestAsciiResourceWithDisposeControl::dispose_calls = 0; 661 TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false); 662 { 663 v8::HandleScope scope; 664 LocalContext env; 665 Local<String> source = String::NewExternal(&res_stack); 666 Local<Script> script = Script::Compile(source); 667 Local<Value> value = script->Run(); 668 CHECK(value->IsNumber()); 669 CHECK_EQ(7, value->Int32Value()); 670 i::Heap::CollectAllGarbage(false); 671 CHECK_EQ(0, TestAsciiResource::dispose_count); 672 } 673 i::CompilationCache::Clear(); 674 i::Heap::CollectAllGarbage(false); 675 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); 676 CHECK_EQ(0, TestAsciiResource::dispose_count); 677 678 // Use a heap allocated external string resource allocated object. 679 TestAsciiResource::dispose_count = 0; 680 TestAsciiResourceWithDisposeControl::dispose_calls = 0; 681 TestAsciiResource* res_heap = 682 new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true); 683 { 684 v8::HandleScope scope; 685 LocalContext env; 686 Local<String> source = String::NewExternal(res_heap); 687 Local<Script> script = Script::Compile(source); 688 Local<Value> value = script->Run(); 689 CHECK(value->IsNumber()); 690 CHECK_EQ(7, value->Int32Value()); 691 i::Heap::CollectAllGarbage(false); 692 CHECK_EQ(0, TestAsciiResource::dispose_count); 693 } 694 i::CompilationCache::Clear(); 695 i::Heap::CollectAllGarbage(false); 696 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); 697 CHECK_EQ(1, TestAsciiResource::dispose_count); 698} 699 700 701THREADED_TEST(StringConcat) { 702 { 703 v8::HandleScope scope; 704 LocalContext env; 705 const char* one_byte_string_1 = "function a_times_t"; 706 const char* two_byte_string_1 = "wo_plus_b(a, b) {return "; 707 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + "; 708 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + "; 709 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 710 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 711 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);"; 712 Local<String> left = v8_str(one_byte_string_1); 713 714 uint16_t* two_byte_source = AsciiToTwoByteString(two_byte_string_1); 715 Local<String> right = String::New(two_byte_source); 716 i::DeleteArray(two_byte_source); 717 718 Local<String> source = String::Concat(left, right); 719 right = String::NewExternal( 720 new TestAsciiResource(i::StrDup(one_byte_extern_1))); 721 source = String::Concat(source, right); 722 right = String::NewExternal( 723 new TestResource(AsciiToTwoByteString(two_byte_extern_1))); 724 source = String::Concat(source, right); 725 right = v8_str(one_byte_string_2); 726 source = String::Concat(source, right); 727 728 two_byte_source = AsciiToTwoByteString(two_byte_string_2); 729 right = String::New(two_byte_source); 730 i::DeleteArray(two_byte_source); 731 732 source = String::Concat(source, right); 733 right = String::NewExternal( 734 new TestResource(AsciiToTwoByteString(two_byte_extern_2))); 735 source = String::Concat(source, right); 736 Local<Script> script = Script::Compile(source); 737 Local<Value> value = script->Run(); 738 CHECK(value->IsNumber()); 739 CHECK_EQ(68, value->Int32Value()); 740 } 741 i::CompilationCache::Clear(); 742 i::Heap::CollectAllGarbage(false); 743 i::Heap::CollectAllGarbage(false); 744} 745 746 747THREADED_TEST(GlobalProperties) { 748 v8::HandleScope scope; 749 LocalContext env; 750 v8::Handle<v8::Object> global = env->Global(); 751 global->Set(v8_str("pi"), v8_num(3.1415926)); 752 Local<Value> pi = global->Get(v8_str("pi")); 753 CHECK_EQ(3.1415926, pi->NumberValue()); 754} 755 756 757static v8::Handle<Value> handle_call(const v8::Arguments& args) { 758 ApiTestFuzzer::Fuzz(); 759 return v8_num(102); 760} 761 762 763static v8::Handle<Value> construct_call(const v8::Arguments& args) { 764 ApiTestFuzzer::Fuzz(); 765 args.This()->Set(v8_str("x"), v8_num(1)); 766 args.This()->Set(v8_str("y"), v8_num(2)); 767 return args.This(); 768} 769 770static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) { 771 ApiTestFuzzer::Fuzz(); 772 return v8_num(239); 773} 774 775 776THREADED_TEST(FunctionTemplate) { 777 v8::HandleScope scope; 778 LocalContext env; 779 { 780 Local<v8::FunctionTemplate> fun_templ = 781 v8::FunctionTemplate::New(handle_call); 782 Local<Function> fun = fun_templ->GetFunction(); 783 env->Global()->Set(v8_str("obj"), fun); 784 Local<Script> script = v8_compile("obj()"); 785 CHECK_EQ(102, script->Run()->Int32Value()); 786 } 787 // Use SetCallHandler to initialize a function template, should work like the 788 // previous one. 789 { 790 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 791 fun_templ->SetCallHandler(handle_call); 792 Local<Function> fun = fun_templ->GetFunction(); 793 env->Global()->Set(v8_str("obj"), fun); 794 Local<Script> script = v8_compile("obj()"); 795 CHECK_EQ(102, script->Run()->Int32Value()); 796 } 797 // Test constructor calls. 798 { 799 Local<v8::FunctionTemplate> fun_templ = 800 v8::FunctionTemplate::New(construct_call); 801 fun_templ->SetClassName(v8_str("funky")); 802 fun_templ->InstanceTemplate()->SetAccessor(v8_str("m"), Return239); 803 Local<Function> fun = fun_templ->GetFunction(); 804 env->Global()->Set(v8_str("obj"), fun); 805 Local<Script> script = v8_compile("var s = new obj(); s.x"); 806 CHECK_EQ(1, script->Run()->Int32Value()); 807 808 Local<Value> result = v8_compile("(new obj()).toString()")->Run(); 809 CHECK_EQ(v8_str("[object funky]"), result); 810 811 result = v8_compile("(new obj()).m")->Run(); 812 CHECK_EQ(239, result->Int32Value()); 813 } 814} 815 816 817static void* expected_ptr; 818static v8::Handle<v8::Value> callback(const v8::Arguments& args) { 819 void* ptr = v8::External::Unwrap(args.Data()); 820 CHECK_EQ(expected_ptr, ptr); 821 return v8::Boolean::New(true); 822} 823 824 825static void TestExternalPointerWrapping() { 826 v8::HandleScope scope; 827 LocalContext env; 828 829 v8::Handle<v8::Value> data = v8::External::Wrap(expected_ptr); 830 831 v8::Handle<v8::Object> obj = v8::Object::New(); 832 obj->Set(v8_str("func"), 833 v8::FunctionTemplate::New(callback, data)->GetFunction()); 834 env->Global()->Set(v8_str("obj"), obj); 835 836 CHECK(CompileRun( 837 "function foo() {\n" 838 " for (var i = 0; i < 13; i++) obj.func();\n" 839 "}\n" 840 "foo(), true")->BooleanValue()); 841} 842 843 844THREADED_TEST(ExternalWrap) { 845 // Check heap allocated object. 846 int* ptr = new int; 847 expected_ptr = ptr; 848 TestExternalPointerWrapping(); 849 delete ptr; 850 851 // Check stack allocated object. 852 int foo; 853 expected_ptr = &foo; 854 TestExternalPointerWrapping(); 855 856 // Check not aligned addresses. 857 const int n = 100; 858 char* s = new char[n]; 859 for (int i = 0; i < n; i++) { 860 expected_ptr = s + i; 861 TestExternalPointerWrapping(); 862 } 863 864 delete[] s; 865 866 // Check several invalid addresses. 867 expected_ptr = reinterpret_cast<void*>(1); 868 TestExternalPointerWrapping(); 869 870 expected_ptr = reinterpret_cast<void*>(0xdeadbeef); 871 TestExternalPointerWrapping(); 872 873 expected_ptr = reinterpret_cast<void*>(0xdeadbeef + 1); 874 TestExternalPointerWrapping(); 875 876#if defined(V8_HOST_ARCH_X64) 877 expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef); 878 TestExternalPointerWrapping(); 879 880 expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef + 1); 881 TestExternalPointerWrapping(); 882#endif 883} 884 885 886THREADED_TEST(FindInstanceInPrototypeChain) { 887 v8::HandleScope scope; 888 LocalContext env; 889 890 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New(); 891 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New(); 892 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New(); 893 derived->Inherit(base); 894 895 Local<v8::Function> base_function = base->GetFunction(); 896 Local<v8::Function> derived_function = derived->GetFunction(); 897 Local<v8::Function> other_function = other->GetFunction(); 898 899 Local<v8::Object> base_instance = base_function->NewInstance(); 900 Local<v8::Object> derived_instance = derived_function->NewInstance(); 901 Local<v8::Object> derived_instance2 = derived_function->NewInstance(); 902 Local<v8::Object> other_instance = other_function->NewInstance(); 903 derived_instance2->Set(v8_str("__proto__"), derived_instance); 904 other_instance->Set(v8_str("__proto__"), derived_instance2); 905 906 // base_instance is only an instance of base. 907 CHECK_EQ(base_instance, 908 base_instance->FindInstanceInPrototypeChain(base)); 909 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty()); 910 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 911 912 // derived_instance is an instance of base and derived. 913 CHECK_EQ(derived_instance, 914 derived_instance->FindInstanceInPrototypeChain(base)); 915 CHECK_EQ(derived_instance, 916 derived_instance->FindInstanceInPrototypeChain(derived)); 917 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 918 919 // other_instance is an instance of other and its immediate 920 // prototype derived_instance2 is an instance of base and derived. 921 // Note, derived_instance is an instance of base and derived too, 922 // but it comes after derived_instance2 in the prototype chain of 923 // other_instance. 924 CHECK_EQ(derived_instance2, 925 other_instance->FindInstanceInPrototypeChain(base)); 926 CHECK_EQ(derived_instance2, 927 other_instance->FindInstanceInPrototypeChain(derived)); 928 CHECK_EQ(other_instance, 929 other_instance->FindInstanceInPrototypeChain(other)); 930} 931 932 933THREADED_TEST(TinyInteger) { 934 v8::HandleScope scope; 935 LocalContext env; 936 int32_t value = 239; 937 Local<v8::Integer> value_obj = v8::Integer::New(value); 938 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 939} 940 941 942THREADED_TEST(BigSmiInteger) { 943 v8::HandleScope scope; 944 LocalContext env; 945 int32_t value = i::Smi::kMaxValue; 946 // We cannot add one to a Smi::kMaxValue without wrapping. 947 if (i::kSmiValueSize < 32) { 948 CHECK(i::Smi::IsValid(value)); 949 CHECK(!i::Smi::IsValid(value + 1)); 950 Local<v8::Integer> value_obj = v8::Integer::New(value); 951 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 952 } 953} 954 955 956THREADED_TEST(BigInteger) { 957 v8::HandleScope scope; 958 LocalContext env; 959 // We cannot add one to a Smi::kMaxValue without wrapping. 960 if (i::kSmiValueSize < 32) { 961 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1. 962 // The code will not be run in that case, due to the "if" guard. 963 int32_t value = 964 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1); 965 CHECK(value > i::Smi::kMaxValue); 966 CHECK(!i::Smi::IsValid(value)); 967 Local<v8::Integer> value_obj = v8::Integer::New(value); 968 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 969 } 970} 971 972 973THREADED_TEST(TinyUnsignedInteger) { 974 v8::HandleScope scope; 975 LocalContext env; 976 uint32_t value = 239; 977 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 978 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 979} 980 981 982THREADED_TEST(BigUnsignedSmiInteger) { 983 v8::HandleScope scope; 984 LocalContext env; 985 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue); 986 CHECK(i::Smi::IsValid(value)); 987 CHECK(!i::Smi::IsValid(value + 1)); 988 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 989 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 990} 991 992 993THREADED_TEST(BigUnsignedInteger) { 994 v8::HandleScope scope; 995 LocalContext env; 996 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1; 997 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue)); 998 CHECK(!i::Smi::IsValid(value)); 999 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 1000 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1001} 1002 1003 1004THREADED_TEST(OutOfSignedRangeUnsignedInteger) { 1005 v8::HandleScope scope; 1006 LocalContext env; 1007 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1; 1008 uint32_t value = INT32_MAX_AS_UINT + 1; 1009 CHECK(value > INT32_MAX_AS_UINT); // No overflow. 1010 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 1011 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1012} 1013 1014 1015THREADED_TEST(Number) { 1016 v8::HandleScope scope; 1017 LocalContext env; 1018 double PI = 3.1415926; 1019 Local<v8::Number> pi_obj = v8::Number::New(PI); 1020 CHECK_EQ(PI, pi_obj->NumberValue()); 1021} 1022 1023 1024THREADED_TEST(ToNumber) { 1025 v8::HandleScope scope; 1026 LocalContext env; 1027 Local<String> str = v8_str("3.1415926"); 1028 CHECK_EQ(3.1415926, str->NumberValue()); 1029 v8::Handle<v8::Boolean> t = v8::True(); 1030 CHECK_EQ(1.0, t->NumberValue()); 1031 v8::Handle<v8::Boolean> f = v8::False(); 1032 CHECK_EQ(0.0, f->NumberValue()); 1033} 1034 1035 1036THREADED_TEST(Date) { 1037 v8::HandleScope scope; 1038 LocalContext env; 1039 double PI = 3.1415926; 1040 Local<Value> date_obj = v8::Date::New(PI); 1041 CHECK_EQ(3.0, date_obj->NumberValue()); 1042} 1043 1044 1045THREADED_TEST(Boolean) { 1046 v8::HandleScope scope; 1047 LocalContext env; 1048 v8::Handle<v8::Boolean> t = v8::True(); 1049 CHECK(t->Value()); 1050 v8::Handle<v8::Boolean> f = v8::False(); 1051 CHECK(!f->Value()); 1052 v8::Handle<v8::Primitive> u = v8::Undefined(); 1053 CHECK(!u->BooleanValue()); 1054 v8::Handle<v8::Primitive> n = v8::Null(); 1055 CHECK(!n->BooleanValue()); 1056 v8::Handle<String> str1 = v8_str(""); 1057 CHECK(!str1->BooleanValue()); 1058 v8::Handle<String> str2 = v8_str("x"); 1059 CHECK(str2->BooleanValue()); 1060 CHECK(!v8::Number::New(0)->BooleanValue()); 1061 CHECK(v8::Number::New(-1)->BooleanValue()); 1062 CHECK(v8::Number::New(1)->BooleanValue()); 1063 CHECK(v8::Number::New(42)->BooleanValue()); 1064 CHECK(!v8_compile("NaN")->Run()->BooleanValue()); 1065} 1066 1067 1068static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) { 1069 ApiTestFuzzer::Fuzz(); 1070 return v8_num(13.4); 1071} 1072 1073 1074static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) { 1075 ApiTestFuzzer::Fuzz(); 1076 return v8_num(876); 1077} 1078 1079 1080THREADED_TEST(GlobalPrototype) { 1081 v8::HandleScope scope; 1082 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 1083 func_templ->PrototypeTemplate()->Set( 1084 "dummy", 1085 v8::FunctionTemplate::New(DummyCallHandler)); 1086 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate(); 1087 templ->Set("x", v8_num(200)); 1088 templ->SetAccessor(v8_str("m"), GetM); 1089 LocalContext env(0, templ); 1090 v8::Handle<v8::Object> obj = env->Global(); 1091 v8::Handle<Script> script = v8_compile("dummy()"); 1092 v8::Handle<Value> result = script->Run(); 1093 CHECK_EQ(13.4, result->NumberValue()); 1094 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value()); 1095 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value()); 1096} 1097 1098 1099THREADED_TEST(ObjectTemplate) { 1100 v8::HandleScope scope; 1101 Local<ObjectTemplate> templ1 = ObjectTemplate::New(); 1102 templ1->Set("x", v8_num(10)); 1103 templ1->Set("y", v8_num(13)); 1104 LocalContext env; 1105 Local<v8::Object> instance1 = templ1->NewInstance(); 1106 env->Global()->Set(v8_str("p"), instance1); 1107 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue()); 1108 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue()); 1109 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); 1110 fun->PrototypeTemplate()->Set("nirk", v8_num(123)); 1111 Local<ObjectTemplate> templ2 = fun->InstanceTemplate(); 1112 templ2->Set("a", v8_num(12)); 1113 templ2->Set("b", templ1); 1114 Local<v8::Object> instance2 = templ2->NewInstance(); 1115 env->Global()->Set(v8_str("q"), instance2); 1116 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue()); 1117 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue()); 1118 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue()); 1119 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue()); 1120} 1121 1122 1123static v8::Handle<Value> GetFlabby(const v8::Arguments& args) { 1124 ApiTestFuzzer::Fuzz(); 1125 return v8_num(17.2); 1126} 1127 1128 1129static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) { 1130 ApiTestFuzzer::Fuzz(); 1131 return v8_num(15.2); 1132} 1133 1134 1135THREADED_TEST(DescriptorInheritance) { 1136 v8::HandleScope scope; 1137 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New(); 1138 super->PrototypeTemplate()->Set("flabby", 1139 v8::FunctionTemplate::New(GetFlabby)); 1140 super->PrototypeTemplate()->Set("PI", v8_num(3.14)); 1141 1142 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd); 1143 1144 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New(); 1145 base1->Inherit(super); 1146 base1->PrototypeTemplate()->Set("v1", v8_num(20.1)); 1147 1148 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New(); 1149 base2->Inherit(super); 1150 base2->PrototypeTemplate()->Set("v2", v8_num(10.1)); 1151 1152 LocalContext env; 1153 1154 env->Global()->Set(v8_str("s"), super->GetFunction()); 1155 env->Global()->Set(v8_str("base1"), base1->GetFunction()); 1156 env->Global()->Set(v8_str("base2"), base2->GetFunction()); 1157 1158 // Checks right __proto__ chain. 1159 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue()); 1160 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue()); 1161 1162 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue()); 1163 1164 // Instance accessor should not be visible on function object or its prototype 1165 CHECK(CompileRun("s.knurd == undefined")->BooleanValue()); 1166 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue()); 1167 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue()); 1168 1169 env->Global()->Set(v8_str("obj"), 1170 base1->GetFunction()->NewInstance()); 1171 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue()); 1172 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue()); 1173 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue()); 1174 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue()); 1175 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue()); 1176 1177 env->Global()->Set(v8_str("obj2"), 1178 base2->GetFunction()->NewInstance()); 1179 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue()); 1180 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue()); 1181 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue()); 1182 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue()); 1183 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue()); 1184 1185 // base1 and base2 cannot cross reference to each's prototype 1186 CHECK(v8_compile("obj.v2")->Run()->IsUndefined()); 1187 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined()); 1188} 1189 1190 1191int echo_named_call_count; 1192 1193 1194static v8::Handle<Value> EchoNamedProperty(Local<String> name, 1195 const AccessorInfo& info) { 1196 ApiTestFuzzer::Fuzz(); 1197 CHECK_EQ(v8_str("data"), info.Data()); 1198 echo_named_call_count++; 1199 return name; 1200} 1201 1202 1203THREADED_TEST(NamedPropertyHandlerGetter) { 1204 echo_named_call_count = 0; 1205 v8::HandleScope scope; 1206 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1207 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty, 1208 0, 0, 0, 0, 1209 v8_str("data")); 1210 LocalContext env; 1211 env->Global()->Set(v8_str("obj"), 1212 templ->GetFunction()->NewInstance()); 1213 CHECK_EQ(echo_named_call_count, 0); 1214 v8_compile("obj.x")->Run(); 1215 CHECK_EQ(echo_named_call_count, 1); 1216 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;"; 1217 v8::Handle<Value> str = CompileRun(code); 1218 String::AsciiValue value(str); 1219 CHECK_EQ(*value, "oddlepoddle"); 1220 // Check default behavior 1221 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10); 1222 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue()); 1223 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue()); 1224} 1225 1226 1227int echo_indexed_call_count = 0; 1228 1229 1230static v8::Handle<Value> EchoIndexedProperty(uint32_t index, 1231 const AccessorInfo& info) { 1232 ApiTestFuzzer::Fuzz(); 1233 CHECK_EQ(v8_num(637), info.Data()); 1234 echo_indexed_call_count++; 1235 return v8_num(index); 1236} 1237 1238 1239THREADED_TEST(IndexedPropertyHandlerGetter) { 1240 v8::HandleScope scope; 1241 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1242 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty, 1243 0, 0, 0, 0, 1244 v8_num(637)); 1245 LocalContext env; 1246 env->Global()->Set(v8_str("obj"), 1247 templ->GetFunction()->NewInstance()); 1248 Local<Script> script = v8_compile("obj[900]"); 1249 CHECK_EQ(script->Run()->Int32Value(), 900); 1250} 1251 1252 1253v8::Handle<v8::Object> bottom; 1254 1255static v8::Handle<Value> CheckThisIndexedPropertyHandler( 1256 uint32_t index, 1257 const AccessorInfo& info) { 1258 ApiTestFuzzer::Fuzz(); 1259 CHECK(info.This()->Equals(bottom)); 1260 return v8::Handle<Value>(); 1261} 1262 1263static v8::Handle<Value> CheckThisNamedPropertyHandler( 1264 Local<String> name, 1265 const AccessorInfo& info) { 1266 ApiTestFuzzer::Fuzz(); 1267 CHECK(info.This()->Equals(bottom)); 1268 return v8::Handle<Value>(); 1269} 1270 1271 1272v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index, 1273 Local<Value> value, 1274 const AccessorInfo& info) { 1275 ApiTestFuzzer::Fuzz(); 1276 CHECK(info.This()->Equals(bottom)); 1277 return v8::Handle<Value>(); 1278} 1279 1280 1281v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property, 1282 Local<Value> value, 1283 const AccessorInfo& info) { 1284 ApiTestFuzzer::Fuzz(); 1285 CHECK(info.This()->Equals(bottom)); 1286 return v8::Handle<Value>(); 1287} 1288 1289v8::Handle<v8::Integer> CheckThisIndexedPropertyQuery( 1290 uint32_t index, 1291 const AccessorInfo& info) { 1292 ApiTestFuzzer::Fuzz(); 1293 CHECK(info.This()->Equals(bottom)); 1294 return v8::Handle<v8::Integer>(); 1295} 1296 1297 1298v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property, 1299 const AccessorInfo& info) { 1300 ApiTestFuzzer::Fuzz(); 1301 CHECK(info.This()->Equals(bottom)); 1302 return v8::Handle<v8::Integer>(); 1303} 1304 1305 1306v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter( 1307 uint32_t index, 1308 const AccessorInfo& info) { 1309 ApiTestFuzzer::Fuzz(); 1310 CHECK(info.This()->Equals(bottom)); 1311 return v8::Handle<v8::Boolean>(); 1312} 1313 1314 1315v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter( 1316 Local<String> property, 1317 const AccessorInfo& info) { 1318 ApiTestFuzzer::Fuzz(); 1319 CHECK(info.This()->Equals(bottom)); 1320 return v8::Handle<v8::Boolean>(); 1321} 1322 1323 1324v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator( 1325 const AccessorInfo& info) { 1326 ApiTestFuzzer::Fuzz(); 1327 CHECK(info.This()->Equals(bottom)); 1328 return v8::Handle<v8::Array>(); 1329} 1330 1331 1332v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator( 1333 const AccessorInfo& info) { 1334 ApiTestFuzzer::Fuzz(); 1335 CHECK(info.This()->Equals(bottom)); 1336 return v8::Handle<v8::Array>(); 1337} 1338 1339 1340THREADED_TEST(PropertyHandlerInPrototype) { 1341 v8::HandleScope scope; 1342 LocalContext env; 1343 1344 // Set up a prototype chain with three interceptors. 1345 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1346 templ->InstanceTemplate()->SetIndexedPropertyHandler( 1347 CheckThisIndexedPropertyHandler, 1348 CheckThisIndexedPropertySetter, 1349 CheckThisIndexedPropertyQuery, 1350 CheckThisIndexedPropertyDeleter, 1351 CheckThisIndexedPropertyEnumerator); 1352 1353 templ->InstanceTemplate()->SetNamedPropertyHandler( 1354 CheckThisNamedPropertyHandler, 1355 CheckThisNamedPropertySetter, 1356 CheckThisNamedPropertyQuery, 1357 CheckThisNamedPropertyDeleter, 1358 CheckThisNamedPropertyEnumerator); 1359 1360 bottom = templ->GetFunction()->NewInstance(); 1361 Local<v8::Object> top = templ->GetFunction()->NewInstance(); 1362 Local<v8::Object> middle = templ->GetFunction()->NewInstance(); 1363 1364 bottom->Set(v8_str("__proto__"), middle); 1365 middle->Set(v8_str("__proto__"), top); 1366 env->Global()->Set(v8_str("obj"), bottom); 1367 1368 // Indexed and named get. 1369 Script::Compile(v8_str("obj[0]"))->Run(); 1370 Script::Compile(v8_str("obj.x"))->Run(); 1371 1372 // Indexed and named set. 1373 Script::Compile(v8_str("obj[1] = 42"))->Run(); 1374 Script::Compile(v8_str("obj.y = 42"))->Run(); 1375 1376 // Indexed and named query. 1377 Script::Compile(v8_str("0 in obj"))->Run(); 1378 Script::Compile(v8_str("'x' in obj"))->Run(); 1379 1380 // Indexed and named deleter. 1381 Script::Compile(v8_str("delete obj[0]"))->Run(); 1382 Script::Compile(v8_str("delete obj.x"))->Run(); 1383 1384 // Enumerators. 1385 Script::Compile(v8_str("for (var p in obj) ;"))->Run(); 1386} 1387 1388 1389static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key, 1390 const AccessorInfo& info) { 1391 ApiTestFuzzer::Fuzz(); 1392 if (v8_str("pre")->Equals(key)) { 1393 return v8_str("PrePropertyHandler: pre"); 1394 } 1395 return v8::Handle<String>(); 1396} 1397 1398 1399static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key, 1400 const AccessorInfo&) { 1401 if (v8_str("pre")->Equals(key)) { 1402 return v8::Integer::New(v8::None); 1403 } 1404 1405 return v8::Handle<v8::Integer>(); // do not intercept the call 1406} 1407 1408 1409THREADED_TEST(PrePropertyHandler) { 1410 v8::HandleScope scope; 1411 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(); 1412 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet, 1413 0, 1414 PrePropertyHandlerQuery); 1415 LocalContext env(NULL, desc->InstanceTemplate()); 1416 Script::Compile(v8_str( 1417 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run(); 1418 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run(); 1419 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre); 1420 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run(); 1421 CHECK_EQ(v8_str("Object: on"), result_on); 1422 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run(); 1423 CHECK(result_post.IsEmpty()); 1424} 1425 1426 1427THREADED_TEST(UndefinedIsNotEnumerable) { 1428 v8::HandleScope scope; 1429 LocalContext env; 1430 v8::Handle<Value> result = Script::Compile(v8_str( 1431 "this.propertyIsEnumerable(undefined)"))->Run(); 1432 CHECK(result->IsFalse()); 1433} 1434 1435 1436v8::Handle<Script> call_recursively_script; 1437static const int kTargetRecursionDepth = 200; // near maximum 1438 1439 1440static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) { 1441 ApiTestFuzzer::Fuzz(); 1442 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1443 if (depth == kTargetRecursionDepth) return v8::Undefined(); 1444 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1445 return call_recursively_script->Run(); 1446} 1447 1448 1449static v8::Handle<Value> CallFunctionRecursivelyCall( 1450 const v8::Arguments& args) { 1451 ApiTestFuzzer::Fuzz(); 1452 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1453 if (depth == kTargetRecursionDepth) { 1454 printf("[depth = %d]\n", depth); 1455 return v8::Undefined(); 1456 } 1457 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1458 v8::Handle<Value> function = 1459 args.This()->Get(v8_str("callFunctionRecursively")); 1460 return function.As<Function>()->Call(args.This(), 0, NULL); 1461} 1462 1463 1464THREADED_TEST(DeepCrossLanguageRecursion) { 1465 v8::HandleScope scope; 1466 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 1467 global->Set(v8_str("callScriptRecursively"), 1468 v8::FunctionTemplate::New(CallScriptRecursivelyCall)); 1469 global->Set(v8_str("callFunctionRecursively"), 1470 v8::FunctionTemplate::New(CallFunctionRecursivelyCall)); 1471 LocalContext env(NULL, global); 1472 1473 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1474 call_recursively_script = v8_compile("callScriptRecursively()"); 1475 v8::Handle<Value> result = call_recursively_script->Run(); 1476 call_recursively_script = v8::Handle<Script>(); 1477 1478 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1479 Script::Compile(v8_str("callFunctionRecursively()"))->Run(); 1480} 1481 1482 1483static v8::Handle<Value> 1484 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) { 1485 ApiTestFuzzer::Fuzz(); 1486 return v8::ThrowException(key); 1487} 1488 1489 1490static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key, 1491 Local<Value>, 1492 const AccessorInfo&) { 1493 v8::ThrowException(key); 1494 return v8::Undefined(); // not the same as v8::Handle<v8::Value>() 1495} 1496 1497 1498THREADED_TEST(CallbackExceptionRegression) { 1499 v8::HandleScope scope; 1500 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 1501 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet, 1502 ThrowingPropertyHandlerSet); 1503 LocalContext env; 1504 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 1505 v8::Handle<Value> otto = Script::Compile(v8_str( 1506 "try { with (obj) { otto; } } catch (e) { e; }"))->Run(); 1507 CHECK_EQ(v8_str("otto"), otto); 1508 v8::Handle<Value> netto = Script::Compile(v8_str( 1509 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run(); 1510 CHECK_EQ(v8_str("netto"), netto); 1511} 1512 1513 1514THREADED_TEST(FunctionPrototype) { 1515 v8::HandleScope scope; 1516 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New(); 1517 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321)); 1518 LocalContext env; 1519 env->Global()->Set(v8_str("Foo"), Foo->GetFunction()); 1520 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak")); 1521 CHECK_EQ(script->Run()->Int32Value(), 321); 1522} 1523 1524 1525THREADED_TEST(InternalFields) { 1526 v8::HandleScope scope; 1527 LocalContext env; 1528 1529 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1530 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1531 instance_templ->SetInternalFieldCount(1); 1532 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1533 CHECK_EQ(1, obj->InternalFieldCount()); 1534 CHECK(obj->GetInternalField(0)->IsUndefined()); 1535 obj->SetInternalField(0, v8_num(17)); 1536 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value()); 1537} 1538 1539 1540THREADED_TEST(GlobalObjectInternalFields) { 1541 v8::HandleScope scope; 1542 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 1543 global_template->SetInternalFieldCount(1); 1544 LocalContext env(NULL, global_template); 1545 v8::Handle<v8::Object> global_proxy = env->Global(); 1546 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); 1547 CHECK_EQ(1, global->InternalFieldCount()); 1548 CHECK(global->GetInternalField(0)->IsUndefined()); 1549 global->SetInternalField(0, v8_num(17)); 1550 CHECK_EQ(17, global->GetInternalField(0)->Int32Value()); 1551} 1552 1553 1554THREADED_TEST(InternalFieldsNativePointers) { 1555 v8::HandleScope scope; 1556 LocalContext env; 1557 1558 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1559 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1560 instance_templ->SetInternalFieldCount(1); 1561 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1562 CHECK_EQ(1, obj->InternalFieldCount()); 1563 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1564 1565 char* data = new char[100]; 1566 1567 void* aligned = data; 1568 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1)); 1569 void* unaligned = data + 1; 1570 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1)); 1571 1572 // Check reading and writing aligned pointers. 1573 obj->SetPointerInInternalField(0, aligned); 1574 i::Heap::CollectAllGarbage(false); 1575 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1576 1577 // Check reading and writing unaligned pointers. 1578 obj->SetPointerInInternalField(0, unaligned); 1579 i::Heap::CollectAllGarbage(false); 1580 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1581 1582 delete[] data; 1583} 1584 1585 1586THREADED_TEST(InternalFieldsNativePointersAndExternal) { 1587 v8::HandleScope scope; 1588 LocalContext env; 1589 1590 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1591 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1592 instance_templ->SetInternalFieldCount(1); 1593 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1594 CHECK_EQ(1, obj->InternalFieldCount()); 1595 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1596 1597 char* data = new char[100]; 1598 1599 void* aligned = data; 1600 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(aligned) & 0x1)); 1601 void* unaligned = data + 1; 1602 CHECK_EQ(1, static_cast<int>(reinterpret_cast<uintptr_t>(unaligned) & 0x1)); 1603 1604 obj->SetPointerInInternalField(0, aligned); 1605 i::Heap::CollectAllGarbage(false); 1606 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0))); 1607 1608 obj->SetPointerInInternalField(0, unaligned); 1609 i::Heap::CollectAllGarbage(false); 1610 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0))); 1611 1612 obj->SetInternalField(0, v8::External::Wrap(aligned)); 1613 i::Heap::CollectAllGarbage(false); 1614 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1615 1616 obj->SetInternalField(0, v8::External::Wrap(unaligned)); 1617 i::Heap::CollectAllGarbage(false); 1618 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1619 1620 delete[] data; 1621} 1622 1623 1624THREADED_TEST(IdentityHash) { 1625 v8::HandleScope scope; 1626 LocalContext env; 1627 1628 // Ensure that the test starts with an fresh heap to test whether the hash 1629 // code is based on the address. 1630 i::Heap::CollectAllGarbage(false); 1631 Local<v8::Object> obj = v8::Object::New(); 1632 int hash = obj->GetIdentityHash(); 1633 int hash1 = obj->GetIdentityHash(); 1634 CHECK_EQ(hash, hash1); 1635 int hash2 = v8::Object::New()->GetIdentityHash(); 1636 // Since the identity hash is essentially a random number two consecutive 1637 // objects should not be assigned the same hash code. If the test below fails 1638 // the random number generator should be evaluated. 1639 CHECK_NE(hash, hash2); 1640 i::Heap::CollectAllGarbage(false); 1641 int hash3 = v8::Object::New()->GetIdentityHash(); 1642 // Make sure that the identity hash is not based on the initial address of 1643 // the object alone. If the test below fails the random number generator 1644 // should be evaluated. 1645 CHECK_NE(hash, hash3); 1646 int hash4 = obj->GetIdentityHash(); 1647 CHECK_EQ(hash, hash4); 1648} 1649 1650 1651THREADED_TEST(HiddenProperties) { 1652 v8::HandleScope scope; 1653 LocalContext env; 1654 1655 v8::Local<v8::Object> obj = v8::Object::New(); 1656 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 1657 v8::Local<v8::String> empty = v8_str(""); 1658 v8::Local<v8::String> prop_name = v8_str("prop_name"); 1659 1660 i::Heap::CollectAllGarbage(false); 1661 1662 // Make sure delete of a non-existent hidden value works 1663 CHECK(obj->DeleteHiddenValue(key)); 1664 1665 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503))); 1666 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value()); 1667 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002))); 1668 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1669 1670 i::Heap::CollectAllGarbage(false); 1671 1672 // Make sure we do not find the hidden property. 1673 CHECK(!obj->Has(empty)); 1674 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1675 CHECK(obj->Get(empty)->IsUndefined()); 1676 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1677 CHECK(obj->Set(empty, v8::Integer::New(2003))); 1678 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1679 CHECK_EQ(2003, obj->Get(empty)->Int32Value()); 1680 1681 i::Heap::CollectAllGarbage(false); 1682 1683 // Add another property and delete it afterwards to force the object in 1684 // slow case. 1685 CHECK(obj->Set(prop_name, v8::Integer::New(2008))); 1686 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1687 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value()); 1688 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1689 CHECK(obj->Delete(prop_name)); 1690 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1691 1692 i::Heap::CollectAllGarbage(false); 1693 1694 CHECK(obj->DeleteHiddenValue(key)); 1695 CHECK(obj->GetHiddenValue(key).IsEmpty()); 1696} 1697 1698 1699static bool interceptor_for_hidden_properties_called; 1700static v8::Handle<Value> InterceptorForHiddenProperties( 1701 Local<String> name, const AccessorInfo& info) { 1702 interceptor_for_hidden_properties_called = true; 1703 return v8::Handle<Value>(); 1704} 1705 1706 1707THREADED_TEST(HiddenPropertiesWithInterceptors) { 1708 v8::HandleScope scope; 1709 LocalContext context; 1710 1711 interceptor_for_hidden_properties_called = false; 1712 1713 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 1714 1715 // Associate an interceptor with an object and start setting hidden values. 1716 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 1717 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 1718 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties); 1719 Local<v8::Function> function = fun_templ->GetFunction(); 1720 Local<v8::Object> obj = function->NewInstance(); 1721 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302))); 1722 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value()); 1723 CHECK(!interceptor_for_hidden_properties_called); 1724} 1725 1726 1727THREADED_TEST(External) { 1728 v8::HandleScope scope; 1729 int x = 3; 1730 Local<v8::External> ext = v8::External::New(&x); 1731 LocalContext env; 1732 env->Global()->Set(v8_str("ext"), ext); 1733 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run(); 1734 v8::Handle<v8::External> reext = reext_obj.As<v8::External>(); 1735 int* ptr = static_cast<int*>(reext->Value()); 1736 CHECK_EQ(x, 3); 1737 *ptr = 10; 1738 CHECK_EQ(x, 10); 1739 1740 // Make sure unaligned pointers are wrapped properly. 1741 char* data = i::StrDup("0123456789"); 1742 Local<v8::Value> zero = v8::External::Wrap(&data[0]); 1743 Local<v8::Value> one = v8::External::Wrap(&data[1]); 1744 Local<v8::Value> two = v8::External::Wrap(&data[2]); 1745 Local<v8::Value> three = v8::External::Wrap(&data[3]); 1746 1747 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero)); 1748 CHECK_EQ('0', *char_ptr); 1749 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one)); 1750 CHECK_EQ('1', *char_ptr); 1751 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two)); 1752 CHECK_EQ('2', *char_ptr); 1753 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three)); 1754 CHECK_EQ('3', *char_ptr); 1755 i::DeleteArray(data); 1756} 1757 1758 1759THREADED_TEST(GlobalHandle) { 1760 v8::Persistent<String> global; 1761 { 1762 v8::HandleScope scope; 1763 Local<String> str = v8_str("str"); 1764 global = v8::Persistent<String>::New(str); 1765 } 1766 CHECK_EQ(global->Length(), 3); 1767 global.Dispose(); 1768} 1769 1770 1771THREADED_TEST(ScriptException) { 1772 v8::HandleScope scope; 1773 LocalContext env; 1774 Local<Script> script = Script::Compile(v8_str("throw 'panama!';")); 1775 v8::TryCatch try_catch; 1776 Local<Value> result = script->Run(); 1777 CHECK(result.IsEmpty()); 1778 CHECK(try_catch.HasCaught()); 1779 String::AsciiValue exception_value(try_catch.Exception()); 1780 CHECK_EQ(*exception_value, "panama!"); 1781} 1782 1783 1784bool message_received; 1785 1786 1787static void check_message(v8::Handle<v8::Message> message, 1788 v8::Handle<Value> data) { 1789 CHECK_EQ(5.76, data->NumberValue()); 1790 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue()); 1791 CHECK_EQ(7.56, message->GetScriptData()->NumberValue()); 1792 message_received = true; 1793} 1794 1795 1796THREADED_TEST(MessageHandlerData) { 1797 message_received = false; 1798 v8::HandleScope scope; 1799 CHECK(!message_received); 1800 v8::V8::AddMessageListener(check_message, v8_num(5.76)); 1801 LocalContext context; 1802 v8::ScriptOrigin origin = 1803 v8::ScriptOrigin(v8_str("6.75")); 1804 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"), 1805 &origin); 1806 script->SetData(v8_str("7.56")); 1807 script->Run(); 1808 CHECK(message_received); 1809 // clear out the message listener 1810 v8::V8::RemoveMessageListeners(check_message); 1811} 1812 1813 1814THREADED_TEST(GetSetProperty) { 1815 v8::HandleScope scope; 1816 LocalContext context; 1817 context->Global()->Set(v8_str("foo"), v8_num(14)); 1818 context->Global()->Set(v8_str("12"), v8_num(92)); 1819 context->Global()->Set(v8::Integer::New(16), v8_num(32)); 1820 context->Global()->Set(v8_num(13), v8_num(56)); 1821 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run(); 1822 CHECK_EQ(14, foo->Int32Value()); 1823 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run(); 1824 CHECK_EQ(92, twelve->Int32Value()); 1825 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run(); 1826 CHECK_EQ(32, sixteen->Int32Value()); 1827 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run(); 1828 CHECK_EQ(56, thirteen->Int32Value()); 1829 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value()); 1830 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value()); 1831 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value()); 1832 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value()); 1833 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value()); 1834 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value()); 1835 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value()); 1836 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value()); 1837 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value()); 1838} 1839 1840 1841THREADED_TEST(PropertyAttributes) { 1842 v8::HandleScope scope; 1843 LocalContext context; 1844 // read-only 1845 Local<String> prop = v8_str("read_only"); 1846 context->Global()->Set(prop, v8_num(7), v8::ReadOnly); 1847 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1848 Script::Compile(v8_str("read_only = 9"))->Run(); 1849 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1850 context->Global()->Set(prop, v8_num(10)); 1851 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1852 // dont-delete 1853 prop = v8_str("dont_delete"); 1854 context->Global()->Set(prop, v8_num(13), v8::DontDelete); 1855 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 1856 Script::Compile(v8_str("delete dont_delete"))->Run(); 1857 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 1858} 1859 1860 1861THREADED_TEST(Array) { 1862 v8::HandleScope scope; 1863 LocalContext context; 1864 Local<v8::Array> array = v8::Array::New(); 1865 CHECK_EQ(0, array->Length()); 1866 CHECK(array->Get(0)->IsUndefined()); 1867 CHECK(!array->Has(0)); 1868 CHECK(array->Get(100)->IsUndefined()); 1869 CHECK(!array->Has(100)); 1870 array->Set(2, v8_num(7)); 1871 CHECK_EQ(3, array->Length()); 1872 CHECK(!array->Has(0)); 1873 CHECK(!array->Has(1)); 1874 CHECK(array->Has(2)); 1875 CHECK_EQ(7, array->Get(2)->Int32Value()); 1876 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run(); 1877 Local<v8::Array> arr = obj.As<v8::Array>(); 1878 CHECK_EQ(3, arr->Length()); 1879 CHECK_EQ(1, arr->Get(0)->Int32Value()); 1880 CHECK_EQ(2, arr->Get(1)->Int32Value()); 1881 CHECK_EQ(3, arr->Get(2)->Int32Value()); 1882} 1883 1884 1885v8::Handle<Value> HandleF(const v8::Arguments& args) { 1886 v8::HandleScope scope; 1887 ApiTestFuzzer::Fuzz(); 1888 Local<v8::Array> result = v8::Array::New(args.Length()); 1889 for (int i = 0; i < args.Length(); i++) 1890 result->Set(i, args[i]); 1891 return scope.Close(result); 1892} 1893 1894 1895THREADED_TEST(Vector) { 1896 v8::HandleScope scope; 1897 Local<ObjectTemplate> global = ObjectTemplate::New(); 1898 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF)); 1899 LocalContext context(0, global); 1900 1901 const char* fun = "f()"; 1902 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>(); 1903 CHECK_EQ(0, a0->Length()); 1904 1905 const char* fun2 = "f(11)"; 1906 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>(); 1907 CHECK_EQ(1, a1->Length()); 1908 CHECK_EQ(11, a1->Get(0)->Int32Value()); 1909 1910 const char* fun3 = "f(12, 13)"; 1911 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>(); 1912 CHECK_EQ(2, a2->Length()); 1913 CHECK_EQ(12, a2->Get(0)->Int32Value()); 1914 CHECK_EQ(13, a2->Get(1)->Int32Value()); 1915 1916 const char* fun4 = "f(14, 15, 16)"; 1917 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>(); 1918 CHECK_EQ(3, a3->Length()); 1919 CHECK_EQ(14, a3->Get(0)->Int32Value()); 1920 CHECK_EQ(15, a3->Get(1)->Int32Value()); 1921 CHECK_EQ(16, a3->Get(2)->Int32Value()); 1922 1923 const char* fun5 = "f(17, 18, 19, 20)"; 1924 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>(); 1925 CHECK_EQ(4, a4->Length()); 1926 CHECK_EQ(17, a4->Get(0)->Int32Value()); 1927 CHECK_EQ(18, a4->Get(1)->Int32Value()); 1928 CHECK_EQ(19, a4->Get(2)->Int32Value()); 1929 CHECK_EQ(20, a4->Get(3)->Int32Value()); 1930} 1931 1932 1933THREADED_TEST(FunctionCall) { 1934 v8::HandleScope scope; 1935 LocalContext context; 1936 CompileRun( 1937 "function Foo() {" 1938 " var result = [];" 1939 " for (var i = 0; i < arguments.length; i++) {" 1940 " result.push(arguments[i]);" 1941 " }" 1942 " return result;" 1943 "}"); 1944 Local<Function> Foo = 1945 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 1946 1947 v8::Handle<Value>* args0 = NULL; 1948 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0)); 1949 CHECK_EQ(0, a0->Length()); 1950 1951 v8::Handle<Value> args1[] = { v8_num(1.1) }; 1952 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1)); 1953 CHECK_EQ(1, a1->Length()); 1954 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 1955 1956 v8::Handle<Value> args2[] = { v8_num(2.2), 1957 v8_num(3.3) }; 1958 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2)); 1959 CHECK_EQ(2, a2->Length()); 1960 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 1961 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 1962 1963 v8::Handle<Value> args3[] = { v8_num(4.4), 1964 v8_num(5.5), 1965 v8_num(6.6) }; 1966 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3)); 1967 CHECK_EQ(3, a3->Length()); 1968 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 1969 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 1970 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 1971 1972 v8::Handle<Value> args4[] = { v8_num(7.7), 1973 v8_num(8.8), 1974 v8_num(9.9), 1975 v8_num(10.11) }; 1976 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4)); 1977 CHECK_EQ(4, a4->Length()); 1978 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 1979 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 1980 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 1981 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 1982} 1983 1984 1985static const char* js_code_causing_out_of_memory = 1986 "var a = new Array(); while(true) a.push(a);"; 1987 1988 1989// These tests run for a long time and prevent us from running tests 1990// that come after them so they cannot run in parallel. 1991TEST(OutOfMemory) { 1992 // It's not possible to read a snapshot into a heap with different dimensions. 1993 if (i::Snapshot::IsEnabled()) return; 1994 // Set heap limits. 1995 static const int K = 1024; 1996 v8::ResourceConstraints constraints; 1997 constraints.set_max_young_space_size(256 * K); 1998 constraints.set_max_old_space_size(4 * K * K); 1999 v8::SetResourceConstraints(&constraints); 2000 2001 // Execute a script that causes out of memory. 2002 v8::HandleScope scope; 2003 LocalContext context; 2004 v8::V8::IgnoreOutOfMemoryException(); 2005 Local<Script> script = 2006 Script::Compile(String::New(js_code_causing_out_of_memory)); 2007 Local<Value> result = script->Run(); 2008 2009 // Check for out of memory state. 2010 CHECK(result.IsEmpty()); 2011 CHECK(context->HasOutOfMemoryException()); 2012} 2013 2014 2015v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) { 2016 ApiTestFuzzer::Fuzz(); 2017 2018 v8::HandleScope scope; 2019 LocalContext context; 2020 Local<Script> script = 2021 Script::Compile(String::New(js_code_causing_out_of_memory)); 2022 Local<Value> result = script->Run(); 2023 2024 // Check for out of memory state. 2025 CHECK(result.IsEmpty()); 2026 CHECK(context->HasOutOfMemoryException()); 2027 2028 return result; 2029} 2030 2031 2032TEST(OutOfMemoryNested) { 2033 // It's not possible to read a snapshot into a heap with different dimensions. 2034 if (i::Snapshot::IsEnabled()) return; 2035 // Set heap limits. 2036 static const int K = 1024; 2037 v8::ResourceConstraints constraints; 2038 constraints.set_max_young_space_size(256 * K); 2039 constraints.set_max_old_space_size(4 * K * K); 2040 v8::SetResourceConstraints(&constraints); 2041 2042 v8::HandleScope scope; 2043 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2044 templ->Set(v8_str("ProvokeOutOfMemory"), 2045 v8::FunctionTemplate::New(ProvokeOutOfMemory)); 2046 LocalContext context(0, templ); 2047 v8::V8::IgnoreOutOfMemoryException(); 2048 Local<Value> result = CompileRun( 2049 "var thrown = false;" 2050 "try {" 2051 " ProvokeOutOfMemory();" 2052 "} catch (e) {" 2053 " thrown = true;" 2054 "}"); 2055 // Check for out of memory state. 2056 CHECK(result.IsEmpty()); 2057 CHECK(context->HasOutOfMemoryException()); 2058} 2059 2060 2061TEST(HugeConsStringOutOfMemory) { 2062 // It's not possible to read a snapshot into a heap with different dimensions. 2063 if (i::Snapshot::IsEnabled()) return; 2064 v8::HandleScope scope; 2065 LocalContext context; 2066 // Set heap limits. 2067 static const int K = 1024; 2068 v8::ResourceConstraints constraints; 2069 constraints.set_max_young_space_size(256 * K); 2070 constraints.set_max_old_space_size(2 * K * K); 2071 v8::SetResourceConstraints(&constraints); 2072 2073 // Execute a script that causes out of memory. 2074 v8::V8::IgnoreOutOfMemoryException(); 2075 2076 // Build huge string. This should fail with out of memory exception. 2077 Local<Value> result = CompileRun( 2078 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();" 2079 "for (var i = 0; i < 22; i++) { str = str + str; }"); 2080 2081 // Check for out of memory state. 2082 CHECK(result.IsEmpty()); 2083 CHECK(context->HasOutOfMemoryException()); 2084} 2085 2086 2087THREADED_TEST(ConstructCall) { 2088 v8::HandleScope scope; 2089 LocalContext context; 2090 CompileRun( 2091 "function Foo() {" 2092 " var result = [];" 2093 " for (var i = 0; i < arguments.length; i++) {" 2094 " result.push(arguments[i]);" 2095 " }" 2096 " return result;" 2097 "}"); 2098 Local<Function> Foo = 2099 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 2100 2101 v8::Handle<Value>* args0 = NULL; 2102 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0)); 2103 CHECK_EQ(0, a0->Length()); 2104 2105 v8::Handle<Value> args1[] = { v8_num(1.1) }; 2106 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1)); 2107 CHECK_EQ(1, a1->Length()); 2108 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 2109 2110 v8::Handle<Value> args2[] = { v8_num(2.2), 2111 v8_num(3.3) }; 2112 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2)); 2113 CHECK_EQ(2, a2->Length()); 2114 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 2115 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 2116 2117 v8::Handle<Value> args3[] = { v8_num(4.4), 2118 v8_num(5.5), 2119 v8_num(6.6) }; 2120 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3)); 2121 CHECK_EQ(3, a3->Length()); 2122 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 2123 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 2124 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 2125 2126 v8::Handle<Value> args4[] = { v8_num(7.7), 2127 v8_num(8.8), 2128 v8_num(9.9), 2129 v8_num(10.11) }; 2130 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4)); 2131 CHECK_EQ(4, a4->Length()); 2132 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 2133 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 2134 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 2135 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 2136} 2137 2138 2139static void CheckUncle(v8::TryCatch* try_catch) { 2140 CHECK(try_catch->HasCaught()); 2141 String::AsciiValue str_value(try_catch->Exception()); 2142 CHECK_EQ(*str_value, "uncle?"); 2143 try_catch->Reset(); 2144} 2145 2146 2147THREADED_TEST(ConversionNumber) { 2148 v8::HandleScope scope; 2149 LocalContext env; 2150 // Very large number. 2151 CompileRun("var obj = Math.pow(2,32) * 1237;"); 2152 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2153 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value()); 2154 CHECK_EQ(0, obj->ToInt32()->Value()); 2155 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned. 2156 // Large number. 2157 CompileRun("var obj = -1234567890123;"); 2158 obj = env->Global()->Get(v8_str("obj")); 2159 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value()); 2160 CHECK_EQ(-1912276171, obj->ToInt32()->Value()); 2161 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT 2162 // Small positive integer. 2163 CompileRun("var obj = 42;"); 2164 obj = env->Global()->Get(v8_str("obj")); 2165 CHECK_EQ(42.0, obj->ToNumber()->Value()); 2166 CHECK_EQ(42, obj->ToInt32()->Value()); 2167 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 2168 // Negative integer. 2169 CompileRun("var obj = -37;"); 2170 obj = env->Global()->Get(v8_str("obj")); 2171 CHECK_EQ(-37.0, obj->ToNumber()->Value()); 2172 CHECK_EQ(-37, obj->ToInt32()->Value()); 2173 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT 2174 // Positive non-int32 integer. 2175 CompileRun("var obj = 0x81234567;"); 2176 obj = env->Global()->Get(v8_str("obj")); 2177 CHECK_EQ(2166572391.0, obj->ToNumber()->Value()); 2178 CHECK_EQ(-2128394905, obj->ToInt32()->Value()); 2179 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT 2180 // Fraction. 2181 CompileRun("var obj = 42.3;"); 2182 obj = env->Global()->Get(v8_str("obj")); 2183 CHECK_EQ(42.3, obj->ToNumber()->Value()); 2184 CHECK_EQ(42, obj->ToInt32()->Value()); 2185 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 2186 // Large negative fraction. 2187 CompileRun("var obj = -5726623061.75;"); 2188 obj = env->Global()->Get(v8_str("obj")); 2189 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value()); 2190 CHECK_EQ(-1431655765, obj->ToInt32()->Value()); 2191 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT 2192} 2193 2194 2195THREADED_TEST(isNumberType) { 2196 v8::HandleScope scope; 2197 LocalContext env; 2198 // Very large number. 2199 CompileRun("var obj = Math.pow(2,32) * 1237;"); 2200 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2201 CHECK(!obj->IsInt32()); 2202 CHECK(!obj->IsUint32()); 2203 // Large negative number. 2204 CompileRun("var obj = -1234567890123;"); 2205 obj = env->Global()->Get(v8_str("obj")); 2206 CHECK(!obj->IsInt32()); 2207 CHECK(!obj->IsUint32()); 2208 // Small positive integer. 2209 CompileRun("var obj = 42;"); 2210 obj = env->Global()->Get(v8_str("obj")); 2211 CHECK(obj->IsInt32()); 2212 CHECK(obj->IsUint32()); 2213 // Negative integer. 2214 CompileRun("var obj = -37;"); 2215 obj = env->Global()->Get(v8_str("obj")); 2216 CHECK(obj->IsInt32()); 2217 CHECK(!obj->IsUint32()); 2218 // Positive non-int32 integer. 2219 CompileRun("var obj = 0x81234567;"); 2220 obj = env->Global()->Get(v8_str("obj")); 2221 CHECK(!obj->IsInt32()); 2222 CHECK(obj->IsUint32()); 2223 // Fraction. 2224 CompileRun("var obj = 42.3;"); 2225 obj = env->Global()->Get(v8_str("obj")); 2226 CHECK(!obj->IsInt32()); 2227 CHECK(!obj->IsUint32()); 2228 // Large negative fraction. 2229 CompileRun("var obj = -5726623061.75;"); 2230 obj = env->Global()->Get(v8_str("obj")); 2231 CHECK(!obj->IsInt32()); 2232 CHECK(!obj->IsUint32()); 2233} 2234 2235 2236THREADED_TEST(ConversionException) { 2237 v8::HandleScope scope; 2238 LocalContext env; 2239 CompileRun( 2240 "function TestClass() { };" 2241 "TestClass.prototype.toString = function () { throw 'uncle?'; };" 2242 "var obj = new TestClass();"); 2243 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2244 2245 v8::TryCatch try_catch; 2246 2247 Local<Value> to_string_result = obj->ToString(); 2248 CHECK(to_string_result.IsEmpty()); 2249 CheckUncle(&try_catch); 2250 2251 Local<Value> to_number_result = obj->ToNumber(); 2252 CHECK(to_number_result.IsEmpty()); 2253 CheckUncle(&try_catch); 2254 2255 Local<Value> to_integer_result = obj->ToInteger(); 2256 CHECK(to_integer_result.IsEmpty()); 2257 CheckUncle(&try_catch); 2258 2259 Local<Value> to_uint32_result = obj->ToUint32(); 2260 CHECK(to_uint32_result.IsEmpty()); 2261 CheckUncle(&try_catch); 2262 2263 Local<Value> to_int32_result = obj->ToInt32(); 2264 CHECK(to_int32_result.IsEmpty()); 2265 CheckUncle(&try_catch); 2266 2267 Local<Value> to_object_result = v8::Undefined()->ToObject(); 2268 CHECK(to_object_result.IsEmpty()); 2269 CHECK(try_catch.HasCaught()); 2270 try_catch.Reset(); 2271 2272 int32_t int32_value = obj->Int32Value(); 2273 CHECK_EQ(0, int32_value); 2274 CheckUncle(&try_catch); 2275 2276 uint32_t uint32_value = obj->Uint32Value(); 2277 CHECK_EQ(0, uint32_value); 2278 CheckUncle(&try_catch); 2279 2280 double number_value = obj->NumberValue(); 2281 CHECK_NE(0, IsNaN(number_value)); 2282 CheckUncle(&try_catch); 2283 2284 int64_t integer_value = obj->IntegerValue(); 2285 CHECK_EQ(0.0, static_cast<double>(integer_value)); 2286 CheckUncle(&try_catch); 2287} 2288 2289 2290v8::Handle<Value> ThrowFromC(const v8::Arguments& args) { 2291 ApiTestFuzzer::Fuzz(); 2292 return v8::ThrowException(v8_str("konto")); 2293} 2294 2295 2296v8::Handle<Value> CCatcher(const v8::Arguments& args) { 2297 if (args.Length() < 1) return v8::Boolean::New(false); 2298 v8::HandleScope scope; 2299 v8::TryCatch try_catch; 2300 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run(); 2301 CHECK(!try_catch.HasCaught() || result.IsEmpty()); 2302 return v8::Boolean::New(try_catch.HasCaught()); 2303} 2304 2305 2306THREADED_TEST(APICatch) { 2307 v8::HandleScope scope; 2308 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2309 templ->Set(v8_str("ThrowFromC"), 2310 v8::FunctionTemplate::New(ThrowFromC)); 2311 LocalContext context(0, templ); 2312 CompileRun( 2313 "var thrown = false;" 2314 "try {" 2315 " ThrowFromC();" 2316 "} catch (e) {" 2317 " thrown = true;" 2318 "}"); 2319 Local<Value> thrown = context->Global()->Get(v8_str("thrown")); 2320 CHECK(thrown->BooleanValue()); 2321} 2322 2323 2324THREADED_TEST(APIThrowTryCatch) { 2325 v8::HandleScope scope; 2326 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2327 templ->Set(v8_str("ThrowFromC"), 2328 v8::FunctionTemplate::New(ThrowFromC)); 2329 LocalContext context(0, templ); 2330 v8::TryCatch try_catch; 2331 CompileRun("ThrowFromC();"); 2332 CHECK(try_catch.HasCaught()); 2333} 2334 2335 2336// Test that a try-finally block doesn't shadow a try-catch block 2337// when setting up an external handler. 2338// 2339// BUG(271): Some of the exception propagation does not work on the 2340// ARM simulator because the simulator separates the C++ stack and the 2341// JS stack. This test therefore fails on the simulator. The test is 2342// not threaded to allow the threading tests to run on the simulator. 2343TEST(TryCatchInTryFinally) { 2344 v8::HandleScope scope; 2345 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2346 templ->Set(v8_str("CCatcher"), 2347 v8::FunctionTemplate::New(CCatcher)); 2348 LocalContext context(0, templ); 2349 Local<Value> result = CompileRun("try {" 2350 " try {" 2351 " CCatcher('throw 7;');" 2352 " } finally {" 2353 " }" 2354 "} catch (e) {" 2355 "}"); 2356 CHECK(result->IsTrue()); 2357} 2358 2359 2360static void check_reference_error_message( 2361 v8::Handle<v8::Message> message, 2362 v8::Handle<v8::Value> data) { 2363 const char* reference_error = "Uncaught ReferenceError: asdf is not defined"; 2364 CHECK(message->Get()->Equals(v8_str(reference_error))); 2365} 2366 2367 2368// Test that overwritten toString methods are not invoked on uncaught 2369// exception formatting. However, they are invoked when performing 2370// normal error string conversions. 2371TEST(APIThrowMessageOverwrittenToString) { 2372 v8::HandleScope scope; 2373 v8::V8::AddMessageListener(check_reference_error_message); 2374 LocalContext context; 2375 CompileRun("ReferenceError.prototype.toString =" 2376 " function() { return 'Whoops' }"); 2377 CompileRun("asdf;"); 2378 v8::Handle<Value> string = CompileRun("try { asdf; } catch(e) { e + ''; }"); 2379 CHECK(string->Equals(v8_str("Whoops"))); 2380 v8::V8::RemoveMessageListeners(check_message); 2381} 2382 2383 2384static void receive_message(v8::Handle<v8::Message> message, 2385 v8::Handle<v8::Value> data) { 2386 message->Get(); 2387 message_received = true; 2388} 2389 2390 2391TEST(APIThrowMessage) { 2392 message_received = false; 2393 v8::HandleScope scope; 2394 v8::V8::AddMessageListener(receive_message); 2395 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2396 templ->Set(v8_str("ThrowFromC"), 2397 v8::FunctionTemplate::New(ThrowFromC)); 2398 LocalContext context(0, templ); 2399 CompileRun("ThrowFromC();"); 2400 CHECK(message_received); 2401 v8::V8::RemoveMessageListeners(check_message); 2402} 2403 2404 2405TEST(APIThrowMessageAndVerboseTryCatch) { 2406 message_received = false; 2407 v8::HandleScope scope; 2408 v8::V8::AddMessageListener(receive_message); 2409 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2410 templ->Set(v8_str("ThrowFromC"), 2411 v8::FunctionTemplate::New(ThrowFromC)); 2412 LocalContext context(0, templ); 2413 v8::TryCatch try_catch; 2414 try_catch.SetVerbose(true); 2415 Local<Value> result = CompileRun("ThrowFromC();"); 2416 CHECK(try_catch.HasCaught()); 2417 CHECK(result.IsEmpty()); 2418 CHECK(message_received); 2419 v8::V8::RemoveMessageListeners(check_message); 2420} 2421 2422 2423THREADED_TEST(ExternalScriptException) { 2424 v8::HandleScope scope; 2425 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2426 templ->Set(v8_str("ThrowFromC"), 2427 v8::FunctionTemplate::New(ThrowFromC)); 2428 LocalContext context(0, templ); 2429 2430 v8::TryCatch try_catch; 2431 Local<Script> script 2432 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';")); 2433 Local<Value> result = script->Run(); 2434 CHECK(result.IsEmpty()); 2435 CHECK(try_catch.HasCaught()); 2436 String::AsciiValue exception_value(try_catch.Exception()); 2437 CHECK_EQ("konto", *exception_value); 2438} 2439 2440 2441 2442v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) { 2443 ApiTestFuzzer::Fuzz(); 2444 CHECK_EQ(4, args.Length()); 2445 int count = args[0]->Int32Value(); 2446 int cInterval = args[2]->Int32Value(); 2447 if (count == 0) { 2448 return v8::ThrowException(v8_str("FromC")); 2449 } else { 2450 Local<v8::Object> global = Context::GetCurrent()->Global(); 2451 Local<Value> fun = global->Get(v8_str("JSThrowCountDown")); 2452 v8::Handle<Value> argv[] = { v8_num(count - 1), 2453 args[1], 2454 args[2], 2455 args[3] }; 2456 if (count % cInterval == 0) { 2457 v8::TryCatch try_catch; 2458 Local<Value> result = fun.As<Function>()->Call(global, 4, argv); 2459 int expected = args[3]->Int32Value(); 2460 if (try_catch.HasCaught()) { 2461 CHECK_EQ(expected, count); 2462 CHECK(result.IsEmpty()); 2463 CHECK(!i::Top::has_scheduled_exception()); 2464 } else { 2465 CHECK_NE(expected, count); 2466 } 2467 return result; 2468 } else { 2469 return fun.As<Function>()->Call(global, 4, argv); 2470 } 2471 } 2472} 2473 2474 2475v8::Handle<Value> JSCheck(const v8::Arguments& args) { 2476 ApiTestFuzzer::Fuzz(); 2477 CHECK_EQ(3, args.Length()); 2478 bool equality = args[0]->BooleanValue(); 2479 int count = args[1]->Int32Value(); 2480 int expected = args[2]->Int32Value(); 2481 if (equality) { 2482 CHECK_EQ(count, expected); 2483 } else { 2484 CHECK_NE(count, expected); 2485 } 2486 return v8::Undefined(); 2487} 2488 2489 2490THREADED_TEST(EvalInTryFinally) { 2491 v8::HandleScope scope; 2492 LocalContext context; 2493 v8::TryCatch try_catch; 2494 CompileRun("(function() {" 2495 " try {" 2496 " eval('asldkf (*&^&*^');" 2497 " } finally {" 2498 " return;" 2499 " }" 2500 "})()"); 2501 CHECK(!try_catch.HasCaught()); 2502} 2503 2504 2505// This test works by making a stack of alternating JavaScript and C 2506// activations. These activations set up exception handlers with regular 2507// intervals, one interval for C activations and another for JavaScript 2508// activations. When enough activations have been created an exception is 2509// thrown and we check that the right activation catches the exception and that 2510// no other activations do. The right activation is always the topmost one with 2511// a handler, regardless of whether it is in JavaScript or C. 2512// 2513// The notation used to describe a test case looks like this: 2514// 2515// *JS[4] *C[3] @JS[2] C[1] JS[0] 2516// 2517// Each entry is an activation, either JS or C. The index is the count at that 2518// level. Stars identify activations with exception handlers, the @ identifies 2519// the exception handler that should catch the exception. 2520// 2521// BUG(271): Some of the exception propagation does not work on the 2522// ARM simulator because the simulator separates the C++ stack and the 2523// JS stack. This test therefore fails on the simulator. The test is 2524// not threaded to allow the threading tests to run on the simulator. 2525TEST(ExceptionOrder) { 2526 v8::HandleScope scope; 2527 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2528 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck)); 2529 templ->Set(v8_str("CThrowCountDown"), 2530 v8::FunctionTemplate::New(CThrowCountDown)); 2531 LocalContext context(0, templ); 2532 CompileRun( 2533 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {" 2534 " if (count == 0) throw 'FromJS';" 2535 " if (count % jsInterval == 0) {" 2536 " try {" 2537 " var value = CThrowCountDown(count - 1," 2538 " jsInterval," 2539 " cInterval," 2540 " expected);" 2541 " check(false, count, expected);" 2542 " return value;" 2543 " } catch (e) {" 2544 " check(true, count, expected);" 2545 " }" 2546 " } else {" 2547 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);" 2548 " }" 2549 "}"); 2550 Local<Function> fun = 2551 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown"))); 2552 2553 const int argc = 4; 2554 // count jsInterval cInterval expected 2555 2556 // *JS[4] *C[3] @JS[2] C[1] JS[0] 2557 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) }; 2558 fun->Call(fun, argc, a0); 2559 2560 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0] 2561 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) }; 2562 fun->Call(fun, argc, a1); 2563 2564 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0] 2565 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) }; 2566 fun->Call(fun, argc, a2); 2567 2568 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0] 2569 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) }; 2570 fun->Call(fun, argc, a3); 2571 2572 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0] 2573 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) }; 2574 fun->Call(fun, argc, a4); 2575 2576 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0] 2577 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) }; 2578 fun->Call(fun, argc, a5); 2579} 2580 2581 2582v8::Handle<Value> ThrowValue(const v8::Arguments& args) { 2583 ApiTestFuzzer::Fuzz(); 2584 CHECK_EQ(1, args.Length()); 2585 return v8::ThrowException(args[0]); 2586} 2587 2588 2589THREADED_TEST(ThrowValues) { 2590 v8::HandleScope scope; 2591 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2592 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue)); 2593 LocalContext context(0, templ); 2594 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 2595 "function Run(obj) {" 2596 " try {" 2597 " Throw(obj);" 2598 " } catch (e) {" 2599 " return e;" 2600 " }" 2601 " return 'no exception';" 2602 "}" 2603 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];")); 2604 CHECK_EQ(5, result->Length()); 2605 CHECK(result->Get(v8::Integer::New(0))->IsString()); 2606 CHECK(result->Get(v8::Integer::New(1))->IsNumber()); 2607 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value()); 2608 CHECK(result->Get(v8::Integer::New(2))->IsNumber()); 2609 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value()); 2610 CHECK(result->Get(v8::Integer::New(3))->IsNull()); 2611 CHECK(result->Get(v8::Integer::New(4))->IsUndefined()); 2612} 2613 2614 2615THREADED_TEST(CatchZero) { 2616 v8::HandleScope scope; 2617 LocalContext context; 2618 v8::TryCatch try_catch; 2619 CHECK(!try_catch.HasCaught()); 2620 Script::Compile(v8_str("throw 10"))->Run(); 2621 CHECK(try_catch.HasCaught()); 2622 CHECK_EQ(10, try_catch.Exception()->Int32Value()); 2623 try_catch.Reset(); 2624 CHECK(!try_catch.HasCaught()); 2625 Script::Compile(v8_str("throw 0"))->Run(); 2626 CHECK(try_catch.HasCaught()); 2627 CHECK_EQ(0, try_catch.Exception()->Int32Value()); 2628} 2629 2630 2631THREADED_TEST(CatchExceptionFromWith) { 2632 v8::HandleScope scope; 2633 LocalContext context; 2634 v8::TryCatch try_catch; 2635 CHECK(!try_catch.HasCaught()); 2636 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run(); 2637 CHECK(try_catch.HasCaught()); 2638} 2639 2640 2641THREADED_TEST(Equality) { 2642 v8::HandleScope scope; 2643 LocalContext context; 2644 // Check that equality works at all before relying on CHECK_EQ 2645 CHECK(v8_str("a")->Equals(v8_str("a"))); 2646 CHECK(!v8_str("a")->Equals(v8_str("b"))); 2647 2648 CHECK_EQ(v8_str("a"), v8_str("a")); 2649 CHECK_NE(v8_str("a"), v8_str("b")); 2650 CHECK_EQ(v8_num(1), v8_num(1)); 2651 CHECK_EQ(v8_num(1.00), v8_num(1)); 2652 CHECK_NE(v8_num(1), v8_num(2)); 2653 2654 // Assume String is not symbol. 2655 CHECK(v8_str("a")->StrictEquals(v8_str("a"))); 2656 CHECK(!v8_str("a")->StrictEquals(v8_str("b"))); 2657 CHECK(!v8_str("5")->StrictEquals(v8_num(5))); 2658 CHECK(v8_num(1)->StrictEquals(v8_num(1))); 2659 CHECK(!v8_num(1)->StrictEquals(v8_num(2))); 2660 CHECK(v8_num(0)->StrictEquals(v8_num(-0))); 2661 Local<Value> not_a_number = v8_num(i::OS::nan_value()); 2662 CHECK(!not_a_number->StrictEquals(not_a_number)); 2663 CHECK(v8::False()->StrictEquals(v8::False())); 2664 CHECK(!v8::False()->StrictEquals(v8::Undefined())); 2665 2666 v8::Handle<v8::Object> obj = v8::Object::New(); 2667 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj); 2668 CHECK(alias->StrictEquals(obj)); 2669 alias.Dispose(); 2670} 2671 2672 2673THREADED_TEST(MultiRun) { 2674 v8::HandleScope scope; 2675 LocalContext context; 2676 Local<Script> script = Script::Compile(v8_str("x")); 2677 for (int i = 0; i < 10; i++) 2678 script->Run(); 2679} 2680 2681 2682static v8::Handle<Value> GetXValue(Local<String> name, 2683 const AccessorInfo& info) { 2684 ApiTestFuzzer::Fuzz(); 2685 CHECK_EQ(info.Data(), v8_str("donut")); 2686 CHECK_EQ(name, v8_str("x")); 2687 return name; 2688} 2689 2690 2691THREADED_TEST(SimplePropertyRead) { 2692 v8::HandleScope scope; 2693 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2694 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2695 LocalContext context; 2696 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2697 Local<Script> script = Script::Compile(v8_str("obj.x")); 2698 for (int i = 0; i < 10; i++) { 2699 Local<Value> result = script->Run(); 2700 CHECK_EQ(result, v8_str("x")); 2701 } 2702} 2703 2704THREADED_TEST(DefinePropertyOnAPIAccessor) { 2705 v8::HandleScope scope; 2706 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2707 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2708 LocalContext context; 2709 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2710 2711 // Uses getOwnPropertyDescriptor to check the configurable status 2712 Local<Script> script_desc 2713 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( " 2714 "obj, 'x');" 2715 "prop.configurable;")); 2716 Local<Value> result = script_desc->Run(); 2717 CHECK_EQ(result->BooleanValue(), true); 2718 2719 // Redefine get - but still configurable 2720 Local<Script> script_define 2721 = Script::Compile(v8_str("var desc = { get: function(){return 42; }," 2722 " configurable: true };" 2723 "Object.defineProperty(obj, 'x', desc);" 2724 "obj.x")); 2725 result = script_define->Run(); 2726 CHECK_EQ(result, v8_num(42)); 2727 2728 // Check that the accessor is still configurable 2729 result = script_desc->Run(); 2730 CHECK_EQ(result->BooleanValue(), true); 2731 2732 // Redefine to a non-configurable 2733 script_define 2734 = Script::Compile(v8_str("var desc = { get: function(){return 43; }," 2735 " configurable: false };" 2736 "Object.defineProperty(obj, 'x', desc);" 2737 "obj.x")); 2738 result = script_define->Run(); 2739 CHECK_EQ(result, v8_num(43)); 2740 result = script_desc->Run(); 2741 CHECK_EQ(result->BooleanValue(), false); 2742 2743 // Make sure that it is not possible to redefine again 2744 v8::TryCatch try_catch; 2745 result = script_define->Run(); 2746 CHECK(try_catch.HasCaught()); 2747 String::AsciiValue exception_value(try_catch.Exception()); 2748 CHECK_EQ(*exception_value, 2749 "TypeError: Cannot redefine property: defineProperty"); 2750} 2751 2752THREADED_TEST(DefinePropertyOnDefineGetterSetter) { 2753 v8::HandleScope scope; 2754 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2755 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2756 LocalContext context; 2757 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2758 2759 Local<Script> script_desc = Script::Compile(v8_str("var prop =" 2760 "Object.getOwnPropertyDescriptor( " 2761 "obj, 'x');" 2762 "prop.configurable;")); 2763 Local<Value> result = script_desc->Run(); 2764 CHECK_EQ(result->BooleanValue(), true); 2765 2766 Local<Script> script_define = 2767 Script::Compile(v8_str("var desc = {get: function(){return 42; }," 2768 " configurable: true };" 2769 "Object.defineProperty(obj, 'x', desc);" 2770 "obj.x")); 2771 result = script_define->Run(); 2772 CHECK_EQ(result, v8_num(42)); 2773 2774 2775 result = script_desc->Run(); 2776 CHECK_EQ(result->BooleanValue(), true); 2777 2778 2779 script_define = 2780 Script::Compile(v8_str("var desc = {get: function(){return 43; }," 2781 " configurable: false };" 2782 "Object.defineProperty(obj, 'x', desc);" 2783 "obj.x")); 2784 result = script_define->Run(); 2785 CHECK_EQ(result, v8_num(43)); 2786 result = script_desc->Run(); 2787 2788 CHECK_EQ(result->BooleanValue(), false); 2789 2790 v8::TryCatch try_catch; 2791 result = script_define->Run(); 2792 CHECK(try_catch.HasCaught()); 2793 String::AsciiValue exception_value(try_catch.Exception()); 2794 CHECK_EQ(*exception_value, 2795 "TypeError: Cannot redefine property: defineProperty"); 2796} 2797 2798 2799static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context, 2800 char const* name) { 2801 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name))); 2802} 2803 2804 2805THREADED_TEST(DefineAPIAccessorOnObject) { 2806 v8::HandleScope scope; 2807 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2808 LocalContext context; 2809 2810 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 2811 CompileRun("var obj2 = {};"); 2812 2813 CHECK(CompileRun("obj1.x")->IsUndefined()); 2814 CHECK(CompileRun("obj2.x")->IsUndefined()); 2815 2816 CHECK(GetGlobalProperty(&context, "obj1")-> 2817 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2818 2819 ExpectString("obj1.x", "x"); 2820 CHECK(CompileRun("obj2.x")->IsUndefined()); 2821 2822 CHECK(GetGlobalProperty(&context, "obj2")-> 2823 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2824 2825 ExpectString("obj1.x", "x"); 2826 ExpectString("obj2.x", "x"); 2827 2828 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2829 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2830 2831 CompileRun("Object.defineProperty(obj1, 'x'," 2832 "{ get: function() { return 'y'; }, configurable: true })"); 2833 2834 ExpectString("obj1.x", "y"); 2835 ExpectString("obj2.x", "x"); 2836 2837 CompileRun("Object.defineProperty(obj2, 'x'," 2838 "{ get: function() { return 'y'; }, configurable: true })"); 2839 2840 ExpectString("obj1.x", "y"); 2841 ExpectString("obj2.x", "y"); 2842 2843 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2844 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2845 2846 CHECK(GetGlobalProperty(&context, "obj1")-> 2847 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2848 CHECK(GetGlobalProperty(&context, "obj2")-> 2849 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2850 2851 ExpectString("obj1.x", "x"); 2852 ExpectString("obj2.x", "x"); 2853 2854 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2855 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2856 2857 // Define getters/setters, but now make them not configurable. 2858 CompileRun("Object.defineProperty(obj1, 'x'," 2859 "{ get: function() { return 'z'; }, configurable: false })"); 2860 CompileRun("Object.defineProperty(obj2, 'x'," 2861 "{ get: function() { return 'z'; }, configurable: false })"); 2862 2863 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2864 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2865 2866 ExpectString("obj1.x", "z"); 2867 ExpectString("obj2.x", "z"); 2868 2869 CHECK(!GetGlobalProperty(&context, "obj1")-> 2870 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2871 CHECK(!GetGlobalProperty(&context, "obj2")-> 2872 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2873 2874 ExpectString("obj1.x", "z"); 2875 ExpectString("obj2.x", "z"); 2876} 2877 2878 2879THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) { 2880 v8::HandleScope scope; 2881 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2882 LocalContext context; 2883 2884 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 2885 CompileRun("var obj2 = {};"); 2886 2887 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 2888 v8_str("x"), 2889 GetXValue, NULL, 2890 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 2891 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 2892 v8_str("x"), 2893 GetXValue, NULL, 2894 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 2895 2896 ExpectString("obj1.x", "x"); 2897 ExpectString("obj2.x", "x"); 2898 2899 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2900 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2901 2902 CHECK(!GetGlobalProperty(&context, "obj1")-> 2903 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2904 CHECK(!GetGlobalProperty(&context, "obj2")-> 2905 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2906 2907 { 2908 v8::TryCatch try_catch; 2909 CompileRun("Object.defineProperty(obj1, 'x'," 2910 "{get: function() { return 'func'; }})"); 2911 CHECK(try_catch.HasCaught()); 2912 String::AsciiValue exception_value(try_catch.Exception()); 2913 CHECK_EQ(*exception_value, 2914 "TypeError: Cannot redefine property: defineProperty"); 2915 } 2916 { 2917 v8::TryCatch try_catch; 2918 CompileRun("Object.defineProperty(obj2, 'x'," 2919 "{get: function() { return 'func'; }})"); 2920 CHECK(try_catch.HasCaught()); 2921 String::AsciiValue exception_value(try_catch.Exception()); 2922 CHECK_EQ(*exception_value, 2923 "TypeError: Cannot redefine property: defineProperty"); 2924 } 2925} 2926 2927 2928static v8::Handle<Value> Get239Value(Local<String> name, 2929 const AccessorInfo& info) { 2930 ApiTestFuzzer::Fuzz(); 2931 CHECK_EQ(info.Data(), v8_str("donut")); 2932 CHECK_EQ(name, v8_str("239")); 2933 return name; 2934} 2935 2936 2937THREADED_TEST(ElementAPIAccessor) { 2938 v8::HandleScope scope; 2939 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2940 LocalContext context; 2941 2942 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 2943 CompileRun("var obj2 = {};"); 2944 2945 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 2946 v8_str("239"), 2947 Get239Value, NULL, 2948 v8_str("donut"))); 2949 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 2950 v8_str("239"), 2951 Get239Value, NULL, 2952 v8_str("donut"))); 2953 2954 ExpectString("obj1[239]", "239"); 2955 ExpectString("obj2[239]", "239"); 2956 ExpectString("obj1['239']", "239"); 2957 ExpectString("obj2['239']", "239"); 2958} 2959 2960 2961v8::Persistent<Value> xValue; 2962 2963 2964static void SetXValue(Local<String> name, 2965 Local<Value> value, 2966 const AccessorInfo& info) { 2967 CHECK_EQ(value, v8_num(4)); 2968 CHECK_EQ(info.Data(), v8_str("donut")); 2969 CHECK_EQ(name, v8_str("x")); 2970 CHECK(xValue.IsEmpty()); 2971 xValue = v8::Persistent<Value>::New(value); 2972} 2973 2974 2975THREADED_TEST(SimplePropertyWrite) { 2976 v8::HandleScope scope; 2977 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2978 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut")); 2979 LocalContext context; 2980 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2981 Local<Script> script = Script::Compile(v8_str("obj.x = 4")); 2982 for (int i = 0; i < 10; i++) { 2983 CHECK(xValue.IsEmpty()); 2984 script->Run(); 2985 CHECK_EQ(v8_num(4), xValue); 2986 xValue.Dispose(); 2987 xValue = v8::Persistent<Value>(); 2988 } 2989} 2990 2991 2992static v8::Handle<Value> XPropertyGetter(Local<String> property, 2993 const AccessorInfo& info) { 2994 ApiTestFuzzer::Fuzz(); 2995 CHECK(info.Data()->IsUndefined()); 2996 return property; 2997} 2998 2999 3000THREADED_TEST(NamedInterceptorPropertyRead) { 3001 v8::HandleScope scope; 3002 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3003 templ->SetNamedPropertyHandler(XPropertyGetter); 3004 LocalContext context; 3005 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3006 Local<Script> script = Script::Compile(v8_str("obj.x")); 3007 for (int i = 0; i < 10; i++) { 3008 Local<Value> result = script->Run(); 3009 CHECK_EQ(result, v8_str("x")); 3010 } 3011} 3012 3013 3014THREADED_TEST(NamedInterceptorDictionaryIC) { 3015 v8::HandleScope scope; 3016 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3017 templ->SetNamedPropertyHandler(XPropertyGetter); 3018 LocalContext context; 3019 // Create an object with a named interceptor. 3020 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance()); 3021 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x")); 3022 for (int i = 0; i < 10; i++) { 3023 Local<Value> result = script->Run(); 3024 CHECK_EQ(result, v8_str("x")); 3025 } 3026 // Create a slow case object and a function accessing a property in 3027 // that slow case object (with dictionary probing in generated 3028 // code). Then force object with a named interceptor into slow-case, 3029 // pass it to the function, and check that the interceptor is called 3030 // instead of accessing the local property. 3031 Local<Value> result = 3032 CompileRun("function get_x(o) { return o.x; };" 3033 "var obj = { x : 42, y : 0 };" 3034 "delete obj.y;" 3035 "for (var i = 0; i < 10; i++) get_x(obj);" 3036 "interceptor_obj.x = 42;" 3037 "interceptor_obj.y = 10;" 3038 "delete interceptor_obj.y;" 3039 "get_x(interceptor_obj)"); 3040 CHECK_EQ(result, v8_str("x")); 3041} 3042 3043 3044THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) { 3045 v8::HandleScope scope; 3046 3047 v8::Persistent<Context> context1 = Context::New(); 3048 3049 context1->Enter(); 3050 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3051 templ->SetNamedPropertyHandler(XPropertyGetter); 3052 // Create an object with a named interceptor. 3053 v8::Local<v8::Object> object = templ->NewInstance(); 3054 context1->Global()->Set(v8_str("interceptor_obj"), object); 3055 3056 // Force the object into the slow case. 3057 CompileRun("interceptor_obj.y = 0;" 3058 "delete interceptor_obj.y;"); 3059 context1->Exit(); 3060 3061 { 3062 // Introduce the object into a different context. 3063 // Repeat named loads to exercise ICs. 3064 LocalContext context2; 3065 context2->Global()->Set(v8_str("interceptor_obj"), object); 3066 Local<Value> result = 3067 CompileRun("function get_x(o) { return o.x; }" 3068 "interceptor_obj.x = 42;" 3069 "for (var i=0; i != 10; i++) {" 3070 " get_x(interceptor_obj);" 3071 "}" 3072 "get_x(interceptor_obj)"); 3073 // Check that the interceptor was actually invoked. 3074 CHECK_EQ(result, v8_str("x")); 3075 } 3076 3077 // Return to the original context and force some object to the slow case 3078 // to cause the NormalizedMapCache to verify. 3079 context1->Enter(); 3080 CompileRun("var obj = { x : 0 }; delete obj.x;"); 3081 context1->Exit(); 3082 3083 context1.Dispose(); 3084} 3085 3086 3087static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property, 3088 const AccessorInfo& info) { 3089 // Set x on the prototype object and do not handle the get request. 3090 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype(); 3091 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23)); 3092 return v8::Handle<Value>(); 3093} 3094 3095 3096// This is a regression test for http://crbug.com/20104. Map 3097// transitions should not interfere with post interceptor lookup. 3098THREADED_TEST(NamedInterceptorMapTransitionRead) { 3099 v8::HandleScope scope; 3100 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(); 3101 Local<v8::ObjectTemplate> instance_template 3102 = function_template->InstanceTemplate(); 3103 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter); 3104 LocalContext context; 3105 context->Global()->Set(v8_str("F"), function_template->GetFunction()); 3106 // Create an instance of F and introduce a map transition for x. 3107 CompileRun("var o = new F(); o.x = 23;"); 3108 // Create an instance of F and invoke the getter. The result should be 23. 3109 Local<Value> result = CompileRun("o = new F(); o.x"); 3110 CHECK_EQ(result->Int32Value(), 23); 3111} 3112 3113 3114static v8::Handle<Value> IndexedPropertyGetter(uint32_t index, 3115 const AccessorInfo& info) { 3116 ApiTestFuzzer::Fuzz(); 3117 if (index == 37) { 3118 return v8::Handle<Value>(v8_num(625)); 3119 } 3120 return v8::Handle<Value>(); 3121} 3122 3123 3124static v8::Handle<Value> IndexedPropertySetter(uint32_t index, 3125 Local<Value> value, 3126 const AccessorInfo& info) { 3127 ApiTestFuzzer::Fuzz(); 3128 if (index == 39) { 3129 return value; 3130 } 3131 return v8::Handle<Value>(); 3132} 3133 3134 3135THREADED_TEST(IndexedInterceptorWithIndexedAccessor) { 3136 v8::HandleScope scope; 3137 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3138 templ->SetIndexedPropertyHandler(IndexedPropertyGetter, 3139 IndexedPropertySetter); 3140 LocalContext context; 3141 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3142 Local<Script> getter_script = Script::Compile(v8_str( 3143 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];")); 3144 Local<Script> setter_script = Script::Compile(v8_str( 3145 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});" 3146 "obj[17] = 23;" 3147 "obj.foo;")); 3148 Local<Script> interceptor_setter_script = Script::Compile(v8_str( 3149 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});" 3150 "obj[39] = 47;" 3151 "obj.foo;")); // This setter should not run, due to the interceptor. 3152 Local<Script> interceptor_getter_script = Script::Compile(v8_str( 3153 "obj[37];")); 3154 Local<Value> result = getter_script->Run(); 3155 CHECK_EQ(v8_num(5), result); 3156 result = setter_script->Run(); 3157 CHECK_EQ(v8_num(23), result); 3158 result = interceptor_setter_script->Run(); 3159 CHECK_EQ(v8_num(23), result); 3160 result = interceptor_getter_script->Run(); 3161 CHECK_EQ(v8_num(625), result); 3162} 3163 3164 3165static v8::Handle<Value> IdentityIndexedPropertyGetter( 3166 uint32_t index, 3167 const AccessorInfo& info) { 3168 return v8::Integer::NewFromUnsigned(index); 3169} 3170 3171 3172THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) { 3173 v8::HandleScope scope; 3174 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3175 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3176 3177 LocalContext context; 3178 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3179 3180 // Check fast object case. 3181 const char* fast_case_code = 3182 "Object.getOwnPropertyDescriptor(obj, 0).value.toString()"; 3183 ExpectString(fast_case_code, "0"); 3184 3185 // Check slow case. 3186 const char* slow_case_code = 3187 "obj.x = 1; delete obj.x;" 3188 "Object.getOwnPropertyDescriptor(obj, 1).value.toString()"; 3189 ExpectString(slow_case_code, "1"); 3190} 3191 3192 3193THREADED_TEST(IndexedInterceptorWithNoSetter) { 3194 v8::HandleScope scope; 3195 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3196 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3197 3198 LocalContext context; 3199 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3200 3201 const char* code = 3202 "try {" 3203 " obj[0] = 239;" 3204 " for (var i = 0; i < 100; i++) {" 3205 " var v = obj[0];" 3206 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;" 3207 " }" 3208 " 'PASSED'" 3209 "} catch(e) {" 3210 " e" 3211 "}"; 3212 ExpectString(code, "PASSED"); 3213} 3214 3215 3216THREADED_TEST(IndexedInterceptorWithAccessorCheck) { 3217 v8::HandleScope scope; 3218 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3219 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3220 3221 LocalContext context; 3222 Local<v8::Object> obj = templ->NewInstance(); 3223 obj->TurnOnAccessCheck(); 3224 context->Global()->Set(v8_str("obj"), obj); 3225 3226 const char* code = 3227 "try {" 3228 " for (var i = 0; i < 100; i++) {" 3229 " var v = obj[0];" 3230 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;" 3231 " }" 3232 " 'PASSED'" 3233 "} catch(e) {" 3234 " e" 3235 "}"; 3236 ExpectString(code, "PASSED"); 3237} 3238 3239 3240THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) { 3241 i::FLAG_allow_natives_syntax = true; 3242 v8::HandleScope scope; 3243 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3244 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3245 3246 LocalContext context; 3247 Local<v8::Object> obj = templ->NewInstance(); 3248 context->Global()->Set(v8_str("obj"), obj); 3249 3250 const char* code = 3251 "try {" 3252 " for (var i = 0; i < 100; i++) {" 3253 " var expected = i;" 3254 " if (i == 5) {" 3255 " %EnableAccessChecks(obj);" 3256 " expected = undefined;" 3257 " }" 3258 " var v = obj[i];" 3259 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3260 " if (i == 5) %DisableAccessChecks(obj);" 3261 " }" 3262 " 'PASSED'" 3263 "} catch(e) {" 3264 " e" 3265 "}"; 3266 ExpectString(code, "PASSED"); 3267} 3268 3269 3270THREADED_TEST(IndexedInterceptorWithDifferentIndices) { 3271 v8::HandleScope scope; 3272 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3273 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3274 3275 LocalContext context; 3276 Local<v8::Object> obj = templ->NewInstance(); 3277 context->Global()->Set(v8_str("obj"), obj); 3278 3279 const char* code = 3280 "try {" 3281 " for (var i = 0; i < 100; i++) {" 3282 " var v = obj[i];" 3283 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 3284 " }" 3285 " 'PASSED'" 3286 "} catch(e) {" 3287 " e" 3288 "}"; 3289 ExpectString(code, "PASSED"); 3290} 3291 3292 3293THREADED_TEST(IndexedInterceptorWithNegativeIndices) { 3294 v8::HandleScope scope; 3295 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3296 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3297 3298 LocalContext context; 3299 Local<v8::Object> obj = templ->NewInstance(); 3300 context->Global()->Set(v8_str("obj"), obj); 3301 3302 const char* code = 3303 "try {" 3304 " for (var i = 0; i < 100; i++) {" 3305 " var expected = i;" 3306 " var key = i;" 3307 " if (i == 25) {" 3308 " key = -1;" 3309 " expected = undefined;" 3310 " }" 3311 " if (i == 50) {" 3312 " /* probe minimal Smi number on 32-bit platforms */" 3313 " key = -(1 << 30);" 3314 " expected = undefined;" 3315 " }" 3316 " if (i == 75) {" 3317 " /* probe minimal Smi number on 64-bit platforms */" 3318 " key = 1 << 31;" 3319 " expected = undefined;" 3320 " }" 3321 " var v = obj[key];" 3322 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3323 " }" 3324 " 'PASSED'" 3325 "} catch(e) {" 3326 " e" 3327 "}"; 3328 ExpectString(code, "PASSED"); 3329} 3330 3331 3332THREADED_TEST(IndexedInterceptorWithNotSmiLookup) { 3333 v8::HandleScope scope; 3334 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3335 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3336 3337 LocalContext context; 3338 Local<v8::Object> obj = templ->NewInstance(); 3339 context->Global()->Set(v8_str("obj"), obj); 3340 3341 const char* code = 3342 "try {" 3343 " for (var i = 0; i < 100; i++) {" 3344 " var expected = i;" 3345 " var key = i;" 3346 " if (i == 50) {" 3347 " key = 'foobar';" 3348 " expected = undefined;" 3349 " }" 3350 " var v = obj[key];" 3351 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3352 " }" 3353 " 'PASSED'" 3354 "} catch(e) {" 3355 " e" 3356 "}"; 3357 ExpectString(code, "PASSED"); 3358} 3359 3360 3361THREADED_TEST(IndexedInterceptorGoingMegamorphic) { 3362 v8::HandleScope scope; 3363 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3364 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3365 3366 LocalContext context; 3367 Local<v8::Object> obj = templ->NewInstance(); 3368 context->Global()->Set(v8_str("obj"), obj); 3369 3370 const char* code = 3371 "var original = obj;" 3372 "try {" 3373 " for (var i = 0; i < 100; i++) {" 3374 " var expected = i;" 3375 " if (i == 50) {" 3376 " obj = {50: 'foobar'};" 3377 " expected = 'foobar';" 3378 " }" 3379 " var v = obj[i];" 3380 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3381 " if (i == 50) obj = original;" 3382 " }" 3383 " 'PASSED'" 3384 "} catch(e) {" 3385 " e" 3386 "}"; 3387 ExpectString(code, "PASSED"); 3388} 3389 3390 3391THREADED_TEST(IndexedInterceptorReceiverTurningSmi) { 3392 v8::HandleScope scope; 3393 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3394 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3395 3396 LocalContext context; 3397 Local<v8::Object> obj = templ->NewInstance(); 3398 context->Global()->Set(v8_str("obj"), obj); 3399 3400 const char* code = 3401 "var original = obj;" 3402 "try {" 3403 " for (var i = 0; i < 100; i++) {" 3404 " var expected = i;" 3405 " if (i == 5) {" 3406 " obj = 239;" 3407 " expected = undefined;" 3408 " }" 3409 " var v = obj[i];" 3410 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3411 " if (i == 5) obj = original;" 3412 " }" 3413 " 'PASSED'" 3414 "} catch(e) {" 3415 " e" 3416 "}"; 3417 ExpectString(code, "PASSED"); 3418} 3419 3420 3421THREADED_TEST(IndexedInterceptorOnProto) { 3422 v8::HandleScope scope; 3423 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3424 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3425 3426 LocalContext context; 3427 Local<v8::Object> obj = templ->NewInstance(); 3428 context->Global()->Set(v8_str("obj"), obj); 3429 3430 const char* code = 3431 "var o = {__proto__: obj};" 3432 "try {" 3433 " for (var i = 0; i < 100; i++) {" 3434 " var v = o[i];" 3435 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 3436 " }" 3437 " 'PASSED'" 3438 "} catch(e) {" 3439 " e" 3440 "}"; 3441 ExpectString(code, "PASSED"); 3442} 3443 3444 3445THREADED_TEST(MultiContexts) { 3446 v8::HandleScope scope; 3447 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New(); 3448 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler)); 3449 3450 Local<String> password = v8_str("Password"); 3451 3452 // Create an environment 3453 LocalContext context0(0, templ); 3454 context0->SetSecurityToken(password); 3455 v8::Handle<v8::Object> global0 = context0->Global(); 3456 global0->Set(v8_str("custom"), v8_num(1234)); 3457 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 3458 3459 // Create an independent environment 3460 LocalContext context1(0, templ); 3461 context1->SetSecurityToken(password); 3462 v8::Handle<v8::Object> global1 = context1->Global(); 3463 global1->Set(v8_str("custom"), v8_num(1234)); 3464 CHECK_NE(global0, global1); 3465 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 3466 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value()); 3467 3468 // Now create a new context with the old global 3469 LocalContext context2(0, templ, global1); 3470 context2->SetSecurityToken(password); 3471 v8::Handle<v8::Object> global2 = context2->Global(); 3472 CHECK_EQ(global1, global2); 3473 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value()); 3474 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value()); 3475} 3476 3477 3478THREADED_TEST(FunctionPrototypeAcrossContexts) { 3479 // Make sure that functions created by cloning boilerplates cannot 3480 // communicate through their __proto__ field. 3481 3482 v8::HandleScope scope; 3483 3484 LocalContext env0; 3485 v8::Handle<v8::Object> global0 = 3486 env0->Global(); 3487 v8::Handle<v8::Object> object0 = 3488 global0->Get(v8_str("Object")).As<v8::Object>(); 3489 v8::Handle<v8::Object> tostring0 = 3490 object0->Get(v8_str("toString")).As<v8::Object>(); 3491 v8::Handle<v8::Object> proto0 = 3492 tostring0->Get(v8_str("__proto__")).As<v8::Object>(); 3493 proto0->Set(v8_str("custom"), v8_num(1234)); 3494 3495 LocalContext env1; 3496 v8::Handle<v8::Object> global1 = 3497 env1->Global(); 3498 v8::Handle<v8::Object> object1 = 3499 global1->Get(v8_str("Object")).As<v8::Object>(); 3500 v8::Handle<v8::Object> tostring1 = 3501 object1->Get(v8_str("toString")).As<v8::Object>(); 3502 v8::Handle<v8::Object> proto1 = 3503 tostring1->Get(v8_str("__proto__")).As<v8::Object>(); 3504 CHECK(!proto1->Has(v8_str("custom"))); 3505} 3506 3507 3508THREADED_TEST(Regress892105) { 3509 // Make sure that object and array literals created by cloning 3510 // boilerplates cannot communicate through their __proto__ 3511 // field. This is rather difficult to check, but we try to add stuff 3512 // to Object.prototype and Array.prototype and create a new 3513 // environment. This should succeed. 3514 3515 v8::HandleScope scope; 3516 3517 Local<String> source = v8_str("Object.prototype.obj = 1234;" 3518 "Array.prototype.arr = 4567;" 3519 "8901"); 3520 3521 LocalContext env0; 3522 Local<Script> script0 = Script::Compile(source); 3523 CHECK_EQ(8901.0, script0->Run()->NumberValue()); 3524 3525 LocalContext env1; 3526 Local<Script> script1 = Script::Compile(source); 3527 CHECK_EQ(8901.0, script1->Run()->NumberValue()); 3528} 3529 3530 3531THREADED_TEST(UndetectableObject) { 3532 v8::HandleScope scope; 3533 LocalContext env; 3534 3535 Local<v8::FunctionTemplate> desc = 3536 v8::FunctionTemplate::New(0, v8::Handle<Value>()); 3537 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 3538 3539 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 3540 env->Global()->Set(v8_str("undetectable"), obj); 3541 3542 ExpectString("undetectable.toString()", "[object Object]"); 3543 ExpectString("typeof undetectable", "undefined"); 3544 ExpectString("typeof(undetectable)", "undefined"); 3545 ExpectBoolean("typeof undetectable == 'undefined'", true); 3546 ExpectBoolean("typeof undetectable == 'object'", false); 3547 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 3548 ExpectBoolean("!undetectable", true); 3549 3550 ExpectObject("true&&undetectable", obj); 3551 ExpectBoolean("false&&undetectable", false); 3552 ExpectBoolean("true||undetectable", true); 3553 ExpectObject("false||undetectable", obj); 3554 3555 ExpectObject("undetectable&&true", obj); 3556 ExpectObject("undetectable&&false", obj); 3557 ExpectBoolean("undetectable||true", true); 3558 ExpectBoolean("undetectable||false", false); 3559 3560 ExpectBoolean("undetectable==null", true); 3561 ExpectBoolean("null==undetectable", true); 3562 ExpectBoolean("undetectable==undefined", true); 3563 ExpectBoolean("undefined==undetectable", true); 3564 ExpectBoolean("undetectable==undetectable", true); 3565 3566 3567 ExpectBoolean("undetectable===null", false); 3568 ExpectBoolean("null===undetectable", false); 3569 ExpectBoolean("undetectable===undefined", false); 3570 ExpectBoolean("undefined===undetectable", false); 3571 ExpectBoolean("undetectable===undetectable", true); 3572} 3573 3574 3575 3576THREADED_TEST(ExtensibleOnUndetectable) { 3577 v8::HandleScope scope; 3578 LocalContext env; 3579 3580 Local<v8::FunctionTemplate> desc = 3581 v8::FunctionTemplate::New(0, v8::Handle<Value>()); 3582 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 3583 3584 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 3585 env->Global()->Set(v8_str("undetectable"), obj); 3586 3587 Local<String> source = v8_str("undetectable.x = 42;" 3588 "undetectable.x"); 3589 3590 Local<Script> script = Script::Compile(source); 3591 3592 CHECK_EQ(v8::Integer::New(42), script->Run()); 3593 3594 ExpectBoolean("Object.isExtensible(undetectable)", true); 3595 3596 source = v8_str("Object.preventExtensions(undetectable);"); 3597 script = Script::Compile(source); 3598 script->Run(); 3599 ExpectBoolean("Object.isExtensible(undetectable)", false); 3600 3601 source = v8_str("undetectable.y = 2000;"); 3602 script = Script::Compile(source); 3603 v8::TryCatch try_catch; 3604 Local<Value> result = script->Run(); 3605 CHECK(result.IsEmpty()); 3606 CHECK(try_catch.HasCaught()); 3607} 3608 3609 3610 3611THREADED_TEST(UndetectableString) { 3612 v8::HandleScope scope; 3613 LocalContext env; 3614 3615 Local<String> obj = String::NewUndetectable("foo"); 3616 env->Global()->Set(v8_str("undetectable"), obj); 3617 3618 ExpectString("undetectable", "foo"); 3619 ExpectString("typeof undetectable", "undefined"); 3620 ExpectString("typeof(undetectable)", "undefined"); 3621 ExpectBoolean("typeof undetectable == 'undefined'", true); 3622 ExpectBoolean("typeof undetectable == 'string'", false); 3623 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 3624 ExpectBoolean("!undetectable", true); 3625 3626 ExpectObject("true&&undetectable", obj); 3627 ExpectBoolean("false&&undetectable", false); 3628 ExpectBoolean("true||undetectable", true); 3629 ExpectObject("false||undetectable", obj); 3630 3631 ExpectObject("undetectable&&true", obj); 3632 ExpectObject("undetectable&&false", obj); 3633 ExpectBoolean("undetectable||true", true); 3634 ExpectBoolean("undetectable||false", false); 3635 3636 ExpectBoolean("undetectable==null", true); 3637 ExpectBoolean("null==undetectable", true); 3638 ExpectBoolean("undetectable==undefined", true); 3639 ExpectBoolean("undefined==undetectable", true); 3640 ExpectBoolean("undetectable==undetectable", true); 3641 3642 3643 ExpectBoolean("undetectable===null", false); 3644 ExpectBoolean("null===undetectable", false); 3645 ExpectBoolean("undetectable===undefined", false); 3646 ExpectBoolean("undefined===undetectable", false); 3647 ExpectBoolean("undetectable===undetectable", true); 3648} 3649 3650 3651template <typename T> static void USE(T) { } 3652 3653 3654// This test is not intended to be run, just type checked. 3655static void PersistentHandles() { 3656 USE(PersistentHandles); 3657 Local<String> str = v8_str("foo"); 3658 v8::Persistent<String> p_str = v8::Persistent<String>::New(str); 3659 USE(p_str); 3660 Local<Script> scr = Script::Compile(v8_str("")); 3661 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr); 3662 USE(p_scr); 3663 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3664 v8::Persistent<ObjectTemplate> p_templ = 3665 v8::Persistent<ObjectTemplate>::New(templ); 3666 USE(p_templ); 3667} 3668 3669 3670static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) { 3671 ApiTestFuzzer::Fuzz(); 3672 return v8::Undefined(); 3673} 3674 3675 3676THREADED_TEST(GlobalObjectTemplate) { 3677 v8::HandleScope handle_scope; 3678 Local<ObjectTemplate> global_template = ObjectTemplate::New(); 3679 global_template->Set(v8_str("JSNI_Log"), 3680 v8::FunctionTemplate::New(HandleLogDelegator)); 3681 v8::Persistent<Context> context = Context::New(0, global_template); 3682 Context::Scope context_scope(context); 3683 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run(); 3684 context.Dispose(); 3685} 3686 3687 3688static const char* kSimpleExtensionSource = 3689 "function Foo() {" 3690 " return 4;" 3691 "}"; 3692 3693 3694THREADED_TEST(SimpleExtensions) { 3695 v8::HandleScope handle_scope; 3696 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource)); 3697 const char* extension_names[] = { "simpletest" }; 3698 v8::ExtensionConfiguration extensions(1, extension_names); 3699 v8::Handle<Context> context = Context::New(&extensions); 3700 Context::Scope lock(context); 3701 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 3702 CHECK_EQ(result, v8::Integer::New(4)); 3703} 3704 3705 3706static const char* kEvalExtensionSource1 = 3707 "function UseEval1() {" 3708 " var x = 42;" 3709 " return eval('x');" 3710 "}"; 3711 3712 3713static const char* kEvalExtensionSource2 = 3714 "(function() {" 3715 " var x = 42;" 3716 " function e() {" 3717 " return eval('x');" 3718 " }" 3719 " this.UseEval2 = e;" 3720 "})()"; 3721 3722 3723THREADED_TEST(UseEvalFromExtension) { 3724 v8::HandleScope handle_scope; 3725 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1)); 3726 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2)); 3727 const char* extension_names[] = { "evaltest1", "evaltest2" }; 3728 v8::ExtensionConfiguration extensions(2, extension_names); 3729 v8::Handle<Context> context = Context::New(&extensions); 3730 Context::Scope lock(context); 3731 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run(); 3732 CHECK_EQ(result, v8::Integer::New(42)); 3733 result = Script::Compile(v8_str("UseEval2()"))->Run(); 3734 CHECK_EQ(result, v8::Integer::New(42)); 3735} 3736 3737 3738static const char* kWithExtensionSource1 = 3739 "function UseWith1() {" 3740 " var x = 42;" 3741 " with({x:87}) { return x; }" 3742 "}"; 3743 3744 3745 3746static const char* kWithExtensionSource2 = 3747 "(function() {" 3748 " var x = 42;" 3749 " function e() {" 3750 " with ({x:87}) { return x; }" 3751 " }" 3752 " this.UseWith2 = e;" 3753 "})()"; 3754 3755 3756THREADED_TEST(UseWithFromExtension) { 3757 v8::HandleScope handle_scope; 3758 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1)); 3759 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2)); 3760 const char* extension_names[] = { "withtest1", "withtest2" }; 3761 v8::ExtensionConfiguration extensions(2, extension_names); 3762 v8::Handle<Context> context = Context::New(&extensions); 3763 Context::Scope lock(context); 3764 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run(); 3765 CHECK_EQ(result, v8::Integer::New(87)); 3766 result = Script::Compile(v8_str("UseWith2()"))->Run(); 3767 CHECK_EQ(result, v8::Integer::New(87)); 3768} 3769 3770 3771THREADED_TEST(AutoExtensions) { 3772 v8::HandleScope handle_scope; 3773 Extension* extension = new Extension("autotest", kSimpleExtensionSource); 3774 extension->set_auto_enable(true); 3775 v8::RegisterExtension(extension); 3776 v8::Handle<Context> context = Context::New(); 3777 Context::Scope lock(context); 3778 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 3779 CHECK_EQ(result, v8::Integer::New(4)); 3780} 3781 3782 3783static const char* kSyntaxErrorInExtensionSource = 3784 "["; 3785 3786 3787// Test that a syntax error in an extension does not cause a fatal 3788// error but results in an empty context. 3789THREADED_TEST(SyntaxErrorExtensions) { 3790 v8::HandleScope handle_scope; 3791 v8::RegisterExtension(new Extension("syntaxerror", 3792 kSyntaxErrorInExtensionSource)); 3793 const char* extension_names[] = { "syntaxerror" }; 3794 v8::ExtensionConfiguration extensions(1, extension_names); 3795 v8::Handle<Context> context = Context::New(&extensions); 3796 CHECK(context.IsEmpty()); 3797} 3798 3799 3800static const char* kExceptionInExtensionSource = 3801 "throw 42"; 3802 3803 3804// Test that an exception when installing an extension does not cause 3805// a fatal error but results in an empty context. 3806THREADED_TEST(ExceptionExtensions) { 3807 v8::HandleScope handle_scope; 3808 v8::RegisterExtension(new Extension("exception", 3809 kExceptionInExtensionSource)); 3810 const char* extension_names[] = { "exception" }; 3811 v8::ExtensionConfiguration extensions(1, extension_names); 3812 v8::Handle<Context> context = Context::New(&extensions); 3813 CHECK(context.IsEmpty()); 3814} 3815 3816 3817static const char* kNativeCallInExtensionSource = 3818 "function call_runtime_last_index_of(x) {" 3819 " return %StringLastIndexOf(x, 'bob', 10);" 3820 "}"; 3821 3822 3823static const char* kNativeCallTest = 3824 "call_runtime_last_index_of('bobbobboellebobboellebobbob');"; 3825 3826// Test that a native runtime calls are supported in extensions. 3827THREADED_TEST(NativeCallInExtensions) { 3828 v8::HandleScope handle_scope; 3829 v8::RegisterExtension(new Extension("nativecall", 3830 kNativeCallInExtensionSource)); 3831 const char* extension_names[] = { "nativecall" }; 3832 v8::ExtensionConfiguration extensions(1, extension_names); 3833 v8::Handle<Context> context = Context::New(&extensions); 3834 Context::Scope lock(context); 3835 v8::Handle<Value> result = Script::Compile(v8_str(kNativeCallTest))->Run(); 3836 CHECK_EQ(result, v8::Integer::New(3)); 3837} 3838 3839 3840static void CheckDependencies(const char* name, const char* expected) { 3841 v8::HandleScope handle_scope; 3842 v8::ExtensionConfiguration config(1, &name); 3843 LocalContext context(&config); 3844 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded"))); 3845} 3846 3847 3848/* 3849 * Configuration: 3850 * 3851 * /-- B <--\ 3852 * A <- -- D <-- E 3853 * \-- C <--/ 3854 */ 3855THREADED_TEST(ExtensionDependency) { 3856 static const char* kEDeps[] = { "D" }; 3857 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps)); 3858 static const char* kDDeps[] = { "B", "C" }; 3859 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps)); 3860 static const char* kBCDeps[] = { "A" }; 3861 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps)); 3862 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps)); 3863 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';")); 3864 CheckDependencies("A", "undefinedA"); 3865 CheckDependencies("B", "undefinedAB"); 3866 CheckDependencies("C", "undefinedAC"); 3867 CheckDependencies("D", "undefinedABCD"); 3868 CheckDependencies("E", "undefinedABCDE"); 3869 v8::HandleScope handle_scope; 3870 static const char* exts[2] = { "C", "E" }; 3871 v8::ExtensionConfiguration config(2, exts); 3872 LocalContext context(&config); 3873 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded"))); 3874} 3875 3876 3877static const char* kExtensionTestScript = 3878 "native function A();" 3879 "native function B();" 3880 "native function C();" 3881 "function Foo(i) {" 3882 " if (i == 0) return A();" 3883 " if (i == 1) return B();" 3884 " if (i == 2) return C();" 3885 "}"; 3886 3887 3888static v8::Handle<Value> CallFun(const v8::Arguments& args) { 3889 ApiTestFuzzer::Fuzz(); 3890 if (args.IsConstructCall()) { 3891 args.This()->Set(v8_str("data"), args.Data()); 3892 return v8::Null(); 3893 } 3894 return args.Data(); 3895} 3896 3897 3898class FunctionExtension : public Extension { 3899 public: 3900 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { } 3901 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( 3902 v8::Handle<String> name); 3903}; 3904 3905 3906static int lookup_count = 0; 3907v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction( 3908 v8::Handle<String> name) { 3909 lookup_count++; 3910 if (name->Equals(v8_str("A"))) { 3911 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8)); 3912 } else if (name->Equals(v8_str("B"))) { 3913 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7)); 3914 } else if (name->Equals(v8_str("C"))) { 3915 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6)); 3916 } else { 3917 return v8::Handle<v8::FunctionTemplate>(); 3918 } 3919} 3920 3921 3922THREADED_TEST(FunctionLookup) { 3923 v8::RegisterExtension(new FunctionExtension()); 3924 v8::HandleScope handle_scope; 3925 static const char* exts[1] = { "functiontest" }; 3926 v8::ExtensionConfiguration config(1, exts); 3927 LocalContext context(&config); 3928 CHECK_EQ(3, lookup_count); 3929 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run()); 3930 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run()); 3931 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run()); 3932} 3933 3934 3935THREADED_TEST(NativeFunctionConstructCall) { 3936 v8::RegisterExtension(new FunctionExtension()); 3937 v8::HandleScope handle_scope; 3938 static const char* exts[1] = { "functiontest" }; 3939 v8::ExtensionConfiguration config(1, exts); 3940 LocalContext context(&config); 3941 for (int i = 0; i < 10; i++) { 3942 // Run a few times to ensure that allocation of objects doesn't 3943 // change behavior of a constructor function. 3944 CHECK_EQ(v8::Integer::New(8), 3945 Script::Compile(v8_str("(new A()).data"))->Run()); 3946 CHECK_EQ(v8::Integer::New(7), 3947 Script::Compile(v8_str("(new B()).data"))->Run()); 3948 CHECK_EQ(v8::Integer::New(6), 3949 Script::Compile(v8_str("(new C()).data"))->Run()); 3950 } 3951} 3952 3953 3954static const char* last_location; 3955static const char* last_message; 3956void StoringErrorCallback(const char* location, const char* message) { 3957 if (last_location == NULL) { 3958 last_location = location; 3959 last_message = message; 3960 } 3961} 3962 3963 3964// ErrorReporting creates a circular extensions configuration and 3965// tests that the fatal error handler gets called. This renders V8 3966// unusable and therefore this test cannot be run in parallel. 3967TEST(ErrorReporting) { 3968 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 3969 static const char* aDeps[] = { "B" }; 3970 v8::RegisterExtension(new Extension("A", "", 1, aDeps)); 3971 static const char* bDeps[] = { "A" }; 3972 v8::RegisterExtension(new Extension("B", "", 1, bDeps)); 3973 last_location = NULL; 3974 v8::ExtensionConfiguration config(1, bDeps); 3975 v8::Handle<Context> context = Context::New(&config); 3976 CHECK(context.IsEmpty()); 3977 CHECK_NE(last_location, NULL); 3978} 3979 3980 3981static const char* js_code_causing_huge_string_flattening = 3982 "var str = 'X';" 3983 "for (var i = 0; i < 30; i++) {" 3984 " str = str + str;" 3985 "}" 3986 "str.match(/X/);"; 3987 3988 3989void OOMCallback(const char* location, const char* message) { 3990 exit(0); 3991} 3992 3993 3994TEST(RegexpOutOfMemory) { 3995 // Execute a script that causes out of memory when flattening a string. 3996 v8::HandleScope scope; 3997 v8::V8::SetFatalErrorHandler(OOMCallback); 3998 LocalContext context; 3999 Local<Script> script = 4000 Script::Compile(String::New(js_code_causing_huge_string_flattening)); 4001 last_location = NULL; 4002 Local<Value> result = script->Run(); 4003 4004 CHECK(false); // Should not return. 4005} 4006 4007 4008static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message, 4009 v8::Handle<Value> data) { 4010 CHECK_EQ(v8::Undefined(), data); 4011 CHECK(message->GetScriptResourceName()->IsUndefined()); 4012 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName()); 4013 message->GetLineNumber(); 4014 message->GetSourceLine(); 4015} 4016 4017 4018THREADED_TEST(ErrorWithMissingScriptInfo) { 4019 v8::HandleScope scope; 4020 LocalContext context; 4021 v8::V8::AddMessageListener(MissingScriptInfoMessageListener); 4022 Script::Compile(v8_str("throw Error()"))->Run(); 4023 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener); 4024} 4025 4026 4027int global_index = 0; 4028 4029class Snorkel { 4030 public: 4031 Snorkel() { index_ = global_index++; } 4032 int index_; 4033}; 4034 4035class Whammy { 4036 public: 4037 Whammy() { 4038 cursor_ = 0; 4039 } 4040 ~Whammy() { 4041 script_.Dispose(); 4042 } 4043 v8::Handle<Script> getScript() { 4044 if (script_.IsEmpty()) 4045 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo")); 4046 return Local<Script>(*script_); 4047 } 4048 4049 public: 4050 static const int kObjectCount = 256; 4051 int cursor_; 4052 v8::Persistent<v8::Object> objects_[kObjectCount]; 4053 v8::Persistent<Script> script_; 4054}; 4055 4056static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) { 4057 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data); 4058 delete snorkel; 4059 obj.ClearWeak(); 4060} 4061 4062v8::Handle<Value> WhammyPropertyGetter(Local<String> name, 4063 const AccessorInfo& info) { 4064 Whammy* whammy = 4065 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); 4066 4067 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_]; 4068 4069 v8::Handle<v8::Object> obj = v8::Object::New(); 4070 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj); 4071 if (!prev.IsEmpty()) { 4072 prev->Set(v8_str("next"), obj); 4073 prev.MakeWeak(new Snorkel(), &HandleWeakReference); 4074 whammy->objects_[whammy->cursor_].Clear(); 4075 } 4076 whammy->objects_[whammy->cursor_] = global; 4077 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount; 4078 return whammy->getScript()->Run(); 4079} 4080 4081THREADED_TEST(WeakReference) { 4082 v8::HandleScope handle_scope; 4083 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New(); 4084 Whammy* whammy = new Whammy(); 4085 templ->SetNamedPropertyHandler(WhammyPropertyGetter, 4086 0, 0, 0, 0, 4087 v8::External::New(whammy)); 4088 const char* extension_list[] = { "v8/gc" }; 4089 v8::ExtensionConfiguration extensions(1, extension_list); 4090 v8::Persistent<Context> context = Context::New(&extensions); 4091 Context::Scope context_scope(context); 4092 4093 v8::Handle<v8::Object> interceptor = templ->NewInstance(); 4094 context->Global()->Set(v8_str("whammy"), interceptor); 4095 const char* code = 4096 "var last;" 4097 "for (var i = 0; i < 10000; i++) {" 4098 " var obj = whammy.length;" 4099 " if (last) last.next = obj;" 4100 " last = obj;" 4101 "}" 4102 "gc();" 4103 "4"; 4104 v8::Handle<Value> result = CompileRun(code); 4105 CHECK_EQ(4.0, result->NumberValue()); 4106 delete whammy; 4107 context.Dispose(); 4108} 4109 4110 4111static bool in_scavenge = false; 4112static int last = -1; 4113 4114static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) { 4115 CHECK_EQ(-1, last); 4116 last = 0; 4117 obj.Dispose(); 4118 obj.Clear(); 4119 in_scavenge = true; 4120 i::Heap::PerformScavenge(); 4121 in_scavenge = false; 4122 *(reinterpret_cast<bool*>(data)) = true; 4123} 4124 4125static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj, 4126 void* data) { 4127 CHECK_EQ(0, last); 4128 last = 1; 4129 *(reinterpret_cast<bool*>(data)) = in_scavenge; 4130 obj.Dispose(); 4131 obj.Clear(); 4132} 4133 4134THREADED_TEST(NoWeakRefCallbacksInScavenge) { 4135 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks. 4136 // Calling callbacks from scavenges is unsafe as objects held by those 4137 // handlers might have become strongly reachable, but scavenge doesn't 4138 // check that. 4139 v8::Persistent<Context> context = Context::New(); 4140 Context::Scope context_scope(context); 4141 4142 v8::Persistent<v8::Object> object_a; 4143 v8::Persistent<v8::Object> object_b; 4144 4145 { 4146 v8::HandleScope handle_scope; 4147 object_b = v8::Persistent<v8::Object>::New(v8::Object::New()); 4148 object_a = v8::Persistent<v8::Object>::New(v8::Object::New()); 4149 } 4150 4151 bool object_a_disposed = false; 4152 object_a.MakeWeak(&object_a_disposed, &ForceScavenge); 4153 bool released_in_scavenge = false; 4154 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge); 4155 4156 while (!object_a_disposed) { 4157 i::Heap::CollectAllGarbage(false); 4158 } 4159 CHECK(!released_in_scavenge); 4160} 4161 4162 4163v8::Handle<Function> args_fun; 4164 4165 4166static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) { 4167 ApiTestFuzzer::Fuzz(); 4168 CHECK_EQ(args_fun, args.Callee()); 4169 CHECK_EQ(3, args.Length()); 4170 CHECK_EQ(v8::Integer::New(1), args[0]); 4171 CHECK_EQ(v8::Integer::New(2), args[1]); 4172 CHECK_EQ(v8::Integer::New(3), args[2]); 4173 CHECK_EQ(v8::Undefined(), args[3]); 4174 v8::HandleScope scope; 4175 i::Heap::CollectAllGarbage(false); 4176 return v8::Undefined(); 4177} 4178 4179 4180THREADED_TEST(Arguments) { 4181 v8::HandleScope scope; 4182 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 4183 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback)); 4184 LocalContext context(NULL, global); 4185 args_fun = context->Global()->Get(v8_str("f")).As<Function>(); 4186 v8_compile("f(1, 2, 3)")->Run(); 4187} 4188 4189 4190static v8::Handle<Value> NoBlockGetterX(Local<String> name, 4191 const AccessorInfo&) { 4192 return v8::Handle<Value>(); 4193} 4194 4195 4196static v8::Handle<Value> NoBlockGetterI(uint32_t index, 4197 const AccessorInfo&) { 4198 return v8::Handle<Value>(); 4199} 4200 4201 4202static v8::Handle<v8::Boolean> PDeleter(Local<String> name, 4203 const AccessorInfo&) { 4204 if (!name->Equals(v8_str("foo"))) { 4205 return v8::Handle<v8::Boolean>(); // not intercepted 4206 } 4207 4208 return v8::False(); // intercepted, and don't delete the property 4209} 4210 4211 4212static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) { 4213 if (index != 2) { 4214 return v8::Handle<v8::Boolean>(); // not intercepted 4215 } 4216 4217 return v8::False(); // intercepted, and don't delete the property 4218} 4219 4220 4221THREADED_TEST(Deleter) { 4222 v8::HandleScope scope; 4223 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4224 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL); 4225 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL); 4226 LocalContext context; 4227 context->Global()->Set(v8_str("k"), obj->NewInstance()); 4228 CompileRun( 4229 "k.foo = 'foo';" 4230 "k.bar = 'bar';" 4231 "k[2] = 2;" 4232 "k[4] = 4;"); 4233 CHECK(v8_compile("delete k.foo")->Run()->IsFalse()); 4234 CHECK(v8_compile("delete k.bar")->Run()->IsTrue()); 4235 4236 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo")); 4237 CHECK(v8_compile("k.bar")->Run()->IsUndefined()); 4238 4239 CHECK(v8_compile("delete k[2]")->Run()->IsFalse()); 4240 CHECK(v8_compile("delete k[4]")->Run()->IsTrue()); 4241 4242 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2)); 4243 CHECK(v8_compile("k[4]")->Run()->IsUndefined()); 4244} 4245 4246 4247static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) { 4248 ApiTestFuzzer::Fuzz(); 4249 if (name->Equals(v8_str("foo")) || 4250 name->Equals(v8_str("bar")) || 4251 name->Equals(v8_str("baz"))) { 4252 return v8::Undefined(); 4253 } 4254 return v8::Handle<Value>(); 4255} 4256 4257 4258static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) { 4259 ApiTestFuzzer::Fuzz(); 4260 if (index == 0 || index == 1) return v8::Undefined(); 4261 return v8::Handle<Value>(); 4262} 4263 4264 4265static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) { 4266 ApiTestFuzzer::Fuzz(); 4267 v8::Handle<v8::Array> result = v8::Array::New(3); 4268 result->Set(v8::Integer::New(0), v8_str("foo")); 4269 result->Set(v8::Integer::New(1), v8_str("bar")); 4270 result->Set(v8::Integer::New(2), v8_str("baz")); 4271 return result; 4272} 4273 4274 4275static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) { 4276 ApiTestFuzzer::Fuzz(); 4277 v8::Handle<v8::Array> result = v8::Array::New(2); 4278 result->Set(v8::Integer::New(0), v8_str("0")); 4279 result->Set(v8::Integer::New(1), v8_str("1")); 4280 return result; 4281} 4282 4283 4284THREADED_TEST(Enumerators) { 4285 v8::HandleScope scope; 4286 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4287 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum); 4288 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum); 4289 LocalContext context; 4290 context->Global()->Set(v8_str("k"), obj->NewInstance()); 4291 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 4292 "k[10] = 0;" 4293 "k.a = 0;" 4294 "k[5] = 0;" 4295 "k.b = 0;" 4296 "k[4294967295] = 0;" 4297 "k.c = 0;" 4298 "k[4294967296] = 0;" 4299 "k.d = 0;" 4300 "k[140000] = 0;" 4301 "k.e = 0;" 4302 "k[30000000000] = 0;" 4303 "k.f = 0;" 4304 "var result = [];" 4305 "for (var prop in k) {" 4306 " result.push(prop);" 4307 "}" 4308 "result")); 4309 // Check that we get all the property names returned including the 4310 // ones from the enumerators in the right order: indexed properties 4311 // in numerical order, indexed interceptor properties, named 4312 // properties in insertion order, named interceptor properties. 4313 // This order is not mandated by the spec, so this test is just 4314 // documenting our behavior. 4315 CHECK_EQ(17, result->Length()); 4316 // Indexed properties in numerical order. 4317 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0))); 4318 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1))); 4319 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2))); 4320 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3))); 4321 // Indexed interceptor properties in the order they are returned 4322 // from the enumerator interceptor. 4323 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4))); 4324 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5))); 4325 // Named properties in insertion order. 4326 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6))); 4327 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7))); 4328 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8))); 4329 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9))); 4330 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10))); 4331 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11))); 4332 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12))); 4333 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13))); 4334 // Named interceptor properties. 4335 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14))); 4336 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15))); 4337 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16))); 4338} 4339 4340 4341int p_getter_count; 4342int p_getter_count2; 4343 4344 4345static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) { 4346 ApiTestFuzzer::Fuzz(); 4347 p_getter_count++; 4348 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 4349 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 4350 if (name->Equals(v8_str("p1"))) { 4351 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 4352 } else if (name->Equals(v8_str("p2"))) { 4353 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 4354 } else if (name->Equals(v8_str("p3"))) { 4355 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 4356 } else if (name->Equals(v8_str("p4"))) { 4357 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 4358 } 4359 return v8::Undefined(); 4360} 4361 4362 4363static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) { 4364 ApiTestFuzzer::Fuzz(); 4365 LocalContext context; 4366 context->Global()->Set(v8_str("o1"), obj->NewInstance()); 4367 CompileRun( 4368 "o1.__proto__ = { };" 4369 "var o2 = { __proto__: o1 };" 4370 "var o3 = { __proto__: o2 };" 4371 "var o4 = { __proto__: o3 };" 4372 "for (var i = 0; i < 10; i++) o4.p4;" 4373 "for (var i = 0; i < 10; i++) o3.p3;" 4374 "for (var i = 0; i < 10; i++) o2.p2;" 4375 "for (var i = 0; i < 10; i++) o1.p1;"); 4376} 4377 4378 4379static v8::Handle<Value> PGetter2(Local<String> name, 4380 const AccessorInfo& info) { 4381 ApiTestFuzzer::Fuzz(); 4382 p_getter_count2++; 4383 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 4384 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 4385 if (name->Equals(v8_str("p1"))) { 4386 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 4387 } else if (name->Equals(v8_str("p2"))) { 4388 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 4389 } else if (name->Equals(v8_str("p3"))) { 4390 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 4391 } else if (name->Equals(v8_str("p4"))) { 4392 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 4393 } 4394 return v8::Undefined(); 4395} 4396 4397 4398THREADED_TEST(GetterHolders) { 4399 v8::HandleScope scope; 4400 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4401 obj->SetAccessor(v8_str("p1"), PGetter); 4402 obj->SetAccessor(v8_str("p2"), PGetter); 4403 obj->SetAccessor(v8_str("p3"), PGetter); 4404 obj->SetAccessor(v8_str("p4"), PGetter); 4405 p_getter_count = 0; 4406 RunHolderTest(obj); 4407 CHECK_EQ(40, p_getter_count); 4408} 4409 4410 4411THREADED_TEST(PreInterceptorHolders) { 4412 v8::HandleScope scope; 4413 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4414 obj->SetNamedPropertyHandler(PGetter2); 4415 p_getter_count2 = 0; 4416 RunHolderTest(obj); 4417 CHECK_EQ(40, p_getter_count2); 4418} 4419 4420 4421THREADED_TEST(ObjectInstantiation) { 4422 v8::HandleScope scope; 4423 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 4424 templ->SetAccessor(v8_str("t"), PGetter2); 4425 LocalContext context; 4426 context->Global()->Set(v8_str("o"), templ->NewInstance()); 4427 for (int i = 0; i < 100; i++) { 4428 v8::HandleScope inner_scope; 4429 v8::Handle<v8::Object> obj = templ->NewInstance(); 4430 CHECK_NE(obj, context->Global()->Get(v8_str("o"))); 4431 context->Global()->Set(v8_str("o2"), obj); 4432 v8::Handle<Value> value = 4433 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run(); 4434 CHECK_EQ(v8::True(), value); 4435 context->Global()->Set(v8_str("o"), obj); 4436 } 4437} 4438 4439 4440static int StrCmp16(uint16_t* a, uint16_t* b) { 4441 while (true) { 4442 if (*a == 0 && *b == 0) return 0; 4443 if (*a != *b) return 0 + *a - *b; 4444 a++; 4445 b++; 4446 } 4447} 4448 4449 4450static int StrNCmp16(uint16_t* a, uint16_t* b, int n) { 4451 while (true) { 4452 if (n-- == 0) return 0; 4453 if (*a == 0 && *b == 0) return 0; 4454 if (*a != *b) return 0 + *a - *b; 4455 a++; 4456 b++; 4457 } 4458} 4459 4460 4461THREADED_TEST(StringWrite) { 4462 v8::HandleScope scope; 4463 v8::Handle<String> str = v8_str("abcde"); 4464 // abc<Icelandic eth><Unicode snowman>. 4465 v8::Handle<String> str2 = v8_str("abc\303\260\342\230\203"); 4466 4467 CHECK_EQ(5, str2->Length()); 4468 4469 char buf[100]; 4470 char utf8buf[100]; 4471 uint16_t wbuf[100]; 4472 int len; 4473 int charlen; 4474 4475 memset(utf8buf, 0x1, sizeof(utf8buf)); 4476 len = str2->WriteUtf8(utf8buf, sizeof(utf8buf), &charlen); 4477 CHECK_EQ(len, 9); 4478 CHECK_EQ(charlen, 5); 4479 CHECK_EQ(strcmp(utf8buf, "abc\303\260\342\230\203"), 0); 4480 4481 memset(utf8buf, 0x1, sizeof(utf8buf)); 4482 len = str2->WriteUtf8(utf8buf, 8, &charlen); 4483 CHECK_EQ(len, 8); 4484 CHECK_EQ(charlen, 5); 4485 CHECK_EQ(strncmp(utf8buf, "abc\303\260\342\230\203\1", 9), 0); 4486 4487 memset(utf8buf, 0x1, sizeof(utf8buf)); 4488 len = str2->WriteUtf8(utf8buf, 7, &charlen); 4489 CHECK_EQ(len, 5); 4490 CHECK_EQ(charlen, 4); 4491 CHECK_EQ(strncmp(utf8buf, "abc\303\260\1", 5), 0); 4492 4493 memset(utf8buf, 0x1, sizeof(utf8buf)); 4494 len = str2->WriteUtf8(utf8buf, 6, &charlen); 4495 CHECK_EQ(len, 5); 4496 CHECK_EQ(charlen, 4); 4497 CHECK_EQ(strncmp(utf8buf, "abc\303\260\1", 5), 0); 4498 4499 memset(utf8buf, 0x1, sizeof(utf8buf)); 4500 len = str2->WriteUtf8(utf8buf, 5, &charlen); 4501 CHECK_EQ(len, 5); 4502 CHECK_EQ(charlen, 4); 4503 CHECK_EQ(strncmp(utf8buf, "abc\303\260\1", 5), 0); 4504 4505 memset(utf8buf, 0x1, sizeof(utf8buf)); 4506 len = str2->WriteUtf8(utf8buf, 4, &charlen); 4507 CHECK_EQ(len, 3); 4508 CHECK_EQ(charlen, 3); 4509 CHECK_EQ(strncmp(utf8buf, "abc\1", 4), 0); 4510 4511 memset(utf8buf, 0x1, sizeof(utf8buf)); 4512 len = str2->WriteUtf8(utf8buf, 3, &charlen); 4513 CHECK_EQ(len, 3); 4514 CHECK_EQ(charlen, 3); 4515 CHECK_EQ(strncmp(utf8buf, "abc\1", 4), 0); 4516 4517 memset(utf8buf, 0x1, sizeof(utf8buf)); 4518 len = str2->WriteUtf8(utf8buf, 2, &charlen); 4519 CHECK_EQ(len, 2); 4520 CHECK_EQ(charlen, 2); 4521 CHECK_EQ(strncmp(utf8buf, "ab\1", 3), 0); 4522 4523 memset(buf, 0x1, sizeof(buf)); 4524 memset(wbuf, 0x1, sizeof(wbuf)); 4525 len = str->WriteAscii(buf); 4526 CHECK_EQ(len, 5); 4527 len = str->Write(wbuf); 4528 CHECK_EQ(len, 5); 4529 CHECK_EQ(strcmp("abcde", buf), 0); 4530 uint16_t answer1[] = {'a', 'b', 'c', 'd', 'e', '\0'}; 4531 CHECK_EQ(StrCmp16(answer1, wbuf), 0); 4532 4533 memset(buf, 0x1, sizeof(buf)); 4534 memset(wbuf, 0x1, sizeof(wbuf)); 4535 len = str->WriteAscii(buf, 0, 4); 4536 CHECK_EQ(len, 4); 4537 len = str->Write(wbuf, 0, 4); 4538 CHECK_EQ(len, 4); 4539 CHECK_EQ(strncmp("abcd\1", buf, 5), 0); 4540 uint16_t answer2[] = {'a', 'b', 'c', 'd', 0x101}; 4541 CHECK_EQ(StrNCmp16(answer2, wbuf, 5), 0); 4542 4543 memset(buf, 0x1, sizeof(buf)); 4544 memset(wbuf, 0x1, sizeof(wbuf)); 4545 len = str->WriteAscii(buf, 0, 5); 4546 CHECK_EQ(len, 5); 4547 len = str->Write(wbuf, 0, 5); 4548 CHECK_EQ(len, 5); 4549 CHECK_EQ(strncmp("abcde\1", buf, 6), 0); 4550 uint16_t answer3[] = {'a', 'b', 'c', 'd', 'e', 0x101}; 4551 CHECK_EQ(StrNCmp16(answer3, wbuf, 6), 0); 4552 4553 memset(buf, 0x1, sizeof(buf)); 4554 memset(wbuf, 0x1, sizeof(wbuf)); 4555 len = str->WriteAscii(buf, 0, 6); 4556 CHECK_EQ(len, 5); 4557 len = str->Write(wbuf, 0, 6); 4558 CHECK_EQ(len, 5); 4559 CHECK_EQ(strcmp("abcde", buf), 0); 4560 uint16_t answer4[] = {'a', 'b', 'c', 'd', 'e', '\0'}; 4561 CHECK_EQ(StrCmp16(answer4, wbuf), 0); 4562 4563 memset(buf, 0x1, sizeof(buf)); 4564 memset(wbuf, 0x1, sizeof(wbuf)); 4565 len = str->WriteAscii(buf, 4, -1); 4566 CHECK_EQ(len, 1); 4567 len = str->Write(wbuf, 4, -1); 4568 CHECK_EQ(len, 1); 4569 CHECK_EQ(strcmp("e", buf), 0); 4570 uint16_t answer5[] = {'e', '\0'}; 4571 CHECK_EQ(StrCmp16(answer5, wbuf), 0); 4572 4573 memset(buf, 0x1, sizeof(buf)); 4574 memset(wbuf, 0x1, sizeof(wbuf)); 4575 len = str->WriteAscii(buf, 4, 6); 4576 CHECK_EQ(len, 1); 4577 len = str->Write(wbuf, 4, 6); 4578 CHECK_EQ(len, 1); 4579 CHECK_EQ(strcmp("e", buf), 0); 4580 CHECK_EQ(StrCmp16(answer5, wbuf), 0); 4581 4582 memset(buf, 0x1, sizeof(buf)); 4583 memset(wbuf, 0x1, sizeof(wbuf)); 4584 len = str->WriteAscii(buf, 4, 1); 4585 CHECK_EQ(len, 1); 4586 len = str->Write(wbuf, 4, 1); 4587 CHECK_EQ(len, 1); 4588 CHECK_EQ(strncmp("e\1", buf, 2), 0); 4589 uint16_t answer6[] = {'e', 0x101}; 4590 CHECK_EQ(StrNCmp16(answer6, wbuf, 2), 0); 4591 4592 memset(buf, 0x1, sizeof(buf)); 4593 memset(wbuf, 0x1, sizeof(wbuf)); 4594 len = str->WriteAscii(buf, 3, 1); 4595 CHECK_EQ(len, 1); 4596 len = str->Write(wbuf, 3, 1); 4597 CHECK_EQ(len, 1); 4598 CHECK_EQ(strncmp("d\1", buf, 2), 0); 4599 uint16_t answer7[] = {'d', 0x101}; 4600 CHECK_EQ(StrNCmp16(answer7, wbuf, 2), 0); 4601} 4602 4603 4604THREADED_TEST(ToArrayIndex) { 4605 v8::HandleScope scope; 4606 LocalContext context; 4607 4608 v8::Handle<String> str = v8_str("42"); 4609 v8::Handle<v8::Uint32> index = str->ToArrayIndex(); 4610 CHECK(!index.IsEmpty()); 4611 CHECK_EQ(42.0, index->Uint32Value()); 4612 str = v8_str("42asdf"); 4613 index = str->ToArrayIndex(); 4614 CHECK(index.IsEmpty()); 4615 str = v8_str("-42"); 4616 index = str->ToArrayIndex(); 4617 CHECK(index.IsEmpty()); 4618 str = v8_str("4294967295"); 4619 index = str->ToArrayIndex(); 4620 CHECK(!index.IsEmpty()); 4621 CHECK_EQ(4294967295.0, index->Uint32Value()); 4622 v8::Handle<v8::Number> num = v8::Number::New(1); 4623 index = num->ToArrayIndex(); 4624 CHECK(!index.IsEmpty()); 4625 CHECK_EQ(1.0, index->Uint32Value()); 4626 num = v8::Number::New(-1); 4627 index = num->ToArrayIndex(); 4628 CHECK(index.IsEmpty()); 4629 v8::Handle<v8::Object> obj = v8::Object::New(); 4630 index = obj->ToArrayIndex(); 4631 CHECK(index.IsEmpty()); 4632} 4633 4634 4635THREADED_TEST(ErrorConstruction) { 4636 v8::HandleScope scope; 4637 LocalContext context; 4638 4639 v8::Handle<String> foo = v8_str("foo"); 4640 v8::Handle<String> message = v8_str("message"); 4641 v8::Handle<Value> range_error = v8::Exception::RangeError(foo); 4642 CHECK(range_error->IsObject()); 4643 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>(); 4644 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo)); 4645 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo); 4646 CHECK(reference_error->IsObject()); 4647 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo)); 4648 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo); 4649 CHECK(syntax_error->IsObject()); 4650 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo)); 4651 v8::Handle<Value> type_error = v8::Exception::TypeError(foo); 4652 CHECK(type_error->IsObject()); 4653 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo)); 4654 v8::Handle<Value> error = v8::Exception::Error(foo); 4655 CHECK(error->IsObject()); 4656 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo)); 4657} 4658 4659 4660static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) { 4661 ApiTestFuzzer::Fuzz(); 4662 return v8_num(10); 4663} 4664 4665 4666static void YSetter(Local<String> name, 4667 Local<Value> value, 4668 const AccessorInfo& info) { 4669 if (info.This()->Has(name)) { 4670 info.This()->Delete(name); 4671 } 4672 info.This()->Set(name, value); 4673} 4674 4675 4676THREADED_TEST(DeleteAccessor) { 4677 v8::HandleScope scope; 4678 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4679 obj->SetAccessor(v8_str("y"), YGetter, YSetter); 4680 LocalContext context; 4681 v8::Handle<v8::Object> holder = obj->NewInstance(); 4682 context->Global()->Set(v8_str("holder"), holder); 4683 v8::Handle<Value> result = CompileRun( 4684 "holder.y = 11; holder.y = 12; holder.y"); 4685 CHECK_EQ(12, result->Uint32Value()); 4686} 4687 4688 4689THREADED_TEST(TypeSwitch) { 4690 v8::HandleScope scope; 4691 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New(); 4692 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New(); 4693 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New(); 4694 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 }; 4695 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs); 4696 LocalContext context; 4697 v8::Handle<v8::Object> obj0 = v8::Object::New(); 4698 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance(); 4699 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance(); 4700 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance(); 4701 for (int i = 0; i < 10; i++) { 4702 CHECK_EQ(0, type_switch->match(obj0)); 4703 CHECK_EQ(1, type_switch->match(obj1)); 4704 CHECK_EQ(2, type_switch->match(obj2)); 4705 CHECK_EQ(3, type_switch->match(obj3)); 4706 CHECK_EQ(3, type_switch->match(obj3)); 4707 CHECK_EQ(2, type_switch->match(obj2)); 4708 CHECK_EQ(1, type_switch->match(obj1)); 4709 CHECK_EQ(0, type_switch->match(obj0)); 4710 } 4711} 4712 4713 4714// For use within the TestSecurityHandler() test. 4715static bool g_security_callback_result = false; 4716static bool NamedSecurityTestCallback(Local<v8::Object> global, 4717 Local<Value> name, 4718 v8::AccessType type, 4719 Local<Value> data) { 4720 // Always allow read access. 4721 if (type == v8::ACCESS_GET) 4722 return true; 4723 4724 // Sometimes allow other access. 4725 return g_security_callback_result; 4726} 4727 4728 4729static bool IndexedSecurityTestCallback(Local<v8::Object> global, 4730 uint32_t key, 4731 v8::AccessType type, 4732 Local<Value> data) { 4733 // Always allow read access. 4734 if (type == v8::ACCESS_GET) 4735 return true; 4736 4737 // Sometimes allow other access. 4738 return g_security_callback_result; 4739} 4740 4741 4742static int trouble_nesting = 0; 4743static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) { 4744 ApiTestFuzzer::Fuzz(); 4745 trouble_nesting++; 4746 4747 // Call a JS function that throws an uncaught exception. 4748 Local<v8::Object> arg_this = Context::GetCurrent()->Global(); 4749 Local<Value> trouble_callee = (trouble_nesting == 3) ? 4750 arg_this->Get(v8_str("trouble_callee")) : 4751 arg_this->Get(v8_str("trouble_caller")); 4752 CHECK(trouble_callee->IsFunction()); 4753 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL); 4754} 4755 4756 4757static int report_count = 0; 4758static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>, 4759 v8::Handle<Value>) { 4760 report_count++; 4761} 4762 4763 4764// Counts uncaught exceptions, but other tests running in parallel 4765// also have uncaught exceptions. 4766TEST(ApiUncaughtException) { 4767 report_count = 0; 4768 v8::HandleScope scope; 4769 LocalContext env; 4770 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener); 4771 4772 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 4773 v8::Local<v8::Object> global = env->Global(); 4774 global->Set(v8_str("trouble"), fun->GetFunction()); 4775 4776 Script::Compile(v8_str("function trouble_callee() {" 4777 " var x = null;" 4778 " return x.foo;" 4779 "};" 4780 "function trouble_caller() {" 4781 " trouble();" 4782 "};"))->Run(); 4783 Local<Value> trouble = global->Get(v8_str("trouble")); 4784 CHECK(trouble->IsFunction()); 4785 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee")); 4786 CHECK(trouble_callee->IsFunction()); 4787 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller")); 4788 CHECK(trouble_caller->IsFunction()); 4789 Function::Cast(*trouble_caller)->Call(global, 0, NULL); 4790 CHECK_EQ(1, report_count); 4791 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener); 4792} 4793 4794static const char* script_resource_name = "ExceptionInNativeScript.js"; 4795static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message, 4796 v8::Handle<Value>) { 4797 v8::Handle<v8::Value> name_val = message->GetScriptResourceName(); 4798 CHECK(!name_val.IsEmpty() && name_val->IsString()); 4799 v8::String::AsciiValue name(message->GetScriptResourceName()); 4800 CHECK_EQ(script_resource_name, *name); 4801 CHECK_EQ(3, message->GetLineNumber()); 4802 v8::String::AsciiValue source_line(message->GetSourceLine()); 4803 CHECK_EQ(" new o.foo();", *source_line); 4804} 4805 4806TEST(ExceptionInNativeScript) { 4807 v8::HandleScope scope; 4808 LocalContext env; 4809 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener); 4810 4811 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 4812 v8::Local<v8::Object> global = env->Global(); 4813 global->Set(v8_str("trouble"), fun->GetFunction()); 4814 4815 Script::Compile(v8_str("function trouble() {\n" 4816 " var o = {};\n" 4817 " new o.foo();\n" 4818 "};"), v8::String::New(script_resource_name))->Run(); 4819 Local<Value> trouble = global->Get(v8_str("trouble")); 4820 CHECK(trouble->IsFunction()); 4821 Function::Cast(*trouble)->Call(global, 0, NULL); 4822 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener); 4823} 4824 4825 4826TEST(CompilationErrorUsingTryCatchHandler) { 4827 v8::HandleScope scope; 4828 LocalContext env; 4829 v8::TryCatch try_catch; 4830 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile.")); 4831 CHECK_NE(NULL, *try_catch.Exception()); 4832 CHECK(try_catch.HasCaught()); 4833} 4834 4835 4836TEST(TryCatchFinallyUsingTryCatchHandler) { 4837 v8::HandleScope scope; 4838 LocalContext env; 4839 v8::TryCatch try_catch; 4840 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run(); 4841 CHECK(!try_catch.HasCaught()); 4842 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run(); 4843 CHECK(try_catch.HasCaught()); 4844 try_catch.Reset(); 4845 Script::Compile(v8_str("(function() {" 4846 "try { throw ''; } finally { return; }" 4847 "})()"))->Run(); 4848 CHECK(!try_catch.HasCaught()); 4849 Script::Compile(v8_str("(function()" 4850 " { try { throw ''; } finally { throw 0; }" 4851 "})()"))->Run(); 4852 CHECK(try_catch.HasCaught()); 4853} 4854 4855 4856// SecurityHandler can't be run twice 4857TEST(SecurityHandler) { 4858 v8::HandleScope scope0; 4859 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 4860 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback, 4861 IndexedSecurityTestCallback); 4862 // Create an environment 4863 v8::Persistent<Context> context0 = 4864 Context::New(NULL, global_template); 4865 context0->Enter(); 4866 4867 v8::Handle<v8::Object> global0 = context0->Global(); 4868 v8::Handle<Script> script0 = v8_compile("foo = 111"); 4869 script0->Run(); 4870 global0->Set(v8_str("0"), v8_num(999)); 4871 v8::Handle<Value> foo0 = global0->Get(v8_str("foo")); 4872 CHECK_EQ(111, foo0->Int32Value()); 4873 v8::Handle<Value> z0 = global0->Get(v8_str("0")); 4874 CHECK_EQ(999, z0->Int32Value()); 4875 4876 // Create another environment, should fail security checks. 4877 v8::HandleScope scope1; 4878 4879 v8::Persistent<Context> context1 = 4880 Context::New(NULL, global_template); 4881 context1->Enter(); 4882 4883 v8::Handle<v8::Object> global1 = context1->Global(); 4884 global1->Set(v8_str("othercontext"), global0); 4885 // This set will fail the security check. 4886 v8::Handle<Script> script1 = 4887 v8_compile("othercontext.foo = 222; othercontext[0] = 888;"); 4888 script1->Run(); 4889 // This read will pass the security check. 4890 v8::Handle<Value> foo1 = global0->Get(v8_str("foo")); 4891 CHECK_EQ(111, foo1->Int32Value()); 4892 // This read will pass the security check. 4893 v8::Handle<Value> z1 = global0->Get(v8_str("0")); 4894 CHECK_EQ(999, z1->Int32Value()); 4895 4896 // Create another environment, should pass security checks. 4897 { g_security_callback_result = true; // allow security handler to pass. 4898 v8::HandleScope scope2; 4899 LocalContext context2; 4900 v8::Handle<v8::Object> global2 = context2->Global(); 4901 global2->Set(v8_str("othercontext"), global0); 4902 v8::Handle<Script> script2 = 4903 v8_compile("othercontext.foo = 333; othercontext[0] = 888;"); 4904 script2->Run(); 4905 v8::Handle<Value> foo2 = global0->Get(v8_str("foo")); 4906 CHECK_EQ(333, foo2->Int32Value()); 4907 v8::Handle<Value> z2 = global0->Get(v8_str("0")); 4908 CHECK_EQ(888, z2->Int32Value()); 4909 } 4910 4911 context1->Exit(); 4912 context1.Dispose(); 4913 4914 context0->Exit(); 4915 context0.Dispose(); 4916} 4917 4918 4919THREADED_TEST(SecurityChecks) { 4920 v8::HandleScope handle_scope; 4921 LocalContext env1; 4922 v8::Persistent<Context> env2 = Context::New(); 4923 4924 Local<Value> foo = v8_str("foo"); 4925 Local<Value> bar = v8_str("bar"); 4926 4927 // Set to the same domain. 4928 env1->SetSecurityToken(foo); 4929 4930 // Create a function in env1. 4931 Script::Compile(v8_str("spy=function(){return spy;}"))->Run(); 4932 Local<Value> spy = env1->Global()->Get(v8_str("spy")); 4933 CHECK(spy->IsFunction()); 4934 4935 // Create another function accessing global objects. 4936 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run(); 4937 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2")); 4938 CHECK(spy2->IsFunction()); 4939 4940 // Switch to env2 in the same domain and invoke spy on env2. 4941 { 4942 env2->SetSecurityToken(foo); 4943 // Enter env2 4944 Context::Scope scope_env2(env2); 4945 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL); 4946 CHECK(result->IsFunction()); 4947 } 4948 4949 { 4950 env2->SetSecurityToken(bar); 4951 Context::Scope scope_env2(env2); 4952 4953 // Call cross_domain_call, it should throw an exception 4954 v8::TryCatch try_catch; 4955 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL); 4956 CHECK(try_catch.HasCaught()); 4957 } 4958 4959 env2.Dispose(); 4960} 4961 4962 4963// Regression test case for issue 1183439. 4964THREADED_TEST(SecurityChecksForPrototypeChain) { 4965 v8::HandleScope scope; 4966 LocalContext current; 4967 v8::Persistent<Context> other = Context::New(); 4968 4969 // Change context to be able to get to the Object function in the 4970 // other context without hitting the security checks. 4971 v8::Local<Value> other_object; 4972 { Context::Scope scope(other); 4973 other_object = other->Global()->Get(v8_str("Object")); 4974 other->Global()->Set(v8_num(42), v8_num(87)); 4975 } 4976 4977 current->Global()->Set(v8_str("other"), other->Global()); 4978 CHECK(v8_compile("other")->Run()->Equals(other->Global())); 4979 4980 // Make sure the security check fails here and we get an undefined 4981 // result instead of getting the Object function. Repeat in a loop 4982 // to make sure to exercise the IC code. 4983 v8::Local<Script> access_other0 = v8_compile("other.Object"); 4984 v8::Local<Script> access_other1 = v8_compile("other[42]"); 4985 for (int i = 0; i < 5; i++) { 4986 CHECK(!access_other0->Run()->Equals(other_object)); 4987 CHECK(access_other0->Run()->IsUndefined()); 4988 CHECK(!access_other1->Run()->Equals(v8_num(87))); 4989 CHECK(access_other1->Run()->IsUndefined()); 4990 } 4991 4992 // Create an object that has 'other' in its prototype chain and make 4993 // sure we cannot access the Object function indirectly through 4994 // that. Repeat in a loop to make sure to exercise the IC code. 4995 v8_compile("function F() { };" 4996 "F.prototype = other;" 4997 "var f = new F();")->Run(); 4998 v8::Local<Script> access_f0 = v8_compile("f.Object"); 4999 v8::Local<Script> access_f1 = v8_compile("f[42]"); 5000 for (int j = 0; j < 5; j++) { 5001 CHECK(!access_f0->Run()->Equals(other_object)); 5002 CHECK(access_f0->Run()->IsUndefined()); 5003 CHECK(!access_f1->Run()->Equals(v8_num(87))); 5004 CHECK(access_f1->Run()->IsUndefined()); 5005 } 5006 5007 // Now it gets hairy: Set the prototype for the other global object 5008 // to be the current global object. The prototype chain for 'f' now 5009 // goes through 'other' but ends up in the current global object. 5010 { Context::Scope scope(other); 5011 other->Global()->Set(v8_str("__proto__"), current->Global()); 5012 } 5013 // Set a named and an index property on the current global 5014 // object. To force the lookup to go through the other global object, 5015 // the properties must not exist in the other global object. 5016 current->Global()->Set(v8_str("foo"), v8_num(100)); 5017 current->Global()->Set(v8_num(99), v8_num(101)); 5018 // Try to read the properties from f and make sure that the access 5019 // gets stopped by the security checks on the other global object. 5020 Local<Script> access_f2 = v8_compile("f.foo"); 5021 Local<Script> access_f3 = v8_compile("f[99]"); 5022 for (int k = 0; k < 5; k++) { 5023 CHECK(!access_f2->Run()->Equals(v8_num(100))); 5024 CHECK(access_f2->Run()->IsUndefined()); 5025 CHECK(!access_f3->Run()->Equals(v8_num(101))); 5026 CHECK(access_f3->Run()->IsUndefined()); 5027 } 5028 other.Dispose(); 5029} 5030 5031 5032THREADED_TEST(CrossDomainDelete) { 5033 v8::HandleScope handle_scope; 5034 LocalContext env1; 5035 v8::Persistent<Context> env2 = Context::New(); 5036 5037 Local<Value> foo = v8_str("foo"); 5038 Local<Value> bar = v8_str("bar"); 5039 5040 // Set to the same domain. 5041 env1->SetSecurityToken(foo); 5042 env2->SetSecurityToken(foo); 5043 5044 env1->Global()->Set(v8_str("prop"), v8_num(3)); 5045 env2->Global()->Set(v8_str("env1"), env1->Global()); 5046 5047 // Change env2 to a different domain and delete env1.prop. 5048 env2->SetSecurityToken(bar); 5049 { 5050 Context::Scope scope_env2(env2); 5051 Local<Value> result = 5052 Script::Compile(v8_str("delete env1.prop"))->Run(); 5053 CHECK(result->IsFalse()); 5054 } 5055 5056 // Check that env1.prop still exists. 5057 Local<Value> v = env1->Global()->Get(v8_str("prop")); 5058 CHECK(v->IsNumber()); 5059 CHECK_EQ(3, v->Int32Value()); 5060 5061 env2.Dispose(); 5062} 5063 5064 5065THREADED_TEST(CrossDomainIsPropertyEnumerable) { 5066 v8::HandleScope handle_scope; 5067 LocalContext env1; 5068 v8::Persistent<Context> env2 = Context::New(); 5069 5070 Local<Value> foo = v8_str("foo"); 5071 Local<Value> bar = v8_str("bar"); 5072 5073 // Set to the same domain. 5074 env1->SetSecurityToken(foo); 5075 env2->SetSecurityToken(foo); 5076 5077 env1->Global()->Set(v8_str("prop"), v8_num(3)); 5078 env2->Global()->Set(v8_str("env1"), env1->Global()); 5079 5080 // env1.prop is enumerable in env2. 5081 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')"); 5082 { 5083 Context::Scope scope_env2(env2); 5084 Local<Value> result = Script::Compile(test)->Run(); 5085 CHECK(result->IsTrue()); 5086 } 5087 5088 // Change env2 to a different domain and test again. 5089 env2->SetSecurityToken(bar); 5090 { 5091 Context::Scope scope_env2(env2); 5092 Local<Value> result = Script::Compile(test)->Run(); 5093 CHECK(result->IsFalse()); 5094 } 5095 5096 env2.Dispose(); 5097} 5098 5099 5100THREADED_TEST(CrossDomainForIn) { 5101 v8::HandleScope handle_scope; 5102 LocalContext env1; 5103 v8::Persistent<Context> env2 = Context::New(); 5104 5105 Local<Value> foo = v8_str("foo"); 5106 Local<Value> bar = v8_str("bar"); 5107 5108 // Set to the same domain. 5109 env1->SetSecurityToken(foo); 5110 env2->SetSecurityToken(foo); 5111 5112 env1->Global()->Set(v8_str("prop"), v8_num(3)); 5113 env2->Global()->Set(v8_str("env1"), env1->Global()); 5114 5115 // Change env2 to a different domain and set env1's global object 5116 // as the __proto__ of an object in env2 and enumerate properties 5117 // in for-in. It shouldn't enumerate properties on env1's global 5118 // object. 5119 env2->SetSecurityToken(bar); 5120 { 5121 Context::Scope scope_env2(env2); 5122 Local<Value> result = 5123 CompileRun("(function(){var obj = {'__proto__':env1};" 5124 "for (var p in obj)" 5125 " if (p == 'prop') return false;" 5126 "return true;})()"); 5127 CHECK(result->IsTrue()); 5128 } 5129 env2.Dispose(); 5130} 5131 5132 5133TEST(ContextDetachGlobal) { 5134 v8::HandleScope handle_scope; 5135 LocalContext env1; 5136 v8::Persistent<Context> env2 = Context::New(); 5137 5138 Local<v8::Object> global1 = env1->Global(); 5139 5140 Local<Value> foo = v8_str("foo"); 5141 5142 // Set to the same domain. 5143 env1->SetSecurityToken(foo); 5144 env2->SetSecurityToken(foo); 5145 5146 // Enter env2 5147 env2->Enter(); 5148 5149 // Create a function in env2 and add a reference to it in env1. 5150 Local<v8::Object> global2 = env2->Global(); 5151 global2->Set(v8_str("prop"), v8::Integer::New(1)); 5152 CompileRun("function getProp() {return prop;}"); 5153 5154 env1->Global()->Set(v8_str("getProp"), 5155 global2->Get(v8_str("getProp"))); 5156 5157 // Detach env2's global, and reuse the global object of env2 5158 env2->Exit(); 5159 env2->DetachGlobal(); 5160 // env2 has a new global object. 5161 CHECK(!env2->Global()->Equals(global2)); 5162 5163 v8::Persistent<Context> env3 = 5164 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 5165 env3->SetSecurityToken(v8_str("bar")); 5166 env3->Enter(); 5167 5168 Local<v8::Object> global3 = env3->Global(); 5169 CHECK_EQ(global2, global3); 5170 CHECK(global3->Get(v8_str("prop"))->IsUndefined()); 5171 CHECK(global3->Get(v8_str("getProp"))->IsUndefined()); 5172 global3->Set(v8_str("prop"), v8::Integer::New(-1)); 5173 global3->Set(v8_str("prop2"), v8::Integer::New(2)); 5174 env3->Exit(); 5175 5176 // Call getProp in env1, and it should return the value 1 5177 { 5178 Local<Value> get_prop = global1->Get(v8_str("getProp")); 5179 CHECK(get_prop->IsFunction()); 5180 v8::TryCatch try_catch; 5181 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL); 5182 CHECK(!try_catch.HasCaught()); 5183 CHECK_EQ(1, r->Int32Value()); 5184 } 5185 5186 // Check that env3 is not accessible from env1 5187 { 5188 Local<Value> r = global3->Get(v8_str("prop2")); 5189 CHECK(r->IsUndefined()); 5190 } 5191 5192 env2.Dispose(); 5193 env3.Dispose(); 5194} 5195 5196 5197TEST(DetachAndReattachGlobal) { 5198 v8::HandleScope scope; 5199 LocalContext env1; 5200 5201 // Create second environment. 5202 v8::Persistent<Context> env2 = Context::New(); 5203 5204 Local<Value> foo = v8_str("foo"); 5205 5206 // Set same security token for env1 and env2. 5207 env1->SetSecurityToken(foo); 5208 env2->SetSecurityToken(foo); 5209 5210 // Create a property on the global object in env2. 5211 { 5212 v8::Context::Scope scope(env2); 5213 env2->Global()->Set(v8_str("p"), v8::Integer::New(42)); 5214 } 5215 5216 // Create a reference to env2 global from env1 global. 5217 env1->Global()->Set(v8_str("other"), env2->Global()); 5218 5219 // Check that we have access to other.p in env2 from env1. 5220 Local<Value> result = CompileRun("other.p"); 5221 CHECK(result->IsInt32()); 5222 CHECK_EQ(42, result->Int32Value()); 5223 5224 // Hold on to global from env2 and detach global from env2. 5225 Local<v8::Object> global2 = env2->Global(); 5226 env2->DetachGlobal(); 5227 5228 // Check that the global has been detached. No other.p property can 5229 // be found. 5230 result = CompileRun("other.p"); 5231 CHECK(result->IsUndefined()); 5232 5233 // Reuse global2 for env3. 5234 v8::Persistent<Context> env3 = 5235 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 5236 CHECK_EQ(global2, env3->Global()); 5237 5238 // Start by using the same security token for env3 as for env1 and env2. 5239 env3->SetSecurityToken(foo); 5240 5241 // Create a property on the global object in env3. 5242 { 5243 v8::Context::Scope scope(env3); 5244 env3->Global()->Set(v8_str("p"), v8::Integer::New(24)); 5245 } 5246 5247 // Check that other.p is now the property in env3 and that we have access. 5248 result = CompileRun("other.p"); 5249 CHECK(result->IsInt32()); 5250 CHECK_EQ(24, result->Int32Value()); 5251 5252 // Change security token for env3 to something different from env1 and env2. 5253 env3->SetSecurityToken(v8_str("bar")); 5254 5255 // Check that we do not have access to other.p in env1. |other| is now 5256 // the global object for env3 which has a different security token, 5257 // so access should be blocked. 5258 result = CompileRun("other.p"); 5259 CHECK(result->IsUndefined()); 5260 5261 // Detach the global for env3 and reattach it to env2. 5262 env3->DetachGlobal(); 5263 env2->ReattachGlobal(global2); 5264 5265 // Check that we have access to other.p again in env1. |other| is now 5266 // the global object for env2 which has the same security token as env1. 5267 result = CompileRun("other.p"); 5268 CHECK(result->IsInt32()); 5269 CHECK_EQ(42, result->Int32Value()); 5270 5271 env2.Dispose(); 5272 env3.Dispose(); 5273} 5274 5275 5276static bool NamedAccessBlocker(Local<v8::Object> global, 5277 Local<Value> name, 5278 v8::AccessType type, 5279 Local<Value> data) { 5280 return Context::GetCurrent()->Global()->Equals(global); 5281} 5282 5283 5284static bool IndexedAccessBlocker(Local<v8::Object> global, 5285 uint32_t key, 5286 v8::AccessType type, 5287 Local<Value> data) { 5288 return Context::GetCurrent()->Global()->Equals(global); 5289} 5290 5291 5292static int g_echo_value = -1; 5293static v8::Handle<Value> EchoGetter(Local<String> name, 5294 const AccessorInfo& info) { 5295 return v8_num(g_echo_value); 5296} 5297 5298 5299static void EchoSetter(Local<String> name, 5300 Local<Value> value, 5301 const AccessorInfo&) { 5302 if (value->IsNumber()) 5303 g_echo_value = value->Int32Value(); 5304} 5305 5306 5307static v8::Handle<Value> UnreachableGetter(Local<String> name, 5308 const AccessorInfo& info) { 5309 CHECK(false); // This function should not be called.. 5310 return v8::Undefined(); 5311} 5312 5313 5314static void UnreachableSetter(Local<String>, Local<Value>, 5315 const AccessorInfo&) { 5316 CHECK(false); // This function should nto be called. 5317} 5318 5319 5320THREADED_TEST(AccessControl) { 5321 v8::HandleScope handle_scope; 5322 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 5323 5324 global_template->SetAccessCheckCallbacks(NamedAccessBlocker, 5325 IndexedAccessBlocker); 5326 5327 // Add an accessor accessible by cross-domain JS code. 5328 global_template->SetAccessor( 5329 v8_str("accessible_prop"), 5330 EchoGetter, EchoSetter, 5331 v8::Handle<Value>(), 5332 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); 5333 5334 // Add an accessor that is not accessible by cross-domain JS code. 5335 global_template->SetAccessor(v8_str("blocked_prop"), 5336 UnreachableGetter, UnreachableSetter, 5337 v8::Handle<Value>(), 5338 v8::DEFAULT); 5339 5340 // Create an environment 5341 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 5342 context0->Enter(); 5343 5344 v8::Handle<v8::Object> global0 = context0->Global(); 5345 5346 v8::HandleScope scope1; 5347 5348 v8::Persistent<Context> context1 = Context::New(); 5349 context1->Enter(); 5350 5351 v8::Handle<v8::Object> global1 = context1->Global(); 5352 global1->Set(v8_str("other"), global0); 5353 5354 v8::Handle<Value> value; 5355 5356 // Access blocked property 5357 value = v8_compile("other.blocked_prop = 1")->Run(); 5358 value = v8_compile("other.blocked_prop")->Run(); 5359 CHECK(value->IsUndefined()); 5360 5361 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run(); 5362 CHECK(value->IsFalse()); 5363 5364 // Access accessible property 5365 value = v8_compile("other.accessible_prop = 3")->Run(); 5366 CHECK(value->IsNumber()); 5367 CHECK_EQ(3, value->Int32Value()); 5368 CHECK_EQ(3, g_echo_value); 5369 5370 value = v8_compile("other.accessible_prop")->Run(); 5371 CHECK(value->IsNumber()); 5372 CHECK_EQ(3, value->Int32Value()); 5373 5374 value = 5375 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run(); 5376 CHECK(value->IsTrue()); 5377 5378 // Enumeration doesn't enumerate accessors from inaccessible objects in 5379 // the prototype chain even if the accessors are in themselves accessible. 5380 Local<Value> result = 5381 CompileRun("(function(){var obj = {'__proto__':other};" 5382 "for (var p in obj)" 5383 " if (p == 'accessible_prop' || p == 'blocked_prop') {" 5384 " return false;" 5385 " }" 5386 "return true;})()"); 5387 CHECK(result->IsTrue()); 5388 5389 context1->Exit(); 5390 context0->Exit(); 5391 context1.Dispose(); 5392 context0.Dispose(); 5393} 5394 5395 5396static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global, 5397 Local<Value> name, 5398 v8::AccessType type, 5399 Local<Value> data) { 5400 return false; 5401} 5402 5403 5404static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global, 5405 uint32_t key, 5406 v8::AccessType type, 5407 Local<Value> data) { 5408 return false; 5409} 5410 5411 5412THREADED_TEST(AccessControlGetOwnPropertyNames) { 5413 v8::HandleScope handle_scope; 5414 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(); 5415 5416 obj_template->Set(v8_str("x"), v8::Integer::New(42)); 5417 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker, 5418 GetOwnPropertyNamesIndexedBlocker); 5419 5420 // Create an environment 5421 v8::Persistent<Context> context0 = Context::New(NULL, obj_template); 5422 context0->Enter(); 5423 5424 v8::Handle<v8::Object> global0 = context0->Global(); 5425 5426 v8::HandleScope scope1; 5427 5428 v8::Persistent<Context> context1 = Context::New(); 5429 context1->Enter(); 5430 5431 v8::Handle<v8::Object> global1 = context1->Global(); 5432 global1->Set(v8_str("other"), global0); 5433 global1->Set(v8_str("object"), obj_template->NewInstance()); 5434 5435 v8::Handle<Value> value; 5436 5437 // Attempt to get the property names of the other global object and 5438 // of an object that requires access checks. Accessing the other 5439 // global object should be blocked by access checks on the global 5440 // proxy object. Accessing the object that requires access checks 5441 // is blocked by the access checks on the object itself. 5442 value = CompileRun("Object.getOwnPropertyNames(other).length == 0"); 5443 CHECK(value->IsTrue()); 5444 5445 value = CompileRun("Object.getOwnPropertyNames(object).length == 0"); 5446 CHECK(value->IsTrue()); 5447 5448 context1->Exit(); 5449 context0->Exit(); 5450 context1.Dispose(); 5451 context0.Dispose(); 5452} 5453 5454 5455static v8::Handle<v8::Array> NamedPropertyEnumerator(const AccessorInfo& info) { 5456 v8::Handle<v8::Array> result = v8::Array::New(1); 5457 result->Set(0, v8_str("x")); 5458 return result; 5459} 5460 5461 5462THREADED_TEST(GetOwnPropertyNamesWithInterceptor) { 5463 v8::HandleScope handle_scope; 5464 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(); 5465 5466 obj_template->Set(v8_str("x"), v8::Integer::New(42)); 5467 obj_template->SetNamedPropertyHandler(NULL, NULL, NULL, NULL, 5468 NamedPropertyEnumerator); 5469 5470 LocalContext context; 5471 v8::Handle<v8::Object> global = context->Global(); 5472 global->Set(v8_str("object"), obj_template->NewInstance()); 5473 5474 v8::Handle<Value> value = 5475 CompileRun("Object.getOwnPropertyNames(object).join(',')"); 5476 CHECK_EQ(v8_str("x"), value); 5477} 5478 5479 5480static v8::Handle<Value> ConstTenGetter(Local<String> name, 5481 const AccessorInfo& info) { 5482 return v8_num(10); 5483} 5484 5485 5486THREADED_TEST(CrossDomainAccessors) { 5487 v8::HandleScope handle_scope; 5488 5489 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(); 5490 5491 v8::Handle<v8::ObjectTemplate> global_template = 5492 func_template->InstanceTemplate(); 5493 5494 v8::Handle<v8::ObjectTemplate> proto_template = 5495 func_template->PrototypeTemplate(); 5496 5497 // Add an accessor to proto that's accessible by cross-domain JS code. 5498 proto_template->SetAccessor(v8_str("accessible"), 5499 ConstTenGetter, 0, 5500 v8::Handle<Value>(), 5501 v8::ALL_CAN_READ); 5502 5503 // Add an accessor that is not accessible by cross-domain JS code. 5504 global_template->SetAccessor(v8_str("unreachable"), 5505 UnreachableGetter, 0, 5506 v8::Handle<Value>(), 5507 v8::DEFAULT); 5508 5509 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 5510 context0->Enter(); 5511 5512 Local<v8::Object> global = context0->Global(); 5513 // Add a normal property that shadows 'accessible' 5514 global->Set(v8_str("accessible"), v8_num(11)); 5515 5516 // Enter a new context. 5517 v8::HandleScope scope1; 5518 v8::Persistent<Context> context1 = Context::New(); 5519 context1->Enter(); 5520 5521 v8::Handle<v8::Object> global1 = context1->Global(); 5522 global1->Set(v8_str("other"), global); 5523 5524 // Should return 10, instead of 11 5525 v8::Handle<Value> value = v8_compile("other.accessible")->Run(); 5526 CHECK(value->IsNumber()); 5527 CHECK_EQ(10, value->Int32Value()); 5528 5529 value = v8_compile("other.unreachable")->Run(); 5530 CHECK(value->IsUndefined()); 5531 5532 context1->Exit(); 5533 context0->Exit(); 5534 context1.Dispose(); 5535 context0.Dispose(); 5536} 5537 5538 5539static int named_access_count = 0; 5540static int indexed_access_count = 0; 5541 5542static bool NamedAccessCounter(Local<v8::Object> global, 5543 Local<Value> name, 5544 v8::AccessType type, 5545 Local<Value> data) { 5546 named_access_count++; 5547 return true; 5548} 5549 5550 5551static bool IndexedAccessCounter(Local<v8::Object> global, 5552 uint32_t key, 5553 v8::AccessType type, 5554 Local<Value> data) { 5555 indexed_access_count++; 5556 return true; 5557} 5558 5559 5560// This one is too easily disturbed by other tests. 5561TEST(AccessControlIC) { 5562 named_access_count = 0; 5563 indexed_access_count = 0; 5564 5565 v8::HandleScope handle_scope; 5566 5567 // Create an environment. 5568 v8::Persistent<Context> context0 = Context::New(); 5569 context0->Enter(); 5570 5571 // Create an object that requires access-check functions to be 5572 // called for cross-domain access. 5573 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 5574 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 5575 IndexedAccessCounter); 5576 Local<v8::Object> object = object_template->NewInstance(); 5577 5578 v8::HandleScope scope1; 5579 5580 // Create another environment. 5581 v8::Persistent<Context> context1 = Context::New(); 5582 context1->Enter(); 5583 5584 // Make easy access to the object from the other environment. 5585 v8::Handle<v8::Object> global1 = context1->Global(); 5586 global1->Set(v8_str("obj"), object); 5587 5588 v8::Handle<Value> value; 5589 5590 // Check that the named access-control function is called every time. 5591 CompileRun("function testProp(obj) {" 5592 " for (var i = 0; i < 10; i++) obj.prop = 1;" 5593 " for (var j = 0; j < 10; j++) obj.prop;" 5594 " return obj.prop" 5595 "}"); 5596 value = CompileRun("testProp(obj)"); 5597 CHECK(value->IsNumber()); 5598 CHECK_EQ(1, value->Int32Value()); 5599 CHECK_EQ(21, named_access_count); 5600 5601 // Check that the named access-control function is called every time. 5602 CompileRun("var p = 'prop';" 5603 "function testKeyed(obj) {" 5604 " for (var i = 0; i < 10; i++) obj[p] = 1;" 5605 " for (var j = 0; j < 10; j++) obj[p];" 5606 " return obj[p];" 5607 "}"); 5608 // Use obj which requires access checks. No inline caching is used 5609 // in that case. 5610 value = CompileRun("testKeyed(obj)"); 5611 CHECK(value->IsNumber()); 5612 CHECK_EQ(1, value->Int32Value()); 5613 CHECK_EQ(42, named_access_count); 5614 // Force the inline caches into generic state and try again. 5615 CompileRun("testKeyed({ a: 0 })"); 5616 CompileRun("testKeyed({ b: 0 })"); 5617 value = CompileRun("testKeyed(obj)"); 5618 CHECK(value->IsNumber()); 5619 CHECK_EQ(1, value->Int32Value()); 5620 CHECK_EQ(63, named_access_count); 5621 5622 // Check that the indexed access-control function is called every time. 5623 CompileRun("function testIndexed(obj) {" 5624 " for (var i = 0; i < 10; i++) obj[0] = 1;" 5625 " for (var j = 0; j < 10; j++) obj[0];" 5626 " return obj[0]" 5627 "}"); 5628 value = CompileRun("testIndexed(obj)"); 5629 CHECK(value->IsNumber()); 5630 CHECK_EQ(1, value->Int32Value()); 5631 CHECK_EQ(21, indexed_access_count); 5632 // Force the inline caches into generic state. 5633 CompileRun("testIndexed(new Array(1))"); 5634 // Test that the indexed access check is called. 5635 value = CompileRun("testIndexed(obj)"); 5636 CHECK(value->IsNumber()); 5637 CHECK_EQ(1, value->Int32Value()); 5638 CHECK_EQ(42, indexed_access_count); 5639 5640 // Check that the named access check is called when invoking 5641 // functions on an object that requires access checks. 5642 CompileRun("obj.f = function() {}"); 5643 CompileRun("function testCallNormal(obj) {" 5644 " for (var i = 0; i < 10; i++) obj.f();" 5645 "}"); 5646 CompileRun("testCallNormal(obj)"); 5647 CHECK_EQ(74, named_access_count); 5648 5649 // Force obj into slow case. 5650 value = CompileRun("delete obj.prop"); 5651 CHECK(value->BooleanValue()); 5652 // Force inline caches into dictionary probing mode. 5653 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);"); 5654 // Test that the named access check is called. 5655 value = CompileRun("testProp(obj);"); 5656 CHECK(value->IsNumber()); 5657 CHECK_EQ(1, value->Int32Value()); 5658 CHECK_EQ(96, named_access_count); 5659 5660 // Force the call inline cache into dictionary probing mode. 5661 CompileRun("o.f = function() {}; testCallNormal(o)"); 5662 // Test that the named access check is still called for each 5663 // invocation of the function. 5664 value = CompileRun("testCallNormal(obj)"); 5665 CHECK_EQ(106, named_access_count); 5666 5667 context1->Exit(); 5668 context0->Exit(); 5669 context1.Dispose(); 5670 context0.Dispose(); 5671} 5672 5673 5674static bool NamedAccessFlatten(Local<v8::Object> global, 5675 Local<Value> name, 5676 v8::AccessType type, 5677 Local<Value> data) { 5678 char buf[100]; 5679 int len; 5680 5681 CHECK(name->IsString()); 5682 5683 memset(buf, 0x1, sizeof(buf)); 5684 len = name.As<String>()->WriteAscii(buf); 5685 CHECK_EQ(4, len); 5686 5687 uint16_t buf2[100]; 5688 5689 memset(buf, 0x1, sizeof(buf)); 5690 len = name.As<String>()->Write(buf2); 5691 CHECK_EQ(4, len); 5692 5693 return true; 5694} 5695 5696 5697static bool IndexedAccessFlatten(Local<v8::Object> global, 5698 uint32_t key, 5699 v8::AccessType type, 5700 Local<Value> data) { 5701 return true; 5702} 5703 5704 5705// Regression test. In access checks, operations that may cause 5706// garbage collection are not allowed. It used to be the case that 5707// using the Write operation on a string could cause a garbage 5708// collection due to flattening of the string. This is no longer the 5709// case. 5710THREADED_TEST(AccessControlFlatten) { 5711 named_access_count = 0; 5712 indexed_access_count = 0; 5713 5714 v8::HandleScope handle_scope; 5715 5716 // Create an environment. 5717 v8::Persistent<Context> context0 = Context::New(); 5718 context0->Enter(); 5719 5720 // Create an object that requires access-check functions to be 5721 // called for cross-domain access. 5722 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 5723 object_template->SetAccessCheckCallbacks(NamedAccessFlatten, 5724 IndexedAccessFlatten); 5725 Local<v8::Object> object = object_template->NewInstance(); 5726 5727 v8::HandleScope scope1; 5728 5729 // Create another environment. 5730 v8::Persistent<Context> context1 = Context::New(); 5731 context1->Enter(); 5732 5733 // Make easy access to the object from the other environment. 5734 v8::Handle<v8::Object> global1 = context1->Global(); 5735 global1->Set(v8_str("obj"), object); 5736 5737 v8::Handle<Value> value; 5738 5739 value = v8_compile("var p = 'as' + 'df';")->Run(); 5740 value = v8_compile("obj[p];")->Run(); 5741 5742 context1->Exit(); 5743 context0->Exit(); 5744 context1.Dispose(); 5745 context0.Dispose(); 5746} 5747 5748 5749static v8::Handle<Value> AccessControlNamedGetter( 5750 Local<String>, const AccessorInfo&) { 5751 return v8::Integer::New(42); 5752} 5753 5754 5755static v8::Handle<Value> AccessControlNamedSetter( 5756 Local<String>, Local<Value> value, const AccessorInfo&) { 5757 return value; 5758} 5759 5760 5761static v8::Handle<Value> AccessControlIndexedGetter( 5762 uint32_t index, 5763 const AccessorInfo& info) { 5764 return v8_num(42); 5765} 5766 5767 5768static v8::Handle<Value> AccessControlIndexedSetter( 5769 uint32_t, Local<Value> value, const AccessorInfo&) { 5770 return value; 5771} 5772 5773 5774THREADED_TEST(AccessControlInterceptorIC) { 5775 named_access_count = 0; 5776 indexed_access_count = 0; 5777 5778 v8::HandleScope handle_scope; 5779 5780 // Create an environment. 5781 v8::Persistent<Context> context0 = Context::New(); 5782 context0->Enter(); 5783 5784 // Create an object that requires access-check functions to be 5785 // called for cross-domain access. The object also has interceptors 5786 // interceptor. 5787 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 5788 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 5789 IndexedAccessCounter); 5790 object_template->SetNamedPropertyHandler(AccessControlNamedGetter, 5791 AccessControlNamedSetter); 5792 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter, 5793 AccessControlIndexedSetter); 5794 Local<v8::Object> object = object_template->NewInstance(); 5795 5796 v8::HandleScope scope1; 5797 5798 // Create another environment. 5799 v8::Persistent<Context> context1 = Context::New(); 5800 context1->Enter(); 5801 5802 // Make easy access to the object from the other environment. 5803 v8::Handle<v8::Object> global1 = context1->Global(); 5804 global1->Set(v8_str("obj"), object); 5805 5806 v8::Handle<Value> value; 5807 5808 // Check that the named access-control function is called every time 5809 // eventhough there is an interceptor on the object. 5810 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run(); 5811 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;" 5812 "obj.x")->Run(); 5813 CHECK(value->IsNumber()); 5814 CHECK_EQ(42, value->Int32Value()); 5815 CHECK_EQ(21, named_access_count); 5816 5817 value = v8_compile("var p = 'x';")->Run(); 5818 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run(); 5819 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];" 5820 "obj[p]")->Run(); 5821 CHECK(value->IsNumber()); 5822 CHECK_EQ(42, value->Int32Value()); 5823 CHECK_EQ(42, named_access_count); 5824 5825 // Check that the indexed access-control function is called every 5826 // time eventhough there is an interceptor on the object. 5827 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run(); 5828 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];" 5829 "obj[0]")->Run(); 5830 CHECK(value->IsNumber()); 5831 CHECK_EQ(42, value->Int32Value()); 5832 CHECK_EQ(21, indexed_access_count); 5833 5834 context1->Exit(); 5835 context0->Exit(); 5836 context1.Dispose(); 5837 context0.Dispose(); 5838} 5839 5840 5841THREADED_TEST(Version) { 5842 v8::V8::GetVersion(); 5843} 5844 5845 5846static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) { 5847 ApiTestFuzzer::Fuzz(); 5848 return v8_num(12); 5849} 5850 5851 5852THREADED_TEST(InstanceProperties) { 5853 v8::HandleScope handle_scope; 5854 LocalContext context; 5855 5856 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5857 Local<ObjectTemplate> instance = t->InstanceTemplate(); 5858 5859 instance->Set(v8_str("x"), v8_num(42)); 5860 instance->Set(v8_str("f"), 5861 v8::FunctionTemplate::New(InstanceFunctionCallback)); 5862 5863 Local<Value> o = t->GetFunction()->NewInstance(); 5864 5865 context->Global()->Set(v8_str("i"), o); 5866 Local<Value> value = Script::Compile(v8_str("i.x"))->Run(); 5867 CHECK_EQ(42, value->Int32Value()); 5868 5869 value = Script::Compile(v8_str("i.f()"))->Run(); 5870 CHECK_EQ(12, value->Int32Value()); 5871} 5872 5873 5874static v8::Handle<Value> 5875GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) { 5876 ApiTestFuzzer::Fuzz(); 5877 return v8::Handle<Value>(); 5878} 5879 5880 5881THREADED_TEST(GlobalObjectInstanceProperties) { 5882 v8::HandleScope handle_scope; 5883 5884 Local<Value> global_object; 5885 5886 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5887 t->InstanceTemplate()->SetNamedPropertyHandler( 5888 GlobalObjectInstancePropertiesGet); 5889 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 5890 instance_template->Set(v8_str("x"), v8_num(42)); 5891 instance_template->Set(v8_str("f"), 5892 v8::FunctionTemplate::New(InstanceFunctionCallback)); 5893 5894 // The script to check how Crankshaft compiles missing global function 5895 // invocations. function g is not defined and should throw on call. 5896 const char* script = 5897 "function wrapper(call) {" 5898 " var x = 0, y = 1;" 5899 " for (var i = 0; i < 1000; i++) {" 5900 " x += i * 100;" 5901 " y += i * 100;" 5902 " }" 5903 " if (call) g();" 5904 "}" 5905 "for (var i = 0; i < 17; i++) wrapper(false);" 5906 "var thrown = 0;" 5907 "try { wrapper(true); } catch (e) { thrown = 1; };" 5908 "thrown"; 5909 5910 { 5911 LocalContext env(NULL, instance_template); 5912 // Hold on to the global object so it can be used again in another 5913 // environment initialization. 5914 global_object = env->Global(); 5915 5916 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 5917 CHECK_EQ(42, value->Int32Value()); 5918 value = Script::Compile(v8_str("f()"))->Run(); 5919 CHECK_EQ(12, value->Int32Value()); 5920 value = Script::Compile(v8_str(script))->Run(); 5921 CHECK_EQ(1, value->Int32Value()); 5922 } 5923 5924 { 5925 // Create new environment reusing the global object. 5926 LocalContext env(NULL, instance_template, global_object); 5927 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 5928 CHECK_EQ(42, value->Int32Value()); 5929 value = Script::Compile(v8_str("f()"))->Run(); 5930 CHECK_EQ(12, value->Int32Value()); 5931 value = Script::Compile(v8_str(script))->Run(); 5932 CHECK_EQ(1, value->Int32Value()); 5933 } 5934} 5935 5936 5937THREADED_TEST(CallKnownGlobalReceiver) { 5938 v8::HandleScope handle_scope; 5939 5940 Local<Value> global_object; 5941 5942 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5943 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 5944 5945 // The script to check that we leave global object not 5946 // global object proxy on stack when we deoptimize from inside 5947 // arguments evaluation. 5948 // To provoke error we need to both force deoptimization 5949 // from arguments evaluation and to force CallIC to take 5950 // CallIC_Miss code path that can't cope with global proxy. 5951 const char* script = 5952 "function bar(x, y) { try { } finally { } }" 5953 "function baz(x) { try { } finally { } }" 5954 "function bom(x) { try { } finally { } }" 5955 "function foo(x) { bar([x], bom(2)); }" 5956 "for (var i = 0; i < 10000; i++) foo(1);" 5957 "foo"; 5958 5959 Local<Value> foo; 5960 { 5961 LocalContext env(NULL, instance_template); 5962 // Hold on to the global object so it can be used again in another 5963 // environment initialization. 5964 global_object = env->Global(); 5965 foo = Script::Compile(v8_str(script))->Run(); 5966 } 5967 5968 { 5969 // Create new environment reusing the global object. 5970 LocalContext env(NULL, instance_template, global_object); 5971 env->Global()->Set(v8_str("foo"), foo); 5972 Local<Value> value = Script::Compile(v8_str("foo()"))->Run(); 5973 } 5974} 5975 5976 5977static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) { 5978 ApiTestFuzzer::Fuzz(); 5979 return v8_num(42); 5980} 5981 5982 5983static int shadow_y; 5984static int shadow_y_setter_call_count; 5985static int shadow_y_getter_call_count; 5986 5987 5988static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) { 5989 shadow_y_setter_call_count++; 5990 shadow_y = 42; 5991} 5992 5993 5994static v8::Handle<Value> ShadowYGetter(Local<String> name, 5995 const AccessorInfo& info) { 5996 ApiTestFuzzer::Fuzz(); 5997 shadow_y_getter_call_count++; 5998 return v8_num(shadow_y); 5999} 6000 6001 6002static v8::Handle<Value> ShadowIndexedGet(uint32_t index, 6003 const AccessorInfo& info) { 6004 return v8::Handle<Value>(); 6005} 6006 6007 6008static v8::Handle<Value> ShadowNamedGet(Local<String> key, 6009 const AccessorInfo&) { 6010 return v8::Handle<Value>(); 6011} 6012 6013 6014THREADED_TEST(ShadowObject) { 6015 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0; 6016 v8::HandleScope handle_scope; 6017 6018 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New(); 6019 LocalContext context(NULL, global_template); 6020 6021 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 6022 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet); 6023 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet); 6024 Local<ObjectTemplate> proto = t->PrototypeTemplate(); 6025 Local<ObjectTemplate> instance = t->InstanceTemplate(); 6026 6027 // Only allow calls of f on instances of t. 6028 Local<v8::Signature> signature = v8::Signature::New(t); 6029 proto->Set(v8_str("f"), 6030 v8::FunctionTemplate::New(ShadowFunctionCallback, 6031 Local<Value>(), 6032 signature)); 6033 proto->Set(v8_str("x"), v8_num(12)); 6034 6035 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter); 6036 6037 Local<Value> o = t->GetFunction()->NewInstance(); 6038 context->Global()->Set(v8_str("__proto__"), o); 6039 6040 Local<Value> value = 6041 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run(); 6042 CHECK(value->IsBoolean()); 6043 CHECK(!value->BooleanValue()); 6044 6045 value = Script::Compile(v8_str("x"))->Run(); 6046 CHECK_EQ(12, value->Int32Value()); 6047 6048 value = Script::Compile(v8_str("f()"))->Run(); 6049 CHECK_EQ(42, value->Int32Value()); 6050 6051 Script::Compile(v8_str("y = 42"))->Run(); 6052 CHECK_EQ(1, shadow_y_setter_call_count); 6053 value = Script::Compile(v8_str("y"))->Run(); 6054 CHECK_EQ(1, shadow_y_getter_call_count); 6055 CHECK_EQ(42, value->Int32Value()); 6056} 6057 6058 6059THREADED_TEST(HiddenPrototype) { 6060 v8::HandleScope handle_scope; 6061 LocalContext context; 6062 6063 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 6064 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 6065 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 6066 t1->SetHiddenPrototype(true); 6067 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 6068 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 6069 t2->SetHiddenPrototype(true); 6070 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 6071 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 6072 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 6073 6074 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 6075 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 6076 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 6077 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 6078 6079 // Setting the prototype on an object skips hidden prototypes. 6080 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 6081 o0->Set(v8_str("__proto__"), o1); 6082 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 6083 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 6084 o0->Set(v8_str("__proto__"), o2); 6085 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 6086 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 6087 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 6088 o0->Set(v8_str("__proto__"), o3); 6089 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 6090 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 6091 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 6092 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 6093 6094 // Getting the prototype of o0 should get the first visible one 6095 // which is o3. Therefore, z should not be defined on the prototype 6096 // object. 6097 Local<Value> proto = o0->Get(v8_str("__proto__")); 6098 CHECK(proto->IsObject()); 6099 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined()); 6100} 6101 6102 6103THREADED_TEST(SetPrototype) { 6104 v8::HandleScope handle_scope; 6105 LocalContext context; 6106 6107 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 6108 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 6109 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 6110 t1->SetHiddenPrototype(true); 6111 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 6112 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 6113 t2->SetHiddenPrototype(true); 6114 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 6115 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 6116 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 6117 6118 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 6119 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 6120 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 6121 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 6122 6123 // Setting the prototype on an object does not skip hidden prototypes. 6124 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 6125 CHECK(o0->SetPrototype(o1)); 6126 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 6127 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 6128 CHECK(o1->SetPrototype(o2)); 6129 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 6130 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 6131 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 6132 CHECK(o2->SetPrototype(o3)); 6133 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 6134 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 6135 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 6136 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 6137 6138 // Getting the prototype of o0 should get the first visible one 6139 // which is o3. Therefore, z should not be defined on the prototype 6140 // object. 6141 Local<Value> proto = o0->Get(v8_str("__proto__")); 6142 CHECK(proto->IsObject()); 6143 CHECK_EQ(proto.As<v8::Object>(), o3); 6144 6145 // However, Object::GetPrototype ignores hidden prototype. 6146 Local<Value> proto0 = o0->GetPrototype(); 6147 CHECK(proto0->IsObject()); 6148 CHECK_EQ(proto0.As<v8::Object>(), o1); 6149 6150 Local<Value> proto1 = o1->GetPrototype(); 6151 CHECK(proto1->IsObject()); 6152 CHECK_EQ(proto1.As<v8::Object>(), o2); 6153 6154 Local<Value> proto2 = o2->GetPrototype(); 6155 CHECK(proto2->IsObject()); 6156 CHECK_EQ(proto2.As<v8::Object>(), o3); 6157} 6158 6159 6160THREADED_TEST(SetPrototypeThrows) { 6161 v8::HandleScope handle_scope; 6162 LocalContext context; 6163 6164 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 6165 6166 Local<v8::Object> o0 = t->GetFunction()->NewInstance(); 6167 Local<v8::Object> o1 = t->GetFunction()->NewInstance(); 6168 6169 CHECK(o0->SetPrototype(o1)); 6170 // If setting the prototype leads to the cycle, SetPrototype should 6171 // return false and keep VM in sane state. 6172 v8::TryCatch try_catch; 6173 CHECK(!o1->SetPrototype(o0)); 6174 CHECK(!try_catch.HasCaught()); 6175 ASSERT(!i::Top::has_pending_exception()); 6176 6177 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value()); 6178} 6179 6180 6181THREADED_TEST(GetterSetterExceptions) { 6182 v8::HandleScope handle_scope; 6183 LocalContext context; 6184 CompileRun( 6185 "function Foo() { };" 6186 "function Throw() { throw 5; };" 6187 "var x = { };" 6188 "x.__defineSetter__('set', Throw);" 6189 "x.__defineGetter__('get', Throw);"); 6190 Local<v8::Object> x = 6191 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x"))); 6192 v8::TryCatch try_catch; 6193 x->Set(v8_str("set"), v8::Integer::New(8)); 6194 x->Get(v8_str("get")); 6195 x->Set(v8_str("set"), v8::Integer::New(8)); 6196 x->Get(v8_str("get")); 6197 x->Set(v8_str("set"), v8::Integer::New(8)); 6198 x->Get(v8_str("get")); 6199 x->Set(v8_str("set"), v8::Integer::New(8)); 6200 x->Get(v8_str("get")); 6201} 6202 6203 6204THREADED_TEST(Constructor) { 6205 v8::HandleScope handle_scope; 6206 LocalContext context; 6207 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 6208 templ->SetClassName(v8_str("Fun")); 6209 Local<Function> cons = templ->GetFunction(); 6210 context->Global()->Set(v8_str("Fun"), cons); 6211 Local<v8::Object> inst = cons->NewInstance(); 6212 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst); 6213 Local<Value> value = CompileRun("(new Fun()).constructor === Fun"); 6214 CHECK(value->BooleanValue()); 6215} 6216 6217THREADED_TEST(FunctionDescriptorException) { 6218 v8::HandleScope handle_scope; 6219 LocalContext context; 6220 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 6221 templ->SetClassName(v8_str("Fun")); 6222 Local<Function> cons = templ->GetFunction(); 6223 context->Global()->Set(v8_str("Fun"), cons); 6224 Local<Value> value = CompileRun( 6225 "function test() {" 6226 " try {" 6227 " (new Fun()).blah()" 6228 " } catch (e) {" 6229 " var str = String(e);" 6230 " if (str.indexOf('TypeError') == -1) return 1;" 6231 " if (str.indexOf('[object Fun]') != -1) return 2;" 6232 " if (str.indexOf('#<a Fun>') == -1) return 3;" 6233 " return 0;" 6234 " }" 6235 " return 4;" 6236 "}" 6237 "test();"); 6238 CHECK_EQ(0, value->Int32Value()); 6239} 6240 6241 6242THREADED_TEST(EvalAliasedDynamic) { 6243 v8::HandleScope scope; 6244 LocalContext current; 6245 6246 // Tests where aliased eval can only be resolved dynamically. 6247 Local<Script> script = 6248 Script::Compile(v8_str("function f(x) { " 6249 " var foo = 2;" 6250 " with (x) { return eval('foo'); }" 6251 "}" 6252 "foo = 0;" 6253 "result1 = f(new Object());" 6254 "result2 = f(this);" 6255 "var x = new Object();" 6256 "x.eval = function(x) { return 1; };" 6257 "result3 = f(x);")); 6258 script->Run(); 6259 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value()); 6260 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value()); 6261 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value()); 6262 6263 v8::TryCatch try_catch; 6264 script = 6265 Script::Compile(v8_str("function f(x) { " 6266 " var bar = 2;" 6267 " with (x) { return eval('bar'); }" 6268 "}" 6269 "f(this)")); 6270 script->Run(); 6271 CHECK(try_catch.HasCaught()); 6272 try_catch.Reset(); 6273} 6274 6275 6276THREADED_TEST(CrossEval) { 6277 v8::HandleScope scope; 6278 LocalContext other; 6279 LocalContext current; 6280 6281 Local<String> token = v8_str("<security token>"); 6282 other->SetSecurityToken(token); 6283 current->SetSecurityToken(token); 6284 6285 // Setup reference from current to other. 6286 current->Global()->Set(v8_str("other"), other->Global()); 6287 6288 // Check that new variables are introduced in other context. 6289 Local<Script> script = 6290 Script::Compile(v8_str("other.eval('var foo = 1234')")); 6291 script->Run(); 6292 Local<Value> foo = other->Global()->Get(v8_str("foo")); 6293 CHECK_EQ(1234, foo->Int32Value()); 6294 CHECK(!current->Global()->Has(v8_str("foo"))); 6295 6296 // Check that writing to non-existing properties introduces them in 6297 // the other context. 6298 script = 6299 Script::Compile(v8_str("other.eval('na = 1234')")); 6300 script->Run(); 6301 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value()); 6302 CHECK(!current->Global()->Has(v8_str("na"))); 6303 6304 // Check that global variables in current context are not visible in other 6305 // context. 6306 v8::TryCatch try_catch; 6307 script = 6308 Script::Compile(v8_str("var bar = 42; other.eval('bar');")); 6309 Local<Value> result = script->Run(); 6310 CHECK(try_catch.HasCaught()); 6311 try_catch.Reset(); 6312 6313 // Check that local variables in current context are not visible in other 6314 // context. 6315 script = 6316 Script::Compile(v8_str("(function() { " 6317 " var baz = 87;" 6318 " return other.eval('baz');" 6319 "})();")); 6320 result = script->Run(); 6321 CHECK(try_catch.HasCaught()); 6322 try_catch.Reset(); 6323 6324 // Check that global variables in the other environment are visible 6325 // when evaluting code. 6326 other->Global()->Set(v8_str("bis"), v8_num(1234)); 6327 script = Script::Compile(v8_str("other.eval('bis')")); 6328 CHECK_EQ(1234, script->Run()->Int32Value()); 6329 CHECK(!try_catch.HasCaught()); 6330 6331 // Check that the 'this' pointer points to the global object evaluating 6332 // code. 6333 other->Global()->Set(v8_str("t"), other->Global()); 6334 script = Script::Compile(v8_str("other.eval('this == t')")); 6335 result = script->Run(); 6336 CHECK(result->IsTrue()); 6337 CHECK(!try_catch.HasCaught()); 6338 6339 // Check that variables introduced in with-statement are not visible in 6340 // other context. 6341 script = 6342 Script::Compile(v8_str("with({x:2}){other.eval('x')}")); 6343 result = script->Run(); 6344 CHECK(try_catch.HasCaught()); 6345 try_catch.Reset(); 6346 6347 // Check that you cannot use 'eval.call' with another object than the 6348 // current global object. 6349 script = 6350 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')")); 6351 result = script->Run(); 6352 CHECK(try_catch.HasCaught()); 6353} 6354 6355 6356// Test that calling eval in a context which has been detached from 6357// its global throws an exception. This behavior is consistent with 6358// other JavaScript implementations. 6359THREADED_TEST(EvalInDetachedGlobal) { 6360 v8::HandleScope scope; 6361 6362 v8::Persistent<Context> context0 = Context::New(); 6363 v8::Persistent<Context> context1 = Context::New(); 6364 6365 // Setup function in context0 that uses eval from context0. 6366 context0->Enter(); 6367 v8::Handle<v8::Value> fun = 6368 CompileRun("var x = 42;" 6369 "(function() {" 6370 " var e = eval;" 6371 " return function(s) { return e(s); }" 6372 "})()"); 6373 context0->Exit(); 6374 6375 // Put the function into context1 and call it before and after 6376 // detaching the global. Before detaching, the call succeeds and 6377 // after detaching and exception is thrown. 6378 context1->Enter(); 6379 context1->Global()->Set(v8_str("fun"), fun); 6380 v8::Handle<v8::Value> x_value = CompileRun("fun('x')"); 6381 CHECK_EQ(42, x_value->Int32Value()); 6382 context0->DetachGlobal(); 6383 v8::TryCatch catcher; 6384 x_value = CompileRun("fun('x')"); 6385 CHECK(x_value.IsEmpty()); 6386 CHECK(catcher.HasCaught()); 6387 context1->Exit(); 6388 6389 context1.Dispose(); 6390 context0.Dispose(); 6391} 6392 6393 6394THREADED_TEST(CrossLazyLoad) { 6395 v8::HandleScope scope; 6396 LocalContext other; 6397 LocalContext current; 6398 6399 Local<String> token = v8_str("<security token>"); 6400 other->SetSecurityToken(token); 6401 current->SetSecurityToken(token); 6402 6403 // Setup reference from current to other. 6404 current->Global()->Set(v8_str("other"), other->Global()); 6405 6406 // Trigger lazy loading in other context. 6407 Local<Script> script = 6408 Script::Compile(v8_str("other.eval('new Date(42)')")); 6409 Local<Value> value = script->Run(); 6410 CHECK_EQ(42.0, value->NumberValue()); 6411} 6412 6413 6414static v8::Handle<Value> call_as_function(const v8::Arguments& args) { 6415 ApiTestFuzzer::Fuzz(); 6416 if (args.IsConstructCall()) { 6417 if (args[0]->IsInt32()) { 6418 return v8_num(-args[0]->Int32Value()); 6419 } 6420 } 6421 6422 return args[0]; 6423} 6424 6425 6426// Test that a call handler can be set for objects which will allow 6427// non-function objects created through the API to be called as 6428// functions. 6429THREADED_TEST(CallAsFunction) { 6430 v8::HandleScope scope; 6431 LocalContext context; 6432 6433 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 6434 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 6435 instance_template->SetCallAsFunctionHandler(call_as_function); 6436 Local<v8::Object> instance = t->GetFunction()->NewInstance(); 6437 context->Global()->Set(v8_str("obj"), instance); 6438 v8::TryCatch try_catch; 6439 Local<Value> value; 6440 CHECK(!try_catch.HasCaught()); 6441 6442 value = CompileRun("obj(42)"); 6443 CHECK(!try_catch.HasCaught()); 6444 CHECK_EQ(42, value->Int32Value()); 6445 6446 value = CompileRun("(function(o){return o(49)})(obj)"); 6447 CHECK(!try_catch.HasCaught()); 6448 CHECK_EQ(49, value->Int32Value()); 6449 6450 // test special case of call as function 6451 value = CompileRun("[obj]['0'](45)"); 6452 CHECK(!try_catch.HasCaught()); 6453 CHECK_EQ(45, value->Int32Value()); 6454 6455 value = CompileRun("obj.call = Function.prototype.call;" 6456 "obj.call(null, 87)"); 6457 CHECK(!try_catch.HasCaught()); 6458 CHECK_EQ(87, value->Int32Value()); 6459 6460 // Regression tests for bug #1116356: Calling call through call/apply 6461 // must work for non-function receivers. 6462 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])"; 6463 value = CompileRun(apply_99); 6464 CHECK(!try_catch.HasCaught()); 6465 CHECK_EQ(99, value->Int32Value()); 6466 6467 const char* call_17 = "Function.prototype.call.call(obj, this, 17)"; 6468 value = CompileRun(call_17); 6469 CHECK(!try_catch.HasCaught()); 6470 CHECK_EQ(17, value->Int32Value()); 6471 6472 // Check that the call-as-function handler can be called through 6473 // new. 6474 value = CompileRun("new obj(43)"); 6475 CHECK(!try_catch.HasCaught()); 6476 CHECK_EQ(-43, value->Int32Value()); 6477} 6478 6479 6480static int CountHandles() { 6481 return v8::HandleScope::NumberOfHandles(); 6482} 6483 6484 6485static int Recurse(int depth, int iterations) { 6486 v8::HandleScope scope; 6487 if (depth == 0) return CountHandles(); 6488 for (int i = 0; i < iterations; i++) { 6489 Local<v8::Number> n = v8::Integer::New(42); 6490 } 6491 return Recurse(depth - 1, iterations); 6492} 6493 6494 6495THREADED_TEST(HandleIteration) { 6496 static const int kIterations = 500; 6497 static const int kNesting = 200; 6498 CHECK_EQ(0, CountHandles()); 6499 { 6500 v8::HandleScope scope1; 6501 CHECK_EQ(0, CountHandles()); 6502 for (int i = 0; i < kIterations; i++) { 6503 Local<v8::Number> n = v8::Integer::New(42); 6504 CHECK_EQ(i + 1, CountHandles()); 6505 } 6506 6507 CHECK_EQ(kIterations, CountHandles()); 6508 { 6509 v8::HandleScope scope2; 6510 for (int j = 0; j < kIterations; j++) { 6511 Local<v8::Number> n = v8::Integer::New(42); 6512 CHECK_EQ(j + 1 + kIterations, CountHandles()); 6513 } 6514 } 6515 CHECK_EQ(kIterations, CountHandles()); 6516 } 6517 CHECK_EQ(0, CountHandles()); 6518 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations)); 6519} 6520 6521 6522static v8::Handle<Value> InterceptorHasOwnPropertyGetter( 6523 Local<String> name, 6524 const AccessorInfo& info) { 6525 ApiTestFuzzer::Fuzz(); 6526 return v8::Handle<Value>(); 6527} 6528 6529 6530THREADED_TEST(InterceptorHasOwnProperty) { 6531 v8::HandleScope scope; 6532 LocalContext context; 6533 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6534 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 6535 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter); 6536 Local<Function> function = fun_templ->GetFunction(); 6537 context->Global()->Set(v8_str("constructor"), function); 6538 v8::Handle<Value> value = CompileRun( 6539 "var o = new constructor();" 6540 "o.hasOwnProperty('ostehaps');"); 6541 CHECK_EQ(false, value->BooleanValue()); 6542 value = CompileRun( 6543 "o.ostehaps = 42;" 6544 "o.hasOwnProperty('ostehaps');"); 6545 CHECK_EQ(true, value->BooleanValue()); 6546 value = CompileRun( 6547 "var p = new constructor();" 6548 "p.hasOwnProperty('ostehaps');"); 6549 CHECK_EQ(false, value->BooleanValue()); 6550} 6551 6552 6553static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC( 6554 Local<String> name, 6555 const AccessorInfo& info) { 6556 ApiTestFuzzer::Fuzz(); 6557 i::Heap::CollectAllGarbage(false); 6558 return v8::Handle<Value>(); 6559} 6560 6561 6562THREADED_TEST(InterceptorHasOwnPropertyCausingGC) { 6563 v8::HandleScope scope; 6564 LocalContext context; 6565 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6566 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 6567 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC); 6568 Local<Function> function = fun_templ->GetFunction(); 6569 context->Global()->Set(v8_str("constructor"), function); 6570 // Let's first make some stuff so we can be sure to get a good GC. 6571 CompileRun( 6572 "function makestr(size) {" 6573 " switch (size) {" 6574 " case 1: return 'f';" 6575 " case 2: return 'fo';" 6576 " case 3: return 'foo';" 6577 " }" 6578 " return makestr(size >> 1) + makestr((size + 1) >> 1);" 6579 "}" 6580 "var x = makestr(12345);" 6581 "x = makestr(31415);" 6582 "x = makestr(23456);"); 6583 v8::Handle<Value> value = CompileRun( 6584 "var o = new constructor();" 6585 "o.__proto__ = new String(x);" 6586 "o.hasOwnProperty('ostehaps');"); 6587 CHECK_EQ(false, value->BooleanValue()); 6588} 6589 6590 6591typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property, 6592 const AccessorInfo& info); 6593 6594 6595static void CheckInterceptorLoadIC(NamedPropertyGetter getter, 6596 const char* source, 6597 int expected) { 6598 v8::HandleScope scope; 6599 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6600 templ->SetNamedPropertyHandler(getter, 0, 0, 0, 0, v8_str("data")); 6601 LocalContext context; 6602 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6603 v8::Handle<Value> value = CompileRun(source); 6604 CHECK_EQ(expected, value->Int32Value()); 6605} 6606 6607 6608static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name, 6609 const AccessorInfo& info) { 6610 ApiTestFuzzer::Fuzz(); 6611 CHECK_EQ(v8_str("data"), info.Data()); 6612 CHECK_EQ(v8_str("x"), name); 6613 return v8::Integer::New(42); 6614} 6615 6616 6617// This test should hit the load IC for the interceptor case. 6618THREADED_TEST(InterceptorLoadIC) { 6619 CheckInterceptorLoadIC(InterceptorLoadICGetter, 6620 "var result = 0;" 6621 "for (var i = 0; i < 1000; i++) {" 6622 " result = o.x;" 6623 "}", 6624 42); 6625} 6626 6627 6628// Below go several tests which verify that JITing for various 6629// configurations of interceptor and explicit fields works fine 6630// (those cases are special cased to get better performance). 6631 6632static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name, 6633 const AccessorInfo& info) { 6634 ApiTestFuzzer::Fuzz(); 6635 return v8_str("x")->Equals(name) 6636 ? v8::Integer::New(42) : v8::Handle<v8::Value>(); 6637} 6638 6639 6640THREADED_TEST(InterceptorLoadICWithFieldOnHolder) { 6641 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6642 "var result = 0;" 6643 "o.y = 239;" 6644 "for (var i = 0; i < 1000; i++) {" 6645 " result = o.y;" 6646 "}", 6647 239); 6648} 6649 6650 6651THREADED_TEST(InterceptorLoadICWithSubstitutedProto) { 6652 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6653 "var result = 0;" 6654 "o.__proto__ = { 'y': 239 };" 6655 "for (var i = 0; i < 1000; i++) {" 6656 " result = o.y + o.x;" 6657 "}", 6658 239 + 42); 6659} 6660 6661 6662THREADED_TEST(InterceptorLoadICWithPropertyOnProto) { 6663 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6664 "var result = 0;" 6665 "o.__proto__.y = 239;" 6666 "for (var i = 0; i < 1000; i++) {" 6667 " result = o.y + o.x;" 6668 "}", 6669 239 + 42); 6670} 6671 6672 6673THREADED_TEST(InterceptorLoadICUndefined) { 6674 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6675 "var result = 0;" 6676 "for (var i = 0; i < 1000; i++) {" 6677 " result = (o.y == undefined) ? 239 : 42;" 6678 "}", 6679 239); 6680} 6681 6682 6683THREADED_TEST(InterceptorLoadICWithOverride) { 6684 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6685 "fst = new Object(); fst.__proto__ = o;" 6686 "snd = new Object(); snd.__proto__ = fst;" 6687 "var result1 = 0;" 6688 "for (var i = 0; i < 1000; i++) {" 6689 " result1 = snd.x;" 6690 "}" 6691 "fst.x = 239;" 6692 "var result = 0;" 6693 "for (var i = 0; i < 1000; i++) {" 6694 " result = snd.x;" 6695 "}" 6696 "result + result1", 6697 239 + 42); 6698} 6699 6700 6701// Test the case when we stored field into 6702// a stub, but interceptor produced value on its own. 6703THREADED_TEST(InterceptorLoadICFieldNotNeeded) { 6704 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6705 "proto = new Object();" 6706 "o.__proto__ = proto;" 6707 "proto.x = 239;" 6708 "for (var i = 0; i < 1000; i++) {" 6709 " o.x;" 6710 // Now it should be ICed and keep a reference to x defined on proto 6711 "}" 6712 "var result = 0;" 6713 "for (var i = 0; i < 1000; i++) {" 6714 " result += o.x;" 6715 "}" 6716 "result;", 6717 42 * 1000); 6718} 6719 6720 6721// Test the case when we stored field into 6722// a stub, but it got invalidated later on. 6723THREADED_TEST(InterceptorLoadICInvalidatedField) { 6724 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6725 "proto1 = new Object();" 6726 "proto2 = new Object();" 6727 "o.__proto__ = proto1;" 6728 "proto1.__proto__ = proto2;" 6729 "proto2.y = 239;" 6730 "for (var i = 0; i < 1000; i++) {" 6731 " o.y;" 6732 // Now it should be ICed and keep a reference to y defined on proto2 6733 "}" 6734 "proto1.y = 42;" 6735 "var result = 0;" 6736 "for (var i = 0; i < 1000; i++) {" 6737 " result += o.y;" 6738 "}" 6739 "result;", 6740 42 * 1000); 6741} 6742 6743 6744static int interceptor_load_not_handled_calls = 0; 6745static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name, 6746 const AccessorInfo& info) { 6747 ++interceptor_load_not_handled_calls; 6748 return v8::Handle<v8::Value>(); 6749} 6750 6751 6752// Test how post-interceptor lookups are done in the non-cacheable 6753// case: the interceptor should not be invoked during this lookup. 6754THREADED_TEST(InterceptorLoadICPostInterceptor) { 6755 interceptor_load_not_handled_calls = 0; 6756 CheckInterceptorLoadIC(InterceptorLoadNotHandled, 6757 "receiver = new Object();" 6758 "receiver.__proto__ = o;" 6759 "proto = new Object();" 6760 "/* Make proto a slow-case object. */" 6761 "for (var i = 0; i < 1000; i++) {" 6762 " proto[\"xxxxxxxx\" + i] = [];" 6763 "}" 6764 "proto.x = 17;" 6765 "o.__proto__ = proto;" 6766 "var result = 0;" 6767 "for (var i = 0; i < 1000; i++) {" 6768 " result += receiver.x;" 6769 "}" 6770 "result;", 6771 17 * 1000); 6772 CHECK_EQ(1000, interceptor_load_not_handled_calls); 6773} 6774 6775 6776// Test the case when we stored field into 6777// a stub, but it got invalidated later on due to override on 6778// global object which is between interceptor and fields' holders. 6779THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) { 6780 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6781 "o.__proto__ = this;" // set a global to be a proto of o. 6782 "this.__proto__.y = 239;" 6783 "for (var i = 0; i < 10; i++) {" 6784 " if (o.y != 239) throw 'oops: ' + o.y;" 6785 // Now it should be ICed and keep a reference to y defined on field_holder. 6786 "}" 6787 "this.y = 42;" // Assign on a global. 6788 "var result = 0;" 6789 "for (var i = 0; i < 10; i++) {" 6790 " result += o.y;" 6791 "}" 6792 "result;", 6793 42 * 10); 6794} 6795 6796 6797static void SetOnThis(Local<String> name, 6798 Local<Value> value, 6799 const AccessorInfo& info) { 6800 info.This()->ForceSet(name, value); 6801} 6802 6803 6804THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) { 6805 v8::HandleScope scope; 6806 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6807 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6808 templ->SetAccessor(v8_str("y"), Return239); 6809 LocalContext context; 6810 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6811 6812 // Check the case when receiver and interceptor's holder 6813 // are the same objects. 6814 v8::Handle<Value> value = CompileRun( 6815 "var result = 0;" 6816 "for (var i = 0; i < 7; i++) {" 6817 " result = o.y;" 6818 "}"); 6819 CHECK_EQ(239, value->Int32Value()); 6820 6821 // Check the case when interceptor's holder is in proto chain 6822 // of receiver. 6823 value = CompileRun( 6824 "r = { __proto__: o };" 6825 "var result = 0;" 6826 "for (var i = 0; i < 7; i++) {" 6827 " result = r.y;" 6828 "}"); 6829 CHECK_EQ(239, value->Int32Value()); 6830} 6831 6832 6833THREADED_TEST(InterceptorLoadICWithCallbackOnProto) { 6834 v8::HandleScope scope; 6835 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6836 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6837 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6838 templ_p->SetAccessor(v8_str("y"), Return239); 6839 6840 LocalContext context; 6841 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6842 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6843 6844 // Check the case when receiver and interceptor's holder 6845 // are the same objects. 6846 v8::Handle<Value> value = CompileRun( 6847 "o.__proto__ = p;" 6848 "var result = 0;" 6849 "for (var i = 0; i < 7; i++) {" 6850 " result = o.x + o.y;" 6851 "}"); 6852 CHECK_EQ(239 + 42, value->Int32Value()); 6853 6854 // Check the case when interceptor's holder is in proto chain 6855 // of receiver. 6856 value = CompileRun( 6857 "r = { __proto__: o };" 6858 "var result = 0;" 6859 "for (var i = 0; i < 7; i++) {" 6860 " result = r.x + r.y;" 6861 "}"); 6862 CHECK_EQ(239 + 42, value->Int32Value()); 6863} 6864 6865 6866THREADED_TEST(InterceptorLoadICForCallbackWithOverride) { 6867 v8::HandleScope scope; 6868 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6869 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6870 templ->SetAccessor(v8_str("y"), Return239); 6871 6872 LocalContext context; 6873 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6874 6875 v8::Handle<Value> value = CompileRun( 6876 "fst = new Object(); fst.__proto__ = o;" 6877 "snd = new Object(); snd.__proto__ = fst;" 6878 "var result1 = 0;" 6879 "for (var i = 0; i < 7; i++) {" 6880 " result1 = snd.x;" 6881 "}" 6882 "fst.x = 239;" 6883 "var result = 0;" 6884 "for (var i = 0; i < 7; i++) {" 6885 " result = snd.x;" 6886 "}" 6887 "result + result1"); 6888 CHECK_EQ(239 + 42, value->Int32Value()); 6889} 6890 6891 6892// Test the case when we stored callback into 6893// a stub, but interceptor produced value on its own. 6894THREADED_TEST(InterceptorLoadICCallbackNotNeeded) { 6895 v8::HandleScope scope; 6896 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6897 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6898 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6899 templ_p->SetAccessor(v8_str("y"), Return239); 6900 6901 LocalContext context; 6902 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6903 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6904 6905 v8::Handle<Value> value = CompileRun( 6906 "o.__proto__ = p;" 6907 "for (var i = 0; i < 7; i++) {" 6908 " o.x;" 6909 // Now it should be ICed and keep a reference to x defined on p 6910 "}" 6911 "var result = 0;" 6912 "for (var i = 0; i < 7; i++) {" 6913 " result += o.x;" 6914 "}" 6915 "result"); 6916 CHECK_EQ(42 * 7, value->Int32Value()); 6917} 6918 6919 6920// Test the case when we stored callback into 6921// a stub, but it got invalidated later on. 6922THREADED_TEST(InterceptorLoadICInvalidatedCallback) { 6923 v8::HandleScope scope; 6924 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6925 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6926 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6927 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 6928 6929 LocalContext context; 6930 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6931 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6932 6933 v8::Handle<Value> value = CompileRun( 6934 "inbetween = new Object();" 6935 "o.__proto__ = inbetween;" 6936 "inbetween.__proto__ = p;" 6937 "for (var i = 0; i < 10; i++) {" 6938 " o.y;" 6939 // Now it should be ICed and keep a reference to y defined on p 6940 "}" 6941 "inbetween.y = 42;" 6942 "var result = 0;" 6943 "for (var i = 0; i < 10; i++) {" 6944 " result += o.y;" 6945 "}" 6946 "result"); 6947 CHECK_EQ(42 * 10, value->Int32Value()); 6948} 6949 6950 6951// Test the case when we stored callback into 6952// a stub, but it got invalidated later on due to override on 6953// global object which is between interceptor and callbacks' holders. 6954THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) { 6955 v8::HandleScope scope; 6956 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6957 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6958 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6959 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 6960 6961 LocalContext context; 6962 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6963 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6964 6965 v8::Handle<Value> value = CompileRun( 6966 "o.__proto__ = this;" 6967 "this.__proto__ = p;" 6968 "for (var i = 0; i < 10; i++) {" 6969 " if (o.y != 239) throw 'oops: ' + o.y;" 6970 // Now it should be ICed and keep a reference to y defined on p 6971 "}" 6972 "this.y = 42;" 6973 "var result = 0;" 6974 "for (var i = 0; i < 10; i++) {" 6975 " result += o.y;" 6976 "}" 6977 "result"); 6978 CHECK_EQ(42 * 10, value->Int32Value()); 6979} 6980 6981 6982static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name, 6983 const AccessorInfo& info) { 6984 ApiTestFuzzer::Fuzz(); 6985 CHECK(v8_str("x")->Equals(name)); 6986 return v8::Integer::New(0); 6987} 6988 6989 6990THREADED_TEST(InterceptorReturningZero) { 6991 CheckInterceptorLoadIC(InterceptorLoadICGetter0, 6992 "o.x == undefined ? 1 : 0", 6993 0); 6994} 6995 6996 6997static v8::Handle<Value> InterceptorStoreICSetter( 6998 Local<String> key, Local<Value> value, const AccessorInfo&) { 6999 CHECK(v8_str("x")->Equals(key)); 7000 CHECK_EQ(42, value->Int32Value()); 7001 return value; 7002} 7003 7004 7005// This test should hit the store IC for the interceptor case. 7006THREADED_TEST(InterceptorStoreIC) { 7007 v8::HandleScope scope; 7008 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7009 templ->SetNamedPropertyHandler(InterceptorLoadICGetter, 7010 InterceptorStoreICSetter, 7011 0, 0, 0, v8_str("data")); 7012 LocalContext context; 7013 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7014 v8::Handle<Value> value = CompileRun( 7015 "for (var i = 0; i < 1000; i++) {" 7016 " o.x = 42;" 7017 "}"); 7018} 7019 7020 7021THREADED_TEST(InterceptorStoreICWithNoSetter) { 7022 v8::HandleScope scope; 7023 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7024 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 7025 LocalContext context; 7026 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7027 v8::Handle<Value> value = CompileRun( 7028 "for (var i = 0; i < 1000; i++) {" 7029 " o.y = 239;" 7030 "}" 7031 "42 + o.y"); 7032 CHECK_EQ(239 + 42, value->Int32Value()); 7033} 7034 7035 7036 7037 7038v8::Handle<Value> call_ic_function; 7039v8::Handle<Value> call_ic_function2; 7040v8::Handle<Value> call_ic_function3; 7041 7042static v8::Handle<Value> InterceptorCallICGetter(Local<String> name, 7043 const AccessorInfo& info) { 7044 ApiTestFuzzer::Fuzz(); 7045 CHECK(v8_str("x")->Equals(name)); 7046 return call_ic_function; 7047} 7048 7049 7050// This test should hit the call IC for the interceptor case. 7051THREADED_TEST(InterceptorCallIC) { 7052 v8::HandleScope scope; 7053 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7054 templ->SetNamedPropertyHandler(InterceptorCallICGetter); 7055 LocalContext context; 7056 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7057 call_ic_function = 7058 v8_compile("function f(x) { return x + 1; }; f")->Run(); 7059 v8::Handle<Value> value = CompileRun( 7060 "var result = 0;" 7061 "for (var i = 0; i < 1000; i++) {" 7062 " result = o.x(41);" 7063 "}"); 7064 CHECK_EQ(42, value->Int32Value()); 7065} 7066 7067 7068// This test checks that if interceptor doesn't provide 7069// a value, we can fetch regular value. 7070THREADED_TEST(InterceptorCallICSeesOthers) { 7071 v8::HandleScope scope; 7072 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7073 templ->SetNamedPropertyHandler(NoBlockGetterX); 7074 LocalContext context; 7075 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7076 v8::Handle<Value> value = CompileRun( 7077 "o.x = function f(x) { return x + 1; };" 7078 "var result = 0;" 7079 "for (var i = 0; i < 7; i++) {" 7080 " result = o.x(41);" 7081 "}"); 7082 CHECK_EQ(42, value->Int32Value()); 7083} 7084 7085 7086static v8::Handle<Value> call_ic_function4; 7087static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name, 7088 const AccessorInfo& info) { 7089 ApiTestFuzzer::Fuzz(); 7090 CHECK(v8_str("x")->Equals(name)); 7091 return call_ic_function4; 7092} 7093 7094 7095// This test checks that if interceptor provides a function, 7096// even if we cached shadowed variant, interceptor's function 7097// is invoked 7098THREADED_TEST(InterceptorCallICCacheableNotNeeded) { 7099 v8::HandleScope scope; 7100 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7101 templ->SetNamedPropertyHandler(InterceptorCallICGetter4); 7102 LocalContext context; 7103 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7104 call_ic_function4 = 7105 v8_compile("function f(x) { return x - 1; }; f")->Run(); 7106 v8::Handle<Value> value = CompileRun( 7107 "o.__proto__.x = function(x) { return x + 1; };" 7108 "var result = 0;" 7109 "for (var i = 0; i < 1000; i++) {" 7110 " result = o.x(42);" 7111 "}"); 7112 CHECK_EQ(41, value->Int32Value()); 7113} 7114 7115 7116// Test the case when we stored cacheable lookup into 7117// a stub, but it got invalidated later on 7118THREADED_TEST(InterceptorCallICInvalidatedCacheable) { 7119 v8::HandleScope scope; 7120 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7121 templ->SetNamedPropertyHandler(NoBlockGetterX); 7122 LocalContext context; 7123 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7124 v8::Handle<Value> value = CompileRun( 7125 "proto1 = new Object();" 7126 "proto2 = new Object();" 7127 "o.__proto__ = proto1;" 7128 "proto1.__proto__ = proto2;" 7129 "proto2.y = function(x) { return x + 1; };" 7130 // Invoke it many times to compile a stub 7131 "for (var i = 0; i < 7; i++) {" 7132 " o.y(42);" 7133 "}" 7134 "proto1.y = function(x) { return x - 1; };" 7135 "var result = 0;" 7136 "for (var i = 0; i < 7; i++) {" 7137 " result += o.y(42);" 7138 "}"); 7139 CHECK_EQ(41 * 7, value->Int32Value()); 7140} 7141 7142 7143static v8::Handle<Value> call_ic_function5; 7144static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name, 7145 const AccessorInfo& info) { 7146 ApiTestFuzzer::Fuzz(); 7147 if (v8_str("x")->Equals(name)) 7148 return call_ic_function5; 7149 else 7150 return Local<Value>(); 7151} 7152 7153 7154// This test checks that if interceptor doesn't provide a function, 7155// cached constant function is used 7156THREADED_TEST(InterceptorCallICConstantFunctionUsed) { 7157 v8::HandleScope scope; 7158 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7159 templ->SetNamedPropertyHandler(NoBlockGetterX); 7160 LocalContext context; 7161 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7162 v8::Handle<Value> value = CompileRun( 7163 "function inc(x) { return x + 1; };" 7164 "inc(1);" 7165 "o.x = inc;" 7166 "var result = 0;" 7167 "for (var i = 0; i < 1000; i++) {" 7168 " result = o.x(42);" 7169 "}"); 7170 CHECK_EQ(43, value->Int32Value()); 7171} 7172 7173 7174// This test checks that if interceptor provides a function, 7175// even if we cached constant function, interceptor's function 7176// is invoked 7177THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) { 7178 v8::HandleScope scope; 7179 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7180 templ->SetNamedPropertyHandler(InterceptorCallICGetter5); 7181 LocalContext context; 7182 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7183 call_ic_function5 = 7184 v8_compile("function f(x) { return x - 1; }; f")->Run(); 7185 v8::Handle<Value> value = CompileRun( 7186 "function inc(x) { return x + 1; };" 7187 "inc(1);" 7188 "o.x = inc;" 7189 "var result = 0;" 7190 "for (var i = 0; i < 1000; i++) {" 7191 " result = o.x(42);" 7192 "}"); 7193 CHECK_EQ(41, value->Int32Value()); 7194} 7195 7196 7197// Test the case when we stored constant function into 7198// a stub, but it got invalidated later on 7199THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) { 7200 v8::HandleScope scope; 7201 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7202 templ->SetNamedPropertyHandler(NoBlockGetterX); 7203 LocalContext context; 7204 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7205 v8::Handle<Value> value = CompileRun( 7206 "function inc(x) { return x + 1; };" 7207 "inc(1);" 7208 "proto1 = new Object();" 7209 "proto2 = new Object();" 7210 "o.__proto__ = proto1;" 7211 "proto1.__proto__ = proto2;" 7212 "proto2.y = inc;" 7213 // Invoke it many times to compile a stub 7214 "for (var i = 0; i < 7; i++) {" 7215 " o.y(42);" 7216 "}" 7217 "proto1.y = function(x) { return x - 1; };" 7218 "var result = 0;" 7219 "for (var i = 0; i < 7; i++) {" 7220 " result += o.y(42);" 7221 "}"); 7222 CHECK_EQ(41 * 7, value->Int32Value()); 7223} 7224 7225 7226// Test the case when we stored constant function into 7227// a stub, but it got invalidated later on due to override on 7228// global object which is between interceptor and constant function' holders. 7229THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) { 7230 v8::HandleScope scope; 7231 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7232 templ->SetNamedPropertyHandler(NoBlockGetterX); 7233 LocalContext context; 7234 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7235 v8::Handle<Value> value = CompileRun( 7236 "function inc(x) { return x + 1; };" 7237 "inc(1);" 7238 "o.__proto__ = this;" 7239 "this.__proto__.y = inc;" 7240 // Invoke it many times to compile a stub 7241 "for (var i = 0; i < 7; i++) {" 7242 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);" 7243 "}" 7244 "this.y = function(x) { return x - 1; };" 7245 "var result = 0;" 7246 "for (var i = 0; i < 7; i++) {" 7247 " result += o.y(42);" 7248 "}"); 7249 CHECK_EQ(41 * 7, value->Int32Value()); 7250} 7251 7252 7253// Test the case when actual function to call sits on global object. 7254THREADED_TEST(InterceptorCallICCachedFromGlobal) { 7255 v8::HandleScope scope; 7256 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7257 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 7258 7259 LocalContext context; 7260 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 7261 7262 v8::Handle<Value> value = CompileRun( 7263 "try {" 7264 " o.__proto__ = this;" 7265 " for (var i = 0; i < 10; i++) {" 7266 " var v = o.parseFloat('239');" 7267 " if (v != 239) throw v;" 7268 // Now it should be ICed and keep a reference to parseFloat. 7269 " }" 7270 " var result = 0;" 7271 " for (var i = 0; i < 10; i++) {" 7272 " result += o.parseFloat('239');" 7273 " }" 7274 " result" 7275 "} catch(e) {" 7276 " e" 7277 "};"); 7278 CHECK_EQ(239 * 10, value->Int32Value()); 7279} 7280 7281static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name, 7282 const AccessorInfo& info) { 7283 ApiTestFuzzer::Fuzz(); 7284 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data())); 7285 ++(*call_count); 7286 if ((*call_count) % 20 == 0) { 7287 i::Heap::CollectAllGarbage(true); 7288 } 7289 return v8::Handle<Value>(); 7290} 7291 7292static v8::Handle<Value> FastApiCallback_TrivialSignature( 7293 const v8::Arguments& args) { 7294 ApiTestFuzzer::Fuzz(); 7295 CHECK_EQ(args.This(), args.Holder()); 7296 CHECK(args.Data()->Equals(v8_str("method_data"))); 7297 return v8::Integer::New(args[0]->Int32Value() + 1); 7298} 7299 7300static v8::Handle<Value> FastApiCallback_SimpleSignature( 7301 const v8::Arguments& args) { 7302 ApiTestFuzzer::Fuzz(); 7303 CHECK_EQ(args.This()->GetPrototype(), args.Holder()); 7304 CHECK(args.Data()->Equals(v8_str("method_data"))); 7305 // Note, we're using HasRealNamedProperty instead of Has to avoid 7306 // invoking the interceptor again. 7307 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo"))); 7308 return v8::Integer::New(args[0]->Int32Value() + 1); 7309} 7310 7311// Helper to maximize the odds of object moving. 7312static void GenerateSomeGarbage() { 7313 CompileRun( 7314 "var garbage;" 7315 "for (var i = 0; i < 1000; i++) {" 7316 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];" 7317 "}" 7318 "garbage = undefined;"); 7319} 7320 7321THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) { 7322 int interceptor_call_count = 0; 7323 v8::HandleScope scope; 7324 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7325 v8::Handle<v8::FunctionTemplate> method_templ = 7326 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 7327 v8_str("method_data"), 7328 v8::Handle<v8::Signature>()); 7329 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7330 proto_templ->Set(v8_str("method"), method_templ); 7331 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7332 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 7333 NULL, NULL, NULL, NULL, 7334 v8::External::Wrap(&interceptor_call_count)); 7335 LocalContext context; 7336 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7337 GenerateSomeGarbage(); 7338 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7339 v8::Handle<Value> value = CompileRun( 7340 "var result = 0;" 7341 "for (var i = 0; i < 100; i++) {" 7342 " result = o.method(41);" 7343 "}"); 7344 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 7345 CHECK_EQ(100, interceptor_call_count); 7346} 7347 7348THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) { 7349 int interceptor_call_count = 0; 7350 v8::HandleScope scope; 7351 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7352 v8::Handle<v8::FunctionTemplate> method_templ = 7353 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7354 v8_str("method_data"), 7355 v8::Signature::New(fun_templ)); 7356 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7357 proto_templ->Set(v8_str("method"), method_templ); 7358 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7359 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 7360 NULL, NULL, NULL, NULL, 7361 v8::External::Wrap(&interceptor_call_count)); 7362 LocalContext context; 7363 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7364 GenerateSomeGarbage(); 7365 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7366 v8::Handle<Value> value = CompileRun( 7367 "o.foo = 17;" 7368 "var receiver = {};" 7369 "receiver.__proto__ = o;" 7370 "var result = 0;" 7371 "for (var i = 0; i < 100; i++) {" 7372 " result = receiver.method(41);" 7373 "}"); 7374 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 7375 CHECK_EQ(100, interceptor_call_count); 7376} 7377 7378THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) { 7379 int interceptor_call_count = 0; 7380 v8::HandleScope scope; 7381 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7382 v8::Handle<v8::FunctionTemplate> method_templ = 7383 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7384 v8_str("method_data"), 7385 v8::Signature::New(fun_templ)); 7386 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7387 proto_templ->Set(v8_str("method"), method_templ); 7388 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7389 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 7390 NULL, NULL, NULL, NULL, 7391 v8::External::Wrap(&interceptor_call_count)); 7392 LocalContext context; 7393 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7394 GenerateSomeGarbage(); 7395 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7396 v8::Handle<Value> value = CompileRun( 7397 "o.foo = 17;" 7398 "var receiver = {};" 7399 "receiver.__proto__ = o;" 7400 "var result = 0;" 7401 "var saved_result = 0;" 7402 "for (var i = 0; i < 100; i++) {" 7403 " result = receiver.method(41);" 7404 " if (i == 50) {" 7405 " saved_result = result;" 7406 " receiver = {method: function(x) { return x - 1 }};" 7407 " }" 7408 "}"); 7409 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 7410 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7411 CHECK_GE(interceptor_call_count, 50); 7412} 7413 7414THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) { 7415 int interceptor_call_count = 0; 7416 v8::HandleScope scope; 7417 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7418 v8::Handle<v8::FunctionTemplate> method_templ = 7419 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7420 v8_str("method_data"), 7421 v8::Signature::New(fun_templ)); 7422 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7423 proto_templ->Set(v8_str("method"), method_templ); 7424 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7425 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 7426 NULL, NULL, NULL, NULL, 7427 v8::External::Wrap(&interceptor_call_count)); 7428 LocalContext context; 7429 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7430 GenerateSomeGarbage(); 7431 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7432 v8::Handle<Value> value = CompileRun( 7433 "o.foo = 17;" 7434 "var receiver = {};" 7435 "receiver.__proto__ = o;" 7436 "var result = 0;" 7437 "var saved_result = 0;" 7438 "for (var i = 0; i < 100; i++) {" 7439 " result = receiver.method(41);" 7440 " if (i == 50) {" 7441 " saved_result = result;" 7442 " o.method = function(x) { return x - 1 };" 7443 " }" 7444 "}"); 7445 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 7446 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7447 CHECK_GE(interceptor_call_count, 50); 7448} 7449 7450THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) { 7451 int interceptor_call_count = 0; 7452 v8::HandleScope scope; 7453 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7454 v8::Handle<v8::FunctionTemplate> method_templ = 7455 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7456 v8_str("method_data"), 7457 v8::Signature::New(fun_templ)); 7458 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7459 proto_templ->Set(v8_str("method"), method_templ); 7460 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7461 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 7462 NULL, NULL, NULL, NULL, 7463 v8::External::Wrap(&interceptor_call_count)); 7464 LocalContext context; 7465 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7466 GenerateSomeGarbage(); 7467 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7468 v8::TryCatch try_catch; 7469 v8::Handle<Value> value = CompileRun( 7470 "o.foo = 17;" 7471 "var receiver = {};" 7472 "receiver.__proto__ = o;" 7473 "var result = 0;" 7474 "var saved_result = 0;" 7475 "for (var i = 0; i < 100; i++) {" 7476 " result = receiver.method(41);" 7477 " if (i == 50) {" 7478 " saved_result = result;" 7479 " receiver = 333;" 7480 " }" 7481 "}"); 7482 CHECK(try_catch.HasCaught()); 7483 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), 7484 try_catch.Exception()->ToString()); 7485 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7486 CHECK_GE(interceptor_call_count, 50); 7487} 7488 7489THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) { 7490 int interceptor_call_count = 0; 7491 v8::HandleScope scope; 7492 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7493 v8::Handle<v8::FunctionTemplate> method_templ = 7494 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7495 v8_str("method_data"), 7496 v8::Signature::New(fun_templ)); 7497 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7498 proto_templ->Set(v8_str("method"), method_templ); 7499 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7500 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 7501 NULL, NULL, NULL, NULL, 7502 v8::External::Wrap(&interceptor_call_count)); 7503 LocalContext context; 7504 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7505 GenerateSomeGarbage(); 7506 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7507 v8::TryCatch try_catch; 7508 v8::Handle<Value> value = CompileRun( 7509 "o.foo = 17;" 7510 "var receiver = {};" 7511 "receiver.__proto__ = o;" 7512 "var result = 0;" 7513 "var saved_result = 0;" 7514 "for (var i = 0; i < 100; i++) {" 7515 " result = receiver.method(41);" 7516 " if (i == 50) {" 7517 " saved_result = result;" 7518 " receiver = {method: receiver.method};" 7519 " }" 7520 "}"); 7521 CHECK(try_catch.HasCaught()); 7522 CHECK_EQ(v8_str("TypeError: Illegal invocation"), 7523 try_catch.Exception()->ToString()); 7524 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7525 CHECK_GE(interceptor_call_count, 50); 7526} 7527 7528THREADED_TEST(CallICFastApi_TrivialSignature) { 7529 v8::HandleScope scope; 7530 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7531 v8::Handle<v8::FunctionTemplate> method_templ = 7532 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 7533 v8_str("method_data"), 7534 v8::Handle<v8::Signature>()); 7535 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7536 proto_templ->Set(v8_str("method"), method_templ); 7537 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7538 LocalContext context; 7539 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7540 GenerateSomeGarbage(); 7541 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7542 v8::Handle<Value> value = CompileRun( 7543 "var result = 0;" 7544 "for (var i = 0; i < 100; i++) {" 7545 " result = o.method(41);" 7546 "}"); 7547 7548 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 7549} 7550 7551THREADED_TEST(CallICFastApi_SimpleSignature) { 7552 v8::HandleScope scope; 7553 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7554 v8::Handle<v8::FunctionTemplate> method_templ = 7555 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7556 v8_str("method_data"), 7557 v8::Signature::New(fun_templ)); 7558 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7559 proto_templ->Set(v8_str("method"), method_templ); 7560 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7561 LocalContext context; 7562 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7563 GenerateSomeGarbage(); 7564 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7565 v8::Handle<Value> value = CompileRun( 7566 "o.foo = 17;" 7567 "var receiver = {};" 7568 "receiver.__proto__ = o;" 7569 "var result = 0;" 7570 "for (var i = 0; i < 100; i++) {" 7571 " result = receiver.method(41);" 7572 "}"); 7573 7574 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 7575} 7576 7577THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) { 7578 v8::HandleScope scope; 7579 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7580 v8::Handle<v8::FunctionTemplate> method_templ = 7581 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7582 v8_str("method_data"), 7583 v8::Signature::New(fun_templ)); 7584 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7585 proto_templ->Set(v8_str("method"), method_templ); 7586 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7587 LocalContext context; 7588 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7589 GenerateSomeGarbage(); 7590 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7591 v8::Handle<Value> value = CompileRun( 7592 "o.foo = 17;" 7593 "var receiver = {};" 7594 "receiver.__proto__ = o;" 7595 "var result = 0;" 7596 "var saved_result = 0;" 7597 "for (var i = 0; i < 100; i++) {" 7598 " result = receiver.method(41);" 7599 " if (i == 50) {" 7600 " saved_result = result;" 7601 " receiver = {method: function(x) { return x - 1 }};" 7602 " }" 7603 "}"); 7604 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 7605 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7606} 7607 7608THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) { 7609 v8::HandleScope scope; 7610 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7611 v8::Handle<v8::FunctionTemplate> method_templ = 7612 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7613 v8_str("method_data"), 7614 v8::Signature::New(fun_templ)); 7615 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7616 proto_templ->Set(v8_str("method"), method_templ); 7617 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7618 LocalContext context; 7619 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7620 GenerateSomeGarbage(); 7621 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7622 v8::TryCatch try_catch; 7623 v8::Handle<Value> value = CompileRun( 7624 "o.foo = 17;" 7625 "var receiver = {};" 7626 "receiver.__proto__ = o;" 7627 "var result = 0;" 7628 "var saved_result = 0;" 7629 "for (var i = 0; i < 100; i++) {" 7630 " result = receiver.method(41);" 7631 " if (i == 50) {" 7632 " saved_result = result;" 7633 " receiver = 333;" 7634 " }" 7635 "}"); 7636 CHECK(try_catch.HasCaught()); 7637 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), 7638 try_catch.Exception()->ToString()); 7639 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7640} 7641 7642 7643v8::Handle<Value> keyed_call_ic_function; 7644 7645static v8::Handle<Value> InterceptorKeyedCallICGetter( 7646 Local<String> name, const AccessorInfo& info) { 7647 ApiTestFuzzer::Fuzz(); 7648 if (v8_str("x")->Equals(name)) { 7649 return keyed_call_ic_function; 7650 } 7651 return v8::Handle<Value>(); 7652} 7653 7654 7655// Test the case when we stored cacheable lookup into 7656// a stub, but the function name changed (to another cacheable function). 7657THREADED_TEST(InterceptorKeyedCallICKeyChange1) { 7658 v8::HandleScope scope; 7659 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7660 templ->SetNamedPropertyHandler(NoBlockGetterX); 7661 LocalContext context; 7662 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7663 v8::Handle<Value> value = CompileRun( 7664 "proto = new Object();" 7665 "proto.y = function(x) { return x + 1; };" 7666 "proto.z = function(x) { return x - 1; };" 7667 "o.__proto__ = proto;" 7668 "var result = 0;" 7669 "var method = 'y';" 7670 "for (var i = 0; i < 10; i++) {" 7671 " if (i == 5) { method = 'z'; };" 7672 " result += o[method](41);" 7673 "}"); 7674 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7675} 7676 7677 7678// Test the case when we stored cacheable lookup into 7679// a stub, but the function name changed (and the new function is present 7680// both before and after the interceptor in the prototype chain). 7681THREADED_TEST(InterceptorKeyedCallICKeyChange2) { 7682 v8::HandleScope scope; 7683 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7684 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter); 7685 LocalContext context; 7686 context->Global()->Set(v8_str("proto1"), templ->NewInstance()); 7687 keyed_call_ic_function = 7688 v8_compile("function f(x) { return x - 1; }; f")->Run(); 7689 v8::Handle<Value> value = CompileRun( 7690 "o = new Object();" 7691 "proto2 = new Object();" 7692 "o.y = function(x) { return x + 1; };" 7693 "proto2.y = function(x) { return x + 2; };" 7694 "o.__proto__ = proto1;" 7695 "proto1.__proto__ = proto2;" 7696 "var result = 0;" 7697 "var method = 'x';" 7698 "for (var i = 0; i < 10; i++) {" 7699 " if (i == 5) { method = 'y'; };" 7700 " result += o[method](41);" 7701 "}"); 7702 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7703} 7704 7705 7706// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit 7707// on the global object. 7708THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) { 7709 v8::HandleScope scope; 7710 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7711 templ->SetNamedPropertyHandler(NoBlockGetterX); 7712 LocalContext context; 7713 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7714 v8::Handle<Value> value = CompileRun( 7715 "function inc(x) { return x + 1; };" 7716 "inc(1);" 7717 "function dec(x) { return x - 1; };" 7718 "dec(1);" 7719 "o.__proto__ = this;" 7720 "this.__proto__.x = inc;" 7721 "this.__proto__.y = dec;" 7722 "var result = 0;" 7723 "var method = 'x';" 7724 "for (var i = 0; i < 10; i++) {" 7725 " if (i == 5) { method = 'y'; };" 7726 " result += o[method](41);" 7727 "}"); 7728 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7729} 7730 7731 7732// Test the case when actual function to call sits on global object. 7733THREADED_TEST(InterceptorKeyedCallICFromGlobal) { 7734 v8::HandleScope scope; 7735 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7736 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 7737 LocalContext context; 7738 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 7739 7740 v8::Handle<Value> value = CompileRun( 7741 "function len(x) { return x.length; };" 7742 "o.__proto__ = this;" 7743 "var m = 'parseFloat';" 7744 "var result = 0;" 7745 "for (var i = 0; i < 10; i++) {" 7746 " if (i == 5) {" 7747 " m = 'len';" 7748 " saved_result = result;" 7749 " };" 7750 " result = o[m]('239');" 7751 "}"); 7752 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value()); 7753 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7754} 7755 7756// Test the map transition before the interceptor. 7757THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) { 7758 v8::HandleScope scope; 7759 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7760 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 7761 LocalContext context; 7762 context->Global()->Set(v8_str("proto"), templ_o->NewInstance()); 7763 7764 v8::Handle<Value> value = CompileRun( 7765 "var o = new Object();" 7766 "o.__proto__ = proto;" 7767 "o.method = function(x) { return x + 1; };" 7768 "var m = 'method';" 7769 "var result = 0;" 7770 "for (var i = 0; i < 10; i++) {" 7771 " if (i == 5) { o.method = function(x) { return x - 1; }; };" 7772 " result += o[m](41);" 7773 "}"); 7774 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7775} 7776 7777 7778// Test the map transition after the interceptor. 7779THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) { 7780 v8::HandleScope scope; 7781 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7782 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 7783 LocalContext context; 7784 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 7785 7786 v8::Handle<Value> value = CompileRun( 7787 "var proto = new Object();" 7788 "o.__proto__ = proto;" 7789 "proto.method = function(x) { return x + 1; };" 7790 "var m = 'method';" 7791 "var result = 0;" 7792 "for (var i = 0; i < 10; i++) {" 7793 " if (i == 5) { proto.method = function(x) { return x - 1; }; };" 7794 " result += o[m](41);" 7795 "}"); 7796 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7797} 7798 7799 7800static int interceptor_call_count = 0; 7801 7802static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name, 7803 const AccessorInfo& info) { 7804 ApiTestFuzzer::Fuzz(); 7805 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) { 7806 return call_ic_function2; 7807 } 7808 return v8::Handle<Value>(); 7809} 7810 7811 7812// This test should hit load and call ICs for the interceptor case. 7813// Once in a while, the interceptor will reply that a property was not 7814// found in which case we should get a reference error. 7815THREADED_TEST(InterceptorICReferenceErrors) { 7816 v8::HandleScope scope; 7817 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7818 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter); 7819 LocalContext context(0, templ, v8::Handle<Value>()); 7820 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run(); 7821 v8::Handle<Value> value = CompileRun( 7822 "function f() {" 7823 " for (var i = 0; i < 1000; i++) {" 7824 " try { x; } catch(e) { return true; }" 7825 " }" 7826 " return false;" 7827 "};" 7828 "f();"); 7829 CHECK_EQ(true, value->BooleanValue()); 7830 interceptor_call_count = 0; 7831 value = CompileRun( 7832 "function g() {" 7833 " for (var i = 0; i < 1000; i++) {" 7834 " try { x(42); } catch(e) { return true; }" 7835 " }" 7836 " return false;" 7837 "};" 7838 "g();"); 7839 CHECK_EQ(true, value->BooleanValue()); 7840} 7841 7842 7843static int interceptor_ic_exception_get_count = 0; 7844 7845static v8::Handle<Value> InterceptorICExceptionGetter( 7846 Local<String> name, 7847 const AccessorInfo& info) { 7848 ApiTestFuzzer::Fuzz(); 7849 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) { 7850 return call_ic_function3; 7851 } 7852 if (interceptor_ic_exception_get_count == 20) { 7853 return v8::ThrowException(v8_num(42)); 7854 } 7855 // Do not handle get for properties other than x. 7856 return v8::Handle<Value>(); 7857} 7858 7859// Test interceptor load/call IC where the interceptor throws an 7860// exception once in a while. 7861THREADED_TEST(InterceptorICGetterExceptions) { 7862 interceptor_ic_exception_get_count = 0; 7863 v8::HandleScope scope; 7864 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7865 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter); 7866 LocalContext context(0, templ, v8::Handle<Value>()); 7867 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run(); 7868 v8::Handle<Value> value = CompileRun( 7869 "function f() {" 7870 " for (var i = 0; i < 100; i++) {" 7871 " try { x; } catch(e) { return true; }" 7872 " }" 7873 " return false;" 7874 "};" 7875 "f();"); 7876 CHECK_EQ(true, value->BooleanValue()); 7877 interceptor_ic_exception_get_count = 0; 7878 value = CompileRun( 7879 "function f() {" 7880 " for (var i = 0; i < 100; i++) {" 7881 " try { x(42); } catch(e) { return true; }" 7882 " }" 7883 " return false;" 7884 "};" 7885 "f();"); 7886 CHECK_EQ(true, value->BooleanValue()); 7887} 7888 7889 7890static int interceptor_ic_exception_set_count = 0; 7891 7892static v8::Handle<Value> InterceptorICExceptionSetter( 7893 Local<String> key, Local<Value> value, const AccessorInfo&) { 7894 ApiTestFuzzer::Fuzz(); 7895 if (++interceptor_ic_exception_set_count > 20) { 7896 return v8::ThrowException(v8_num(42)); 7897 } 7898 // Do not actually handle setting. 7899 return v8::Handle<Value>(); 7900} 7901 7902// Test interceptor store IC where the interceptor throws an exception 7903// once in a while. 7904THREADED_TEST(InterceptorICSetterExceptions) { 7905 interceptor_ic_exception_set_count = 0; 7906 v8::HandleScope scope; 7907 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7908 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter); 7909 LocalContext context(0, templ, v8::Handle<Value>()); 7910 v8::Handle<Value> value = CompileRun( 7911 "function f() {" 7912 " for (var i = 0; i < 100; i++) {" 7913 " try { x = 42; } catch(e) { return true; }" 7914 " }" 7915 " return false;" 7916 "};" 7917 "f();"); 7918 CHECK_EQ(true, value->BooleanValue()); 7919} 7920 7921 7922// Test that we ignore null interceptors. 7923THREADED_TEST(NullNamedInterceptor) { 7924 v8::HandleScope scope; 7925 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7926 templ->SetNamedPropertyHandler(0); 7927 LocalContext context; 7928 templ->Set("x", v8_num(42)); 7929 v8::Handle<v8::Object> obj = templ->NewInstance(); 7930 context->Global()->Set(v8_str("obj"), obj); 7931 v8::Handle<Value> value = CompileRun("obj.x"); 7932 CHECK(value->IsInt32()); 7933 CHECK_EQ(42, value->Int32Value()); 7934} 7935 7936 7937// Test that we ignore null interceptors. 7938THREADED_TEST(NullIndexedInterceptor) { 7939 v8::HandleScope scope; 7940 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7941 templ->SetIndexedPropertyHandler(0); 7942 LocalContext context; 7943 templ->Set("42", v8_num(42)); 7944 v8::Handle<v8::Object> obj = templ->NewInstance(); 7945 context->Global()->Set(v8_str("obj"), obj); 7946 v8::Handle<Value> value = CompileRun("obj[42]"); 7947 CHECK(value->IsInt32()); 7948 CHECK_EQ(42, value->Int32Value()); 7949} 7950 7951 7952THREADED_TEST(NamedPropertyHandlerGetterAttributes) { 7953 v8::HandleScope scope; 7954 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 7955 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter); 7956 LocalContext env; 7957 env->Global()->Set(v8_str("obj"), 7958 templ->GetFunction()->NewInstance()); 7959 ExpectTrue("obj.x === 42"); 7960 ExpectTrue("!obj.propertyIsEnumerable('x')"); 7961} 7962 7963 7964static v8::Handle<Value> ParentGetter(Local<String> name, 7965 const AccessorInfo& info) { 7966 ApiTestFuzzer::Fuzz(); 7967 return v8_num(1); 7968} 7969 7970 7971static v8::Handle<Value> ChildGetter(Local<String> name, 7972 const AccessorInfo& info) { 7973 ApiTestFuzzer::Fuzz(); 7974 return v8_num(42); 7975} 7976 7977 7978THREADED_TEST(Overriding) { 7979 v8::HandleScope scope; 7980 LocalContext context; 7981 7982 // Parent template. 7983 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New(); 7984 Local<ObjectTemplate> parent_instance_templ = 7985 parent_templ->InstanceTemplate(); 7986 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter); 7987 7988 // Template that inherits from the parent template. 7989 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New(); 7990 Local<ObjectTemplate> child_instance_templ = 7991 child_templ->InstanceTemplate(); 7992 child_templ->Inherit(parent_templ); 7993 // Override 'f'. The child version of 'f' should get called for child 7994 // instances. 7995 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter); 7996 // Add 'g' twice. The 'g' added last should get called for instances. 7997 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter); 7998 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter); 7999 8000 // Add 'h' as an accessor to the proto template with ReadOnly attributes 8001 // so 'h' can be shadowed on the instance object. 8002 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate(); 8003 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0, 8004 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 8005 8006 // Add 'i' as an accessor to the instance template with ReadOnly attributes 8007 // but the attribute does not have effect because it is duplicated with 8008 // NULL setter. 8009 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0, 8010 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 8011 8012 8013 8014 // Instantiate the child template. 8015 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance(); 8016 8017 // Check that the child function overrides the parent one. 8018 context->Global()->Set(v8_str("o"), instance); 8019 Local<Value> value = v8_compile("o.f")->Run(); 8020 // Check that the 'g' that was added last is hit. 8021 CHECK_EQ(42, value->Int32Value()); 8022 value = v8_compile("o.g")->Run(); 8023 CHECK_EQ(42, value->Int32Value()); 8024 8025 // Check 'h' can be shadowed. 8026 value = v8_compile("o.h = 3; o.h")->Run(); 8027 CHECK_EQ(3, value->Int32Value()); 8028 8029 // Check 'i' is cannot be shadowed or changed. 8030 value = v8_compile("o.i = 3; o.i")->Run(); 8031 CHECK_EQ(42, value->Int32Value()); 8032} 8033 8034 8035static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) { 8036 ApiTestFuzzer::Fuzz(); 8037 if (args.IsConstructCall()) { 8038 return v8::Boolean::New(true); 8039 } 8040 return v8::Boolean::New(false); 8041} 8042 8043 8044THREADED_TEST(IsConstructCall) { 8045 v8::HandleScope scope; 8046 8047 // Function template with call handler. 8048 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 8049 templ->SetCallHandler(IsConstructHandler); 8050 8051 LocalContext context; 8052 8053 context->Global()->Set(v8_str("f"), templ->GetFunction()); 8054 Local<Value> value = v8_compile("f()")->Run(); 8055 CHECK(!value->BooleanValue()); 8056 value = v8_compile("new f()")->Run(); 8057 CHECK(value->BooleanValue()); 8058} 8059 8060 8061THREADED_TEST(ObjectProtoToString) { 8062 v8::HandleScope scope; 8063 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 8064 templ->SetClassName(v8_str("MyClass")); 8065 8066 LocalContext context; 8067 8068 Local<String> customized_tostring = v8_str("customized toString"); 8069 8070 // Replace Object.prototype.toString 8071 v8_compile("Object.prototype.toString = function() {" 8072 " return 'customized toString';" 8073 "}")->Run(); 8074 8075 // Normal ToString call should call replaced Object.prototype.toString 8076 Local<v8::Object> instance = templ->GetFunction()->NewInstance(); 8077 Local<String> value = instance->ToString(); 8078 CHECK(value->IsString() && value->Equals(customized_tostring)); 8079 8080 // ObjectProtoToString should not call replace toString function. 8081 value = instance->ObjectProtoToString(); 8082 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]"))); 8083 8084 // Check global 8085 value = context->Global()->ObjectProtoToString(); 8086 CHECK(value->IsString() && value->Equals(v8_str("[object global]"))); 8087 8088 // Check ordinary object 8089 Local<Value> object = v8_compile("new Object()")->Run(); 8090 value = object.As<v8::Object>()->ObjectProtoToString(); 8091 CHECK(value->IsString() && value->Equals(v8_str("[object Object]"))); 8092} 8093 8094 8095THREADED_TEST(ObjectGetConstructorName) { 8096 v8::HandleScope scope; 8097 LocalContext context; 8098 v8_compile("function Parent() {};" 8099 "function Child() {};" 8100 "Child.prototype = new Parent();" 8101 "var outer = { inner: function() { } };" 8102 "var p = new Parent();" 8103 "var c = new Child();" 8104 "var x = new outer.inner();")->Run(); 8105 8106 Local<v8::Value> p = context->Global()->Get(v8_str("p")); 8107 CHECK(p->IsObject() && p->ToObject()->GetConstructorName()->Equals( 8108 v8_str("Parent"))); 8109 8110 Local<v8::Value> c = context->Global()->Get(v8_str("c")); 8111 CHECK(c->IsObject() && c->ToObject()->GetConstructorName()->Equals( 8112 v8_str("Child"))); 8113 8114 Local<v8::Value> x = context->Global()->Get(v8_str("x")); 8115 CHECK(x->IsObject() && x->ToObject()->GetConstructorName()->Equals( 8116 v8_str("outer.inner"))); 8117} 8118 8119 8120bool ApiTestFuzzer::fuzzing_ = false; 8121i::Semaphore* ApiTestFuzzer::all_tests_done_= 8122 i::OS::CreateSemaphore(0); 8123int ApiTestFuzzer::active_tests_; 8124int ApiTestFuzzer::tests_being_run_; 8125int ApiTestFuzzer::current_; 8126 8127 8128// We are in a callback and want to switch to another thread (if we 8129// are currently running the thread fuzzing test). 8130void ApiTestFuzzer::Fuzz() { 8131 if (!fuzzing_) return; 8132 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_; 8133 test->ContextSwitch(); 8134} 8135 8136 8137// Let the next thread go. Since it is also waiting on the V8 lock it may 8138// not start immediately. 8139bool ApiTestFuzzer::NextThread() { 8140 int test_position = GetNextTestNumber(); 8141 const char* test_name = RegisterThreadedTest::nth(current_)->name(); 8142 if (test_position == current_) { 8143 if (kLogThreading) 8144 printf("Stay with %s\n", test_name); 8145 return false; 8146 } 8147 if (kLogThreading) { 8148 printf("Switch from %s to %s\n", 8149 test_name, 8150 RegisterThreadedTest::nth(test_position)->name()); 8151 } 8152 current_ = test_position; 8153 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal(); 8154 return true; 8155} 8156 8157 8158void ApiTestFuzzer::Run() { 8159 // When it is our turn... 8160 gate_->Wait(); 8161 { 8162 // ... get the V8 lock and start running the test. 8163 v8::Locker locker; 8164 CallTest(); 8165 } 8166 // This test finished. 8167 active_ = false; 8168 active_tests_--; 8169 // If it was the last then signal that fact. 8170 if (active_tests_ == 0) { 8171 all_tests_done_->Signal(); 8172 } else { 8173 // Otherwise select a new test and start that. 8174 NextThread(); 8175 } 8176} 8177 8178 8179static unsigned linear_congruential_generator; 8180 8181 8182void ApiTestFuzzer::Setup(PartOfTest part) { 8183 linear_congruential_generator = i::FLAG_testing_prng_seed; 8184 fuzzing_ = true; 8185 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1); 8186 int end = (part == FIRST_PART) 8187 ? (RegisterThreadedTest::count() >> 1) 8188 : RegisterThreadedTest::count(); 8189 active_tests_ = tests_being_run_ = end - start; 8190 for (int i = 0; i < tests_being_run_; i++) { 8191 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start); 8192 } 8193 for (int i = 0; i < active_tests_; i++) { 8194 RegisterThreadedTest::nth(i)->fuzzer_->Start(); 8195 } 8196} 8197 8198 8199static void CallTestNumber(int test_number) { 8200 (RegisterThreadedTest::nth(test_number)->callback())(); 8201} 8202 8203 8204void ApiTestFuzzer::RunAllTests() { 8205 // Set off the first test. 8206 current_ = -1; 8207 NextThread(); 8208 // Wait till they are all done. 8209 all_tests_done_->Wait(); 8210} 8211 8212 8213int ApiTestFuzzer::GetNextTestNumber() { 8214 int next_test; 8215 do { 8216 next_test = (linear_congruential_generator >> 16) % tests_being_run_; 8217 linear_congruential_generator *= 1664525u; 8218 linear_congruential_generator += 1013904223u; 8219 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_); 8220 return next_test; 8221} 8222 8223 8224void ApiTestFuzzer::ContextSwitch() { 8225 // If the new thread is the same as the current thread there is nothing to do. 8226 if (NextThread()) { 8227 // Now it can start. 8228 v8::Unlocker unlocker; 8229 // Wait till someone starts us again. 8230 gate_->Wait(); 8231 // And we're off. 8232 } 8233} 8234 8235 8236void ApiTestFuzzer::TearDown() { 8237 fuzzing_ = false; 8238 for (int i = 0; i < RegisterThreadedTest::count(); i++) { 8239 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_; 8240 if (fuzzer != NULL) fuzzer->Join(); 8241 } 8242} 8243 8244 8245// Lets not be needlessly self-referential. 8246TEST(Threading) { 8247 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART); 8248 ApiTestFuzzer::RunAllTests(); 8249 ApiTestFuzzer::TearDown(); 8250} 8251 8252TEST(Threading2) { 8253 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART); 8254 ApiTestFuzzer::RunAllTests(); 8255 ApiTestFuzzer::TearDown(); 8256} 8257 8258 8259void ApiTestFuzzer::CallTest() { 8260 if (kLogThreading) 8261 printf("Start test %d\n", test_number_); 8262 CallTestNumber(test_number_); 8263 if (kLogThreading) 8264 printf("End test %d\n", test_number_); 8265} 8266 8267 8268static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) { 8269 CHECK(v8::Locker::IsLocked()); 8270 ApiTestFuzzer::Fuzz(); 8271 v8::Unlocker unlocker; 8272 const char* code = "throw 7;"; 8273 { 8274 v8::Locker nested_locker; 8275 v8::HandleScope scope; 8276 v8::Handle<Value> exception; 8277 { v8::TryCatch try_catch; 8278 v8::Handle<Value> value = CompileRun(code); 8279 CHECK(value.IsEmpty()); 8280 CHECK(try_catch.HasCaught()); 8281 // Make sure to wrap the exception in a new handle because 8282 // the handle returned from the TryCatch is destroyed 8283 // when the TryCatch is destroyed. 8284 exception = Local<Value>::New(try_catch.Exception()); 8285 } 8286 return v8::ThrowException(exception); 8287 } 8288} 8289 8290 8291static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) { 8292 CHECK(v8::Locker::IsLocked()); 8293 ApiTestFuzzer::Fuzz(); 8294 v8::Unlocker unlocker; 8295 const char* code = "throw 7;"; 8296 { 8297 v8::Locker nested_locker; 8298 v8::HandleScope scope; 8299 v8::Handle<Value> value = CompileRun(code); 8300 CHECK(value.IsEmpty()); 8301 return v8_str("foo"); 8302 } 8303} 8304 8305 8306// These are locking tests that don't need to be run again 8307// as part of the locking aggregation tests. 8308TEST(NestedLockers) { 8309 v8::Locker locker; 8310 CHECK(v8::Locker::IsLocked()); 8311 v8::HandleScope scope; 8312 LocalContext env; 8313 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS); 8314 Local<Function> fun = fun_templ->GetFunction(); 8315 env->Global()->Set(v8_str("throw_in_js"), fun); 8316 Local<Script> script = v8_compile("(function () {" 8317 " try {" 8318 " throw_in_js();" 8319 " return 42;" 8320 " } catch (e) {" 8321 " return e * 13;" 8322 " }" 8323 "})();"); 8324 CHECK_EQ(91, script->Run()->Int32Value()); 8325} 8326 8327 8328// These are locking tests that don't need to be run again 8329// as part of the locking aggregation tests. 8330TEST(NestedLockersNoTryCatch) { 8331 v8::Locker locker; 8332 v8::HandleScope scope; 8333 LocalContext env; 8334 Local<v8::FunctionTemplate> fun_templ = 8335 v8::FunctionTemplate::New(ThrowInJSNoCatch); 8336 Local<Function> fun = fun_templ->GetFunction(); 8337 env->Global()->Set(v8_str("throw_in_js"), fun); 8338 Local<Script> script = v8_compile("(function () {" 8339 " try {" 8340 " throw_in_js();" 8341 " return 42;" 8342 " } catch (e) {" 8343 " return e * 13;" 8344 " }" 8345 "})();"); 8346 CHECK_EQ(91, script->Run()->Int32Value()); 8347} 8348 8349 8350THREADED_TEST(RecursiveLocking) { 8351 v8::Locker locker; 8352 { 8353 v8::Locker locker2; 8354 CHECK(v8::Locker::IsLocked()); 8355 } 8356} 8357 8358 8359static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) { 8360 ApiTestFuzzer::Fuzz(); 8361 v8::Unlocker unlocker; 8362 return v8::Undefined(); 8363} 8364 8365 8366THREADED_TEST(LockUnlockLock) { 8367 { 8368 v8::Locker locker; 8369 v8::HandleScope scope; 8370 LocalContext env; 8371 Local<v8::FunctionTemplate> fun_templ = 8372 v8::FunctionTemplate::New(UnlockForAMoment); 8373 Local<Function> fun = fun_templ->GetFunction(); 8374 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 8375 Local<Script> script = v8_compile("(function () {" 8376 " unlock_for_a_moment();" 8377 " return 42;" 8378 "})();"); 8379 CHECK_EQ(42, script->Run()->Int32Value()); 8380 } 8381 { 8382 v8::Locker locker; 8383 v8::HandleScope scope; 8384 LocalContext env; 8385 Local<v8::FunctionTemplate> fun_templ = 8386 v8::FunctionTemplate::New(UnlockForAMoment); 8387 Local<Function> fun = fun_templ->GetFunction(); 8388 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 8389 Local<Script> script = v8_compile("(function () {" 8390 " unlock_for_a_moment();" 8391 " return 42;" 8392 "})();"); 8393 CHECK_EQ(42, script->Run()->Int32Value()); 8394 } 8395} 8396 8397 8398static int GetGlobalObjectsCount() { 8399 int count = 0; 8400 i::HeapIterator it; 8401 for (i::HeapObject* object = it.next(); object != NULL; object = it.next()) 8402 if (object->IsJSGlobalObject()) count++; 8403 return count; 8404} 8405 8406 8407static void CheckSurvivingGlobalObjectsCount(int expected) { 8408 // We need to collect all garbage twice to be sure that everything 8409 // has been collected. This is because inline caches are cleared in 8410 // the first garbage collection but some of the maps have already 8411 // been marked at that point. Therefore some of the maps are not 8412 // collected until the second garbage collection. 8413 i::Heap::CollectAllGarbage(false); 8414 i::Heap::CollectAllGarbage(false); 8415 int count = GetGlobalObjectsCount(); 8416#ifdef DEBUG 8417 if (count != expected) i::Heap::TracePathToGlobal(); 8418#endif 8419 CHECK_EQ(expected, count); 8420} 8421 8422 8423TEST(DontLeakGlobalObjects) { 8424 // Regression test for issues 1139850 and 1174891. 8425 8426 v8::V8::Initialize(); 8427 8428 for (int i = 0; i < 5; i++) { 8429 { v8::HandleScope scope; 8430 LocalContext context; 8431 } 8432 CheckSurvivingGlobalObjectsCount(0); 8433 8434 { v8::HandleScope scope; 8435 LocalContext context; 8436 v8_compile("Date")->Run(); 8437 } 8438 CheckSurvivingGlobalObjectsCount(0); 8439 8440 { v8::HandleScope scope; 8441 LocalContext context; 8442 v8_compile("/aaa/")->Run(); 8443 } 8444 CheckSurvivingGlobalObjectsCount(0); 8445 8446 { v8::HandleScope scope; 8447 const char* extension_list[] = { "v8/gc" }; 8448 v8::ExtensionConfiguration extensions(1, extension_list); 8449 LocalContext context(&extensions); 8450 v8_compile("gc();")->Run(); 8451 } 8452 CheckSurvivingGlobalObjectsCount(0); 8453 } 8454} 8455 8456 8457v8::Persistent<v8::Object> some_object; 8458v8::Persistent<v8::Object> bad_handle; 8459 8460void NewPersistentHandleCallback(v8::Persistent<v8::Value> handle, void*) { 8461 v8::HandleScope scope; 8462 bad_handle = v8::Persistent<v8::Object>::New(some_object); 8463 handle.Dispose(); 8464} 8465 8466 8467THREADED_TEST(NewPersistentHandleFromWeakCallback) { 8468 LocalContext context; 8469 8470 v8::Persistent<v8::Object> handle1, handle2; 8471 { 8472 v8::HandleScope scope; 8473 some_object = v8::Persistent<v8::Object>::New(v8::Object::New()); 8474 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8475 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8476 } 8477 // Note: order is implementation dependent alas: currently 8478 // global handle nodes are processed by PostGarbageCollectionProcessing 8479 // in reverse allocation order, so if second allocated handle is deleted, 8480 // weak callback of the first handle would be able to 'reallocate' it. 8481 handle1.MakeWeak(NULL, NewPersistentHandleCallback); 8482 handle2.Dispose(); 8483 i::Heap::CollectAllGarbage(false); 8484} 8485 8486 8487v8::Persistent<v8::Object> to_be_disposed; 8488 8489void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) { 8490 to_be_disposed.Dispose(); 8491 i::Heap::CollectAllGarbage(false); 8492 handle.Dispose(); 8493} 8494 8495 8496THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) { 8497 LocalContext context; 8498 8499 v8::Persistent<v8::Object> handle1, handle2; 8500 { 8501 v8::HandleScope scope; 8502 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8503 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8504 } 8505 handle1.MakeWeak(NULL, DisposeAndForceGcCallback); 8506 to_be_disposed = handle2; 8507 i::Heap::CollectAllGarbage(false); 8508} 8509 8510void DisposingCallback(v8::Persistent<v8::Value> handle, void*) { 8511 handle.Dispose(); 8512} 8513 8514void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) { 8515 v8::HandleScope scope; 8516 v8::Persistent<v8::Object>::New(v8::Object::New()); 8517 handle.Dispose(); 8518} 8519 8520 8521THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) { 8522 LocalContext context; 8523 8524 v8::Persistent<v8::Object> handle1, handle2, handle3; 8525 { 8526 v8::HandleScope scope; 8527 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8528 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8529 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8530 } 8531 handle2.MakeWeak(NULL, DisposingCallback); 8532 handle3.MakeWeak(NULL, HandleCreatingCallback); 8533 i::Heap::CollectAllGarbage(false); 8534} 8535 8536 8537THREADED_TEST(CheckForCrossContextObjectLiterals) { 8538 v8::V8::Initialize(); 8539 8540 const int nof = 2; 8541 const char* sources[nof] = { 8542 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }", 8543 "Object()" 8544 }; 8545 8546 for (int i = 0; i < nof; i++) { 8547 const char* source = sources[i]; 8548 { v8::HandleScope scope; 8549 LocalContext context; 8550 CompileRun(source); 8551 } 8552 { v8::HandleScope scope; 8553 LocalContext context; 8554 CompileRun(source); 8555 } 8556 } 8557} 8558 8559 8560static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) { 8561 v8::HandleScope inner; 8562 env->Enter(); 8563 v8::Handle<Value> three = v8_num(3); 8564 v8::Handle<Value> value = inner.Close(three); 8565 env->Exit(); 8566 return value; 8567} 8568 8569 8570THREADED_TEST(NestedHandleScopeAndContexts) { 8571 v8::HandleScope outer; 8572 v8::Persistent<Context> env = Context::New(); 8573 env->Enter(); 8574 v8::Handle<Value> value = NestedScope(env); 8575 v8::Handle<String> str = value->ToString(); 8576 env->Exit(); 8577 env.Dispose(); 8578} 8579 8580 8581THREADED_TEST(ExternalAllocatedMemory) { 8582 v8::HandleScope outer; 8583 v8::Persistent<Context> env = Context::New(); 8584 const int kSize = 1024*1024; 8585 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize); 8586 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0); 8587} 8588 8589 8590THREADED_TEST(DisposeEnteredContext) { 8591 v8::HandleScope scope; 8592 LocalContext outer; 8593 { v8::Persistent<v8::Context> inner = v8::Context::New(); 8594 inner->Enter(); 8595 inner.Dispose(); 8596 inner.Clear(); 8597 inner->Exit(); 8598 } 8599} 8600 8601 8602// Regression test for issue 54, object templates with internal fields 8603// but no accessors or interceptors did not get their internal field 8604// count set on instances. 8605THREADED_TEST(Regress54) { 8606 v8::HandleScope outer; 8607 LocalContext context; 8608 static v8::Persistent<v8::ObjectTemplate> templ; 8609 if (templ.IsEmpty()) { 8610 v8::HandleScope inner; 8611 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New(); 8612 local->SetInternalFieldCount(1); 8613 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local)); 8614 } 8615 v8::Handle<v8::Object> result = templ->NewInstance(); 8616 CHECK_EQ(1, result->InternalFieldCount()); 8617} 8618 8619 8620// If part of the threaded tests, this test makes ThreadingTest fail 8621// on mac. 8622TEST(CatchStackOverflow) { 8623 v8::HandleScope scope; 8624 LocalContext context; 8625 v8::TryCatch try_catch; 8626 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New( 8627 "function f() {" 8628 " return f();" 8629 "}" 8630 "" 8631 "f();")); 8632 v8::Handle<v8::Value> result = script->Run(); 8633 CHECK(result.IsEmpty()); 8634} 8635 8636 8637static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script, 8638 const char* resource_name, 8639 int line_offset) { 8640 v8::HandleScope scope; 8641 v8::TryCatch try_catch; 8642 v8::Handle<v8::Value> result = script->Run(); 8643 CHECK(result.IsEmpty()); 8644 CHECK(try_catch.HasCaught()); 8645 v8::Handle<v8::Message> message = try_catch.Message(); 8646 CHECK(!message.IsEmpty()); 8647 CHECK_EQ(10 + line_offset, message->GetLineNumber()); 8648 CHECK_EQ(91, message->GetStartPosition()); 8649 CHECK_EQ(92, message->GetEndPosition()); 8650 CHECK_EQ(2, message->GetStartColumn()); 8651 CHECK_EQ(3, message->GetEndColumn()); 8652 v8::String::AsciiValue line(message->GetSourceLine()); 8653 CHECK_EQ(" throw 'nirk';", *line); 8654 v8::String::AsciiValue name(message->GetScriptResourceName()); 8655 CHECK_EQ(resource_name, *name); 8656} 8657 8658 8659THREADED_TEST(TryCatchSourceInfo) { 8660 v8::HandleScope scope; 8661 LocalContext context; 8662 v8::Handle<v8::String> source = v8::String::New( 8663 "function Foo() {\n" 8664 " return Bar();\n" 8665 "}\n" 8666 "\n" 8667 "function Bar() {\n" 8668 " return Baz();\n" 8669 "}\n" 8670 "\n" 8671 "function Baz() {\n" 8672 " throw 'nirk';\n" 8673 "}\n" 8674 "\n" 8675 "Foo();\n"); 8676 8677 const char* resource_name; 8678 v8::Handle<v8::Script> script; 8679 resource_name = "test.js"; 8680 script = v8::Script::Compile(source, v8::String::New(resource_name)); 8681 CheckTryCatchSourceInfo(script, resource_name, 0); 8682 8683 resource_name = "test1.js"; 8684 v8::ScriptOrigin origin1(v8::String::New(resource_name)); 8685 script = v8::Script::Compile(source, &origin1); 8686 CheckTryCatchSourceInfo(script, resource_name, 0); 8687 8688 resource_name = "test2.js"; 8689 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7)); 8690 script = v8::Script::Compile(source, &origin2); 8691 CheckTryCatchSourceInfo(script, resource_name, 7); 8692} 8693 8694 8695THREADED_TEST(CompilationCache) { 8696 v8::HandleScope scope; 8697 LocalContext context; 8698 v8::Handle<v8::String> source0 = v8::String::New("1234"); 8699 v8::Handle<v8::String> source1 = v8::String::New("1234"); 8700 v8::Handle<v8::Script> script0 = 8701 v8::Script::Compile(source0, v8::String::New("test.js")); 8702 v8::Handle<v8::Script> script1 = 8703 v8::Script::Compile(source1, v8::String::New("test.js")); 8704 v8::Handle<v8::Script> script2 = 8705 v8::Script::Compile(source0); // different origin 8706 CHECK_EQ(1234, script0->Run()->Int32Value()); 8707 CHECK_EQ(1234, script1->Run()->Int32Value()); 8708 CHECK_EQ(1234, script2->Run()->Int32Value()); 8709} 8710 8711 8712static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) { 8713 ApiTestFuzzer::Fuzz(); 8714 return v8_num(42); 8715} 8716 8717 8718THREADED_TEST(CallbackFunctionName) { 8719 v8::HandleScope scope; 8720 LocalContext context; 8721 Local<ObjectTemplate> t = ObjectTemplate::New(); 8722 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback)); 8723 context->Global()->Set(v8_str("obj"), t->NewInstance()); 8724 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name"); 8725 CHECK(value->IsString()); 8726 v8::String::AsciiValue name(value); 8727 CHECK_EQ("asdf", *name); 8728} 8729 8730 8731THREADED_TEST(DateAccess) { 8732 v8::HandleScope scope; 8733 LocalContext context; 8734 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0); 8735 CHECK(date->IsDate()); 8736 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue()); 8737} 8738 8739 8740void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) { 8741 v8::Handle<v8::Object> obj = val.As<v8::Object>(); 8742 v8::Handle<v8::Array> props = obj->GetPropertyNames(); 8743 CHECK_EQ(elmc, props->Length()); 8744 for (int i = 0; i < elmc; i++) { 8745 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i))); 8746 CHECK_EQ(elmv[i], *elm); 8747 } 8748} 8749 8750 8751THREADED_TEST(PropertyEnumeration) { 8752 v8::HandleScope scope; 8753 LocalContext context; 8754 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New( 8755 "var result = [];" 8756 "result[0] = {};" 8757 "result[1] = {a: 1, b: 2};" 8758 "result[2] = [1, 2, 3];" 8759 "var proto = {x: 1, y: 2, z: 3};" 8760 "var x = { __proto__: proto, w: 0, z: 1 };" 8761 "result[3] = x;" 8762 "result;"))->Run(); 8763 v8::Handle<v8::Array> elms = obj.As<v8::Array>(); 8764 CHECK_EQ(4, elms->Length()); 8765 int elmc0 = 0; 8766 const char** elmv0 = NULL; 8767 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0); 8768 int elmc1 = 2; 8769 const char* elmv1[] = {"a", "b"}; 8770 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1); 8771 int elmc2 = 3; 8772 const char* elmv2[] = {"0", "1", "2"}; 8773 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2); 8774 int elmc3 = 4; 8775 const char* elmv3[] = {"w", "z", "x", "y"}; 8776 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3); 8777} 8778 8779 8780static bool NamedSetAccessBlocker(Local<v8::Object> obj, 8781 Local<Value> name, 8782 v8::AccessType type, 8783 Local<Value> data) { 8784 return type != v8::ACCESS_SET; 8785} 8786 8787 8788static bool IndexedSetAccessBlocker(Local<v8::Object> obj, 8789 uint32_t key, 8790 v8::AccessType type, 8791 Local<Value> data) { 8792 return type != v8::ACCESS_SET; 8793} 8794 8795 8796THREADED_TEST(DisableAccessChecksWhileConfiguring) { 8797 v8::HandleScope scope; 8798 LocalContext context; 8799 Local<ObjectTemplate> templ = ObjectTemplate::New(); 8800 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker, 8801 IndexedSetAccessBlocker); 8802 templ->Set(v8_str("x"), v8::True()); 8803 Local<v8::Object> instance = templ->NewInstance(); 8804 context->Global()->Set(v8_str("obj"), instance); 8805 Local<Value> value = CompileRun("obj.x"); 8806 CHECK(value->BooleanValue()); 8807} 8808 8809 8810static bool NamedGetAccessBlocker(Local<v8::Object> obj, 8811 Local<Value> name, 8812 v8::AccessType type, 8813 Local<Value> data) { 8814 return false; 8815} 8816 8817 8818static bool IndexedGetAccessBlocker(Local<v8::Object> obj, 8819 uint32_t key, 8820 v8::AccessType type, 8821 Local<Value> data) { 8822 return false; 8823} 8824 8825 8826 8827THREADED_TEST(AccessChecksReenabledCorrectly) { 8828 v8::HandleScope scope; 8829 LocalContext context; 8830 Local<ObjectTemplate> templ = ObjectTemplate::New(); 8831 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker, 8832 IndexedGetAccessBlocker); 8833 templ->Set(v8_str("a"), v8_str("a")); 8834 // Add more than 8 (see kMaxFastProperties) properties 8835 // so that the constructor will force copying map. 8836 // Cannot sprintf, gcc complains unsafety. 8837 char buf[4]; 8838 for (char i = '0'; i <= '9' ; i++) { 8839 buf[0] = i; 8840 for (char j = '0'; j <= '9'; j++) { 8841 buf[1] = j; 8842 for (char k = '0'; k <= '9'; k++) { 8843 buf[2] = k; 8844 buf[3] = 0; 8845 templ->Set(v8_str(buf), v8::Number::New(k)); 8846 } 8847 } 8848 } 8849 8850 Local<v8::Object> instance_1 = templ->NewInstance(); 8851 context->Global()->Set(v8_str("obj_1"), instance_1); 8852 8853 Local<Value> value_1 = CompileRun("obj_1.a"); 8854 CHECK(value_1->IsUndefined()); 8855 8856 Local<v8::Object> instance_2 = templ->NewInstance(); 8857 context->Global()->Set(v8_str("obj_2"), instance_2); 8858 8859 Local<Value> value_2 = CompileRun("obj_2.a"); 8860 CHECK(value_2->IsUndefined()); 8861} 8862 8863 8864// This tests that access check information remains on the global 8865// object template when creating contexts. 8866THREADED_TEST(AccessControlRepeatedContextCreation) { 8867 v8::HandleScope handle_scope; 8868 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 8869 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker, 8870 IndexedSetAccessBlocker); 8871 i::Handle<i::ObjectTemplateInfo> internal_template = 8872 v8::Utils::OpenHandle(*global_template); 8873 CHECK(!internal_template->constructor()->IsUndefined()); 8874 i::Handle<i::FunctionTemplateInfo> constructor( 8875 i::FunctionTemplateInfo::cast(internal_template->constructor())); 8876 CHECK(!constructor->access_check_info()->IsUndefined()); 8877 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 8878 CHECK(!constructor->access_check_info()->IsUndefined()); 8879} 8880 8881 8882THREADED_TEST(TurnOnAccessCheck) { 8883 v8::HandleScope handle_scope; 8884 8885 // Create an environment with access check to the global object disabled by 8886 // default. 8887 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 8888 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker, 8889 IndexedGetAccessBlocker, 8890 v8::Handle<v8::Value>(), 8891 false); 8892 v8::Persistent<Context> context = Context::New(NULL, global_template); 8893 Context::Scope context_scope(context); 8894 8895 // Set up a property and a number of functions. 8896 context->Global()->Set(v8_str("a"), v8_num(1)); 8897 CompileRun("function f1() {return a;}" 8898 "function f2() {return a;}" 8899 "function g1() {return h();}" 8900 "function g2() {return h();}" 8901 "function h() {return 1;}"); 8902 Local<Function> f1 = 8903 Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); 8904 Local<Function> f2 = 8905 Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); 8906 Local<Function> g1 = 8907 Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); 8908 Local<Function> g2 = 8909 Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); 8910 Local<Function> h = 8911 Local<Function>::Cast(context->Global()->Get(v8_str("h"))); 8912 8913 // Get the global object. 8914 v8::Handle<v8::Object> global = context->Global(); 8915 8916 // Call f1 one time and f2 a number of times. This will ensure that f1 still 8917 // uses the runtime system to retreive property a whereas f2 uses global load 8918 // inline cache. 8919 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1))); 8920 for (int i = 0; i < 4; i++) { 8921 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1))); 8922 } 8923 8924 // Same for g1 and g2. 8925 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1))); 8926 for (int i = 0; i < 4; i++) { 8927 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1))); 8928 } 8929 8930 // Detach the global and turn on access check. 8931 context->DetachGlobal(); 8932 context->Global()->TurnOnAccessCheck(); 8933 8934 // Failing access check to property get results in undefined. 8935 CHECK(f1->Call(global, 0, NULL)->IsUndefined()); 8936 CHECK(f2->Call(global, 0, NULL)->IsUndefined()); 8937 8938 // Failing access check to function call results in exception. 8939 CHECK(g1->Call(global, 0, NULL).IsEmpty()); 8940 CHECK(g2->Call(global, 0, NULL).IsEmpty()); 8941 8942 // No failing access check when just returning a constant. 8943 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1))); 8944} 8945 8946 8947v8::Handle<v8::String> a; 8948v8::Handle<v8::String> h; 8949 8950static bool NamedGetAccessBlockAandH(Local<v8::Object> obj, 8951 Local<Value> name, 8952 v8::AccessType type, 8953 Local<Value> data) { 8954 return !(name->Equals(a) || name->Equals(h)); 8955} 8956 8957 8958THREADED_TEST(TurnOnAccessCheckAndRecompile) { 8959 v8::HandleScope handle_scope; 8960 8961 // Create an environment with access check to the global object disabled by 8962 // default. When the registered access checker will block access to properties 8963 // a and h 8964 a = v8_str("a"); 8965 h = v8_str("h"); 8966 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 8967 global_template->SetAccessCheckCallbacks(NamedGetAccessBlockAandH, 8968 IndexedGetAccessBlocker, 8969 v8::Handle<v8::Value>(), 8970 false); 8971 v8::Persistent<Context> context = Context::New(NULL, global_template); 8972 Context::Scope context_scope(context); 8973 8974 // Set up a property and a number of functions. 8975 context->Global()->Set(v8_str("a"), v8_num(1)); 8976 static const char* source = "function f1() {return a;}" 8977 "function f2() {return a;}" 8978 "function g1() {return h();}" 8979 "function g2() {return h();}" 8980 "function h() {return 1;}"; 8981 8982 CompileRun(source); 8983 Local<Function> f1; 8984 Local<Function> f2; 8985 Local<Function> g1; 8986 Local<Function> g2; 8987 Local<Function> h; 8988 f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); 8989 f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); 8990 g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); 8991 g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); 8992 h = Local<Function>::Cast(context->Global()->Get(v8_str("h"))); 8993 8994 // Get the global object. 8995 v8::Handle<v8::Object> global = context->Global(); 8996 8997 // Call f1 one time and f2 a number of times. This will ensure that f1 still 8998 // uses the runtime system to retreive property a whereas f2 uses global load 8999 // inline cache. 9000 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1))); 9001 for (int i = 0; i < 4; i++) { 9002 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1))); 9003 } 9004 9005 // Same for g1 and g2. 9006 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1))); 9007 for (int i = 0; i < 4; i++) { 9008 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1))); 9009 } 9010 9011 // Detach the global and turn on access check now blocking access to property 9012 // a and function h. 9013 context->DetachGlobal(); 9014 context->Global()->TurnOnAccessCheck(); 9015 9016 // Failing access check to property get results in undefined. 9017 CHECK(f1->Call(global, 0, NULL)->IsUndefined()); 9018 CHECK(f2->Call(global, 0, NULL)->IsUndefined()); 9019 9020 // Failing access check to function call results in exception. 9021 CHECK(g1->Call(global, 0, NULL).IsEmpty()); 9022 CHECK(g2->Call(global, 0, NULL).IsEmpty()); 9023 9024 // No failing access check when just returning a constant. 9025 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1))); 9026 9027 // Now compile the source again. And get the newly compiled functions, except 9028 // for h for which access is blocked. 9029 CompileRun(source); 9030 f1 = Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); 9031 f2 = Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); 9032 g1 = Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); 9033 g2 = Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); 9034 CHECK(context->Global()->Get(v8_str("h"))->IsUndefined()); 9035 9036 // Failing access check to property get results in undefined. 9037 CHECK(f1->Call(global, 0, NULL)->IsUndefined()); 9038 CHECK(f2->Call(global, 0, NULL)->IsUndefined()); 9039 9040 // Failing access check to function call results in exception. 9041 CHECK(g1->Call(global, 0, NULL).IsEmpty()); 9042 CHECK(g2->Call(global, 0, NULL).IsEmpty()); 9043} 9044 9045 9046// This test verifies that pre-compilation (aka preparsing) can be called 9047// without initializing the whole VM. Thus we cannot run this test in a 9048// multi-threaded setup. 9049TEST(PreCompile) { 9050 // TODO(155): This test would break without the initialization of V8. This is 9051 // a workaround for now to make this test not fail. 9052 v8::V8::Initialize(); 9053 const char* script = "function foo(a) { return a+1; }"; 9054 v8::ScriptData* sd = 9055 v8::ScriptData::PreCompile(script, i::StrLength(script)); 9056 CHECK_NE(sd->Length(), 0); 9057 CHECK_NE(sd->Data(), NULL); 9058 CHECK(!sd->HasError()); 9059 delete sd; 9060} 9061 9062 9063TEST(PreCompileWithError) { 9064 v8::V8::Initialize(); 9065 const char* script = "function foo(a) { return 1 * * 2; }"; 9066 v8::ScriptData* sd = 9067 v8::ScriptData::PreCompile(script, i::StrLength(script)); 9068 CHECK(sd->HasError()); 9069 delete sd; 9070} 9071 9072 9073TEST(Regress31661) { 9074 v8::V8::Initialize(); 9075 const char* script = " The Definintive Guide"; 9076 v8::ScriptData* sd = 9077 v8::ScriptData::PreCompile(script, i::StrLength(script)); 9078 CHECK(sd->HasError()); 9079 delete sd; 9080} 9081 9082 9083// Tests that ScriptData can be serialized and deserialized. 9084TEST(PreCompileSerialization) { 9085 v8::V8::Initialize(); 9086 const char* script = "function foo(a) { return a+1; }"; 9087 v8::ScriptData* sd = 9088 v8::ScriptData::PreCompile(script, i::StrLength(script)); 9089 9090 // Serialize. 9091 int serialized_data_length = sd->Length(); 9092 char* serialized_data = i::NewArray<char>(serialized_data_length); 9093 memcpy(serialized_data, sd->Data(), serialized_data_length); 9094 9095 // Deserialize. 9096 v8::ScriptData* deserialized_sd = 9097 v8::ScriptData::New(serialized_data, serialized_data_length); 9098 9099 // Verify that the original is the same as the deserialized. 9100 CHECK_EQ(sd->Length(), deserialized_sd->Length()); 9101 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length())); 9102 CHECK_EQ(sd->HasError(), deserialized_sd->HasError()); 9103 9104 delete sd; 9105 delete deserialized_sd; 9106} 9107 9108 9109// Attempts to deserialize bad data. 9110TEST(PreCompileDeserializationError) { 9111 v8::V8::Initialize(); 9112 const char* data = "DONT CARE"; 9113 int invalid_size = 3; 9114 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size); 9115 9116 CHECK_EQ(0, sd->Length()); 9117 9118 delete sd; 9119} 9120 9121 9122// Attempts to deserialize bad data. 9123TEST(PreCompileInvalidPreparseDataError) { 9124 v8::V8::Initialize(); 9125 v8::HandleScope scope; 9126 LocalContext context; 9127 9128 const char* script = "function foo(){ return 5;}\n" 9129 "function bar(){ return 6 + 7;} foo();"; 9130 v8::ScriptData* sd = 9131 v8::ScriptData::PreCompile(script, i::StrLength(script)); 9132 CHECK(!sd->HasError()); 9133 // ScriptDataImpl private implementation details 9134 const int kHeaderSize = i::PreparseDataConstants::kHeaderSize; 9135 const int kFunctionEntrySize = i::FunctionEntry::kSize; 9136 const int kFunctionEntryStartOffset = 0; 9137 const int kFunctionEntryEndOffset = 1; 9138 unsigned* sd_data = 9139 reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data())); 9140 9141 // Overwrite function bar's end position with 0. 9142 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0; 9143 v8::TryCatch try_catch; 9144 9145 Local<String> source = String::New(script); 9146 Local<Script> compiled_script = Script::New(source, NULL, sd); 9147 CHECK(try_catch.HasCaught()); 9148 String::AsciiValue exception_value(try_catch.Message()->Get()); 9149 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar", 9150 *exception_value); 9151 9152 try_catch.Reset(); 9153 // Overwrite function bar's start position with 200. The function entry 9154 // will not be found when searching for it by position. 9155 sd = v8::ScriptData::PreCompile(script, i::StrLength(script)); 9156 sd_data = reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data())); 9157 sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] = 9158 200; 9159 compiled_script = Script::New(source, NULL, sd); 9160 CHECK(try_catch.HasCaught()); 9161 String::AsciiValue second_exception_value(try_catch.Message()->Get()); 9162 CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar", 9163 *second_exception_value); 9164 9165 delete sd; 9166} 9167 9168 9169// Verifies that the Handle<String> and const char* versions of the API produce 9170// the same results (at least for one trivial case). 9171TEST(PreCompileAPIVariationsAreSame) { 9172 v8::V8::Initialize(); 9173 v8::HandleScope scope; 9174 9175 const char* cstring = "function foo(a) { return a+1; }"; 9176 9177 v8::ScriptData* sd_from_cstring = 9178 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring)); 9179 9180 TestAsciiResource* resource = new TestAsciiResource(cstring); 9181 v8::ScriptData* sd_from_external_string = v8::ScriptData::PreCompile( 9182 v8::String::NewExternal(resource)); 9183 9184 v8::ScriptData* sd_from_string = v8::ScriptData::PreCompile( 9185 v8::String::New(cstring)); 9186 9187 CHECK_EQ(sd_from_cstring->Length(), sd_from_external_string->Length()); 9188 CHECK_EQ(0, memcmp(sd_from_cstring->Data(), 9189 sd_from_external_string->Data(), 9190 sd_from_cstring->Length())); 9191 9192 CHECK_EQ(sd_from_cstring->Length(), sd_from_string->Length()); 9193 CHECK_EQ(0, memcmp(sd_from_cstring->Data(), 9194 sd_from_string->Data(), 9195 sd_from_cstring->Length())); 9196 9197 9198 delete sd_from_cstring; 9199 delete sd_from_external_string; 9200 delete sd_from_string; 9201} 9202 9203 9204// This tests that we do not allow dictionary load/call inline caches 9205// to use functions that have not yet been compiled. The potential 9206// problem of loading a function that has not yet been compiled can 9207// arise because we share code between contexts via the compilation 9208// cache. 9209THREADED_TEST(DictionaryICLoadedFunction) { 9210 v8::HandleScope scope; 9211 // Test LoadIC. 9212 for (int i = 0; i < 2; i++) { 9213 LocalContext context; 9214 context->Global()->Set(v8_str("tmp"), v8::True()); 9215 context->Global()->Delete(v8_str("tmp")); 9216 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');"); 9217 } 9218 // Test CallIC. 9219 for (int i = 0; i < 2; i++) { 9220 LocalContext context; 9221 context->Global()->Set(v8_str("tmp"), v8::True()); 9222 context->Global()->Delete(v8_str("tmp")); 9223 CompileRun("for (var j = 0; j < 10; j++) RegExp('')"); 9224 } 9225} 9226 9227 9228// Test that cross-context new calls use the context of the callee to 9229// create the new JavaScript object. 9230THREADED_TEST(CrossContextNew) { 9231 v8::HandleScope scope; 9232 v8::Persistent<Context> context0 = Context::New(); 9233 v8::Persistent<Context> context1 = Context::New(); 9234 9235 // Allow cross-domain access. 9236 Local<String> token = v8_str("<security token>"); 9237 context0->SetSecurityToken(token); 9238 context1->SetSecurityToken(token); 9239 9240 // Set an 'x' property on the Object prototype and define a 9241 // constructor function in context0. 9242 context0->Enter(); 9243 CompileRun("Object.prototype.x = 42; function C() {};"); 9244 context0->Exit(); 9245 9246 // Call the constructor function from context0 and check that the 9247 // result has the 'x' property. 9248 context1->Enter(); 9249 context1->Global()->Set(v8_str("other"), context0->Global()); 9250 Local<Value> value = CompileRun("var instance = new other.C(); instance.x"); 9251 CHECK(value->IsInt32()); 9252 CHECK_EQ(42, value->Int32Value()); 9253 context1->Exit(); 9254 9255 // Dispose the contexts to allow them to be garbage collected. 9256 context0.Dispose(); 9257 context1.Dispose(); 9258} 9259 9260 9261class RegExpInterruptTest { 9262 public: 9263 RegExpInterruptTest() : block_(NULL) {} 9264 ~RegExpInterruptTest() { delete block_; } 9265 void RunTest() { 9266 block_ = i::OS::CreateSemaphore(0); 9267 gc_count_ = 0; 9268 gc_during_regexp_ = 0; 9269 regexp_success_ = false; 9270 gc_success_ = false; 9271 GCThread gc_thread(this); 9272 gc_thread.Start(); 9273 v8::Locker::StartPreemption(1); 9274 9275 LongRunningRegExp(); 9276 { 9277 v8::Unlocker unlock; 9278 gc_thread.Join(); 9279 } 9280 v8::Locker::StopPreemption(); 9281 CHECK(regexp_success_); 9282 CHECK(gc_success_); 9283 } 9284 private: 9285 // Number of garbage collections required. 9286 static const int kRequiredGCs = 5; 9287 9288 class GCThread : public i::Thread { 9289 public: 9290 explicit GCThread(RegExpInterruptTest* test) 9291 : test_(test) {} 9292 virtual void Run() { 9293 test_->CollectGarbage(); 9294 } 9295 private: 9296 RegExpInterruptTest* test_; 9297 }; 9298 9299 void CollectGarbage() { 9300 block_->Wait(); 9301 while (gc_during_regexp_ < kRequiredGCs) { 9302 { 9303 v8::Locker lock; 9304 // TODO(lrn): Perhaps create some garbage before collecting. 9305 i::Heap::CollectAllGarbage(false); 9306 gc_count_++; 9307 } 9308 i::OS::Sleep(1); 9309 } 9310 gc_success_ = true; 9311 } 9312 9313 void LongRunningRegExp() { 9314 block_->Signal(); // Enable garbage collection thread on next preemption. 9315 int rounds = 0; 9316 while (gc_during_regexp_ < kRequiredGCs) { 9317 int gc_before = gc_count_; 9318 { 9319 // Match 15-30 "a"'s against 14 and a "b". 9320 const char* c_source = 9321 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 9322 ".exec('aaaaaaaaaaaaaaab') === null"; 9323 Local<String> source = String::New(c_source); 9324 Local<Script> script = Script::Compile(source); 9325 Local<Value> result = script->Run(); 9326 if (!result->BooleanValue()) { 9327 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit. 9328 return; 9329 } 9330 } 9331 { 9332 // Match 15-30 "a"'s against 15 and a "b". 9333 const char* c_source = 9334 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 9335 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'"; 9336 Local<String> source = String::New(c_source); 9337 Local<Script> script = Script::Compile(source); 9338 Local<Value> result = script->Run(); 9339 if (!result->BooleanValue()) { 9340 gc_during_regexp_ = kRequiredGCs; 9341 return; 9342 } 9343 } 9344 int gc_after = gc_count_; 9345 gc_during_regexp_ += gc_after - gc_before; 9346 rounds++; 9347 i::OS::Sleep(1); 9348 } 9349 regexp_success_ = true; 9350 } 9351 9352 i::Semaphore* block_; 9353 int gc_count_; 9354 int gc_during_regexp_; 9355 bool regexp_success_; 9356 bool gc_success_; 9357}; 9358 9359 9360// Test that a regular expression execution can be interrupted and 9361// survive a garbage collection. 9362TEST(RegExpInterruption) { 9363 v8::Locker lock; 9364 v8::V8::Initialize(); 9365 v8::HandleScope scope; 9366 Local<Context> local_env; 9367 { 9368 LocalContext env; 9369 local_env = env.local(); 9370 } 9371 9372 // Local context should still be live. 9373 CHECK(!local_env.IsEmpty()); 9374 local_env->Enter(); 9375 9376 // Should complete without problems. 9377 RegExpInterruptTest().RunTest(); 9378 9379 local_env->Exit(); 9380} 9381 9382 9383class ApplyInterruptTest { 9384 public: 9385 ApplyInterruptTest() : block_(NULL) {} 9386 ~ApplyInterruptTest() { delete block_; } 9387 void RunTest() { 9388 block_ = i::OS::CreateSemaphore(0); 9389 gc_count_ = 0; 9390 gc_during_apply_ = 0; 9391 apply_success_ = false; 9392 gc_success_ = false; 9393 GCThread gc_thread(this); 9394 gc_thread.Start(); 9395 v8::Locker::StartPreemption(1); 9396 9397 LongRunningApply(); 9398 { 9399 v8::Unlocker unlock; 9400 gc_thread.Join(); 9401 } 9402 v8::Locker::StopPreemption(); 9403 CHECK(apply_success_); 9404 CHECK(gc_success_); 9405 } 9406 private: 9407 // Number of garbage collections required. 9408 static const int kRequiredGCs = 2; 9409 9410 class GCThread : public i::Thread { 9411 public: 9412 explicit GCThread(ApplyInterruptTest* test) 9413 : test_(test) {} 9414 virtual void Run() { 9415 test_->CollectGarbage(); 9416 } 9417 private: 9418 ApplyInterruptTest* test_; 9419 }; 9420 9421 void CollectGarbage() { 9422 block_->Wait(); 9423 while (gc_during_apply_ < kRequiredGCs) { 9424 { 9425 v8::Locker lock; 9426 i::Heap::CollectAllGarbage(false); 9427 gc_count_++; 9428 } 9429 i::OS::Sleep(1); 9430 } 9431 gc_success_ = true; 9432 } 9433 9434 void LongRunningApply() { 9435 block_->Signal(); 9436 int rounds = 0; 9437 while (gc_during_apply_ < kRequiredGCs) { 9438 int gc_before = gc_count_; 9439 { 9440 const char* c_source = 9441 "function do_very_little(bar) {" 9442 " this.foo = bar;" 9443 "}" 9444 "for (var i = 0; i < 100000; i++) {" 9445 " do_very_little.apply(this, ['bar']);" 9446 "}"; 9447 Local<String> source = String::New(c_source); 9448 Local<Script> script = Script::Compile(source); 9449 Local<Value> result = script->Run(); 9450 // Check that no exception was thrown. 9451 CHECK(!result.IsEmpty()); 9452 } 9453 int gc_after = gc_count_; 9454 gc_during_apply_ += gc_after - gc_before; 9455 rounds++; 9456 } 9457 apply_success_ = true; 9458 } 9459 9460 i::Semaphore* block_; 9461 int gc_count_; 9462 int gc_during_apply_; 9463 bool apply_success_; 9464 bool gc_success_; 9465}; 9466 9467 9468// Test that nothing bad happens if we get a preemption just when we were 9469// about to do an apply(). 9470TEST(ApplyInterruption) { 9471 v8::Locker lock; 9472 v8::V8::Initialize(); 9473 v8::HandleScope scope; 9474 Local<Context> local_env; 9475 { 9476 LocalContext env; 9477 local_env = env.local(); 9478 } 9479 9480 // Local context should still be live. 9481 CHECK(!local_env.IsEmpty()); 9482 local_env->Enter(); 9483 9484 // Should complete without problems. 9485 ApplyInterruptTest().RunTest(); 9486 9487 local_env->Exit(); 9488} 9489 9490 9491// Verify that we can clone an object 9492TEST(ObjectClone) { 9493 v8::HandleScope scope; 9494 LocalContext env; 9495 9496 const char* sample = 9497 "var rv = {};" \ 9498 "rv.alpha = 'hello';" \ 9499 "rv.beta = 123;" \ 9500 "rv;"; 9501 9502 // Create an object, verify basics. 9503 Local<Value> val = CompileRun(sample); 9504 CHECK(val->IsObject()); 9505 Local<v8::Object> obj = val.As<v8::Object>(); 9506 obj->Set(v8_str("gamma"), v8_str("cloneme")); 9507 9508 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha"))); 9509 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 9510 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma"))); 9511 9512 // Clone it. 9513 Local<v8::Object> clone = obj->Clone(); 9514 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha"))); 9515 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta"))); 9516 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma"))); 9517 9518 // Set a property on the clone, verify each object. 9519 clone->Set(v8_str("beta"), v8::Integer::New(456)); 9520 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 9521 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta"))); 9522} 9523 9524 9525class AsciiVectorResource : public v8::String::ExternalAsciiStringResource { 9526 public: 9527 explicit AsciiVectorResource(i::Vector<const char> vector) 9528 : data_(vector) {} 9529 virtual ~AsciiVectorResource() {} 9530 virtual size_t length() const { return data_.length(); } 9531 virtual const char* data() const { return data_.start(); } 9532 private: 9533 i::Vector<const char> data_; 9534}; 9535 9536 9537class UC16VectorResource : public v8::String::ExternalStringResource { 9538 public: 9539 explicit UC16VectorResource(i::Vector<const i::uc16> vector) 9540 : data_(vector) {} 9541 virtual ~UC16VectorResource() {} 9542 virtual size_t length() const { return data_.length(); } 9543 virtual const i::uc16* data() const { return data_.start(); } 9544 private: 9545 i::Vector<const i::uc16> data_; 9546}; 9547 9548 9549static void MorphAString(i::String* string, 9550 AsciiVectorResource* ascii_resource, 9551 UC16VectorResource* uc16_resource) { 9552 CHECK(i::StringShape(string).IsExternal()); 9553 if (string->IsAsciiRepresentation()) { 9554 // Check old map is not symbol or long. 9555 CHECK(string->map() == i::Heap::external_ascii_string_map()); 9556 // Morph external string to be TwoByte string. 9557 string->set_map(i::Heap::external_string_map()); 9558 i::ExternalTwoByteString* morphed = 9559 i::ExternalTwoByteString::cast(string); 9560 morphed->set_resource(uc16_resource); 9561 } else { 9562 // Check old map is not symbol or long. 9563 CHECK(string->map() == i::Heap::external_string_map()); 9564 // Morph external string to be ASCII string. 9565 string->set_map(i::Heap::external_ascii_string_map()); 9566 i::ExternalAsciiString* morphed = 9567 i::ExternalAsciiString::cast(string); 9568 morphed->set_resource(ascii_resource); 9569 } 9570} 9571 9572 9573// Test that we can still flatten a string if the components it is built up 9574// from have been turned into 16 bit strings in the mean time. 9575THREADED_TEST(MorphCompositeStringTest) { 9576 const char* c_string = "Now is the time for all good men" 9577 " to come to the aid of the party"; 9578 uint16_t* two_byte_string = AsciiToTwoByteString(c_string); 9579 { 9580 v8::HandleScope scope; 9581 LocalContext env; 9582 AsciiVectorResource ascii_resource( 9583 i::Vector<const char>(c_string, i::StrLength(c_string))); 9584 UC16VectorResource uc16_resource( 9585 i::Vector<const uint16_t>(two_byte_string, 9586 i::StrLength(c_string))); 9587 9588 Local<String> lhs(v8::Utils::ToLocal( 9589 i::Factory::NewExternalStringFromAscii(&ascii_resource))); 9590 Local<String> rhs(v8::Utils::ToLocal( 9591 i::Factory::NewExternalStringFromAscii(&ascii_resource))); 9592 9593 env->Global()->Set(v8_str("lhs"), lhs); 9594 env->Global()->Set(v8_str("rhs"), rhs); 9595 9596 CompileRun( 9597 "var cons = lhs + rhs;" 9598 "var slice = lhs.substring(1, lhs.length - 1);" 9599 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);"); 9600 9601 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource); 9602 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource); 9603 9604 // Now do some stuff to make sure the strings are flattened, etc. 9605 CompileRun( 9606 "/[^a-z]/.test(cons);" 9607 "/[^a-z]/.test(slice);" 9608 "/[^a-z]/.test(slice_on_cons);"); 9609 const char* expected_cons = 9610 "Now is the time for all good men to come to the aid of the party" 9611 "Now is the time for all good men to come to the aid of the party"; 9612 const char* expected_slice = 9613 "ow is the time for all good men to come to the aid of the part"; 9614 const char* expected_slice_on_cons = 9615 "ow is the time for all good men to come to the aid of the party" 9616 "Now is the time for all good men to come to the aid of the part"; 9617 CHECK_EQ(String::New(expected_cons), 9618 env->Global()->Get(v8_str("cons"))); 9619 CHECK_EQ(String::New(expected_slice), 9620 env->Global()->Get(v8_str("slice"))); 9621 CHECK_EQ(String::New(expected_slice_on_cons), 9622 env->Global()->Get(v8_str("slice_on_cons"))); 9623 } 9624 i::DeleteArray(two_byte_string); 9625} 9626 9627 9628TEST(CompileExternalTwoByteSource) { 9629 v8::HandleScope scope; 9630 LocalContext context; 9631 9632 // This is a very short list of sources, which currently is to check for a 9633 // regression caused by r2703. 9634 const char* ascii_sources[] = { 9635 "0.5", 9636 "-0.5", // This mainly testes PushBack in the Scanner. 9637 "--0.5", // This mainly testes PushBack in the Scanner. 9638 NULL 9639 }; 9640 9641 // Compile the sources as external two byte strings. 9642 for (int i = 0; ascii_sources[i] != NULL; i++) { 9643 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]); 9644 UC16VectorResource uc16_resource( 9645 i::Vector<const uint16_t>(two_byte_string, 9646 i::StrLength(ascii_sources[i]))); 9647 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource); 9648 v8::Script::Compile(source); 9649 i::DeleteArray(two_byte_string); 9650 } 9651} 9652 9653 9654class RegExpStringModificationTest { 9655 public: 9656 RegExpStringModificationTest() 9657 : block_(i::OS::CreateSemaphore(0)), 9658 morphs_(0), 9659 morphs_during_regexp_(0), 9660 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)), 9661 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {} 9662 ~RegExpStringModificationTest() { delete block_; } 9663 void RunTest() { 9664 regexp_success_ = false; 9665 morph_success_ = false; 9666 9667 // Initialize the contents of two_byte_content_ to be a uc16 representation 9668 // of "aaaaaaaaaaaaaab". 9669 for (int i = 0; i < 14; i++) { 9670 two_byte_content_[i] = 'a'; 9671 } 9672 two_byte_content_[14] = 'b'; 9673 9674 // Create the input string for the regexp - the one we are going to change 9675 // properties of. 9676 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_); 9677 9678 // Inject the input as a global variable. 9679 i::Handle<i::String> input_name = 9680 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5)); 9681 i::Top::global_context()->global()->SetProperty(*input_name, 9682 *input_, 9683 NONE)->ToObjectChecked(); 9684 9685 9686 MorphThread morph_thread(this); 9687 morph_thread.Start(); 9688 v8::Locker::StartPreemption(1); 9689 LongRunningRegExp(); 9690 { 9691 v8::Unlocker unlock; 9692 morph_thread.Join(); 9693 } 9694 v8::Locker::StopPreemption(); 9695 CHECK(regexp_success_); 9696 CHECK(morph_success_); 9697 } 9698 private: 9699 9700 // Number of string modifications required. 9701 static const int kRequiredModifications = 5; 9702 static const int kMaxModifications = 100; 9703 9704 class MorphThread : public i::Thread { 9705 public: 9706 explicit MorphThread(RegExpStringModificationTest* test) 9707 : test_(test) {} 9708 virtual void Run() { 9709 test_->MorphString(); 9710 } 9711 private: 9712 RegExpStringModificationTest* test_; 9713 }; 9714 9715 void MorphString() { 9716 block_->Wait(); 9717 while (morphs_during_regexp_ < kRequiredModifications && 9718 morphs_ < kMaxModifications) { 9719 { 9720 v8::Locker lock; 9721 // Swap string between ascii and two-byte representation. 9722 i::String* string = *input_; 9723 MorphAString(string, &ascii_resource_, &uc16_resource_); 9724 morphs_++; 9725 } 9726 i::OS::Sleep(1); 9727 } 9728 morph_success_ = true; 9729 } 9730 9731 void LongRunningRegExp() { 9732 block_->Signal(); // Enable morphing thread on next preemption. 9733 while (morphs_during_regexp_ < kRequiredModifications && 9734 morphs_ < kMaxModifications) { 9735 int morphs_before = morphs_; 9736 { 9737 v8::HandleScope scope; 9738 // Match 15-30 "a"'s against 14 and a "b". 9739 const char* c_source = 9740 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 9741 ".exec(input) === null"; 9742 Local<String> source = String::New(c_source); 9743 Local<Script> script = Script::Compile(source); 9744 Local<Value> result = script->Run(); 9745 CHECK(result->IsTrue()); 9746 } 9747 int morphs_after = morphs_; 9748 morphs_during_regexp_ += morphs_after - morphs_before; 9749 } 9750 regexp_success_ = true; 9751 } 9752 9753 i::uc16 two_byte_content_[15]; 9754 i::Semaphore* block_; 9755 int morphs_; 9756 int morphs_during_regexp_; 9757 bool regexp_success_; 9758 bool morph_success_; 9759 i::Handle<i::String> input_; 9760 AsciiVectorResource ascii_resource_; 9761 UC16VectorResource uc16_resource_; 9762}; 9763 9764 9765// Test that a regular expression execution can be interrupted and 9766// the string changed without failing. 9767TEST(RegExpStringModification) { 9768 v8::Locker lock; 9769 v8::V8::Initialize(); 9770 v8::HandleScope scope; 9771 Local<Context> local_env; 9772 { 9773 LocalContext env; 9774 local_env = env.local(); 9775 } 9776 9777 // Local context should still be live. 9778 CHECK(!local_env.IsEmpty()); 9779 local_env->Enter(); 9780 9781 // Should complete without problems. 9782 RegExpStringModificationTest().RunTest(); 9783 9784 local_env->Exit(); 9785} 9786 9787 9788// Test that we can set a property on the global object even if there 9789// is a read-only property in the prototype chain. 9790TEST(ReadOnlyPropertyInGlobalProto) { 9791 v8::HandleScope scope; 9792 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9793 LocalContext context(0, templ); 9794 v8::Handle<v8::Object> global = context->Global(); 9795 v8::Handle<v8::Object> global_proto = 9796 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__"))); 9797 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly); 9798 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly); 9799 // Check without 'eval' or 'with'. 9800 v8::Handle<v8::Value> res = 9801 CompileRun("function f() { x = 42; return x; }; f()"); 9802 // Check with 'eval'. 9803 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()"); 9804 CHECK_EQ(v8::Integer::New(42), res); 9805 // Check with 'with'. 9806 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()"); 9807 CHECK_EQ(v8::Integer::New(42), res); 9808} 9809 9810static int force_set_set_count = 0; 9811static int force_set_get_count = 0; 9812bool pass_on_get = false; 9813 9814static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name, 9815 const v8::AccessorInfo& info) { 9816 force_set_get_count++; 9817 if (pass_on_get) { 9818 return v8::Handle<v8::Value>(); 9819 } else { 9820 return v8::Int32::New(3); 9821 } 9822} 9823 9824static void ForceSetSetter(v8::Local<v8::String> name, 9825 v8::Local<v8::Value> value, 9826 const v8::AccessorInfo& info) { 9827 force_set_set_count++; 9828} 9829 9830static v8::Handle<v8::Value> ForceSetInterceptSetter( 9831 v8::Local<v8::String> name, 9832 v8::Local<v8::Value> value, 9833 const v8::AccessorInfo& info) { 9834 force_set_set_count++; 9835 return v8::Undefined(); 9836} 9837 9838TEST(ForceSet) { 9839 force_set_get_count = 0; 9840 force_set_set_count = 0; 9841 pass_on_get = false; 9842 9843 v8::HandleScope scope; 9844 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9845 v8::Handle<v8::String> access_property = v8::String::New("a"); 9846 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter); 9847 LocalContext context(NULL, templ); 9848 v8::Handle<v8::Object> global = context->Global(); 9849 9850 // Ordinary properties 9851 v8::Handle<v8::String> simple_property = v8::String::New("p"); 9852 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly); 9853 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9854 // This should fail because the property is read-only 9855 global->Set(simple_property, v8::Int32::New(5)); 9856 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9857 // This should succeed even though the property is read-only 9858 global->ForceSet(simple_property, v8::Int32::New(6)); 9859 CHECK_EQ(6, global->Get(simple_property)->Int32Value()); 9860 9861 // Accessors 9862 CHECK_EQ(0, force_set_set_count); 9863 CHECK_EQ(0, force_set_get_count); 9864 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 9865 // CHECK_EQ the property shouldn't override it, just call the setter 9866 // which in this case does nothing. 9867 global->Set(access_property, v8::Int32::New(7)); 9868 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 9869 CHECK_EQ(1, force_set_set_count); 9870 CHECK_EQ(2, force_set_get_count); 9871 // Forcing the property to be set should override the accessor without 9872 // calling it 9873 global->ForceSet(access_property, v8::Int32::New(8)); 9874 CHECK_EQ(8, global->Get(access_property)->Int32Value()); 9875 CHECK_EQ(1, force_set_set_count); 9876 CHECK_EQ(2, force_set_get_count); 9877} 9878 9879TEST(ForceSetWithInterceptor) { 9880 force_set_get_count = 0; 9881 force_set_set_count = 0; 9882 pass_on_get = false; 9883 9884 v8::HandleScope scope; 9885 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9886 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter); 9887 LocalContext context(NULL, templ); 9888 v8::Handle<v8::Object> global = context->Global(); 9889 9890 v8::Handle<v8::String> some_property = v8::String::New("a"); 9891 CHECK_EQ(0, force_set_set_count); 9892 CHECK_EQ(0, force_set_get_count); 9893 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 9894 // Setting the property shouldn't override it, just call the setter 9895 // which in this case does nothing. 9896 global->Set(some_property, v8::Int32::New(7)); 9897 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 9898 CHECK_EQ(1, force_set_set_count); 9899 CHECK_EQ(2, force_set_get_count); 9900 // Getting the property when the interceptor returns an empty handle 9901 // should yield undefined, since the property isn't present on the 9902 // object itself yet. 9903 pass_on_get = true; 9904 CHECK(global->Get(some_property)->IsUndefined()); 9905 CHECK_EQ(1, force_set_set_count); 9906 CHECK_EQ(3, force_set_get_count); 9907 // Forcing the property to be set should cause the value to be 9908 // set locally without calling the interceptor. 9909 global->ForceSet(some_property, v8::Int32::New(8)); 9910 CHECK_EQ(8, global->Get(some_property)->Int32Value()); 9911 CHECK_EQ(1, force_set_set_count); 9912 CHECK_EQ(4, force_set_get_count); 9913 // Reenabling the interceptor should cause it to take precedence over 9914 // the property 9915 pass_on_get = false; 9916 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 9917 CHECK_EQ(1, force_set_set_count); 9918 CHECK_EQ(5, force_set_get_count); 9919 // The interceptor should also work for other properties 9920 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value()); 9921 CHECK_EQ(1, force_set_set_count); 9922 CHECK_EQ(6, force_set_get_count); 9923} 9924 9925 9926THREADED_TEST(ForceDelete) { 9927 v8::HandleScope scope; 9928 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9929 LocalContext context(NULL, templ); 9930 v8::Handle<v8::Object> global = context->Global(); 9931 9932 // Ordinary properties 9933 v8::Handle<v8::String> simple_property = v8::String::New("p"); 9934 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete); 9935 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9936 // This should fail because the property is dont-delete. 9937 CHECK(!global->Delete(simple_property)); 9938 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9939 // This should succeed even though the property is dont-delete. 9940 CHECK(global->ForceDelete(simple_property)); 9941 CHECK(global->Get(simple_property)->IsUndefined()); 9942} 9943 9944 9945static int force_delete_interceptor_count = 0; 9946static bool pass_on_delete = false; 9947 9948 9949static v8::Handle<v8::Boolean> ForceDeleteDeleter( 9950 v8::Local<v8::String> name, 9951 const v8::AccessorInfo& info) { 9952 force_delete_interceptor_count++; 9953 if (pass_on_delete) { 9954 return v8::Handle<v8::Boolean>(); 9955 } else { 9956 return v8::True(); 9957 } 9958} 9959 9960 9961THREADED_TEST(ForceDeleteWithInterceptor) { 9962 force_delete_interceptor_count = 0; 9963 pass_on_delete = false; 9964 9965 v8::HandleScope scope; 9966 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9967 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter); 9968 LocalContext context(NULL, templ); 9969 v8::Handle<v8::Object> global = context->Global(); 9970 9971 v8::Handle<v8::String> some_property = v8::String::New("a"); 9972 global->Set(some_property, v8::Integer::New(42), v8::DontDelete); 9973 9974 // Deleting a property should get intercepted and nothing should 9975 // happen. 9976 CHECK_EQ(0, force_delete_interceptor_count); 9977 CHECK(global->Delete(some_property)); 9978 CHECK_EQ(1, force_delete_interceptor_count); 9979 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 9980 // Deleting the property when the interceptor returns an empty 9981 // handle should not delete the property since it is DontDelete. 9982 pass_on_delete = true; 9983 CHECK(!global->Delete(some_property)); 9984 CHECK_EQ(2, force_delete_interceptor_count); 9985 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 9986 // Forcing the property to be deleted should delete the value 9987 // without calling the interceptor. 9988 CHECK(global->ForceDelete(some_property)); 9989 CHECK(global->Get(some_property)->IsUndefined()); 9990 CHECK_EQ(2, force_delete_interceptor_count); 9991} 9992 9993 9994// Make sure that forcing a delete invalidates any IC stubs, so we 9995// don't read the hole value. 9996THREADED_TEST(ForceDeleteIC) { 9997 v8::HandleScope scope; 9998 LocalContext context; 9999 // Create a DontDelete variable on the global object. 10000 CompileRun("this.__proto__ = { foo: 'horse' };" 10001 "var foo = 'fish';" 10002 "function f() { return foo.length; }"); 10003 // Initialize the IC for foo in f. 10004 CompileRun("for (var i = 0; i < 4; i++) f();"); 10005 // Make sure the value of foo is correct before the deletion. 10006 CHECK_EQ(4, CompileRun("f()")->Int32Value()); 10007 // Force the deletion of foo. 10008 CHECK(context->Global()->ForceDelete(v8_str("foo"))); 10009 // Make sure the value for foo is read from the prototype, and that 10010 // we don't get in trouble with reading the deleted cell value 10011 // sentinel. 10012 CHECK_EQ(5, CompileRun("f()")->Int32Value()); 10013} 10014 10015 10016v8::Persistent<Context> calling_context0; 10017v8::Persistent<Context> calling_context1; 10018v8::Persistent<Context> calling_context2; 10019 10020 10021// Check that the call to the callback is initiated in 10022// calling_context2, the directly calling context is calling_context1 10023// and the callback itself is in calling_context0. 10024static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) { 10025 ApiTestFuzzer::Fuzz(); 10026 CHECK(Context::GetCurrent() == calling_context0); 10027 CHECK(Context::GetCalling() == calling_context1); 10028 CHECK(Context::GetEntered() == calling_context2); 10029 return v8::Integer::New(42); 10030} 10031 10032 10033THREADED_TEST(GetCallingContext) { 10034 v8::HandleScope scope; 10035 10036 calling_context0 = Context::New(); 10037 calling_context1 = Context::New(); 10038 calling_context2 = Context::New(); 10039 10040 // Allow cross-domain access. 10041 Local<String> token = v8_str("<security token>"); 10042 calling_context0->SetSecurityToken(token); 10043 calling_context1->SetSecurityToken(token); 10044 calling_context2->SetSecurityToken(token); 10045 10046 // Create an object with a C++ callback in context0. 10047 calling_context0->Enter(); 10048 Local<v8::FunctionTemplate> callback_templ = 10049 v8::FunctionTemplate::New(GetCallingContextCallback); 10050 calling_context0->Global()->Set(v8_str("callback"), 10051 callback_templ->GetFunction()); 10052 calling_context0->Exit(); 10053 10054 // Expose context0 in context1 and setup a function that calls the 10055 // callback function. 10056 calling_context1->Enter(); 10057 calling_context1->Global()->Set(v8_str("context0"), 10058 calling_context0->Global()); 10059 CompileRun("function f() { context0.callback() }"); 10060 calling_context1->Exit(); 10061 10062 // Expose context1 in context2 and call the callback function in 10063 // context0 indirectly through f in context1. 10064 calling_context2->Enter(); 10065 calling_context2->Global()->Set(v8_str("context1"), 10066 calling_context1->Global()); 10067 CompileRun("context1.f()"); 10068 calling_context2->Exit(); 10069 10070 // Dispose the contexts to allow them to be garbage collected. 10071 calling_context0.Dispose(); 10072 calling_context1.Dispose(); 10073 calling_context2.Dispose(); 10074 calling_context0.Clear(); 10075 calling_context1.Clear(); 10076 calling_context2.Clear(); 10077} 10078 10079 10080// Check that a variable declaration with no explicit initialization 10081// value does not shadow an existing property in the prototype chain. 10082// 10083// This is consistent with Firefox and Safari. 10084// 10085// See http://crbug.com/12548. 10086THREADED_TEST(InitGlobalVarInProtoChain) { 10087 v8::HandleScope scope; 10088 LocalContext context; 10089 // Introduce a variable in the prototype chain. 10090 CompileRun("__proto__.x = 42"); 10091 v8::Handle<v8::Value> result = CompileRun("var x; x"); 10092 CHECK(!result->IsUndefined()); 10093 CHECK_EQ(42, result->Int32Value()); 10094} 10095 10096 10097// Regression test for issue 398. 10098// If a function is added to an object, creating a constant function 10099// field, and the result is cloned, replacing the constant function on the 10100// original should not affect the clone. 10101// See http://code.google.com/p/v8/issues/detail?id=398 10102THREADED_TEST(ReplaceConstantFunction) { 10103 v8::HandleScope scope; 10104 LocalContext context; 10105 v8::Handle<v8::Object> obj = v8::Object::New(); 10106 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 10107 v8::Handle<v8::String> foo_string = v8::String::New("foo"); 10108 obj->Set(foo_string, func_templ->GetFunction()); 10109 v8::Handle<v8::Object> obj_clone = obj->Clone(); 10110 obj_clone->Set(foo_string, v8::String::New("Hello")); 10111 CHECK(!obj->Get(foo_string)->IsUndefined()); 10112} 10113 10114 10115// Regression test for http://crbug.com/16276. 10116THREADED_TEST(Regress16276) { 10117 v8::HandleScope scope; 10118 LocalContext context; 10119 // Force the IC in f to be a dictionary load IC. 10120 CompileRun("function f(obj) { return obj.x; }\n" 10121 "var obj = { x: { foo: 42 }, y: 87 };\n" 10122 "var x = obj.x;\n" 10123 "delete obj.y;\n" 10124 "for (var i = 0; i < 5; i++) f(obj);"); 10125 // Detach the global object to make 'this' refer directly to the 10126 // global object (not the proxy), and make sure that the dictionary 10127 // load IC doesn't mess up loading directly from the global object. 10128 context->DetachGlobal(); 10129 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value()); 10130} 10131 10132 10133THREADED_TEST(PixelArray) { 10134 v8::HandleScope scope; 10135 LocalContext context; 10136 const int kElementCount = 260; 10137 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); 10138 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount, 10139 pixel_data); 10140 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 10141 for (int i = 0; i < kElementCount; i++) { 10142 pixels->set(i, i % 256); 10143 } 10144 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 10145 for (int i = 0; i < kElementCount; i++) { 10146 CHECK_EQ(i % 256, pixels->get(i)); 10147 CHECK_EQ(i % 256, pixel_data[i]); 10148 } 10149 10150 v8::Handle<v8::Object> obj = v8::Object::New(); 10151 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 10152 // Set the elements to be the pixels. 10153 // jsobj->set_elements(*pixels); 10154 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount); 10155 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 10156 obj->Set(v8_str("field"), v8::Int32::New(1503)); 10157 context->Global()->Set(v8_str("pixels"), obj); 10158 v8::Handle<v8::Value> result = CompileRun("pixels.field"); 10159 CHECK_EQ(1503, result->Int32Value()); 10160 result = CompileRun("pixels[1]"); 10161 CHECK_EQ(1, result->Int32Value()); 10162 10163 result = CompileRun("var sum = 0;" 10164 "for (var i = 0; i < 8; i++) {" 10165 " sum += pixels[i] = pixels[i] = -i;" 10166 "}" 10167 "sum;"); 10168 CHECK_EQ(-28, result->Int32Value()); 10169 10170 result = CompileRun("var sum = 0;" 10171 "for (var i = 0; i < 8; i++) {" 10172 " sum += pixels[i] = pixels[i] = 0;" 10173 "}" 10174 "sum;"); 10175 CHECK_EQ(0, result->Int32Value()); 10176 10177 result = CompileRun("var sum = 0;" 10178 "for (var i = 0; i < 8; i++) {" 10179 " sum += pixels[i] = pixels[i] = 255;" 10180 "}" 10181 "sum;"); 10182 CHECK_EQ(8 * 255, result->Int32Value()); 10183 10184 result = CompileRun("var sum = 0;" 10185 "for (var i = 0; i < 8; i++) {" 10186 " sum += pixels[i] = pixels[i] = 256 + i;" 10187 "}" 10188 "sum;"); 10189 CHECK_EQ(2076, result->Int32Value()); 10190 10191 result = CompileRun("var sum = 0;" 10192 "for (var i = 0; i < 8; i++) {" 10193 " sum += pixels[i] = pixels[i] = i;" 10194 "}" 10195 "sum;"); 10196 CHECK_EQ(28, result->Int32Value()); 10197 10198 result = CompileRun("var sum = 0;" 10199 "for (var i = 0; i < 8; i++) {" 10200 " sum += pixels[i];" 10201 "}" 10202 "sum;"); 10203 CHECK_EQ(28, result->Int32Value()); 10204 10205 i::Handle<i::Smi> value(i::Smi::FromInt(2)); 10206 i::SetElement(jsobj, 1, value); 10207 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 10208 *value.location() = i::Smi::FromInt(256); 10209 i::SetElement(jsobj, 1, value); 10210 CHECK_EQ(255, 10211 i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 10212 *value.location() = i::Smi::FromInt(-1); 10213 i::SetElement(jsobj, 1, value); 10214 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 10215 10216 result = CompileRun("for (var i = 0; i < 8; i++) {" 10217 " pixels[i] = (i * 65) - 109;" 10218 "}" 10219 "pixels[1] + pixels[6];"); 10220 CHECK_EQ(255, result->Int32Value()); 10221 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value()); 10222 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 10223 CHECK_EQ(21, 10224 i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value()); 10225 CHECK_EQ(86, 10226 i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value()); 10227 CHECK_EQ(151, 10228 i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value()); 10229 CHECK_EQ(216, 10230 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 10231 CHECK_EQ(255, 10232 i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value()); 10233 CHECK_EQ(255, 10234 i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value()); 10235 result = CompileRun("var sum = 0;" 10236 "for (var i = 0; i < 8; i++) {" 10237 " sum += pixels[i];" 10238 "}" 10239 "sum;"); 10240 CHECK_EQ(984, result->Int32Value()); 10241 10242 result = CompileRun("for (var i = 0; i < 8; i++) {" 10243 " pixels[i] = (i * 1.1);" 10244 "}" 10245 "pixels[1] + pixels[6];"); 10246 CHECK_EQ(8, result->Int32Value()); 10247 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0)->ToObjectChecked())->value()); 10248 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1)->ToObjectChecked())->value()); 10249 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2)->ToObjectChecked())->value()); 10250 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3)->ToObjectChecked())->value()); 10251 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4)->ToObjectChecked())->value()); 10252 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 10253 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value()); 10254 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value()); 10255 10256 result = CompileRun("for (var i = 0; i < 8; i++) {" 10257 " pixels[7] = undefined;" 10258 "}" 10259 "pixels[7];"); 10260 CHECK_EQ(0, result->Int32Value()); 10261 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7)->ToObjectChecked())->value()); 10262 10263 result = CompileRun("for (var i = 0; i < 8; i++) {" 10264 " pixels[6] = '2.3';" 10265 "}" 10266 "pixels[6];"); 10267 CHECK_EQ(2, result->Int32Value()); 10268 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6)->ToObjectChecked())->value()); 10269 10270 result = CompileRun("for (var i = 0; i < 8; i++) {" 10271 " pixels[5] = NaN;" 10272 "}" 10273 "pixels[5];"); 10274 CHECK_EQ(0, result->Int32Value()); 10275 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 10276 10277 result = CompileRun("for (var i = 0; i < 8; i++) {" 10278 " pixels[8] = Infinity;" 10279 "}" 10280 "pixels[8];"); 10281 CHECK_EQ(255, result->Int32Value()); 10282 CHECK_EQ(255, 10283 i::Smi::cast(jsobj->GetElement(8)->ToObjectChecked())->value()); 10284 10285 result = CompileRun("for (var i = 0; i < 8; i++) {" 10286 " pixels[9] = -Infinity;" 10287 "}" 10288 "pixels[9];"); 10289 CHECK_EQ(0, result->Int32Value()); 10290 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9)->ToObjectChecked())->value()); 10291 10292 result = CompileRun("pixels[3] = 33;" 10293 "delete pixels[3];" 10294 "pixels[3];"); 10295 CHECK_EQ(33, result->Int32Value()); 10296 10297 result = CompileRun("pixels[0] = 10; pixels[1] = 11;" 10298 "pixels[2] = 12; pixels[3] = 13;" 10299 "pixels.__defineGetter__('2'," 10300 "function() { return 120; });" 10301 "pixels[2];"); 10302 CHECK_EQ(12, result->Int32Value()); 10303 10304 result = CompileRun("var js_array = new Array(40);" 10305 "js_array[0] = 77;" 10306 "js_array;"); 10307 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 10308 10309 result = CompileRun("pixels[1] = 23;" 10310 "pixels.__proto__ = [];" 10311 "js_array.__proto__ = pixels;" 10312 "js_array.concat(pixels);"); 10313 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 10314 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 10315 10316 result = CompileRun("pixels[1] = 23;"); 10317 CHECK_EQ(23, result->Int32Value()); 10318 10319 // Test for index greater than 255. Regression test for: 10320 // http://code.google.com/p/chromium/issues/detail?id=26337. 10321 result = CompileRun("pixels[256] = 255;"); 10322 CHECK_EQ(255, result->Int32Value()); 10323 result = CompileRun("var i = 0;" 10324 "for (var j = 0; j < 8; j++) { i = pixels[256]; }" 10325 "i"); 10326 CHECK_EQ(255, result->Int32Value()); 10327 10328 free(pixel_data); 10329} 10330 10331 10332THREADED_TEST(PixelArrayInfo) { 10333 v8::HandleScope scope; 10334 LocalContext context; 10335 for (int size = 0; size < 100; size += 10) { 10336 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size)); 10337 v8::Handle<v8::Object> obj = v8::Object::New(); 10338 obj->SetIndexedPropertiesToPixelData(pixel_data, size); 10339 CHECK(obj->HasIndexedPropertiesInPixelData()); 10340 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData()); 10341 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength()); 10342 free(pixel_data); 10343 } 10344} 10345 10346 10347static int ExternalArrayElementSize(v8::ExternalArrayType array_type) { 10348 switch (array_type) { 10349 case v8::kExternalByteArray: 10350 case v8::kExternalUnsignedByteArray: 10351 return 1; 10352 break; 10353 case v8::kExternalShortArray: 10354 case v8::kExternalUnsignedShortArray: 10355 return 2; 10356 break; 10357 case v8::kExternalIntArray: 10358 case v8::kExternalUnsignedIntArray: 10359 case v8::kExternalFloatArray: 10360 return 4; 10361 break; 10362 default: 10363 UNREACHABLE(); 10364 return -1; 10365 } 10366 UNREACHABLE(); 10367 return -1; 10368} 10369 10370 10371template <class ExternalArrayClass, class ElementType> 10372static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, 10373 int64_t low, 10374 int64_t high) { 10375 v8::HandleScope scope; 10376 LocalContext context; 10377 const int kElementCount = 40; 10378 int element_size = ExternalArrayElementSize(array_type); 10379 ElementType* array_data = 10380 static_cast<ElementType*>(malloc(kElementCount * element_size)); 10381 i::Handle<ExternalArrayClass> array = 10382 i::Handle<ExternalArrayClass>::cast( 10383 i::Factory::NewExternalArray(kElementCount, array_type, array_data)); 10384 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 10385 for (int i = 0; i < kElementCount; i++) { 10386 array->set(i, static_cast<ElementType>(i)); 10387 } 10388 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 10389 for (int i = 0; i < kElementCount; i++) { 10390 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i))); 10391 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i])); 10392 } 10393 10394 v8::Handle<v8::Object> obj = v8::Object::New(); 10395 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 10396 // Set the elements to be the external array. 10397 obj->SetIndexedPropertiesToExternalArrayData(array_data, 10398 array_type, 10399 kElementCount); 10400 CHECK_EQ( 10401 1, static_cast<int>(jsobj->GetElement(1)->ToObjectChecked()->Number())); 10402 obj->Set(v8_str("field"), v8::Int32::New(1503)); 10403 context->Global()->Set(v8_str("ext_array"), obj); 10404 v8::Handle<v8::Value> result = CompileRun("ext_array.field"); 10405 CHECK_EQ(1503, result->Int32Value()); 10406 result = CompileRun("ext_array[1]"); 10407 CHECK_EQ(1, result->Int32Value()); 10408 10409 // Check pass through of assigned smis 10410 result = CompileRun("var sum = 0;" 10411 "for (var i = 0; i < 8; i++) {" 10412 " sum += ext_array[i] = ext_array[i] = -i;" 10413 "}" 10414 "sum;"); 10415 CHECK_EQ(-28, result->Int32Value()); 10416 10417 // Check assigned smis 10418 result = CompileRun("for (var i = 0; i < 8; i++) {" 10419 " ext_array[i] = i;" 10420 "}" 10421 "var sum = 0;" 10422 "for (var i = 0; i < 8; i++) {" 10423 " sum += ext_array[i];" 10424 "}" 10425 "sum;"); 10426 CHECK_EQ(28, result->Int32Value()); 10427 10428 // Check assigned smis in reverse order 10429 result = CompileRun("for (var i = 8; --i >= 0; ) {" 10430 " ext_array[i] = i;" 10431 "}" 10432 "var sum = 0;" 10433 "for (var i = 0; i < 8; i++) {" 10434 " sum += ext_array[i];" 10435 "}" 10436 "sum;"); 10437 CHECK_EQ(28, result->Int32Value()); 10438 10439 // Check pass through of assigned HeapNumbers 10440 result = CompileRun("var sum = 0;" 10441 "for (var i = 0; i < 16; i+=2) {" 10442 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);" 10443 "}" 10444 "sum;"); 10445 CHECK_EQ(-28, result->Int32Value()); 10446 10447 // Check assigned HeapNumbers 10448 result = CompileRun("for (var i = 0; i < 16; i+=2) {" 10449 " ext_array[i] = (i * 0.5);" 10450 "}" 10451 "var sum = 0;" 10452 "for (var i = 0; i < 16; i+=2) {" 10453 " sum += ext_array[i];" 10454 "}" 10455 "sum;"); 10456 CHECK_EQ(28, result->Int32Value()); 10457 10458 // Check assigned HeapNumbers in reverse order 10459 result = CompileRun("for (var i = 14; i >= 0; i-=2) {" 10460 " ext_array[i] = (i * 0.5);" 10461 "}" 10462 "var sum = 0;" 10463 "for (var i = 0; i < 16; i+=2) {" 10464 " sum += ext_array[i];" 10465 "}" 10466 "sum;"); 10467 CHECK_EQ(28, result->Int32Value()); 10468 10469 i::ScopedVector<char> test_buf(1024); 10470 10471 // Check legal boundary conditions. 10472 // The repeated loads and stores ensure the ICs are exercised. 10473 const char* boundary_program = 10474 "var res = 0;" 10475 "for (var i = 0; i < 16; i++) {" 10476 " ext_array[i] = %lld;" 10477 " if (i > 8) {" 10478 " res = ext_array[i];" 10479 " }" 10480 "}" 10481 "res;"; 10482 i::OS::SNPrintF(test_buf, 10483 boundary_program, 10484 low); 10485 result = CompileRun(test_buf.start()); 10486 CHECK_EQ(low, result->IntegerValue()); 10487 10488 i::OS::SNPrintF(test_buf, 10489 boundary_program, 10490 high); 10491 result = CompileRun(test_buf.start()); 10492 CHECK_EQ(high, result->IntegerValue()); 10493 10494 // Check misprediction of type in IC. 10495 result = CompileRun("var tmp_array = ext_array;" 10496 "var sum = 0;" 10497 "for (var i = 0; i < 8; i++) {" 10498 " tmp_array[i] = i;" 10499 " sum += tmp_array[i];" 10500 " if (i == 4) {" 10501 " tmp_array = {};" 10502 " }" 10503 "}" 10504 "sum;"); 10505 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 10506 CHECK_EQ(28, result->Int32Value()); 10507 10508 // Make sure out-of-range loads do not throw. 10509 i::OS::SNPrintF(test_buf, 10510 "var caught_exception = false;" 10511 "try {" 10512 " ext_array[%d];" 10513 "} catch (e) {" 10514 " caught_exception = true;" 10515 "}" 10516 "caught_exception;", 10517 kElementCount); 10518 result = CompileRun(test_buf.start()); 10519 CHECK_EQ(false, result->BooleanValue()); 10520 10521 // Make sure out-of-range stores do not throw. 10522 i::OS::SNPrintF(test_buf, 10523 "var caught_exception = false;" 10524 "try {" 10525 " ext_array[%d] = 1;" 10526 "} catch (e) {" 10527 " caught_exception = true;" 10528 "}" 10529 "caught_exception;", 10530 kElementCount); 10531 result = CompileRun(test_buf.start()); 10532 CHECK_EQ(false, result->BooleanValue()); 10533 10534 // Check other boundary conditions, values and operations. 10535 result = CompileRun("for (var i = 0; i < 8; i++) {" 10536 " ext_array[7] = undefined;" 10537 "}" 10538 "ext_array[7];"); 10539 CHECK_EQ(0, result->Int32Value()); 10540 CHECK_EQ( 10541 0, static_cast<int>(jsobj->GetElement(7)->ToObjectChecked()->Number())); 10542 10543 result = CompileRun("for (var i = 0; i < 8; i++) {" 10544 " ext_array[6] = '2.3';" 10545 "}" 10546 "ext_array[6];"); 10547 CHECK_EQ(2, result->Int32Value()); 10548 CHECK_EQ( 10549 2, static_cast<int>(jsobj->GetElement(6)->ToObjectChecked()->Number())); 10550 10551 if (array_type != v8::kExternalFloatArray) { 10552 // Though the specification doesn't state it, be explicit about 10553 // converting NaNs and +/-Infinity to zero. 10554 result = CompileRun("for (var i = 0; i < 8; i++) {" 10555 " ext_array[i] = 5;" 10556 "}" 10557 "for (var i = 0; i < 8; i++) {" 10558 " ext_array[i] = NaN;" 10559 "}" 10560 "ext_array[5];"); 10561 CHECK_EQ(0, result->Int32Value()); 10562 CHECK_EQ(0, 10563 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 10564 10565 result = CompileRun("for (var i = 0; i < 8; i++) {" 10566 " ext_array[i] = 5;" 10567 "}" 10568 "for (var i = 0; i < 8; i++) {" 10569 " ext_array[i] = Infinity;" 10570 "}" 10571 "ext_array[5];"); 10572 CHECK_EQ(0, result->Int32Value()); 10573 CHECK_EQ(0, 10574 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 10575 10576 result = CompileRun("for (var i = 0; i < 8; i++) {" 10577 " ext_array[i] = 5;" 10578 "}" 10579 "for (var i = 0; i < 8; i++) {" 10580 " ext_array[i] = -Infinity;" 10581 "}" 10582 "ext_array[5];"); 10583 CHECK_EQ(0, result->Int32Value()); 10584 CHECK_EQ(0, 10585 i::Smi::cast(jsobj->GetElement(5)->ToObjectChecked())->value()); 10586 } 10587 10588 result = CompileRun("ext_array[3] = 33;" 10589 "delete ext_array[3];" 10590 "ext_array[3];"); 10591 CHECK_EQ(33, result->Int32Value()); 10592 10593 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;" 10594 "ext_array[2] = 12; ext_array[3] = 13;" 10595 "ext_array.__defineGetter__('2'," 10596 "function() { return 120; });" 10597 "ext_array[2];"); 10598 CHECK_EQ(12, result->Int32Value()); 10599 10600 result = CompileRun("var js_array = new Array(40);" 10601 "js_array[0] = 77;" 10602 "js_array;"); 10603 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 10604 10605 result = CompileRun("ext_array[1] = 23;" 10606 "ext_array.__proto__ = [];" 10607 "js_array.__proto__ = ext_array;" 10608 "js_array.concat(ext_array);"); 10609 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 10610 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 10611 10612 result = CompileRun("ext_array[1] = 23;"); 10613 CHECK_EQ(23, result->Int32Value()); 10614 10615 // Test more complex manipulations which cause eax to contain values 10616 // that won't be completely overwritten by loads from the arrays. 10617 // This catches bugs in the instructions used for the KeyedLoadIC 10618 // for byte and word types. 10619 { 10620 const int kXSize = 300; 10621 const int kYSize = 300; 10622 const int kLargeElementCount = kXSize * kYSize * 4; 10623 ElementType* large_array_data = 10624 static_cast<ElementType*>(malloc(kLargeElementCount * element_size)); 10625 i::Handle<ExternalArrayClass> large_array = 10626 i::Handle<ExternalArrayClass>::cast( 10627 i::Factory::NewExternalArray(kLargeElementCount, 10628 array_type, 10629 array_data)); 10630 v8::Handle<v8::Object> large_obj = v8::Object::New(); 10631 // Set the elements to be the external array. 10632 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data, 10633 array_type, 10634 kLargeElementCount); 10635 context->Global()->Set(v8_str("large_array"), large_obj); 10636 // Initialize contents of a few rows. 10637 for (int x = 0; x < 300; x++) { 10638 int row = 0; 10639 int offset = row * 300 * 4; 10640 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 10641 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 10642 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 10643 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 10644 row = 150; 10645 offset = row * 300 * 4; 10646 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 10647 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 10648 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 10649 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 10650 row = 298; 10651 offset = row * 300 * 4; 10652 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 10653 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 10654 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 10655 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 10656 } 10657 // The goal of the code below is to make "offset" large enough 10658 // that the computation of the index (which goes into eax) has 10659 // high bits set which will not be overwritten by a byte or short 10660 // load. 10661 result = CompileRun("var failed = false;" 10662 "var offset = 0;" 10663 "for (var i = 0; i < 300; i++) {" 10664 " if (large_array[4 * i] != 127 ||" 10665 " large_array[4 * i + 1] != 0 ||" 10666 " large_array[4 * i + 2] != 0 ||" 10667 " large_array[4 * i + 3] != 127) {" 10668 " failed = true;" 10669 " }" 10670 "}" 10671 "offset = 150 * 300 * 4;" 10672 "for (var i = 0; i < 300; i++) {" 10673 " if (large_array[offset + 4 * i] != 127 ||" 10674 " large_array[offset + 4 * i + 1] != 0 ||" 10675 " large_array[offset + 4 * i + 2] != 0 ||" 10676 " large_array[offset + 4 * i + 3] != 127) {" 10677 " failed = true;" 10678 " }" 10679 "}" 10680 "offset = 298 * 300 * 4;" 10681 "for (var i = 0; i < 300; i++) {" 10682 " if (large_array[offset + 4 * i] != 127 ||" 10683 " large_array[offset + 4 * i + 1] != 0 ||" 10684 " large_array[offset + 4 * i + 2] != 0 ||" 10685 " large_array[offset + 4 * i + 3] != 127) {" 10686 " failed = true;" 10687 " }" 10688 "}" 10689 "!failed;"); 10690 CHECK_EQ(true, result->BooleanValue()); 10691 free(large_array_data); 10692 } 10693 10694 free(array_data); 10695} 10696 10697 10698THREADED_TEST(ExternalByteArray) { 10699 ExternalArrayTestHelper<i::ExternalByteArray, int8_t>( 10700 v8::kExternalByteArray, 10701 -128, 10702 127); 10703} 10704 10705 10706THREADED_TEST(ExternalUnsignedByteArray) { 10707 ExternalArrayTestHelper<i::ExternalUnsignedByteArray, uint8_t>( 10708 v8::kExternalUnsignedByteArray, 10709 0, 10710 255); 10711} 10712 10713 10714THREADED_TEST(ExternalShortArray) { 10715 ExternalArrayTestHelper<i::ExternalShortArray, int16_t>( 10716 v8::kExternalShortArray, 10717 -32768, 10718 32767); 10719} 10720 10721 10722THREADED_TEST(ExternalUnsignedShortArray) { 10723 ExternalArrayTestHelper<i::ExternalUnsignedShortArray, uint16_t>( 10724 v8::kExternalUnsignedShortArray, 10725 0, 10726 65535); 10727} 10728 10729 10730THREADED_TEST(ExternalIntArray) { 10731 ExternalArrayTestHelper<i::ExternalIntArray, int32_t>( 10732 v8::kExternalIntArray, 10733 INT_MIN, // -2147483648 10734 INT_MAX); // 2147483647 10735} 10736 10737 10738THREADED_TEST(ExternalUnsignedIntArray) { 10739 ExternalArrayTestHelper<i::ExternalUnsignedIntArray, uint32_t>( 10740 v8::kExternalUnsignedIntArray, 10741 0, 10742 UINT_MAX); // 4294967295 10743} 10744 10745 10746THREADED_TEST(ExternalFloatArray) { 10747 ExternalArrayTestHelper<i::ExternalFloatArray, float>( 10748 v8::kExternalFloatArray, 10749 -500, 10750 500); 10751} 10752 10753 10754THREADED_TEST(ExternalArrays) { 10755 TestExternalByteArray(); 10756 TestExternalUnsignedByteArray(); 10757 TestExternalShortArray(); 10758 TestExternalUnsignedShortArray(); 10759 TestExternalIntArray(); 10760 TestExternalUnsignedIntArray(); 10761 TestExternalFloatArray(); 10762} 10763 10764 10765void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) { 10766 v8::HandleScope scope; 10767 LocalContext context; 10768 for (int size = 0; size < 100; size += 10) { 10769 int element_size = ExternalArrayElementSize(array_type); 10770 void* external_data = malloc(size * element_size); 10771 v8::Handle<v8::Object> obj = v8::Object::New(); 10772 obj->SetIndexedPropertiesToExternalArrayData( 10773 external_data, array_type, size); 10774 CHECK(obj->HasIndexedPropertiesInExternalArrayData()); 10775 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData()); 10776 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType()); 10777 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength()); 10778 free(external_data); 10779 } 10780} 10781 10782 10783THREADED_TEST(ExternalArrayInfo) { 10784 ExternalArrayInfoTestHelper(v8::kExternalByteArray); 10785 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray); 10786 ExternalArrayInfoTestHelper(v8::kExternalShortArray); 10787 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray); 10788 ExternalArrayInfoTestHelper(v8::kExternalIntArray); 10789 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray); 10790 ExternalArrayInfoTestHelper(v8::kExternalFloatArray); 10791} 10792 10793 10794THREADED_TEST(ScriptContextDependence) { 10795 v8::HandleScope scope; 10796 LocalContext c1; 10797 const char *source = "foo"; 10798 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source)); 10799 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source)); 10800 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100)); 10801 CHECK_EQ(dep->Run()->Int32Value(), 100); 10802 CHECK_EQ(indep->Run()->Int32Value(), 100); 10803 LocalContext c2; 10804 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101)); 10805 CHECK_EQ(dep->Run()->Int32Value(), 100); 10806 CHECK_EQ(indep->Run()->Int32Value(), 101); 10807} 10808 10809 10810THREADED_TEST(StackTrace) { 10811 v8::HandleScope scope; 10812 LocalContext context; 10813 v8::TryCatch try_catch; 10814 const char *source = "function foo() { FAIL.FAIL; }; foo();"; 10815 v8::Handle<v8::String> src = v8::String::New(source); 10816 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test"); 10817 v8::Script::New(src, origin)->Run(); 10818 CHECK(try_catch.HasCaught()); 10819 v8::String::Utf8Value stack(try_catch.StackTrace()); 10820 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL); 10821} 10822 10823 10824// Checks that a StackFrame has certain expected values. 10825void checkStackFrame(const char* expected_script_name, 10826 const char* expected_func_name, int expected_line_number, 10827 int expected_column, bool is_eval, bool is_constructor, 10828 v8::Handle<v8::StackFrame> frame) { 10829 v8::HandleScope scope; 10830 v8::String::Utf8Value func_name(frame->GetFunctionName()); 10831 v8::String::Utf8Value script_name(frame->GetScriptName()); 10832 if (*script_name == NULL) { 10833 // The situation where there is no associated script, like for evals. 10834 CHECK(expected_script_name == NULL); 10835 } else { 10836 CHECK(strstr(*script_name, expected_script_name) != NULL); 10837 } 10838 CHECK(strstr(*func_name, expected_func_name) != NULL); 10839 CHECK_EQ(expected_line_number, frame->GetLineNumber()); 10840 CHECK_EQ(expected_column, frame->GetColumn()); 10841 CHECK_EQ(is_eval, frame->IsEval()); 10842 CHECK_EQ(is_constructor, frame->IsConstructor()); 10843} 10844 10845 10846v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) { 10847 v8::HandleScope scope; 10848 const char* origin = "capture-stack-trace-test"; 10849 const int kOverviewTest = 1; 10850 const int kDetailedTest = 2; 10851 10852 ASSERT(args.Length() == 1); 10853 10854 int testGroup = args[0]->Int32Value(); 10855 if (testGroup == kOverviewTest) { 10856 v8::Handle<v8::StackTrace> stackTrace = 10857 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview); 10858 CHECK_EQ(4, stackTrace->GetFrameCount()); 10859 checkStackFrame(origin, "bar", 2, 10, false, false, 10860 stackTrace->GetFrame(0)); 10861 checkStackFrame(origin, "foo", 6, 3, false, false, 10862 stackTrace->GetFrame(1)); 10863 checkStackFrame(NULL, "", 1, 1, false, false, 10864 stackTrace->GetFrame(2)); 10865 // The last frame is an anonymous function that has the initial call. 10866 checkStackFrame(origin, "", 8, 7, false, false, 10867 stackTrace->GetFrame(3)); 10868 10869 CHECK(stackTrace->AsArray()->IsArray()); 10870 } else if (testGroup == kDetailedTest) { 10871 v8::Handle<v8::StackTrace> stackTrace = 10872 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); 10873 CHECK_EQ(4, stackTrace->GetFrameCount()); 10874 checkStackFrame(origin, "bat", 4, 22, false, false, 10875 stackTrace->GetFrame(0)); 10876 checkStackFrame(origin, "baz", 8, 3, false, true, 10877 stackTrace->GetFrame(1)); 10878#ifdef ENABLE_DEBUGGER_SUPPORT 10879 bool is_eval = true; 10880#else // ENABLE_DEBUGGER_SUPPORT 10881 bool is_eval = false; 10882#endif // ENABLE_DEBUGGER_SUPPORT 10883 10884 checkStackFrame(NULL, "", 1, 1, is_eval, false, 10885 stackTrace->GetFrame(2)); 10886 // The last frame is an anonymous function that has the initial call to foo. 10887 checkStackFrame(origin, "", 10, 1, false, false, 10888 stackTrace->GetFrame(3)); 10889 10890 CHECK(stackTrace->AsArray()->IsArray()); 10891 } 10892 return v8::Undefined(); 10893} 10894 10895 10896// Tests the C++ StackTrace API. 10897// TODO(3074796): Reenable this as a THREADED_TEST once it passes. 10898// THREADED_TEST(CaptureStackTrace) { 10899TEST(CaptureStackTrace) { 10900 v8::HandleScope scope; 10901 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test"); 10902 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10903 templ->Set(v8_str("AnalyzeStackInNativeCode"), 10904 v8::FunctionTemplate::New(AnalyzeStackInNativeCode)); 10905 LocalContext context(0, templ); 10906 10907 // Test getting OVERVIEW information. Should ignore information that is not 10908 // script name, function name, line number, and column offset. 10909 const char *overview_source = 10910 "function bar() {\n" 10911 " var y; AnalyzeStackInNativeCode(1);\n" 10912 "}\n" 10913 "function foo() {\n" 10914 "\n" 10915 " bar();\n" 10916 "}\n" 10917 "var x;eval('new foo();');"; 10918 v8::Handle<v8::String> overview_src = v8::String::New(overview_source); 10919 v8::Handle<Value> overview_result = 10920 v8::Script::New(overview_src, origin)->Run(); 10921 ASSERT(!overview_result.IsEmpty()); 10922 ASSERT(overview_result->IsObject()); 10923 10924 // Test getting DETAILED information. 10925 const char *detailed_source = 10926 "function bat() {AnalyzeStackInNativeCode(2);\n" 10927 "}\n" 10928 "\n" 10929 "function baz() {\n" 10930 " bat();\n" 10931 "}\n" 10932 "eval('new baz();');"; 10933 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source); 10934 // Make the script using a non-zero line and column offset. 10935 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3); 10936 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5); 10937 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset); 10938 v8::Handle<v8::Script> detailed_script( 10939 v8::Script::New(detailed_src, &detailed_origin)); 10940 v8::Handle<Value> detailed_result = detailed_script->Run(); 10941 ASSERT(!detailed_result.IsEmpty()); 10942 ASSERT(detailed_result->IsObject()); 10943} 10944 10945 10946static void StackTraceForUncaughtExceptionListener( 10947 v8::Handle<v8::Message> message, 10948 v8::Handle<Value>) { 10949 v8::Handle<v8::StackTrace> stack_trace = message->GetStackTrace(); 10950 CHECK_EQ(2, stack_trace->GetFrameCount()); 10951 checkStackFrame("origin", "foo", 2, 3, false, false, 10952 stack_trace->GetFrame(0)); 10953 checkStackFrame("origin", "bar", 5, 3, false, false, 10954 stack_trace->GetFrame(1)); 10955} 10956 10957TEST(CaptureStackTraceForUncaughtException) { 10958 report_count = 0; 10959 v8::HandleScope scope; 10960 LocalContext env; 10961 v8::V8::AddMessageListener(StackTraceForUncaughtExceptionListener); 10962 v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); 10963 10964 Script::Compile(v8_str("function foo() {\n" 10965 " throw 1;\n" 10966 "};\n" 10967 "function bar() {\n" 10968 " foo();\n" 10969 "};"), 10970 v8_str("origin"))->Run(); 10971 v8::Local<v8::Object> global = env->Global(); 10972 Local<Value> trouble = global->Get(v8_str("bar")); 10973 CHECK(trouble->IsFunction()); 10974 Function::Cast(*trouble)->Call(global, 0, NULL); 10975 v8::V8::SetCaptureStackTraceForUncaughtExceptions(false); 10976 v8::V8::RemoveMessageListeners(StackTraceForUncaughtExceptionListener); 10977} 10978 10979 10980v8::Handle<Value> AnalyzeStackOfEvalWithSourceURL(const v8::Arguments& args) { 10981 v8::HandleScope scope; 10982 v8::Handle<v8::StackTrace> stackTrace = 10983 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); 10984 CHECK_EQ(5, stackTrace->GetFrameCount()); 10985 v8::Handle<v8::String> url = v8_str("eval_url"); 10986 for (int i = 0; i < 3; i++) { 10987 v8::Handle<v8::String> name = 10988 stackTrace->GetFrame(i)->GetScriptNameOrSourceURL(); 10989 CHECK(!name.IsEmpty()); 10990 CHECK_EQ(url, name); 10991 } 10992 return v8::Undefined(); 10993} 10994 10995 10996TEST(SourceURLInStackTrace) { 10997 v8::HandleScope scope; 10998 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10999 templ->Set(v8_str("AnalyzeStackOfEvalWithSourceURL"), 11000 v8::FunctionTemplate::New(AnalyzeStackOfEvalWithSourceURL)); 11001 LocalContext context(0, templ); 11002 11003 const char *source = 11004 "function outer() {\n" 11005 "function bar() {\n" 11006 " AnalyzeStackOfEvalWithSourceURL();\n" 11007 "}\n" 11008 "function foo() {\n" 11009 "\n" 11010 " bar();\n" 11011 "}\n" 11012 "foo();\n" 11013 "}\n" 11014 "eval('(' + outer +')()//@ sourceURL=eval_url');"; 11015 CHECK(CompileRun(source)->IsUndefined()); 11016} 11017 11018 11019// Test that idle notification can be handled and eventually returns true. 11020THREADED_TEST(IdleNotification) { 11021 bool rv = false; 11022 for (int i = 0; i < 100; i++) { 11023 rv = v8::V8::IdleNotification(); 11024 if (rv) 11025 break; 11026 } 11027 CHECK(rv == true); 11028} 11029 11030 11031static uint32_t* stack_limit; 11032 11033static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) { 11034 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::real_climit()); 11035 return v8::Undefined(); 11036} 11037 11038 11039// Uses the address of a local variable to determine the stack top now. 11040// Given a size, returns an address that is that far from the current 11041// top of stack. 11042static uint32_t* ComputeStackLimit(uint32_t size) { 11043 uint32_t* answer = &size - (size / sizeof(size)); 11044 // If the size is very large and the stack is very near the bottom of 11045 // memory then the calculation above may wrap around and give an address 11046 // that is above the (downwards-growing) stack. In that case we return 11047 // a very low address. 11048 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size)); 11049 return answer; 11050} 11051 11052 11053TEST(SetResourceConstraints) { 11054 static const int K = 1024; 11055 uint32_t* set_limit = ComputeStackLimit(128 * K); 11056 11057 // Set stack limit. 11058 v8::ResourceConstraints constraints; 11059 constraints.set_stack_limit(set_limit); 11060 CHECK(v8::SetResourceConstraints(&constraints)); 11061 11062 // Execute a script. 11063 v8::HandleScope scope; 11064 LocalContext env; 11065 Local<v8::FunctionTemplate> fun_templ = 11066 v8::FunctionTemplate::New(GetStackLimitCallback); 11067 Local<Function> fun = fun_templ->GetFunction(); 11068 env->Global()->Set(v8_str("get_stack_limit"), fun); 11069 CompileRun("get_stack_limit();"); 11070 11071 CHECK(stack_limit == set_limit); 11072} 11073 11074 11075TEST(SetResourceConstraintsInThread) { 11076 uint32_t* set_limit; 11077 { 11078 v8::Locker locker; 11079 static const int K = 1024; 11080 set_limit = ComputeStackLimit(128 * K); 11081 11082 // Set stack limit. 11083 v8::ResourceConstraints constraints; 11084 constraints.set_stack_limit(set_limit); 11085 CHECK(v8::SetResourceConstraints(&constraints)); 11086 11087 // Execute a script. 11088 v8::HandleScope scope; 11089 LocalContext env; 11090 Local<v8::FunctionTemplate> fun_templ = 11091 v8::FunctionTemplate::New(GetStackLimitCallback); 11092 Local<Function> fun = fun_templ->GetFunction(); 11093 env->Global()->Set(v8_str("get_stack_limit"), fun); 11094 CompileRun("get_stack_limit();"); 11095 11096 CHECK(stack_limit == set_limit); 11097 } 11098 { 11099 v8::Locker locker; 11100 CHECK(stack_limit == set_limit); 11101 } 11102} 11103 11104 11105THREADED_TEST(GetHeapStatistics) { 11106 v8::HandleScope scope; 11107 LocalContext c1; 11108 v8::HeapStatistics heap_statistics; 11109 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0); 11110 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0); 11111 v8::V8::GetHeapStatistics(&heap_statistics); 11112 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0); 11113 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0); 11114} 11115 11116 11117static double DoubleFromBits(uint64_t value) { 11118 double target; 11119#ifdef BIG_ENDIAN_FLOATING_POINT 11120 const int kIntSize = 4; 11121 // Somebody swapped the lower and higher half of doubles. 11122 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); 11123 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); 11124#else 11125 memcpy(&target, &value, sizeof(target)); 11126#endif 11127 return target; 11128} 11129 11130 11131static uint64_t DoubleToBits(double value) { 11132 uint64_t target; 11133#ifdef BIG_ENDIAN_FLOATING_POINT 11134 const int kIntSize = 4; 11135 // Somebody swapped the lower and higher half of doubles. 11136 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); 11137 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); 11138#else 11139 memcpy(&target, &value, sizeof(target)); 11140#endif 11141 return target; 11142} 11143 11144 11145static double DoubleToDateTime(double input) { 11146 double date_limit = 864e13; 11147 if (IsNaN(input) || input < -date_limit || input > date_limit) { 11148 return i::OS::nan_value(); 11149 } 11150 return (input < 0) ? -(floor(-input)) : floor(input); 11151} 11152 11153// We don't have a consistent way to write 64-bit constants syntactically, so we 11154// split them into two 32-bit constants and combine them programmatically. 11155static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) { 11156 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits); 11157} 11158 11159 11160THREADED_TEST(QuietSignalingNaNs) { 11161 v8::HandleScope scope; 11162 LocalContext context; 11163 v8::TryCatch try_catch; 11164 11165 // Special double values. 11166 double snan = DoubleFromBits(0x7ff00000, 0x00000001); 11167 double qnan = DoubleFromBits(0x7ff80000, 0x00000000); 11168 double infinity = DoubleFromBits(0x7ff00000, 0x00000000); 11169 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu); 11170 double min_normal = DoubleFromBits(0x00100000, 0x00000000); 11171 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu); 11172 double min_denormal = DoubleFromBits(0x00000000, 0x00000001); 11173 11174 // Date values are capped at +/-100000000 days (times 864e5 ms per day) 11175 // on either side of the epoch. 11176 double date_limit = 864e13; 11177 11178 double test_values[] = { 11179 snan, 11180 qnan, 11181 infinity, 11182 max_normal, 11183 date_limit + 1, 11184 date_limit, 11185 min_normal, 11186 max_denormal, 11187 min_denormal, 11188 0, 11189 -0, 11190 -min_denormal, 11191 -max_denormal, 11192 -min_normal, 11193 -date_limit, 11194 -date_limit - 1, 11195 -max_normal, 11196 -infinity, 11197 -qnan, 11198 -snan 11199 }; 11200 int num_test_values = 20; 11201 11202 for (int i = 0; i < num_test_values; i++) { 11203 double test_value = test_values[i]; 11204 11205 // Check that Number::New preserves non-NaNs and quiets SNaNs. 11206 v8::Handle<v8::Value> number = v8::Number::New(test_value); 11207 double stored_number = number->NumberValue(); 11208 if (!IsNaN(test_value)) { 11209 CHECK_EQ(test_value, stored_number); 11210 } else { 11211 uint64_t stored_bits = DoubleToBits(stored_number); 11212 // Check if quiet nan (bits 51..62 all set). 11213 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 11214 } 11215 11216 // Check that Date::New preserves non-NaNs in the date range and 11217 // quiets SNaNs. 11218 v8::Handle<v8::Value> date = v8::Date::New(test_value); 11219 double expected_stored_date = DoubleToDateTime(test_value); 11220 double stored_date = date->NumberValue(); 11221 if (!IsNaN(expected_stored_date)) { 11222 CHECK_EQ(expected_stored_date, stored_date); 11223 } else { 11224 uint64_t stored_bits = DoubleToBits(stored_date); 11225 // Check if quiet nan (bits 51..62 all set). 11226 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 11227 } 11228 } 11229} 11230 11231 11232static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) { 11233 v8::HandleScope scope; 11234 v8::TryCatch tc; 11235 v8::Handle<v8::String> str = args[0]->ToString(); 11236 if (tc.HasCaught()) 11237 return tc.ReThrow(); 11238 return v8::Undefined(); 11239} 11240 11241 11242// Test that an exception can be propagated down through a spaghetti 11243// stack using ReThrow. 11244THREADED_TEST(SpaghettiStackReThrow) { 11245 v8::HandleScope scope; 11246 LocalContext context; 11247 context->Global()->Set( 11248 v8::String::New("s"), 11249 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction()); 11250 v8::TryCatch try_catch; 11251 CompileRun( 11252 "var i = 0;" 11253 "var o = {" 11254 " toString: function () {" 11255 " if (i == 10) {" 11256 " throw 'Hey!';" 11257 " } else {" 11258 " i++;" 11259 " return s(o);" 11260 " }" 11261 " }" 11262 "};" 11263 "s(o);"); 11264 CHECK(try_catch.HasCaught()); 11265 v8::String::Utf8Value value(try_catch.Exception()); 11266 CHECK_EQ(0, strcmp(*value, "Hey!")); 11267} 11268 11269 11270TEST(Regress528) { 11271 v8::V8::Initialize(); 11272 11273 v8::HandleScope scope; 11274 v8::Persistent<Context> context; 11275 v8::Persistent<Context> other_context; 11276 int gc_count; 11277 11278 // Create a context used to keep the code from aging in the compilation 11279 // cache. 11280 other_context = Context::New(); 11281 11282 // Context-dependent context data creates reference from the compilation 11283 // cache to the global object. 11284 const char* source_simple = "1"; 11285 context = Context::New(); 11286 { 11287 v8::HandleScope scope; 11288 11289 context->Enter(); 11290 Local<v8::String> obj = v8::String::New(""); 11291 context->SetData(obj); 11292 CompileRun(source_simple); 11293 context->Exit(); 11294 } 11295 context.Dispose(); 11296 for (gc_count = 1; gc_count < 10; gc_count++) { 11297 other_context->Enter(); 11298 CompileRun(source_simple); 11299 other_context->Exit(); 11300 i::Heap::CollectAllGarbage(false); 11301 if (GetGlobalObjectsCount() == 1) break; 11302 } 11303 CHECK_GE(2, gc_count); 11304 CHECK_EQ(1, GetGlobalObjectsCount()); 11305 11306 // Eval in a function creates reference from the compilation cache to the 11307 // global object. 11308 const char* source_eval = "function f(){eval('1')}; f()"; 11309 context = Context::New(); 11310 { 11311 v8::HandleScope scope; 11312 11313 context->Enter(); 11314 CompileRun(source_eval); 11315 context->Exit(); 11316 } 11317 context.Dispose(); 11318 for (gc_count = 1; gc_count < 10; gc_count++) { 11319 other_context->Enter(); 11320 CompileRun(source_eval); 11321 other_context->Exit(); 11322 i::Heap::CollectAllGarbage(false); 11323 if (GetGlobalObjectsCount() == 1) break; 11324 } 11325 CHECK_GE(2, gc_count); 11326 CHECK_EQ(1, GetGlobalObjectsCount()); 11327 11328 // Looking up the line number for an exception creates reference from the 11329 // compilation cache to the global object. 11330 const char* source_exception = "function f(){throw 1;} f()"; 11331 context = Context::New(); 11332 { 11333 v8::HandleScope scope; 11334 11335 context->Enter(); 11336 v8::TryCatch try_catch; 11337 CompileRun(source_exception); 11338 CHECK(try_catch.HasCaught()); 11339 v8::Handle<v8::Message> message = try_catch.Message(); 11340 CHECK(!message.IsEmpty()); 11341 CHECK_EQ(1, message->GetLineNumber()); 11342 context->Exit(); 11343 } 11344 context.Dispose(); 11345 for (gc_count = 1; gc_count < 10; gc_count++) { 11346 other_context->Enter(); 11347 CompileRun(source_exception); 11348 other_context->Exit(); 11349 i::Heap::CollectAllGarbage(false); 11350 if (GetGlobalObjectsCount() == 1) break; 11351 } 11352 CHECK_GE(2, gc_count); 11353 CHECK_EQ(1, GetGlobalObjectsCount()); 11354 11355 other_context.Dispose(); 11356} 11357 11358 11359THREADED_TEST(ScriptOrigin) { 11360 v8::HandleScope scope; 11361 LocalContext env; 11362 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 11363 v8::Handle<v8::String> script = v8::String::New( 11364 "function f() {}\n\nfunction g() {}"); 11365 v8::Script::Compile(script, &origin)->Run(); 11366 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 11367 env->Global()->Get(v8::String::New("f"))); 11368 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 11369 env->Global()->Get(v8::String::New("g"))); 11370 11371 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin(); 11372 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName())); 11373 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value()); 11374 11375 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin(); 11376 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName())); 11377 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value()); 11378} 11379 11380 11381THREADED_TEST(ScriptLineNumber) { 11382 v8::HandleScope scope; 11383 LocalContext env; 11384 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 11385 v8::Handle<v8::String> script = v8::String::New( 11386 "function f() {}\n\nfunction g() {}"); 11387 v8::Script::Compile(script, &origin)->Run(); 11388 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 11389 env->Global()->Get(v8::String::New("f"))); 11390 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 11391 env->Global()->Get(v8::String::New("g"))); 11392 CHECK_EQ(0, f->GetScriptLineNumber()); 11393 CHECK_EQ(2, g->GetScriptLineNumber()); 11394} 11395 11396 11397static v8::Handle<Value> GetterWhichReturns42(Local<String> name, 11398 const AccessorInfo& info) { 11399 return v8_num(42); 11400} 11401 11402 11403static void SetterWhichSetsYOnThisTo23(Local<String> name, 11404 Local<Value> value, 11405 const AccessorInfo& info) { 11406 info.This()->Set(v8_str("y"), v8_num(23)); 11407} 11408 11409 11410TEST(SetterOnConstructorPrototype) { 11411 v8::HandleScope scope; 11412 Local<ObjectTemplate> templ = ObjectTemplate::New(); 11413 templ->SetAccessor(v8_str("x"), 11414 GetterWhichReturns42, 11415 SetterWhichSetsYOnThisTo23); 11416 LocalContext context; 11417 context->Global()->Set(v8_str("P"), templ->NewInstance()); 11418 CompileRun("function C1() {" 11419 " this.x = 23;" 11420 "};" 11421 "C1.prototype = P;" 11422 "function C2() {" 11423 " this.x = 23" 11424 "};" 11425 "C2.prototype = { };" 11426 "C2.prototype.__proto__ = P;"); 11427 11428 v8::Local<v8::Script> script; 11429 script = v8::Script::Compile(v8_str("new C1();")); 11430 for (int i = 0; i < 10; i++) { 11431 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 11432 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); 11433 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); 11434 } 11435 11436 script = v8::Script::Compile(v8_str("new C2();")); 11437 for (int i = 0; i < 10; i++) { 11438 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 11439 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value()); 11440 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value()); 11441 } 11442} 11443 11444 11445static v8::Handle<Value> NamedPropertyGetterWhichReturns42( 11446 Local<String> name, const AccessorInfo& info) { 11447 return v8_num(42); 11448} 11449 11450 11451static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23( 11452 Local<String> name, Local<Value> value, const AccessorInfo& info) { 11453 if (name->Equals(v8_str("x"))) { 11454 info.This()->Set(v8_str("y"), v8_num(23)); 11455 } 11456 return v8::Handle<Value>(); 11457} 11458 11459 11460THREADED_TEST(InterceptorOnConstructorPrototype) { 11461 v8::HandleScope scope; 11462 Local<ObjectTemplate> templ = ObjectTemplate::New(); 11463 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42, 11464 NamedPropertySetterWhichSetsYOnThisTo23); 11465 LocalContext context; 11466 context->Global()->Set(v8_str("P"), templ->NewInstance()); 11467 CompileRun("function C1() {" 11468 " this.x = 23;" 11469 "};" 11470 "C1.prototype = P;" 11471 "function C2() {" 11472 " this.x = 23" 11473 "};" 11474 "C2.prototype = { };" 11475 "C2.prototype.__proto__ = P;"); 11476 11477 v8::Local<v8::Script> script; 11478 script = v8::Script::Compile(v8_str("new C1();")); 11479 for (int i = 0; i < 10; i++) { 11480 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 11481 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); 11482 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); 11483 } 11484 11485 script = v8::Script::Compile(v8_str("new C2();")); 11486 for (int i = 0; i < 10; i++) { 11487 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 11488 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value()); 11489 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value()); 11490 } 11491} 11492 11493 11494TEST(Bug618) { 11495 const char* source = "function C1() {" 11496 " this.x = 23;" 11497 "};" 11498 "C1.prototype = P;"; 11499 11500 v8::HandleScope scope; 11501 LocalContext context; 11502 v8::Local<v8::Script> script; 11503 11504 // Use a simple object as prototype. 11505 v8::Local<v8::Object> prototype = v8::Object::New(); 11506 prototype->Set(v8_str("y"), v8_num(42)); 11507 context->Global()->Set(v8_str("P"), prototype); 11508 11509 // This compile will add the code to the compilation cache. 11510 CompileRun(source); 11511 11512 script = v8::Script::Compile(v8_str("new C1();")); 11513 // Allow enough iterations for the inobject slack tracking logic 11514 // to finalize instance size and install the fast construct stub. 11515 for (int i = 0; i < 256; i++) { 11516 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 11517 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); 11518 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); 11519 } 11520 11521 // Use an API object with accessors as prototype. 11522 Local<ObjectTemplate> templ = ObjectTemplate::New(); 11523 templ->SetAccessor(v8_str("x"), 11524 GetterWhichReturns42, 11525 SetterWhichSetsYOnThisTo23); 11526 context->Global()->Set(v8_str("P"), templ->NewInstance()); 11527 11528 // This compile will get the code from the compilation cache. 11529 CompileRun(source); 11530 11531 script = v8::Script::Compile(v8_str("new C1();")); 11532 for (int i = 0; i < 10; i++) { 11533 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 11534 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); 11535 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); 11536 } 11537} 11538 11539int prologue_call_count = 0; 11540int epilogue_call_count = 0; 11541int prologue_call_count_second = 0; 11542int epilogue_call_count_second = 0; 11543 11544void PrologueCallback(v8::GCType, v8::GCCallbackFlags) { 11545 ++prologue_call_count; 11546} 11547 11548void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) { 11549 ++epilogue_call_count; 11550} 11551 11552void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { 11553 ++prologue_call_count_second; 11554} 11555 11556void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { 11557 ++epilogue_call_count_second; 11558} 11559 11560TEST(GCCallbacks) { 11561 LocalContext context; 11562 11563 v8::V8::AddGCPrologueCallback(PrologueCallback); 11564 v8::V8::AddGCEpilogueCallback(EpilogueCallback); 11565 CHECK_EQ(0, prologue_call_count); 11566 CHECK_EQ(0, epilogue_call_count); 11567 i::Heap::CollectAllGarbage(false); 11568 CHECK_EQ(1, prologue_call_count); 11569 CHECK_EQ(1, epilogue_call_count); 11570 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond); 11571 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond); 11572 i::Heap::CollectAllGarbage(false); 11573 CHECK_EQ(2, prologue_call_count); 11574 CHECK_EQ(2, epilogue_call_count); 11575 CHECK_EQ(1, prologue_call_count_second); 11576 CHECK_EQ(1, epilogue_call_count_second); 11577 v8::V8::RemoveGCPrologueCallback(PrologueCallback); 11578 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback); 11579 i::Heap::CollectAllGarbage(false); 11580 CHECK_EQ(2, prologue_call_count); 11581 CHECK_EQ(2, epilogue_call_count); 11582 CHECK_EQ(2, prologue_call_count_second); 11583 CHECK_EQ(2, epilogue_call_count_second); 11584 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond); 11585 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond); 11586 i::Heap::CollectAllGarbage(false); 11587 CHECK_EQ(2, prologue_call_count); 11588 CHECK_EQ(2, epilogue_call_count); 11589 CHECK_EQ(2, prologue_call_count_second); 11590 CHECK_EQ(2, epilogue_call_count_second); 11591} 11592 11593 11594THREADED_TEST(AddToJSFunctionResultCache) { 11595 i::FLAG_allow_natives_syntax = true; 11596 v8::HandleScope scope; 11597 11598 LocalContext context; 11599 11600 const char* code = 11601 "(function() {" 11602 " var key0 = 'a';" 11603 " var key1 = 'b';" 11604 " var r0 = %_GetFromCache(0, key0);" 11605 " var r1 = %_GetFromCache(0, key1);" 11606 " var r0_ = %_GetFromCache(0, key0);" 11607 " if (r0 !== r0_)" 11608 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;" 11609 " var r1_ = %_GetFromCache(0, key1);" 11610 " if (r1 !== r1_)" 11611 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;" 11612 " return 'PASSED';" 11613 "})()"; 11614 i::Heap::ClearJSFunctionResultCaches(); 11615 ExpectString(code, "PASSED"); 11616} 11617 11618 11619static const int k0CacheSize = 16; 11620 11621THREADED_TEST(FillJSFunctionResultCache) { 11622 i::FLAG_allow_natives_syntax = true; 11623 v8::HandleScope scope; 11624 11625 LocalContext context; 11626 11627 const char* code = 11628 "(function() {" 11629 " var k = 'a';" 11630 " var r = %_GetFromCache(0, k);" 11631 " for (var i = 0; i < 16; i++) {" 11632 " %_GetFromCache(0, 'a' + i);" 11633 " };" 11634 " if (r === %_GetFromCache(0, k))" 11635 " return 'FAILED: k0CacheSize is too small';" 11636 " return 'PASSED';" 11637 "})()"; 11638 i::Heap::ClearJSFunctionResultCaches(); 11639 ExpectString(code, "PASSED"); 11640} 11641 11642 11643THREADED_TEST(RoundRobinGetFromCache) { 11644 i::FLAG_allow_natives_syntax = true; 11645 v8::HandleScope scope; 11646 11647 LocalContext context; 11648 11649 const char* code = 11650 "(function() {" 11651 " var keys = [];" 11652 " for (var i = 0; i < 16; i++) keys.push(i);" 11653 " var values = [];" 11654 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" 11655 " for (var i = 0; i < 16; i++) {" 11656 " var v = %_GetFromCache(0, keys[i]);" 11657 " if (v !== values[i])" 11658 " return 'Wrong value for ' + " 11659 " keys[i] + ': ' + v + ' vs. ' + values[i];" 11660 " };" 11661 " return 'PASSED';" 11662 "})()"; 11663 i::Heap::ClearJSFunctionResultCaches(); 11664 ExpectString(code, "PASSED"); 11665} 11666 11667 11668THREADED_TEST(ReverseGetFromCache) { 11669 i::FLAG_allow_natives_syntax = true; 11670 v8::HandleScope scope; 11671 11672 LocalContext context; 11673 11674 const char* code = 11675 "(function() {" 11676 " var keys = [];" 11677 " for (var i = 0; i < 16; i++) keys.push(i);" 11678 " var values = [];" 11679 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" 11680 " for (var i = 15; i >= 16; i--) {" 11681 " var v = %_GetFromCache(0, keys[i]);" 11682 " if (v !== values[i])" 11683 " return 'Wrong value for ' + " 11684 " keys[i] + ': ' + v + ' vs. ' + values[i];" 11685 " };" 11686 " return 'PASSED';" 11687 "})()"; 11688 i::Heap::ClearJSFunctionResultCaches(); 11689 ExpectString(code, "PASSED"); 11690} 11691 11692 11693THREADED_TEST(TestEviction) { 11694 i::FLAG_allow_natives_syntax = true; 11695 v8::HandleScope scope; 11696 11697 LocalContext context; 11698 11699 const char* code = 11700 "(function() {" 11701 " for (var i = 0; i < 2*16; i++) {" 11702 " %_GetFromCache(0, 'a' + i);" 11703 " };" 11704 " return 'PASSED';" 11705 "})()"; 11706 i::Heap::ClearJSFunctionResultCaches(); 11707 ExpectString(code, "PASSED"); 11708} 11709 11710 11711THREADED_TEST(TwoByteStringInAsciiCons) { 11712 // See Chromium issue 47824. 11713 v8::HandleScope scope; 11714 11715 LocalContext context; 11716 const char* init_code = 11717 "var str1 = 'abelspendabel';" 11718 "var str2 = str1 + str1 + str1;" 11719 "str2;"; 11720 Local<Value> result = CompileRun(init_code); 11721 11722 CHECK(result->IsString()); 11723 i::Handle<i::String> string = v8::Utils::OpenHandle(String::Cast(*result)); 11724 int length = string->length(); 11725 CHECK(string->IsAsciiRepresentation()); 11726 11727 FlattenString(string); 11728 i::Handle<i::String> flat_string = FlattenGetString(string); 11729 11730 CHECK(string->IsAsciiRepresentation()); 11731 CHECK(flat_string->IsAsciiRepresentation()); 11732 11733 // Create external resource. 11734 uint16_t* uc16_buffer = new uint16_t[length + 1]; 11735 11736 i::String::WriteToFlat(*flat_string, uc16_buffer, 0, length); 11737 uc16_buffer[length] = 0; 11738 11739 TestResource resource(uc16_buffer); 11740 11741 flat_string->MakeExternal(&resource); 11742 11743 CHECK(flat_string->IsTwoByteRepresentation()); 11744 11745 // At this point, we should have a Cons string which is flat and ASCII, 11746 // with a first half that is a two-byte string (although it only contains 11747 // ASCII characters). This is a valid sequence of steps, and it can happen 11748 // in real pages. 11749 11750 CHECK(string->IsAsciiRepresentation()); 11751 i::ConsString* cons = i::ConsString::cast(*string); 11752 CHECK_EQ(0, cons->second()->length()); 11753 CHECK(cons->first()->IsTwoByteRepresentation()); 11754 11755 // Check that some string operations work. 11756 11757 // Atom RegExp. 11758 Local<Value> reresult = CompileRun("str2.match(/abel/g).length;"); 11759 CHECK_EQ(6, reresult->Int32Value()); 11760 11761 // Nonatom RegExp. 11762 reresult = CompileRun("str2.match(/abe./g).length;"); 11763 CHECK_EQ(6, reresult->Int32Value()); 11764 11765 reresult = CompileRun("str2.search(/bel/g);"); 11766 CHECK_EQ(1, reresult->Int32Value()); 11767 11768 reresult = CompileRun("str2.search(/be./g);"); 11769 CHECK_EQ(1, reresult->Int32Value()); 11770 11771 ExpectTrue("/bel/g.test(str2);"); 11772 11773 ExpectTrue("/be./g.test(str2);"); 11774 11775 reresult = CompileRun("/bel/g.exec(str2);"); 11776 CHECK(!reresult->IsNull()); 11777 11778 reresult = CompileRun("/be./g.exec(str2);"); 11779 CHECK(!reresult->IsNull()); 11780 11781 ExpectString("str2.substring(2, 10);", "elspenda"); 11782 11783 ExpectString("str2.substring(2, 20);", "elspendabelabelspe"); 11784 11785 ExpectString("str2.charAt(2);", "e"); 11786 11787 reresult = CompileRun("str2.charCodeAt(2);"); 11788 CHECK_EQ(static_cast<int32_t>('e'), reresult->Int32Value()); 11789} 11790 11791 11792// Failed access check callback that performs a GC on each invocation. 11793void FailedAccessCheckCallbackGC(Local<v8::Object> target, 11794 v8::AccessType type, 11795 Local<v8::Value> data) { 11796 i::Heap::CollectAllGarbage(true); 11797} 11798 11799 11800TEST(GCInFailedAccessCheckCallback) { 11801 // Install a failed access check callback that performs a GC on each 11802 // invocation. Then force the callback to be called from va 11803 11804 v8::V8::Initialize(); 11805 v8::V8::SetFailedAccessCheckCallbackFunction(&FailedAccessCheckCallbackGC); 11806 11807 v8::HandleScope scope; 11808 11809 // Create an ObjectTemplate for global objects and install access 11810 // check callbacks that will block access. 11811 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 11812 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker, 11813 IndexedGetAccessBlocker, 11814 v8::Handle<v8::Value>(), 11815 false); 11816 11817 // Create a context and set an x property on it's global object. 11818 LocalContext context0(NULL, global_template); 11819 context0->Global()->Set(v8_str("x"), v8_num(42)); 11820 v8::Handle<v8::Object> global0 = context0->Global(); 11821 11822 // Create a context with a different security token so that the 11823 // failed access check callback will be called on each access. 11824 LocalContext context1(NULL, global_template); 11825 context1->Global()->Set(v8_str("other"), global0); 11826 11827 // Get property with failed access check. 11828 ExpectUndefined("other.x"); 11829 11830 // Get element with failed access check. 11831 ExpectUndefined("other[0]"); 11832 11833 // Set property with failed access check. 11834 v8::Handle<v8::Value> result = CompileRun("other.x = new Object()"); 11835 CHECK(result->IsObject()); 11836 11837 // Set element with failed access check. 11838 result = CompileRun("other[0] = new Object()"); 11839 CHECK(result->IsObject()); 11840 11841 // Get property attribute with failed access check. 11842 ExpectFalse("\'x\' in other"); 11843 11844 // Get property attribute for element with failed access check. 11845 ExpectFalse("0 in other"); 11846 11847 // Delete property. 11848 ExpectFalse("delete other.x"); 11849 11850 // Delete element. 11851 CHECK_EQ(false, global0->Delete(0)); 11852 11853 // DefineAccessor. 11854 CHECK_EQ(false, 11855 global0->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("x"))); 11856 11857 // Define JavaScript accessor. 11858 ExpectUndefined("Object.prototype.__defineGetter__.call(" 11859 " other, \'x\', function() { return 42; })"); 11860 11861 // LookupAccessor. 11862 ExpectUndefined("Object.prototype.__lookupGetter__.call(" 11863 " other, \'x\')"); 11864 11865 // HasLocalElement. 11866 ExpectFalse("Object.prototype.hasOwnProperty.call(other, \'0\')"); 11867 11868 CHECK_EQ(false, global0->HasRealIndexedProperty(0)); 11869 CHECK_EQ(false, global0->HasRealNamedProperty(v8_str("x"))); 11870 CHECK_EQ(false, global0->HasRealNamedCallbackProperty(v8_str("x"))); 11871 11872 // Reset the failed access check callback so it does not influence 11873 // the other tests. 11874 v8::V8::SetFailedAccessCheckCallbackFunction(NULL); 11875} 11876 11877 11878TEST(StringCheckMultipleContexts) { 11879 const char* code = 11880 "(function() { return \"a\".charAt(0); })()"; 11881 11882 { 11883 // Run the code twice in the first context to initialize the call IC. 11884 v8::HandleScope scope; 11885 LocalContext context1; 11886 ExpectString(code, "a"); 11887 ExpectString(code, "a"); 11888 } 11889 11890 { 11891 // Change the String.prototype in the second context and check 11892 // that the right function gets called. 11893 v8::HandleScope scope; 11894 LocalContext context2; 11895 CompileRun("String.prototype.charAt = function() { return \"not a\"; }"); 11896 ExpectString(code, "not a"); 11897 } 11898} 11899 11900 11901TEST(NumberCheckMultipleContexts) { 11902 const char* code = 11903 "(function() { return (42).toString(); })()"; 11904 11905 { 11906 // Run the code twice in the first context to initialize the call IC. 11907 v8::HandleScope scope; 11908 LocalContext context1; 11909 ExpectString(code, "42"); 11910 ExpectString(code, "42"); 11911 } 11912 11913 { 11914 // Change the Number.prototype in the second context and check 11915 // that the right function gets called. 11916 v8::HandleScope scope; 11917 LocalContext context2; 11918 CompileRun("Number.prototype.toString = function() { return \"not 42\"; }"); 11919 ExpectString(code, "not 42"); 11920 } 11921} 11922 11923 11924TEST(BooleanCheckMultipleContexts) { 11925 const char* code = 11926 "(function() { return true.toString(); })()"; 11927 11928 { 11929 // Run the code twice in the first context to initialize the call IC. 11930 v8::HandleScope scope; 11931 LocalContext context1; 11932 ExpectString(code, "true"); 11933 ExpectString(code, "true"); 11934 } 11935 11936 { 11937 // Change the Boolean.prototype in the second context and check 11938 // that the right function gets called. 11939 v8::HandleScope scope; 11940 LocalContext context2; 11941 CompileRun("Boolean.prototype.toString = function() { return \"\"; }"); 11942 ExpectString(code, ""); 11943 } 11944} 11945 11946 11947TEST(DontDeleteCellLoadIC) { 11948 const char* function_code = 11949 "function readCell() { while (true) { return cell; } }"; 11950 11951 { 11952 // Run the code twice in the first context to initialize the load 11953 // IC for a don't delete cell. 11954 v8::HandleScope scope; 11955 LocalContext context1; 11956 CompileRun("var cell = \"first\";"); 11957 ExpectBoolean("delete cell", false); 11958 CompileRun(function_code); 11959 ExpectString("readCell()", "first"); 11960 ExpectString("readCell()", "first"); 11961 } 11962 11963 { 11964 // Use a deletable cell in the second context. 11965 v8::HandleScope scope; 11966 LocalContext context2; 11967 CompileRun("cell = \"second\";"); 11968 CompileRun(function_code); 11969 ExpectString("readCell()", "second"); 11970 ExpectBoolean("delete cell", true); 11971 ExpectString("(function() {" 11972 " try {" 11973 " return readCell();" 11974 " } catch(e) {" 11975 " return e.toString();" 11976 " }" 11977 "})()", 11978 "ReferenceError: cell is not defined"); 11979 CompileRun("cell = \"new_second\";"); 11980 i::Heap::CollectAllGarbage(true); 11981 ExpectString("readCell()", "new_second"); 11982 ExpectString("readCell()", "new_second"); 11983 } 11984} 11985 11986 11987TEST(DontDeleteCellLoadICForceDelete) { 11988 const char* function_code = 11989 "function readCell() { while (true) { return cell; } }"; 11990 11991 // Run the code twice to initialize the load IC for a don't delete 11992 // cell. 11993 v8::HandleScope scope; 11994 LocalContext context; 11995 CompileRun("var cell = \"value\";"); 11996 ExpectBoolean("delete cell", false); 11997 CompileRun(function_code); 11998 ExpectString("readCell()", "value"); 11999 ExpectString("readCell()", "value"); 12000 12001 // Delete the cell using the API and check the inlined code works 12002 // correctly. 12003 CHECK(context->Global()->ForceDelete(v8_str("cell"))); 12004 ExpectString("(function() {" 12005 " try {" 12006 " return readCell();" 12007 " } catch(e) {" 12008 " return e.toString();" 12009 " }" 12010 "})()", 12011 "ReferenceError: cell is not defined"); 12012} 12013 12014 12015TEST(DontDeleteCellLoadICAPI) { 12016 const char* function_code = 12017 "function readCell() { while (true) { return cell; } }"; 12018 12019 // Run the code twice to initialize the load IC for a don't delete 12020 // cell created using the API. 12021 v8::HandleScope scope; 12022 LocalContext context; 12023 context->Global()->Set(v8_str("cell"), v8_str("value"), v8::DontDelete); 12024 ExpectBoolean("delete cell", false); 12025 CompileRun(function_code); 12026 ExpectString("readCell()", "value"); 12027 ExpectString("readCell()", "value"); 12028 12029 // Delete the cell using the API and check the inlined code works 12030 // correctly. 12031 CHECK(context->Global()->ForceDelete(v8_str("cell"))); 12032 ExpectString("(function() {" 12033 " try {" 12034 " return readCell();" 12035 " } catch(e) {" 12036 " return e.toString();" 12037 " }" 12038 "})()", 12039 "ReferenceError: cell is not defined"); 12040} 12041 12042 12043TEST(GlobalLoadICGC) { 12044 const char* function_code = 12045 "function readCell() { while (true) { return cell; } }"; 12046 12047 // Check inline load code for a don't delete cell is cleared during 12048 // GC. 12049 { 12050 v8::HandleScope scope; 12051 LocalContext context; 12052 CompileRun("var cell = \"value\";"); 12053 ExpectBoolean("delete cell", false); 12054 CompileRun(function_code); 12055 ExpectString("readCell()", "value"); 12056 ExpectString("readCell()", "value"); 12057 } 12058 { 12059 v8::HandleScope scope; 12060 LocalContext context2; 12061 // Hold the code object in the second context. 12062 CompileRun(function_code); 12063 CheckSurvivingGlobalObjectsCount(1); 12064 } 12065 12066 // Check inline load code for a deletable cell is cleared during GC. 12067 { 12068 v8::HandleScope scope; 12069 LocalContext context; 12070 CompileRun("cell = \"value\";"); 12071 CompileRun(function_code); 12072 ExpectString("readCell()", "value"); 12073 ExpectString("readCell()", "value"); 12074 } 12075 { 12076 v8::HandleScope scope; 12077 LocalContext context2; 12078 // Hold the code object in the second context. 12079 CompileRun(function_code); 12080 CheckSurvivingGlobalObjectsCount(1); 12081 } 12082} 12083 12084 12085TEST(RegExp) { 12086 v8::HandleScope scope; 12087 LocalContext context; 12088 12089 v8::Handle<v8::RegExp> re = v8::RegExp::New(v8_str("foo"), v8::RegExp::kNone); 12090 CHECK(re->IsRegExp()); 12091 CHECK(re->GetSource()->Equals(v8_str("foo"))); 12092 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone); 12093 12094 re = v8::RegExp::New(v8_str("bar"), 12095 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | 12096 v8::RegExp::kGlobal)); 12097 CHECK(re->IsRegExp()); 12098 CHECK(re->GetSource()->Equals(v8_str("bar"))); 12099 CHECK_EQ(static_cast<int>(re->GetFlags()), 12100 v8::RegExp::kIgnoreCase | v8::RegExp::kGlobal); 12101 12102 re = v8::RegExp::New(v8_str("baz"), 12103 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | 12104 v8::RegExp::kMultiline)); 12105 CHECK(re->IsRegExp()); 12106 CHECK(re->GetSource()->Equals(v8_str("baz"))); 12107 CHECK_EQ(static_cast<int>(re->GetFlags()), 12108 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline); 12109 12110 re = CompileRun("/quux/").As<v8::RegExp>(); 12111 CHECK(re->IsRegExp()); 12112 CHECK(re->GetSource()->Equals(v8_str("quux"))); 12113 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone); 12114 12115 re = CompileRun("/quux/gm").As<v8::RegExp>(); 12116 CHECK(re->IsRegExp()); 12117 CHECK(re->GetSource()->Equals(v8_str("quux"))); 12118 CHECK_EQ(static_cast<int>(re->GetFlags()), 12119 v8::RegExp::kGlobal | v8::RegExp::kMultiline); 12120 12121 // Override the RegExp constructor and check the API constructor 12122 // still works. 12123 CompileRun("RegExp = function() {}"); 12124 12125 re = v8::RegExp::New(v8_str("foobar"), v8::RegExp::kNone); 12126 CHECK(re->IsRegExp()); 12127 CHECK(re->GetSource()->Equals(v8_str("foobar"))); 12128 CHECK_EQ(re->GetFlags(), v8::RegExp::kNone); 12129 12130 re = v8::RegExp::New(v8_str("foobarbaz"), 12131 static_cast<v8::RegExp::Flags>(v8::RegExp::kIgnoreCase | 12132 v8::RegExp::kMultiline)); 12133 CHECK(re->IsRegExp()); 12134 CHECK(re->GetSource()->Equals(v8_str("foobarbaz"))); 12135 CHECK_EQ(static_cast<int>(re->GetFlags()), 12136 v8::RegExp::kIgnoreCase | v8::RegExp::kMultiline); 12137 12138 context->Global()->Set(v8_str("re"), re); 12139 ExpectTrue("re.test('FoobarbaZ')"); 12140 12141 v8::TryCatch try_catch; 12142 re = v8::RegExp::New(v8_str("foo["), v8::RegExp::kNone); 12143 CHECK(re.IsEmpty()); 12144 CHECK(try_catch.HasCaught()); 12145 context->Global()->Set(v8_str("ex"), try_catch.Exception()); 12146 ExpectTrue("ex instanceof SyntaxError"); 12147} 12148 12149 12150static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, 12151 const v8::AccessorInfo& info ) { 12152 return v8_str("42!"); 12153} 12154 12155 12156static v8::Handle<v8::Array> Enumerator(const v8::AccessorInfo& info) { 12157 v8::Handle<v8::Array> result = v8::Array::New(); 12158 result->Set(0, v8_str("universalAnswer")); 12159 return result; 12160} 12161 12162 12163TEST(NamedEnumeratorAndForIn) { 12164 v8::HandleScope handle_scope; 12165 LocalContext context; 12166 v8::Context::Scope context_scope(context.local()); 12167 12168 v8::Handle<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New(); 12169 tmpl->SetNamedPropertyHandler(Getter, NULL, NULL, NULL, Enumerator); 12170 context->Global()->Set(v8_str("o"), tmpl->NewInstance()); 12171 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 12172 "var result = []; for (var k in o) result.push(k); result")); 12173 CHECK_EQ(1, result->Length()); 12174 CHECK_EQ(v8_str("universalAnswer"), result->Get(0)); 12175} 12176