test-api.cc revision 7f4d5bd8c03935e2c0cd412e561b8fc5a6a880ae
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#define USE_NEW_QUERY_CALLBACKS 31 32#include "v8.h" 33 34#include "api.h" 35#include "compilation-cache.h" 36#include "execution.h" 37#include "snapshot.h" 38#include "platform.h" 39#include "top.h" 40#include "utils.h" 41#include "cctest.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 = ::v8::internal; 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 ExpectObject(const char* code, Local<Value> expected) { 87 Local<Value> result = CompileRun(code); 88 CHECK(result->Equals(expected)); 89} 90 91 92static int signature_callback_count; 93static v8::Handle<Value> IncrementingSignatureCallback( 94 const v8::Arguments& args) { 95 ApiTestFuzzer::Fuzz(); 96 signature_callback_count++; 97 v8::Handle<v8::Array> result = v8::Array::New(args.Length()); 98 for (int i = 0; i < args.Length(); i++) 99 result->Set(v8::Integer::New(i), args[i]); 100 return result; 101} 102 103 104static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) { 105 ApiTestFuzzer::Fuzz(); 106 v8::Handle<v8::Array> result = v8::Array::New(args.Length()); 107 for (int i = 0; i < args.Length(); i++) { 108 result->Set(v8::Integer::New(i), args[i]); 109 } 110 return result; 111} 112 113 114THREADED_TEST(Handles) { 115 v8::HandleScope scope; 116 Local<Context> local_env; 117 { 118 LocalContext env; 119 local_env = env.local(); 120 } 121 122 // Local context should still be live. 123 CHECK(!local_env.IsEmpty()); 124 local_env->Enter(); 125 126 v8::Handle<v8::Primitive> undef = v8::Undefined(); 127 CHECK(!undef.IsEmpty()); 128 CHECK(undef->IsUndefined()); 129 130 const char* c_source = "1 + 2 + 3"; 131 Local<String> source = String::New(c_source); 132 Local<Script> script = Script::Compile(source); 133 CHECK_EQ(6, script->Run()->Int32Value()); 134 135 local_env->Exit(); 136} 137 138 139THREADED_TEST(ReceiverSignature) { 140 v8::HandleScope scope; 141 LocalContext env; 142 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); 143 v8::Handle<v8::Signature> sig = v8::Signature::New(fun); 144 fun->PrototypeTemplate()->Set( 145 v8_str("m"), 146 v8::FunctionTemplate::New(IncrementingSignatureCallback, 147 v8::Handle<Value>(), 148 sig)); 149 env->Global()->Set(v8_str("Fun"), fun->GetFunction()); 150 signature_callback_count = 0; 151 CompileRun( 152 "var o = new Fun();" 153 "o.m();"); 154 CHECK_EQ(1, signature_callback_count); 155 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New(); 156 sub_fun->Inherit(fun); 157 env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction()); 158 CompileRun( 159 "var o = new SubFun();" 160 "o.m();"); 161 CHECK_EQ(2, signature_callback_count); 162 163 v8::TryCatch try_catch; 164 CompileRun( 165 "var o = { };" 166 "o.m = Fun.prototype.m;" 167 "o.m();"); 168 CHECK_EQ(2, signature_callback_count); 169 CHECK(try_catch.HasCaught()); 170 try_catch.Reset(); 171 v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New(); 172 sub_fun->Inherit(fun); 173 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction()); 174 CompileRun( 175 "var o = new UnrelFun();" 176 "o.m = Fun.prototype.m;" 177 "o.m();"); 178 CHECK_EQ(2, signature_callback_count); 179 CHECK(try_catch.HasCaught()); 180} 181 182 183 184 185THREADED_TEST(ArgumentSignature) { 186 v8::HandleScope scope; 187 LocalContext env; 188 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New(); 189 cons->SetClassName(v8_str("Cons")); 190 v8::Handle<v8::Signature> sig = 191 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons); 192 v8::Handle<v8::FunctionTemplate> fun = 193 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig); 194 env->Global()->Set(v8_str("Cons"), cons->GetFunction()); 195 env->Global()->Set(v8_str("Fun1"), fun->GetFunction()); 196 197 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';"); 198 CHECK(value1->IsTrue()); 199 200 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';"); 201 CHECK(value2->IsTrue()); 202 203 v8::Handle<Value> value3 = CompileRun("Fun1() == '';"); 204 CHECK(value3->IsTrue()); 205 206 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New(); 207 cons1->SetClassName(v8_str("Cons1")); 208 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New(); 209 cons2->SetClassName(v8_str("Cons2")); 210 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New(); 211 cons3->SetClassName(v8_str("Cons3")); 212 213 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 }; 214 v8::Handle<v8::Signature> wsig = 215 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args); 216 v8::Handle<v8::FunctionTemplate> fun2 = 217 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig); 218 219 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction()); 220 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction()); 221 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction()); 222 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction()); 223 v8::Handle<Value> value4 = CompileRun( 224 "Fun2(new Cons1(), new Cons2(), new Cons3()) ==" 225 "'[object Cons1],[object Cons2],[object Cons3]'"); 226 CHECK(value4->IsTrue()); 227 228 v8::Handle<Value> value5 = CompileRun( 229 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'"); 230 CHECK(value5->IsTrue()); 231 232 v8::Handle<Value> value6 = CompileRun( 233 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'"); 234 CHECK(value6->IsTrue()); 235 236 v8::Handle<Value> value7 = CompileRun( 237 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == " 238 "'[object Cons1],[object Cons2],[object Cons3],d';"); 239 CHECK(value7->IsTrue()); 240 241 v8::Handle<Value> value8 = CompileRun( 242 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'"); 243 CHECK(value8->IsTrue()); 244} 245 246 247THREADED_TEST(HulIgennem) { 248 v8::HandleScope scope; 249 LocalContext env; 250 v8::Handle<v8::Primitive> undef = v8::Undefined(); 251 Local<String> undef_str = undef->ToString(); 252 char* value = i::NewArray<char>(undef_str->Length() + 1); 253 undef_str->WriteAscii(value); 254 CHECK_EQ(0, strcmp(value, "undefined")); 255 i::DeleteArray(value); 256} 257 258 259THREADED_TEST(Access) { 260 v8::HandleScope scope; 261 LocalContext env; 262 Local<v8::Object> obj = v8::Object::New(); 263 Local<Value> foo_before = obj->Get(v8_str("foo")); 264 CHECK(foo_before->IsUndefined()); 265 Local<String> bar_str = v8_str("bar"); 266 obj->Set(v8_str("foo"), bar_str); 267 Local<Value> foo_after = obj->Get(v8_str("foo")); 268 CHECK(!foo_after->IsUndefined()); 269 CHECK(foo_after->IsString()); 270 CHECK_EQ(bar_str, foo_after); 271} 272 273 274THREADED_TEST(AccessElement) { 275 v8::HandleScope scope; 276 LocalContext env; 277 Local<v8::Object> obj = v8::Object::New(); 278 Local<Value> before = obj->Get(1); 279 CHECK(before->IsUndefined()); 280 Local<String> bar_str = v8_str("bar"); 281 obj->Set(1, bar_str); 282 Local<Value> after = obj->Get(1); 283 CHECK(!after->IsUndefined()); 284 CHECK(after->IsString()); 285 CHECK_EQ(bar_str, after); 286 287 Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>(); 288 CHECK_EQ(v8_str("a"), value->Get(0)); 289 CHECK_EQ(v8_str("b"), value->Get(1)); 290} 291 292 293THREADED_TEST(Script) { 294 v8::HandleScope scope; 295 LocalContext env; 296 const char* c_source = "1 + 2 + 3"; 297 Local<String> source = String::New(c_source); 298 Local<Script> script = Script::Compile(source); 299 CHECK_EQ(6, script->Run()->Int32Value()); 300} 301 302 303static uint16_t* AsciiToTwoByteString(const char* source) { 304 int array_length = i::StrLength(source) + 1; 305 uint16_t* converted = i::NewArray<uint16_t>(array_length); 306 for (int i = 0; i < array_length; i++) converted[i] = source[i]; 307 return converted; 308} 309 310 311class TestResource: public String::ExternalStringResource { 312 public: 313 static int dispose_count; 314 315 explicit TestResource(uint16_t* data) 316 : data_(data), length_(0) { 317 while (data[length_]) ++length_; 318 } 319 320 ~TestResource() { 321 i::DeleteArray(data_); 322 ++dispose_count; 323 } 324 325 const uint16_t* data() const { 326 return data_; 327 } 328 329 size_t length() const { 330 return length_; 331 } 332 private: 333 uint16_t* data_; 334 size_t length_; 335}; 336 337 338int TestResource::dispose_count = 0; 339 340 341class TestAsciiResource: public String::ExternalAsciiStringResource { 342 public: 343 static int dispose_count; 344 345 explicit TestAsciiResource(const char* data) 346 : data_(data), 347 length_(strlen(data)) { } 348 349 ~TestAsciiResource() { 350 i::DeleteArray(data_); 351 ++dispose_count; 352 } 353 354 const char* data() const { 355 return data_; 356 } 357 358 size_t length() const { 359 return length_; 360 } 361 private: 362 const char* data_; 363 size_t length_; 364}; 365 366 367int TestAsciiResource::dispose_count = 0; 368 369 370THREADED_TEST(ScriptUsingStringResource) { 371 TestResource::dispose_count = 0; 372 const char* c_source = "1 + 2 * 3"; 373 uint16_t* two_byte_source = AsciiToTwoByteString(c_source); 374 { 375 v8::HandleScope scope; 376 LocalContext env; 377 TestResource* resource = new TestResource(two_byte_source); 378 Local<String> source = String::NewExternal(resource); 379 Local<Script> script = Script::Compile(source); 380 Local<Value> value = script->Run(); 381 CHECK(value->IsNumber()); 382 CHECK_EQ(7, value->Int32Value()); 383 CHECK(source->IsExternal()); 384 CHECK_EQ(resource, 385 static_cast<TestResource*>(source->GetExternalStringResource())); 386 v8::internal::Heap::CollectAllGarbage(false); 387 CHECK_EQ(0, TestResource::dispose_count); 388 } 389 v8::internal::CompilationCache::Clear(); 390 v8::internal::Heap::CollectAllGarbage(false); 391 CHECK_EQ(1, TestResource::dispose_count); 392} 393 394 395THREADED_TEST(ScriptUsingAsciiStringResource) { 396 TestAsciiResource::dispose_count = 0; 397 const char* c_source = "1 + 2 * 3"; 398 { 399 v8::HandleScope scope; 400 LocalContext env; 401 Local<String> source = 402 String::NewExternal(new TestAsciiResource(i::StrDup(c_source))); 403 Local<Script> script = Script::Compile(source); 404 Local<Value> value = script->Run(); 405 CHECK(value->IsNumber()); 406 CHECK_EQ(7, value->Int32Value()); 407 v8::internal::Heap::CollectAllGarbage(false); 408 CHECK_EQ(0, TestAsciiResource::dispose_count); 409 } 410 v8::internal::CompilationCache::Clear(); 411 v8::internal::Heap::CollectAllGarbage(false); 412 CHECK_EQ(1, TestAsciiResource::dispose_count); 413} 414 415 416THREADED_TEST(ScriptMakingExternalString) { 417 TestResource::dispose_count = 0; 418 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3"); 419 { 420 v8::HandleScope scope; 421 LocalContext env; 422 Local<String> source = String::New(two_byte_source); 423 // Trigger GCs so that the newly allocated string moves to old gen. 424 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 425 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 426 bool success = source->MakeExternal(new TestResource(two_byte_source)); 427 CHECK(success); 428 Local<Script> script = Script::Compile(source); 429 Local<Value> value = script->Run(); 430 CHECK(value->IsNumber()); 431 CHECK_EQ(7, value->Int32Value()); 432 v8::internal::Heap::CollectAllGarbage(false); 433 CHECK_EQ(0, TestResource::dispose_count); 434 } 435 v8::internal::CompilationCache::Clear(); 436 v8::internal::Heap::CollectAllGarbage(false); 437 CHECK_EQ(1, TestResource::dispose_count); 438} 439 440 441THREADED_TEST(ScriptMakingExternalAsciiString) { 442 TestAsciiResource::dispose_count = 0; 443 const char* c_source = "1 + 2 * 3"; 444 { 445 v8::HandleScope scope; 446 LocalContext env; 447 Local<String> source = v8_str(c_source); 448 // Trigger GCs so that the newly allocated string moves to old gen. 449 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 450 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 451 bool success = source->MakeExternal( 452 new TestAsciiResource(i::StrDup(c_source))); 453 CHECK(success); 454 Local<Script> script = Script::Compile(source); 455 Local<Value> value = script->Run(); 456 CHECK(value->IsNumber()); 457 CHECK_EQ(7, value->Int32Value()); 458 v8::internal::Heap::CollectAllGarbage(false); 459 CHECK_EQ(0, TestAsciiResource::dispose_count); 460 } 461 v8::internal::CompilationCache::Clear(); 462 v8::internal::Heap::CollectAllGarbage(false); 463 CHECK_EQ(1, TestAsciiResource::dispose_count); 464} 465 466 467TEST(MakingExternalStringConditions) { 468 v8::HandleScope scope; 469 LocalContext env; 470 471 // Free some space in the new space so that we can check freshness. 472 i::Heap::CollectGarbage(0, i::NEW_SPACE); 473 i::Heap::CollectGarbage(0, i::NEW_SPACE); 474 475 Local<String> small_string = String::New(AsciiToTwoByteString("small")); 476 // We should refuse to externalize newly created small string. 477 CHECK(!small_string->CanMakeExternal()); 478 // Trigger GCs so that the newly allocated string moves to old gen. 479 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 480 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 481 // Old space strings should be accepted. 482 CHECK(small_string->CanMakeExternal()); 483 484 small_string = String::New(AsciiToTwoByteString("small 2")); 485 // We should refuse externalizing newly created small string. 486 CHECK(!small_string->CanMakeExternal()); 487 for (int i = 0; i < 100; i++) { 488 String::Value value(small_string); 489 } 490 // Frequently used strings should be accepted. 491 CHECK(small_string->CanMakeExternal()); 492 493 const int buf_size = 10 * 1024; 494 char* buf = i::NewArray<char>(buf_size); 495 memset(buf, 'a', buf_size); 496 buf[buf_size - 1] = '\0'; 497 Local<String> large_string = String::New(AsciiToTwoByteString(buf)); 498 i::DeleteArray(buf); 499 // Large strings should be immediately accepted. 500 CHECK(large_string->CanMakeExternal()); 501} 502 503 504TEST(MakingExternalAsciiStringConditions) { 505 v8::HandleScope scope; 506 LocalContext env; 507 508 // Free some space in the new space so that we can check freshness. 509 i::Heap::CollectGarbage(0, i::NEW_SPACE); 510 i::Heap::CollectGarbage(0, i::NEW_SPACE); 511 512 Local<String> small_string = String::New("small"); 513 // We should refuse to externalize newly created small string. 514 CHECK(!small_string->CanMakeExternal()); 515 // Trigger GCs so that the newly allocated string moves to old gen. 516 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 517 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 518 // Old space strings should be accepted. 519 CHECK(small_string->CanMakeExternal()); 520 521 small_string = String::New("small 2"); 522 // We should refuse externalizing newly created small string. 523 CHECK(!small_string->CanMakeExternal()); 524 for (int i = 0; i < 100; i++) { 525 String::Value value(small_string); 526 } 527 // Frequently used strings should be accepted. 528 CHECK(small_string->CanMakeExternal()); 529 530 const int buf_size = 10 * 1024; 531 char* buf = i::NewArray<char>(buf_size); 532 memset(buf, 'a', buf_size); 533 buf[buf_size - 1] = '\0'; 534 Local<String> large_string = String::New(buf); 535 i::DeleteArray(buf); 536 // Large strings should be immediately accepted. 537 CHECK(large_string->CanMakeExternal()); 538} 539 540 541THREADED_TEST(UsingExternalString) { 542 { 543 v8::HandleScope scope; 544 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 545 Local<String> string = 546 String::NewExternal(new TestResource(two_byte_string)); 547 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 548 // Trigger GCs so that the newly allocated string moves to old gen. 549 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 550 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 551 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring); 552 CHECK(isymbol->IsSymbol()); 553 } 554 i::Heap::CollectAllGarbage(false); 555 i::Heap::CollectAllGarbage(false); 556} 557 558 559THREADED_TEST(UsingExternalAsciiString) { 560 { 561 v8::HandleScope scope; 562 const char* one_byte_string = "test string"; 563 Local<String> string = String::NewExternal( 564 new TestAsciiResource(i::StrDup(one_byte_string))); 565 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 566 // Trigger GCs so that the newly allocated string moves to old gen. 567 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 568 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 569 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring); 570 CHECK(isymbol->IsSymbol()); 571 } 572 i::Heap::CollectAllGarbage(false); 573 i::Heap::CollectAllGarbage(false); 574} 575 576 577THREADED_TEST(ScavengeExternalString) { 578 TestResource::dispose_count = 0; 579 bool in_new_space = false; 580 { 581 v8::HandleScope scope; 582 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 583 Local<String> string = 584 String::NewExternal(new TestResource(two_byte_string)); 585 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 586 i::Heap::CollectGarbage(0, i::NEW_SPACE); 587 in_new_space = i::Heap::InNewSpace(*istring); 588 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring)); 589 CHECK_EQ(0, TestResource::dispose_count); 590 } 591 i::Heap::CollectGarbage(0, in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); 592 CHECK_EQ(1, TestResource::dispose_count); 593} 594 595 596THREADED_TEST(ScavengeExternalAsciiString) { 597 TestAsciiResource::dispose_count = 0; 598 bool in_new_space = false; 599 { 600 v8::HandleScope scope; 601 const char* one_byte_string = "test string"; 602 Local<String> string = String::NewExternal( 603 new TestAsciiResource(i::StrDup(one_byte_string))); 604 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 605 i::Heap::CollectGarbage(0, i::NEW_SPACE); 606 in_new_space = i::Heap::InNewSpace(*istring); 607 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring)); 608 CHECK_EQ(0, TestAsciiResource::dispose_count); 609 } 610 i::Heap::CollectGarbage(0, in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); 611 CHECK_EQ(1, TestAsciiResource::dispose_count); 612} 613 614 615class TestAsciiResourceWithDisposeControl: public TestAsciiResource { 616 public: 617 static int dispose_calls; 618 619 TestAsciiResourceWithDisposeControl(const char* data, bool dispose) 620 : TestAsciiResource(data), 621 dispose_(dispose) { } 622 623 void Dispose() { 624 ++dispose_calls; 625 if (dispose_) delete this; 626 } 627 private: 628 bool dispose_; 629}; 630 631 632int TestAsciiResourceWithDisposeControl::dispose_calls = 0; 633 634 635TEST(ExternalStringWithDisposeHandling) { 636 const char* c_source = "1 + 2 * 3"; 637 638 // Use a stack allocated external string resource allocated object. 639 TestAsciiResource::dispose_count = 0; 640 TestAsciiResourceWithDisposeControl::dispose_calls = 0; 641 TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false); 642 { 643 v8::HandleScope scope; 644 LocalContext env; 645 Local<String> source = String::NewExternal(&res_stack); 646 Local<Script> script = Script::Compile(source); 647 Local<Value> value = script->Run(); 648 CHECK(value->IsNumber()); 649 CHECK_EQ(7, value->Int32Value()); 650 v8::internal::Heap::CollectAllGarbage(false); 651 CHECK_EQ(0, TestAsciiResource::dispose_count); 652 } 653 v8::internal::CompilationCache::Clear(); 654 v8::internal::Heap::CollectAllGarbage(false); 655 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); 656 CHECK_EQ(0, TestAsciiResource::dispose_count); 657 658 // Use a heap allocated external string resource allocated object. 659 TestAsciiResource::dispose_count = 0; 660 TestAsciiResourceWithDisposeControl::dispose_calls = 0; 661 TestAsciiResource* res_heap = 662 new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true); 663 { 664 v8::HandleScope scope; 665 LocalContext env; 666 Local<String> source = String::NewExternal(res_heap); 667 Local<Script> script = Script::Compile(source); 668 Local<Value> value = script->Run(); 669 CHECK(value->IsNumber()); 670 CHECK_EQ(7, value->Int32Value()); 671 v8::internal::Heap::CollectAllGarbage(false); 672 CHECK_EQ(0, TestAsciiResource::dispose_count); 673 } 674 v8::internal::CompilationCache::Clear(); 675 v8::internal::Heap::CollectAllGarbage(false); 676 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); 677 CHECK_EQ(1, TestAsciiResource::dispose_count); 678} 679 680 681THREADED_TEST(StringConcat) { 682 { 683 v8::HandleScope scope; 684 LocalContext env; 685 const char* one_byte_string_1 = "function a_times_t"; 686 const char* two_byte_string_1 = "wo_plus_b(a, b) {return "; 687 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + "; 688 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + "; 689 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 690 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 691 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);"; 692 Local<String> left = v8_str(one_byte_string_1); 693 Local<String> right = String::New(AsciiToTwoByteString(two_byte_string_1)); 694 Local<String> source = String::Concat(left, right); 695 right = String::NewExternal( 696 new TestAsciiResource(i::StrDup(one_byte_extern_1))); 697 source = String::Concat(source, right); 698 right = String::NewExternal( 699 new TestResource(AsciiToTwoByteString(two_byte_extern_1))); 700 source = String::Concat(source, right); 701 right = v8_str(one_byte_string_2); 702 source = String::Concat(source, right); 703 right = String::New(AsciiToTwoByteString(two_byte_string_2)); 704 source = String::Concat(source, right); 705 right = String::NewExternal( 706 new TestResource(AsciiToTwoByteString(two_byte_extern_2))); 707 source = String::Concat(source, right); 708 Local<Script> script = Script::Compile(source); 709 Local<Value> value = script->Run(); 710 CHECK(value->IsNumber()); 711 CHECK_EQ(68, value->Int32Value()); 712 } 713 v8::internal::CompilationCache::Clear(); 714 i::Heap::CollectAllGarbage(false); 715 i::Heap::CollectAllGarbage(false); 716} 717 718 719THREADED_TEST(GlobalProperties) { 720 v8::HandleScope scope; 721 LocalContext env; 722 v8::Handle<v8::Object> global = env->Global(); 723 global->Set(v8_str("pi"), v8_num(3.1415926)); 724 Local<Value> pi = global->Get(v8_str("pi")); 725 CHECK_EQ(3.1415926, pi->NumberValue()); 726} 727 728 729static v8::Handle<Value> handle_call(const v8::Arguments& args) { 730 ApiTestFuzzer::Fuzz(); 731 return v8_num(102); 732} 733 734 735static v8::Handle<Value> construct_call(const v8::Arguments& args) { 736 ApiTestFuzzer::Fuzz(); 737 args.This()->Set(v8_str("x"), v8_num(1)); 738 args.This()->Set(v8_str("y"), v8_num(2)); 739 return args.This(); 740} 741 742THREADED_TEST(FunctionTemplate) { 743 v8::HandleScope scope; 744 LocalContext env; 745 { 746 Local<v8::FunctionTemplate> fun_templ = 747 v8::FunctionTemplate::New(handle_call); 748 Local<Function> fun = fun_templ->GetFunction(); 749 env->Global()->Set(v8_str("obj"), fun); 750 Local<Script> script = v8_compile("obj()"); 751 CHECK_EQ(102, script->Run()->Int32Value()); 752 } 753 // Use SetCallHandler to initialize a function template, should work like the 754 // previous one. 755 { 756 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 757 fun_templ->SetCallHandler(handle_call); 758 Local<Function> fun = fun_templ->GetFunction(); 759 env->Global()->Set(v8_str("obj"), fun); 760 Local<Script> script = v8_compile("obj()"); 761 CHECK_EQ(102, script->Run()->Int32Value()); 762 } 763 // Test constructor calls. 764 { 765 Local<v8::FunctionTemplate> fun_templ = 766 v8::FunctionTemplate::New(construct_call); 767 fun_templ->SetClassName(v8_str("funky")); 768 Local<Function> fun = fun_templ->GetFunction(); 769 env->Global()->Set(v8_str("obj"), fun); 770 Local<Script> script = v8_compile("var s = new obj(); s.x"); 771 CHECK_EQ(1, script->Run()->Int32Value()); 772 773 Local<Value> result = v8_compile("(new obj()).toString()")->Run(); 774 CHECK_EQ(v8_str("[object funky]"), result); 775 } 776} 777 778 779THREADED_TEST(FindInstanceInPrototypeChain) { 780 v8::HandleScope scope; 781 LocalContext env; 782 783 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New(); 784 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New(); 785 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New(); 786 derived->Inherit(base); 787 788 Local<v8::Function> base_function = base->GetFunction(); 789 Local<v8::Function> derived_function = derived->GetFunction(); 790 Local<v8::Function> other_function = other->GetFunction(); 791 792 Local<v8::Object> base_instance = base_function->NewInstance(); 793 Local<v8::Object> derived_instance = derived_function->NewInstance(); 794 Local<v8::Object> derived_instance2 = derived_function->NewInstance(); 795 Local<v8::Object> other_instance = other_function->NewInstance(); 796 derived_instance2->Set(v8_str("__proto__"), derived_instance); 797 other_instance->Set(v8_str("__proto__"), derived_instance2); 798 799 // base_instance is only an instance of base. 800 CHECK_EQ(base_instance, 801 base_instance->FindInstanceInPrototypeChain(base)); 802 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty()); 803 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 804 805 // derived_instance is an instance of base and derived. 806 CHECK_EQ(derived_instance, 807 derived_instance->FindInstanceInPrototypeChain(base)); 808 CHECK_EQ(derived_instance, 809 derived_instance->FindInstanceInPrototypeChain(derived)); 810 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 811 812 // other_instance is an instance of other and its immediate 813 // prototype derived_instance2 is an instance of base and derived. 814 // Note, derived_instance is an instance of base and derived too, 815 // but it comes after derived_instance2 in the prototype chain of 816 // other_instance. 817 CHECK_EQ(derived_instance2, 818 other_instance->FindInstanceInPrototypeChain(base)); 819 CHECK_EQ(derived_instance2, 820 other_instance->FindInstanceInPrototypeChain(derived)); 821 CHECK_EQ(other_instance, 822 other_instance->FindInstanceInPrototypeChain(other)); 823} 824 825 826THREADED_TEST(TinyInteger) { 827 v8::HandleScope scope; 828 LocalContext env; 829 int32_t value = 239; 830 Local<v8::Integer> value_obj = v8::Integer::New(value); 831 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 832} 833 834 835THREADED_TEST(BigSmiInteger) { 836 v8::HandleScope scope; 837 LocalContext env; 838 int32_t value = i::Smi::kMaxValue; 839 // We cannot add one to a Smi::kMaxValue without wrapping. 840 if (i::kSmiValueSize < 32) { 841 CHECK(i::Smi::IsValid(value)); 842 CHECK(!i::Smi::IsValid(value + 1)); 843 Local<v8::Integer> value_obj = v8::Integer::New(value); 844 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 845 } 846} 847 848 849THREADED_TEST(BigInteger) { 850 v8::HandleScope scope; 851 LocalContext env; 852 // We cannot add one to a Smi::kMaxValue without wrapping. 853 if (i::kSmiValueSize < 32) { 854 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1. 855 // The code will not be run in that case, due to the "if" guard. 856 int32_t value = 857 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1); 858 CHECK(value > i::Smi::kMaxValue); 859 CHECK(!i::Smi::IsValid(value)); 860 Local<v8::Integer> value_obj = v8::Integer::New(value); 861 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 862 } 863} 864 865 866THREADED_TEST(TinyUnsignedInteger) { 867 v8::HandleScope scope; 868 LocalContext env; 869 uint32_t value = 239; 870 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 871 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 872} 873 874 875THREADED_TEST(BigUnsignedSmiInteger) { 876 v8::HandleScope scope; 877 LocalContext env; 878 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue); 879 CHECK(i::Smi::IsValid(value)); 880 CHECK(!i::Smi::IsValid(value + 1)); 881 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 882 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 883} 884 885 886THREADED_TEST(BigUnsignedInteger) { 887 v8::HandleScope scope; 888 LocalContext env; 889 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1; 890 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue)); 891 CHECK(!i::Smi::IsValid(value)); 892 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 893 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 894} 895 896 897THREADED_TEST(OutOfSignedRangeUnsignedInteger) { 898 v8::HandleScope scope; 899 LocalContext env; 900 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1; 901 uint32_t value = INT32_MAX_AS_UINT + 1; 902 CHECK(value > INT32_MAX_AS_UINT); // No overflow. 903 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 904 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 905} 906 907 908THREADED_TEST(Number) { 909 v8::HandleScope scope; 910 LocalContext env; 911 double PI = 3.1415926; 912 Local<v8::Number> pi_obj = v8::Number::New(PI); 913 CHECK_EQ(PI, pi_obj->NumberValue()); 914} 915 916 917THREADED_TEST(ToNumber) { 918 v8::HandleScope scope; 919 LocalContext env; 920 Local<String> str = v8_str("3.1415926"); 921 CHECK_EQ(3.1415926, str->NumberValue()); 922 v8::Handle<v8::Boolean> t = v8::True(); 923 CHECK_EQ(1.0, t->NumberValue()); 924 v8::Handle<v8::Boolean> f = v8::False(); 925 CHECK_EQ(0.0, f->NumberValue()); 926} 927 928 929THREADED_TEST(Date) { 930 v8::HandleScope scope; 931 LocalContext env; 932 double PI = 3.1415926; 933 Local<Value> date_obj = v8::Date::New(PI); 934 CHECK_EQ(3.0, date_obj->NumberValue()); 935} 936 937 938THREADED_TEST(Boolean) { 939 v8::HandleScope scope; 940 LocalContext env; 941 v8::Handle<v8::Boolean> t = v8::True(); 942 CHECK(t->Value()); 943 v8::Handle<v8::Boolean> f = v8::False(); 944 CHECK(!f->Value()); 945 v8::Handle<v8::Primitive> u = v8::Undefined(); 946 CHECK(!u->BooleanValue()); 947 v8::Handle<v8::Primitive> n = v8::Null(); 948 CHECK(!n->BooleanValue()); 949 v8::Handle<String> str1 = v8_str(""); 950 CHECK(!str1->BooleanValue()); 951 v8::Handle<String> str2 = v8_str("x"); 952 CHECK(str2->BooleanValue()); 953 CHECK(!v8::Number::New(0)->BooleanValue()); 954 CHECK(v8::Number::New(-1)->BooleanValue()); 955 CHECK(v8::Number::New(1)->BooleanValue()); 956 CHECK(v8::Number::New(42)->BooleanValue()); 957 CHECK(!v8_compile("NaN")->Run()->BooleanValue()); 958} 959 960 961static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) { 962 ApiTestFuzzer::Fuzz(); 963 return v8_num(13.4); 964} 965 966 967static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) { 968 ApiTestFuzzer::Fuzz(); 969 return v8_num(876); 970} 971 972 973THREADED_TEST(GlobalPrototype) { 974 v8::HandleScope scope; 975 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 976 func_templ->PrototypeTemplate()->Set( 977 "dummy", 978 v8::FunctionTemplate::New(DummyCallHandler)); 979 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate(); 980 templ->Set("x", v8_num(200)); 981 templ->SetAccessor(v8_str("m"), GetM); 982 LocalContext env(0, templ); 983 v8::Handle<v8::Object> obj = env->Global(); 984 v8::Handle<Script> script = v8_compile("dummy()"); 985 v8::Handle<Value> result = script->Run(); 986 CHECK_EQ(13.4, result->NumberValue()); 987 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value()); 988 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value()); 989} 990 991 992THREADED_TEST(ObjectTemplate) { 993 v8::HandleScope scope; 994 Local<ObjectTemplate> templ1 = ObjectTemplate::New(); 995 templ1->Set("x", v8_num(10)); 996 templ1->Set("y", v8_num(13)); 997 LocalContext env; 998 Local<v8::Object> instance1 = templ1->NewInstance(); 999 env->Global()->Set(v8_str("p"), instance1); 1000 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue()); 1001 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue()); 1002 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); 1003 fun->PrototypeTemplate()->Set("nirk", v8_num(123)); 1004 Local<ObjectTemplate> templ2 = fun->InstanceTemplate(); 1005 templ2->Set("a", v8_num(12)); 1006 templ2->Set("b", templ1); 1007 Local<v8::Object> instance2 = templ2->NewInstance(); 1008 env->Global()->Set(v8_str("q"), instance2); 1009 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue()); 1010 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue()); 1011 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue()); 1012 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue()); 1013} 1014 1015 1016static v8::Handle<Value> GetFlabby(const v8::Arguments& args) { 1017 ApiTestFuzzer::Fuzz(); 1018 return v8_num(17.2); 1019} 1020 1021 1022static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) { 1023 ApiTestFuzzer::Fuzz(); 1024 return v8_num(15.2); 1025} 1026 1027 1028THREADED_TEST(DescriptorInheritance) { 1029 v8::HandleScope scope; 1030 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New(); 1031 super->PrototypeTemplate()->Set("flabby", 1032 v8::FunctionTemplate::New(GetFlabby)); 1033 super->PrototypeTemplate()->Set("PI", v8_num(3.14)); 1034 1035 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd); 1036 1037 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New(); 1038 base1->Inherit(super); 1039 base1->PrototypeTemplate()->Set("v1", v8_num(20.1)); 1040 1041 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New(); 1042 base2->Inherit(super); 1043 base2->PrototypeTemplate()->Set("v2", v8_num(10.1)); 1044 1045 LocalContext env; 1046 1047 env->Global()->Set(v8_str("s"), super->GetFunction()); 1048 env->Global()->Set(v8_str("base1"), base1->GetFunction()); 1049 env->Global()->Set(v8_str("base2"), base2->GetFunction()); 1050 1051 // Checks right __proto__ chain. 1052 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue()); 1053 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue()); 1054 1055 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue()); 1056 1057 // Instance accessor should not be visible on function object or its prototype 1058 CHECK(CompileRun("s.knurd == undefined")->BooleanValue()); 1059 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue()); 1060 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue()); 1061 1062 env->Global()->Set(v8_str("obj"), 1063 base1->GetFunction()->NewInstance()); 1064 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue()); 1065 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue()); 1066 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue()); 1067 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue()); 1068 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue()); 1069 1070 env->Global()->Set(v8_str("obj2"), 1071 base2->GetFunction()->NewInstance()); 1072 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue()); 1073 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue()); 1074 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue()); 1075 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue()); 1076 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue()); 1077 1078 // base1 and base2 cannot cross reference to each's prototype 1079 CHECK(v8_compile("obj.v2")->Run()->IsUndefined()); 1080 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined()); 1081} 1082 1083 1084int echo_named_call_count; 1085 1086 1087static v8::Handle<Value> EchoNamedProperty(Local<String> name, 1088 const AccessorInfo& info) { 1089 ApiTestFuzzer::Fuzz(); 1090 CHECK_EQ(v8_str("data"), info.Data()); 1091 echo_named_call_count++; 1092 return name; 1093} 1094 1095 1096THREADED_TEST(NamedPropertyHandlerGetter) { 1097 echo_named_call_count = 0; 1098 v8::HandleScope scope; 1099 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1100 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty, 1101 0, 0, 0, 0, 1102 v8_str("data")); 1103 LocalContext env; 1104 env->Global()->Set(v8_str("obj"), 1105 templ->GetFunction()->NewInstance()); 1106 CHECK_EQ(echo_named_call_count, 0); 1107 v8_compile("obj.x")->Run(); 1108 CHECK_EQ(echo_named_call_count, 1); 1109 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;"; 1110 v8::Handle<Value> str = CompileRun(code); 1111 String::AsciiValue value(str); 1112 CHECK_EQ(*value, "oddlepoddle"); 1113 // Check default behavior 1114 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10); 1115 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue()); 1116 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue()); 1117} 1118 1119 1120int echo_indexed_call_count = 0; 1121 1122 1123static v8::Handle<Value> EchoIndexedProperty(uint32_t index, 1124 const AccessorInfo& info) { 1125 ApiTestFuzzer::Fuzz(); 1126 CHECK_EQ(v8_num(637), info.Data()); 1127 echo_indexed_call_count++; 1128 return v8_num(index); 1129} 1130 1131 1132THREADED_TEST(IndexedPropertyHandlerGetter) { 1133 v8::HandleScope scope; 1134 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1135 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty, 1136 0, 0, 0, 0, 1137 v8_num(637)); 1138 LocalContext env; 1139 env->Global()->Set(v8_str("obj"), 1140 templ->GetFunction()->NewInstance()); 1141 Local<Script> script = v8_compile("obj[900]"); 1142 CHECK_EQ(script->Run()->Int32Value(), 900); 1143} 1144 1145 1146v8::Handle<v8::Object> bottom; 1147 1148static v8::Handle<Value> CheckThisIndexedPropertyHandler( 1149 uint32_t index, 1150 const AccessorInfo& info) { 1151 ApiTestFuzzer::Fuzz(); 1152 CHECK(info.This()->Equals(bottom)); 1153 return v8::Handle<Value>(); 1154} 1155 1156static v8::Handle<Value> CheckThisNamedPropertyHandler( 1157 Local<String> name, 1158 const AccessorInfo& info) { 1159 ApiTestFuzzer::Fuzz(); 1160 CHECK(info.This()->Equals(bottom)); 1161 return v8::Handle<Value>(); 1162} 1163 1164 1165v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index, 1166 Local<Value> value, 1167 const AccessorInfo& info) { 1168 ApiTestFuzzer::Fuzz(); 1169 CHECK(info.This()->Equals(bottom)); 1170 return v8::Handle<Value>(); 1171} 1172 1173 1174v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property, 1175 Local<Value> value, 1176 const AccessorInfo& info) { 1177 ApiTestFuzzer::Fuzz(); 1178 CHECK(info.This()->Equals(bottom)); 1179 return v8::Handle<Value>(); 1180} 1181 1182v8::Handle<v8::Boolean> CheckThisIndexedPropertyQuery( 1183 uint32_t index, 1184 const AccessorInfo& info) { 1185 ApiTestFuzzer::Fuzz(); 1186 CHECK(info.This()->Equals(bottom)); 1187 return v8::Handle<v8::Boolean>(); 1188} 1189 1190 1191v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property, 1192 const AccessorInfo& info) { 1193 ApiTestFuzzer::Fuzz(); 1194 CHECK(info.This()->Equals(bottom)); 1195 return v8::Handle<v8::Integer>(); 1196} 1197 1198 1199v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter( 1200 uint32_t index, 1201 const AccessorInfo& info) { 1202 ApiTestFuzzer::Fuzz(); 1203 CHECK(info.This()->Equals(bottom)); 1204 return v8::Handle<v8::Boolean>(); 1205} 1206 1207 1208v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter( 1209 Local<String> property, 1210 const AccessorInfo& info) { 1211 ApiTestFuzzer::Fuzz(); 1212 CHECK(info.This()->Equals(bottom)); 1213 return v8::Handle<v8::Boolean>(); 1214} 1215 1216 1217v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator( 1218 const AccessorInfo& info) { 1219 ApiTestFuzzer::Fuzz(); 1220 CHECK(info.This()->Equals(bottom)); 1221 return v8::Handle<v8::Array>(); 1222} 1223 1224 1225v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator( 1226 const AccessorInfo& info) { 1227 ApiTestFuzzer::Fuzz(); 1228 CHECK(info.This()->Equals(bottom)); 1229 return v8::Handle<v8::Array>(); 1230} 1231 1232 1233THREADED_TEST(PropertyHandlerInPrototype) { 1234 v8::HandleScope scope; 1235 LocalContext env; 1236 1237 // Set up a prototype chain with three interceptors. 1238 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1239 templ->InstanceTemplate()->SetIndexedPropertyHandler( 1240 CheckThisIndexedPropertyHandler, 1241 CheckThisIndexedPropertySetter, 1242 CheckThisIndexedPropertyQuery, 1243 CheckThisIndexedPropertyDeleter, 1244 CheckThisIndexedPropertyEnumerator); 1245 1246 templ->InstanceTemplate()->SetNamedPropertyHandler( 1247 CheckThisNamedPropertyHandler, 1248 CheckThisNamedPropertySetter, 1249 CheckThisNamedPropertyQuery, 1250 CheckThisNamedPropertyDeleter, 1251 CheckThisNamedPropertyEnumerator); 1252 1253 bottom = templ->GetFunction()->NewInstance(); 1254 Local<v8::Object> top = templ->GetFunction()->NewInstance(); 1255 Local<v8::Object> middle = templ->GetFunction()->NewInstance(); 1256 1257 bottom->Set(v8_str("__proto__"), middle); 1258 middle->Set(v8_str("__proto__"), top); 1259 env->Global()->Set(v8_str("obj"), bottom); 1260 1261 // Indexed and named get. 1262 Script::Compile(v8_str("obj[0]"))->Run(); 1263 Script::Compile(v8_str("obj.x"))->Run(); 1264 1265 // Indexed and named set. 1266 Script::Compile(v8_str("obj[1] = 42"))->Run(); 1267 Script::Compile(v8_str("obj.y = 42"))->Run(); 1268 1269 // Indexed and named query. 1270 Script::Compile(v8_str("0 in obj"))->Run(); 1271 Script::Compile(v8_str("'x' in obj"))->Run(); 1272 1273 // Indexed and named deleter. 1274 Script::Compile(v8_str("delete obj[0]"))->Run(); 1275 Script::Compile(v8_str("delete obj.x"))->Run(); 1276 1277 // Enumerators. 1278 Script::Compile(v8_str("for (var p in obj) ;"))->Run(); 1279} 1280 1281 1282static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key, 1283 const AccessorInfo& info) { 1284 ApiTestFuzzer::Fuzz(); 1285 if (v8_str("pre")->Equals(key)) { 1286 return v8_str("PrePropertyHandler: pre"); 1287 } 1288 return v8::Handle<String>(); 1289} 1290 1291 1292static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key, 1293 const AccessorInfo&) { 1294 if (v8_str("pre")->Equals(key)) { 1295 return v8::Integer::New(v8::None); 1296 } 1297 1298 return v8::Handle<v8::Integer>(); // do not intercept the call 1299} 1300 1301 1302THREADED_TEST(PrePropertyHandler) { 1303 v8::HandleScope scope; 1304 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(); 1305 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet, 1306 0, 1307 PrePropertyHandlerQuery); 1308 LocalContext env(NULL, desc->InstanceTemplate()); 1309 Script::Compile(v8_str( 1310 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run(); 1311 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run(); 1312 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre); 1313 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run(); 1314 CHECK_EQ(v8_str("Object: on"), result_on); 1315 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run(); 1316 CHECK(result_post.IsEmpty()); 1317} 1318 1319 1320THREADED_TEST(UndefinedIsNotEnumerable) { 1321 v8::HandleScope scope; 1322 LocalContext env; 1323 v8::Handle<Value> result = Script::Compile(v8_str( 1324 "this.propertyIsEnumerable(undefined)"))->Run(); 1325 CHECK(result->IsFalse()); 1326} 1327 1328 1329v8::Handle<Script> call_recursively_script; 1330static const int kTargetRecursionDepth = 200; // near maximum 1331 1332 1333static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) { 1334 ApiTestFuzzer::Fuzz(); 1335 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1336 if (depth == kTargetRecursionDepth) return v8::Undefined(); 1337 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1338 return call_recursively_script->Run(); 1339} 1340 1341 1342static v8::Handle<Value> CallFunctionRecursivelyCall( 1343 const v8::Arguments& args) { 1344 ApiTestFuzzer::Fuzz(); 1345 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1346 if (depth == kTargetRecursionDepth) { 1347 printf("[depth = %d]\n", depth); 1348 return v8::Undefined(); 1349 } 1350 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1351 v8::Handle<Value> function = 1352 args.This()->Get(v8_str("callFunctionRecursively")); 1353 return function.As<Function>()->Call(args.This(), 0, NULL); 1354} 1355 1356 1357THREADED_TEST(DeepCrossLanguageRecursion) { 1358 v8::HandleScope scope; 1359 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 1360 global->Set(v8_str("callScriptRecursively"), 1361 v8::FunctionTemplate::New(CallScriptRecursivelyCall)); 1362 global->Set(v8_str("callFunctionRecursively"), 1363 v8::FunctionTemplate::New(CallFunctionRecursivelyCall)); 1364 LocalContext env(NULL, global); 1365 1366 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1367 call_recursively_script = v8_compile("callScriptRecursively()"); 1368 v8::Handle<Value> result = call_recursively_script->Run(); 1369 call_recursively_script = v8::Handle<Script>(); 1370 1371 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1372 Script::Compile(v8_str("callFunctionRecursively()"))->Run(); 1373} 1374 1375 1376static v8::Handle<Value> 1377 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) { 1378 ApiTestFuzzer::Fuzz(); 1379 return v8::ThrowException(key); 1380} 1381 1382 1383static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key, 1384 Local<Value>, 1385 const AccessorInfo&) { 1386 v8::ThrowException(key); 1387 return v8::Undefined(); // not the same as v8::Handle<v8::Value>() 1388} 1389 1390 1391THREADED_TEST(CallbackExceptionRegression) { 1392 v8::HandleScope scope; 1393 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 1394 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet, 1395 ThrowingPropertyHandlerSet); 1396 LocalContext env; 1397 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 1398 v8::Handle<Value> otto = Script::Compile(v8_str( 1399 "try { with (obj) { otto; } } catch (e) { e; }"))->Run(); 1400 CHECK_EQ(v8_str("otto"), otto); 1401 v8::Handle<Value> netto = Script::Compile(v8_str( 1402 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run(); 1403 CHECK_EQ(v8_str("netto"), netto); 1404} 1405 1406 1407THREADED_TEST(FunctionPrototype) { 1408 v8::HandleScope scope; 1409 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New(); 1410 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321)); 1411 LocalContext env; 1412 env->Global()->Set(v8_str("Foo"), Foo->GetFunction()); 1413 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak")); 1414 CHECK_EQ(script->Run()->Int32Value(), 321); 1415} 1416 1417 1418THREADED_TEST(InternalFields) { 1419 v8::HandleScope scope; 1420 LocalContext env; 1421 1422 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1423 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1424 instance_templ->SetInternalFieldCount(1); 1425 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1426 CHECK_EQ(1, obj->InternalFieldCount()); 1427 CHECK(obj->GetInternalField(0)->IsUndefined()); 1428 obj->SetInternalField(0, v8_num(17)); 1429 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value()); 1430} 1431 1432 1433THREADED_TEST(GlobalObjectInternalFields) { 1434 v8::HandleScope scope; 1435 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 1436 global_template->SetInternalFieldCount(1); 1437 LocalContext env(NULL, global_template); 1438 v8::Handle<v8::Object> global_proxy = env->Global(); 1439 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); 1440 CHECK_EQ(1, global->InternalFieldCount()); 1441 CHECK(global->GetInternalField(0)->IsUndefined()); 1442 global->SetInternalField(0, v8_num(17)); 1443 CHECK_EQ(17, global->GetInternalField(0)->Int32Value()); 1444} 1445 1446 1447THREADED_TEST(InternalFieldsNativePointers) { 1448 v8::HandleScope scope; 1449 LocalContext env; 1450 1451 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1452 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1453 instance_templ->SetInternalFieldCount(1); 1454 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1455 CHECK_EQ(1, obj->InternalFieldCount()); 1456 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1457 1458 char* data = new char[100]; 1459 1460 void* aligned = data; 1461 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1); 1462 void* unaligned = data + 1; 1463 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1); 1464 1465 // Check reading and writing aligned pointers. 1466 obj->SetPointerInInternalField(0, aligned); 1467 i::Heap::CollectAllGarbage(false); 1468 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1469 1470 // Check reading and writing unaligned pointers. 1471 obj->SetPointerInInternalField(0, unaligned); 1472 i::Heap::CollectAllGarbage(false); 1473 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1474 1475 delete[] data; 1476} 1477 1478 1479THREADED_TEST(InternalFieldsNativePointersAndExternal) { 1480 v8::HandleScope scope; 1481 LocalContext env; 1482 1483 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1484 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1485 instance_templ->SetInternalFieldCount(1); 1486 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1487 CHECK_EQ(1, obj->InternalFieldCount()); 1488 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1489 1490 char* data = new char[100]; 1491 1492 void* aligned = data; 1493 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1); 1494 void* unaligned = data + 1; 1495 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1); 1496 1497 obj->SetPointerInInternalField(0, aligned); 1498 i::Heap::CollectAllGarbage(false); 1499 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0))); 1500 1501 obj->SetPointerInInternalField(0, unaligned); 1502 i::Heap::CollectAllGarbage(false); 1503 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0))); 1504 1505 obj->SetInternalField(0, v8::External::Wrap(aligned)); 1506 i::Heap::CollectAllGarbage(false); 1507 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1508 1509 obj->SetInternalField(0, v8::External::Wrap(unaligned)); 1510 i::Heap::CollectAllGarbage(false); 1511 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1512 1513 delete[] data; 1514} 1515 1516 1517THREADED_TEST(IdentityHash) { 1518 v8::HandleScope scope; 1519 LocalContext env; 1520 1521 // Ensure that the test starts with an fresh heap to test whether the hash 1522 // code is based on the address. 1523 i::Heap::CollectAllGarbage(false); 1524 Local<v8::Object> obj = v8::Object::New(); 1525 int hash = obj->GetIdentityHash(); 1526 int hash1 = obj->GetIdentityHash(); 1527 CHECK_EQ(hash, hash1); 1528 int hash2 = v8::Object::New()->GetIdentityHash(); 1529 // Since the identity hash is essentially a random number two consecutive 1530 // objects should not be assigned the same hash code. If the test below fails 1531 // the random number generator should be evaluated. 1532 CHECK_NE(hash, hash2); 1533 i::Heap::CollectAllGarbage(false); 1534 int hash3 = v8::Object::New()->GetIdentityHash(); 1535 // Make sure that the identity hash is not based on the initial address of 1536 // the object alone. If the test below fails the random number generator 1537 // should be evaluated. 1538 CHECK_NE(hash, hash3); 1539 int hash4 = obj->GetIdentityHash(); 1540 CHECK_EQ(hash, hash4); 1541} 1542 1543 1544THREADED_TEST(HiddenProperties) { 1545 v8::HandleScope scope; 1546 LocalContext env; 1547 1548 v8::Local<v8::Object> obj = v8::Object::New(); 1549 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 1550 v8::Local<v8::String> empty = v8_str(""); 1551 v8::Local<v8::String> prop_name = v8_str("prop_name"); 1552 1553 i::Heap::CollectAllGarbage(false); 1554 1555 // Make sure delete of a non-existent hidden value works 1556 CHECK(obj->DeleteHiddenValue(key)); 1557 1558 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503))); 1559 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value()); 1560 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002))); 1561 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1562 1563 i::Heap::CollectAllGarbage(false); 1564 1565 // Make sure we do not find the hidden property. 1566 CHECK(!obj->Has(empty)); 1567 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1568 CHECK(obj->Get(empty)->IsUndefined()); 1569 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1570 CHECK(obj->Set(empty, v8::Integer::New(2003))); 1571 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1572 CHECK_EQ(2003, obj->Get(empty)->Int32Value()); 1573 1574 i::Heap::CollectAllGarbage(false); 1575 1576 // Add another property and delete it afterwards to force the object in 1577 // slow case. 1578 CHECK(obj->Set(prop_name, v8::Integer::New(2008))); 1579 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1580 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value()); 1581 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1582 CHECK(obj->Delete(prop_name)); 1583 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1584 1585 i::Heap::CollectAllGarbage(false); 1586 1587 CHECK(obj->DeleteHiddenValue(key)); 1588 CHECK(obj->GetHiddenValue(key).IsEmpty()); 1589} 1590 1591 1592static bool interceptor_for_hidden_properties_called; 1593static v8::Handle<Value> InterceptorForHiddenProperties( 1594 Local<String> name, const AccessorInfo& info) { 1595 interceptor_for_hidden_properties_called = true; 1596 return v8::Handle<Value>(); 1597} 1598 1599 1600THREADED_TEST(HiddenPropertiesWithInterceptors) { 1601 v8::HandleScope scope; 1602 LocalContext context; 1603 1604 interceptor_for_hidden_properties_called = false; 1605 1606 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 1607 1608 // Associate an interceptor with an object and start setting hidden values. 1609 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 1610 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 1611 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties); 1612 Local<v8::Function> function = fun_templ->GetFunction(); 1613 Local<v8::Object> obj = function->NewInstance(); 1614 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302))); 1615 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value()); 1616 CHECK(!interceptor_for_hidden_properties_called); 1617} 1618 1619 1620THREADED_TEST(External) { 1621 v8::HandleScope scope; 1622 int x = 3; 1623 Local<v8::External> ext = v8::External::New(&x); 1624 LocalContext env; 1625 env->Global()->Set(v8_str("ext"), ext); 1626 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run(); 1627 v8::Handle<v8::External> reext = reext_obj.As<v8::External>(); 1628 int* ptr = static_cast<int*>(reext->Value()); 1629 CHECK_EQ(x, 3); 1630 *ptr = 10; 1631 CHECK_EQ(x, 10); 1632 1633 // Make sure unaligned pointers are wrapped properly. 1634 char* data = i::StrDup("0123456789"); 1635 Local<v8::Value> zero = v8::External::Wrap(&data[0]); 1636 Local<v8::Value> one = v8::External::Wrap(&data[1]); 1637 Local<v8::Value> two = v8::External::Wrap(&data[2]); 1638 Local<v8::Value> three = v8::External::Wrap(&data[3]); 1639 1640 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero)); 1641 CHECK_EQ('0', *char_ptr); 1642 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one)); 1643 CHECK_EQ('1', *char_ptr); 1644 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two)); 1645 CHECK_EQ('2', *char_ptr); 1646 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three)); 1647 CHECK_EQ('3', *char_ptr); 1648 i::DeleteArray(data); 1649} 1650 1651 1652THREADED_TEST(GlobalHandle) { 1653 v8::Persistent<String> global; 1654 { 1655 v8::HandleScope scope; 1656 Local<String> str = v8_str("str"); 1657 global = v8::Persistent<String>::New(str); 1658 } 1659 CHECK_EQ(global->Length(), 3); 1660 global.Dispose(); 1661} 1662 1663 1664THREADED_TEST(ScriptException) { 1665 v8::HandleScope scope; 1666 LocalContext env; 1667 Local<Script> script = Script::Compile(v8_str("throw 'panama!';")); 1668 v8::TryCatch try_catch; 1669 Local<Value> result = script->Run(); 1670 CHECK(result.IsEmpty()); 1671 CHECK(try_catch.HasCaught()); 1672 String::AsciiValue exception_value(try_catch.Exception()); 1673 CHECK_EQ(*exception_value, "panama!"); 1674} 1675 1676 1677bool message_received; 1678 1679 1680static void check_message(v8::Handle<v8::Message> message, 1681 v8::Handle<Value> data) { 1682 CHECK_EQ(5.76, data->NumberValue()); 1683 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue()); 1684 CHECK_EQ(7.56, message->GetScriptData()->NumberValue()); 1685 message_received = true; 1686} 1687 1688 1689THREADED_TEST(MessageHandlerData) { 1690 message_received = false; 1691 v8::HandleScope scope; 1692 CHECK(!message_received); 1693 v8::V8::AddMessageListener(check_message, v8_num(5.76)); 1694 LocalContext context; 1695 v8::ScriptOrigin origin = 1696 v8::ScriptOrigin(v8_str("6.75")); 1697 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"), 1698 &origin); 1699 script->SetData(v8_str("7.56")); 1700 script->Run(); 1701 CHECK(message_received); 1702 // clear out the message listener 1703 v8::V8::RemoveMessageListeners(check_message); 1704} 1705 1706 1707THREADED_TEST(GetSetProperty) { 1708 v8::HandleScope scope; 1709 LocalContext context; 1710 context->Global()->Set(v8_str("foo"), v8_num(14)); 1711 context->Global()->Set(v8_str("12"), v8_num(92)); 1712 context->Global()->Set(v8::Integer::New(16), v8_num(32)); 1713 context->Global()->Set(v8_num(13), v8_num(56)); 1714 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run(); 1715 CHECK_EQ(14, foo->Int32Value()); 1716 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run(); 1717 CHECK_EQ(92, twelve->Int32Value()); 1718 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run(); 1719 CHECK_EQ(32, sixteen->Int32Value()); 1720 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run(); 1721 CHECK_EQ(56, thirteen->Int32Value()); 1722 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value()); 1723 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value()); 1724 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value()); 1725 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value()); 1726 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value()); 1727 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value()); 1728 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value()); 1729 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value()); 1730 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value()); 1731} 1732 1733 1734THREADED_TEST(PropertyAttributes) { 1735 v8::HandleScope scope; 1736 LocalContext context; 1737 // read-only 1738 Local<String> prop = v8_str("read_only"); 1739 context->Global()->Set(prop, v8_num(7), v8::ReadOnly); 1740 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1741 Script::Compile(v8_str("read_only = 9"))->Run(); 1742 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1743 context->Global()->Set(prop, v8_num(10)); 1744 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1745 // dont-delete 1746 prop = v8_str("dont_delete"); 1747 context->Global()->Set(prop, v8_num(13), v8::DontDelete); 1748 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 1749 Script::Compile(v8_str("delete dont_delete"))->Run(); 1750 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 1751} 1752 1753 1754THREADED_TEST(Array) { 1755 v8::HandleScope scope; 1756 LocalContext context; 1757 Local<v8::Array> array = v8::Array::New(); 1758 CHECK_EQ(0, array->Length()); 1759 CHECK(array->Get(0)->IsUndefined()); 1760 CHECK(!array->Has(0)); 1761 CHECK(array->Get(100)->IsUndefined()); 1762 CHECK(!array->Has(100)); 1763 array->Set(2, v8_num(7)); 1764 CHECK_EQ(3, array->Length()); 1765 CHECK(!array->Has(0)); 1766 CHECK(!array->Has(1)); 1767 CHECK(array->Has(2)); 1768 CHECK_EQ(7, array->Get(2)->Int32Value()); 1769 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run(); 1770 Local<v8::Array> arr = obj.As<v8::Array>(); 1771 CHECK_EQ(3, arr->Length()); 1772 CHECK_EQ(1, arr->Get(0)->Int32Value()); 1773 CHECK_EQ(2, arr->Get(1)->Int32Value()); 1774 CHECK_EQ(3, arr->Get(2)->Int32Value()); 1775} 1776 1777 1778v8::Handle<Value> HandleF(const v8::Arguments& args) { 1779 v8::HandleScope scope; 1780 ApiTestFuzzer::Fuzz(); 1781 Local<v8::Array> result = v8::Array::New(args.Length()); 1782 for (int i = 0; i < args.Length(); i++) 1783 result->Set(i, args[i]); 1784 return scope.Close(result); 1785} 1786 1787 1788THREADED_TEST(Vector) { 1789 v8::HandleScope scope; 1790 Local<ObjectTemplate> global = ObjectTemplate::New(); 1791 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF)); 1792 LocalContext context(0, global); 1793 1794 const char* fun = "f()"; 1795 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>(); 1796 CHECK_EQ(0, a0->Length()); 1797 1798 const char* fun2 = "f(11)"; 1799 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>(); 1800 CHECK_EQ(1, a1->Length()); 1801 CHECK_EQ(11, a1->Get(0)->Int32Value()); 1802 1803 const char* fun3 = "f(12, 13)"; 1804 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>(); 1805 CHECK_EQ(2, a2->Length()); 1806 CHECK_EQ(12, a2->Get(0)->Int32Value()); 1807 CHECK_EQ(13, a2->Get(1)->Int32Value()); 1808 1809 const char* fun4 = "f(14, 15, 16)"; 1810 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>(); 1811 CHECK_EQ(3, a3->Length()); 1812 CHECK_EQ(14, a3->Get(0)->Int32Value()); 1813 CHECK_EQ(15, a3->Get(1)->Int32Value()); 1814 CHECK_EQ(16, a3->Get(2)->Int32Value()); 1815 1816 const char* fun5 = "f(17, 18, 19, 20)"; 1817 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>(); 1818 CHECK_EQ(4, a4->Length()); 1819 CHECK_EQ(17, a4->Get(0)->Int32Value()); 1820 CHECK_EQ(18, a4->Get(1)->Int32Value()); 1821 CHECK_EQ(19, a4->Get(2)->Int32Value()); 1822 CHECK_EQ(20, a4->Get(3)->Int32Value()); 1823} 1824 1825 1826THREADED_TEST(FunctionCall) { 1827 v8::HandleScope scope; 1828 LocalContext context; 1829 CompileRun( 1830 "function Foo() {" 1831 " var result = [];" 1832 " for (var i = 0; i < arguments.length; i++) {" 1833 " result.push(arguments[i]);" 1834 " }" 1835 " return result;" 1836 "}"); 1837 Local<Function> Foo = 1838 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 1839 1840 v8::Handle<Value>* args0 = NULL; 1841 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0)); 1842 CHECK_EQ(0, a0->Length()); 1843 1844 v8::Handle<Value> args1[] = { v8_num(1.1) }; 1845 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1)); 1846 CHECK_EQ(1, a1->Length()); 1847 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 1848 1849 v8::Handle<Value> args2[] = { v8_num(2.2), 1850 v8_num(3.3) }; 1851 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2)); 1852 CHECK_EQ(2, a2->Length()); 1853 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 1854 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 1855 1856 v8::Handle<Value> args3[] = { v8_num(4.4), 1857 v8_num(5.5), 1858 v8_num(6.6) }; 1859 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3)); 1860 CHECK_EQ(3, a3->Length()); 1861 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 1862 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 1863 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 1864 1865 v8::Handle<Value> args4[] = { v8_num(7.7), 1866 v8_num(8.8), 1867 v8_num(9.9), 1868 v8_num(10.11) }; 1869 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4)); 1870 CHECK_EQ(4, a4->Length()); 1871 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 1872 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 1873 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 1874 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 1875} 1876 1877 1878static const char* js_code_causing_out_of_memory = 1879 "var a = new Array(); while(true) a.push(a);"; 1880 1881 1882// These tests run for a long time and prevent us from running tests 1883// that come after them so they cannot run in parallel. 1884TEST(OutOfMemory) { 1885 // It's not possible to read a snapshot into a heap with different dimensions. 1886 if (v8::internal::Snapshot::IsEnabled()) return; 1887 // Set heap limits. 1888 static const int K = 1024; 1889 v8::ResourceConstraints constraints; 1890 constraints.set_max_young_space_size(256 * K); 1891 constraints.set_max_old_space_size(4 * K * K); 1892 v8::SetResourceConstraints(&constraints); 1893 1894 // Execute a script that causes out of memory. 1895 v8::HandleScope scope; 1896 LocalContext context; 1897 v8::V8::IgnoreOutOfMemoryException(); 1898 Local<Script> script = 1899 Script::Compile(String::New(js_code_causing_out_of_memory)); 1900 Local<Value> result = script->Run(); 1901 1902 // Check for out of memory state. 1903 CHECK(result.IsEmpty()); 1904 CHECK(context->HasOutOfMemoryException()); 1905} 1906 1907 1908v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) { 1909 ApiTestFuzzer::Fuzz(); 1910 1911 v8::HandleScope scope; 1912 LocalContext context; 1913 Local<Script> script = 1914 Script::Compile(String::New(js_code_causing_out_of_memory)); 1915 Local<Value> result = script->Run(); 1916 1917 // Check for out of memory state. 1918 CHECK(result.IsEmpty()); 1919 CHECK(context->HasOutOfMemoryException()); 1920 1921 return result; 1922} 1923 1924 1925TEST(OutOfMemoryNested) { 1926 // It's not possible to read a snapshot into a heap with different dimensions. 1927 if (v8::internal::Snapshot::IsEnabled()) return; 1928 // Set heap limits. 1929 static const int K = 1024; 1930 v8::ResourceConstraints constraints; 1931 constraints.set_max_young_space_size(256 * K); 1932 constraints.set_max_old_space_size(4 * K * K); 1933 v8::SetResourceConstraints(&constraints); 1934 1935 v8::HandleScope scope; 1936 Local<ObjectTemplate> templ = ObjectTemplate::New(); 1937 templ->Set(v8_str("ProvokeOutOfMemory"), 1938 v8::FunctionTemplate::New(ProvokeOutOfMemory)); 1939 LocalContext context(0, templ); 1940 v8::V8::IgnoreOutOfMemoryException(); 1941 Local<Value> result = CompileRun( 1942 "var thrown = false;" 1943 "try {" 1944 " ProvokeOutOfMemory();" 1945 "} catch (e) {" 1946 " thrown = true;" 1947 "}"); 1948 // Check for out of memory state. 1949 CHECK(result.IsEmpty()); 1950 CHECK(context->HasOutOfMemoryException()); 1951} 1952 1953 1954TEST(HugeConsStringOutOfMemory) { 1955 // It's not possible to read a snapshot into a heap with different dimensions. 1956 if (v8::internal::Snapshot::IsEnabled()) return; 1957 v8::HandleScope scope; 1958 LocalContext context; 1959 // Set heap limits. 1960 static const int K = 1024; 1961 v8::ResourceConstraints constraints; 1962 constraints.set_max_young_space_size(256 * K); 1963 constraints.set_max_old_space_size(2 * K * K); 1964 v8::SetResourceConstraints(&constraints); 1965 1966 // Execute a script that causes out of memory. 1967 v8::V8::IgnoreOutOfMemoryException(); 1968 1969 // Build huge string. This should fail with out of memory exception. 1970 Local<Value> result = CompileRun( 1971 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();" 1972 "for (var i = 0; i < 22; i++) { str = str + str; }"); 1973 1974 // Check for out of memory state. 1975 CHECK(result.IsEmpty()); 1976 CHECK(context->HasOutOfMemoryException()); 1977} 1978 1979 1980THREADED_TEST(ConstructCall) { 1981 v8::HandleScope scope; 1982 LocalContext context; 1983 CompileRun( 1984 "function Foo() {" 1985 " var result = [];" 1986 " for (var i = 0; i < arguments.length; i++) {" 1987 " result.push(arguments[i]);" 1988 " }" 1989 " return result;" 1990 "}"); 1991 Local<Function> Foo = 1992 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 1993 1994 v8::Handle<Value>* args0 = NULL; 1995 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0)); 1996 CHECK_EQ(0, a0->Length()); 1997 1998 v8::Handle<Value> args1[] = { v8_num(1.1) }; 1999 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1)); 2000 CHECK_EQ(1, a1->Length()); 2001 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 2002 2003 v8::Handle<Value> args2[] = { v8_num(2.2), 2004 v8_num(3.3) }; 2005 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2)); 2006 CHECK_EQ(2, a2->Length()); 2007 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 2008 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 2009 2010 v8::Handle<Value> args3[] = { v8_num(4.4), 2011 v8_num(5.5), 2012 v8_num(6.6) }; 2013 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3)); 2014 CHECK_EQ(3, a3->Length()); 2015 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 2016 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 2017 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 2018 2019 v8::Handle<Value> args4[] = { v8_num(7.7), 2020 v8_num(8.8), 2021 v8_num(9.9), 2022 v8_num(10.11) }; 2023 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4)); 2024 CHECK_EQ(4, a4->Length()); 2025 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 2026 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 2027 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 2028 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 2029} 2030 2031 2032static void CheckUncle(v8::TryCatch* try_catch) { 2033 CHECK(try_catch->HasCaught()); 2034 String::AsciiValue str_value(try_catch->Exception()); 2035 CHECK_EQ(*str_value, "uncle?"); 2036 try_catch->Reset(); 2037} 2038 2039 2040THREADED_TEST(ConversionNumber) { 2041 v8::HandleScope scope; 2042 LocalContext env; 2043 // Very large number. 2044 CompileRun("var obj = Math.pow(2,32) * 1237;"); 2045 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2046 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value()); 2047 CHECK_EQ(0, obj->ToInt32()->Value()); 2048 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned. 2049 // Large number. 2050 CompileRun("var obj = -1234567890123;"); 2051 obj = env->Global()->Get(v8_str("obj")); 2052 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value()); 2053 CHECK_EQ(-1912276171, obj->ToInt32()->Value()); 2054 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT 2055 // Small positive integer. 2056 CompileRun("var obj = 42;"); 2057 obj = env->Global()->Get(v8_str("obj")); 2058 CHECK_EQ(42.0, obj->ToNumber()->Value()); 2059 CHECK_EQ(42, obj->ToInt32()->Value()); 2060 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 2061 // Negative integer. 2062 CompileRun("var obj = -37;"); 2063 obj = env->Global()->Get(v8_str("obj")); 2064 CHECK_EQ(-37.0, obj->ToNumber()->Value()); 2065 CHECK_EQ(-37, obj->ToInt32()->Value()); 2066 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT 2067 // Positive non-int32 integer. 2068 CompileRun("var obj = 0x81234567;"); 2069 obj = env->Global()->Get(v8_str("obj")); 2070 CHECK_EQ(2166572391.0, obj->ToNumber()->Value()); 2071 CHECK_EQ(-2128394905, obj->ToInt32()->Value()); 2072 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT 2073 // Fraction. 2074 CompileRun("var obj = 42.3;"); 2075 obj = env->Global()->Get(v8_str("obj")); 2076 CHECK_EQ(42.3, obj->ToNumber()->Value()); 2077 CHECK_EQ(42, obj->ToInt32()->Value()); 2078 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 2079 // Large negative fraction. 2080 CompileRun("var obj = -5726623061.75;"); 2081 obj = env->Global()->Get(v8_str("obj")); 2082 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value()); 2083 CHECK_EQ(-1431655765, obj->ToInt32()->Value()); 2084 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT 2085} 2086 2087 2088THREADED_TEST(isNumberType) { 2089 v8::HandleScope scope; 2090 LocalContext env; 2091 // Very large number. 2092 CompileRun("var obj = Math.pow(2,32) * 1237;"); 2093 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2094 CHECK(!obj->IsInt32()); 2095 CHECK(!obj->IsUint32()); 2096 // Large negative number. 2097 CompileRun("var obj = -1234567890123;"); 2098 obj = env->Global()->Get(v8_str("obj")); 2099 CHECK(!obj->IsInt32()); 2100 CHECK(!obj->IsUint32()); 2101 // Small positive integer. 2102 CompileRun("var obj = 42;"); 2103 obj = env->Global()->Get(v8_str("obj")); 2104 CHECK(obj->IsInt32()); 2105 CHECK(obj->IsUint32()); 2106 // Negative integer. 2107 CompileRun("var obj = -37;"); 2108 obj = env->Global()->Get(v8_str("obj")); 2109 CHECK(obj->IsInt32()); 2110 CHECK(!obj->IsUint32()); 2111 // Positive non-int32 integer. 2112 CompileRun("var obj = 0x81234567;"); 2113 obj = env->Global()->Get(v8_str("obj")); 2114 CHECK(!obj->IsInt32()); 2115 CHECK(obj->IsUint32()); 2116 // Fraction. 2117 CompileRun("var obj = 42.3;"); 2118 obj = env->Global()->Get(v8_str("obj")); 2119 CHECK(!obj->IsInt32()); 2120 CHECK(!obj->IsUint32()); 2121 // Large negative fraction. 2122 CompileRun("var obj = -5726623061.75;"); 2123 obj = env->Global()->Get(v8_str("obj")); 2124 CHECK(!obj->IsInt32()); 2125 CHECK(!obj->IsUint32()); 2126} 2127 2128 2129THREADED_TEST(ConversionException) { 2130 v8::HandleScope scope; 2131 LocalContext env; 2132 CompileRun( 2133 "function TestClass() { };" 2134 "TestClass.prototype.toString = function () { throw 'uncle?'; };" 2135 "var obj = new TestClass();"); 2136 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2137 2138 v8::TryCatch try_catch; 2139 2140 Local<Value> to_string_result = obj->ToString(); 2141 CHECK(to_string_result.IsEmpty()); 2142 CheckUncle(&try_catch); 2143 2144 Local<Value> to_number_result = obj->ToNumber(); 2145 CHECK(to_number_result.IsEmpty()); 2146 CheckUncle(&try_catch); 2147 2148 Local<Value> to_integer_result = obj->ToInteger(); 2149 CHECK(to_integer_result.IsEmpty()); 2150 CheckUncle(&try_catch); 2151 2152 Local<Value> to_uint32_result = obj->ToUint32(); 2153 CHECK(to_uint32_result.IsEmpty()); 2154 CheckUncle(&try_catch); 2155 2156 Local<Value> to_int32_result = obj->ToInt32(); 2157 CHECK(to_int32_result.IsEmpty()); 2158 CheckUncle(&try_catch); 2159 2160 Local<Value> to_object_result = v8::Undefined()->ToObject(); 2161 CHECK(to_object_result.IsEmpty()); 2162 CHECK(try_catch.HasCaught()); 2163 try_catch.Reset(); 2164 2165 int32_t int32_value = obj->Int32Value(); 2166 CHECK_EQ(0, int32_value); 2167 CheckUncle(&try_catch); 2168 2169 uint32_t uint32_value = obj->Uint32Value(); 2170 CHECK_EQ(0, uint32_value); 2171 CheckUncle(&try_catch); 2172 2173 double number_value = obj->NumberValue(); 2174 CHECK_NE(0, IsNaN(number_value)); 2175 CheckUncle(&try_catch); 2176 2177 int64_t integer_value = obj->IntegerValue(); 2178 CHECK_EQ(0.0, static_cast<double>(integer_value)); 2179 CheckUncle(&try_catch); 2180} 2181 2182 2183v8::Handle<Value> ThrowFromC(const v8::Arguments& args) { 2184 ApiTestFuzzer::Fuzz(); 2185 return v8::ThrowException(v8_str("konto")); 2186} 2187 2188 2189v8::Handle<Value> CCatcher(const v8::Arguments& args) { 2190 if (args.Length() < 1) return v8::Boolean::New(false); 2191 v8::HandleScope scope; 2192 v8::TryCatch try_catch; 2193 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run(); 2194 CHECK(!try_catch.HasCaught() || result.IsEmpty()); 2195 return v8::Boolean::New(try_catch.HasCaught()); 2196} 2197 2198 2199THREADED_TEST(APICatch) { 2200 v8::HandleScope scope; 2201 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2202 templ->Set(v8_str("ThrowFromC"), 2203 v8::FunctionTemplate::New(ThrowFromC)); 2204 LocalContext context(0, templ); 2205 CompileRun( 2206 "var thrown = false;" 2207 "try {" 2208 " ThrowFromC();" 2209 "} catch (e) {" 2210 " thrown = true;" 2211 "}"); 2212 Local<Value> thrown = context->Global()->Get(v8_str("thrown")); 2213 CHECK(thrown->BooleanValue()); 2214} 2215 2216 2217THREADED_TEST(APIThrowTryCatch) { 2218 v8::HandleScope scope; 2219 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2220 templ->Set(v8_str("ThrowFromC"), 2221 v8::FunctionTemplate::New(ThrowFromC)); 2222 LocalContext context(0, templ); 2223 v8::TryCatch try_catch; 2224 CompileRun("ThrowFromC();"); 2225 CHECK(try_catch.HasCaught()); 2226} 2227 2228 2229// Test that a try-finally block doesn't shadow a try-catch block 2230// when setting up an external handler. 2231// 2232// BUG(271): Some of the exception propagation does not work on the 2233// ARM simulator because the simulator separates the C++ stack and the 2234// JS stack. This test therefore fails on the simulator. The test is 2235// not threaded to allow the threading tests to run on the simulator. 2236TEST(TryCatchInTryFinally) { 2237 v8::HandleScope scope; 2238 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2239 templ->Set(v8_str("CCatcher"), 2240 v8::FunctionTemplate::New(CCatcher)); 2241 LocalContext context(0, templ); 2242 Local<Value> result = CompileRun("try {" 2243 " try {" 2244 " CCatcher('throw 7;');" 2245 " } finally {" 2246 " }" 2247 "} catch (e) {" 2248 "}"); 2249 CHECK(result->IsTrue()); 2250} 2251 2252 2253static void receive_message(v8::Handle<v8::Message> message, 2254 v8::Handle<v8::Value> data) { 2255 message->Get(); 2256 message_received = true; 2257} 2258 2259 2260TEST(APIThrowMessage) { 2261 message_received = false; 2262 v8::HandleScope scope; 2263 v8::V8::AddMessageListener(receive_message); 2264 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2265 templ->Set(v8_str("ThrowFromC"), 2266 v8::FunctionTemplate::New(ThrowFromC)); 2267 LocalContext context(0, templ); 2268 CompileRun("ThrowFromC();"); 2269 CHECK(message_received); 2270 v8::V8::RemoveMessageListeners(check_message); 2271} 2272 2273 2274TEST(APIThrowMessageAndVerboseTryCatch) { 2275 message_received = false; 2276 v8::HandleScope scope; 2277 v8::V8::AddMessageListener(receive_message); 2278 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2279 templ->Set(v8_str("ThrowFromC"), 2280 v8::FunctionTemplate::New(ThrowFromC)); 2281 LocalContext context(0, templ); 2282 v8::TryCatch try_catch; 2283 try_catch.SetVerbose(true); 2284 Local<Value> result = CompileRun("ThrowFromC();"); 2285 CHECK(try_catch.HasCaught()); 2286 CHECK(result.IsEmpty()); 2287 CHECK(message_received); 2288 v8::V8::RemoveMessageListeners(check_message); 2289} 2290 2291 2292THREADED_TEST(ExternalScriptException) { 2293 v8::HandleScope scope; 2294 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2295 templ->Set(v8_str("ThrowFromC"), 2296 v8::FunctionTemplate::New(ThrowFromC)); 2297 LocalContext context(0, templ); 2298 2299 v8::TryCatch try_catch; 2300 Local<Script> script 2301 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';")); 2302 Local<Value> result = script->Run(); 2303 CHECK(result.IsEmpty()); 2304 CHECK(try_catch.HasCaught()); 2305 String::AsciiValue exception_value(try_catch.Exception()); 2306 CHECK_EQ("konto", *exception_value); 2307} 2308 2309 2310 2311v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) { 2312 ApiTestFuzzer::Fuzz(); 2313 CHECK_EQ(4, args.Length()); 2314 int count = args[0]->Int32Value(); 2315 int cInterval = args[2]->Int32Value(); 2316 if (count == 0) { 2317 return v8::ThrowException(v8_str("FromC")); 2318 } else { 2319 Local<v8::Object> global = Context::GetCurrent()->Global(); 2320 Local<Value> fun = global->Get(v8_str("JSThrowCountDown")); 2321 v8::Handle<Value> argv[] = { v8_num(count - 1), 2322 args[1], 2323 args[2], 2324 args[3] }; 2325 if (count % cInterval == 0) { 2326 v8::TryCatch try_catch; 2327 Local<Value> result = fun.As<Function>()->Call(global, 4, argv); 2328 int expected = args[3]->Int32Value(); 2329 if (try_catch.HasCaught()) { 2330 CHECK_EQ(expected, count); 2331 CHECK(result.IsEmpty()); 2332 CHECK(!i::Top::has_scheduled_exception()); 2333 } else { 2334 CHECK_NE(expected, count); 2335 } 2336 return result; 2337 } else { 2338 return fun.As<Function>()->Call(global, 4, argv); 2339 } 2340 } 2341} 2342 2343 2344v8::Handle<Value> JSCheck(const v8::Arguments& args) { 2345 ApiTestFuzzer::Fuzz(); 2346 CHECK_EQ(3, args.Length()); 2347 bool equality = args[0]->BooleanValue(); 2348 int count = args[1]->Int32Value(); 2349 int expected = args[2]->Int32Value(); 2350 if (equality) { 2351 CHECK_EQ(count, expected); 2352 } else { 2353 CHECK_NE(count, expected); 2354 } 2355 return v8::Undefined(); 2356} 2357 2358 2359THREADED_TEST(EvalInTryFinally) { 2360 v8::HandleScope scope; 2361 LocalContext context; 2362 v8::TryCatch try_catch; 2363 CompileRun("(function() {" 2364 " try {" 2365 " eval('asldkf (*&^&*^');" 2366 " } finally {" 2367 " return;" 2368 " }" 2369 "})()"); 2370 CHECK(!try_catch.HasCaught()); 2371} 2372 2373 2374// This test works by making a stack of alternating JavaScript and C 2375// activations. These activations set up exception handlers with regular 2376// intervals, one interval for C activations and another for JavaScript 2377// activations. When enough activations have been created an exception is 2378// thrown and we check that the right activation catches the exception and that 2379// no other activations do. The right activation is always the topmost one with 2380// a handler, regardless of whether it is in JavaScript or C. 2381// 2382// The notation used to describe a test case looks like this: 2383// 2384// *JS[4] *C[3] @JS[2] C[1] JS[0] 2385// 2386// Each entry is an activation, either JS or C. The index is the count at that 2387// level. Stars identify activations with exception handlers, the @ identifies 2388// the exception handler that should catch the exception. 2389// 2390// BUG(271): Some of the exception propagation does not work on the 2391// ARM simulator because the simulator separates the C++ stack and the 2392// JS stack. This test therefore fails on the simulator. The test is 2393// not threaded to allow the threading tests to run on the simulator. 2394TEST(ExceptionOrder) { 2395 v8::HandleScope scope; 2396 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2397 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck)); 2398 templ->Set(v8_str("CThrowCountDown"), 2399 v8::FunctionTemplate::New(CThrowCountDown)); 2400 LocalContext context(0, templ); 2401 CompileRun( 2402 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {" 2403 " if (count == 0) throw 'FromJS';" 2404 " if (count % jsInterval == 0) {" 2405 " try {" 2406 " var value = CThrowCountDown(count - 1," 2407 " jsInterval," 2408 " cInterval," 2409 " expected);" 2410 " check(false, count, expected);" 2411 " return value;" 2412 " } catch (e) {" 2413 " check(true, count, expected);" 2414 " }" 2415 " } else {" 2416 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);" 2417 " }" 2418 "}"); 2419 Local<Function> fun = 2420 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown"))); 2421 2422 const int argc = 4; 2423 // count jsInterval cInterval expected 2424 2425 // *JS[4] *C[3] @JS[2] C[1] JS[0] 2426 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) }; 2427 fun->Call(fun, argc, a0); 2428 2429 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0] 2430 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) }; 2431 fun->Call(fun, argc, a1); 2432 2433 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0] 2434 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) }; 2435 fun->Call(fun, argc, a2); 2436 2437 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0] 2438 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) }; 2439 fun->Call(fun, argc, a3); 2440 2441 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0] 2442 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) }; 2443 fun->Call(fun, argc, a4); 2444 2445 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0] 2446 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) }; 2447 fun->Call(fun, argc, a5); 2448} 2449 2450 2451v8::Handle<Value> ThrowValue(const v8::Arguments& args) { 2452 ApiTestFuzzer::Fuzz(); 2453 CHECK_EQ(1, args.Length()); 2454 return v8::ThrowException(args[0]); 2455} 2456 2457 2458THREADED_TEST(ThrowValues) { 2459 v8::HandleScope scope; 2460 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2461 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue)); 2462 LocalContext context(0, templ); 2463 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 2464 "function Run(obj) {" 2465 " try {" 2466 " Throw(obj);" 2467 " } catch (e) {" 2468 " return e;" 2469 " }" 2470 " return 'no exception';" 2471 "}" 2472 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];")); 2473 CHECK_EQ(5, result->Length()); 2474 CHECK(result->Get(v8::Integer::New(0))->IsString()); 2475 CHECK(result->Get(v8::Integer::New(1))->IsNumber()); 2476 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value()); 2477 CHECK(result->Get(v8::Integer::New(2))->IsNumber()); 2478 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value()); 2479 CHECK(result->Get(v8::Integer::New(3))->IsNull()); 2480 CHECK(result->Get(v8::Integer::New(4))->IsUndefined()); 2481} 2482 2483 2484THREADED_TEST(CatchZero) { 2485 v8::HandleScope scope; 2486 LocalContext context; 2487 v8::TryCatch try_catch; 2488 CHECK(!try_catch.HasCaught()); 2489 Script::Compile(v8_str("throw 10"))->Run(); 2490 CHECK(try_catch.HasCaught()); 2491 CHECK_EQ(10, try_catch.Exception()->Int32Value()); 2492 try_catch.Reset(); 2493 CHECK(!try_catch.HasCaught()); 2494 Script::Compile(v8_str("throw 0"))->Run(); 2495 CHECK(try_catch.HasCaught()); 2496 CHECK_EQ(0, try_catch.Exception()->Int32Value()); 2497} 2498 2499 2500THREADED_TEST(CatchExceptionFromWith) { 2501 v8::HandleScope scope; 2502 LocalContext context; 2503 v8::TryCatch try_catch; 2504 CHECK(!try_catch.HasCaught()); 2505 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run(); 2506 CHECK(try_catch.HasCaught()); 2507} 2508 2509 2510THREADED_TEST(Equality) { 2511 v8::HandleScope scope; 2512 LocalContext context; 2513 // Check that equality works at all before relying on CHECK_EQ 2514 CHECK(v8_str("a")->Equals(v8_str("a"))); 2515 CHECK(!v8_str("a")->Equals(v8_str("b"))); 2516 2517 CHECK_EQ(v8_str("a"), v8_str("a")); 2518 CHECK_NE(v8_str("a"), v8_str("b")); 2519 CHECK_EQ(v8_num(1), v8_num(1)); 2520 CHECK_EQ(v8_num(1.00), v8_num(1)); 2521 CHECK_NE(v8_num(1), v8_num(2)); 2522 2523 // Assume String is not symbol. 2524 CHECK(v8_str("a")->StrictEquals(v8_str("a"))); 2525 CHECK(!v8_str("a")->StrictEquals(v8_str("b"))); 2526 CHECK(!v8_str("5")->StrictEquals(v8_num(5))); 2527 CHECK(v8_num(1)->StrictEquals(v8_num(1))); 2528 CHECK(!v8_num(1)->StrictEquals(v8_num(2))); 2529 CHECK(v8_num(0)->StrictEquals(v8_num(-0))); 2530 Local<Value> not_a_number = v8_num(i::OS::nan_value()); 2531 CHECK(!not_a_number->StrictEquals(not_a_number)); 2532 CHECK(v8::False()->StrictEquals(v8::False())); 2533 CHECK(!v8::False()->StrictEquals(v8::Undefined())); 2534 2535 v8::Handle<v8::Object> obj = v8::Object::New(); 2536 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj); 2537 CHECK(alias->StrictEquals(obj)); 2538 alias.Dispose(); 2539} 2540 2541 2542THREADED_TEST(MultiRun) { 2543 v8::HandleScope scope; 2544 LocalContext context; 2545 Local<Script> script = Script::Compile(v8_str("x")); 2546 for (int i = 0; i < 10; i++) 2547 script->Run(); 2548} 2549 2550 2551static v8::Handle<Value> GetXValue(Local<String> name, 2552 const AccessorInfo& info) { 2553 ApiTestFuzzer::Fuzz(); 2554 CHECK_EQ(info.Data(), v8_str("donut")); 2555 CHECK_EQ(name, v8_str("x")); 2556 return name; 2557} 2558 2559 2560THREADED_TEST(SimplePropertyRead) { 2561 v8::HandleScope scope; 2562 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2563 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2564 LocalContext context; 2565 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2566 Local<Script> script = Script::Compile(v8_str("obj.x")); 2567 for (int i = 0; i < 10; i++) { 2568 Local<Value> result = script->Run(); 2569 CHECK_EQ(result, v8_str("x")); 2570 } 2571} 2572 2573THREADED_TEST(DefinePropertyOnAPIAccessor) { 2574 v8::HandleScope scope; 2575 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2576 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2577 LocalContext context; 2578 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2579 2580 // Uses getOwnPropertyDescriptor to check the configurable status 2581 Local<Script> script_desc 2582 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( " 2583 "obj, 'x');" 2584 "prop.configurable;")); 2585 Local<Value> result = script_desc->Run(); 2586 CHECK_EQ(result->BooleanValue(), true); 2587 2588 // Redefine get - but still configurable 2589 Local<Script> script_define 2590 = Script::Compile(v8_str("var desc = { get: function(){return 42; }," 2591 " configurable: true };" 2592 "Object.defineProperty(obj, 'x', desc);" 2593 "obj.x")); 2594 result = script_define->Run(); 2595 CHECK_EQ(result, v8_num(42)); 2596 2597 // Check that the accessor is still configurable 2598 result = script_desc->Run(); 2599 CHECK_EQ(result->BooleanValue(), true); 2600 2601 // Redefine to a non-configurable 2602 script_define 2603 = Script::Compile(v8_str("var desc = { get: function(){return 43; }," 2604 " configurable: false };" 2605 "Object.defineProperty(obj, 'x', desc);" 2606 "obj.x")); 2607 result = script_define->Run(); 2608 CHECK_EQ(result, v8_num(43)); 2609 result = script_desc->Run(); 2610 CHECK_EQ(result->BooleanValue(), false); 2611 2612 // Make sure that it is not possible to redefine again 2613 v8::TryCatch try_catch; 2614 result = script_define->Run(); 2615 CHECK(try_catch.HasCaught()); 2616 String::AsciiValue exception_value(try_catch.Exception()); 2617 CHECK_EQ(*exception_value, 2618 "TypeError: Cannot redefine property: defineProperty"); 2619} 2620 2621THREADED_TEST(DefinePropertyOnDefineGetterSetter) { 2622 v8::HandleScope scope; 2623 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2624 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2625 LocalContext context; 2626 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2627 2628 Local<Script> script_desc = Script::Compile(v8_str("var prop =" 2629 "Object.getOwnPropertyDescriptor( " 2630 "obj, 'x');" 2631 "prop.configurable;")); 2632 Local<Value> result = script_desc->Run(); 2633 CHECK_EQ(result->BooleanValue(), true); 2634 2635 Local<Script> script_define = 2636 Script::Compile(v8_str("var desc = {get: function(){return 42; }," 2637 " configurable: true };" 2638 "Object.defineProperty(obj, 'x', desc);" 2639 "obj.x")); 2640 result = script_define->Run(); 2641 CHECK_EQ(result, v8_num(42)); 2642 2643 2644 result = script_desc->Run(); 2645 CHECK_EQ(result->BooleanValue(), true); 2646 2647 2648 script_define = 2649 Script::Compile(v8_str("var desc = {get: function(){return 43; }," 2650 " configurable: false };" 2651 "Object.defineProperty(obj, 'x', desc);" 2652 "obj.x")); 2653 result = script_define->Run(); 2654 CHECK_EQ(result, v8_num(43)); 2655 result = script_desc->Run(); 2656 2657 CHECK_EQ(result->BooleanValue(), false); 2658 2659 v8::TryCatch try_catch; 2660 result = script_define->Run(); 2661 CHECK(try_catch.HasCaught()); 2662 String::AsciiValue exception_value(try_catch.Exception()); 2663 CHECK_EQ(*exception_value, 2664 "TypeError: Cannot redefine property: defineProperty"); 2665} 2666 2667 2668static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context, 2669 char const* name) { 2670 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name))); 2671} 2672 2673 2674THREADED_TEST(DefineAPIAccessorOnObject) { 2675 v8::HandleScope scope; 2676 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2677 LocalContext context; 2678 2679 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 2680 CompileRun("var obj2 = {};"); 2681 2682 CHECK(CompileRun("obj1.x")->IsUndefined()); 2683 CHECK(CompileRun("obj2.x")->IsUndefined()); 2684 2685 CHECK(GetGlobalProperty(&context, "obj1")-> 2686 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2687 2688 ExpectString("obj1.x", "x"); 2689 CHECK(CompileRun("obj2.x")->IsUndefined()); 2690 2691 CHECK(GetGlobalProperty(&context, "obj2")-> 2692 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2693 2694 ExpectString("obj1.x", "x"); 2695 ExpectString("obj2.x", "x"); 2696 2697 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2698 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2699 2700 CompileRun("Object.defineProperty(obj1, 'x'," 2701 "{ get: function() { return 'y'; }, configurable: true })"); 2702 2703 ExpectString("obj1.x", "y"); 2704 ExpectString("obj2.x", "x"); 2705 2706 CompileRun("Object.defineProperty(obj2, 'x'," 2707 "{ get: function() { return 'y'; }, configurable: true })"); 2708 2709 ExpectString("obj1.x", "y"); 2710 ExpectString("obj2.x", "y"); 2711 2712 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2713 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2714 2715 CHECK(GetGlobalProperty(&context, "obj1")-> 2716 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2717 CHECK(GetGlobalProperty(&context, "obj2")-> 2718 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2719 2720 ExpectString("obj1.x", "x"); 2721 ExpectString("obj2.x", "x"); 2722 2723 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2724 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2725 2726 // Define getters/setters, but now make them not configurable. 2727 CompileRun("Object.defineProperty(obj1, 'x'," 2728 "{ get: function() { return 'z'; }, configurable: false })"); 2729 CompileRun("Object.defineProperty(obj2, 'x'," 2730 "{ get: function() { return 'z'; }, configurable: false })"); 2731 2732 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2733 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2734 2735 ExpectString("obj1.x", "z"); 2736 ExpectString("obj2.x", "z"); 2737 2738 CHECK(!GetGlobalProperty(&context, "obj1")-> 2739 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2740 CHECK(!GetGlobalProperty(&context, "obj2")-> 2741 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2742 2743 ExpectString("obj1.x", "z"); 2744 ExpectString("obj2.x", "z"); 2745} 2746 2747 2748THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) { 2749 v8::HandleScope scope; 2750 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2751 LocalContext context; 2752 2753 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 2754 CompileRun("var obj2 = {};"); 2755 2756 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 2757 v8_str("x"), 2758 GetXValue, NULL, 2759 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 2760 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 2761 v8_str("x"), 2762 GetXValue, NULL, 2763 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 2764 2765 ExpectString("obj1.x", "x"); 2766 ExpectString("obj2.x", "x"); 2767 2768 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2769 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2770 2771 CHECK(!GetGlobalProperty(&context, "obj1")-> 2772 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2773 CHECK(!GetGlobalProperty(&context, "obj2")-> 2774 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2775 2776 { 2777 v8::TryCatch try_catch; 2778 CompileRun("Object.defineProperty(obj1, 'x'," 2779 "{get: function() { return 'func'; }})"); 2780 CHECK(try_catch.HasCaught()); 2781 String::AsciiValue exception_value(try_catch.Exception()); 2782 CHECK_EQ(*exception_value, 2783 "TypeError: Cannot redefine property: defineProperty"); 2784 } 2785 { 2786 v8::TryCatch try_catch; 2787 CompileRun("Object.defineProperty(obj2, 'x'," 2788 "{get: function() { return 'func'; }})"); 2789 CHECK(try_catch.HasCaught()); 2790 String::AsciiValue exception_value(try_catch.Exception()); 2791 CHECK_EQ(*exception_value, 2792 "TypeError: Cannot redefine property: defineProperty"); 2793 } 2794} 2795 2796 2797static v8::Handle<Value> Get239Value(Local<String> name, 2798 const AccessorInfo& info) { 2799 ApiTestFuzzer::Fuzz(); 2800 CHECK_EQ(info.Data(), v8_str("donut")); 2801 CHECK_EQ(name, v8_str("239")); 2802 return name; 2803} 2804 2805 2806THREADED_TEST(ElementAPIAccessor) { 2807 v8::HandleScope scope; 2808 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2809 LocalContext context; 2810 2811 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 2812 CompileRun("var obj2 = {};"); 2813 2814 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 2815 v8_str("239"), 2816 Get239Value, NULL, 2817 v8_str("donut"))); 2818 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 2819 v8_str("239"), 2820 Get239Value, NULL, 2821 v8_str("donut"))); 2822 2823 ExpectString("obj1[239]", "239"); 2824 ExpectString("obj2[239]", "239"); 2825 ExpectString("obj1['239']", "239"); 2826 ExpectString("obj2['239']", "239"); 2827} 2828 2829 2830v8::Persistent<Value> xValue; 2831 2832 2833static void SetXValue(Local<String> name, 2834 Local<Value> value, 2835 const AccessorInfo& info) { 2836 CHECK_EQ(value, v8_num(4)); 2837 CHECK_EQ(info.Data(), v8_str("donut")); 2838 CHECK_EQ(name, v8_str("x")); 2839 CHECK(xValue.IsEmpty()); 2840 xValue = v8::Persistent<Value>::New(value); 2841} 2842 2843 2844THREADED_TEST(SimplePropertyWrite) { 2845 v8::HandleScope scope; 2846 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2847 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut")); 2848 LocalContext context; 2849 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2850 Local<Script> script = Script::Compile(v8_str("obj.x = 4")); 2851 for (int i = 0; i < 10; i++) { 2852 CHECK(xValue.IsEmpty()); 2853 script->Run(); 2854 CHECK_EQ(v8_num(4), xValue); 2855 xValue.Dispose(); 2856 xValue = v8::Persistent<Value>(); 2857 } 2858} 2859 2860 2861static v8::Handle<Value> XPropertyGetter(Local<String> property, 2862 const AccessorInfo& info) { 2863 ApiTestFuzzer::Fuzz(); 2864 CHECK(info.Data()->IsUndefined()); 2865 return property; 2866} 2867 2868 2869THREADED_TEST(NamedInterceptorPropertyRead) { 2870 v8::HandleScope scope; 2871 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2872 templ->SetNamedPropertyHandler(XPropertyGetter); 2873 LocalContext context; 2874 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2875 Local<Script> script = Script::Compile(v8_str("obj.x")); 2876 for (int i = 0; i < 10; i++) { 2877 Local<Value> result = script->Run(); 2878 CHECK_EQ(result, v8_str("x")); 2879 } 2880} 2881 2882 2883THREADED_TEST(NamedInterceptorDictionaryIC) { 2884 v8::HandleScope scope; 2885 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2886 templ->SetNamedPropertyHandler(XPropertyGetter); 2887 LocalContext context; 2888 // Create an object with a named interceptor. 2889 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance()); 2890 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x")); 2891 for (int i = 0; i < 10; i++) { 2892 Local<Value> result = script->Run(); 2893 CHECK_EQ(result, v8_str("x")); 2894 } 2895 // Create a slow case object and a function accessing a property in 2896 // that slow case object (with dictionary probing in generated 2897 // code). Then force object with a named interceptor into slow-case, 2898 // pass it to the function, and check that the interceptor is called 2899 // instead of accessing the local property. 2900 Local<Value> result = 2901 CompileRun("function get_x(o) { return o.x; };" 2902 "var obj = { x : 42, y : 0 };" 2903 "delete obj.y;" 2904 "for (var i = 0; i < 10; i++) get_x(obj);" 2905 "interceptor_obj.x = 42;" 2906 "interceptor_obj.y = 10;" 2907 "delete interceptor_obj.y;" 2908 "get_x(interceptor_obj)"); 2909 CHECK_EQ(result, v8_str("x")); 2910} 2911 2912 2913static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property, 2914 const AccessorInfo& info) { 2915 // Set x on the prototype object and do not handle the get request. 2916 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype(); 2917 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23)); 2918 return v8::Handle<Value>(); 2919} 2920 2921 2922// This is a regression test for http://crbug.com/20104. Map 2923// transitions should not interfere with post interceptor lookup. 2924THREADED_TEST(NamedInterceptorMapTransitionRead) { 2925 v8::HandleScope scope; 2926 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(); 2927 Local<v8::ObjectTemplate> instance_template 2928 = function_template->InstanceTemplate(); 2929 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter); 2930 LocalContext context; 2931 context->Global()->Set(v8_str("F"), function_template->GetFunction()); 2932 // Create an instance of F and introduce a map transition for x. 2933 CompileRun("var o = new F(); o.x = 23;"); 2934 // Create an instance of F and invoke the getter. The result should be 23. 2935 Local<Value> result = CompileRun("o = new F(); o.x"); 2936 CHECK_EQ(result->Int32Value(), 23); 2937} 2938 2939 2940static v8::Handle<Value> IndexedPropertyGetter(uint32_t index, 2941 const AccessorInfo& info) { 2942 ApiTestFuzzer::Fuzz(); 2943 if (index == 37) { 2944 return v8::Handle<Value>(v8_num(625)); 2945 } 2946 return v8::Handle<Value>(); 2947} 2948 2949 2950static v8::Handle<Value> IndexedPropertySetter(uint32_t index, 2951 Local<Value> value, 2952 const AccessorInfo& info) { 2953 ApiTestFuzzer::Fuzz(); 2954 if (index == 39) { 2955 return value; 2956 } 2957 return v8::Handle<Value>(); 2958} 2959 2960 2961THREADED_TEST(IndexedInterceptorWithIndexedAccessor) { 2962 v8::HandleScope scope; 2963 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2964 templ->SetIndexedPropertyHandler(IndexedPropertyGetter, 2965 IndexedPropertySetter); 2966 LocalContext context; 2967 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2968 Local<Script> getter_script = Script::Compile(v8_str( 2969 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];")); 2970 Local<Script> setter_script = Script::Compile(v8_str( 2971 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});" 2972 "obj[17] = 23;" 2973 "obj.foo;")); 2974 Local<Script> interceptor_setter_script = Script::Compile(v8_str( 2975 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});" 2976 "obj[39] = 47;" 2977 "obj.foo;")); // This setter should not run, due to the interceptor. 2978 Local<Script> interceptor_getter_script = Script::Compile(v8_str( 2979 "obj[37];")); 2980 Local<Value> result = getter_script->Run(); 2981 CHECK_EQ(v8_num(5), result); 2982 result = setter_script->Run(); 2983 CHECK_EQ(v8_num(23), result); 2984 result = interceptor_setter_script->Run(); 2985 CHECK_EQ(v8_num(23), result); 2986 result = interceptor_getter_script->Run(); 2987 CHECK_EQ(v8_num(625), result); 2988} 2989 2990 2991static v8::Handle<Value> IdentityIndexedPropertyGetter( 2992 uint32_t index, 2993 const AccessorInfo& info) { 2994 return v8::Integer::New(index); 2995} 2996 2997 2998THREADED_TEST(IndexedInterceptorWithNoSetter) { 2999 v8::HandleScope scope; 3000 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3001 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3002 3003 LocalContext context; 3004 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3005 3006 const char* code = 3007 "try {" 3008 " obj[0] = 239;" 3009 " for (var i = 0; i < 100; i++) {" 3010 " var v = obj[0];" 3011 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;" 3012 " }" 3013 " 'PASSED'" 3014 "} catch(e) {" 3015 " e" 3016 "}"; 3017 ExpectString(code, "PASSED"); 3018} 3019 3020 3021THREADED_TEST(IndexedInterceptorWithAccessorCheck) { 3022 v8::HandleScope scope; 3023 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3024 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3025 3026 LocalContext context; 3027 Local<v8::Object> obj = templ->NewInstance(); 3028 obj->TurnOnAccessCheck(); 3029 context->Global()->Set(v8_str("obj"), obj); 3030 3031 const char* code = 3032 "try {" 3033 " for (var i = 0; i < 100; i++) {" 3034 " var v = obj[0];" 3035 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;" 3036 " }" 3037 " 'PASSED'" 3038 "} catch(e) {" 3039 " e" 3040 "}"; 3041 ExpectString(code, "PASSED"); 3042} 3043 3044 3045THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) { 3046 i::FLAG_allow_natives_syntax = true; 3047 v8::HandleScope scope; 3048 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3049 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3050 3051 LocalContext context; 3052 Local<v8::Object> obj = templ->NewInstance(); 3053 context->Global()->Set(v8_str("obj"), obj); 3054 3055 const char* code = 3056 "try {" 3057 " for (var i = 0; i < 100; i++) {" 3058 " var expected = i;" 3059 " if (i == 5) {" 3060 " %EnableAccessChecks(obj);" 3061 " expected = undefined;" 3062 " }" 3063 " var v = obj[i];" 3064 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3065 " if (i == 5) %DisableAccessChecks(obj);" 3066 " }" 3067 " 'PASSED'" 3068 "} catch(e) {" 3069 " e" 3070 "}"; 3071 ExpectString(code, "PASSED"); 3072} 3073 3074 3075THREADED_TEST(IndexedInterceptorWithDifferentIndices) { 3076 v8::HandleScope scope; 3077 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3078 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3079 3080 LocalContext context; 3081 Local<v8::Object> obj = templ->NewInstance(); 3082 context->Global()->Set(v8_str("obj"), obj); 3083 3084 const char* code = 3085 "try {" 3086 " for (var i = 0; i < 100; i++) {" 3087 " var v = obj[i];" 3088 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 3089 " }" 3090 " 'PASSED'" 3091 "} catch(e) {" 3092 " e" 3093 "}"; 3094 ExpectString(code, "PASSED"); 3095} 3096 3097 3098THREADED_TEST(IndexedInterceptorWithNotSmiLookup) { 3099 v8::HandleScope scope; 3100 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3101 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3102 3103 LocalContext context; 3104 Local<v8::Object> obj = templ->NewInstance(); 3105 context->Global()->Set(v8_str("obj"), obj); 3106 3107 const char* code = 3108 "try {" 3109 " for (var i = 0; i < 100; i++) {" 3110 " var expected = i;" 3111 " if (i == 50) {" 3112 " i = 'foobar';" 3113 " expected = undefined;" 3114 " }" 3115 " var v = obj[i];" 3116 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3117 " }" 3118 " 'PASSED'" 3119 "} catch(e) {" 3120 " e" 3121 "}"; 3122 ExpectString(code, "PASSED"); 3123} 3124 3125 3126THREADED_TEST(IndexedInterceptorGoingMegamorphic) { 3127 v8::HandleScope scope; 3128 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3129 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3130 3131 LocalContext context; 3132 Local<v8::Object> obj = templ->NewInstance(); 3133 context->Global()->Set(v8_str("obj"), obj); 3134 3135 const char* code = 3136 "var original = obj;" 3137 "try {" 3138 " for (var i = 0; i < 100; i++) {" 3139 " var expected = i;" 3140 " if (i == 50) {" 3141 " obj = {50: 'foobar'};" 3142 " expected = 'foobar';" 3143 " }" 3144 " var v = obj[i];" 3145 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3146 " if (i == 50) obj = original;" 3147 " }" 3148 " 'PASSED'" 3149 "} catch(e) {" 3150 " e" 3151 "}"; 3152 ExpectString(code, "PASSED"); 3153} 3154 3155 3156THREADED_TEST(IndexedInterceptorReceiverTurningSmi) { 3157 v8::HandleScope scope; 3158 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3159 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3160 3161 LocalContext context; 3162 Local<v8::Object> obj = templ->NewInstance(); 3163 context->Global()->Set(v8_str("obj"), obj); 3164 3165 const char* code = 3166 "var original = obj;" 3167 "try {" 3168 " for (var i = 0; i < 100; i++) {" 3169 " var expected = i;" 3170 " if (i == 5) {" 3171 " obj = 239;" 3172 " expected = undefined;" 3173 " }" 3174 " var v = obj[i];" 3175 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3176 " if (i == 5) obj = original;" 3177 " }" 3178 " 'PASSED'" 3179 "} catch(e) {" 3180 " e" 3181 "}"; 3182 ExpectString(code, "PASSED"); 3183} 3184 3185 3186THREADED_TEST(IndexedInterceptorOnProto) { 3187 v8::HandleScope scope; 3188 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3189 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3190 3191 LocalContext context; 3192 Local<v8::Object> obj = templ->NewInstance(); 3193 context->Global()->Set(v8_str("obj"), obj); 3194 3195 const char* code = 3196 "var o = {__proto__: obj};" 3197 "try {" 3198 " for (var i = 0; i < 100; i++) {" 3199 " var v = o[i];" 3200 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 3201 " }" 3202 " 'PASSED'" 3203 "} catch(e) {" 3204 " e" 3205 "}"; 3206 ExpectString(code, "PASSED"); 3207} 3208 3209 3210THREADED_TEST(MultiContexts) { 3211 v8::HandleScope scope; 3212 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New(); 3213 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler)); 3214 3215 Local<String> password = v8_str("Password"); 3216 3217 // Create an environment 3218 LocalContext context0(0, templ); 3219 context0->SetSecurityToken(password); 3220 v8::Handle<v8::Object> global0 = context0->Global(); 3221 global0->Set(v8_str("custom"), v8_num(1234)); 3222 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 3223 3224 // Create an independent environment 3225 LocalContext context1(0, templ); 3226 context1->SetSecurityToken(password); 3227 v8::Handle<v8::Object> global1 = context1->Global(); 3228 global1->Set(v8_str("custom"), v8_num(1234)); 3229 CHECK_NE(global0, global1); 3230 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 3231 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value()); 3232 3233 // Now create a new context with the old global 3234 LocalContext context2(0, templ, global1); 3235 context2->SetSecurityToken(password); 3236 v8::Handle<v8::Object> global2 = context2->Global(); 3237 CHECK_EQ(global1, global2); 3238 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value()); 3239 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value()); 3240} 3241 3242 3243THREADED_TEST(FunctionPrototypeAcrossContexts) { 3244 // Make sure that functions created by cloning boilerplates cannot 3245 // communicate through their __proto__ field. 3246 3247 v8::HandleScope scope; 3248 3249 LocalContext env0; 3250 v8::Handle<v8::Object> global0 = 3251 env0->Global(); 3252 v8::Handle<v8::Object> object0 = 3253 global0->Get(v8_str("Object")).As<v8::Object>(); 3254 v8::Handle<v8::Object> tostring0 = 3255 object0->Get(v8_str("toString")).As<v8::Object>(); 3256 v8::Handle<v8::Object> proto0 = 3257 tostring0->Get(v8_str("__proto__")).As<v8::Object>(); 3258 proto0->Set(v8_str("custom"), v8_num(1234)); 3259 3260 LocalContext env1; 3261 v8::Handle<v8::Object> global1 = 3262 env1->Global(); 3263 v8::Handle<v8::Object> object1 = 3264 global1->Get(v8_str("Object")).As<v8::Object>(); 3265 v8::Handle<v8::Object> tostring1 = 3266 object1->Get(v8_str("toString")).As<v8::Object>(); 3267 v8::Handle<v8::Object> proto1 = 3268 tostring1->Get(v8_str("__proto__")).As<v8::Object>(); 3269 CHECK(!proto1->Has(v8_str("custom"))); 3270} 3271 3272 3273THREADED_TEST(Regress892105) { 3274 // Make sure that object and array literals created by cloning 3275 // boilerplates cannot communicate through their __proto__ 3276 // field. This is rather difficult to check, but we try to add stuff 3277 // to Object.prototype and Array.prototype and create a new 3278 // environment. This should succeed. 3279 3280 v8::HandleScope scope; 3281 3282 Local<String> source = v8_str("Object.prototype.obj = 1234;" 3283 "Array.prototype.arr = 4567;" 3284 "8901"); 3285 3286 LocalContext env0; 3287 Local<Script> script0 = Script::Compile(source); 3288 CHECK_EQ(8901.0, script0->Run()->NumberValue()); 3289 3290 LocalContext env1; 3291 Local<Script> script1 = Script::Compile(source); 3292 CHECK_EQ(8901.0, script1->Run()->NumberValue()); 3293} 3294 3295 3296THREADED_TEST(UndetectableObject) { 3297 v8::HandleScope scope; 3298 LocalContext env; 3299 3300 Local<v8::FunctionTemplate> desc = 3301 v8::FunctionTemplate::New(0, v8::Handle<Value>()); 3302 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 3303 3304 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 3305 env->Global()->Set(v8_str("undetectable"), obj); 3306 3307 ExpectString("undetectable.toString()", "[object Object]"); 3308 ExpectString("typeof undetectable", "undefined"); 3309 ExpectString("typeof(undetectable)", "undefined"); 3310 ExpectBoolean("typeof undetectable == 'undefined'", true); 3311 ExpectBoolean("typeof undetectable == 'object'", false); 3312 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 3313 ExpectBoolean("!undetectable", true); 3314 3315 ExpectObject("true&&undetectable", obj); 3316 ExpectBoolean("false&&undetectable", false); 3317 ExpectBoolean("true||undetectable", true); 3318 ExpectObject("false||undetectable", obj); 3319 3320 ExpectObject("undetectable&&true", obj); 3321 ExpectObject("undetectable&&false", obj); 3322 ExpectBoolean("undetectable||true", true); 3323 ExpectBoolean("undetectable||false", false); 3324 3325 ExpectBoolean("undetectable==null", true); 3326 ExpectBoolean("null==undetectable", true); 3327 ExpectBoolean("undetectable==undefined", true); 3328 ExpectBoolean("undefined==undetectable", true); 3329 ExpectBoolean("undetectable==undetectable", true); 3330 3331 3332 ExpectBoolean("undetectable===null", false); 3333 ExpectBoolean("null===undetectable", false); 3334 ExpectBoolean("undetectable===undefined", false); 3335 ExpectBoolean("undefined===undetectable", false); 3336 ExpectBoolean("undetectable===undetectable", true); 3337} 3338 3339 3340THREADED_TEST(UndetectableString) { 3341 v8::HandleScope scope; 3342 LocalContext env; 3343 3344 Local<String> obj = String::NewUndetectable("foo"); 3345 env->Global()->Set(v8_str("undetectable"), obj); 3346 3347 ExpectString("undetectable", "foo"); 3348 ExpectString("typeof undetectable", "undefined"); 3349 ExpectString("typeof(undetectable)", "undefined"); 3350 ExpectBoolean("typeof undetectable == 'undefined'", true); 3351 ExpectBoolean("typeof undetectable == 'string'", false); 3352 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 3353 ExpectBoolean("!undetectable", true); 3354 3355 ExpectObject("true&&undetectable", obj); 3356 ExpectBoolean("false&&undetectable", false); 3357 ExpectBoolean("true||undetectable", true); 3358 ExpectObject("false||undetectable", obj); 3359 3360 ExpectObject("undetectable&&true", obj); 3361 ExpectObject("undetectable&&false", obj); 3362 ExpectBoolean("undetectable||true", true); 3363 ExpectBoolean("undetectable||false", false); 3364 3365 ExpectBoolean("undetectable==null", true); 3366 ExpectBoolean("null==undetectable", true); 3367 ExpectBoolean("undetectable==undefined", true); 3368 ExpectBoolean("undefined==undetectable", true); 3369 ExpectBoolean("undetectable==undetectable", true); 3370 3371 3372 ExpectBoolean("undetectable===null", false); 3373 ExpectBoolean("null===undetectable", false); 3374 ExpectBoolean("undetectable===undefined", false); 3375 ExpectBoolean("undefined===undetectable", false); 3376 ExpectBoolean("undetectable===undetectable", true); 3377} 3378 3379 3380template <typename T> static void USE(T) { } 3381 3382 3383// This test is not intended to be run, just type checked. 3384static void PersistentHandles() { 3385 USE(PersistentHandles); 3386 Local<String> str = v8_str("foo"); 3387 v8::Persistent<String> p_str = v8::Persistent<String>::New(str); 3388 USE(p_str); 3389 Local<Script> scr = Script::Compile(v8_str("")); 3390 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr); 3391 USE(p_scr); 3392 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3393 v8::Persistent<ObjectTemplate> p_templ = 3394 v8::Persistent<ObjectTemplate>::New(templ); 3395 USE(p_templ); 3396} 3397 3398 3399static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) { 3400 ApiTestFuzzer::Fuzz(); 3401 return v8::Undefined(); 3402} 3403 3404 3405THREADED_TEST(GlobalObjectTemplate) { 3406 v8::HandleScope handle_scope; 3407 Local<ObjectTemplate> global_template = ObjectTemplate::New(); 3408 global_template->Set(v8_str("JSNI_Log"), 3409 v8::FunctionTemplate::New(HandleLogDelegator)); 3410 v8::Persistent<Context> context = Context::New(0, global_template); 3411 Context::Scope context_scope(context); 3412 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run(); 3413 context.Dispose(); 3414} 3415 3416 3417static const char* kSimpleExtensionSource = 3418 "function Foo() {" 3419 " return 4;" 3420 "}"; 3421 3422 3423THREADED_TEST(SimpleExtensions) { 3424 v8::HandleScope handle_scope; 3425 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource)); 3426 const char* extension_names[] = { "simpletest" }; 3427 v8::ExtensionConfiguration extensions(1, extension_names); 3428 v8::Handle<Context> context = Context::New(&extensions); 3429 Context::Scope lock(context); 3430 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 3431 CHECK_EQ(result, v8::Integer::New(4)); 3432} 3433 3434 3435static const char* kEvalExtensionSource1 = 3436 "function UseEval1() {" 3437 " var x = 42;" 3438 " return eval('x');" 3439 "}"; 3440 3441 3442static const char* kEvalExtensionSource2 = 3443 "(function() {" 3444 " var x = 42;" 3445 " function e() {" 3446 " return eval('x');" 3447 " }" 3448 " this.UseEval2 = e;" 3449 "})()"; 3450 3451 3452THREADED_TEST(UseEvalFromExtension) { 3453 v8::HandleScope handle_scope; 3454 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1)); 3455 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2)); 3456 const char* extension_names[] = { "evaltest1", "evaltest2" }; 3457 v8::ExtensionConfiguration extensions(2, extension_names); 3458 v8::Handle<Context> context = Context::New(&extensions); 3459 Context::Scope lock(context); 3460 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run(); 3461 CHECK_EQ(result, v8::Integer::New(42)); 3462 result = Script::Compile(v8_str("UseEval2()"))->Run(); 3463 CHECK_EQ(result, v8::Integer::New(42)); 3464} 3465 3466 3467static const char* kWithExtensionSource1 = 3468 "function UseWith1() {" 3469 " var x = 42;" 3470 " with({x:87}) { return x; }" 3471 "}"; 3472 3473 3474 3475static const char* kWithExtensionSource2 = 3476 "(function() {" 3477 " var x = 42;" 3478 " function e() {" 3479 " with ({x:87}) { return x; }" 3480 " }" 3481 " this.UseWith2 = e;" 3482 "})()"; 3483 3484 3485THREADED_TEST(UseWithFromExtension) { 3486 v8::HandleScope handle_scope; 3487 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1)); 3488 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2)); 3489 const char* extension_names[] = { "withtest1", "withtest2" }; 3490 v8::ExtensionConfiguration extensions(2, extension_names); 3491 v8::Handle<Context> context = Context::New(&extensions); 3492 Context::Scope lock(context); 3493 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run(); 3494 CHECK_EQ(result, v8::Integer::New(87)); 3495 result = Script::Compile(v8_str("UseWith2()"))->Run(); 3496 CHECK_EQ(result, v8::Integer::New(87)); 3497} 3498 3499 3500THREADED_TEST(AutoExtensions) { 3501 v8::HandleScope handle_scope; 3502 Extension* extension = new Extension("autotest", kSimpleExtensionSource); 3503 extension->set_auto_enable(true); 3504 v8::RegisterExtension(extension); 3505 v8::Handle<Context> context = Context::New(); 3506 Context::Scope lock(context); 3507 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 3508 CHECK_EQ(result, v8::Integer::New(4)); 3509} 3510 3511 3512static const char* kSyntaxErrorInExtensionSource = 3513 "["; 3514 3515 3516// Test that a syntax error in an extension does not cause a fatal 3517// error but results in an empty context. 3518THREADED_TEST(SyntaxErrorExtensions) { 3519 v8::HandleScope handle_scope; 3520 v8::RegisterExtension(new Extension("syntaxerror", 3521 kSyntaxErrorInExtensionSource)); 3522 const char* extension_names[] = { "syntaxerror" }; 3523 v8::ExtensionConfiguration extensions(1, extension_names); 3524 v8::Handle<Context> context = Context::New(&extensions); 3525 CHECK(context.IsEmpty()); 3526} 3527 3528 3529static const char* kExceptionInExtensionSource = 3530 "throw 42"; 3531 3532 3533// Test that an exception when installing an extension does not cause 3534// a fatal error but results in an empty context. 3535THREADED_TEST(ExceptionExtensions) { 3536 v8::HandleScope handle_scope; 3537 v8::RegisterExtension(new Extension("exception", 3538 kExceptionInExtensionSource)); 3539 const char* extension_names[] = { "exception" }; 3540 v8::ExtensionConfiguration extensions(1, extension_names); 3541 v8::Handle<Context> context = Context::New(&extensions); 3542 CHECK(context.IsEmpty()); 3543} 3544 3545 3546static void CheckDependencies(const char* name, const char* expected) { 3547 v8::HandleScope handle_scope; 3548 v8::ExtensionConfiguration config(1, &name); 3549 LocalContext context(&config); 3550 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded"))); 3551} 3552 3553 3554/* 3555 * Configuration: 3556 * 3557 * /-- B <--\ 3558 * A <- -- D <-- E 3559 * \-- C <--/ 3560 */ 3561THREADED_TEST(ExtensionDependency) { 3562 static const char* kEDeps[] = { "D" }; 3563 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps)); 3564 static const char* kDDeps[] = { "B", "C" }; 3565 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps)); 3566 static const char* kBCDeps[] = { "A" }; 3567 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps)); 3568 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps)); 3569 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';")); 3570 CheckDependencies("A", "undefinedA"); 3571 CheckDependencies("B", "undefinedAB"); 3572 CheckDependencies("C", "undefinedAC"); 3573 CheckDependencies("D", "undefinedABCD"); 3574 CheckDependencies("E", "undefinedABCDE"); 3575 v8::HandleScope handle_scope; 3576 static const char* exts[2] = { "C", "E" }; 3577 v8::ExtensionConfiguration config(2, exts); 3578 LocalContext context(&config); 3579 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded"))); 3580} 3581 3582 3583static const char* kExtensionTestScript = 3584 "native function A();" 3585 "native function B();" 3586 "native function C();" 3587 "function Foo(i) {" 3588 " if (i == 0) return A();" 3589 " if (i == 1) return B();" 3590 " if (i == 2) return C();" 3591 "}"; 3592 3593 3594static v8::Handle<Value> CallFun(const v8::Arguments& args) { 3595 ApiTestFuzzer::Fuzz(); 3596 if (args.IsConstructCall()) { 3597 args.This()->Set(v8_str("data"), args.Data()); 3598 return v8::Null(); 3599 } 3600 return args.Data(); 3601} 3602 3603 3604class FunctionExtension : public Extension { 3605 public: 3606 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { } 3607 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( 3608 v8::Handle<String> name); 3609}; 3610 3611 3612static int lookup_count = 0; 3613v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction( 3614 v8::Handle<String> name) { 3615 lookup_count++; 3616 if (name->Equals(v8_str("A"))) { 3617 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8)); 3618 } else if (name->Equals(v8_str("B"))) { 3619 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7)); 3620 } else if (name->Equals(v8_str("C"))) { 3621 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6)); 3622 } else { 3623 return v8::Handle<v8::FunctionTemplate>(); 3624 } 3625} 3626 3627 3628THREADED_TEST(FunctionLookup) { 3629 v8::RegisterExtension(new FunctionExtension()); 3630 v8::HandleScope handle_scope; 3631 static const char* exts[1] = { "functiontest" }; 3632 v8::ExtensionConfiguration config(1, exts); 3633 LocalContext context(&config); 3634 CHECK_EQ(3, lookup_count); 3635 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run()); 3636 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run()); 3637 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run()); 3638} 3639 3640 3641THREADED_TEST(NativeFunctionConstructCall) { 3642 v8::RegisterExtension(new FunctionExtension()); 3643 v8::HandleScope handle_scope; 3644 static const char* exts[1] = { "functiontest" }; 3645 v8::ExtensionConfiguration config(1, exts); 3646 LocalContext context(&config); 3647 for (int i = 0; i < 10; i++) { 3648 // Run a few times to ensure that allocation of objects doesn't 3649 // change behavior of a constructor function. 3650 CHECK_EQ(v8::Integer::New(8), 3651 Script::Compile(v8_str("(new A()).data"))->Run()); 3652 CHECK_EQ(v8::Integer::New(7), 3653 Script::Compile(v8_str("(new B()).data"))->Run()); 3654 CHECK_EQ(v8::Integer::New(6), 3655 Script::Compile(v8_str("(new C()).data"))->Run()); 3656 } 3657} 3658 3659 3660static const char* last_location; 3661static const char* last_message; 3662void StoringErrorCallback(const char* location, const char* message) { 3663 if (last_location == NULL) { 3664 last_location = location; 3665 last_message = message; 3666 } 3667} 3668 3669 3670// ErrorReporting creates a circular extensions configuration and 3671// tests that the fatal error handler gets called. This renders V8 3672// unusable and therefore this test cannot be run in parallel. 3673TEST(ErrorReporting) { 3674 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 3675 static const char* aDeps[] = { "B" }; 3676 v8::RegisterExtension(new Extension("A", "", 1, aDeps)); 3677 static const char* bDeps[] = { "A" }; 3678 v8::RegisterExtension(new Extension("B", "", 1, bDeps)); 3679 last_location = NULL; 3680 v8::ExtensionConfiguration config(1, bDeps); 3681 v8::Handle<Context> context = Context::New(&config); 3682 CHECK(context.IsEmpty()); 3683 CHECK_NE(last_location, NULL); 3684} 3685 3686 3687static const char* js_code_causing_huge_string_flattening = 3688 "var str = 'X';" 3689 "for (var i = 0; i < 30; i++) {" 3690 " str = str + str;" 3691 "}" 3692 "str.match(/X/);"; 3693 3694 3695void OOMCallback(const char* location, const char* message) { 3696 exit(0); 3697} 3698 3699 3700TEST(RegexpOutOfMemory) { 3701 // Execute a script that causes out of memory when flattening a string. 3702 v8::HandleScope scope; 3703 v8::V8::SetFatalErrorHandler(OOMCallback); 3704 LocalContext context; 3705 Local<Script> script = 3706 Script::Compile(String::New(js_code_causing_huge_string_flattening)); 3707 last_location = NULL; 3708 Local<Value> result = script->Run(); 3709 3710 CHECK(false); // Should not return. 3711} 3712 3713 3714static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message, 3715 v8::Handle<Value> data) { 3716 CHECK_EQ(v8::Undefined(), data); 3717 CHECK(message->GetScriptResourceName()->IsUndefined()); 3718 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName()); 3719 message->GetLineNumber(); 3720 message->GetSourceLine(); 3721} 3722 3723 3724THREADED_TEST(ErrorWithMissingScriptInfo) { 3725 v8::HandleScope scope; 3726 LocalContext context; 3727 v8::V8::AddMessageListener(MissingScriptInfoMessageListener); 3728 Script::Compile(v8_str("throw Error()"))->Run(); 3729 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener); 3730} 3731 3732 3733int global_index = 0; 3734 3735class Snorkel { 3736 public: 3737 Snorkel() { index_ = global_index++; } 3738 int index_; 3739}; 3740 3741class Whammy { 3742 public: 3743 Whammy() { 3744 cursor_ = 0; 3745 } 3746 ~Whammy() { 3747 script_.Dispose(); 3748 } 3749 v8::Handle<Script> getScript() { 3750 if (script_.IsEmpty()) 3751 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo")); 3752 return Local<Script>(*script_); 3753 } 3754 3755 public: 3756 static const int kObjectCount = 256; 3757 int cursor_; 3758 v8::Persistent<v8::Object> objects_[kObjectCount]; 3759 v8::Persistent<Script> script_; 3760}; 3761 3762static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) { 3763 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data); 3764 delete snorkel; 3765 obj.ClearWeak(); 3766} 3767 3768v8::Handle<Value> WhammyPropertyGetter(Local<String> name, 3769 const AccessorInfo& info) { 3770 Whammy* whammy = 3771 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); 3772 3773 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_]; 3774 3775 v8::Handle<v8::Object> obj = v8::Object::New(); 3776 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj); 3777 if (!prev.IsEmpty()) { 3778 prev->Set(v8_str("next"), obj); 3779 prev.MakeWeak(new Snorkel(), &HandleWeakReference); 3780 whammy->objects_[whammy->cursor_].Clear(); 3781 } 3782 whammy->objects_[whammy->cursor_] = global; 3783 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount; 3784 return whammy->getScript()->Run(); 3785} 3786 3787THREADED_TEST(WeakReference) { 3788 v8::HandleScope handle_scope; 3789 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New(); 3790 templ->SetNamedPropertyHandler(WhammyPropertyGetter, 3791 0, 0, 0, 0, 3792 v8::External::New(new Whammy())); 3793 const char* extension_list[] = { "v8/gc" }; 3794 v8::ExtensionConfiguration extensions(1, extension_list); 3795 v8::Persistent<Context> context = Context::New(&extensions); 3796 Context::Scope context_scope(context); 3797 3798 v8::Handle<v8::Object> interceptor = templ->NewInstance(); 3799 context->Global()->Set(v8_str("whammy"), interceptor); 3800 const char* code = 3801 "var last;" 3802 "for (var i = 0; i < 10000; i++) {" 3803 " var obj = whammy.length;" 3804 " if (last) last.next = obj;" 3805 " last = obj;" 3806 "}" 3807 "gc();" 3808 "4"; 3809 v8::Handle<Value> result = CompileRun(code); 3810 CHECK_EQ(4.0, result->NumberValue()); 3811 3812 context.Dispose(); 3813} 3814 3815 3816static bool in_scavenge = false; 3817static int last = -1; 3818 3819static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) { 3820 CHECK_EQ(-1, last); 3821 last = 0; 3822 obj.Dispose(); 3823 obj.Clear(); 3824 in_scavenge = true; 3825 i::Heap::PerformScavenge(); 3826 in_scavenge = false; 3827 *(reinterpret_cast<bool*>(data)) = true; 3828} 3829 3830static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj, 3831 void* data) { 3832 CHECK_EQ(0, last); 3833 last = 1; 3834 *(reinterpret_cast<bool*>(data)) = in_scavenge; 3835 obj.Dispose(); 3836 obj.Clear(); 3837} 3838 3839THREADED_TEST(NoWeakRefCallbacksInScavenge) { 3840 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks. 3841 // Calling callbacks from scavenges is unsafe as objects held by those 3842 // handlers might have become strongly reachable, but scavenge doesn't 3843 // check that. 3844 v8::Persistent<Context> context = Context::New(); 3845 Context::Scope context_scope(context); 3846 3847 v8::Persistent<v8::Object> object_a; 3848 v8::Persistent<v8::Object> object_b; 3849 3850 { 3851 v8::HandleScope handle_scope; 3852 object_b = v8::Persistent<v8::Object>::New(v8::Object::New()); 3853 object_a = v8::Persistent<v8::Object>::New(v8::Object::New()); 3854 } 3855 3856 bool object_a_disposed = false; 3857 object_a.MakeWeak(&object_a_disposed, &ForceScavenge); 3858 bool released_in_scavenge = false; 3859 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge); 3860 3861 while (!object_a_disposed) { 3862 i::Heap::CollectAllGarbage(false); 3863 } 3864 CHECK(!released_in_scavenge); 3865} 3866 3867 3868v8::Handle<Function> args_fun; 3869 3870 3871static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) { 3872 ApiTestFuzzer::Fuzz(); 3873 CHECK_EQ(args_fun, args.Callee()); 3874 CHECK_EQ(3, args.Length()); 3875 CHECK_EQ(v8::Integer::New(1), args[0]); 3876 CHECK_EQ(v8::Integer::New(2), args[1]); 3877 CHECK_EQ(v8::Integer::New(3), args[2]); 3878 CHECK_EQ(v8::Undefined(), args[3]); 3879 v8::HandleScope scope; 3880 i::Heap::CollectAllGarbage(false); 3881 return v8::Undefined(); 3882} 3883 3884 3885THREADED_TEST(Arguments) { 3886 v8::HandleScope scope; 3887 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 3888 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback)); 3889 LocalContext context(NULL, global); 3890 args_fun = context->Global()->Get(v8_str("f")).As<Function>(); 3891 v8_compile("f(1, 2, 3)")->Run(); 3892} 3893 3894 3895static v8::Handle<Value> NoBlockGetterX(Local<String> name, 3896 const AccessorInfo&) { 3897 return v8::Handle<Value>(); 3898} 3899 3900 3901static v8::Handle<Value> NoBlockGetterI(uint32_t index, 3902 const AccessorInfo&) { 3903 return v8::Handle<Value>(); 3904} 3905 3906 3907static v8::Handle<v8::Boolean> PDeleter(Local<String> name, 3908 const AccessorInfo&) { 3909 if (!name->Equals(v8_str("foo"))) { 3910 return v8::Handle<v8::Boolean>(); // not intercepted 3911 } 3912 3913 return v8::False(); // intercepted, and don't delete the property 3914} 3915 3916 3917static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) { 3918 if (index != 2) { 3919 return v8::Handle<v8::Boolean>(); // not intercepted 3920 } 3921 3922 return v8::False(); // intercepted, and don't delete the property 3923} 3924 3925 3926THREADED_TEST(Deleter) { 3927 v8::HandleScope scope; 3928 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 3929 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL); 3930 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL); 3931 LocalContext context; 3932 context->Global()->Set(v8_str("k"), obj->NewInstance()); 3933 CompileRun( 3934 "k.foo = 'foo';" 3935 "k.bar = 'bar';" 3936 "k[2] = 2;" 3937 "k[4] = 4;"); 3938 CHECK(v8_compile("delete k.foo")->Run()->IsFalse()); 3939 CHECK(v8_compile("delete k.bar")->Run()->IsTrue()); 3940 3941 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo")); 3942 CHECK(v8_compile("k.bar")->Run()->IsUndefined()); 3943 3944 CHECK(v8_compile("delete k[2]")->Run()->IsFalse()); 3945 CHECK(v8_compile("delete k[4]")->Run()->IsTrue()); 3946 3947 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2)); 3948 CHECK(v8_compile("k[4]")->Run()->IsUndefined()); 3949} 3950 3951 3952static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) { 3953 ApiTestFuzzer::Fuzz(); 3954 if (name->Equals(v8_str("foo")) || 3955 name->Equals(v8_str("bar")) || 3956 name->Equals(v8_str("baz"))) { 3957 return v8::Undefined(); 3958 } 3959 return v8::Handle<Value>(); 3960} 3961 3962 3963static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) { 3964 ApiTestFuzzer::Fuzz(); 3965 if (index == 0 || index == 1) return v8::Undefined(); 3966 return v8::Handle<Value>(); 3967} 3968 3969 3970static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) { 3971 ApiTestFuzzer::Fuzz(); 3972 v8::Handle<v8::Array> result = v8::Array::New(3); 3973 result->Set(v8::Integer::New(0), v8_str("foo")); 3974 result->Set(v8::Integer::New(1), v8_str("bar")); 3975 result->Set(v8::Integer::New(2), v8_str("baz")); 3976 return result; 3977} 3978 3979 3980static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) { 3981 ApiTestFuzzer::Fuzz(); 3982 v8::Handle<v8::Array> result = v8::Array::New(2); 3983 result->Set(v8::Integer::New(0), v8_str("0")); 3984 result->Set(v8::Integer::New(1), v8_str("1")); 3985 return result; 3986} 3987 3988 3989THREADED_TEST(Enumerators) { 3990 v8::HandleScope scope; 3991 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 3992 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum); 3993 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum); 3994 LocalContext context; 3995 context->Global()->Set(v8_str("k"), obj->NewInstance()); 3996 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 3997 "k[10] = 0;" 3998 "k.a = 0;" 3999 "k[5] = 0;" 4000 "k.b = 0;" 4001 "k[4294967295] = 0;" 4002 "k.c = 0;" 4003 "k[4294967296] = 0;" 4004 "k.d = 0;" 4005 "k[140000] = 0;" 4006 "k.e = 0;" 4007 "k[30000000000] = 0;" 4008 "k.f = 0;" 4009 "var result = [];" 4010 "for (var prop in k) {" 4011 " result.push(prop);" 4012 "}" 4013 "result")); 4014 // Check that we get all the property names returned including the 4015 // ones from the enumerators in the right order: indexed properties 4016 // in numerical order, indexed interceptor properties, named 4017 // properties in insertion order, named interceptor properties. 4018 // This order is not mandated by the spec, so this test is just 4019 // documenting our behavior. 4020 CHECK_EQ(17, result->Length()); 4021 // Indexed properties in numerical order. 4022 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0))); 4023 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1))); 4024 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2))); 4025 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3))); 4026 // Indexed interceptor properties in the order they are returned 4027 // from the enumerator interceptor. 4028 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4))); 4029 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5))); 4030 // Named properties in insertion order. 4031 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6))); 4032 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7))); 4033 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8))); 4034 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9))); 4035 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10))); 4036 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11))); 4037 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12))); 4038 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13))); 4039 // Named interceptor properties. 4040 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14))); 4041 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15))); 4042 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16))); 4043} 4044 4045 4046int p_getter_count; 4047int p_getter_count2; 4048 4049 4050static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) { 4051 ApiTestFuzzer::Fuzz(); 4052 p_getter_count++; 4053 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 4054 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 4055 if (name->Equals(v8_str("p1"))) { 4056 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 4057 } else if (name->Equals(v8_str("p2"))) { 4058 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 4059 } else if (name->Equals(v8_str("p3"))) { 4060 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 4061 } else if (name->Equals(v8_str("p4"))) { 4062 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 4063 } 4064 return v8::Undefined(); 4065} 4066 4067 4068static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) { 4069 ApiTestFuzzer::Fuzz(); 4070 LocalContext context; 4071 context->Global()->Set(v8_str("o1"), obj->NewInstance()); 4072 CompileRun( 4073 "o1.__proto__ = { };" 4074 "var o2 = { __proto__: o1 };" 4075 "var o3 = { __proto__: o2 };" 4076 "var o4 = { __proto__: o3 };" 4077 "for (var i = 0; i < 10; i++) o4.p4;" 4078 "for (var i = 0; i < 10; i++) o3.p3;" 4079 "for (var i = 0; i < 10; i++) o2.p2;" 4080 "for (var i = 0; i < 10; i++) o1.p1;"); 4081} 4082 4083 4084static v8::Handle<Value> PGetter2(Local<String> name, 4085 const AccessorInfo& info) { 4086 ApiTestFuzzer::Fuzz(); 4087 p_getter_count2++; 4088 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 4089 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 4090 if (name->Equals(v8_str("p1"))) { 4091 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 4092 } else if (name->Equals(v8_str("p2"))) { 4093 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 4094 } else if (name->Equals(v8_str("p3"))) { 4095 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 4096 } else if (name->Equals(v8_str("p4"))) { 4097 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 4098 } 4099 return v8::Undefined(); 4100} 4101 4102 4103THREADED_TEST(GetterHolders) { 4104 v8::HandleScope scope; 4105 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4106 obj->SetAccessor(v8_str("p1"), PGetter); 4107 obj->SetAccessor(v8_str("p2"), PGetter); 4108 obj->SetAccessor(v8_str("p3"), PGetter); 4109 obj->SetAccessor(v8_str("p4"), PGetter); 4110 p_getter_count = 0; 4111 RunHolderTest(obj); 4112 CHECK_EQ(40, p_getter_count); 4113} 4114 4115 4116THREADED_TEST(PreInterceptorHolders) { 4117 v8::HandleScope scope; 4118 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4119 obj->SetNamedPropertyHandler(PGetter2); 4120 p_getter_count2 = 0; 4121 RunHolderTest(obj); 4122 CHECK_EQ(40, p_getter_count2); 4123} 4124 4125 4126THREADED_TEST(ObjectInstantiation) { 4127 v8::HandleScope scope; 4128 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 4129 templ->SetAccessor(v8_str("t"), PGetter2); 4130 LocalContext context; 4131 context->Global()->Set(v8_str("o"), templ->NewInstance()); 4132 for (int i = 0; i < 100; i++) { 4133 v8::HandleScope inner_scope; 4134 v8::Handle<v8::Object> obj = templ->NewInstance(); 4135 CHECK_NE(obj, context->Global()->Get(v8_str("o"))); 4136 context->Global()->Set(v8_str("o2"), obj); 4137 v8::Handle<Value> value = 4138 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run(); 4139 CHECK_EQ(v8::True(), value); 4140 context->Global()->Set(v8_str("o"), obj); 4141 } 4142} 4143 4144 4145THREADED_TEST(StringWrite) { 4146 v8::HandleScope scope; 4147 v8::Handle<String> str = v8_str("abcde"); 4148 4149 char buf[100]; 4150 int len; 4151 4152 memset(buf, 0x1, sizeof(buf)); 4153 len = str->WriteAscii(buf); 4154 CHECK_EQ(len, 5); 4155 CHECK_EQ(strncmp("abcde\0", buf, 6), 0); 4156 4157 memset(buf, 0x1, sizeof(buf)); 4158 len = str->WriteAscii(buf, 0, 4); 4159 CHECK_EQ(len, 4); 4160 CHECK_EQ(strncmp("abcd\1", buf, 5), 0); 4161 4162 memset(buf, 0x1, sizeof(buf)); 4163 len = str->WriteAscii(buf, 0, 5); 4164 CHECK_EQ(len, 5); 4165 CHECK_EQ(strncmp("abcde\1", buf, 6), 0); 4166 4167 memset(buf, 0x1, sizeof(buf)); 4168 len = str->WriteAscii(buf, 0, 6); 4169 CHECK_EQ(len, 5); 4170 CHECK_EQ(strncmp("abcde\0", buf, 6), 0); 4171 4172 memset(buf, 0x1, sizeof(buf)); 4173 len = str->WriteAscii(buf, 4, -1); 4174 CHECK_EQ(len, 1); 4175 CHECK_EQ(strncmp("e\0", buf, 2), 0); 4176 4177 memset(buf, 0x1, sizeof(buf)); 4178 len = str->WriteAscii(buf, 4, 6); 4179 CHECK_EQ(len, 1); 4180 CHECK_EQ(strncmp("e\0", buf, 2), 0); 4181 4182 memset(buf, 0x1, sizeof(buf)); 4183 len = str->WriteAscii(buf, 4, 1); 4184 CHECK_EQ(len, 1); 4185 CHECK_EQ(strncmp("e\1", buf, 2), 0); 4186} 4187 4188 4189THREADED_TEST(ToArrayIndex) { 4190 v8::HandleScope scope; 4191 LocalContext context; 4192 4193 v8::Handle<String> str = v8_str("42"); 4194 v8::Handle<v8::Uint32> index = str->ToArrayIndex(); 4195 CHECK(!index.IsEmpty()); 4196 CHECK_EQ(42.0, index->Uint32Value()); 4197 str = v8_str("42asdf"); 4198 index = str->ToArrayIndex(); 4199 CHECK(index.IsEmpty()); 4200 str = v8_str("-42"); 4201 index = str->ToArrayIndex(); 4202 CHECK(index.IsEmpty()); 4203 str = v8_str("4294967295"); 4204 index = str->ToArrayIndex(); 4205 CHECK(!index.IsEmpty()); 4206 CHECK_EQ(4294967295.0, index->Uint32Value()); 4207 v8::Handle<v8::Number> num = v8::Number::New(1); 4208 index = num->ToArrayIndex(); 4209 CHECK(!index.IsEmpty()); 4210 CHECK_EQ(1.0, index->Uint32Value()); 4211 num = v8::Number::New(-1); 4212 index = num->ToArrayIndex(); 4213 CHECK(index.IsEmpty()); 4214 v8::Handle<v8::Object> obj = v8::Object::New(); 4215 index = obj->ToArrayIndex(); 4216 CHECK(index.IsEmpty()); 4217} 4218 4219 4220THREADED_TEST(ErrorConstruction) { 4221 v8::HandleScope scope; 4222 LocalContext context; 4223 4224 v8::Handle<String> foo = v8_str("foo"); 4225 v8::Handle<String> message = v8_str("message"); 4226 v8::Handle<Value> range_error = v8::Exception::RangeError(foo); 4227 CHECK(range_error->IsObject()); 4228 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>(); 4229 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo)); 4230 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo); 4231 CHECK(reference_error->IsObject()); 4232 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo)); 4233 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo); 4234 CHECK(syntax_error->IsObject()); 4235 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo)); 4236 v8::Handle<Value> type_error = v8::Exception::TypeError(foo); 4237 CHECK(type_error->IsObject()); 4238 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo)); 4239 v8::Handle<Value> error = v8::Exception::Error(foo); 4240 CHECK(error->IsObject()); 4241 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo)); 4242} 4243 4244 4245static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) { 4246 ApiTestFuzzer::Fuzz(); 4247 return v8_num(10); 4248} 4249 4250 4251static void YSetter(Local<String> name, 4252 Local<Value> value, 4253 const AccessorInfo& info) { 4254 if (info.This()->Has(name)) { 4255 info.This()->Delete(name); 4256 } 4257 info.This()->Set(name, value); 4258} 4259 4260 4261THREADED_TEST(DeleteAccessor) { 4262 v8::HandleScope scope; 4263 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4264 obj->SetAccessor(v8_str("y"), YGetter, YSetter); 4265 LocalContext context; 4266 v8::Handle<v8::Object> holder = obj->NewInstance(); 4267 context->Global()->Set(v8_str("holder"), holder); 4268 v8::Handle<Value> result = CompileRun( 4269 "holder.y = 11; holder.y = 12; holder.y"); 4270 CHECK_EQ(12, result->Uint32Value()); 4271} 4272 4273 4274THREADED_TEST(TypeSwitch) { 4275 v8::HandleScope scope; 4276 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New(); 4277 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New(); 4278 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New(); 4279 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 }; 4280 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs); 4281 LocalContext context; 4282 v8::Handle<v8::Object> obj0 = v8::Object::New(); 4283 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance(); 4284 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance(); 4285 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance(); 4286 for (int i = 0; i < 10; i++) { 4287 CHECK_EQ(0, type_switch->match(obj0)); 4288 CHECK_EQ(1, type_switch->match(obj1)); 4289 CHECK_EQ(2, type_switch->match(obj2)); 4290 CHECK_EQ(3, type_switch->match(obj3)); 4291 CHECK_EQ(3, type_switch->match(obj3)); 4292 CHECK_EQ(2, type_switch->match(obj2)); 4293 CHECK_EQ(1, type_switch->match(obj1)); 4294 CHECK_EQ(0, type_switch->match(obj0)); 4295 } 4296} 4297 4298 4299// For use within the TestSecurityHandler() test. 4300static bool g_security_callback_result = false; 4301static bool NamedSecurityTestCallback(Local<v8::Object> global, 4302 Local<Value> name, 4303 v8::AccessType type, 4304 Local<Value> data) { 4305 // Always allow read access. 4306 if (type == v8::ACCESS_GET) 4307 return true; 4308 4309 // Sometimes allow other access. 4310 return g_security_callback_result; 4311} 4312 4313 4314static bool IndexedSecurityTestCallback(Local<v8::Object> global, 4315 uint32_t key, 4316 v8::AccessType type, 4317 Local<Value> data) { 4318 // Always allow read access. 4319 if (type == v8::ACCESS_GET) 4320 return true; 4321 4322 // Sometimes allow other access. 4323 return g_security_callback_result; 4324} 4325 4326 4327static int trouble_nesting = 0; 4328static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) { 4329 ApiTestFuzzer::Fuzz(); 4330 trouble_nesting++; 4331 4332 // Call a JS function that throws an uncaught exception. 4333 Local<v8::Object> arg_this = Context::GetCurrent()->Global(); 4334 Local<Value> trouble_callee = (trouble_nesting == 3) ? 4335 arg_this->Get(v8_str("trouble_callee")) : 4336 arg_this->Get(v8_str("trouble_caller")); 4337 CHECK(trouble_callee->IsFunction()); 4338 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL); 4339} 4340 4341 4342static int report_count = 0; 4343static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>, 4344 v8::Handle<Value>) { 4345 report_count++; 4346} 4347 4348 4349// Counts uncaught exceptions, but other tests running in parallel 4350// also have uncaught exceptions. 4351TEST(ApiUncaughtException) { 4352 report_count = 0; 4353 v8::HandleScope scope; 4354 LocalContext env; 4355 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener); 4356 4357 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 4358 v8::Local<v8::Object> global = env->Global(); 4359 global->Set(v8_str("trouble"), fun->GetFunction()); 4360 4361 Script::Compile(v8_str("function trouble_callee() {" 4362 " var x = null;" 4363 " return x.foo;" 4364 "};" 4365 "function trouble_caller() {" 4366 " trouble();" 4367 "};"))->Run(); 4368 Local<Value> trouble = global->Get(v8_str("trouble")); 4369 CHECK(trouble->IsFunction()); 4370 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee")); 4371 CHECK(trouble_callee->IsFunction()); 4372 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller")); 4373 CHECK(trouble_caller->IsFunction()); 4374 Function::Cast(*trouble_caller)->Call(global, 0, NULL); 4375 CHECK_EQ(1, report_count); 4376 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener); 4377} 4378 4379static const char* script_resource_name = "ExceptionInNativeScript.js"; 4380static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message, 4381 v8::Handle<Value>) { 4382 v8::Handle<v8::Value> name_val = message->GetScriptResourceName(); 4383 CHECK(!name_val.IsEmpty() && name_val->IsString()); 4384 v8::String::AsciiValue name(message->GetScriptResourceName()); 4385 CHECK_EQ(script_resource_name, *name); 4386 CHECK_EQ(3, message->GetLineNumber()); 4387 v8::String::AsciiValue source_line(message->GetSourceLine()); 4388 CHECK_EQ(" new o.foo();", *source_line); 4389} 4390 4391TEST(ExceptionInNativeScript) { 4392 v8::HandleScope scope; 4393 LocalContext env; 4394 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener); 4395 4396 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 4397 v8::Local<v8::Object> global = env->Global(); 4398 global->Set(v8_str("trouble"), fun->GetFunction()); 4399 4400 Script::Compile(v8_str("function trouble() {\n" 4401 " var o = {};\n" 4402 " new o.foo();\n" 4403 "};"), v8::String::New(script_resource_name))->Run(); 4404 Local<Value> trouble = global->Get(v8_str("trouble")); 4405 CHECK(trouble->IsFunction()); 4406 Function::Cast(*trouble)->Call(global, 0, NULL); 4407 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener); 4408} 4409 4410 4411TEST(CompilationErrorUsingTryCatchHandler) { 4412 v8::HandleScope scope; 4413 LocalContext env; 4414 v8::TryCatch try_catch; 4415 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile.")); 4416 CHECK_NE(NULL, *try_catch.Exception()); 4417 CHECK(try_catch.HasCaught()); 4418} 4419 4420 4421TEST(TryCatchFinallyUsingTryCatchHandler) { 4422 v8::HandleScope scope; 4423 LocalContext env; 4424 v8::TryCatch try_catch; 4425 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run(); 4426 CHECK(!try_catch.HasCaught()); 4427 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run(); 4428 CHECK(try_catch.HasCaught()); 4429 try_catch.Reset(); 4430 Script::Compile(v8_str("(function() {" 4431 "try { throw ''; } finally { return; }" 4432 "})()"))->Run(); 4433 CHECK(!try_catch.HasCaught()); 4434 Script::Compile(v8_str("(function()" 4435 " { try { throw ''; } finally { throw 0; }" 4436 "})()"))->Run(); 4437 CHECK(try_catch.HasCaught()); 4438} 4439 4440 4441// SecurityHandler can't be run twice 4442TEST(SecurityHandler) { 4443 v8::HandleScope scope0; 4444 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 4445 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback, 4446 IndexedSecurityTestCallback); 4447 // Create an environment 4448 v8::Persistent<Context> context0 = 4449 Context::New(NULL, global_template); 4450 context0->Enter(); 4451 4452 v8::Handle<v8::Object> global0 = context0->Global(); 4453 v8::Handle<Script> script0 = v8_compile("foo = 111"); 4454 script0->Run(); 4455 global0->Set(v8_str("0"), v8_num(999)); 4456 v8::Handle<Value> foo0 = global0->Get(v8_str("foo")); 4457 CHECK_EQ(111, foo0->Int32Value()); 4458 v8::Handle<Value> z0 = global0->Get(v8_str("0")); 4459 CHECK_EQ(999, z0->Int32Value()); 4460 4461 // Create another environment, should fail security checks. 4462 v8::HandleScope scope1; 4463 4464 v8::Persistent<Context> context1 = 4465 Context::New(NULL, global_template); 4466 context1->Enter(); 4467 4468 v8::Handle<v8::Object> global1 = context1->Global(); 4469 global1->Set(v8_str("othercontext"), global0); 4470 // This set will fail the security check. 4471 v8::Handle<Script> script1 = 4472 v8_compile("othercontext.foo = 222; othercontext[0] = 888;"); 4473 script1->Run(); 4474 // This read will pass the security check. 4475 v8::Handle<Value> foo1 = global0->Get(v8_str("foo")); 4476 CHECK_EQ(111, foo1->Int32Value()); 4477 // This read will pass the security check. 4478 v8::Handle<Value> z1 = global0->Get(v8_str("0")); 4479 CHECK_EQ(999, z1->Int32Value()); 4480 4481 // Create another environment, should pass security checks. 4482 { g_security_callback_result = true; // allow security handler to pass. 4483 v8::HandleScope scope2; 4484 LocalContext context2; 4485 v8::Handle<v8::Object> global2 = context2->Global(); 4486 global2->Set(v8_str("othercontext"), global0); 4487 v8::Handle<Script> script2 = 4488 v8_compile("othercontext.foo = 333; othercontext[0] = 888;"); 4489 script2->Run(); 4490 v8::Handle<Value> foo2 = global0->Get(v8_str("foo")); 4491 CHECK_EQ(333, foo2->Int32Value()); 4492 v8::Handle<Value> z2 = global0->Get(v8_str("0")); 4493 CHECK_EQ(888, z2->Int32Value()); 4494 } 4495 4496 context1->Exit(); 4497 context1.Dispose(); 4498 4499 context0->Exit(); 4500 context0.Dispose(); 4501} 4502 4503 4504THREADED_TEST(SecurityChecks) { 4505 v8::HandleScope handle_scope; 4506 LocalContext env1; 4507 v8::Persistent<Context> env2 = Context::New(); 4508 4509 Local<Value> foo = v8_str("foo"); 4510 Local<Value> bar = v8_str("bar"); 4511 4512 // Set to the same domain. 4513 env1->SetSecurityToken(foo); 4514 4515 // Create a function in env1. 4516 Script::Compile(v8_str("spy=function(){return spy;}"))->Run(); 4517 Local<Value> spy = env1->Global()->Get(v8_str("spy")); 4518 CHECK(spy->IsFunction()); 4519 4520 // Create another function accessing global objects. 4521 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run(); 4522 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2")); 4523 CHECK(spy2->IsFunction()); 4524 4525 // Switch to env2 in the same domain and invoke spy on env2. 4526 { 4527 env2->SetSecurityToken(foo); 4528 // Enter env2 4529 Context::Scope scope_env2(env2); 4530 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL); 4531 CHECK(result->IsFunction()); 4532 } 4533 4534 { 4535 env2->SetSecurityToken(bar); 4536 Context::Scope scope_env2(env2); 4537 4538 // Call cross_domain_call, it should throw an exception 4539 v8::TryCatch try_catch; 4540 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL); 4541 CHECK(try_catch.HasCaught()); 4542 } 4543 4544 env2.Dispose(); 4545} 4546 4547 4548// Regression test case for issue 1183439. 4549THREADED_TEST(SecurityChecksForPrototypeChain) { 4550 v8::HandleScope scope; 4551 LocalContext current; 4552 v8::Persistent<Context> other = Context::New(); 4553 4554 // Change context to be able to get to the Object function in the 4555 // other context without hitting the security checks. 4556 v8::Local<Value> other_object; 4557 { Context::Scope scope(other); 4558 other_object = other->Global()->Get(v8_str("Object")); 4559 other->Global()->Set(v8_num(42), v8_num(87)); 4560 } 4561 4562 current->Global()->Set(v8_str("other"), other->Global()); 4563 CHECK(v8_compile("other")->Run()->Equals(other->Global())); 4564 4565 // Make sure the security check fails here and we get an undefined 4566 // result instead of getting the Object function. Repeat in a loop 4567 // to make sure to exercise the IC code. 4568 v8::Local<Script> access_other0 = v8_compile("other.Object"); 4569 v8::Local<Script> access_other1 = v8_compile("other[42]"); 4570 for (int i = 0; i < 5; i++) { 4571 CHECK(!access_other0->Run()->Equals(other_object)); 4572 CHECK(access_other0->Run()->IsUndefined()); 4573 CHECK(!access_other1->Run()->Equals(v8_num(87))); 4574 CHECK(access_other1->Run()->IsUndefined()); 4575 } 4576 4577 // Create an object that has 'other' in its prototype chain and make 4578 // sure we cannot access the Object function indirectly through 4579 // that. Repeat in a loop to make sure to exercise the IC code. 4580 v8_compile("function F() { };" 4581 "F.prototype = other;" 4582 "var f = new F();")->Run(); 4583 v8::Local<Script> access_f0 = v8_compile("f.Object"); 4584 v8::Local<Script> access_f1 = v8_compile("f[42]"); 4585 for (int j = 0; j < 5; j++) { 4586 CHECK(!access_f0->Run()->Equals(other_object)); 4587 CHECK(access_f0->Run()->IsUndefined()); 4588 CHECK(!access_f1->Run()->Equals(v8_num(87))); 4589 CHECK(access_f1->Run()->IsUndefined()); 4590 } 4591 4592 // Now it gets hairy: Set the prototype for the other global object 4593 // to be the current global object. The prototype chain for 'f' now 4594 // goes through 'other' but ends up in the current global object. 4595 { Context::Scope scope(other); 4596 other->Global()->Set(v8_str("__proto__"), current->Global()); 4597 } 4598 // Set a named and an index property on the current global 4599 // object. To force the lookup to go through the other global object, 4600 // the properties must not exist in the other global object. 4601 current->Global()->Set(v8_str("foo"), v8_num(100)); 4602 current->Global()->Set(v8_num(99), v8_num(101)); 4603 // Try to read the properties from f and make sure that the access 4604 // gets stopped by the security checks on the other global object. 4605 Local<Script> access_f2 = v8_compile("f.foo"); 4606 Local<Script> access_f3 = v8_compile("f[99]"); 4607 for (int k = 0; k < 5; k++) { 4608 CHECK(!access_f2->Run()->Equals(v8_num(100))); 4609 CHECK(access_f2->Run()->IsUndefined()); 4610 CHECK(!access_f3->Run()->Equals(v8_num(101))); 4611 CHECK(access_f3->Run()->IsUndefined()); 4612 } 4613 other.Dispose(); 4614} 4615 4616 4617THREADED_TEST(CrossDomainDelete) { 4618 v8::HandleScope handle_scope; 4619 LocalContext env1; 4620 v8::Persistent<Context> env2 = Context::New(); 4621 4622 Local<Value> foo = v8_str("foo"); 4623 Local<Value> bar = v8_str("bar"); 4624 4625 // Set to the same domain. 4626 env1->SetSecurityToken(foo); 4627 env2->SetSecurityToken(foo); 4628 4629 env1->Global()->Set(v8_str("prop"), v8_num(3)); 4630 env2->Global()->Set(v8_str("env1"), env1->Global()); 4631 4632 // Change env2 to a different domain and delete env1.prop. 4633 env2->SetSecurityToken(bar); 4634 { 4635 Context::Scope scope_env2(env2); 4636 Local<Value> result = 4637 Script::Compile(v8_str("delete env1.prop"))->Run(); 4638 CHECK(result->IsFalse()); 4639 } 4640 4641 // Check that env1.prop still exists. 4642 Local<Value> v = env1->Global()->Get(v8_str("prop")); 4643 CHECK(v->IsNumber()); 4644 CHECK_EQ(3, v->Int32Value()); 4645 4646 env2.Dispose(); 4647} 4648 4649 4650THREADED_TEST(CrossDomainIsPropertyEnumerable) { 4651 v8::HandleScope handle_scope; 4652 LocalContext env1; 4653 v8::Persistent<Context> env2 = Context::New(); 4654 4655 Local<Value> foo = v8_str("foo"); 4656 Local<Value> bar = v8_str("bar"); 4657 4658 // Set to the same domain. 4659 env1->SetSecurityToken(foo); 4660 env2->SetSecurityToken(foo); 4661 4662 env1->Global()->Set(v8_str("prop"), v8_num(3)); 4663 env2->Global()->Set(v8_str("env1"), env1->Global()); 4664 4665 // env1.prop is enumerable in env2. 4666 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')"); 4667 { 4668 Context::Scope scope_env2(env2); 4669 Local<Value> result = Script::Compile(test)->Run(); 4670 CHECK(result->IsTrue()); 4671 } 4672 4673 // Change env2 to a different domain and test again. 4674 env2->SetSecurityToken(bar); 4675 { 4676 Context::Scope scope_env2(env2); 4677 Local<Value> result = Script::Compile(test)->Run(); 4678 CHECK(result->IsFalse()); 4679 } 4680 4681 env2.Dispose(); 4682} 4683 4684 4685THREADED_TEST(CrossDomainForIn) { 4686 v8::HandleScope handle_scope; 4687 LocalContext env1; 4688 v8::Persistent<Context> env2 = Context::New(); 4689 4690 Local<Value> foo = v8_str("foo"); 4691 Local<Value> bar = v8_str("bar"); 4692 4693 // Set to the same domain. 4694 env1->SetSecurityToken(foo); 4695 env2->SetSecurityToken(foo); 4696 4697 env1->Global()->Set(v8_str("prop"), v8_num(3)); 4698 env2->Global()->Set(v8_str("env1"), env1->Global()); 4699 4700 // Change env2 to a different domain and set env1's global object 4701 // as the __proto__ of an object in env2 and enumerate properties 4702 // in for-in. It shouldn't enumerate properties on env1's global 4703 // object. 4704 env2->SetSecurityToken(bar); 4705 { 4706 Context::Scope scope_env2(env2); 4707 Local<Value> result = 4708 CompileRun("(function(){var obj = {'__proto__':env1};" 4709 "for (var p in obj)" 4710 " if (p == 'prop') return false;" 4711 "return true;})()"); 4712 CHECK(result->IsTrue()); 4713 } 4714 env2.Dispose(); 4715} 4716 4717 4718TEST(ContextDetachGlobal) { 4719 v8::HandleScope handle_scope; 4720 LocalContext env1; 4721 v8::Persistent<Context> env2 = Context::New(); 4722 4723 Local<v8::Object> global1 = env1->Global(); 4724 4725 Local<Value> foo = v8_str("foo"); 4726 4727 // Set to the same domain. 4728 env1->SetSecurityToken(foo); 4729 env2->SetSecurityToken(foo); 4730 4731 // Enter env2 4732 env2->Enter(); 4733 4734 // Create a function in env2 and add a reference to it in env1. 4735 Local<v8::Object> global2 = env2->Global(); 4736 global2->Set(v8_str("prop"), v8::Integer::New(1)); 4737 CompileRun("function getProp() {return prop;}"); 4738 4739 env1->Global()->Set(v8_str("getProp"), 4740 global2->Get(v8_str("getProp"))); 4741 4742 // Detach env2's global, and reuse the global object of env2 4743 env2->Exit(); 4744 env2->DetachGlobal(); 4745 // env2 has a new global object. 4746 CHECK(!env2->Global()->Equals(global2)); 4747 4748 v8::Persistent<Context> env3 = 4749 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 4750 env3->SetSecurityToken(v8_str("bar")); 4751 env3->Enter(); 4752 4753 Local<v8::Object> global3 = env3->Global(); 4754 CHECK_EQ(global2, global3); 4755 CHECK(global3->Get(v8_str("prop"))->IsUndefined()); 4756 CHECK(global3->Get(v8_str("getProp"))->IsUndefined()); 4757 global3->Set(v8_str("prop"), v8::Integer::New(-1)); 4758 global3->Set(v8_str("prop2"), v8::Integer::New(2)); 4759 env3->Exit(); 4760 4761 // Call getProp in env1, and it should return the value 1 4762 { 4763 Local<Value> get_prop = global1->Get(v8_str("getProp")); 4764 CHECK(get_prop->IsFunction()); 4765 v8::TryCatch try_catch; 4766 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL); 4767 CHECK(!try_catch.HasCaught()); 4768 CHECK_EQ(1, r->Int32Value()); 4769 } 4770 4771 // Check that env3 is not accessible from env1 4772 { 4773 Local<Value> r = global3->Get(v8_str("prop2")); 4774 CHECK(r->IsUndefined()); 4775 } 4776 4777 env2.Dispose(); 4778 env3.Dispose(); 4779} 4780 4781 4782TEST(DetachAndReattachGlobal) { 4783 v8::HandleScope scope; 4784 LocalContext env1; 4785 4786 // Create second environment. 4787 v8::Persistent<Context> env2 = Context::New(); 4788 4789 Local<Value> foo = v8_str("foo"); 4790 4791 // Set same security token for env1 and env2. 4792 env1->SetSecurityToken(foo); 4793 env2->SetSecurityToken(foo); 4794 4795 // Create a property on the global object in env2. 4796 { 4797 v8::Context::Scope scope(env2); 4798 env2->Global()->Set(v8_str("p"), v8::Integer::New(42)); 4799 } 4800 4801 // Create a reference to env2 global from env1 global. 4802 env1->Global()->Set(v8_str("other"), env2->Global()); 4803 4804 // Check that we have access to other.p in env2 from env1. 4805 Local<Value> result = CompileRun("other.p"); 4806 CHECK(result->IsInt32()); 4807 CHECK_EQ(42, result->Int32Value()); 4808 4809 // Hold on to global from env2 and detach global from env2. 4810 Local<v8::Object> global2 = env2->Global(); 4811 env2->DetachGlobal(); 4812 4813 // Check that the global has been detached. No other.p property can 4814 // be found. 4815 result = CompileRun("other.p"); 4816 CHECK(result->IsUndefined()); 4817 4818 // Reuse global2 for env3. 4819 v8::Persistent<Context> env3 = 4820 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 4821 CHECK_EQ(global2, env3->Global()); 4822 4823 // Start by using the same security token for env3 as for env1 and env2. 4824 env3->SetSecurityToken(foo); 4825 4826 // Create a property on the global object in env3. 4827 { 4828 v8::Context::Scope scope(env3); 4829 env3->Global()->Set(v8_str("p"), v8::Integer::New(24)); 4830 } 4831 4832 // Check that other.p is now the property in env3 and that we have access. 4833 result = CompileRun("other.p"); 4834 CHECK(result->IsInt32()); 4835 CHECK_EQ(24, result->Int32Value()); 4836 4837 // Change security token for env3 to something different from env1 and env2. 4838 env3->SetSecurityToken(v8_str("bar")); 4839 4840 // Check that we do not have access to other.p in env1. |other| is now 4841 // the global object for env3 which has a different security token, 4842 // so access should be blocked. 4843 result = CompileRun("other.p"); 4844 CHECK(result->IsUndefined()); 4845 4846 // Detach the global for env3 and reattach it to env2. 4847 env3->DetachGlobal(); 4848 env2->ReattachGlobal(global2); 4849 4850 // Check that we have access to other.p again in env1. |other| is now 4851 // the global object for env2 which has the same security token as env1. 4852 result = CompileRun("other.p"); 4853 CHECK(result->IsInt32()); 4854 CHECK_EQ(42, result->Int32Value()); 4855 4856 env2.Dispose(); 4857 env3.Dispose(); 4858} 4859 4860 4861static bool NamedAccessBlocker(Local<v8::Object> global, 4862 Local<Value> name, 4863 v8::AccessType type, 4864 Local<Value> data) { 4865 return Context::GetCurrent()->Global()->Equals(global); 4866} 4867 4868 4869static bool IndexedAccessBlocker(Local<v8::Object> global, 4870 uint32_t key, 4871 v8::AccessType type, 4872 Local<Value> data) { 4873 return Context::GetCurrent()->Global()->Equals(global); 4874} 4875 4876 4877static int g_echo_value = -1; 4878static v8::Handle<Value> EchoGetter(Local<String> name, 4879 const AccessorInfo& info) { 4880 return v8_num(g_echo_value); 4881} 4882 4883 4884static void EchoSetter(Local<String> name, 4885 Local<Value> value, 4886 const AccessorInfo&) { 4887 if (value->IsNumber()) 4888 g_echo_value = value->Int32Value(); 4889} 4890 4891 4892static v8::Handle<Value> UnreachableGetter(Local<String> name, 4893 const AccessorInfo& info) { 4894 CHECK(false); // This function should not be called.. 4895 return v8::Undefined(); 4896} 4897 4898 4899static void UnreachableSetter(Local<String>, Local<Value>, 4900 const AccessorInfo&) { 4901 CHECK(false); // This function should nto be called. 4902} 4903 4904 4905THREADED_TEST(AccessControl) { 4906 v8::HandleScope handle_scope; 4907 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 4908 4909 global_template->SetAccessCheckCallbacks(NamedAccessBlocker, 4910 IndexedAccessBlocker); 4911 4912 // Add an accessor accessible by cross-domain JS code. 4913 global_template->SetAccessor( 4914 v8_str("accessible_prop"), 4915 EchoGetter, EchoSetter, 4916 v8::Handle<Value>(), 4917 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); 4918 4919 // Add an accessor that is not accessible by cross-domain JS code. 4920 global_template->SetAccessor(v8_str("blocked_prop"), 4921 UnreachableGetter, UnreachableSetter, 4922 v8::Handle<Value>(), 4923 v8::DEFAULT); 4924 4925 // Create an environment 4926 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 4927 context0->Enter(); 4928 4929 v8::Handle<v8::Object> global0 = context0->Global(); 4930 4931 v8::HandleScope scope1; 4932 4933 v8::Persistent<Context> context1 = Context::New(); 4934 context1->Enter(); 4935 4936 v8::Handle<v8::Object> global1 = context1->Global(); 4937 global1->Set(v8_str("other"), global0); 4938 4939 v8::Handle<Value> value; 4940 4941 // Access blocked property 4942 value = v8_compile("other.blocked_prop = 1")->Run(); 4943 value = v8_compile("other.blocked_prop")->Run(); 4944 CHECK(value->IsUndefined()); 4945 4946 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run(); 4947 CHECK(value->IsFalse()); 4948 4949 // Access accessible property 4950 value = v8_compile("other.accessible_prop = 3")->Run(); 4951 CHECK(value->IsNumber()); 4952 CHECK_EQ(3, value->Int32Value()); 4953 CHECK_EQ(3, g_echo_value); 4954 4955 value = v8_compile("other.accessible_prop")->Run(); 4956 CHECK(value->IsNumber()); 4957 CHECK_EQ(3, value->Int32Value()); 4958 4959 value = 4960 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run(); 4961 CHECK(value->IsTrue()); 4962 4963 // Enumeration doesn't enumerate accessors from inaccessible objects in 4964 // the prototype chain even if the accessors are in themselves accessible. 4965 Local<Value> result = 4966 CompileRun("(function(){var obj = {'__proto__':other};" 4967 "for (var p in obj)" 4968 " if (p == 'accessible_prop' || p == 'blocked_prop') {" 4969 " return false;" 4970 " }" 4971 "return true;})()"); 4972 CHECK(result->IsTrue()); 4973 4974 context1->Exit(); 4975 context0->Exit(); 4976 context1.Dispose(); 4977 context0.Dispose(); 4978} 4979 4980 4981static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global, 4982 Local<Value> name, 4983 v8::AccessType type, 4984 Local<Value> data) { 4985 return false; 4986} 4987 4988 4989static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global, 4990 uint32_t key, 4991 v8::AccessType type, 4992 Local<Value> data) { 4993 return false; 4994} 4995 4996 4997THREADED_TEST(AccessControlGetOwnPropertyNames) { 4998 v8::HandleScope handle_scope; 4999 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(); 5000 5001 obj_template->Set(v8_str("x"), v8::Integer::New(42)); 5002 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker, 5003 GetOwnPropertyNamesIndexedBlocker); 5004 5005 // Create an environment 5006 v8::Persistent<Context> context0 = Context::New(NULL, obj_template); 5007 context0->Enter(); 5008 5009 v8::Handle<v8::Object> global0 = context0->Global(); 5010 5011 v8::HandleScope scope1; 5012 5013 v8::Persistent<Context> context1 = Context::New(); 5014 context1->Enter(); 5015 5016 v8::Handle<v8::Object> global1 = context1->Global(); 5017 global1->Set(v8_str("other"), global0); 5018 global1->Set(v8_str("object"), obj_template->NewInstance()); 5019 5020 v8::Handle<Value> value; 5021 5022 // Attempt to get the property names of the other global object and 5023 // of an object that requires access checks. Accessing the other 5024 // global object should be blocked by access checks on the global 5025 // proxy object. Accessing the object that requires access checks 5026 // is blocked by the access checks on the object itself. 5027 value = CompileRun("Object.getOwnPropertyNames(other).length == 0"); 5028 CHECK(value->IsTrue()); 5029 5030 value = CompileRun("Object.getOwnPropertyNames(object).length == 0"); 5031 CHECK(value->IsTrue()); 5032 5033 context1->Exit(); 5034 context0->Exit(); 5035 context1.Dispose(); 5036 context0.Dispose(); 5037} 5038 5039 5040static v8::Handle<Value> ConstTenGetter(Local<String> name, 5041 const AccessorInfo& info) { 5042 return v8_num(10); 5043} 5044 5045 5046THREADED_TEST(CrossDomainAccessors) { 5047 v8::HandleScope handle_scope; 5048 5049 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(); 5050 5051 v8::Handle<v8::ObjectTemplate> global_template = 5052 func_template->InstanceTemplate(); 5053 5054 v8::Handle<v8::ObjectTemplate> proto_template = 5055 func_template->PrototypeTemplate(); 5056 5057 // Add an accessor to proto that's accessible by cross-domain JS code. 5058 proto_template->SetAccessor(v8_str("accessible"), 5059 ConstTenGetter, 0, 5060 v8::Handle<Value>(), 5061 v8::ALL_CAN_READ); 5062 5063 // Add an accessor that is not accessible by cross-domain JS code. 5064 global_template->SetAccessor(v8_str("unreachable"), 5065 UnreachableGetter, 0, 5066 v8::Handle<Value>(), 5067 v8::DEFAULT); 5068 5069 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 5070 context0->Enter(); 5071 5072 Local<v8::Object> global = context0->Global(); 5073 // Add a normal property that shadows 'accessible' 5074 global->Set(v8_str("accessible"), v8_num(11)); 5075 5076 // Enter a new context. 5077 v8::HandleScope scope1; 5078 v8::Persistent<Context> context1 = Context::New(); 5079 context1->Enter(); 5080 5081 v8::Handle<v8::Object> global1 = context1->Global(); 5082 global1->Set(v8_str("other"), global); 5083 5084 // Should return 10, instead of 11 5085 v8::Handle<Value> value = v8_compile("other.accessible")->Run(); 5086 CHECK(value->IsNumber()); 5087 CHECK_EQ(10, value->Int32Value()); 5088 5089 value = v8_compile("other.unreachable")->Run(); 5090 CHECK(value->IsUndefined()); 5091 5092 context1->Exit(); 5093 context0->Exit(); 5094 context1.Dispose(); 5095 context0.Dispose(); 5096} 5097 5098 5099static int named_access_count = 0; 5100static int indexed_access_count = 0; 5101 5102static bool NamedAccessCounter(Local<v8::Object> global, 5103 Local<Value> name, 5104 v8::AccessType type, 5105 Local<Value> data) { 5106 named_access_count++; 5107 return true; 5108} 5109 5110 5111static bool IndexedAccessCounter(Local<v8::Object> global, 5112 uint32_t key, 5113 v8::AccessType type, 5114 Local<Value> data) { 5115 indexed_access_count++; 5116 return true; 5117} 5118 5119 5120// This one is too easily disturbed by other tests. 5121TEST(AccessControlIC) { 5122 named_access_count = 0; 5123 indexed_access_count = 0; 5124 5125 v8::HandleScope handle_scope; 5126 5127 // Create an environment. 5128 v8::Persistent<Context> context0 = Context::New(); 5129 context0->Enter(); 5130 5131 // Create an object that requires access-check functions to be 5132 // called for cross-domain access. 5133 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 5134 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 5135 IndexedAccessCounter); 5136 Local<v8::Object> object = object_template->NewInstance(); 5137 5138 v8::HandleScope scope1; 5139 5140 // Create another environment. 5141 v8::Persistent<Context> context1 = Context::New(); 5142 context1->Enter(); 5143 5144 // Make easy access to the object from the other environment. 5145 v8::Handle<v8::Object> global1 = context1->Global(); 5146 global1->Set(v8_str("obj"), object); 5147 5148 v8::Handle<Value> value; 5149 5150 // Check that the named access-control function is called every time. 5151 CompileRun("function testProp(obj) {" 5152 " for (var i = 0; i < 10; i++) obj.prop = 1;" 5153 " for (var j = 0; j < 10; j++) obj.prop;" 5154 " return obj.prop" 5155 "}"); 5156 value = CompileRun("testProp(obj)"); 5157 CHECK(value->IsNumber()); 5158 CHECK_EQ(1, value->Int32Value()); 5159 CHECK_EQ(21, named_access_count); 5160 5161 // Check that the named access-control function is called every time. 5162 CompileRun("var p = 'prop';" 5163 "function testKeyed(obj) {" 5164 " for (var i = 0; i < 10; i++) obj[p] = 1;" 5165 " for (var j = 0; j < 10; j++) obj[p];" 5166 " return obj[p];" 5167 "}"); 5168 // Use obj which requires access checks. No inline caching is used 5169 // in that case. 5170 value = CompileRun("testKeyed(obj)"); 5171 CHECK(value->IsNumber()); 5172 CHECK_EQ(1, value->Int32Value()); 5173 CHECK_EQ(42, named_access_count); 5174 // Force the inline caches into generic state and try again. 5175 CompileRun("testKeyed({ a: 0 })"); 5176 CompileRun("testKeyed({ b: 0 })"); 5177 value = CompileRun("testKeyed(obj)"); 5178 CHECK(value->IsNumber()); 5179 CHECK_EQ(1, value->Int32Value()); 5180 CHECK_EQ(63, named_access_count); 5181 5182 // Check that the indexed access-control function is called every time. 5183 CompileRun("function testIndexed(obj) {" 5184 " for (var i = 0; i < 10; i++) obj[0] = 1;" 5185 " for (var j = 0; j < 10; j++) obj[0];" 5186 " return obj[0]" 5187 "}"); 5188 value = CompileRun("testIndexed(obj)"); 5189 CHECK(value->IsNumber()); 5190 CHECK_EQ(1, value->Int32Value()); 5191 CHECK_EQ(21, indexed_access_count); 5192 // Force the inline caches into generic state. 5193 CompileRun("testIndexed(new Array(1))"); 5194 // Test that the indexed access check is called. 5195 value = CompileRun("testIndexed(obj)"); 5196 CHECK(value->IsNumber()); 5197 CHECK_EQ(1, value->Int32Value()); 5198 CHECK_EQ(42, indexed_access_count); 5199 5200 // Check that the named access check is called when invoking 5201 // functions on an object that requires access checks. 5202 CompileRun("obj.f = function() {}"); 5203 CompileRun("function testCallNormal(obj) {" 5204 " for (var i = 0; i < 10; i++) obj.f();" 5205 "}"); 5206 CompileRun("testCallNormal(obj)"); 5207 CHECK_EQ(74, named_access_count); 5208 5209 // Force obj into slow case. 5210 value = CompileRun("delete obj.prop"); 5211 CHECK(value->BooleanValue()); 5212 // Force inline caches into dictionary probing mode. 5213 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);"); 5214 // Test that the named access check is called. 5215 value = CompileRun("testProp(obj);"); 5216 CHECK(value->IsNumber()); 5217 CHECK_EQ(1, value->Int32Value()); 5218 CHECK_EQ(96, named_access_count); 5219 5220 // Force the call inline cache into dictionary probing mode. 5221 CompileRun("o.f = function() {}; testCallNormal(o)"); 5222 // Test that the named access check is still called for each 5223 // invocation of the function. 5224 value = CompileRun("testCallNormal(obj)"); 5225 CHECK_EQ(106, named_access_count); 5226 5227 context1->Exit(); 5228 context0->Exit(); 5229 context1.Dispose(); 5230 context0.Dispose(); 5231} 5232 5233 5234static bool NamedAccessFlatten(Local<v8::Object> global, 5235 Local<Value> name, 5236 v8::AccessType type, 5237 Local<Value> data) { 5238 char buf[100]; 5239 int len; 5240 5241 CHECK(name->IsString()); 5242 5243 memset(buf, 0x1, sizeof(buf)); 5244 len = name.As<String>()->WriteAscii(buf); 5245 CHECK_EQ(4, len); 5246 5247 uint16_t buf2[100]; 5248 5249 memset(buf, 0x1, sizeof(buf)); 5250 len = name.As<String>()->Write(buf2); 5251 CHECK_EQ(4, len); 5252 5253 return true; 5254} 5255 5256 5257static bool IndexedAccessFlatten(Local<v8::Object> global, 5258 uint32_t key, 5259 v8::AccessType type, 5260 Local<Value> data) { 5261 return true; 5262} 5263 5264 5265// Regression test. In access checks, operations that may cause 5266// garbage collection are not allowed. It used to be the case that 5267// using the Write operation on a string could cause a garbage 5268// collection due to flattening of the string. This is no longer the 5269// case. 5270THREADED_TEST(AccessControlFlatten) { 5271 named_access_count = 0; 5272 indexed_access_count = 0; 5273 5274 v8::HandleScope handle_scope; 5275 5276 // Create an environment. 5277 v8::Persistent<Context> context0 = Context::New(); 5278 context0->Enter(); 5279 5280 // Create an object that requires access-check functions to be 5281 // called for cross-domain access. 5282 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 5283 object_template->SetAccessCheckCallbacks(NamedAccessFlatten, 5284 IndexedAccessFlatten); 5285 Local<v8::Object> object = object_template->NewInstance(); 5286 5287 v8::HandleScope scope1; 5288 5289 // Create another environment. 5290 v8::Persistent<Context> context1 = Context::New(); 5291 context1->Enter(); 5292 5293 // Make easy access to the object from the other environment. 5294 v8::Handle<v8::Object> global1 = context1->Global(); 5295 global1->Set(v8_str("obj"), object); 5296 5297 v8::Handle<Value> value; 5298 5299 value = v8_compile("var p = 'as' + 'df';")->Run(); 5300 value = v8_compile("obj[p];")->Run(); 5301 5302 context1->Exit(); 5303 context0->Exit(); 5304 context1.Dispose(); 5305 context0.Dispose(); 5306} 5307 5308 5309static v8::Handle<Value> AccessControlNamedGetter( 5310 Local<String>, const AccessorInfo&) { 5311 return v8::Integer::New(42); 5312} 5313 5314 5315static v8::Handle<Value> AccessControlNamedSetter( 5316 Local<String>, Local<Value> value, const AccessorInfo&) { 5317 return value; 5318} 5319 5320 5321static v8::Handle<Value> AccessControlIndexedGetter( 5322 uint32_t index, 5323 const AccessorInfo& info) { 5324 return v8_num(42); 5325} 5326 5327 5328static v8::Handle<Value> AccessControlIndexedSetter( 5329 uint32_t, Local<Value> value, const AccessorInfo&) { 5330 return value; 5331} 5332 5333 5334THREADED_TEST(AccessControlInterceptorIC) { 5335 named_access_count = 0; 5336 indexed_access_count = 0; 5337 5338 v8::HandleScope handle_scope; 5339 5340 // Create an environment. 5341 v8::Persistent<Context> context0 = Context::New(); 5342 context0->Enter(); 5343 5344 // Create an object that requires access-check functions to be 5345 // called for cross-domain access. The object also has interceptors 5346 // interceptor. 5347 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 5348 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 5349 IndexedAccessCounter); 5350 object_template->SetNamedPropertyHandler(AccessControlNamedGetter, 5351 AccessControlNamedSetter); 5352 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter, 5353 AccessControlIndexedSetter); 5354 Local<v8::Object> object = object_template->NewInstance(); 5355 5356 v8::HandleScope scope1; 5357 5358 // Create another environment. 5359 v8::Persistent<Context> context1 = Context::New(); 5360 context1->Enter(); 5361 5362 // Make easy access to the object from the other environment. 5363 v8::Handle<v8::Object> global1 = context1->Global(); 5364 global1->Set(v8_str("obj"), object); 5365 5366 v8::Handle<Value> value; 5367 5368 // Check that the named access-control function is called every time 5369 // eventhough there is an interceptor on the object. 5370 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run(); 5371 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;" 5372 "obj.x")->Run(); 5373 CHECK(value->IsNumber()); 5374 CHECK_EQ(42, value->Int32Value()); 5375 CHECK_EQ(21, named_access_count); 5376 5377 value = v8_compile("var p = 'x';")->Run(); 5378 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run(); 5379 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];" 5380 "obj[p]")->Run(); 5381 CHECK(value->IsNumber()); 5382 CHECK_EQ(42, value->Int32Value()); 5383 CHECK_EQ(42, named_access_count); 5384 5385 // Check that the indexed access-control function is called every 5386 // time eventhough there is an interceptor on the object. 5387 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run(); 5388 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];" 5389 "obj[0]")->Run(); 5390 CHECK(value->IsNumber()); 5391 CHECK_EQ(42, value->Int32Value()); 5392 CHECK_EQ(21, indexed_access_count); 5393 5394 context1->Exit(); 5395 context0->Exit(); 5396 context1.Dispose(); 5397 context0.Dispose(); 5398} 5399 5400 5401THREADED_TEST(Version) { 5402 v8::V8::GetVersion(); 5403} 5404 5405 5406static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) { 5407 ApiTestFuzzer::Fuzz(); 5408 return v8_num(12); 5409} 5410 5411 5412THREADED_TEST(InstanceProperties) { 5413 v8::HandleScope handle_scope; 5414 LocalContext context; 5415 5416 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5417 Local<ObjectTemplate> instance = t->InstanceTemplate(); 5418 5419 instance->Set(v8_str("x"), v8_num(42)); 5420 instance->Set(v8_str("f"), 5421 v8::FunctionTemplate::New(InstanceFunctionCallback)); 5422 5423 Local<Value> o = t->GetFunction()->NewInstance(); 5424 5425 context->Global()->Set(v8_str("i"), o); 5426 Local<Value> value = Script::Compile(v8_str("i.x"))->Run(); 5427 CHECK_EQ(42, value->Int32Value()); 5428 5429 value = Script::Compile(v8_str("i.f()"))->Run(); 5430 CHECK_EQ(12, value->Int32Value()); 5431} 5432 5433 5434static v8::Handle<Value> 5435GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) { 5436 ApiTestFuzzer::Fuzz(); 5437 return v8::Handle<Value>(); 5438} 5439 5440 5441THREADED_TEST(GlobalObjectInstanceProperties) { 5442 v8::HandleScope handle_scope; 5443 5444 Local<Value> global_object; 5445 5446 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5447 t->InstanceTemplate()->SetNamedPropertyHandler( 5448 GlobalObjectInstancePropertiesGet); 5449 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 5450 instance_template->Set(v8_str("x"), v8_num(42)); 5451 instance_template->Set(v8_str("f"), 5452 v8::FunctionTemplate::New(InstanceFunctionCallback)); 5453 5454 { 5455 LocalContext env(NULL, instance_template); 5456 // Hold on to the global object so it can be used again in another 5457 // environment initialization. 5458 global_object = env->Global(); 5459 5460 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 5461 CHECK_EQ(42, value->Int32Value()); 5462 value = Script::Compile(v8_str("f()"))->Run(); 5463 CHECK_EQ(12, value->Int32Value()); 5464 } 5465 5466 { 5467 // Create new environment reusing the global object. 5468 LocalContext env(NULL, instance_template, global_object); 5469 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 5470 CHECK_EQ(42, value->Int32Value()); 5471 value = Script::Compile(v8_str("f()"))->Run(); 5472 CHECK_EQ(12, value->Int32Value()); 5473 } 5474} 5475 5476 5477static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) { 5478 ApiTestFuzzer::Fuzz(); 5479 return v8_num(42); 5480} 5481 5482 5483static int shadow_y; 5484static int shadow_y_setter_call_count; 5485static int shadow_y_getter_call_count; 5486 5487 5488static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) { 5489 shadow_y_setter_call_count++; 5490 shadow_y = 42; 5491} 5492 5493 5494static v8::Handle<Value> ShadowYGetter(Local<String> name, 5495 const AccessorInfo& info) { 5496 ApiTestFuzzer::Fuzz(); 5497 shadow_y_getter_call_count++; 5498 return v8_num(shadow_y); 5499} 5500 5501 5502static v8::Handle<Value> ShadowIndexedGet(uint32_t index, 5503 const AccessorInfo& info) { 5504 return v8::Handle<Value>(); 5505} 5506 5507 5508static v8::Handle<Value> ShadowNamedGet(Local<String> key, 5509 const AccessorInfo&) { 5510 return v8::Handle<Value>(); 5511} 5512 5513 5514THREADED_TEST(ShadowObject) { 5515 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0; 5516 v8::HandleScope handle_scope; 5517 5518 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New(); 5519 LocalContext context(NULL, global_template); 5520 5521 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5522 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet); 5523 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet); 5524 Local<ObjectTemplate> proto = t->PrototypeTemplate(); 5525 Local<ObjectTemplate> instance = t->InstanceTemplate(); 5526 5527 // Only allow calls of f on instances of t. 5528 Local<v8::Signature> signature = v8::Signature::New(t); 5529 proto->Set(v8_str("f"), 5530 v8::FunctionTemplate::New(ShadowFunctionCallback, 5531 Local<Value>(), 5532 signature)); 5533 proto->Set(v8_str("x"), v8_num(12)); 5534 5535 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter); 5536 5537 Local<Value> o = t->GetFunction()->NewInstance(); 5538 context->Global()->Set(v8_str("__proto__"), o); 5539 5540 Local<Value> value = 5541 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run(); 5542 CHECK(value->IsBoolean()); 5543 CHECK(!value->BooleanValue()); 5544 5545 value = Script::Compile(v8_str("x"))->Run(); 5546 CHECK_EQ(12, value->Int32Value()); 5547 5548 value = Script::Compile(v8_str("f()"))->Run(); 5549 CHECK_EQ(42, value->Int32Value()); 5550 5551 Script::Compile(v8_str("y = 42"))->Run(); 5552 CHECK_EQ(1, shadow_y_setter_call_count); 5553 value = Script::Compile(v8_str("y"))->Run(); 5554 CHECK_EQ(1, shadow_y_getter_call_count); 5555 CHECK_EQ(42, value->Int32Value()); 5556} 5557 5558 5559THREADED_TEST(HiddenPrototype) { 5560 v8::HandleScope handle_scope; 5561 LocalContext context; 5562 5563 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 5564 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 5565 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 5566 t1->SetHiddenPrototype(true); 5567 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 5568 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 5569 t2->SetHiddenPrototype(true); 5570 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 5571 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 5572 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 5573 5574 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 5575 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 5576 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 5577 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 5578 5579 // Setting the prototype on an object skips hidden prototypes. 5580 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5581 o0->Set(v8_str("__proto__"), o1); 5582 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5583 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5584 o0->Set(v8_str("__proto__"), o2); 5585 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5586 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5587 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5588 o0->Set(v8_str("__proto__"), o3); 5589 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5590 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5591 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5592 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 5593 5594 // Getting the prototype of o0 should get the first visible one 5595 // which is o3. Therefore, z should not be defined on the prototype 5596 // object. 5597 Local<Value> proto = o0->Get(v8_str("__proto__")); 5598 CHECK(proto->IsObject()); 5599 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined()); 5600} 5601 5602 5603THREADED_TEST(SetPrototype) { 5604 v8::HandleScope handle_scope; 5605 LocalContext context; 5606 5607 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 5608 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 5609 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 5610 t1->SetHiddenPrototype(true); 5611 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 5612 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 5613 t2->SetHiddenPrototype(true); 5614 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 5615 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 5616 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 5617 5618 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 5619 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 5620 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 5621 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 5622 5623 // Setting the prototype on an object does not skip hidden prototypes. 5624 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5625 CHECK(o0->SetPrototype(o1)); 5626 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5627 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5628 CHECK(o1->SetPrototype(o2)); 5629 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5630 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5631 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5632 CHECK(o2->SetPrototype(o3)); 5633 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5634 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5635 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5636 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 5637 5638 // Getting the prototype of o0 should get the first visible one 5639 // which is o3. Therefore, z should not be defined on the prototype 5640 // object. 5641 Local<Value> proto = o0->Get(v8_str("__proto__")); 5642 CHECK(proto->IsObject()); 5643 CHECK_EQ(proto.As<v8::Object>(), o3); 5644 5645 // However, Object::GetPrototype ignores hidden prototype. 5646 Local<Value> proto0 = o0->GetPrototype(); 5647 CHECK(proto0->IsObject()); 5648 CHECK_EQ(proto0.As<v8::Object>(), o1); 5649 5650 Local<Value> proto1 = o1->GetPrototype(); 5651 CHECK(proto1->IsObject()); 5652 CHECK_EQ(proto1.As<v8::Object>(), o2); 5653 5654 Local<Value> proto2 = o2->GetPrototype(); 5655 CHECK(proto2->IsObject()); 5656 CHECK_EQ(proto2.As<v8::Object>(), o3); 5657} 5658 5659 5660THREADED_TEST(SetPrototypeThrows) { 5661 v8::HandleScope handle_scope; 5662 LocalContext context; 5663 5664 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5665 5666 Local<v8::Object> o0 = t->GetFunction()->NewInstance(); 5667 Local<v8::Object> o1 = t->GetFunction()->NewInstance(); 5668 5669 CHECK(o0->SetPrototype(o1)); 5670 // If setting the prototype leads to the cycle, SetPrototype should 5671 // return false and keep VM in sane state. 5672 v8::TryCatch try_catch; 5673 CHECK(!o1->SetPrototype(o0)); 5674 CHECK(!try_catch.HasCaught()); 5675 ASSERT(!i::Top::has_pending_exception()); 5676 5677 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value()); 5678} 5679 5680 5681THREADED_TEST(GetterSetterExceptions) { 5682 v8::HandleScope handle_scope; 5683 LocalContext context; 5684 CompileRun( 5685 "function Foo() { };" 5686 "function Throw() { throw 5; };" 5687 "var x = { };" 5688 "x.__defineSetter__('set', Throw);" 5689 "x.__defineGetter__('get', Throw);"); 5690 Local<v8::Object> x = 5691 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x"))); 5692 v8::TryCatch try_catch; 5693 x->Set(v8_str("set"), v8::Integer::New(8)); 5694 x->Get(v8_str("get")); 5695 x->Set(v8_str("set"), v8::Integer::New(8)); 5696 x->Get(v8_str("get")); 5697 x->Set(v8_str("set"), v8::Integer::New(8)); 5698 x->Get(v8_str("get")); 5699 x->Set(v8_str("set"), v8::Integer::New(8)); 5700 x->Get(v8_str("get")); 5701} 5702 5703 5704THREADED_TEST(Constructor) { 5705 v8::HandleScope handle_scope; 5706 LocalContext context; 5707 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 5708 templ->SetClassName(v8_str("Fun")); 5709 Local<Function> cons = templ->GetFunction(); 5710 context->Global()->Set(v8_str("Fun"), cons); 5711 Local<v8::Object> inst = cons->NewInstance(); 5712 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst); 5713 Local<Value> value = CompileRun("(new Fun()).constructor === Fun"); 5714 CHECK(value->BooleanValue()); 5715} 5716 5717THREADED_TEST(FunctionDescriptorException) { 5718 v8::HandleScope handle_scope; 5719 LocalContext context; 5720 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 5721 templ->SetClassName(v8_str("Fun")); 5722 Local<Function> cons = templ->GetFunction(); 5723 context->Global()->Set(v8_str("Fun"), cons); 5724 Local<Value> value = CompileRun( 5725 "function test() {" 5726 " try {" 5727 " (new Fun()).blah()" 5728 " } catch (e) {" 5729 " var str = String(e);" 5730 " if (str.indexOf('TypeError') == -1) return 1;" 5731 " if (str.indexOf('[object Fun]') != -1) return 2;" 5732 " if (str.indexOf('#<a Fun>') == -1) return 3;" 5733 " return 0;" 5734 " }" 5735 " return 4;" 5736 "}" 5737 "test();"); 5738 CHECK_EQ(0, value->Int32Value()); 5739} 5740 5741 5742THREADED_TEST(EvalAliasedDynamic) { 5743 v8::HandleScope scope; 5744 LocalContext current; 5745 5746 // Tests where aliased eval can only be resolved dynamically. 5747 Local<Script> script = 5748 Script::Compile(v8_str("function f(x) { " 5749 " var foo = 2;" 5750 " with (x) { return eval('foo'); }" 5751 "}" 5752 "foo = 0;" 5753 "result1 = f(new Object());" 5754 "result2 = f(this);" 5755 "var x = new Object();" 5756 "x.eval = function(x) { return 1; };" 5757 "result3 = f(x);")); 5758 script->Run(); 5759 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value()); 5760 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value()); 5761 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value()); 5762 5763 v8::TryCatch try_catch; 5764 script = 5765 Script::Compile(v8_str("function f(x) { " 5766 " var bar = 2;" 5767 " with (x) { return eval('bar'); }" 5768 "}" 5769 "f(this)")); 5770 script->Run(); 5771 CHECK(try_catch.HasCaught()); 5772 try_catch.Reset(); 5773} 5774 5775 5776THREADED_TEST(CrossEval) { 5777 v8::HandleScope scope; 5778 LocalContext other; 5779 LocalContext current; 5780 5781 Local<String> token = v8_str("<security token>"); 5782 other->SetSecurityToken(token); 5783 current->SetSecurityToken(token); 5784 5785 // Setup reference from current to other. 5786 current->Global()->Set(v8_str("other"), other->Global()); 5787 5788 // Check that new variables are introduced in other context. 5789 Local<Script> script = 5790 Script::Compile(v8_str("other.eval('var foo = 1234')")); 5791 script->Run(); 5792 Local<Value> foo = other->Global()->Get(v8_str("foo")); 5793 CHECK_EQ(1234, foo->Int32Value()); 5794 CHECK(!current->Global()->Has(v8_str("foo"))); 5795 5796 // Check that writing to non-existing properties introduces them in 5797 // the other context. 5798 script = 5799 Script::Compile(v8_str("other.eval('na = 1234')")); 5800 script->Run(); 5801 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value()); 5802 CHECK(!current->Global()->Has(v8_str("na"))); 5803 5804 // Check that global variables in current context are not visible in other 5805 // context. 5806 v8::TryCatch try_catch; 5807 script = 5808 Script::Compile(v8_str("var bar = 42; other.eval('bar');")); 5809 Local<Value> result = script->Run(); 5810 CHECK(try_catch.HasCaught()); 5811 try_catch.Reset(); 5812 5813 // Check that local variables in current context are not visible in other 5814 // context. 5815 script = 5816 Script::Compile(v8_str("(function() { " 5817 " var baz = 87;" 5818 " return other.eval('baz');" 5819 "})();")); 5820 result = script->Run(); 5821 CHECK(try_catch.HasCaught()); 5822 try_catch.Reset(); 5823 5824 // Check that global variables in the other environment are visible 5825 // when evaluting code. 5826 other->Global()->Set(v8_str("bis"), v8_num(1234)); 5827 script = Script::Compile(v8_str("other.eval('bis')")); 5828 CHECK_EQ(1234, script->Run()->Int32Value()); 5829 CHECK(!try_catch.HasCaught()); 5830 5831 // Check that the 'this' pointer points to the global object evaluating 5832 // code. 5833 other->Global()->Set(v8_str("t"), other->Global()); 5834 script = Script::Compile(v8_str("other.eval('this == t')")); 5835 result = script->Run(); 5836 CHECK(result->IsTrue()); 5837 CHECK(!try_catch.HasCaught()); 5838 5839 // Check that variables introduced in with-statement are not visible in 5840 // other context. 5841 script = 5842 Script::Compile(v8_str("with({x:2}){other.eval('x')}")); 5843 result = script->Run(); 5844 CHECK(try_catch.HasCaught()); 5845 try_catch.Reset(); 5846 5847 // Check that you cannot use 'eval.call' with another object than the 5848 // current global object. 5849 script = 5850 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')")); 5851 result = script->Run(); 5852 CHECK(try_catch.HasCaught()); 5853} 5854 5855 5856// Test that calling eval in a context which has been detached from 5857// its global throws an exception. This behavior is consistent with 5858// other JavaScript implementations. 5859THREADED_TEST(EvalInDetachedGlobal) { 5860 v8::HandleScope scope; 5861 5862 v8::Persistent<Context> context0 = Context::New(); 5863 v8::Persistent<Context> context1 = Context::New(); 5864 5865 // Setup function in context0 that uses eval from context0. 5866 context0->Enter(); 5867 v8::Handle<v8::Value> fun = 5868 CompileRun("var x = 42;" 5869 "(function() {" 5870 " var e = eval;" 5871 " return function(s) { return e(s); }" 5872 "})()"); 5873 context0->Exit(); 5874 5875 // Put the function into context1 and call it before and after 5876 // detaching the global. Before detaching, the call succeeds and 5877 // after detaching and exception is thrown. 5878 context1->Enter(); 5879 context1->Global()->Set(v8_str("fun"), fun); 5880 v8::Handle<v8::Value> x_value = CompileRun("fun('x')"); 5881 CHECK_EQ(42, x_value->Int32Value()); 5882 context0->DetachGlobal(); 5883 v8::TryCatch catcher; 5884 x_value = CompileRun("fun('x')"); 5885 CHECK(x_value.IsEmpty()); 5886 CHECK(catcher.HasCaught()); 5887 context1->Exit(); 5888 5889 context1.Dispose(); 5890 context0.Dispose(); 5891} 5892 5893 5894THREADED_TEST(CrossLazyLoad) { 5895 v8::HandleScope scope; 5896 LocalContext other; 5897 LocalContext current; 5898 5899 Local<String> token = v8_str("<security token>"); 5900 other->SetSecurityToken(token); 5901 current->SetSecurityToken(token); 5902 5903 // Setup reference from current to other. 5904 current->Global()->Set(v8_str("other"), other->Global()); 5905 5906 // Trigger lazy loading in other context. 5907 Local<Script> script = 5908 Script::Compile(v8_str("other.eval('new Date(42)')")); 5909 Local<Value> value = script->Run(); 5910 CHECK_EQ(42.0, value->NumberValue()); 5911} 5912 5913 5914static v8::Handle<Value> call_as_function(const v8::Arguments& args) { 5915 ApiTestFuzzer::Fuzz(); 5916 if (args.IsConstructCall()) { 5917 if (args[0]->IsInt32()) { 5918 return v8_num(-args[0]->Int32Value()); 5919 } 5920 } 5921 5922 return args[0]; 5923} 5924 5925 5926// Test that a call handler can be set for objects which will allow 5927// non-function objects created through the API to be called as 5928// functions. 5929THREADED_TEST(CallAsFunction) { 5930 v8::HandleScope scope; 5931 LocalContext context; 5932 5933 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5934 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 5935 instance_template->SetCallAsFunctionHandler(call_as_function); 5936 Local<v8::Object> instance = t->GetFunction()->NewInstance(); 5937 context->Global()->Set(v8_str("obj"), instance); 5938 v8::TryCatch try_catch; 5939 Local<Value> value; 5940 CHECK(!try_catch.HasCaught()); 5941 5942 value = CompileRun("obj(42)"); 5943 CHECK(!try_catch.HasCaught()); 5944 CHECK_EQ(42, value->Int32Value()); 5945 5946 value = CompileRun("(function(o){return o(49)})(obj)"); 5947 CHECK(!try_catch.HasCaught()); 5948 CHECK_EQ(49, value->Int32Value()); 5949 5950 // test special case of call as function 5951 value = CompileRun("[obj]['0'](45)"); 5952 CHECK(!try_catch.HasCaught()); 5953 CHECK_EQ(45, value->Int32Value()); 5954 5955 value = CompileRun("obj.call = Function.prototype.call;" 5956 "obj.call(null, 87)"); 5957 CHECK(!try_catch.HasCaught()); 5958 CHECK_EQ(87, value->Int32Value()); 5959 5960 // Regression tests for bug #1116356: Calling call through call/apply 5961 // must work for non-function receivers. 5962 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])"; 5963 value = CompileRun(apply_99); 5964 CHECK(!try_catch.HasCaught()); 5965 CHECK_EQ(99, value->Int32Value()); 5966 5967 const char* call_17 = "Function.prototype.call.call(obj, this, 17)"; 5968 value = CompileRun(call_17); 5969 CHECK(!try_catch.HasCaught()); 5970 CHECK_EQ(17, value->Int32Value()); 5971 5972 // Check that the call-as-function handler can be called through 5973 // new. 5974 value = CompileRun("new obj(43)"); 5975 CHECK(!try_catch.HasCaught()); 5976 CHECK_EQ(-43, value->Int32Value()); 5977} 5978 5979 5980static int CountHandles() { 5981 return v8::HandleScope::NumberOfHandles(); 5982} 5983 5984 5985static int Recurse(int depth, int iterations) { 5986 v8::HandleScope scope; 5987 if (depth == 0) return CountHandles(); 5988 for (int i = 0; i < iterations; i++) { 5989 Local<v8::Number> n = v8::Integer::New(42); 5990 } 5991 return Recurse(depth - 1, iterations); 5992} 5993 5994 5995THREADED_TEST(HandleIteration) { 5996 static const int kIterations = 500; 5997 static const int kNesting = 200; 5998 CHECK_EQ(0, CountHandles()); 5999 { 6000 v8::HandleScope scope1; 6001 CHECK_EQ(0, CountHandles()); 6002 for (int i = 0; i < kIterations; i++) { 6003 Local<v8::Number> n = v8::Integer::New(42); 6004 CHECK_EQ(i + 1, CountHandles()); 6005 } 6006 6007 CHECK_EQ(kIterations, CountHandles()); 6008 { 6009 v8::HandleScope scope2; 6010 for (int j = 0; j < kIterations; j++) { 6011 Local<v8::Number> n = v8::Integer::New(42); 6012 CHECK_EQ(j + 1 + kIterations, CountHandles()); 6013 } 6014 } 6015 CHECK_EQ(kIterations, CountHandles()); 6016 } 6017 CHECK_EQ(0, CountHandles()); 6018 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations)); 6019} 6020 6021 6022static v8::Handle<Value> InterceptorHasOwnPropertyGetter( 6023 Local<String> name, 6024 const AccessorInfo& info) { 6025 ApiTestFuzzer::Fuzz(); 6026 return v8::Handle<Value>(); 6027} 6028 6029 6030THREADED_TEST(InterceptorHasOwnProperty) { 6031 v8::HandleScope scope; 6032 LocalContext context; 6033 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6034 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 6035 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter); 6036 Local<Function> function = fun_templ->GetFunction(); 6037 context->Global()->Set(v8_str("constructor"), function); 6038 v8::Handle<Value> value = CompileRun( 6039 "var o = new constructor();" 6040 "o.hasOwnProperty('ostehaps');"); 6041 CHECK_EQ(false, value->BooleanValue()); 6042 value = CompileRun( 6043 "o.ostehaps = 42;" 6044 "o.hasOwnProperty('ostehaps');"); 6045 CHECK_EQ(true, value->BooleanValue()); 6046 value = CompileRun( 6047 "var p = new constructor();" 6048 "p.hasOwnProperty('ostehaps');"); 6049 CHECK_EQ(false, value->BooleanValue()); 6050} 6051 6052 6053static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC( 6054 Local<String> name, 6055 const AccessorInfo& info) { 6056 ApiTestFuzzer::Fuzz(); 6057 i::Heap::CollectAllGarbage(false); 6058 return v8::Handle<Value>(); 6059} 6060 6061 6062THREADED_TEST(InterceptorHasOwnPropertyCausingGC) { 6063 v8::HandleScope scope; 6064 LocalContext context; 6065 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6066 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 6067 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC); 6068 Local<Function> function = fun_templ->GetFunction(); 6069 context->Global()->Set(v8_str("constructor"), function); 6070 // Let's first make some stuff so we can be sure to get a good GC. 6071 CompileRun( 6072 "function makestr(size) {" 6073 " switch (size) {" 6074 " case 1: return 'f';" 6075 " case 2: return 'fo';" 6076 " case 3: return 'foo';" 6077 " }" 6078 " return makestr(size >> 1) + makestr((size + 1) >> 1);" 6079 "}" 6080 "var x = makestr(12345);" 6081 "x = makestr(31415);" 6082 "x = makestr(23456);"); 6083 v8::Handle<Value> value = CompileRun( 6084 "var o = new constructor();" 6085 "o.__proto__ = new String(x);" 6086 "o.hasOwnProperty('ostehaps');"); 6087 CHECK_EQ(false, value->BooleanValue()); 6088} 6089 6090 6091typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property, 6092 const AccessorInfo& info); 6093 6094 6095static void CheckInterceptorLoadIC(NamedPropertyGetter getter, 6096 const char* source, 6097 int expected) { 6098 v8::HandleScope scope; 6099 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6100 templ->SetNamedPropertyHandler(getter); 6101 LocalContext context; 6102 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6103 v8::Handle<Value> value = CompileRun(source); 6104 CHECK_EQ(expected, value->Int32Value()); 6105} 6106 6107 6108static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name, 6109 const AccessorInfo& info) { 6110 ApiTestFuzzer::Fuzz(); 6111 CHECK(v8_str("x")->Equals(name)); 6112 return v8::Integer::New(42); 6113} 6114 6115 6116// This test should hit the load IC for the interceptor case. 6117THREADED_TEST(InterceptorLoadIC) { 6118 CheckInterceptorLoadIC(InterceptorLoadICGetter, 6119 "var result = 0;" 6120 "for (var i = 0; i < 1000; i++) {" 6121 " result = o.x;" 6122 "}", 6123 42); 6124} 6125 6126 6127// Below go several tests which verify that JITing for various 6128// configurations of interceptor and explicit fields works fine 6129// (those cases are special cased to get better performance). 6130 6131static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name, 6132 const AccessorInfo& info) { 6133 ApiTestFuzzer::Fuzz(); 6134 return v8_str("x")->Equals(name) 6135 ? v8::Integer::New(42) : v8::Handle<v8::Value>(); 6136} 6137 6138 6139THREADED_TEST(InterceptorLoadICWithFieldOnHolder) { 6140 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6141 "var result = 0;" 6142 "o.y = 239;" 6143 "for (var i = 0; i < 1000; i++) {" 6144 " result = o.y;" 6145 "}", 6146 239); 6147} 6148 6149 6150THREADED_TEST(InterceptorLoadICWithSubstitutedProto) { 6151 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6152 "var result = 0;" 6153 "o.__proto__ = { 'y': 239 };" 6154 "for (var i = 0; i < 1000; i++) {" 6155 " result = o.y + o.x;" 6156 "}", 6157 239 + 42); 6158} 6159 6160 6161THREADED_TEST(InterceptorLoadICWithPropertyOnProto) { 6162 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6163 "var result = 0;" 6164 "o.__proto__.y = 239;" 6165 "for (var i = 0; i < 1000; i++) {" 6166 " result = o.y + o.x;" 6167 "}", 6168 239 + 42); 6169} 6170 6171 6172THREADED_TEST(InterceptorLoadICUndefined) { 6173 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6174 "var result = 0;" 6175 "for (var i = 0; i < 1000; i++) {" 6176 " result = (o.y == undefined) ? 239 : 42;" 6177 "}", 6178 239); 6179} 6180 6181 6182THREADED_TEST(InterceptorLoadICWithOverride) { 6183 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6184 "fst = new Object(); fst.__proto__ = o;" 6185 "snd = new Object(); snd.__proto__ = fst;" 6186 "var result1 = 0;" 6187 "for (var i = 0; i < 1000; i++) {" 6188 " result1 = snd.x;" 6189 "}" 6190 "fst.x = 239;" 6191 "var result = 0;" 6192 "for (var i = 0; i < 1000; i++) {" 6193 " result = snd.x;" 6194 "}" 6195 "result + result1", 6196 239 + 42); 6197} 6198 6199 6200// Test the case when we stored field into 6201// a stub, but interceptor produced value on its own. 6202THREADED_TEST(InterceptorLoadICFieldNotNeeded) { 6203 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6204 "proto = new Object();" 6205 "o.__proto__ = proto;" 6206 "proto.x = 239;" 6207 "for (var i = 0; i < 1000; i++) {" 6208 " o.x;" 6209 // Now it should be ICed and keep a reference to x defined on proto 6210 "}" 6211 "var result = 0;" 6212 "for (var i = 0; i < 1000; i++) {" 6213 " result += o.x;" 6214 "}" 6215 "result;", 6216 42 * 1000); 6217} 6218 6219 6220// Test the case when we stored field into 6221// a stub, but it got invalidated later on. 6222THREADED_TEST(InterceptorLoadICInvalidatedField) { 6223 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6224 "proto1 = new Object();" 6225 "proto2 = new Object();" 6226 "o.__proto__ = proto1;" 6227 "proto1.__proto__ = proto2;" 6228 "proto2.y = 239;" 6229 "for (var i = 0; i < 1000; i++) {" 6230 " o.y;" 6231 // Now it should be ICed and keep a reference to y defined on proto2 6232 "}" 6233 "proto1.y = 42;" 6234 "var result = 0;" 6235 "for (var i = 0; i < 1000; i++) {" 6236 " result += o.y;" 6237 "}" 6238 "result;", 6239 42 * 1000); 6240} 6241 6242 6243static int interceptor_load_not_handled_calls = 0; 6244static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name, 6245 const AccessorInfo& info) { 6246 ++interceptor_load_not_handled_calls; 6247 return v8::Handle<v8::Value>(); 6248} 6249 6250 6251// Test how post-interceptor lookups are done in the non-cacheable 6252// case: the interceptor should not be invoked during this lookup. 6253THREADED_TEST(InterceptorLoadICPostInterceptor) { 6254 interceptor_load_not_handled_calls = 0; 6255 CheckInterceptorLoadIC(InterceptorLoadNotHandled, 6256 "receiver = new Object();" 6257 "receiver.__proto__ = o;" 6258 "proto = new Object();" 6259 "/* Make proto a slow-case object. */" 6260 "for (var i = 0; i < 1000; i++) {" 6261 " proto[\"xxxxxxxx\" + i] = [];" 6262 "}" 6263 "proto.x = 17;" 6264 "o.__proto__ = proto;" 6265 "var result = 0;" 6266 "for (var i = 0; i < 1000; i++) {" 6267 " result += receiver.x;" 6268 "}" 6269 "result;", 6270 17 * 1000); 6271 CHECK_EQ(1000, interceptor_load_not_handled_calls); 6272} 6273 6274 6275// Test the case when we stored field into 6276// a stub, but it got invalidated later on due to override on 6277// global object which is between interceptor and fields' holders. 6278THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) { 6279 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6280 "o.__proto__ = this;" // set a global to be a proto of o. 6281 "this.__proto__.y = 239;" 6282 "for (var i = 0; i < 10; i++) {" 6283 " if (o.y != 239) throw 'oops: ' + o.y;" 6284 // Now it should be ICed and keep a reference to y defined on field_holder. 6285 "}" 6286 "this.y = 42;" // Assign on a global. 6287 "var result = 0;" 6288 "for (var i = 0; i < 10; i++) {" 6289 " result += o.y;" 6290 "}" 6291 "result;", 6292 42 * 10); 6293} 6294 6295 6296static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) { 6297 ApiTestFuzzer::Fuzz(); 6298 return v8_num(239); 6299} 6300 6301 6302static void SetOnThis(Local<String> name, 6303 Local<Value> value, 6304 const AccessorInfo& info) { 6305 info.This()->ForceSet(name, value); 6306} 6307 6308 6309THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) { 6310 v8::HandleScope scope; 6311 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6312 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6313 templ->SetAccessor(v8_str("y"), Return239); 6314 LocalContext context; 6315 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6316 6317 // Check the case when receiver and interceptor's holder 6318 // are the same objects. 6319 v8::Handle<Value> value = CompileRun( 6320 "var result = 0;" 6321 "for (var i = 0; i < 7; i++) {" 6322 " result = o.y;" 6323 "}"); 6324 CHECK_EQ(239, value->Int32Value()); 6325 6326 // Check the case when interceptor's holder is in proto chain 6327 // of receiver. 6328 value = CompileRun( 6329 "r = { __proto__: o };" 6330 "var result = 0;" 6331 "for (var i = 0; i < 7; i++) {" 6332 " result = r.y;" 6333 "}"); 6334 CHECK_EQ(239, value->Int32Value()); 6335} 6336 6337 6338THREADED_TEST(InterceptorLoadICWithCallbackOnProto) { 6339 v8::HandleScope scope; 6340 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6341 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6342 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6343 templ_p->SetAccessor(v8_str("y"), Return239); 6344 6345 LocalContext context; 6346 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6347 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6348 6349 // Check the case when receiver and interceptor's holder 6350 // are the same objects. 6351 v8::Handle<Value> value = CompileRun( 6352 "o.__proto__ = p;" 6353 "var result = 0;" 6354 "for (var i = 0; i < 7; i++) {" 6355 " result = o.x + o.y;" 6356 "}"); 6357 CHECK_EQ(239 + 42, value->Int32Value()); 6358 6359 // Check the case when interceptor's holder is in proto chain 6360 // of receiver. 6361 value = CompileRun( 6362 "r = { __proto__: o };" 6363 "var result = 0;" 6364 "for (var i = 0; i < 7; i++) {" 6365 " result = r.x + r.y;" 6366 "}"); 6367 CHECK_EQ(239 + 42, value->Int32Value()); 6368} 6369 6370 6371THREADED_TEST(InterceptorLoadICForCallbackWithOverride) { 6372 v8::HandleScope scope; 6373 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6374 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6375 templ->SetAccessor(v8_str("y"), Return239); 6376 6377 LocalContext context; 6378 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6379 6380 v8::Handle<Value> value = CompileRun( 6381 "fst = new Object(); fst.__proto__ = o;" 6382 "snd = new Object(); snd.__proto__ = fst;" 6383 "var result1 = 0;" 6384 "for (var i = 0; i < 7; i++) {" 6385 " result1 = snd.x;" 6386 "}" 6387 "fst.x = 239;" 6388 "var result = 0;" 6389 "for (var i = 0; i < 7; i++) {" 6390 " result = snd.x;" 6391 "}" 6392 "result + result1"); 6393 CHECK_EQ(239 + 42, value->Int32Value()); 6394} 6395 6396 6397// Test the case when we stored callback into 6398// a stub, but interceptor produced value on its own. 6399THREADED_TEST(InterceptorLoadICCallbackNotNeeded) { 6400 v8::HandleScope scope; 6401 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6402 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6403 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6404 templ_p->SetAccessor(v8_str("y"), Return239); 6405 6406 LocalContext context; 6407 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6408 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6409 6410 v8::Handle<Value> value = CompileRun( 6411 "o.__proto__ = p;" 6412 "for (var i = 0; i < 7; i++) {" 6413 " o.x;" 6414 // Now it should be ICed and keep a reference to x defined on p 6415 "}" 6416 "var result = 0;" 6417 "for (var i = 0; i < 7; i++) {" 6418 " result += o.x;" 6419 "}" 6420 "result"); 6421 CHECK_EQ(42 * 7, value->Int32Value()); 6422} 6423 6424 6425// Test the case when we stored callback into 6426// a stub, but it got invalidated later on. 6427THREADED_TEST(InterceptorLoadICInvalidatedCallback) { 6428 v8::HandleScope scope; 6429 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6430 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6431 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6432 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 6433 6434 LocalContext context; 6435 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6436 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6437 6438 v8::Handle<Value> value = CompileRun( 6439 "inbetween = new Object();" 6440 "o.__proto__ = inbetween;" 6441 "inbetween.__proto__ = p;" 6442 "for (var i = 0; i < 10; i++) {" 6443 " o.y;" 6444 // Now it should be ICed and keep a reference to y defined on p 6445 "}" 6446 "inbetween.y = 42;" 6447 "var result = 0;" 6448 "for (var i = 0; i < 10; i++) {" 6449 " result += o.y;" 6450 "}" 6451 "result"); 6452 CHECK_EQ(42 * 10, value->Int32Value()); 6453} 6454 6455 6456// Test the case when we stored callback into 6457// a stub, but it got invalidated later on due to override on 6458// global object which is between interceptor and callbacks' holders. 6459THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) { 6460 v8::HandleScope scope; 6461 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6462 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6463 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6464 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 6465 6466 LocalContext context; 6467 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6468 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6469 6470 v8::Handle<Value> value = CompileRun( 6471 "o.__proto__ = this;" 6472 "this.__proto__ = p;" 6473 "for (var i = 0; i < 10; i++) {" 6474 " if (o.y != 239) throw 'oops: ' + o.y;" 6475 // Now it should be ICed and keep a reference to y defined on p 6476 "}" 6477 "this.y = 42;" 6478 "var result = 0;" 6479 "for (var i = 0; i < 10; i++) {" 6480 " result += o.y;" 6481 "}" 6482 "result"); 6483 CHECK_EQ(42 * 10, value->Int32Value()); 6484} 6485 6486 6487static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name, 6488 const AccessorInfo& info) { 6489 ApiTestFuzzer::Fuzz(); 6490 CHECK(v8_str("x")->Equals(name)); 6491 return v8::Integer::New(0); 6492} 6493 6494 6495THREADED_TEST(InterceptorReturningZero) { 6496 CheckInterceptorLoadIC(InterceptorLoadICGetter0, 6497 "o.x == undefined ? 1 : 0", 6498 0); 6499} 6500 6501 6502static v8::Handle<Value> InterceptorStoreICSetter( 6503 Local<String> key, Local<Value> value, const AccessorInfo&) { 6504 CHECK(v8_str("x")->Equals(key)); 6505 CHECK_EQ(42, value->Int32Value()); 6506 return value; 6507} 6508 6509 6510// This test should hit the store IC for the interceptor case. 6511THREADED_TEST(InterceptorStoreIC) { 6512 v8::HandleScope scope; 6513 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6514 templ->SetNamedPropertyHandler(InterceptorLoadICGetter, 6515 InterceptorStoreICSetter); 6516 LocalContext context; 6517 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6518 v8::Handle<Value> value = CompileRun( 6519 "for (var i = 0; i < 1000; i++) {" 6520 " o.x = 42;" 6521 "}"); 6522} 6523 6524 6525THREADED_TEST(InterceptorStoreICWithNoSetter) { 6526 v8::HandleScope scope; 6527 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6528 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6529 LocalContext context; 6530 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6531 v8::Handle<Value> value = CompileRun( 6532 "for (var i = 0; i < 1000; i++) {" 6533 " o.y = 239;" 6534 "}" 6535 "42 + o.y"); 6536 CHECK_EQ(239 + 42, value->Int32Value()); 6537} 6538 6539 6540 6541 6542v8::Handle<Value> call_ic_function; 6543v8::Handle<Value> call_ic_function2; 6544v8::Handle<Value> call_ic_function3; 6545 6546static v8::Handle<Value> InterceptorCallICGetter(Local<String> name, 6547 const AccessorInfo& info) { 6548 ApiTestFuzzer::Fuzz(); 6549 CHECK(v8_str("x")->Equals(name)); 6550 return call_ic_function; 6551} 6552 6553 6554// This test should hit the call IC for the interceptor case. 6555THREADED_TEST(InterceptorCallIC) { 6556 v8::HandleScope scope; 6557 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6558 templ->SetNamedPropertyHandler(InterceptorCallICGetter); 6559 LocalContext context; 6560 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6561 call_ic_function = 6562 v8_compile("function f(x) { return x + 1; }; f")->Run(); 6563 v8::Handle<Value> value = CompileRun( 6564 "var result = 0;" 6565 "for (var i = 0; i < 1000; i++) {" 6566 " result = o.x(41);" 6567 "}"); 6568 CHECK_EQ(42, value->Int32Value()); 6569} 6570 6571 6572// This test checks that if interceptor doesn't provide 6573// a value, we can fetch regular value. 6574THREADED_TEST(InterceptorCallICSeesOthers) { 6575 v8::HandleScope scope; 6576 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6577 templ->SetNamedPropertyHandler(NoBlockGetterX); 6578 LocalContext context; 6579 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6580 v8::Handle<Value> value = CompileRun( 6581 "o.x = function f(x) { return x + 1; };" 6582 "var result = 0;" 6583 "for (var i = 0; i < 7; i++) {" 6584 " result = o.x(41);" 6585 "}"); 6586 CHECK_EQ(42, value->Int32Value()); 6587} 6588 6589 6590static v8::Handle<Value> call_ic_function4; 6591static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name, 6592 const AccessorInfo& info) { 6593 ApiTestFuzzer::Fuzz(); 6594 CHECK(v8_str("x")->Equals(name)); 6595 return call_ic_function4; 6596} 6597 6598 6599// This test checks that if interceptor provides a function, 6600// even if we cached shadowed variant, interceptor's function 6601// is invoked 6602THREADED_TEST(InterceptorCallICCacheableNotNeeded) { 6603 v8::HandleScope scope; 6604 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6605 templ->SetNamedPropertyHandler(InterceptorCallICGetter4); 6606 LocalContext context; 6607 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6608 call_ic_function4 = 6609 v8_compile("function f(x) { return x - 1; }; f")->Run(); 6610 v8::Handle<Value> value = CompileRun( 6611 "o.__proto__.x = function(x) { return x + 1; };" 6612 "var result = 0;" 6613 "for (var i = 0; i < 1000; i++) {" 6614 " result = o.x(42);" 6615 "}"); 6616 CHECK_EQ(41, value->Int32Value()); 6617} 6618 6619 6620// Test the case when we stored cacheable lookup into 6621// a stub, but it got invalidated later on 6622THREADED_TEST(InterceptorCallICInvalidatedCacheable) { 6623 v8::HandleScope scope; 6624 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6625 templ->SetNamedPropertyHandler(NoBlockGetterX); 6626 LocalContext context; 6627 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6628 v8::Handle<Value> value = CompileRun( 6629 "proto1 = new Object();" 6630 "proto2 = new Object();" 6631 "o.__proto__ = proto1;" 6632 "proto1.__proto__ = proto2;" 6633 "proto2.y = function(x) { return x + 1; };" 6634 // Invoke it many times to compile a stub 6635 "for (var i = 0; i < 7; i++) {" 6636 " o.y(42);" 6637 "}" 6638 "proto1.y = function(x) { return x - 1; };" 6639 "var result = 0;" 6640 "for (var i = 0; i < 7; i++) {" 6641 " result += o.y(42);" 6642 "}"); 6643 CHECK_EQ(41 * 7, value->Int32Value()); 6644} 6645 6646 6647static v8::Handle<Value> call_ic_function5; 6648static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name, 6649 const AccessorInfo& info) { 6650 ApiTestFuzzer::Fuzz(); 6651 if (v8_str("x")->Equals(name)) 6652 return call_ic_function5; 6653 else 6654 return Local<Value>(); 6655} 6656 6657 6658// This test checks that if interceptor doesn't provide a function, 6659// cached constant function is used 6660THREADED_TEST(InterceptorCallICConstantFunctionUsed) { 6661 v8::HandleScope scope; 6662 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6663 templ->SetNamedPropertyHandler(NoBlockGetterX); 6664 LocalContext context; 6665 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6666 v8::Handle<Value> value = CompileRun( 6667 "function inc(x) { return x + 1; };" 6668 "inc(1);" 6669 "o.x = inc;" 6670 "var result = 0;" 6671 "for (var i = 0; i < 1000; i++) {" 6672 " result = o.x(42);" 6673 "}"); 6674 CHECK_EQ(43, value->Int32Value()); 6675} 6676 6677 6678// This test checks that if interceptor provides a function, 6679// even if we cached constant function, interceptor's function 6680// is invoked 6681THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) { 6682 v8::HandleScope scope; 6683 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6684 templ->SetNamedPropertyHandler(InterceptorCallICGetter5); 6685 LocalContext context; 6686 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6687 call_ic_function5 = 6688 v8_compile("function f(x) { return x - 1; }; f")->Run(); 6689 v8::Handle<Value> value = CompileRun( 6690 "function inc(x) { return x + 1; };" 6691 "inc(1);" 6692 "o.x = inc;" 6693 "var result = 0;" 6694 "for (var i = 0; i < 1000; i++) {" 6695 " result = o.x(42);" 6696 "}"); 6697 CHECK_EQ(41, value->Int32Value()); 6698} 6699 6700 6701// Test the case when we stored constant function into 6702// a stub, but it got invalidated later on 6703THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) { 6704 v8::HandleScope scope; 6705 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6706 templ->SetNamedPropertyHandler(NoBlockGetterX); 6707 LocalContext context; 6708 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6709 v8::Handle<Value> value = CompileRun( 6710 "function inc(x) { return x + 1; };" 6711 "inc(1);" 6712 "proto1 = new Object();" 6713 "proto2 = new Object();" 6714 "o.__proto__ = proto1;" 6715 "proto1.__proto__ = proto2;" 6716 "proto2.y = inc;" 6717 // Invoke it many times to compile a stub 6718 "for (var i = 0; i < 7; i++) {" 6719 " o.y(42);" 6720 "}" 6721 "proto1.y = function(x) { return x - 1; };" 6722 "var result = 0;" 6723 "for (var i = 0; i < 7; i++) {" 6724 " result += o.y(42);" 6725 "}"); 6726 CHECK_EQ(41 * 7, value->Int32Value()); 6727} 6728 6729 6730// Test the case when we stored constant function into 6731// a stub, but it got invalidated later on due to override on 6732// global object which is between interceptor and constant function' holders. 6733THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) { 6734 v8::HandleScope scope; 6735 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6736 templ->SetNamedPropertyHandler(NoBlockGetterX); 6737 LocalContext context; 6738 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6739 v8::Handle<Value> value = CompileRun( 6740 "function inc(x) { return x + 1; };" 6741 "inc(1);" 6742 "o.__proto__ = this;" 6743 "this.__proto__.y = inc;" 6744 // Invoke it many times to compile a stub 6745 "for (var i = 0; i < 7; i++) {" 6746 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);" 6747 "}" 6748 "this.y = function(x) { return x - 1; };" 6749 "var result = 0;" 6750 "for (var i = 0; i < 7; i++) {" 6751 " result += o.y(42);" 6752 "}"); 6753 CHECK_EQ(41 * 7, value->Int32Value()); 6754} 6755 6756 6757// Test the case when actual function to call sits on global object. 6758THREADED_TEST(InterceptorCallICCachedFromGlobal) { 6759 v8::HandleScope scope; 6760 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6761 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 6762 6763 LocalContext context; 6764 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6765 6766 v8::Handle<Value> value = CompileRun( 6767 "try {" 6768 " o.__proto__ = this;" 6769 " for (var i = 0; i < 10; i++) {" 6770 " var v = o.parseFloat('239');" 6771 " if (v != 239) throw v;" 6772 // Now it should be ICed and keep a reference to parseFloat. 6773 " }" 6774 " var result = 0;" 6775 " for (var i = 0; i < 10; i++) {" 6776 " result += o.parseFloat('239');" 6777 " }" 6778 " result" 6779 "} catch(e) {" 6780 " e" 6781 "};"); 6782 CHECK_EQ(239 * 10, value->Int32Value()); 6783} 6784 6785static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name, 6786 const AccessorInfo& info) { 6787 ApiTestFuzzer::Fuzz(); 6788 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data())); 6789 ++(*call_count); 6790 if ((*call_count) % 20 == 0) { 6791 v8::internal::Heap::CollectAllGarbage(true); 6792 } 6793 return v8::Handle<Value>(); 6794} 6795 6796static v8::Handle<Value> FastApiCallback_TrivialSignature( 6797 const v8::Arguments& args) { 6798 ApiTestFuzzer::Fuzz(); 6799 CHECK_EQ(args.This(), args.Holder()); 6800 CHECK(args.Data()->Equals(v8_str("method_data"))); 6801 return v8::Integer::New(args[0]->Int32Value() + 1); 6802} 6803 6804static v8::Handle<Value> FastApiCallback_SimpleSignature( 6805 const v8::Arguments& args) { 6806 ApiTestFuzzer::Fuzz(); 6807 CHECK_EQ(args.This()->GetPrototype(), args.Holder()); 6808 CHECK(args.Data()->Equals(v8_str("method_data"))); 6809 // Note, we're using HasRealNamedProperty instead of Has to avoid 6810 // invoking the interceptor again. 6811 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo"))); 6812 return v8::Integer::New(args[0]->Int32Value() + 1); 6813} 6814 6815// Helper to maximize the odds of object moving. 6816static void GenerateSomeGarbage() { 6817 CompileRun( 6818 "var garbage;" 6819 "for (var i = 0; i < 1000; i++) {" 6820 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];" 6821 "}" 6822 "garbage = undefined;"); 6823} 6824 6825THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) { 6826 int interceptor_call_count = 0; 6827 v8::HandleScope scope; 6828 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6829 v8::Handle<v8::FunctionTemplate> method_templ = 6830 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 6831 v8_str("method_data"), 6832 v8::Handle<v8::Signature>()); 6833 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6834 proto_templ->Set(v8_str("method"), method_templ); 6835 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6836 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6837 NULL, NULL, NULL, NULL, 6838 v8::External::Wrap(&interceptor_call_count)); 6839 LocalContext context; 6840 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6841 GenerateSomeGarbage(); 6842 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6843 v8::Handle<Value> value = CompileRun( 6844 "var result = 0;" 6845 "for (var i = 0; i < 100; i++) {" 6846 " result = o.method(41);" 6847 "}"); 6848 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 6849 CHECK_EQ(100, interceptor_call_count); 6850} 6851 6852THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) { 6853 int interceptor_call_count = 0; 6854 v8::HandleScope scope; 6855 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6856 v8::Handle<v8::FunctionTemplate> method_templ = 6857 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6858 v8_str("method_data"), 6859 v8::Signature::New(fun_templ)); 6860 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6861 proto_templ->Set(v8_str("method"), method_templ); 6862 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6863 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6864 NULL, NULL, NULL, NULL, 6865 v8::External::Wrap(&interceptor_call_count)); 6866 LocalContext context; 6867 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6868 GenerateSomeGarbage(); 6869 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6870 v8::Handle<Value> value = CompileRun( 6871 "o.foo = 17;" 6872 "var receiver = {};" 6873 "receiver.__proto__ = o;" 6874 "var result = 0;" 6875 "for (var i = 0; i < 100; i++) {" 6876 " result = receiver.method(41);" 6877 "}"); 6878 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 6879 CHECK_EQ(100, interceptor_call_count); 6880} 6881 6882THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) { 6883 int interceptor_call_count = 0; 6884 v8::HandleScope scope; 6885 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6886 v8::Handle<v8::FunctionTemplate> method_templ = 6887 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6888 v8_str("method_data"), 6889 v8::Signature::New(fun_templ)); 6890 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6891 proto_templ->Set(v8_str("method"), method_templ); 6892 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6893 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6894 NULL, NULL, NULL, NULL, 6895 v8::External::Wrap(&interceptor_call_count)); 6896 LocalContext context; 6897 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6898 GenerateSomeGarbage(); 6899 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6900 v8::Handle<Value> value = CompileRun( 6901 "o.foo = 17;" 6902 "var receiver = {};" 6903 "receiver.__proto__ = o;" 6904 "var result = 0;" 6905 "var saved_result = 0;" 6906 "for (var i = 0; i < 100; i++) {" 6907 " result = receiver.method(41);" 6908 " if (i == 50) {" 6909 " saved_result = result;" 6910 " receiver = {method: function(x) { return x - 1 }};" 6911 " }" 6912 "}"); 6913 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 6914 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 6915 CHECK_GE(interceptor_call_count, 50); 6916} 6917 6918THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) { 6919 int interceptor_call_count = 0; 6920 v8::HandleScope scope; 6921 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6922 v8::Handle<v8::FunctionTemplate> method_templ = 6923 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6924 v8_str("method_data"), 6925 v8::Signature::New(fun_templ)); 6926 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6927 proto_templ->Set(v8_str("method"), method_templ); 6928 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6929 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6930 NULL, NULL, NULL, NULL, 6931 v8::External::Wrap(&interceptor_call_count)); 6932 LocalContext context; 6933 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6934 GenerateSomeGarbage(); 6935 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6936 v8::Handle<Value> value = CompileRun( 6937 "o.foo = 17;" 6938 "var receiver = {};" 6939 "receiver.__proto__ = o;" 6940 "var result = 0;" 6941 "var saved_result = 0;" 6942 "for (var i = 0; i < 100; i++) {" 6943 " result = receiver.method(41);" 6944 " if (i == 50) {" 6945 " saved_result = result;" 6946 " o.method = function(x) { return x - 1 };" 6947 " }" 6948 "}"); 6949 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 6950 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 6951 CHECK_GE(interceptor_call_count, 50); 6952} 6953 6954THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) { 6955 int interceptor_call_count = 0; 6956 v8::HandleScope scope; 6957 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6958 v8::Handle<v8::FunctionTemplate> method_templ = 6959 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6960 v8_str("method_data"), 6961 v8::Signature::New(fun_templ)); 6962 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6963 proto_templ->Set(v8_str("method"), method_templ); 6964 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6965 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6966 NULL, NULL, NULL, NULL, 6967 v8::External::Wrap(&interceptor_call_count)); 6968 LocalContext context; 6969 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6970 GenerateSomeGarbage(); 6971 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6972 v8::TryCatch try_catch; 6973 v8::Handle<Value> value = CompileRun( 6974 "o.foo = 17;" 6975 "var receiver = {};" 6976 "receiver.__proto__ = o;" 6977 "var result = 0;" 6978 "var saved_result = 0;" 6979 "for (var i = 0; i < 100; i++) {" 6980 " result = receiver.method(41);" 6981 " if (i == 50) {" 6982 " saved_result = result;" 6983 " receiver = 333;" 6984 " }" 6985 "}"); 6986 CHECK(try_catch.HasCaught()); 6987 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), 6988 try_catch.Exception()->ToString()); 6989 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 6990 CHECK_GE(interceptor_call_count, 50); 6991} 6992 6993THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) { 6994 int interceptor_call_count = 0; 6995 v8::HandleScope scope; 6996 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6997 v8::Handle<v8::FunctionTemplate> method_templ = 6998 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6999 v8_str("method_data"), 7000 v8::Signature::New(fun_templ)); 7001 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7002 proto_templ->Set(v8_str("method"), method_templ); 7003 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7004 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 7005 NULL, NULL, NULL, NULL, 7006 v8::External::Wrap(&interceptor_call_count)); 7007 LocalContext context; 7008 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7009 GenerateSomeGarbage(); 7010 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7011 v8::TryCatch try_catch; 7012 v8::Handle<Value> value = CompileRun( 7013 "o.foo = 17;" 7014 "var receiver = {};" 7015 "receiver.__proto__ = o;" 7016 "var result = 0;" 7017 "var saved_result = 0;" 7018 "for (var i = 0; i < 100; i++) {" 7019 " result = receiver.method(41);" 7020 " if (i == 50) {" 7021 " saved_result = result;" 7022 " receiver = {method: receiver.method};" 7023 " }" 7024 "}"); 7025 CHECK(try_catch.HasCaught()); 7026 CHECK_EQ(v8_str("TypeError: Illegal invocation"), 7027 try_catch.Exception()->ToString()); 7028 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7029 CHECK_GE(interceptor_call_count, 50); 7030} 7031 7032THREADED_TEST(CallICFastApi_TrivialSignature) { 7033 v8::HandleScope scope; 7034 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7035 v8::Handle<v8::FunctionTemplate> method_templ = 7036 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 7037 v8_str("method_data"), 7038 v8::Handle<v8::Signature>()); 7039 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7040 proto_templ->Set(v8_str("method"), method_templ); 7041 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7042 LocalContext context; 7043 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7044 GenerateSomeGarbage(); 7045 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7046 v8::Handle<Value> value = CompileRun( 7047 "var result = 0;" 7048 "for (var i = 0; i < 100; i++) {" 7049 " result = o.method(41);" 7050 "}"); 7051 7052 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 7053} 7054 7055THREADED_TEST(CallICFastApi_SimpleSignature) { 7056 v8::HandleScope scope; 7057 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7058 v8::Handle<v8::FunctionTemplate> method_templ = 7059 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7060 v8_str("method_data"), 7061 v8::Signature::New(fun_templ)); 7062 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7063 proto_templ->Set(v8_str("method"), method_templ); 7064 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7065 LocalContext context; 7066 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7067 GenerateSomeGarbage(); 7068 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7069 v8::Handle<Value> value = CompileRun( 7070 "o.foo = 17;" 7071 "var receiver = {};" 7072 "receiver.__proto__ = o;" 7073 "var result = 0;" 7074 "for (var i = 0; i < 100; i++) {" 7075 " result = receiver.method(41);" 7076 "}"); 7077 7078 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 7079} 7080 7081THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) { 7082 v8::HandleScope scope; 7083 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7084 v8::Handle<v8::FunctionTemplate> method_templ = 7085 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7086 v8_str("method_data"), 7087 v8::Signature::New(fun_templ)); 7088 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7089 proto_templ->Set(v8_str("method"), method_templ); 7090 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7091 LocalContext context; 7092 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7093 GenerateSomeGarbage(); 7094 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7095 v8::Handle<Value> value = CompileRun( 7096 "o.foo = 17;" 7097 "var receiver = {};" 7098 "receiver.__proto__ = o;" 7099 "var result = 0;" 7100 "var saved_result = 0;" 7101 "for (var i = 0; i < 100; i++) {" 7102 " result = receiver.method(41);" 7103 " if (i == 50) {" 7104 " saved_result = result;" 7105 " receiver = {method: function(x) { return x - 1 }};" 7106 " }" 7107 "}"); 7108 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 7109 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7110} 7111 7112THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) { 7113 v8::HandleScope scope; 7114 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7115 v8::Handle<v8::FunctionTemplate> method_templ = 7116 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7117 v8_str("method_data"), 7118 v8::Signature::New(fun_templ)); 7119 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7120 proto_templ->Set(v8_str("method"), method_templ); 7121 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7122 LocalContext context; 7123 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7124 GenerateSomeGarbage(); 7125 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7126 v8::TryCatch try_catch; 7127 v8::Handle<Value> value = CompileRun( 7128 "o.foo = 17;" 7129 "var receiver = {};" 7130 "receiver.__proto__ = o;" 7131 "var result = 0;" 7132 "var saved_result = 0;" 7133 "for (var i = 0; i < 100; i++) {" 7134 " result = receiver.method(41);" 7135 " if (i == 50) {" 7136 " saved_result = result;" 7137 " receiver = 333;" 7138 " }" 7139 "}"); 7140 CHECK(try_catch.HasCaught()); 7141 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), 7142 try_catch.Exception()->ToString()); 7143 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7144} 7145 7146 7147v8::Handle<Value> keyed_call_ic_function; 7148 7149static v8::Handle<Value> InterceptorKeyedCallICGetter( 7150 Local<String> name, const AccessorInfo& info) { 7151 ApiTestFuzzer::Fuzz(); 7152 if (v8_str("x")->Equals(name)) { 7153 return keyed_call_ic_function; 7154 } 7155 return v8::Handle<Value>(); 7156} 7157 7158 7159// Test the case when we stored cacheable lookup into 7160// a stub, but the function name changed (to another cacheable function). 7161THREADED_TEST(InterceptorKeyedCallICKeyChange1) { 7162 v8::HandleScope scope; 7163 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7164 templ->SetNamedPropertyHandler(NoBlockGetterX); 7165 LocalContext context; 7166 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7167 v8::Handle<Value> value = CompileRun( 7168 "proto = new Object();" 7169 "proto.y = function(x) { return x + 1; };" 7170 "proto.z = function(x) { return x - 1; };" 7171 "o.__proto__ = proto;" 7172 "var result = 0;" 7173 "var method = 'y';" 7174 "for (var i = 0; i < 10; i++) {" 7175 " if (i == 5) { method = 'z'; };" 7176 " result += o[method](41);" 7177 "}"); 7178 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7179} 7180 7181 7182// Test the case when we stored cacheable lookup into 7183// a stub, but the function name changed (and the new function is present 7184// both before and after the interceptor in the prototype chain). 7185THREADED_TEST(InterceptorKeyedCallICKeyChange2) { 7186 v8::HandleScope scope; 7187 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7188 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter); 7189 LocalContext context; 7190 context->Global()->Set(v8_str("proto1"), templ->NewInstance()); 7191 keyed_call_ic_function = 7192 v8_compile("function f(x) { return x - 1; }; f")->Run(); 7193 v8::Handle<Value> value = CompileRun( 7194 "o = new Object();" 7195 "proto2 = new Object();" 7196 "o.y = function(x) { return x + 1; };" 7197 "proto2.y = function(x) { return x + 2; };" 7198 "o.__proto__ = proto1;" 7199 "proto1.__proto__ = proto2;" 7200 "var result = 0;" 7201 "var method = 'x';" 7202 "for (var i = 0; i < 10; i++) {" 7203 " if (i == 5) { method = 'y'; };" 7204 " result += o[method](41);" 7205 "}"); 7206 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7207} 7208 7209 7210// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit 7211// on the global object. 7212THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) { 7213 v8::HandleScope scope; 7214 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7215 templ->SetNamedPropertyHandler(NoBlockGetterX); 7216 LocalContext context; 7217 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7218 v8::Handle<Value> value = CompileRun( 7219 "function inc(x) { return x + 1; };" 7220 "inc(1);" 7221 "function dec(x) { return x - 1; };" 7222 "dec(1);" 7223 "o.__proto__ = this;" 7224 "this.__proto__.x = inc;" 7225 "this.__proto__.y = dec;" 7226 "var result = 0;" 7227 "var method = 'x';" 7228 "for (var i = 0; i < 10; i++) {" 7229 " if (i == 5) { method = 'y'; };" 7230 " result += o[method](41);" 7231 "}"); 7232 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7233} 7234 7235 7236// Test the case when actual function to call sits on global object. 7237THREADED_TEST(InterceptorKeyedCallICFromGlobal) { 7238 v8::HandleScope scope; 7239 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7240 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 7241 LocalContext context; 7242 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 7243 7244 v8::Handle<Value> value = CompileRun( 7245 "function len(x) { return x.length; };" 7246 "o.__proto__ = this;" 7247 "var m = 'parseFloat';" 7248 "var result = 0;" 7249 "for (var i = 0; i < 10; i++) {" 7250 " if (i == 5) {" 7251 " m = 'len';" 7252 " saved_result = result;" 7253 " };" 7254 " result = o[m]('239');" 7255 "}"); 7256 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value()); 7257 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7258} 7259 7260// Test the map transition before the interceptor. 7261THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) { 7262 v8::HandleScope scope; 7263 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7264 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 7265 LocalContext context; 7266 context->Global()->Set(v8_str("proto"), templ_o->NewInstance()); 7267 7268 v8::Handle<Value> value = CompileRun( 7269 "var o = new Object();" 7270 "o.__proto__ = proto;" 7271 "o.method = function(x) { return x + 1; };" 7272 "var m = 'method';" 7273 "var result = 0;" 7274 "for (var i = 0; i < 10; i++) {" 7275 " if (i == 5) { o.method = function(x) { return x - 1; }; };" 7276 " result += o[m](41);" 7277 "}"); 7278 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7279} 7280 7281 7282// Test the map transition after the interceptor. 7283THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) { 7284 v8::HandleScope scope; 7285 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7286 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 7287 LocalContext context; 7288 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 7289 7290 v8::Handle<Value> value = CompileRun( 7291 "var proto = new Object();" 7292 "o.__proto__ = proto;" 7293 "proto.method = function(x) { return x + 1; };" 7294 "var m = 'method';" 7295 "var result = 0;" 7296 "for (var i = 0; i < 10; i++) {" 7297 " if (i == 5) { proto.method = function(x) { return x - 1; }; };" 7298 " result += o[m](41);" 7299 "}"); 7300 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7301} 7302 7303 7304static int interceptor_call_count = 0; 7305 7306static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name, 7307 const AccessorInfo& info) { 7308 ApiTestFuzzer::Fuzz(); 7309 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) { 7310 return call_ic_function2; 7311 } 7312 return v8::Handle<Value>(); 7313} 7314 7315 7316// This test should hit load and call ICs for the interceptor case. 7317// Once in a while, the interceptor will reply that a property was not 7318// found in which case we should get a reference error. 7319THREADED_TEST(InterceptorICReferenceErrors) { 7320 v8::HandleScope scope; 7321 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7322 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter); 7323 LocalContext context(0, templ, v8::Handle<Value>()); 7324 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run(); 7325 v8::Handle<Value> value = CompileRun( 7326 "function f() {" 7327 " for (var i = 0; i < 1000; i++) {" 7328 " try { x; } catch(e) { return true; }" 7329 " }" 7330 " return false;" 7331 "};" 7332 "f();"); 7333 CHECK_EQ(true, value->BooleanValue()); 7334 interceptor_call_count = 0; 7335 value = CompileRun( 7336 "function g() {" 7337 " for (var i = 0; i < 1000; i++) {" 7338 " try { x(42); } catch(e) { return true; }" 7339 " }" 7340 " return false;" 7341 "};" 7342 "g();"); 7343 CHECK_EQ(true, value->BooleanValue()); 7344} 7345 7346 7347static int interceptor_ic_exception_get_count = 0; 7348 7349static v8::Handle<Value> InterceptorICExceptionGetter( 7350 Local<String> name, 7351 const AccessorInfo& info) { 7352 ApiTestFuzzer::Fuzz(); 7353 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) { 7354 return call_ic_function3; 7355 } 7356 if (interceptor_ic_exception_get_count == 20) { 7357 return v8::ThrowException(v8_num(42)); 7358 } 7359 // Do not handle get for properties other than x. 7360 return v8::Handle<Value>(); 7361} 7362 7363// Test interceptor load/call IC where the interceptor throws an 7364// exception once in a while. 7365THREADED_TEST(InterceptorICGetterExceptions) { 7366 interceptor_ic_exception_get_count = 0; 7367 v8::HandleScope scope; 7368 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7369 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter); 7370 LocalContext context(0, templ, v8::Handle<Value>()); 7371 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run(); 7372 v8::Handle<Value> value = CompileRun( 7373 "function f() {" 7374 " for (var i = 0; i < 100; i++) {" 7375 " try { x; } catch(e) { return true; }" 7376 " }" 7377 " return false;" 7378 "};" 7379 "f();"); 7380 CHECK_EQ(true, value->BooleanValue()); 7381 interceptor_ic_exception_get_count = 0; 7382 value = CompileRun( 7383 "function f() {" 7384 " for (var i = 0; i < 100; i++) {" 7385 " try { x(42); } catch(e) { return true; }" 7386 " }" 7387 " return false;" 7388 "};" 7389 "f();"); 7390 CHECK_EQ(true, value->BooleanValue()); 7391} 7392 7393 7394static int interceptor_ic_exception_set_count = 0; 7395 7396static v8::Handle<Value> InterceptorICExceptionSetter( 7397 Local<String> key, Local<Value> value, const AccessorInfo&) { 7398 ApiTestFuzzer::Fuzz(); 7399 if (++interceptor_ic_exception_set_count > 20) { 7400 return v8::ThrowException(v8_num(42)); 7401 } 7402 // Do not actually handle setting. 7403 return v8::Handle<Value>(); 7404} 7405 7406// Test interceptor store IC where the interceptor throws an exception 7407// once in a while. 7408THREADED_TEST(InterceptorICSetterExceptions) { 7409 interceptor_ic_exception_set_count = 0; 7410 v8::HandleScope scope; 7411 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7412 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter); 7413 LocalContext context(0, templ, v8::Handle<Value>()); 7414 v8::Handle<Value> value = CompileRun( 7415 "function f() {" 7416 " for (var i = 0; i < 100; i++) {" 7417 " try { x = 42; } catch(e) { return true; }" 7418 " }" 7419 " return false;" 7420 "};" 7421 "f();"); 7422 CHECK_EQ(true, value->BooleanValue()); 7423} 7424 7425 7426// Test that we ignore null interceptors. 7427THREADED_TEST(NullNamedInterceptor) { 7428 v8::HandleScope scope; 7429 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7430 templ->SetNamedPropertyHandler(0); 7431 LocalContext context; 7432 templ->Set("x", v8_num(42)); 7433 v8::Handle<v8::Object> obj = templ->NewInstance(); 7434 context->Global()->Set(v8_str("obj"), obj); 7435 v8::Handle<Value> value = CompileRun("obj.x"); 7436 CHECK(value->IsInt32()); 7437 CHECK_EQ(42, value->Int32Value()); 7438} 7439 7440 7441// Test that we ignore null interceptors. 7442THREADED_TEST(NullIndexedInterceptor) { 7443 v8::HandleScope scope; 7444 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7445 templ->SetIndexedPropertyHandler(0); 7446 LocalContext context; 7447 templ->Set("42", v8_num(42)); 7448 v8::Handle<v8::Object> obj = templ->NewInstance(); 7449 context->Global()->Set(v8_str("obj"), obj); 7450 v8::Handle<Value> value = CompileRun("obj[42]"); 7451 CHECK(value->IsInt32()); 7452 CHECK_EQ(42, value->Int32Value()); 7453} 7454 7455 7456THREADED_TEST(NamedPropertyHandlerGetterAttributes) { 7457 v8::HandleScope scope; 7458 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 7459 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter); 7460 LocalContext env; 7461 env->Global()->Set(v8_str("obj"), 7462 templ->GetFunction()->NewInstance()); 7463 ExpectTrue("obj.x === 42"); 7464 ExpectTrue("!obj.propertyIsEnumerable('x')"); 7465} 7466 7467 7468static v8::Handle<Value> ParentGetter(Local<String> name, 7469 const AccessorInfo& info) { 7470 ApiTestFuzzer::Fuzz(); 7471 return v8_num(1); 7472} 7473 7474 7475static v8::Handle<Value> ChildGetter(Local<String> name, 7476 const AccessorInfo& info) { 7477 ApiTestFuzzer::Fuzz(); 7478 return v8_num(42); 7479} 7480 7481 7482THREADED_TEST(Overriding) { 7483 v8::HandleScope scope; 7484 LocalContext context; 7485 7486 // Parent template. 7487 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New(); 7488 Local<ObjectTemplate> parent_instance_templ = 7489 parent_templ->InstanceTemplate(); 7490 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter); 7491 7492 // Template that inherits from the parent template. 7493 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New(); 7494 Local<ObjectTemplate> child_instance_templ = 7495 child_templ->InstanceTemplate(); 7496 child_templ->Inherit(parent_templ); 7497 // Override 'f'. The child version of 'f' should get called for child 7498 // instances. 7499 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter); 7500 // Add 'g' twice. The 'g' added last should get called for instances. 7501 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter); 7502 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter); 7503 7504 // Add 'h' as an accessor to the proto template with ReadOnly attributes 7505 // so 'h' can be shadowed on the instance object. 7506 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate(); 7507 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0, 7508 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 7509 7510 // Add 'i' as an accessor to the instance template with ReadOnly attributes 7511 // but the attribute does not have effect because it is duplicated with 7512 // NULL setter. 7513 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0, 7514 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 7515 7516 7517 7518 // Instantiate the child template. 7519 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance(); 7520 7521 // Check that the child function overrides the parent one. 7522 context->Global()->Set(v8_str("o"), instance); 7523 Local<Value> value = v8_compile("o.f")->Run(); 7524 // Check that the 'g' that was added last is hit. 7525 CHECK_EQ(42, value->Int32Value()); 7526 value = v8_compile("o.g")->Run(); 7527 CHECK_EQ(42, value->Int32Value()); 7528 7529 // Check 'h' can be shadowed. 7530 value = v8_compile("o.h = 3; o.h")->Run(); 7531 CHECK_EQ(3, value->Int32Value()); 7532 7533 // Check 'i' is cannot be shadowed or changed. 7534 value = v8_compile("o.i = 3; o.i")->Run(); 7535 CHECK_EQ(42, value->Int32Value()); 7536} 7537 7538 7539static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) { 7540 ApiTestFuzzer::Fuzz(); 7541 if (args.IsConstructCall()) { 7542 return v8::Boolean::New(true); 7543 } 7544 return v8::Boolean::New(false); 7545} 7546 7547 7548THREADED_TEST(IsConstructCall) { 7549 v8::HandleScope scope; 7550 7551 // Function template with call handler. 7552 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 7553 templ->SetCallHandler(IsConstructHandler); 7554 7555 LocalContext context; 7556 7557 context->Global()->Set(v8_str("f"), templ->GetFunction()); 7558 Local<Value> value = v8_compile("f()")->Run(); 7559 CHECK(!value->BooleanValue()); 7560 value = v8_compile("new f()")->Run(); 7561 CHECK(value->BooleanValue()); 7562} 7563 7564 7565THREADED_TEST(ObjectProtoToString) { 7566 v8::HandleScope scope; 7567 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 7568 templ->SetClassName(v8_str("MyClass")); 7569 7570 LocalContext context; 7571 7572 Local<String> customized_tostring = v8_str("customized toString"); 7573 7574 // Replace Object.prototype.toString 7575 v8_compile("Object.prototype.toString = function() {" 7576 " return 'customized toString';" 7577 "}")->Run(); 7578 7579 // Normal ToString call should call replaced Object.prototype.toString 7580 Local<v8::Object> instance = templ->GetFunction()->NewInstance(); 7581 Local<String> value = instance->ToString(); 7582 CHECK(value->IsString() && value->Equals(customized_tostring)); 7583 7584 // ObjectProtoToString should not call replace toString function. 7585 value = instance->ObjectProtoToString(); 7586 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]"))); 7587 7588 // Check global 7589 value = context->Global()->ObjectProtoToString(); 7590 CHECK(value->IsString() && value->Equals(v8_str("[object global]"))); 7591 7592 // Check ordinary object 7593 Local<Value> object = v8_compile("new Object()")->Run(); 7594 value = object.As<v8::Object>()->ObjectProtoToString(); 7595 CHECK(value->IsString() && value->Equals(v8_str("[object Object]"))); 7596} 7597 7598 7599bool ApiTestFuzzer::fuzzing_ = false; 7600v8::internal::Semaphore* ApiTestFuzzer::all_tests_done_= 7601 v8::internal::OS::CreateSemaphore(0); 7602int ApiTestFuzzer::active_tests_; 7603int ApiTestFuzzer::tests_being_run_; 7604int ApiTestFuzzer::current_; 7605 7606 7607// We are in a callback and want to switch to another thread (if we 7608// are currently running the thread fuzzing test). 7609void ApiTestFuzzer::Fuzz() { 7610 if (!fuzzing_) return; 7611 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_; 7612 test->ContextSwitch(); 7613} 7614 7615 7616// Let the next thread go. Since it is also waiting on the V8 lock it may 7617// not start immediately. 7618bool ApiTestFuzzer::NextThread() { 7619 int test_position = GetNextTestNumber(); 7620 const char* test_name = RegisterThreadedTest::nth(current_)->name(); 7621 if (test_position == current_) { 7622 if (kLogThreading) 7623 printf("Stay with %s\n", test_name); 7624 return false; 7625 } 7626 if (kLogThreading) { 7627 printf("Switch from %s to %s\n", 7628 test_name, 7629 RegisterThreadedTest::nth(test_position)->name()); 7630 } 7631 current_ = test_position; 7632 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal(); 7633 return true; 7634} 7635 7636 7637void ApiTestFuzzer::Run() { 7638 // When it is our turn... 7639 gate_->Wait(); 7640 { 7641 // ... get the V8 lock and start running the test. 7642 v8::Locker locker; 7643 CallTest(); 7644 } 7645 // This test finished. 7646 active_ = false; 7647 active_tests_--; 7648 // If it was the last then signal that fact. 7649 if (active_tests_ == 0) { 7650 all_tests_done_->Signal(); 7651 } else { 7652 // Otherwise select a new test and start that. 7653 NextThread(); 7654 } 7655} 7656 7657 7658static unsigned linear_congruential_generator; 7659 7660 7661void ApiTestFuzzer::Setup(PartOfTest part) { 7662 linear_congruential_generator = i::FLAG_testing_prng_seed; 7663 fuzzing_ = true; 7664 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1); 7665 int end = (part == FIRST_PART) 7666 ? (RegisterThreadedTest::count() >> 1) 7667 : RegisterThreadedTest::count(); 7668 active_tests_ = tests_being_run_ = end - start; 7669 for (int i = 0; i < tests_being_run_; i++) { 7670 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start); 7671 } 7672 for (int i = 0; i < active_tests_; i++) { 7673 RegisterThreadedTest::nth(i)->fuzzer_->Start(); 7674 } 7675} 7676 7677 7678static void CallTestNumber(int test_number) { 7679 (RegisterThreadedTest::nth(test_number)->callback())(); 7680} 7681 7682 7683void ApiTestFuzzer::RunAllTests() { 7684 // Set off the first test. 7685 current_ = -1; 7686 NextThread(); 7687 // Wait till they are all done. 7688 all_tests_done_->Wait(); 7689} 7690 7691 7692int ApiTestFuzzer::GetNextTestNumber() { 7693 int next_test; 7694 do { 7695 next_test = (linear_congruential_generator >> 16) % tests_being_run_; 7696 linear_congruential_generator *= 1664525u; 7697 linear_congruential_generator += 1013904223u; 7698 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_); 7699 return next_test; 7700} 7701 7702 7703void ApiTestFuzzer::ContextSwitch() { 7704 // If the new thread is the same as the current thread there is nothing to do. 7705 if (NextThread()) { 7706 // Now it can start. 7707 v8::Unlocker unlocker; 7708 // Wait till someone starts us again. 7709 gate_->Wait(); 7710 // And we're off. 7711 } 7712} 7713 7714 7715void ApiTestFuzzer::TearDown() { 7716 fuzzing_ = false; 7717 for (int i = 0; i < RegisterThreadedTest::count(); i++) { 7718 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_; 7719 if (fuzzer != NULL) fuzzer->Join(); 7720 } 7721} 7722 7723 7724// Lets not be needlessly self-referential. 7725TEST(Threading) { 7726 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART); 7727 ApiTestFuzzer::RunAllTests(); 7728 ApiTestFuzzer::TearDown(); 7729} 7730 7731TEST(Threading2) { 7732 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART); 7733 ApiTestFuzzer::RunAllTests(); 7734 ApiTestFuzzer::TearDown(); 7735} 7736 7737 7738void ApiTestFuzzer::CallTest() { 7739 if (kLogThreading) 7740 printf("Start test %d\n", test_number_); 7741 CallTestNumber(test_number_); 7742 if (kLogThreading) 7743 printf("End test %d\n", test_number_); 7744} 7745 7746 7747static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) { 7748 CHECK(v8::Locker::IsLocked()); 7749 ApiTestFuzzer::Fuzz(); 7750 v8::Unlocker unlocker; 7751 const char* code = "throw 7;"; 7752 { 7753 v8::Locker nested_locker; 7754 v8::HandleScope scope; 7755 v8::Handle<Value> exception; 7756 { v8::TryCatch try_catch; 7757 v8::Handle<Value> value = CompileRun(code); 7758 CHECK(value.IsEmpty()); 7759 CHECK(try_catch.HasCaught()); 7760 // Make sure to wrap the exception in a new handle because 7761 // the handle returned from the TryCatch is destroyed 7762 // when the TryCatch is destroyed. 7763 exception = Local<Value>::New(try_catch.Exception()); 7764 } 7765 return v8::ThrowException(exception); 7766 } 7767} 7768 7769 7770static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) { 7771 CHECK(v8::Locker::IsLocked()); 7772 ApiTestFuzzer::Fuzz(); 7773 v8::Unlocker unlocker; 7774 const char* code = "throw 7;"; 7775 { 7776 v8::Locker nested_locker; 7777 v8::HandleScope scope; 7778 v8::Handle<Value> value = CompileRun(code); 7779 CHECK(value.IsEmpty()); 7780 return v8_str("foo"); 7781 } 7782} 7783 7784 7785// These are locking tests that don't need to be run again 7786// as part of the locking aggregation tests. 7787TEST(NestedLockers) { 7788 v8::Locker locker; 7789 CHECK(v8::Locker::IsLocked()); 7790 v8::HandleScope scope; 7791 LocalContext env; 7792 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS); 7793 Local<Function> fun = fun_templ->GetFunction(); 7794 env->Global()->Set(v8_str("throw_in_js"), fun); 7795 Local<Script> script = v8_compile("(function () {" 7796 " try {" 7797 " throw_in_js();" 7798 " return 42;" 7799 " } catch (e) {" 7800 " return e * 13;" 7801 " }" 7802 "})();"); 7803 CHECK_EQ(91, script->Run()->Int32Value()); 7804} 7805 7806 7807// These are locking tests that don't need to be run again 7808// as part of the locking aggregation tests. 7809TEST(NestedLockersNoTryCatch) { 7810 v8::Locker locker; 7811 v8::HandleScope scope; 7812 LocalContext env; 7813 Local<v8::FunctionTemplate> fun_templ = 7814 v8::FunctionTemplate::New(ThrowInJSNoCatch); 7815 Local<Function> fun = fun_templ->GetFunction(); 7816 env->Global()->Set(v8_str("throw_in_js"), fun); 7817 Local<Script> script = v8_compile("(function () {" 7818 " try {" 7819 " throw_in_js();" 7820 " return 42;" 7821 " } catch (e) {" 7822 " return e * 13;" 7823 " }" 7824 "})();"); 7825 CHECK_EQ(91, script->Run()->Int32Value()); 7826} 7827 7828 7829THREADED_TEST(RecursiveLocking) { 7830 v8::Locker locker; 7831 { 7832 v8::Locker locker2; 7833 CHECK(v8::Locker::IsLocked()); 7834 } 7835} 7836 7837 7838static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) { 7839 ApiTestFuzzer::Fuzz(); 7840 v8::Unlocker unlocker; 7841 return v8::Undefined(); 7842} 7843 7844 7845THREADED_TEST(LockUnlockLock) { 7846 { 7847 v8::Locker locker; 7848 v8::HandleScope scope; 7849 LocalContext env; 7850 Local<v8::FunctionTemplate> fun_templ = 7851 v8::FunctionTemplate::New(UnlockForAMoment); 7852 Local<Function> fun = fun_templ->GetFunction(); 7853 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 7854 Local<Script> script = v8_compile("(function () {" 7855 " unlock_for_a_moment();" 7856 " return 42;" 7857 "})();"); 7858 CHECK_EQ(42, script->Run()->Int32Value()); 7859 } 7860 { 7861 v8::Locker locker; 7862 v8::HandleScope scope; 7863 LocalContext env; 7864 Local<v8::FunctionTemplate> fun_templ = 7865 v8::FunctionTemplate::New(UnlockForAMoment); 7866 Local<Function> fun = fun_templ->GetFunction(); 7867 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 7868 Local<Script> script = v8_compile("(function () {" 7869 " unlock_for_a_moment();" 7870 " return 42;" 7871 "})();"); 7872 CHECK_EQ(42, script->Run()->Int32Value()); 7873 } 7874} 7875 7876 7877static int GetGlobalObjectsCount() { 7878 int count = 0; 7879 v8::internal::HeapIterator it; 7880 for (i::HeapObject* object = it.next(); object != NULL; object = it.next()) 7881 if (object->IsJSGlobalObject()) count++; 7882 return count; 7883} 7884 7885 7886static int GetSurvivingGlobalObjectsCount() { 7887 // We need to collect all garbage twice to be sure that everything 7888 // has been collected. This is because inline caches are cleared in 7889 // the first garbage collection but some of the maps have already 7890 // been marked at that point. Therefore some of the maps are not 7891 // collected until the second garbage collection. 7892 v8::internal::Heap::CollectAllGarbage(false); 7893 v8::internal::Heap::CollectAllGarbage(false); 7894 int count = GetGlobalObjectsCount(); 7895#ifdef DEBUG 7896 if (count > 0) v8::internal::Heap::TracePathToGlobal(); 7897#endif 7898 return count; 7899} 7900 7901 7902TEST(DontLeakGlobalObjects) { 7903 // Regression test for issues 1139850 and 1174891. 7904 7905 v8::V8::Initialize(); 7906 7907 int count = GetSurvivingGlobalObjectsCount(); 7908 7909 for (int i = 0; i < 5; i++) { 7910 { v8::HandleScope scope; 7911 LocalContext context; 7912 } 7913 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 7914 7915 { v8::HandleScope scope; 7916 LocalContext context; 7917 v8_compile("Date")->Run(); 7918 } 7919 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 7920 7921 { v8::HandleScope scope; 7922 LocalContext context; 7923 v8_compile("/aaa/")->Run(); 7924 } 7925 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 7926 7927 { v8::HandleScope scope; 7928 const char* extension_list[] = { "v8/gc" }; 7929 v8::ExtensionConfiguration extensions(1, extension_list); 7930 LocalContext context(&extensions); 7931 v8_compile("gc();")->Run(); 7932 } 7933 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 7934 } 7935} 7936 7937 7938v8::Persistent<v8::Object> some_object; 7939v8::Persistent<v8::Object> bad_handle; 7940 7941void NewPersistentHandleCallback(v8::Persistent<v8::Value>, void*) { 7942 v8::HandleScope scope; 7943 bad_handle = v8::Persistent<v8::Object>::New(some_object); 7944} 7945 7946 7947THREADED_TEST(NewPersistentHandleFromWeakCallback) { 7948 LocalContext context; 7949 7950 v8::Persistent<v8::Object> handle1, handle2; 7951 { 7952 v8::HandleScope scope; 7953 some_object = v8::Persistent<v8::Object>::New(v8::Object::New()); 7954 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7955 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7956 } 7957 // Note: order is implementation dependent alas: currently 7958 // global handle nodes are processed by PostGarbageCollectionProcessing 7959 // in reverse allocation order, so if second allocated handle is deleted, 7960 // weak callback of the first handle would be able to 'reallocate' it. 7961 handle1.MakeWeak(NULL, NewPersistentHandleCallback); 7962 handle2.Dispose(); 7963 i::Heap::CollectAllGarbage(false); 7964} 7965 7966 7967v8::Persistent<v8::Object> to_be_disposed; 7968 7969void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) { 7970 to_be_disposed.Dispose(); 7971 i::Heap::CollectAllGarbage(false); 7972} 7973 7974 7975THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) { 7976 LocalContext context; 7977 7978 v8::Persistent<v8::Object> handle1, handle2; 7979 { 7980 v8::HandleScope scope; 7981 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7982 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7983 } 7984 handle1.MakeWeak(NULL, DisposeAndForceGcCallback); 7985 to_be_disposed = handle2; 7986 i::Heap::CollectAllGarbage(false); 7987} 7988 7989void DisposingCallback(v8::Persistent<v8::Value> handle, void*) { 7990 handle.Dispose(); 7991} 7992 7993void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) { 7994 v8::HandleScope scope; 7995 v8::Persistent<v8::Object>::New(v8::Object::New()); 7996} 7997 7998 7999THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) { 8000 LocalContext context; 8001 8002 v8::Persistent<v8::Object> handle1, handle2, handle3; 8003 { 8004 v8::HandleScope scope; 8005 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8006 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8007 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8008 } 8009 handle2.MakeWeak(NULL, DisposingCallback); 8010 handle3.MakeWeak(NULL, HandleCreatingCallback); 8011 i::Heap::CollectAllGarbage(false); 8012} 8013 8014 8015THREADED_TEST(CheckForCrossContextObjectLiterals) { 8016 v8::V8::Initialize(); 8017 8018 const int nof = 2; 8019 const char* sources[nof] = { 8020 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }", 8021 "Object()" 8022 }; 8023 8024 for (int i = 0; i < nof; i++) { 8025 const char* source = sources[i]; 8026 { v8::HandleScope scope; 8027 LocalContext context; 8028 CompileRun(source); 8029 } 8030 { v8::HandleScope scope; 8031 LocalContext context; 8032 CompileRun(source); 8033 } 8034 } 8035} 8036 8037 8038static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) { 8039 v8::HandleScope inner; 8040 env->Enter(); 8041 v8::Handle<Value> three = v8_num(3); 8042 v8::Handle<Value> value = inner.Close(three); 8043 env->Exit(); 8044 return value; 8045} 8046 8047 8048THREADED_TEST(NestedHandleScopeAndContexts) { 8049 v8::HandleScope outer; 8050 v8::Persistent<Context> env = Context::New(); 8051 env->Enter(); 8052 v8::Handle<Value> value = NestedScope(env); 8053 v8::Handle<String> str = value->ToString(); 8054 env->Exit(); 8055 env.Dispose(); 8056} 8057 8058 8059THREADED_TEST(ExternalAllocatedMemory) { 8060 v8::HandleScope outer; 8061 v8::Persistent<Context> env = Context::New(); 8062 const int kSize = 1024*1024; 8063 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize); 8064 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0); 8065} 8066 8067 8068THREADED_TEST(DisposeEnteredContext) { 8069 v8::HandleScope scope; 8070 LocalContext outer; 8071 { v8::Persistent<v8::Context> inner = v8::Context::New(); 8072 inner->Enter(); 8073 inner.Dispose(); 8074 inner.Clear(); 8075 inner->Exit(); 8076 } 8077} 8078 8079 8080// Regression test for issue 54, object templates with internal fields 8081// but no accessors or interceptors did not get their internal field 8082// count set on instances. 8083THREADED_TEST(Regress54) { 8084 v8::HandleScope outer; 8085 LocalContext context; 8086 static v8::Persistent<v8::ObjectTemplate> templ; 8087 if (templ.IsEmpty()) { 8088 v8::HandleScope inner; 8089 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New(); 8090 local->SetInternalFieldCount(1); 8091 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local)); 8092 } 8093 v8::Handle<v8::Object> result = templ->NewInstance(); 8094 CHECK_EQ(1, result->InternalFieldCount()); 8095} 8096 8097 8098// If part of the threaded tests, this test makes ThreadingTest fail 8099// on mac. 8100TEST(CatchStackOverflow) { 8101 v8::HandleScope scope; 8102 LocalContext context; 8103 v8::TryCatch try_catch; 8104 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New( 8105 "function f() {" 8106 " return f();" 8107 "}" 8108 "" 8109 "f();")); 8110 v8::Handle<v8::Value> result = script->Run(); 8111 CHECK(result.IsEmpty()); 8112} 8113 8114 8115static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script, 8116 const char* resource_name, 8117 int line_offset) { 8118 v8::HandleScope scope; 8119 v8::TryCatch try_catch; 8120 v8::Handle<v8::Value> result = script->Run(); 8121 CHECK(result.IsEmpty()); 8122 CHECK(try_catch.HasCaught()); 8123 v8::Handle<v8::Message> message = try_catch.Message(); 8124 CHECK(!message.IsEmpty()); 8125 CHECK_EQ(10 + line_offset, message->GetLineNumber()); 8126 CHECK_EQ(91, message->GetStartPosition()); 8127 CHECK_EQ(92, message->GetEndPosition()); 8128 CHECK_EQ(2, message->GetStartColumn()); 8129 CHECK_EQ(3, message->GetEndColumn()); 8130 v8::String::AsciiValue line(message->GetSourceLine()); 8131 CHECK_EQ(" throw 'nirk';", *line); 8132 v8::String::AsciiValue name(message->GetScriptResourceName()); 8133 CHECK_EQ(resource_name, *name); 8134} 8135 8136 8137THREADED_TEST(TryCatchSourceInfo) { 8138 v8::HandleScope scope; 8139 LocalContext context; 8140 v8::Handle<v8::String> source = v8::String::New( 8141 "function Foo() {\n" 8142 " return Bar();\n" 8143 "}\n" 8144 "\n" 8145 "function Bar() {\n" 8146 " return Baz();\n" 8147 "}\n" 8148 "\n" 8149 "function Baz() {\n" 8150 " throw 'nirk';\n" 8151 "}\n" 8152 "\n" 8153 "Foo();\n"); 8154 8155 const char* resource_name; 8156 v8::Handle<v8::Script> script; 8157 resource_name = "test.js"; 8158 script = v8::Script::Compile(source, v8::String::New(resource_name)); 8159 CheckTryCatchSourceInfo(script, resource_name, 0); 8160 8161 resource_name = "test1.js"; 8162 v8::ScriptOrigin origin1(v8::String::New(resource_name)); 8163 script = v8::Script::Compile(source, &origin1); 8164 CheckTryCatchSourceInfo(script, resource_name, 0); 8165 8166 resource_name = "test2.js"; 8167 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7)); 8168 script = v8::Script::Compile(source, &origin2); 8169 CheckTryCatchSourceInfo(script, resource_name, 7); 8170} 8171 8172 8173THREADED_TEST(CompilationCache) { 8174 v8::HandleScope scope; 8175 LocalContext context; 8176 v8::Handle<v8::String> source0 = v8::String::New("1234"); 8177 v8::Handle<v8::String> source1 = v8::String::New("1234"); 8178 v8::Handle<v8::Script> script0 = 8179 v8::Script::Compile(source0, v8::String::New("test.js")); 8180 v8::Handle<v8::Script> script1 = 8181 v8::Script::Compile(source1, v8::String::New("test.js")); 8182 v8::Handle<v8::Script> script2 = 8183 v8::Script::Compile(source0); // different origin 8184 CHECK_EQ(1234, script0->Run()->Int32Value()); 8185 CHECK_EQ(1234, script1->Run()->Int32Value()); 8186 CHECK_EQ(1234, script2->Run()->Int32Value()); 8187} 8188 8189 8190static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) { 8191 ApiTestFuzzer::Fuzz(); 8192 return v8_num(42); 8193} 8194 8195 8196THREADED_TEST(CallbackFunctionName) { 8197 v8::HandleScope scope; 8198 LocalContext context; 8199 Local<ObjectTemplate> t = ObjectTemplate::New(); 8200 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback)); 8201 context->Global()->Set(v8_str("obj"), t->NewInstance()); 8202 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name"); 8203 CHECK(value->IsString()); 8204 v8::String::AsciiValue name(value); 8205 CHECK_EQ("asdf", *name); 8206} 8207 8208 8209THREADED_TEST(DateAccess) { 8210 v8::HandleScope scope; 8211 LocalContext context; 8212 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0); 8213 CHECK(date->IsDate()); 8214 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue()); 8215} 8216 8217 8218void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) { 8219 v8::Handle<v8::Object> obj = val.As<v8::Object>(); 8220 v8::Handle<v8::Array> props = obj->GetPropertyNames(); 8221 CHECK_EQ(elmc, props->Length()); 8222 for (int i = 0; i < elmc; i++) { 8223 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i))); 8224 CHECK_EQ(elmv[i], *elm); 8225 } 8226} 8227 8228 8229THREADED_TEST(PropertyEnumeration) { 8230 v8::HandleScope scope; 8231 LocalContext context; 8232 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New( 8233 "var result = [];" 8234 "result[0] = {};" 8235 "result[1] = {a: 1, b: 2};" 8236 "result[2] = [1, 2, 3];" 8237 "var proto = {x: 1, y: 2, z: 3};" 8238 "var x = { __proto__: proto, w: 0, z: 1 };" 8239 "result[3] = x;" 8240 "result;"))->Run(); 8241 v8::Handle<v8::Array> elms = obj.As<v8::Array>(); 8242 CHECK_EQ(4, elms->Length()); 8243 int elmc0 = 0; 8244 const char** elmv0 = NULL; 8245 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0); 8246 int elmc1 = 2; 8247 const char* elmv1[] = {"a", "b"}; 8248 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1); 8249 int elmc2 = 3; 8250 const char* elmv2[] = {"0", "1", "2"}; 8251 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2); 8252 int elmc3 = 4; 8253 const char* elmv3[] = {"w", "z", "x", "y"}; 8254 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3); 8255} 8256 8257 8258static bool NamedSetAccessBlocker(Local<v8::Object> obj, 8259 Local<Value> name, 8260 v8::AccessType type, 8261 Local<Value> data) { 8262 return type != v8::ACCESS_SET; 8263} 8264 8265 8266static bool IndexedSetAccessBlocker(Local<v8::Object> obj, 8267 uint32_t key, 8268 v8::AccessType type, 8269 Local<Value> data) { 8270 return type != v8::ACCESS_SET; 8271} 8272 8273 8274THREADED_TEST(DisableAccessChecksWhileConfiguring) { 8275 v8::HandleScope scope; 8276 LocalContext context; 8277 Local<ObjectTemplate> templ = ObjectTemplate::New(); 8278 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker, 8279 IndexedSetAccessBlocker); 8280 templ->Set(v8_str("x"), v8::True()); 8281 Local<v8::Object> instance = templ->NewInstance(); 8282 context->Global()->Set(v8_str("obj"), instance); 8283 Local<Value> value = CompileRun("obj.x"); 8284 CHECK(value->BooleanValue()); 8285} 8286 8287 8288static bool NamedGetAccessBlocker(Local<v8::Object> obj, 8289 Local<Value> name, 8290 v8::AccessType type, 8291 Local<Value> data) { 8292 return false; 8293} 8294 8295 8296static bool IndexedGetAccessBlocker(Local<v8::Object> obj, 8297 uint32_t key, 8298 v8::AccessType type, 8299 Local<Value> data) { 8300 return false; 8301} 8302 8303 8304 8305THREADED_TEST(AccessChecksReenabledCorrectly) { 8306 v8::HandleScope scope; 8307 LocalContext context; 8308 Local<ObjectTemplate> templ = ObjectTemplate::New(); 8309 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker, 8310 IndexedGetAccessBlocker); 8311 templ->Set(v8_str("a"), v8_str("a")); 8312 // Add more than 8 (see kMaxFastProperties) properties 8313 // so that the constructor will force copying map. 8314 // Cannot sprintf, gcc complains unsafety. 8315 char buf[4]; 8316 for (char i = '0'; i <= '9' ; i++) { 8317 buf[0] = i; 8318 for (char j = '0'; j <= '9'; j++) { 8319 buf[1] = j; 8320 for (char k = '0'; k <= '9'; k++) { 8321 buf[2] = k; 8322 buf[3] = 0; 8323 templ->Set(v8_str(buf), v8::Number::New(k)); 8324 } 8325 } 8326 } 8327 8328 Local<v8::Object> instance_1 = templ->NewInstance(); 8329 context->Global()->Set(v8_str("obj_1"), instance_1); 8330 8331 Local<Value> value_1 = CompileRun("obj_1.a"); 8332 CHECK(value_1->IsUndefined()); 8333 8334 Local<v8::Object> instance_2 = templ->NewInstance(); 8335 context->Global()->Set(v8_str("obj_2"), instance_2); 8336 8337 Local<Value> value_2 = CompileRun("obj_2.a"); 8338 CHECK(value_2->IsUndefined()); 8339} 8340 8341 8342// This tests that access check information remains on the global 8343// object template when creating contexts. 8344THREADED_TEST(AccessControlRepeatedContextCreation) { 8345 v8::HandleScope handle_scope; 8346 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 8347 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker, 8348 IndexedSetAccessBlocker); 8349 i::Handle<i::ObjectTemplateInfo> internal_template = 8350 v8::Utils::OpenHandle(*global_template); 8351 CHECK(!internal_template->constructor()->IsUndefined()); 8352 i::Handle<i::FunctionTemplateInfo> constructor( 8353 i::FunctionTemplateInfo::cast(internal_template->constructor())); 8354 CHECK(!constructor->access_check_info()->IsUndefined()); 8355 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 8356 CHECK(!constructor->access_check_info()->IsUndefined()); 8357} 8358 8359 8360THREADED_TEST(TurnOnAccessCheck) { 8361 v8::HandleScope handle_scope; 8362 8363 // Create an environment with access check to the global object disabled by 8364 // default. 8365 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 8366 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker, 8367 IndexedGetAccessBlocker, 8368 v8::Handle<v8::Value>(), 8369 false); 8370 v8::Persistent<Context> context = Context::New(NULL, global_template); 8371 Context::Scope context_scope(context); 8372 8373 // Set up a property and a number of functions. 8374 context->Global()->Set(v8_str("a"), v8_num(1)); 8375 CompileRun("function f1() {return a;}" 8376 "function f2() {return a;}" 8377 "function g1() {return h();}" 8378 "function g2() {return h();}" 8379 "function h() {return 1;}"); 8380 Local<Function> f1 = 8381 Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); 8382 Local<Function> f2 = 8383 Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); 8384 Local<Function> g1 = 8385 Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); 8386 Local<Function> g2 = 8387 Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); 8388 Local<Function> h = 8389 Local<Function>::Cast(context->Global()->Get(v8_str("h"))); 8390 8391 // Get the global object. 8392 v8::Handle<v8::Object> global = context->Global(); 8393 8394 // Call f1 one time and f2 a number of times. This will ensure that f1 still 8395 // uses the runtime system to retreive property a whereas f2 uses global load 8396 // inline cache. 8397 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1))); 8398 for (int i = 0; i < 4; i++) { 8399 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1))); 8400 } 8401 8402 // Same for g1 and g2. 8403 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1))); 8404 for (int i = 0; i < 4; i++) { 8405 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1))); 8406 } 8407 8408 // Detach the global and turn on access check. 8409 context->DetachGlobal(); 8410 context->Global()->TurnOnAccessCheck(); 8411 8412 // Failing access check to property get results in undefined. 8413 CHECK(f1->Call(global, 0, NULL)->IsUndefined()); 8414 CHECK(f2->Call(global, 0, NULL)->IsUndefined()); 8415 8416 // Failing access check to function call results in exception. 8417 CHECK(g1->Call(global, 0, NULL).IsEmpty()); 8418 CHECK(g2->Call(global, 0, NULL).IsEmpty()); 8419 8420 // No failing access check when just returning a constant. 8421 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1))); 8422} 8423 8424 8425// This test verifies that pre-compilation (aka preparsing) can be called 8426// without initializing the whole VM. Thus we cannot run this test in a 8427// multi-threaded setup. 8428TEST(PreCompile) { 8429 // TODO(155): This test would break without the initialization of V8. This is 8430 // a workaround for now to make this test not fail. 8431 v8::V8::Initialize(); 8432 const char* script = "function foo(a) { return a+1; }"; 8433 v8::ScriptData* sd = 8434 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8435 CHECK_NE(sd->Length(), 0); 8436 CHECK_NE(sd->Data(), NULL); 8437 CHECK(!sd->HasError()); 8438 delete sd; 8439} 8440 8441 8442TEST(PreCompileWithError) { 8443 v8::V8::Initialize(); 8444 const char* script = "function foo(a) { return 1 * * 2; }"; 8445 v8::ScriptData* sd = 8446 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8447 CHECK(sd->HasError()); 8448 delete sd; 8449} 8450 8451 8452TEST(Regress31661) { 8453 v8::V8::Initialize(); 8454 const char* script = " The Definintive Guide"; 8455 v8::ScriptData* sd = 8456 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8457 CHECK(sd->HasError()); 8458 delete sd; 8459} 8460 8461 8462// Tests that ScriptData can be serialized and deserialized. 8463TEST(PreCompileSerialization) { 8464 v8::V8::Initialize(); 8465 const char* script = "function foo(a) { return a+1; }"; 8466 v8::ScriptData* sd = 8467 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8468 8469 // Serialize. 8470 int serialized_data_length = sd->Length(); 8471 char* serialized_data = i::NewArray<char>(serialized_data_length); 8472 memcpy(serialized_data, sd->Data(), serialized_data_length); 8473 8474 // Deserialize. 8475 v8::ScriptData* deserialized_sd = 8476 v8::ScriptData::New(serialized_data, serialized_data_length); 8477 8478 // Verify that the original is the same as the deserialized. 8479 CHECK_EQ(sd->Length(), deserialized_sd->Length()); 8480 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length())); 8481 CHECK_EQ(sd->HasError(), deserialized_sd->HasError()); 8482 8483 delete sd; 8484 delete deserialized_sd; 8485} 8486 8487 8488// Attempts to deserialize bad data. 8489TEST(PreCompileDeserializationError) { 8490 v8::V8::Initialize(); 8491 const char* data = "DONT CARE"; 8492 int invalid_size = 3; 8493 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size); 8494 8495 CHECK_EQ(0, sd->Length()); 8496 8497 delete sd; 8498} 8499 8500 8501// Verifies that the Handle<String> and const char* versions of the API produce 8502// the same results (at least for one trivial case). 8503TEST(PreCompileAPIVariationsAreSame) { 8504 v8::V8::Initialize(); 8505 v8::HandleScope scope; 8506 8507 const char* cstring = "function foo(a) { return a+1; }"; 8508 v8::ScriptData* sd_from_cstring = 8509 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring)); 8510 8511 TestAsciiResource* resource = new TestAsciiResource(cstring); 8512 v8::ScriptData* sd_from_istring = v8::ScriptData::PreCompile( 8513 v8::String::NewExternal(resource)); 8514 8515 CHECK_EQ(sd_from_cstring->Length(), sd_from_istring->Length()); 8516 CHECK_EQ(0, memcmp(sd_from_cstring->Data(), 8517 sd_from_istring->Data(), 8518 sd_from_cstring->Length())); 8519 8520 delete sd_from_cstring; 8521 delete sd_from_istring; 8522} 8523 8524 8525// This tests that we do not allow dictionary load/call inline caches 8526// to use functions that have not yet been compiled. The potential 8527// problem of loading a function that has not yet been compiled can 8528// arise because we share code between contexts via the compilation 8529// cache. 8530THREADED_TEST(DictionaryICLoadedFunction) { 8531 v8::HandleScope scope; 8532 // Test LoadIC. 8533 for (int i = 0; i < 2; i++) { 8534 LocalContext context; 8535 context->Global()->Set(v8_str("tmp"), v8::True()); 8536 context->Global()->Delete(v8_str("tmp")); 8537 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');"); 8538 } 8539 // Test CallIC. 8540 for (int i = 0; i < 2; i++) { 8541 LocalContext context; 8542 context->Global()->Set(v8_str("tmp"), v8::True()); 8543 context->Global()->Delete(v8_str("tmp")); 8544 CompileRun("for (var j = 0; j < 10; j++) RegExp('')"); 8545 } 8546} 8547 8548 8549// Test that cross-context new calls use the context of the callee to 8550// create the new JavaScript object. 8551THREADED_TEST(CrossContextNew) { 8552 v8::HandleScope scope; 8553 v8::Persistent<Context> context0 = Context::New(); 8554 v8::Persistent<Context> context1 = Context::New(); 8555 8556 // Allow cross-domain access. 8557 Local<String> token = v8_str("<security token>"); 8558 context0->SetSecurityToken(token); 8559 context1->SetSecurityToken(token); 8560 8561 // Set an 'x' property on the Object prototype and define a 8562 // constructor function in context0. 8563 context0->Enter(); 8564 CompileRun("Object.prototype.x = 42; function C() {};"); 8565 context0->Exit(); 8566 8567 // Call the constructor function from context0 and check that the 8568 // result has the 'x' property. 8569 context1->Enter(); 8570 context1->Global()->Set(v8_str("other"), context0->Global()); 8571 Local<Value> value = CompileRun("var instance = new other.C(); instance.x"); 8572 CHECK(value->IsInt32()); 8573 CHECK_EQ(42, value->Int32Value()); 8574 context1->Exit(); 8575 8576 // Dispose the contexts to allow them to be garbage collected. 8577 context0.Dispose(); 8578 context1.Dispose(); 8579} 8580 8581 8582class RegExpInterruptTest { 8583 public: 8584 RegExpInterruptTest() : block_(NULL) {} 8585 ~RegExpInterruptTest() { delete block_; } 8586 void RunTest() { 8587 block_ = i::OS::CreateSemaphore(0); 8588 gc_count_ = 0; 8589 gc_during_regexp_ = 0; 8590 regexp_success_ = false; 8591 gc_success_ = false; 8592 GCThread gc_thread(this); 8593 gc_thread.Start(); 8594 v8::Locker::StartPreemption(1); 8595 8596 LongRunningRegExp(); 8597 { 8598 v8::Unlocker unlock; 8599 gc_thread.Join(); 8600 } 8601 v8::Locker::StopPreemption(); 8602 CHECK(regexp_success_); 8603 CHECK(gc_success_); 8604 } 8605 private: 8606 // Number of garbage collections required. 8607 static const int kRequiredGCs = 5; 8608 8609 class GCThread : public i::Thread { 8610 public: 8611 explicit GCThread(RegExpInterruptTest* test) 8612 : test_(test) {} 8613 virtual void Run() { 8614 test_->CollectGarbage(); 8615 } 8616 private: 8617 RegExpInterruptTest* test_; 8618 }; 8619 8620 void CollectGarbage() { 8621 block_->Wait(); 8622 while (gc_during_regexp_ < kRequiredGCs) { 8623 { 8624 v8::Locker lock; 8625 // TODO(lrn): Perhaps create some garbage before collecting. 8626 i::Heap::CollectAllGarbage(false); 8627 gc_count_++; 8628 } 8629 i::OS::Sleep(1); 8630 } 8631 gc_success_ = true; 8632 } 8633 8634 void LongRunningRegExp() { 8635 block_->Signal(); // Enable garbage collection thread on next preemption. 8636 int rounds = 0; 8637 while (gc_during_regexp_ < kRequiredGCs) { 8638 int gc_before = gc_count_; 8639 { 8640 // Match 15-30 "a"'s against 14 and a "b". 8641 const char* c_source = 8642 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 8643 ".exec('aaaaaaaaaaaaaaab') === null"; 8644 Local<String> source = String::New(c_source); 8645 Local<Script> script = Script::Compile(source); 8646 Local<Value> result = script->Run(); 8647 if (!result->BooleanValue()) { 8648 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit. 8649 return; 8650 } 8651 } 8652 { 8653 // Match 15-30 "a"'s against 15 and a "b". 8654 const char* c_source = 8655 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 8656 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'"; 8657 Local<String> source = String::New(c_source); 8658 Local<Script> script = Script::Compile(source); 8659 Local<Value> result = script->Run(); 8660 if (!result->BooleanValue()) { 8661 gc_during_regexp_ = kRequiredGCs; 8662 return; 8663 } 8664 } 8665 int gc_after = gc_count_; 8666 gc_during_regexp_ += gc_after - gc_before; 8667 rounds++; 8668 i::OS::Sleep(1); 8669 } 8670 regexp_success_ = true; 8671 } 8672 8673 i::Semaphore* block_; 8674 int gc_count_; 8675 int gc_during_regexp_; 8676 bool regexp_success_; 8677 bool gc_success_; 8678}; 8679 8680 8681// Test that a regular expression execution can be interrupted and 8682// survive a garbage collection. 8683TEST(RegExpInterruption) { 8684 v8::Locker lock; 8685 v8::V8::Initialize(); 8686 v8::HandleScope scope; 8687 Local<Context> local_env; 8688 { 8689 LocalContext env; 8690 local_env = env.local(); 8691 } 8692 8693 // Local context should still be live. 8694 CHECK(!local_env.IsEmpty()); 8695 local_env->Enter(); 8696 8697 // Should complete without problems. 8698 RegExpInterruptTest().RunTest(); 8699 8700 local_env->Exit(); 8701} 8702 8703 8704class ApplyInterruptTest { 8705 public: 8706 ApplyInterruptTest() : block_(NULL) {} 8707 ~ApplyInterruptTest() { delete block_; } 8708 void RunTest() { 8709 block_ = i::OS::CreateSemaphore(0); 8710 gc_count_ = 0; 8711 gc_during_apply_ = 0; 8712 apply_success_ = false; 8713 gc_success_ = false; 8714 GCThread gc_thread(this); 8715 gc_thread.Start(); 8716 v8::Locker::StartPreemption(1); 8717 8718 LongRunningApply(); 8719 { 8720 v8::Unlocker unlock; 8721 gc_thread.Join(); 8722 } 8723 v8::Locker::StopPreemption(); 8724 CHECK(apply_success_); 8725 CHECK(gc_success_); 8726 } 8727 private: 8728 // Number of garbage collections required. 8729 static const int kRequiredGCs = 2; 8730 8731 class GCThread : public i::Thread { 8732 public: 8733 explicit GCThread(ApplyInterruptTest* test) 8734 : test_(test) {} 8735 virtual void Run() { 8736 test_->CollectGarbage(); 8737 } 8738 private: 8739 ApplyInterruptTest* test_; 8740 }; 8741 8742 void CollectGarbage() { 8743 block_->Wait(); 8744 while (gc_during_apply_ < kRequiredGCs) { 8745 { 8746 v8::Locker lock; 8747 i::Heap::CollectAllGarbage(false); 8748 gc_count_++; 8749 } 8750 i::OS::Sleep(1); 8751 } 8752 gc_success_ = true; 8753 } 8754 8755 void LongRunningApply() { 8756 block_->Signal(); 8757 int rounds = 0; 8758 while (gc_during_apply_ < kRequiredGCs) { 8759 int gc_before = gc_count_; 8760 { 8761 const char* c_source = 8762 "function do_very_little(bar) {" 8763 " this.foo = bar;" 8764 "}" 8765 "for (var i = 0; i < 100000; i++) {" 8766 " do_very_little.apply(this, ['bar']);" 8767 "}"; 8768 Local<String> source = String::New(c_source); 8769 Local<Script> script = Script::Compile(source); 8770 Local<Value> result = script->Run(); 8771 // Check that no exception was thrown. 8772 CHECK(!result.IsEmpty()); 8773 } 8774 int gc_after = gc_count_; 8775 gc_during_apply_ += gc_after - gc_before; 8776 rounds++; 8777 } 8778 apply_success_ = true; 8779 } 8780 8781 i::Semaphore* block_; 8782 int gc_count_; 8783 int gc_during_apply_; 8784 bool apply_success_; 8785 bool gc_success_; 8786}; 8787 8788 8789// Test that nothing bad happens if we get a preemption just when we were 8790// about to do an apply(). 8791TEST(ApplyInterruption) { 8792 v8::Locker lock; 8793 v8::V8::Initialize(); 8794 v8::HandleScope scope; 8795 Local<Context> local_env; 8796 { 8797 LocalContext env; 8798 local_env = env.local(); 8799 } 8800 8801 // Local context should still be live. 8802 CHECK(!local_env.IsEmpty()); 8803 local_env->Enter(); 8804 8805 // Should complete without problems. 8806 ApplyInterruptTest().RunTest(); 8807 8808 local_env->Exit(); 8809} 8810 8811 8812// Verify that we can clone an object 8813TEST(ObjectClone) { 8814 v8::HandleScope scope; 8815 LocalContext env; 8816 8817 const char* sample = 8818 "var rv = {};" \ 8819 "rv.alpha = 'hello';" \ 8820 "rv.beta = 123;" \ 8821 "rv;"; 8822 8823 // Create an object, verify basics. 8824 Local<Value> val = CompileRun(sample); 8825 CHECK(val->IsObject()); 8826 Local<v8::Object> obj = val.As<v8::Object>(); 8827 obj->Set(v8_str("gamma"), v8_str("cloneme")); 8828 8829 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha"))); 8830 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 8831 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma"))); 8832 8833 // Clone it. 8834 Local<v8::Object> clone = obj->Clone(); 8835 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha"))); 8836 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta"))); 8837 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma"))); 8838 8839 // Set a property on the clone, verify each object. 8840 clone->Set(v8_str("beta"), v8::Integer::New(456)); 8841 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 8842 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta"))); 8843} 8844 8845 8846class AsciiVectorResource : public v8::String::ExternalAsciiStringResource { 8847 public: 8848 explicit AsciiVectorResource(i::Vector<const char> vector) 8849 : data_(vector) {} 8850 virtual ~AsciiVectorResource() {} 8851 virtual size_t length() const { return data_.length(); } 8852 virtual const char* data() const { return data_.start(); } 8853 private: 8854 i::Vector<const char> data_; 8855}; 8856 8857 8858class UC16VectorResource : public v8::String::ExternalStringResource { 8859 public: 8860 explicit UC16VectorResource(i::Vector<const i::uc16> vector) 8861 : data_(vector) {} 8862 virtual ~UC16VectorResource() {} 8863 virtual size_t length() const { return data_.length(); } 8864 virtual const i::uc16* data() const { return data_.start(); } 8865 private: 8866 i::Vector<const i::uc16> data_; 8867}; 8868 8869 8870static void MorphAString(i::String* string, 8871 AsciiVectorResource* ascii_resource, 8872 UC16VectorResource* uc16_resource) { 8873 CHECK(i::StringShape(string).IsExternal()); 8874 if (string->IsAsciiRepresentation()) { 8875 // Check old map is not symbol or long. 8876 CHECK(string->map() == i::Heap::external_ascii_string_map()); 8877 // Morph external string to be TwoByte string. 8878 string->set_map(i::Heap::external_string_map()); 8879 i::ExternalTwoByteString* morphed = 8880 i::ExternalTwoByteString::cast(string); 8881 morphed->set_resource(uc16_resource); 8882 } else { 8883 // Check old map is not symbol or long. 8884 CHECK(string->map() == i::Heap::external_string_map()); 8885 // Morph external string to be ASCII string. 8886 string->set_map(i::Heap::external_ascii_string_map()); 8887 i::ExternalAsciiString* morphed = 8888 i::ExternalAsciiString::cast(string); 8889 morphed->set_resource(ascii_resource); 8890 } 8891} 8892 8893 8894// Test that we can still flatten a string if the components it is built up 8895// from have been turned into 16 bit strings in the mean time. 8896THREADED_TEST(MorphCompositeStringTest) { 8897 const char* c_string = "Now is the time for all good men" 8898 " to come to the aid of the party"; 8899 uint16_t* two_byte_string = AsciiToTwoByteString(c_string); 8900 { 8901 v8::HandleScope scope; 8902 LocalContext env; 8903 AsciiVectorResource ascii_resource( 8904 i::Vector<const char>(c_string, i::StrLength(c_string))); 8905 UC16VectorResource uc16_resource( 8906 i::Vector<const uint16_t>(two_byte_string, 8907 i::StrLength(c_string))); 8908 8909 Local<String> lhs(v8::Utils::ToLocal( 8910 i::Factory::NewExternalStringFromAscii(&ascii_resource))); 8911 Local<String> rhs(v8::Utils::ToLocal( 8912 i::Factory::NewExternalStringFromAscii(&ascii_resource))); 8913 8914 env->Global()->Set(v8_str("lhs"), lhs); 8915 env->Global()->Set(v8_str("rhs"), rhs); 8916 8917 CompileRun( 8918 "var cons = lhs + rhs;" 8919 "var slice = lhs.substring(1, lhs.length - 1);" 8920 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);"); 8921 8922 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource); 8923 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource); 8924 8925 // Now do some stuff to make sure the strings are flattened, etc. 8926 CompileRun( 8927 "/[^a-z]/.test(cons);" 8928 "/[^a-z]/.test(slice);" 8929 "/[^a-z]/.test(slice_on_cons);"); 8930 const char* expected_cons = 8931 "Now is the time for all good men to come to the aid of the party" 8932 "Now is the time for all good men to come to the aid of the party"; 8933 const char* expected_slice = 8934 "ow is the time for all good men to come to the aid of the part"; 8935 const char* expected_slice_on_cons = 8936 "ow is the time for all good men to come to the aid of the party" 8937 "Now is the time for all good men to come to the aid of the part"; 8938 CHECK_EQ(String::New(expected_cons), 8939 env->Global()->Get(v8_str("cons"))); 8940 CHECK_EQ(String::New(expected_slice), 8941 env->Global()->Get(v8_str("slice"))); 8942 CHECK_EQ(String::New(expected_slice_on_cons), 8943 env->Global()->Get(v8_str("slice_on_cons"))); 8944 } 8945} 8946 8947 8948TEST(CompileExternalTwoByteSource) { 8949 v8::HandleScope scope; 8950 LocalContext context; 8951 8952 // This is a very short list of sources, which currently is to check for a 8953 // regression caused by r2703. 8954 const char* ascii_sources[] = { 8955 "0.5", 8956 "-0.5", // This mainly testes PushBack in the Scanner. 8957 "--0.5", // This mainly testes PushBack in the Scanner. 8958 NULL 8959 }; 8960 8961 // Compile the sources as external two byte strings. 8962 for (int i = 0; ascii_sources[i] != NULL; i++) { 8963 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]); 8964 UC16VectorResource uc16_resource( 8965 i::Vector<const uint16_t>(two_byte_string, 8966 i::StrLength(ascii_sources[i]))); 8967 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource); 8968 v8::Script::Compile(source); 8969 } 8970} 8971 8972 8973class RegExpStringModificationTest { 8974 public: 8975 RegExpStringModificationTest() 8976 : block_(i::OS::CreateSemaphore(0)), 8977 morphs_(0), 8978 morphs_during_regexp_(0), 8979 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)), 8980 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {} 8981 ~RegExpStringModificationTest() { delete block_; } 8982 void RunTest() { 8983 regexp_success_ = false; 8984 morph_success_ = false; 8985 8986 // Initialize the contents of two_byte_content_ to be a uc16 representation 8987 // of "aaaaaaaaaaaaaab". 8988 for (int i = 0; i < 14; i++) { 8989 two_byte_content_[i] = 'a'; 8990 } 8991 two_byte_content_[14] = 'b'; 8992 8993 // Create the input string for the regexp - the one we are going to change 8994 // properties of. 8995 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_); 8996 8997 // Inject the input as a global variable. 8998 i::Handle<i::String> input_name = 8999 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5)); 9000 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE); 9001 9002 9003 MorphThread morph_thread(this); 9004 morph_thread.Start(); 9005 v8::Locker::StartPreemption(1); 9006 LongRunningRegExp(); 9007 { 9008 v8::Unlocker unlock; 9009 morph_thread.Join(); 9010 } 9011 v8::Locker::StopPreemption(); 9012 CHECK(regexp_success_); 9013 CHECK(morph_success_); 9014 } 9015 private: 9016 9017 // Number of string modifications required. 9018 static const int kRequiredModifications = 5; 9019 static const int kMaxModifications = 100; 9020 9021 class MorphThread : public i::Thread { 9022 public: 9023 explicit MorphThread(RegExpStringModificationTest* test) 9024 : test_(test) {} 9025 virtual void Run() { 9026 test_->MorphString(); 9027 } 9028 private: 9029 RegExpStringModificationTest* test_; 9030 }; 9031 9032 void MorphString() { 9033 block_->Wait(); 9034 while (morphs_during_regexp_ < kRequiredModifications && 9035 morphs_ < kMaxModifications) { 9036 { 9037 v8::Locker lock; 9038 // Swap string between ascii and two-byte representation. 9039 i::String* string = *input_; 9040 MorphAString(string, &ascii_resource_, &uc16_resource_); 9041 morphs_++; 9042 } 9043 i::OS::Sleep(1); 9044 } 9045 morph_success_ = true; 9046 } 9047 9048 void LongRunningRegExp() { 9049 block_->Signal(); // Enable morphing thread on next preemption. 9050 while (morphs_during_regexp_ < kRequiredModifications && 9051 morphs_ < kMaxModifications) { 9052 int morphs_before = morphs_; 9053 { 9054 // Match 15-30 "a"'s against 14 and a "b". 9055 const char* c_source = 9056 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 9057 ".exec(input) === null"; 9058 Local<String> source = String::New(c_source); 9059 Local<Script> script = Script::Compile(source); 9060 Local<Value> result = script->Run(); 9061 CHECK(result->IsTrue()); 9062 } 9063 int morphs_after = morphs_; 9064 morphs_during_regexp_ += morphs_after - morphs_before; 9065 } 9066 regexp_success_ = true; 9067 } 9068 9069 i::uc16 two_byte_content_[15]; 9070 i::Semaphore* block_; 9071 int morphs_; 9072 int morphs_during_regexp_; 9073 bool regexp_success_; 9074 bool morph_success_; 9075 i::Handle<i::String> input_; 9076 AsciiVectorResource ascii_resource_; 9077 UC16VectorResource uc16_resource_; 9078}; 9079 9080 9081// Test that a regular expression execution can be interrupted and 9082// the string changed without failing. 9083TEST(RegExpStringModification) { 9084 v8::Locker lock; 9085 v8::V8::Initialize(); 9086 v8::HandleScope scope; 9087 Local<Context> local_env; 9088 { 9089 LocalContext env; 9090 local_env = env.local(); 9091 } 9092 9093 // Local context should still be live. 9094 CHECK(!local_env.IsEmpty()); 9095 local_env->Enter(); 9096 9097 // Should complete without problems. 9098 RegExpStringModificationTest().RunTest(); 9099 9100 local_env->Exit(); 9101} 9102 9103 9104// Test that we can set a property on the global object even if there 9105// is a read-only property in the prototype chain. 9106TEST(ReadOnlyPropertyInGlobalProto) { 9107 v8::HandleScope scope; 9108 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9109 LocalContext context(0, templ); 9110 v8::Handle<v8::Object> global = context->Global(); 9111 v8::Handle<v8::Object> global_proto = 9112 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__"))); 9113 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly); 9114 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly); 9115 // Check without 'eval' or 'with'. 9116 v8::Handle<v8::Value> res = 9117 CompileRun("function f() { x = 42; return x; }; f()"); 9118 // Check with 'eval'. 9119 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()"); 9120 CHECK_EQ(v8::Integer::New(42), res); 9121 // Check with 'with'. 9122 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()"); 9123 CHECK_EQ(v8::Integer::New(42), res); 9124} 9125 9126static int force_set_set_count = 0; 9127static int force_set_get_count = 0; 9128bool pass_on_get = false; 9129 9130static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name, 9131 const v8::AccessorInfo& info) { 9132 force_set_get_count++; 9133 if (pass_on_get) { 9134 return v8::Handle<v8::Value>(); 9135 } else { 9136 return v8::Int32::New(3); 9137 } 9138} 9139 9140static void ForceSetSetter(v8::Local<v8::String> name, 9141 v8::Local<v8::Value> value, 9142 const v8::AccessorInfo& info) { 9143 force_set_set_count++; 9144} 9145 9146static v8::Handle<v8::Value> ForceSetInterceptSetter( 9147 v8::Local<v8::String> name, 9148 v8::Local<v8::Value> value, 9149 const v8::AccessorInfo& info) { 9150 force_set_set_count++; 9151 return v8::Undefined(); 9152} 9153 9154TEST(ForceSet) { 9155 force_set_get_count = 0; 9156 force_set_set_count = 0; 9157 pass_on_get = false; 9158 9159 v8::HandleScope scope; 9160 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9161 v8::Handle<v8::String> access_property = v8::String::New("a"); 9162 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter); 9163 LocalContext context(NULL, templ); 9164 v8::Handle<v8::Object> global = context->Global(); 9165 9166 // Ordinary properties 9167 v8::Handle<v8::String> simple_property = v8::String::New("p"); 9168 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly); 9169 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9170 // This should fail because the property is read-only 9171 global->Set(simple_property, v8::Int32::New(5)); 9172 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9173 // This should succeed even though the property is read-only 9174 global->ForceSet(simple_property, v8::Int32::New(6)); 9175 CHECK_EQ(6, global->Get(simple_property)->Int32Value()); 9176 9177 // Accessors 9178 CHECK_EQ(0, force_set_set_count); 9179 CHECK_EQ(0, force_set_get_count); 9180 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 9181 // CHECK_EQ the property shouldn't override it, just call the setter 9182 // which in this case does nothing. 9183 global->Set(access_property, v8::Int32::New(7)); 9184 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 9185 CHECK_EQ(1, force_set_set_count); 9186 CHECK_EQ(2, force_set_get_count); 9187 // Forcing the property to be set should override the accessor without 9188 // calling it 9189 global->ForceSet(access_property, v8::Int32::New(8)); 9190 CHECK_EQ(8, global->Get(access_property)->Int32Value()); 9191 CHECK_EQ(1, force_set_set_count); 9192 CHECK_EQ(2, force_set_get_count); 9193} 9194 9195TEST(ForceSetWithInterceptor) { 9196 force_set_get_count = 0; 9197 force_set_set_count = 0; 9198 pass_on_get = false; 9199 9200 v8::HandleScope scope; 9201 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9202 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter); 9203 LocalContext context(NULL, templ); 9204 v8::Handle<v8::Object> global = context->Global(); 9205 9206 v8::Handle<v8::String> some_property = v8::String::New("a"); 9207 CHECK_EQ(0, force_set_set_count); 9208 CHECK_EQ(0, force_set_get_count); 9209 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 9210 // Setting the property shouldn't override it, just call the setter 9211 // which in this case does nothing. 9212 global->Set(some_property, v8::Int32::New(7)); 9213 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 9214 CHECK_EQ(1, force_set_set_count); 9215 CHECK_EQ(2, force_set_get_count); 9216 // Getting the property when the interceptor returns an empty handle 9217 // should yield undefined, since the property isn't present on the 9218 // object itself yet. 9219 pass_on_get = true; 9220 CHECK(global->Get(some_property)->IsUndefined()); 9221 CHECK_EQ(1, force_set_set_count); 9222 CHECK_EQ(3, force_set_get_count); 9223 // Forcing the property to be set should cause the value to be 9224 // set locally without calling the interceptor. 9225 global->ForceSet(some_property, v8::Int32::New(8)); 9226 CHECK_EQ(8, global->Get(some_property)->Int32Value()); 9227 CHECK_EQ(1, force_set_set_count); 9228 CHECK_EQ(4, force_set_get_count); 9229 // Reenabling the interceptor should cause it to take precedence over 9230 // the property 9231 pass_on_get = false; 9232 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 9233 CHECK_EQ(1, force_set_set_count); 9234 CHECK_EQ(5, force_set_get_count); 9235 // The interceptor should also work for other properties 9236 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value()); 9237 CHECK_EQ(1, force_set_set_count); 9238 CHECK_EQ(6, force_set_get_count); 9239} 9240 9241 9242THREADED_TEST(ForceDelete) { 9243 v8::HandleScope scope; 9244 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9245 LocalContext context(NULL, templ); 9246 v8::Handle<v8::Object> global = context->Global(); 9247 9248 // Ordinary properties 9249 v8::Handle<v8::String> simple_property = v8::String::New("p"); 9250 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete); 9251 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9252 // This should fail because the property is dont-delete. 9253 CHECK(!global->Delete(simple_property)); 9254 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9255 // This should succeed even though the property is dont-delete. 9256 CHECK(global->ForceDelete(simple_property)); 9257 CHECK(global->Get(simple_property)->IsUndefined()); 9258} 9259 9260 9261static int force_delete_interceptor_count = 0; 9262static bool pass_on_delete = false; 9263 9264 9265static v8::Handle<v8::Boolean> ForceDeleteDeleter( 9266 v8::Local<v8::String> name, 9267 const v8::AccessorInfo& info) { 9268 force_delete_interceptor_count++; 9269 if (pass_on_delete) { 9270 return v8::Handle<v8::Boolean>(); 9271 } else { 9272 return v8::True(); 9273 } 9274} 9275 9276 9277THREADED_TEST(ForceDeleteWithInterceptor) { 9278 force_delete_interceptor_count = 0; 9279 pass_on_delete = false; 9280 9281 v8::HandleScope scope; 9282 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9283 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter); 9284 LocalContext context(NULL, templ); 9285 v8::Handle<v8::Object> global = context->Global(); 9286 9287 v8::Handle<v8::String> some_property = v8::String::New("a"); 9288 global->Set(some_property, v8::Integer::New(42), v8::DontDelete); 9289 9290 // Deleting a property should get intercepted and nothing should 9291 // happen. 9292 CHECK_EQ(0, force_delete_interceptor_count); 9293 CHECK(global->Delete(some_property)); 9294 CHECK_EQ(1, force_delete_interceptor_count); 9295 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 9296 // Deleting the property when the interceptor returns an empty 9297 // handle should not delete the property since it is DontDelete. 9298 pass_on_delete = true; 9299 CHECK(!global->Delete(some_property)); 9300 CHECK_EQ(2, force_delete_interceptor_count); 9301 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 9302 // Forcing the property to be deleted should delete the value 9303 // without calling the interceptor. 9304 CHECK(global->ForceDelete(some_property)); 9305 CHECK(global->Get(some_property)->IsUndefined()); 9306 CHECK_EQ(2, force_delete_interceptor_count); 9307} 9308 9309 9310// Make sure that forcing a delete invalidates any IC stubs, so we 9311// don't read the hole value. 9312THREADED_TEST(ForceDeleteIC) { 9313 v8::HandleScope scope; 9314 LocalContext context; 9315 // Create a DontDelete variable on the global object. 9316 CompileRun("this.__proto__ = { foo: 'horse' };" 9317 "var foo = 'fish';" 9318 "function f() { return foo.length; }"); 9319 // Initialize the IC for foo in f. 9320 CompileRun("for (var i = 0; i < 4; i++) f();"); 9321 // Make sure the value of foo is correct before the deletion. 9322 CHECK_EQ(4, CompileRun("f()")->Int32Value()); 9323 // Force the deletion of foo. 9324 CHECK(context->Global()->ForceDelete(v8_str("foo"))); 9325 // Make sure the value for foo is read from the prototype, and that 9326 // we don't get in trouble with reading the deleted cell value 9327 // sentinel. 9328 CHECK_EQ(5, CompileRun("f()")->Int32Value()); 9329} 9330 9331 9332v8::Persistent<Context> calling_context0; 9333v8::Persistent<Context> calling_context1; 9334v8::Persistent<Context> calling_context2; 9335 9336 9337// Check that the call to the callback is initiated in 9338// calling_context2, the directly calling context is calling_context1 9339// and the callback itself is in calling_context0. 9340static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) { 9341 ApiTestFuzzer::Fuzz(); 9342 CHECK(Context::GetCurrent() == calling_context0); 9343 CHECK(Context::GetCalling() == calling_context1); 9344 CHECK(Context::GetEntered() == calling_context2); 9345 return v8::Integer::New(42); 9346} 9347 9348 9349THREADED_TEST(GetCallingContext) { 9350 v8::HandleScope scope; 9351 9352 calling_context0 = Context::New(); 9353 calling_context1 = Context::New(); 9354 calling_context2 = Context::New(); 9355 9356 // Allow cross-domain access. 9357 Local<String> token = v8_str("<security token>"); 9358 calling_context0->SetSecurityToken(token); 9359 calling_context1->SetSecurityToken(token); 9360 calling_context2->SetSecurityToken(token); 9361 9362 // Create an object with a C++ callback in context0. 9363 calling_context0->Enter(); 9364 Local<v8::FunctionTemplate> callback_templ = 9365 v8::FunctionTemplate::New(GetCallingContextCallback); 9366 calling_context0->Global()->Set(v8_str("callback"), 9367 callback_templ->GetFunction()); 9368 calling_context0->Exit(); 9369 9370 // Expose context0 in context1 and setup a function that calls the 9371 // callback function. 9372 calling_context1->Enter(); 9373 calling_context1->Global()->Set(v8_str("context0"), 9374 calling_context0->Global()); 9375 CompileRun("function f() { context0.callback() }"); 9376 calling_context1->Exit(); 9377 9378 // Expose context1 in context2 and call the callback function in 9379 // context0 indirectly through f in context1. 9380 calling_context2->Enter(); 9381 calling_context2->Global()->Set(v8_str("context1"), 9382 calling_context1->Global()); 9383 CompileRun("context1.f()"); 9384 calling_context2->Exit(); 9385 9386 // Dispose the contexts to allow them to be garbage collected. 9387 calling_context0.Dispose(); 9388 calling_context1.Dispose(); 9389 calling_context2.Dispose(); 9390 calling_context0.Clear(); 9391 calling_context1.Clear(); 9392 calling_context2.Clear(); 9393} 9394 9395 9396// Check that a variable declaration with no explicit initialization 9397// value does not shadow an existing property in the prototype chain. 9398// 9399// This is consistent with Firefox and Safari. 9400// 9401// See http://crbug.com/12548. 9402THREADED_TEST(InitGlobalVarInProtoChain) { 9403 v8::HandleScope scope; 9404 LocalContext context; 9405 // Introduce a variable in the prototype chain. 9406 CompileRun("__proto__.x = 42"); 9407 v8::Handle<v8::Value> result = CompileRun("var x; x"); 9408 CHECK(!result->IsUndefined()); 9409 CHECK_EQ(42, result->Int32Value()); 9410} 9411 9412 9413// Regression test for issue 398. 9414// If a function is added to an object, creating a constant function 9415// field, and the result is cloned, replacing the constant function on the 9416// original should not affect the clone. 9417// See http://code.google.com/p/v8/issues/detail?id=398 9418THREADED_TEST(ReplaceConstantFunction) { 9419 v8::HandleScope scope; 9420 LocalContext context; 9421 v8::Handle<v8::Object> obj = v8::Object::New(); 9422 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 9423 v8::Handle<v8::String> foo_string = v8::String::New("foo"); 9424 obj->Set(foo_string, func_templ->GetFunction()); 9425 v8::Handle<v8::Object> obj_clone = obj->Clone(); 9426 obj_clone->Set(foo_string, v8::String::New("Hello")); 9427 CHECK(!obj->Get(foo_string)->IsUndefined()); 9428} 9429 9430 9431// Regression test for http://crbug.com/16276. 9432THREADED_TEST(Regress16276) { 9433 v8::HandleScope scope; 9434 LocalContext context; 9435 // Force the IC in f to be a dictionary load IC. 9436 CompileRun("function f(obj) { return obj.x; }\n" 9437 "var obj = { x: { foo: 42 }, y: 87 };\n" 9438 "var x = obj.x;\n" 9439 "delete obj.y;\n" 9440 "for (var i = 0; i < 5; i++) f(obj);"); 9441 // Detach the global object to make 'this' refer directly to the 9442 // global object (not the proxy), and make sure that the dictionary 9443 // load IC doesn't mess up loading directly from the global object. 9444 context->DetachGlobal(); 9445 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value()); 9446} 9447 9448 9449THREADED_TEST(PixelArray) { 9450 v8::HandleScope scope; 9451 LocalContext context; 9452 const int kElementCount = 260; 9453 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); 9454 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount, 9455 pixel_data); 9456 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9457 for (int i = 0; i < kElementCount; i++) { 9458 pixels->set(i, i % 256); 9459 } 9460 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9461 for (int i = 0; i < kElementCount; i++) { 9462 CHECK_EQ(i % 256, pixels->get(i)); 9463 CHECK_EQ(i % 256, pixel_data[i]); 9464 } 9465 9466 v8::Handle<v8::Object> obj = v8::Object::New(); 9467 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 9468 // Set the elements to be the pixels. 9469 // jsobj->set_elements(*pixels); 9470 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount); 9471 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value()); 9472 obj->Set(v8_str("field"), v8::Int32::New(1503)); 9473 context->Global()->Set(v8_str("pixels"), obj); 9474 v8::Handle<v8::Value> result = CompileRun("pixels.field"); 9475 CHECK_EQ(1503, result->Int32Value()); 9476 result = CompileRun("pixels[1]"); 9477 CHECK_EQ(1, result->Int32Value()); 9478 9479 result = CompileRun("var sum = 0;" 9480 "for (var i = 0; i < 8; i++) {" 9481 " sum += pixels[i] = pixels[i] = -i;" 9482 "}" 9483 "sum;"); 9484 CHECK_EQ(-28, result->Int32Value()); 9485 9486 result = CompileRun("var sum = 0;" 9487 "for (var i = 0; i < 8; i++) {" 9488 " sum += pixels[i] = pixels[i] = 0;" 9489 "}" 9490 "sum;"); 9491 CHECK_EQ(0, result->Int32Value()); 9492 9493 result = CompileRun("var sum = 0;" 9494 "for (var i = 0; i < 8; i++) {" 9495 " sum += pixels[i] = pixels[i] = 255;" 9496 "}" 9497 "sum;"); 9498 CHECK_EQ(8 * 255, result->Int32Value()); 9499 9500 result = CompileRun("var sum = 0;" 9501 "for (var i = 0; i < 8; i++) {" 9502 " sum += pixels[i] = pixels[i] = 256 + i;" 9503 "}" 9504 "sum;"); 9505 CHECK_EQ(2076, result->Int32Value()); 9506 9507 result = CompileRun("var sum = 0;" 9508 "for (var i = 0; i < 8; i++) {" 9509 " sum += pixels[i] = pixels[i] = i;" 9510 "}" 9511 "sum;"); 9512 CHECK_EQ(28, result->Int32Value()); 9513 9514 result = CompileRun("var sum = 0;" 9515 "for (var i = 0; i < 8; i++) {" 9516 " sum += pixels[i];" 9517 "}" 9518 "sum;"); 9519 CHECK_EQ(28, result->Int32Value()); 9520 9521 i::Handle<i::Smi> value(i::Smi::FromInt(2)); 9522 i::SetElement(jsobj, 1, value); 9523 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value()); 9524 *value.location() = i::Smi::FromInt(256); 9525 i::SetElement(jsobj, 1, value); 9526 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value()); 9527 *value.location() = i::Smi::FromInt(-1); 9528 i::SetElement(jsobj, 1, value); 9529 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value()); 9530 9531 result = CompileRun("for (var i = 0; i < 8; i++) {" 9532 " pixels[i] = (i * 65) - 109;" 9533 "}" 9534 "pixels[1] + pixels[6];"); 9535 CHECK_EQ(255, result->Int32Value()); 9536 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value()); 9537 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value()); 9538 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value()); 9539 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value()); 9540 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value()); 9541 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value()); 9542 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value()); 9543 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value()); 9544 result = CompileRun("var sum = 0;" 9545 "for (var i = 0; i < 8; i++) {" 9546 " sum += pixels[i];" 9547 "}" 9548 "sum;"); 9549 CHECK_EQ(984, result->Int32Value()); 9550 9551 result = CompileRun("for (var i = 0; i < 8; i++) {" 9552 " pixels[i] = (i * 1.1);" 9553 "}" 9554 "pixels[1] + pixels[6];"); 9555 CHECK_EQ(8, result->Int32Value()); 9556 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value()); 9557 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value()); 9558 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value()); 9559 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value()); 9560 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value()); 9561 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value()); 9562 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value()); 9563 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value()); 9564 9565 result = CompileRun("for (var i = 0; i < 8; i++) {" 9566 " pixels[7] = undefined;" 9567 "}" 9568 "pixels[7];"); 9569 CHECK_EQ(0, result->Int32Value()); 9570 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value()); 9571 9572 result = CompileRun("for (var i = 0; i < 8; i++) {" 9573 " pixels[6] = '2.3';" 9574 "}" 9575 "pixels[6];"); 9576 CHECK_EQ(2, result->Int32Value()); 9577 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value()); 9578 9579 result = CompileRun("for (var i = 0; i < 8; i++) {" 9580 " pixels[5] = NaN;" 9581 "}" 9582 "pixels[5];"); 9583 CHECK_EQ(0, result->Int32Value()); 9584 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 9585 9586 result = CompileRun("for (var i = 0; i < 8; i++) {" 9587 " pixels[8] = Infinity;" 9588 "}" 9589 "pixels[8];"); 9590 CHECK_EQ(255, result->Int32Value()); 9591 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value()); 9592 9593 result = CompileRun("for (var i = 0; i < 8; i++) {" 9594 " pixels[9] = -Infinity;" 9595 "}" 9596 "pixels[9];"); 9597 CHECK_EQ(0, result->Int32Value()); 9598 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value()); 9599 9600 result = CompileRun("pixels[3] = 33;" 9601 "delete pixels[3];" 9602 "pixels[3];"); 9603 CHECK_EQ(33, result->Int32Value()); 9604 9605 result = CompileRun("pixels[0] = 10; pixels[1] = 11;" 9606 "pixels[2] = 12; pixels[3] = 13;" 9607 "pixels.__defineGetter__('2'," 9608 "function() { return 120; });" 9609 "pixels[2];"); 9610 CHECK_EQ(12, result->Int32Value()); 9611 9612 result = CompileRun("var js_array = new Array(40);" 9613 "js_array[0] = 77;" 9614 "js_array;"); 9615 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 9616 9617 result = CompileRun("pixels[1] = 23;" 9618 "pixels.__proto__ = [];" 9619 "js_array.__proto__ = pixels;" 9620 "js_array.concat(pixels);"); 9621 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 9622 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 9623 9624 result = CompileRun("pixels[1] = 23;"); 9625 CHECK_EQ(23, result->Int32Value()); 9626 9627 // Test for index greater than 255. Regression test for: 9628 // http://code.google.com/p/chromium/issues/detail?id=26337. 9629 result = CompileRun("pixels[256] = 255;"); 9630 CHECK_EQ(255, result->Int32Value()); 9631 result = CompileRun("var i = 0;" 9632 "for (var j = 0; j < 8; j++) { i = pixels[256]; }" 9633 "i"); 9634 CHECK_EQ(255, result->Int32Value()); 9635 9636 free(pixel_data); 9637} 9638 9639 9640template <class ExternalArrayClass, class ElementType> 9641static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, 9642 int64_t low, 9643 int64_t high) { 9644 v8::HandleScope scope; 9645 LocalContext context; 9646 const int kElementCount = 40; 9647 int element_size = 0; 9648 switch (array_type) { 9649 case v8::kExternalByteArray: 9650 case v8::kExternalUnsignedByteArray: 9651 element_size = 1; 9652 break; 9653 case v8::kExternalShortArray: 9654 case v8::kExternalUnsignedShortArray: 9655 element_size = 2; 9656 break; 9657 case v8::kExternalIntArray: 9658 case v8::kExternalUnsignedIntArray: 9659 case v8::kExternalFloatArray: 9660 element_size = 4; 9661 break; 9662 default: 9663 UNREACHABLE(); 9664 break; 9665 } 9666 ElementType* array_data = 9667 static_cast<ElementType*>(malloc(kElementCount * element_size)); 9668 i::Handle<ExternalArrayClass> array = 9669 i::Handle<ExternalArrayClass>::cast( 9670 i::Factory::NewExternalArray(kElementCount, array_type, array_data)); 9671 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9672 for (int i = 0; i < kElementCount; i++) { 9673 array->set(i, static_cast<ElementType>(i)); 9674 } 9675 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9676 for (int i = 0; i < kElementCount; i++) { 9677 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i))); 9678 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i])); 9679 } 9680 9681 v8::Handle<v8::Object> obj = v8::Object::New(); 9682 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 9683 // Set the elements to be the external array. 9684 obj->SetIndexedPropertiesToExternalArrayData(array_data, 9685 array_type, 9686 kElementCount); 9687 CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number())); 9688 obj->Set(v8_str("field"), v8::Int32::New(1503)); 9689 context->Global()->Set(v8_str("ext_array"), obj); 9690 v8::Handle<v8::Value> result = CompileRun("ext_array.field"); 9691 CHECK_EQ(1503, result->Int32Value()); 9692 result = CompileRun("ext_array[1]"); 9693 CHECK_EQ(1, result->Int32Value()); 9694 9695 // Check pass through of assigned smis 9696 result = CompileRun("var sum = 0;" 9697 "for (var i = 0; i < 8; i++) {" 9698 " sum += ext_array[i] = ext_array[i] = -i;" 9699 "}" 9700 "sum;"); 9701 CHECK_EQ(-28, result->Int32Value()); 9702 9703 // Check assigned smis 9704 result = CompileRun("for (var i = 0; i < 8; i++) {" 9705 " ext_array[i] = i;" 9706 "}" 9707 "var sum = 0;" 9708 "for (var i = 0; i < 8; i++) {" 9709 " sum += ext_array[i];" 9710 "}" 9711 "sum;"); 9712 CHECK_EQ(28, result->Int32Value()); 9713 9714 // Check assigned smis in reverse order 9715 result = CompileRun("for (var i = 8; --i >= 0; ) {" 9716 " ext_array[i] = i;" 9717 "}" 9718 "var sum = 0;" 9719 "for (var i = 0; i < 8; i++) {" 9720 " sum += ext_array[i];" 9721 "}" 9722 "sum;"); 9723 CHECK_EQ(28, result->Int32Value()); 9724 9725 // Check pass through of assigned HeapNumbers 9726 result = CompileRun("var sum = 0;" 9727 "for (var i = 0; i < 16; i+=2) {" 9728 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);" 9729 "}" 9730 "sum;"); 9731 CHECK_EQ(-28, result->Int32Value()); 9732 9733 // Check assigned HeapNumbers 9734 result = CompileRun("for (var i = 0; i < 16; i+=2) {" 9735 " ext_array[i] = (i * 0.5);" 9736 "}" 9737 "var sum = 0;" 9738 "for (var i = 0; i < 16; i+=2) {" 9739 " sum += ext_array[i];" 9740 "}" 9741 "sum;"); 9742 CHECK_EQ(28, result->Int32Value()); 9743 9744 // Check assigned HeapNumbers in reverse order 9745 result = CompileRun("for (var i = 14; i >= 0; i-=2) {" 9746 " ext_array[i] = (i * 0.5);" 9747 "}" 9748 "var sum = 0;" 9749 "for (var i = 0; i < 16; i+=2) {" 9750 " sum += ext_array[i];" 9751 "}" 9752 "sum;"); 9753 CHECK_EQ(28, result->Int32Value()); 9754 9755 i::ScopedVector<char> test_buf(1024); 9756 9757 // Check legal boundary conditions. 9758 // The repeated loads and stores ensure the ICs are exercised. 9759 const char* boundary_program = 9760 "var res = 0;" 9761 "for (var i = 0; i < 16; i++) {" 9762 " ext_array[i] = %lld;" 9763 " if (i > 8) {" 9764 " res = ext_array[i];" 9765 " }" 9766 "}" 9767 "res;"; 9768 i::OS::SNPrintF(test_buf, 9769 boundary_program, 9770 low); 9771 result = CompileRun(test_buf.start()); 9772 CHECK_EQ(low, result->IntegerValue()); 9773 9774 i::OS::SNPrintF(test_buf, 9775 boundary_program, 9776 high); 9777 result = CompileRun(test_buf.start()); 9778 CHECK_EQ(high, result->IntegerValue()); 9779 9780 // Check misprediction of type in IC. 9781 result = CompileRun("var tmp_array = ext_array;" 9782 "var sum = 0;" 9783 "for (var i = 0; i < 8; i++) {" 9784 " tmp_array[i] = i;" 9785 " sum += tmp_array[i];" 9786 " if (i == 4) {" 9787 " tmp_array = {};" 9788 " }" 9789 "}" 9790 "sum;"); 9791 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9792 CHECK_EQ(28, result->Int32Value()); 9793 9794 // Make sure out-of-range loads do not throw. 9795 i::OS::SNPrintF(test_buf, 9796 "var caught_exception = false;" 9797 "try {" 9798 " ext_array[%d];" 9799 "} catch (e) {" 9800 " caught_exception = true;" 9801 "}" 9802 "caught_exception;", 9803 kElementCount); 9804 result = CompileRun(test_buf.start()); 9805 CHECK_EQ(false, result->BooleanValue()); 9806 9807 // Make sure out-of-range stores do not throw. 9808 i::OS::SNPrintF(test_buf, 9809 "var caught_exception = false;" 9810 "try {" 9811 " ext_array[%d] = 1;" 9812 "} catch (e) {" 9813 " caught_exception = true;" 9814 "}" 9815 "caught_exception;", 9816 kElementCount); 9817 result = CompileRun(test_buf.start()); 9818 CHECK_EQ(false, result->BooleanValue()); 9819 9820 // Check other boundary conditions, values and operations. 9821 result = CompileRun("for (var i = 0; i < 8; i++) {" 9822 " ext_array[7] = undefined;" 9823 "}" 9824 "ext_array[7];"); 9825 CHECK_EQ(0, result->Int32Value()); 9826 CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number())); 9827 9828 result = CompileRun("for (var i = 0; i < 8; i++) {" 9829 " ext_array[6] = '2.3';" 9830 "}" 9831 "ext_array[6];"); 9832 CHECK_EQ(2, result->Int32Value()); 9833 CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number())); 9834 9835 if (array_type != v8::kExternalFloatArray) { 9836 // Though the specification doesn't state it, be explicit about 9837 // converting NaNs and +/-Infinity to zero. 9838 result = CompileRun("for (var i = 0; i < 8; i++) {" 9839 " ext_array[i] = 5;" 9840 "}" 9841 "for (var i = 0; i < 8; i++) {" 9842 " ext_array[i] = NaN;" 9843 "}" 9844 "ext_array[5];"); 9845 CHECK_EQ(0, result->Int32Value()); 9846 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 9847 9848 result = CompileRun("for (var i = 0; i < 8; i++) {" 9849 " ext_array[i] = 5;" 9850 "}" 9851 "for (var i = 0; i < 8; i++) {" 9852 " ext_array[i] = Infinity;" 9853 "}" 9854 "ext_array[5];"); 9855 CHECK_EQ(0, result->Int32Value()); 9856 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 9857 9858 result = CompileRun("for (var i = 0; i < 8; i++) {" 9859 " ext_array[i] = 5;" 9860 "}" 9861 "for (var i = 0; i < 8; i++) {" 9862 " ext_array[i] = -Infinity;" 9863 "}" 9864 "ext_array[5];"); 9865 CHECK_EQ(0, result->Int32Value()); 9866 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 9867 } 9868 9869 result = CompileRun("ext_array[3] = 33;" 9870 "delete ext_array[3];" 9871 "ext_array[3];"); 9872 CHECK_EQ(33, result->Int32Value()); 9873 9874 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;" 9875 "ext_array[2] = 12; ext_array[3] = 13;" 9876 "ext_array.__defineGetter__('2'," 9877 "function() { return 120; });" 9878 "ext_array[2];"); 9879 CHECK_EQ(12, result->Int32Value()); 9880 9881 result = CompileRun("var js_array = new Array(40);" 9882 "js_array[0] = 77;" 9883 "js_array;"); 9884 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 9885 9886 result = CompileRun("ext_array[1] = 23;" 9887 "ext_array.__proto__ = [];" 9888 "js_array.__proto__ = ext_array;" 9889 "js_array.concat(ext_array);"); 9890 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 9891 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 9892 9893 result = CompileRun("ext_array[1] = 23;"); 9894 CHECK_EQ(23, result->Int32Value()); 9895 9896 // Test more complex manipulations which cause eax to contain values 9897 // that won't be completely overwritten by loads from the arrays. 9898 // This catches bugs in the instructions used for the KeyedLoadIC 9899 // for byte and word types. 9900 { 9901 const int kXSize = 300; 9902 const int kYSize = 300; 9903 const int kLargeElementCount = kXSize * kYSize * 4; 9904 ElementType* large_array_data = 9905 static_cast<ElementType*>(malloc(kLargeElementCount * element_size)); 9906 i::Handle<ExternalArrayClass> large_array = 9907 i::Handle<ExternalArrayClass>::cast( 9908 i::Factory::NewExternalArray(kLargeElementCount, 9909 array_type, 9910 array_data)); 9911 v8::Handle<v8::Object> large_obj = v8::Object::New(); 9912 // Set the elements to be the external array. 9913 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data, 9914 array_type, 9915 kLargeElementCount); 9916 context->Global()->Set(v8_str("large_array"), large_obj); 9917 // Initialize contents of a few rows. 9918 for (int x = 0; x < 300; x++) { 9919 int row = 0; 9920 int offset = row * 300 * 4; 9921 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 9922 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 9923 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 9924 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 9925 row = 150; 9926 offset = row * 300 * 4; 9927 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 9928 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 9929 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 9930 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 9931 row = 298; 9932 offset = row * 300 * 4; 9933 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 9934 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 9935 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 9936 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 9937 } 9938 // The goal of the code below is to make "offset" large enough 9939 // that the computation of the index (which goes into eax) has 9940 // high bits set which will not be overwritten by a byte or short 9941 // load. 9942 result = CompileRun("var failed = false;" 9943 "var offset = 0;" 9944 "for (var i = 0; i < 300; i++) {" 9945 " if (large_array[4 * i] != 127 ||" 9946 " large_array[4 * i + 1] != 0 ||" 9947 " large_array[4 * i + 2] != 0 ||" 9948 " large_array[4 * i + 3] != 127) {" 9949 " failed = true;" 9950 " }" 9951 "}" 9952 "offset = 150 * 300 * 4;" 9953 "for (var i = 0; i < 300; i++) {" 9954 " if (large_array[offset + 4 * i] != 127 ||" 9955 " large_array[offset + 4 * i + 1] != 0 ||" 9956 " large_array[offset + 4 * i + 2] != 0 ||" 9957 " large_array[offset + 4 * i + 3] != 127) {" 9958 " failed = true;" 9959 " }" 9960 "}" 9961 "offset = 298 * 300 * 4;" 9962 "for (var i = 0; i < 300; i++) {" 9963 " if (large_array[offset + 4 * i] != 127 ||" 9964 " large_array[offset + 4 * i + 1] != 0 ||" 9965 " large_array[offset + 4 * i + 2] != 0 ||" 9966 " large_array[offset + 4 * i + 3] != 127) {" 9967 " failed = true;" 9968 " }" 9969 "}" 9970 "!failed;"); 9971 CHECK_EQ(true, result->BooleanValue()); 9972 free(large_array_data); 9973 } 9974 9975 free(array_data); 9976} 9977 9978 9979THREADED_TEST(ExternalByteArray) { 9980 ExternalArrayTestHelper<v8::internal::ExternalByteArray, int8_t>( 9981 v8::kExternalByteArray, 9982 -128, 9983 127); 9984} 9985 9986 9987THREADED_TEST(ExternalUnsignedByteArray) { 9988 ExternalArrayTestHelper<v8::internal::ExternalUnsignedByteArray, uint8_t>( 9989 v8::kExternalUnsignedByteArray, 9990 0, 9991 255); 9992} 9993 9994 9995THREADED_TEST(ExternalShortArray) { 9996 ExternalArrayTestHelper<v8::internal::ExternalShortArray, int16_t>( 9997 v8::kExternalShortArray, 9998 -32768, 9999 32767); 10000} 10001 10002 10003THREADED_TEST(ExternalUnsignedShortArray) { 10004 ExternalArrayTestHelper<v8::internal::ExternalUnsignedShortArray, uint16_t>( 10005 v8::kExternalUnsignedShortArray, 10006 0, 10007 65535); 10008} 10009 10010 10011THREADED_TEST(ExternalIntArray) { 10012 ExternalArrayTestHelper<v8::internal::ExternalIntArray, int32_t>( 10013 v8::kExternalIntArray, 10014 INT_MIN, // -2147483648 10015 INT_MAX); // 2147483647 10016} 10017 10018 10019THREADED_TEST(ExternalUnsignedIntArray) { 10020 ExternalArrayTestHelper<v8::internal::ExternalUnsignedIntArray, uint32_t>( 10021 v8::kExternalUnsignedIntArray, 10022 0, 10023 UINT_MAX); // 4294967295 10024} 10025 10026 10027THREADED_TEST(ExternalFloatArray) { 10028 ExternalArrayTestHelper<v8::internal::ExternalFloatArray, float>( 10029 v8::kExternalFloatArray, 10030 -500, 10031 500); 10032} 10033 10034 10035THREADED_TEST(ExternalArrays) { 10036 TestExternalByteArray(); 10037 TestExternalUnsignedByteArray(); 10038 TestExternalShortArray(); 10039 TestExternalUnsignedShortArray(); 10040 TestExternalIntArray(); 10041 TestExternalUnsignedIntArray(); 10042 TestExternalFloatArray(); 10043} 10044 10045 10046THREADED_TEST(ScriptContextDependence) { 10047 v8::HandleScope scope; 10048 LocalContext c1; 10049 const char *source = "foo"; 10050 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source)); 10051 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source)); 10052 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100)); 10053 CHECK_EQ(dep->Run()->Int32Value(), 100); 10054 CHECK_EQ(indep->Run()->Int32Value(), 100); 10055 LocalContext c2; 10056 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101)); 10057 CHECK_EQ(dep->Run()->Int32Value(), 100); 10058 CHECK_EQ(indep->Run()->Int32Value(), 101); 10059} 10060 10061 10062THREADED_TEST(StackTrace) { 10063 v8::HandleScope scope; 10064 LocalContext context; 10065 v8::TryCatch try_catch; 10066 const char *source = "function foo() { FAIL.FAIL; }; foo();"; 10067 v8::Handle<v8::String> src = v8::String::New(source); 10068 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test"); 10069 v8::Script::New(src, origin)->Run(); 10070 CHECK(try_catch.HasCaught()); 10071 v8::String::Utf8Value stack(try_catch.StackTrace()); 10072 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL); 10073} 10074 10075 10076// Checks that a StackFrame has certain expected values. 10077void checkStackFrame(const char* expected_script_name, 10078 const char* expected_func_name, int expected_line_number, 10079 int expected_column, bool is_eval, bool is_constructor, 10080 v8::Handle<v8::StackFrame> frame) { 10081 v8::HandleScope scope; 10082 v8::String::Utf8Value func_name(frame->GetFunctionName()); 10083 v8::String::Utf8Value script_name(frame->GetScriptName()); 10084 if (*script_name == NULL) { 10085 // The situation where there is no associated script, like for evals. 10086 CHECK(expected_script_name == NULL); 10087 } else { 10088 CHECK(strstr(*script_name, expected_script_name) != NULL); 10089 } 10090 CHECK(strstr(*func_name, expected_func_name) != NULL); 10091 CHECK_EQ(expected_line_number, frame->GetLineNumber()); 10092 CHECK_EQ(expected_column, frame->GetColumn()); 10093 CHECK_EQ(is_eval, frame->IsEval()); 10094 CHECK_EQ(is_constructor, frame->IsConstructor()); 10095} 10096 10097 10098v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) { 10099 v8::HandleScope scope; 10100 const char* origin = "capture-stack-trace-test"; 10101 const int kOverviewTest = 1; 10102 const int kDetailedTest = 2; 10103 10104 ASSERT(args.Length() == 1); 10105 10106 int testGroup = args[0]->Int32Value(); 10107 if (testGroup == kOverviewTest) { 10108 v8::Handle<v8::StackTrace> stackTrace = 10109 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview); 10110 CHECK_EQ(4, stackTrace->GetFrameCount()); 10111 checkStackFrame(origin, "bar", 2, 10, false, false, 10112 stackTrace->GetFrame(0)); 10113 checkStackFrame(origin, "foo", 6, 3, false, false, 10114 stackTrace->GetFrame(1)); 10115 checkStackFrame(NULL, "", 1, 1, false, false, 10116 stackTrace->GetFrame(2)); 10117 // The last frame is an anonymous function that has the initial call. 10118 checkStackFrame(origin, "", 8, 7, false, false, 10119 stackTrace->GetFrame(3)); 10120 10121 CHECK(stackTrace->AsArray()->IsArray()); 10122 } else if (testGroup == kDetailedTest) { 10123 v8::Handle<v8::StackTrace> stackTrace = 10124 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); 10125 CHECK_EQ(4, stackTrace->GetFrameCount()); 10126 checkStackFrame(origin, "bat", 4, 22, false, false, 10127 stackTrace->GetFrame(0)); 10128 checkStackFrame(origin, "baz", 8, 3, false, true, 10129 stackTrace->GetFrame(1)); 10130 checkStackFrame(NULL, "", 1, 1, true, false, 10131 stackTrace->GetFrame(2)); 10132 // The last frame is an anonymous function that has the initial call to foo. 10133 checkStackFrame(origin, "", 10, 1, false, false, 10134 stackTrace->GetFrame(3)); 10135 10136 CHECK(stackTrace->AsArray()->IsArray()); 10137 } 10138 return v8::Undefined(); 10139} 10140 10141 10142// Tests the C++ StackTrace API. 10143THREADED_TEST(CaptureStackTrace) { 10144 v8::HandleScope scope; 10145 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test"); 10146 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10147 templ->Set(v8_str("AnalyzeStackInNativeCode"), 10148 v8::FunctionTemplate::New(AnalyzeStackInNativeCode)); 10149 LocalContext context(0, templ); 10150 10151 // Test getting OVERVIEW information. Should ignore information that is not 10152 // script name, function name, line number, and column offset. 10153 const char *overview_source = 10154 "function bar() {\n" 10155 " var y; AnalyzeStackInNativeCode(1);\n" 10156 "}\n" 10157 "function foo() {\n" 10158 "\n" 10159 " bar();\n" 10160 "}\n" 10161 "var x;eval('new foo();');"; 10162 v8::Handle<v8::String> overview_src = v8::String::New(overview_source); 10163 v8::Handle<Value> overview_result = 10164 v8::Script::New(overview_src, origin)->Run(); 10165 ASSERT(!overview_result.IsEmpty()); 10166 ASSERT(overview_result->IsObject()); 10167 10168 // Test getting DETAILED information. 10169 const char *detailed_source = 10170 "function bat() {AnalyzeStackInNativeCode(2);\n" 10171 "}\n" 10172 "\n" 10173 "function baz() {\n" 10174 " bat();\n" 10175 "}\n" 10176 "eval('new baz();');"; 10177 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source); 10178 // Make the script using a non-zero line and column offset. 10179 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3); 10180 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5); 10181 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset); 10182 v8::Handle<v8::Script> detailed_script( 10183 v8::Script::New(detailed_src, &detailed_origin)); 10184 v8::Handle<Value> detailed_result = detailed_script->Run(); 10185 ASSERT(!detailed_result.IsEmpty()); 10186 ASSERT(detailed_result->IsObject()); 10187} 10188 10189 10190// Test that idle notification can be handled and eventually returns true. 10191THREADED_TEST(IdleNotification) { 10192 bool rv = false; 10193 for (int i = 0; i < 100; i++) { 10194 rv = v8::V8::IdleNotification(); 10195 if (rv) 10196 break; 10197 } 10198 CHECK(rv == true); 10199} 10200 10201 10202static uint32_t* stack_limit; 10203 10204static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) { 10205 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit()); 10206 return v8::Undefined(); 10207} 10208 10209 10210// Uses the address of a local variable to determine the stack top now. 10211// Given a size, returns an address that is that far from the current 10212// top of stack. 10213static uint32_t* ComputeStackLimit(uint32_t size) { 10214 uint32_t* answer = &size - (size / sizeof(size)); 10215 // If the size is very large and the stack is very near the bottom of 10216 // memory then the calculation above may wrap around and give an address 10217 // that is above the (downwards-growing) stack. In that case we return 10218 // a very low address. 10219 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size)); 10220 return answer; 10221} 10222 10223 10224TEST(SetResourceConstraints) { 10225 static const int K = 1024; 10226 uint32_t* set_limit = ComputeStackLimit(128 * K); 10227 10228 // Set stack limit. 10229 v8::ResourceConstraints constraints; 10230 constraints.set_stack_limit(set_limit); 10231 CHECK(v8::SetResourceConstraints(&constraints)); 10232 10233 // Execute a script. 10234 v8::HandleScope scope; 10235 LocalContext env; 10236 Local<v8::FunctionTemplate> fun_templ = 10237 v8::FunctionTemplate::New(GetStackLimitCallback); 10238 Local<Function> fun = fun_templ->GetFunction(); 10239 env->Global()->Set(v8_str("get_stack_limit"), fun); 10240 CompileRun("get_stack_limit();"); 10241 10242 CHECK(stack_limit == set_limit); 10243} 10244 10245 10246TEST(SetResourceConstraintsInThread) { 10247 uint32_t* set_limit; 10248 { 10249 v8::Locker locker; 10250 static const int K = 1024; 10251 set_limit = ComputeStackLimit(128 * K); 10252 10253 // Set stack limit. 10254 v8::ResourceConstraints constraints; 10255 constraints.set_stack_limit(set_limit); 10256 CHECK(v8::SetResourceConstraints(&constraints)); 10257 10258 // Execute a script. 10259 v8::HandleScope scope; 10260 LocalContext env; 10261 Local<v8::FunctionTemplate> fun_templ = 10262 v8::FunctionTemplate::New(GetStackLimitCallback); 10263 Local<Function> fun = fun_templ->GetFunction(); 10264 env->Global()->Set(v8_str("get_stack_limit"), fun); 10265 CompileRun("get_stack_limit();"); 10266 10267 CHECK(stack_limit == set_limit); 10268 } 10269 { 10270 v8::Locker locker; 10271 CHECK(stack_limit == set_limit); 10272 } 10273} 10274 10275 10276THREADED_TEST(GetHeapStatistics) { 10277 v8::HandleScope scope; 10278 LocalContext c1; 10279 v8::HeapStatistics heap_statistics; 10280 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0); 10281 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0); 10282 v8::V8::GetHeapStatistics(&heap_statistics); 10283 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0); 10284 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0); 10285} 10286 10287 10288static double DoubleFromBits(uint64_t value) { 10289 double target; 10290#ifdef BIG_ENDIAN_FLOATING_POINT 10291 const int kIntSize = 4; 10292 // Somebody swapped the lower and higher half of doubles. 10293 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); 10294 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); 10295#else 10296 memcpy(&target, &value, sizeof(target)); 10297#endif 10298 return target; 10299} 10300 10301 10302static uint64_t DoubleToBits(double value) { 10303 uint64_t target; 10304#ifdef BIG_ENDIAN_FLOATING_POINT 10305 const int kIntSize = 4; 10306 // Somebody swapped the lower and higher half of doubles. 10307 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); 10308 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); 10309#else 10310 memcpy(&target, &value, sizeof(target)); 10311#endif 10312 return target; 10313} 10314 10315 10316static double DoubleToDateTime(double input) { 10317 double date_limit = 864e13; 10318 if (IsNaN(input) || input < -date_limit || input > date_limit) { 10319 return i::OS::nan_value(); 10320 } 10321 return (input < 0) ? -(floor(-input)) : floor(input); 10322} 10323 10324// We don't have a consistent way to write 64-bit constants syntactically, so we 10325// split them into two 32-bit constants and combine them programmatically. 10326static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) { 10327 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits); 10328} 10329 10330 10331THREADED_TEST(QuietSignalingNaNs) { 10332 v8::HandleScope scope; 10333 LocalContext context; 10334 v8::TryCatch try_catch; 10335 10336 // Special double values. 10337 double snan = DoubleFromBits(0x7ff00000, 0x00000001); 10338 double qnan = DoubleFromBits(0x7ff80000, 0x00000000); 10339 double infinity = DoubleFromBits(0x7ff00000, 0x00000000); 10340 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu); 10341 double min_normal = DoubleFromBits(0x00100000, 0x00000000); 10342 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu); 10343 double min_denormal = DoubleFromBits(0x00000000, 0x00000001); 10344 10345 // Date values are capped at +/-100000000 days (times 864e5 ms per day) 10346 // on either side of the epoch. 10347 double date_limit = 864e13; 10348 10349 double test_values[] = { 10350 snan, 10351 qnan, 10352 infinity, 10353 max_normal, 10354 date_limit + 1, 10355 date_limit, 10356 min_normal, 10357 max_denormal, 10358 min_denormal, 10359 0, 10360 -0, 10361 -min_denormal, 10362 -max_denormal, 10363 -min_normal, 10364 -date_limit, 10365 -date_limit - 1, 10366 -max_normal, 10367 -infinity, 10368 -qnan, 10369 -snan 10370 }; 10371 int num_test_values = 20; 10372 10373 for (int i = 0; i < num_test_values; i++) { 10374 double test_value = test_values[i]; 10375 10376 // Check that Number::New preserves non-NaNs and quiets SNaNs. 10377 v8::Handle<v8::Value> number = v8::Number::New(test_value); 10378 double stored_number = number->NumberValue(); 10379 if (!IsNaN(test_value)) { 10380 CHECK_EQ(test_value, stored_number); 10381 } else { 10382 uint64_t stored_bits = DoubleToBits(stored_number); 10383 // Check if quiet nan (bits 51..62 all set). 10384 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 10385 } 10386 10387 // Check that Date::New preserves non-NaNs in the date range and 10388 // quiets SNaNs. 10389 v8::Handle<v8::Value> date = v8::Date::New(test_value); 10390 double expected_stored_date = DoubleToDateTime(test_value); 10391 double stored_date = date->NumberValue(); 10392 if (!IsNaN(expected_stored_date)) { 10393 CHECK_EQ(expected_stored_date, stored_date); 10394 } else { 10395 uint64_t stored_bits = DoubleToBits(stored_date); 10396 // Check if quiet nan (bits 51..62 all set). 10397 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 10398 } 10399 } 10400} 10401 10402 10403static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) { 10404 v8::HandleScope scope; 10405 v8::TryCatch tc; 10406 v8::Handle<v8::String> str = args[0]->ToString(); 10407 if (tc.HasCaught()) 10408 return tc.ReThrow(); 10409 return v8::Undefined(); 10410} 10411 10412 10413// Test that an exception can be propagated down through a spaghetti 10414// stack using ReThrow. 10415THREADED_TEST(SpaghettiStackReThrow) { 10416 v8::HandleScope scope; 10417 LocalContext context; 10418 context->Global()->Set( 10419 v8::String::New("s"), 10420 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction()); 10421 v8::TryCatch try_catch; 10422 CompileRun( 10423 "var i = 0;" 10424 "var o = {" 10425 " toString: function () {" 10426 " if (i == 10) {" 10427 " throw 'Hey!';" 10428 " } else {" 10429 " i++;" 10430 " return s(o);" 10431 " }" 10432 " }" 10433 "};" 10434 "s(o);"); 10435 CHECK(try_catch.HasCaught()); 10436 v8::String::Utf8Value value(try_catch.Exception()); 10437 CHECK_EQ(0, strcmp(*value, "Hey!")); 10438} 10439 10440 10441TEST(Regress528) { 10442 v8::V8::Initialize(); 10443 10444 v8::HandleScope scope; 10445 v8::Persistent<Context> context; 10446 v8::Persistent<Context> other_context; 10447 int gc_count; 10448 10449 // Create a context used to keep the code from aging in the compilation 10450 // cache. 10451 other_context = Context::New(); 10452 10453 // Context-dependent context data creates reference from the compilation 10454 // cache to the global object. 10455 const char* source_simple = "1"; 10456 context = Context::New(); 10457 { 10458 v8::HandleScope scope; 10459 10460 context->Enter(); 10461 Local<v8::String> obj = v8::String::New(""); 10462 context->SetData(obj); 10463 CompileRun(source_simple); 10464 context->Exit(); 10465 } 10466 context.Dispose(); 10467 for (gc_count = 1; gc_count < 10; gc_count++) { 10468 other_context->Enter(); 10469 CompileRun(source_simple); 10470 other_context->Exit(); 10471 v8::internal::Heap::CollectAllGarbage(false); 10472 if (GetGlobalObjectsCount() == 1) break; 10473 } 10474 CHECK_GE(2, gc_count); 10475 CHECK_EQ(1, GetGlobalObjectsCount()); 10476 10477 // Eval in a function creates reference from the compilation cache to the 10478 // global object. 10479 const char* source_eval = "function f(){eval('1')}; f()"; 10480 context = Context::New(); 10481 { 10482 v8::HandleScope scope; 10483 10484 context->Enter(); 10485 CompileRun(source_eval); 10486 context->Exit(); 10487 } 10488 context.Dispose(); 10489 for (gc_count = 1; gc_count < 10; gc_count++) { 10490 other_context->Enter(); 10491 CompileRun(source_eval); 10492 other_context->Exit(); 10493 v8::internal::Heap::CollectAllGarbage(false); 10494 if (GetGlobalObjectsCount() == 1) break; 10495 } 10496 CHECK_GE(2, gc_count); 10497 CHECK_EQ(1, GetGlobalObjectsCount()); 10498 10499 // Looking up the line number for an exception creates reference from the 10500 // compilation cache to the global object. 10501 const char* source_exception = "function f(){throw 1;} f()"; 10502 context = Context::New(); 10503 { 10504 v8::HandleScope scope; 10505 10506 context->Enter(); 10507 v8::TryCatch try_catch; 10508 CompileRun(source_exception); 10509 CHECK(try_catch.HasCaught()); 10510 v8::Handle<v8::Message> message = try_catch.Message(); 10511 CHECK(!message.IsEmpty()); 10512 CHECK_EQ(1, message->GetLineNumber()); 10513 context->Exit(); 10514 } 10515 context.Dispose(); 10516 for (gc_count = 1; gc_count < 10; gc_count++) { 10517 other_context->Enter(); 10518 CompileRun(source_exception); 10519 other_context->Exit(); 10520 v8::internal::Heap::CollectAllGarbage(false); 10521 if (GetGlobalObjectsCount() == 1) break; 10522 } 10523 CHECK_GE(2, gc_count); 10524 CHECK_EQ(1, GetGlobalObjectsCount()); 10525 10526 other_context.Dispose(); 10527} 10528 10529 10530THREADED_TEST(ScriptOrigin) { 10531 v8::HandleScope scope; 10532 LocalContext env; 10533 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 10534 v8::Handle<v8::String> script = v8::String::New( 10535 "function f() {}\n\nfunction g() {}"); 10536 v8::Script::Compile(script, &origin)->Run(); 10537 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 10538 env->Global()->Get(v8::String::New("f"))); 10539 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 10540 env->Global()->Get(v8::String::New("g"))); 10541 10542 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin(); 10543 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName())); 10544 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value()); 10545 10546 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin(); 10547 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName())); 10548 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value()); 10549} 10550 10551 10552THREADED_TEST(ScriptLineNumber) { 10553 v8::HandleScope scope; 10554 LocalContext env; 10555 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 10556 v8::Handle<v8::String> script = v8::String::New( 10557 "function f() {}\n\nfunction g() {}"); 10558 v8::Script::Compile(script, &origin)->Run(); 10559 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 10560 env->Global()->Get(v8::String::New("f"))); 10561 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 10562 env->Global()->Get(v8::String::New("g"))); 10563 CHECK_EQ(0, f->GetScriptLineNumber()); 10564 CHECK_EQ(2, g->GetScriptLineNumber()); 10565} 10566 10567 10568static v8::Handle<Value> GetterWhichReturns42(Local<String> name, 10569 const AccessorInfo& info) { 10570 return v8_num(42); 10571} 10572 10573 10574static void SetterWhichSetsYOnThisTo23(Local<String> name, 10575 Local<Value> value, 10576 const AccessorInfo& info) { 10577 info.This()->Set(v8_str("y"), v8_num(23)); 10578} 10579 10580 10581TEST(SetterOnConstructorPrototype) { 10582 v8::HandleScope scope; 10583 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10584 templ->SetAccessor(v8_str("x"), 10585 GetterWhichReturns42, 10586 SetterWhichSetsYOnThisTo23); 10587 LocalContext context; 10588 context->Global()->Set(v8_str("P"), templ->NewInstance()); 10589 CompileRun("function C1() {" 10590 " this.x = 23;" 10591 "};" 10592 "C1.prototype = P;" 10593 "function C2() {" 10594 " this.x = 23" 10595 "};" 10596 "C2.prototype = { };" 10597 "C2.prototype.__proto__ = P;"); 10598 10599 v8::Local<v8::Script> script; 10600 script = v8::Script::Compile(v8_str("new C1();")); 10601 for (int i = 0; i < 10; i++) { 10602 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 10603 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); 10604 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); 10605 } 10606 10607 script = v8::Script::Compile(v8_str("new C2();")); 10608 for (int i = 0; i < 10; i++) { 10609 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 10610 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value()); 10611 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value()); 10612 } 10613} 10614 10615 10616static v8::Handle<Value> NamedPropertyGetterWhichReturns42( 10617 Local<String> name, const AccessorInfo& info) { 10618 return v8_num(42); 10619} 10620 10621 10622static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23( 10623 Local<String> name, Local<Value> value, const AccessorInfo& info) { 10624 if (name->Equals(v8_str("x"))) { 10625 info.This()->Set(v8_str("y"), v8_num(23)); 10626 } 10627 return v8::Handle<Value>(); 10628} 10629 10630 10631THREADED_TEST(InterceptorOnConstructorPrototype) { 10632 v8::HandleScope scope; 10633 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10634 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42, 10635 NamedPropertySetterWhichSetsYOnThisTo23); 10636 LocalContext context; 10637 context->Global()->Set(v8_str("P"), templ->NewInstance()); 10638 CompileRun("function C1() {" 10639 " this.x = 23;" 10640 "};" 10641 "C1.prototype = P;" 10642 "function C2() {" 10643 " this.x = 23" 10644 "};" 10645 "C2.prototype = { };" 10646 "C2.prototype.__proto__ = P;"); 10647 10648 v8::Local<v8::Script> script; 10649 script = v8::Script::Compile(v8_str("new C1();")); 10650 for (int i = 0; i < 10; i++) { 10651 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 10652 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); 10653 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); 10654 } 10655 10656 script = v8::Script::Compile(v8_str("new C2();")); 10657 for (int i = 0; i < 10; i++) { 10658 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 10659 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value()); 10660 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value()); 10661 } 10662} 10663 10664 10665TEST(Bug618) { 10666 const char* source = "function C1() {" 10667 " this.x = 23;" 10668 "};" 10669 "C1.prototype = P;"; 10670 10671 v8::HandleScope scope; 10672 LocalContext context; 10673 v8::Local<v8::Script> script; 10674 10675 // Use a simple object as prototype. 10676 v8::Local<v8::Object> prototype = v8::Object::New(); 10677 prototype->Set(v8_str("y"), v8_num(42)); 10678 context->Global()->Set(v8_str("P"), prototype); 10679 10680 // This compile will add the code to the compilation cache. 10681 CompileRun(source); 10682 10683 script = v8::Script::Compile(v8_str("new C1();")); 10684 for (int i = 0; i < 10; i++) { 10685 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 10686 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); 10687 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); 10688 } 10689 10690 // Use an API object with accessors as prototype. 10691 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10692 templ->SetAccessor(v8_str("x"), 10693 GetterWhichReturns42, 10694 SetterWhichSetsYOnThisTo23); 10695 context->Global()->Set(v8_str("P"), templ->NewInstance()); 10696 10697 // This compile will get the code from the compilation cache. 10698 CompileRun(source); 10699 10700 script = v8::Script::Compile(v8_str("new C1();")); 10701 for (int i = 0; i < 10; i++) { 10702 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 10703 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); 10704 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); 10705 } 10706} 10707 10708int prologue_call_count = 0; 10709int epilogue_call_count = 0; 10710int prologue_call_count_second = 0; 10711int epilogue_call_count_second = 0; 10712 10713void PrologueCallback(v8::GCType, v8::GCCallbackFlags) { 10714 ++prologue_call_count; 10715} 10716 10717void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) { 10718 ++epilogue_call_count; 10719} 10720 10721void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { 10722 ++prologue_call_count_second; 10723} 10724 10725void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { 10726 ++epilogue_call_count_second; 10727} 10728 10729TEST(GCCallbacks) { 10730 LocalContext context; 10731 10732 v8::V8::AddGCPrologueCallback(PrologueCallback); 10733 v8::V8::AddGCEpilogueCallback(EpilogueCallback); 10734 CHECK_EQ(0, prologue_call_count); 10735 CHECK_EQ(0, epilogue_call_count); 10736 i::Heap::CollectAllGarbage(false); 10737 CHECK_EQ(1, prologue_call_count); 10738 CHECK_EQ(1, epilogue_call_count); 10739 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond); 10740 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond); 10741 i::Heap::CollectAllGarbage(false); 10742 CHECK_EQ(2, prologue_call_count); 10743 CHECK_EQ(2, epilogue_call_count); 10744 CHECK_EQ(1, prologue_call_count_second); 10745 CHECK_EQ(1, epilogue_call_count_second); 10746 v8::V8::RemoveGCPrologueCallback(PrologueCallback); 10747 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback); 10748 i::Heap::CollectAllGarbage(false); 10749 CHECK_EQ(2, prologue_call_count); 10750 CHECK_EQ(2, epilogue_call_count); 10751 CHECK_EQ(2, prologue_call_count_second); 10752 CHECK_EQ(2, epilogue_call_count_second); 10753 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond); 10754 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond); 10755 i::Heap::CollectAllGarbage(false); 10756 CHECK_EQ(2, prologue_call_count); 10757 CHECK_EQ(2, epilogue_call_count); 10758 CHECK_EQ(2, prologue_call_count_second); 10759 CHECK_EQ(2, epilogue_call_count_second); 10760} 10761 10762 10763THREADED_TEST(AddToJSFunctionResultCache) { 10764 i::FLAG_allow_natives_syntax = true; 10765 v8::HandleScope scope; 10766 10767 LocalContext context; 10768 10769 const char* code = 10770 "(function() {" 10771 " var key0 = 'a';" 10772 " var key1 = 'b';" 10773 " var r0 = %_GetFromCache(0, key0);" 10774 " var r1 = %_GetFromCache(0, key1);" 10775 " var r0_ = %_GetFromCache(0, key0);" 10776 " if (r0 !== r0_)" 10777 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;" 10778 " var r1_ = %_GetFromCache(0, key1);" 10779 " if (r1 !== r1_)" 10780 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;" 10781 " return 'PASSED';" 10782 "})()"; 10783 v8::internal::Heap::ClearJSFunctionResultCaches(); 10784 ExpectString(code, "PASSED"); 10785} 10786 10787 10788static const int k0CacheSize = 16; 10789 10790THREADED_TEST(FillJSFunctionResultCache) { 10791 i::FLAG_allow_natives_syntax = true; 10792 v8::HandleScope scope; 10793 10794 LocalContext context; 10795 10796 const char* code = 10797 "(function() {" 10798 " var k = 'a';" 10799 " var r = %_GetFromCache(0, k);" 10800 " for (var i = 0; i < 16; i++) {" 10801 " %_GetFromCache(0, 'a' + i);" 10802 " };" 10803 " if (r === %_GetFromCache(0, k))" 10804 " return 'FAILED: k0CacheSize is too small';" 10805 " return 'PASSED';" 10806 "})()"; 10807 v8::internal::Heap::ClearJSFunctionResultCaches(); 10808 ExpectString(code, "PASSED"); 10809} 10810 10811 10812THREADED_TEST(RoundRobinGetFromCache) { 10813 i::FLAG_allow_natives_syntax = true; 10814 v8::HandleScope scope; 10815 10816 LocalContext context; 10817 10818 const char* code = 10819 "(function() {" 10820 " var keys = [];" 10821 " for (var i = 0; i < 16; i++) keys.push(i);" 10822 " var values = [];" 10823 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" 10824 " for (var i = 0; i < 16; i++) {" 10825 " var v = %_GetFromCache(0, keys[i]);" 10826 " if (v !== values[i])" 10827 " return 'Wrong value for ' + " 10828 " keys[i] + ': ' + v + ' vs. ' + values[i];" 10829 " };" 10830 " return 'PASSED';" 10831 "})()"; 10832 v8::internal::Heap::ClearJSFunctionResultCaches(); 10833 ExpectString(code, "PASSED"); 10834} 10835 10836 10837THREADED_TEST(ReverseGetFromCache) { 10838 i::FLAG_allow_natives_syntax = true; 10839 v8::HandleScope scope; 10840 10841 LocalContext context; 10842 10843 const char* code = 10844 "(function() {" 10845 " var keys = [];" 10846 " for (var i = 0; i < 16; i++) keys.push(i);" 10847 " var values = [];" 10848 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" 10849 " for (var i = 15; i >= 16; i--) {" 10850 " var v = %_GetFromCache(0, keys[i]);" 10851 " if (v !== values[i])" 10852 " return 'Wrong value for ' + " 10853 " keys[i] + ': ' + v + ' vs. ' + values[i];" 10854 " };" 10855 " return 'PASSED';" 10856 "})()"; 10857 v8::internal::Heap::ClearJSFunctionResultCaches(); 10858 ExpectString(code, "PASSED"); 10859} 10860 10861 10862THREADED_TEST(TestEviction) { 10863 i::FLAG_allow_natives_syntax = true; 10864 v8::HandleScope scope; 10865 10866 LocalContext context; 10867 10868 const char* code = 10869 "(function() {" 10870 " for (var i = 0; i < 2*16; i++) {" 10871 " %_GetFromCache(0, 'a' + i);" 10872 " };" 10873 " return 'PASSED';" 10874 "})()"; 10875 v8::internal::Heap::ClearJSFunctionResultCaches(); 10876 ExpectString(code, "PASSED"); 10877} 10878