test-api.cc revision 9dcf7e2f83591d471e88bf7d230651900b8e424b
1// Copyright 2007-2009 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28#include <limits.h> 29 30#include "v8.h" 31 32#include "api.h" 33#include "compilation-cache.h" 34#include "execution.h" 35#include "snapshot.h" 36#include "platform.h" 37#include "top.h" 38#include "utils.h" 39#include "cctest.h" 40 41static const bool kLogThreading = true; 42 43static bool IsNaN(double x) { 44#ifdef WIN32 45 return _isnan(x); 46#else 47 return isnan(x); 48#endif 49} 50 51using ::v8::ObjectTemplate; 52using ::v8::Value; 53using ::v8::Context; 54using ::v8::Local; 55using ::v8::String; 56using ::v8::Script; 57using ::v8::Function; 58using ::v8::AccessorInfo; 59using ::v8::Extension; 60 61namespace i = ::v8::internal; 62 63 64static void ExpectString(const char* code, const char* expected) { 65 Local<Value> result = CompileRun(code); 66 CHECK(result->IsString()); 67 String::AsciiValue ascii(result); 68 CHECK_EQ(expected, *ascii); 69} 70 71 72static void ExpectBoolean(const char* code, bool expected) { 73 Local<Value> result = CompileRun(code); 74 CHECK(result->IsBoolean()); 75 CHECK_EQ(expected, result->BooleanValue()); 76} 77 78 79static void ExpectTrue(const char* code) { 80 ExpectBoolean(code, true); 81} 82 83 84static void ExpectObject(const char* code, Local<Value> expected) { 85 Local<Value> result = CompileRun(code); 86 CHECK(result->Equals(expected)); 87} 88 89 90static int signature_callback_count; 91static v8::Handle<Value> IncrementingSignatureCallback( 92 const v8::Arguments& args) { 93 ApiTestFuzzer::Fuzz(); 94 signature_callback_count++; 95 v8::Handle<v8::Array> result = v8::Array::New(args.Length()); 96 for (int i = 0; i < args.Length(); i++) 97 result->Set(v8::Integer::New(i), args[i]); 98 return result; 99} 100 101 102static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) { 103 ApiTestFuzzer::Fuzz(); 104 v8::Handle<v8::Array> result = v8::Array::New(args.Length()); 105 for (int i = 0; i < args.Length(); i++) { 106 result->Set(v8::Integer::New(i), args[i]); 107 } 108 return result; 109} 110 111 112THREADED_TEST(Handles) { 113 v8::HandleScope scope; 114 Local<Context> local_env; 115 { 116 LocalContext env; 117 local_env = env.local(); 118 } 119 120 // Local context should still be live. 121 CHECK(!local_env.IsEmpty()); 122 local_env->Enter(); 123 124 v8::Handle<v8::Primitive> undef = v8::Undefined(); 125 CHECK(!undef.IsEmpty()); 126 CHECK(undef->IsUndefined()); 127 128 const char* c_source = "1 + 2 + 3"; 129 Local<String> source = String::New(c_source); 130 Local<Script> script = Script::Compile(source); 131 CHECK_EQ(6, script->Run()->Int32Value()); 132 133 local_env->Exit(); 134} 135 136 137THREADED_TEST(ReceiverSignature) { 138 v8::HandleScope scope; 139 LocalContext env; 140 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); 141 v8::Handle<v8::Signature> sig = v8::Signature::New(fun); 142 fun->PrototypeTemplate()->Set( 143 v8_str("m"), 144 v8::FunctionTemplate::New(IncrementingSignatureCallback, 145 v8::Handle<Value>(), 146 sig)); 147 env->Global()->Set(v8_str("Fun"), fun->GetFunction()); 148 signature_callback_count = 0; 149 CompileRun( 150 "var o = new Fun();" 151 "o.m();"); 152 CHECK_EQ(1, signature_callback_count); 153 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New(); 154 sub_fun->Inherit(fun); 155 env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction()); 156 CompileRun( 157 "var o = new SubFun();" 158 "o.m();"); 159 CHECK_EQ(2, signature_callback_count); 160 161 v8::TryCatch try_catch; 162 CompileRun( 163 "var o = { };" 164 "o.m = Fun.prototype.m;" 165 "o.m();"); 166 CHECK_EQ(2, signature_callback_count); 167 CHECK(try_catch.HasCaught()); 168 try_catch.Reset(); 169 v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New(); 170 sub_fun->Inherit(fun); 171 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction()); 172 CompileRun( 173 "var o = new UnrelFun();" 174 "o.m = Fun.prototype.m;" 175 "o.m();"); 176 CHECK_EQ(2, signature_callback_count); 177 CHECK(try_catch.HasCaught()); 178} 179 180 181 182 183THREADED_TEST(ArgumentSignature) { 184 v8::HandleScope scope; 185 LocalContext env; 186 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New(); 187 cons->SetClassName(v8_str("Cons")); 188 v8::Handle<v8::Signature> sig = 189 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons); 190 v8::Handle<v8::FunctionTemplate> fun = 191 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig); 192 env->Global()->Set(v8_str("Cons"), cons->GetFunction()); 193 env->Global()->Set(v8_str("Fun1"), fun->GetFunction()); 194 195 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';"); 196 CHECK(value1->IsTrue()); 197 198 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';"); 199 CHECK(value2->IsTrue()); 200 201 v8::Handle<Value> value3 = CompileRun("Fun1() == '';"); 202 CHECK(value3->IsTrue()); 203 204 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New(); 205 cons1->SetClassName(v8_str("Cons1")); 206 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New(); 207 cons2->SetClassName(v8_str("Cons2")); 208 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New(); 209 cons3->SetClassName(v8_str("Cons3")); 210 211 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 }; 212 v8::Handle<v8::Signature> wsig = 213 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args); 214 v8::Handle<v8::FunctionTemplate> fun2 = 215 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig); 216 217 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction()); 218 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction()); 219 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction()); 220 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction()); 221 v8::Handle<Value> value4 = CompileRun( 222 "Fun2(new Cons1(), new Cons2(), new Cons3()) ==" 223 "'[object Cons1],[object Cons2],[object Cons3]'"); 224 CHECK(value4->IsTrue()); 225 226 v8::Handle<Value> value5 = CompileRun( 227 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'"); 228 CHECK(value5->IsTrue()); 229 230 v8::Handle<Value> value6 = CompileRun( 231 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'"); 232 CHECK(value6->IsTrue()); 233 234 v8::Handle<Value> value7 = CompileRun( 235 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == " 236 "'[object Cons1],[object Cons2],[object Cons3],d';"); 237 CHECK(value7->IsTrue()); 238 239 v8::Handle<Value> value8 = CompileRun( 240 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'"); 241 CHECK(value8->IsTrue()); 242} 243 244 245THREADED_TEST(HulIgennem) { 246 v8::HandleScope scope; 247 LocalContext env; 248 v8::Handle<v8::Primitive> undef = v8::Undefined(); 249 Local<String> undef_str = undef->ToString(); 250 char* value = i::NewArray<char>(undef_str->Length() + 1); 251 undef_str->WriteAscii(value); 252 CHECK_EQ(0, strcmp(value, "undefined")); 253 i::DeleteArray(value); 254} 255 256 257THREADED_TEST(Access) { 258 v8::HandleScope scope; 259 LocalContext env; 260 Local<v8::Object> obj = v8::Object::New(); 261 Local<Value> foo_before = obj->Get(v8_str("foo")); 262 CHECK(foo_before->IsUndefined()); 263 Local<String> bar_str = v8_str("bar"); 264 obj->Set(v8_str("foo"), bar_str); 265 Local<Value> foo_after = obj->Get(v8_str("foo")); 266 CHECK(!foo_after->IsUndefined()); 267 CHECK(foo_after->IsString()); 268 CHECK_EQ(bar_str, foo_after); 269} 270 271 272THREADED_TEST(AccessElement) { 273 v8::HandleScope scope; 274 LocalContext env; 275 Local<v8::Object> obj = v8::Object::New(); 276 Local<Value> before = obj->Get(1); 277 CHECK(before->IsUndefined()); 278 Local<String> bar_str = v8_str("bar"); 279 obj->Set(1, bar_str); 280 Local<Value> after = obj->Get(1); 281 CHECK(!after->IsUndefined()); 282 CHECK(after->IsString()); 283 CHECK_EQ(bar_str, after); 284 285 Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>(); 286 CHECK_EQ(v8_str("a"), value->Get(0)); 287 CHECK_EQ(v8_str("b"), value->Get(1)); 288} 289 290 291THREADED_TEST(Script) { 292 v8::HandleScope scope; 293 LocalContext env; 294 const char* c_source = "1 + 2 + 3"; 295 Local<String> source = String::New(c_source); 296 Local<Script> script = Script::Compile(source); 297 CHECK_EQ(6, script->Run()->Int32Value()); 298} 299 300 301static uint16_t* AsciiToTwoByteString(const char* source) { 302 int array_length = i::StrLength(source) + 1; 303 uint16_t* converted = i::NewArray<uint16_t>(array_length); 304 for (int i = 0; i < array_length; i++) converted[i] = source[i]; 305 return converted; 306} 307 308 309class TestResource: public String::ExternalStringResource { 310 public: 311 static int dispose_count; 312 313 explicit TestResource(uint16_t* data) 314 : data_(data), length_(0) { 315 while (data[length_]) ++length_; 316 } 317 318 ~TestResource() { 319 i::DeleteArray(data_); 320 ++dispose_count; 321 } 322 323 const uint16_t* data() const { 324 return data_; 325 } 326 327 size_t length() const { 328 return length_; 329 } 330 private: 331 uint16_t* data_; 332 size_t length_; 333}; 334 335 336int TestResource::dispose_count = 0; 337 338 339class TestAsciiResource: public String::ExternalAsciiStringResource { 340 public: 341 static int dispose_count; 342 343 explicit TestAsciiResource(const char* data) 344 : data_(data), 345 length_(strlen(data)) { } 346 347 ~TestAsciiResource() { 348 i::DeleteArray(data_); 349 ++dispose_count; 350 } 351 352 const char* data() const { 353 return data_; 354 } 355 356 size_t length() const { 357 return length_; 358 } 359 private: 360 const char* data_; 361 size_t length_; 362}; 363 364 365int TestAsciiResource::dispose_count = 0; 366 367 368THREADED_TEST(ScriptUsingStringResource) { 369 TestResource::dispose_count = 0; 370 const char* c_source = "1 + 2 * 3"; 371 uint16_t* two_byte_source = AsciiToTwoByteString(c_source); 372 { 373 v8::HandleScope scope; 374 LocalContext env; 375 TestResource* resource = new TestResource(two_byte_source); 376 Local<String> source = String::NewExternal(resource); 377 Local<Script> script = Script::Compile(source); 378 Local<Value> value = script->Run(); 379 CHECK(value->IsNumber()); 380 CHECK_EQ(7, value->Int32Value()); 381 CHECK(source->IsExternal()); 382 CHECK_EQ(resource, 383 static_cast<TestResource*>(source->GetExternalStringResource())); 384 v8::internal::Heap::CollectAllGarbage(false); 385 CHECK_EQ(0, TestResource::dispose_count); 386 } 387 v8::internal::CompilationCache::Clear(); 388 v8::internal::Heap::CollectAllGarbage(false); 389 CHECK_EQ(1, TestResource::dispose_count); 390} 391 392 393THREADED_TEST(ScriptUsingAsciiStringResource) { 394 TestAsciiResource::dispose_count = 0; 395 const char* c_source = "1 + 2 * 3"; 396 { 397 v8::HandleScope scope; 398 LocalContext env; 399 Local<String> source = 400 String::NewExternal(new TestAsciiResource(i::StrDup(c_source))); 401 Local<Script> script = Script::Compile(source); 402 Local<Value> value = script->Run(); 403 CHECK(value->IsNumber()); 404 CHECK_EQ(7, value->Int32Value()); 405 v8::internal::Heap::CollectAllGarbage(false); 406 CHECK_EQ(0, TestAsciiResource::dispose_count); 407 } 408 v8::internal::CompilationCache::Clear(); 409 v8::internal::Heap::CollectAllGarbage(false); 410 CHECK_EQ(1, TestAsciiResource::dispose_count); 411} 412 413 414THREADED_TEST(ScriptMakingExternalString) { 415 TestResource::dispose_count = 0; 416 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3"); 417 { 418 v8::HandleScope scope; 419 LocalContext env; 420 Local<String> source = String::New(two_byte_source); 421 // Trigger GCs so that the newly allocated string moves to old gen. 422 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 423 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 424 bool success = source->MakeExternal(new TestResource(two_byte_source)); 425 CHECK(success); 426 Local<Script> script = Script::Compile(source); 427 Local<Value> value = script->Run(); 428 CHECK(value->IsNumber()); 429 CHECK_EQ(7, value->Int32Value()); 430 v8::internal::Heap::CollectAllGarbage(false); 431 CHECK_EQ(0, TestResource::dispose_count); 432 } 433 v8::internal::CompilationCache::Clear(); 434 v8::internal::Heap::CollectAllGarbage(false); 435 CHECK_EQ(1, TestResource::dispose_count); 436} 437 438 439THREADED_TEST(ScriptMakingExternalAsciiString) { 440 TestAsciiResource::dispose_count = 0; 441 const char* c_source = "1 + 2 * 3"; 442 { 443 v8::HandleScope scope; 444 LocalContext env; 445 Local<String> source = v8_str(c_source); 446 // Trigger GCs so that the newly allocated string moves to old gen. 447 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 448 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 449 bool success = source->MakeExternal( 450 new TestAsciiResource(i::StrDup(c_source))); 451 CHECK(success); 452 Local<Script> script = Script::Compile(source); 453 Local<Value> value = script->Run(); 454 CHECK(value->IsNumber()); 455 CHECK_EQ(7, value->Int32Value()); 456 v8::internal::Heap::CollectAllGarbage(false); 457 CHECK_EQ(0, TestAsciiResource::dispose_count); 458 } 459 v8::internal::CompilationCache::Clear(); 460 v8::internal::Heap::CollectAllGarbage(false); 461 CHECK_EQ(1, TestAsciiResource::dispose_count); 462} 463 464 465TEST(MakingExternalStringConditions) { 466 v8::HandleScope scope; 467 LocalContext env; 468 469 // Free some space in the new space so that we can check freshness. 470 i::Heap::CollectGarbage(0, i::NEW_SPACE); 471 i::Heap::CollectGarbage(0, i::NEW_SPACE); 472 473 Local<String> small_string = String::New(AsciiToTwoByteString("small")); 474 // We should refuse to externalize newly created small string. 475 CHECK(!small_string->CanMakeExternal()); 476 // Trigger GCs so that the newly allocated string moves to old gen. 477 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 478 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 479 // Old space strings should be accepted. 480 CHECK(small_string->CanMakeExternal()); 481 482 small_string = String::New(AsciiToTwoByteString("small 2")); 483 // We should refuse externalizing newly created small string. 484 CHECK(!small_string->CanMakeExternal()); 485 for (int i = 0; i < 100; i++) { 486 String::Value value(small_string); 487 } 488 // Frequently used strings should be accepted. 489 CHECK(small_string->CanMakeExternal()); 490 491 const int buf_size = 10 * 1024; 492 char* buf = i::NewArray<char>(buf_size); 493 memset(buf, 'a', buf_size); 494 buf[buf_size - 1] = '\0'; 495 Local<String> large_string = String::New(AsciiToTwoByteString(buf)); 496 i::DeleteArray(buf); 497 // Large strings should be immediately accepted. 498 CHECK(large_string->CanMakeExternal()); 499} 500 501 502TEST(MakingExternalAsciiStringConditions) { 503 v8::HandleScope scope; 504 LocalContext env; 505 506 // Free some space in the new space so that we can check freshness. 507 i::Heap::CollectGarbage(0, i::NEW_SPACE); 508 i::Heap::CollectGarbage(0, i::NEW_SPACE); 509 510 Local<String> small_string = String::New("small"); 511 // We should refuse to externalize newly created small string. 512 CHECK(!small_string->CanMakeExternal()); 513 // Trigger GCs so that the newly allocated string moves to old gen. 514 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 515 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 516 // Old space strings should be accepted. 517 CHECK(small_string->CanMakeExternal()); 518 519 small_string = String::New("small 2"); 520 // We should refuse externalizing newly created small string. 521 CHECK(!small_string->CanMakeExternal()); 522 for (int i = 0; i < 100; i++) { 523 String::Value value(small_string); 524 } 525 // Frequently used strings should be accepted. 526 CHECK(small_string->CanMakeExternal()); 527 528 const int buf_size = 10 * 1024; 529 char* buf = i::NewArray<char>(buf_size); 530 memset(buf, 'a', buf_size); 531 buf[buf_size - 1] = '\0'; 532 Local<String> large_string = String::New(buf); 533 i::DeleteArray(buf); 534 // Large strings should be immediately accepted. 535 CHECK(large_string->CanMakeExternal()); 536} 537 538 539THREADED_TEST(UsingExternalString) { 540 { 541 v8::HandleScope scope; 542 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 543 Local<String> string = 544 String::NewExternal(new TestResource(two_byte_string)); 545 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 546 // Trigger GCs so that the newly allocated string moves to old gen. 547 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 548 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 549 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring); 550 CHECK(isymbol->IsSymbol()); 551 } 552 i::Heap::CollectAllGarbage(false); 553 i::Heap::CollectAllGarbage(false); 554} 555 556 557THREADED_TEST(UsingExternalAsciiString) { 558 { 559 v8::HandleScope scope; 560 const char* one_byte_string = "test string"; 561 Local<String> string = String::NewExternal( 562 new TestAsciiResource(i::StrDup(one_byte_string))); 563 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 564 // Trigger GCs so that the newly allocated string moves to old gen. 565 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 566 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 567 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring); 568 CHECK(isymbol->IsSymbol()); 569 } 570 i::Heap::CollectAllGarbage(false); 571 i::Heap::CollectAllGarbage(false); 572} 573 574 575THREADED_TEST(ScavengeExternalString) { 576 TestResource::dispose_count = 0; 577 bool in_new_space = false; 578 { 579 v8::HandleScope scope; 580 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 581 Local<String> string = 582 String::NewExternal(new TestResource(two_byte_string)); 583 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 584 i::Heap::CollectGarbage(0, i::NEW_SPACE); 585 in_new_space = i::Heap::InNewSpace(*istring); 586 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring)); 587 CHECK_EQ(0, TestResource::dispose_count); 588 } 589 i::Heap::CollectGarbage(0, in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); 590 CHECK_EQ(1, TestResource::dispose_count); 591} 592 593 594THREADED_TEST(ScavengeExternalAsciiString) { 595 TestAsciiResource::dispose_count = 0; 596 bool in_new_space = false; 597 { 598 v8::HandleScope scope; 599 const char* one_byte_string = "test string"; 600 Local<String> string = String::NewExternal( 601 new TestAsciiResource(i::StrDup(one_byte_string))); 602 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 603 i::Heap::CollectGarbage(0, i::NEW_SPACE); 604 in_new_space = i::Heap::InNewSpace(*istring); 605 CHECK(in_new_space || i::Heap::old_data_space()->Contains(*istring)); 606 CHECK_EQ(0, TestAsciiResource::dispose_count); 607 } 608 i::Heap::CollectGarbage(0, in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); 609 CHECK_EQ(1, TestAsciiResource::dispose_count); 610} 611 612 613class TestAsciiResourceWithDisposeControl: public TestAsciiResource { 614 public: 615 static int dispose_calls; 616 617 TestAsciiResourceWithDisposeControl(const char* data, bool dispose) 618 : TestAsciiResource(data), 619 dispose_(dispose) { } 620 621 void Dispose() { 622 ++dispose_calls; 623 if (dispose_) delete this; 624 } 625 private: 626 bool dispose_; 627}; 628 629 630int TestAsciiResourceWithDisposeControl::dispose_calls = 0; 631 632 633TEST(ExternalStringWithDisposeHandling) { 634 const char* c_source = "1 + 2 * 3"; 635 636 // Use a stack allocated external string resource allocated object. 637 TestAsciiResource::dispose_count = 0; 638 TestAsciiResourceWithDisposeControl::dispose_calls = 0; 639 TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false); 640 { 641 v8::HandleScope scope; 642 LocalContext env; 643 Local<String> source = String::NewExternal(&res_stack); 644 Local<Script> script = Script::Compile(source); 645 Local<Value> value = script->Run(); 646 CHECK(value->IsNumber()); 647 CHECK_EQ(7, value->Int32Value()); 648 v8::internal::Heap::CollectAllGarbage(false); 649 CHECK_EQ(0, TestAsciiResource::dispose_count); 650 } 651 v8::internal::CompilationCache::Clear(); 652 v8::internal::Heap::CollectAllGarbage(false); 653 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); 654 CHECK_EQ(0, TestAsciiResource::dispose_count); 655 656 // Use a heap allocated external string resource allocated object. 657 TestAsciiResource::dispose_count = 0; 658 TestAsciiResourceWithDisposeControl::dispose_calls = 0; 659 TestAsciiResource* res_heap = 660 new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true); 661 { 662 v8::HandleScope scope; 663 LocalContext env; 664 Local<String> source = String::NewExternal(res_heap); 665 Local<Script> script = Script::Compile(source); 666 Local<Value> value = script->Run(); 667 CHECK(value->IsNumber()); 668 CHECK_EQ(7, value->Int32Value()); 669 v8::internal::Heap::CollectAllGarbage(false); 670 CHECK_EQ(0, TestAsciiResource::dispose_count); 671 } 672 v8::internal::CompilationCache::Clear(); 673 v8::internal::Heap::CollectAllGarbage(false); 674 CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls); 675 CHECK_EQ(1, TestAsciiResource::dispose_count); 676} 677 678 679THREADED_TEST(StringConcat) { 680 { 681 v8::HandleScope scope; 682 LocalContext env; 683 const char* one_byte_string_1 = "function a_times_t"; 684 const char* two_byte_string_1 = "wo_plus_b(a, b) {return "; 685 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + "; 686 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + "; 687 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 688 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 689 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);"; 690 Local<String> left = v8_str(one_byte_string_1); 691 Local<String> right = String::New(AsciiToTwoByteString(two_byte_string_1)); 692 Local<String> source = String::Concat(left, right); 693 right = String::NewExternal( 694 new TestAsciiResource(i::StrDup(one_byte_extern_1))); 695 source = String::Concat(source, right); 696 right = String::NewExternal( 697 new TestResource(AsciiToTwoByteString(two_byte_extern_1))); 698 source = String::Concat(source, right); 699 right = v8_str(one_byte_string_2); 700 source = String::Concat(source, right); 701 right = String::New(AsciiToTwoByteString(two_byte_string_2)); 702 source = String::Concat(source, right); 703 right = String::NewExternal( 704 new TestResource(AsciiToTwoByteString(two_byte_extern_2))); 705 source = String::Concat(source, right); 706 Local<Script> script = Script::Compile(source); 707 Local<Value> value = script->Run(); 708 CHECK(value->IsNumber()); 709 CHECK_EQ(68, value->Int32Value()); 710 } 711 v8::internal::CompilationCache::Clear(); 712 i::Heap::CollectAllGarbage(false); 713 i::Heap::CollectAllGarbage(false); 714} 715 716 717THREADED_TEST(GlobalProperties) { 718 v8::HandleScope scope; 719 LocalContext env; 720 v8::Handle<v8::Object> global = env->Global(); 721 global->Set(v8_str("pi"), v8_num(3.1415926)); 722 Local<Value> pi = global->Get(v8_str("pi")); 723 CHECK_EQ(3.1415926, pi->NumberValue()); 724} 725 726 727static v8::Handle<Value> handle_call(const v8::Arguments& args) { 728 ApiTestFuzzer::Fuzz(); 729 return v8_num(102); 730} 731 732 733static v8::Handle<Value> construct_call(const v8::Arguments& args) { 734 ApiTestFuzzer::Fuzz(); 735 args.This()->Set(v8_str("x"), v8_num(1)); 736 args.This()->Set(v8_str("y"), v8_num(2)); 737 return args.This(); 738} 739 740THREADED_TEST(FunctionTemplate) { 741 v8::HandleScope scope; 742 LocalContext env; 743 { 744 Local<v8::FunctionTemplate> fun_templ = 745 v8::FunctionTemplate::New(handle_call); 746 Local<Function> fun = fun_templ->GetFunction(); 747 env->Global()->Set(v8_str("obj"), fun); 748 Local<Script> script = v8_compile("obj()"); 749 CHECK_EQ(102, script->Run()->Int32Value()); 750 } 751 // Use SetCallHandler to initialize a function template, should work like the 752 // previous one. 753 { 754 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 755 fun_templ->SetCallHandler(handle_call); 756 Local<Function> fun = fun_templ->GetFunction(); 757 env->Global()->Set(v8_str("obj"), fun); 758 Local<Script> script = v8_compile("obj()"); 759 CHECK_EQ(102, script->Run()->Int32Value()); 760 } 761 // Test constructor calls. 762 { 763 Local<v8::FunctionTemplate> fun_templ = 764 v8::FunctionTemplate::New(construct_call); 765 fun_templ->SetClassName(v8_str("funky")); 766 Local<Function> fun = fun_templ->GetFunction(); 767 env->Global()->Set(v8_str("obj"), fun); 768 Local<Script> script = v8_compile("var s = new obj(); s.x"); 769 CHECK_EQ(1, script->Run()->Int32Value()); 770 771 Local<Value> result = v8_compile("(new obj()).toString()")->Run(); 772 CHECK_EQ(v8_str("[object funky]"), result); 773 } 774} 775 776 777THREADED_TEST(FindInstanceInPrototypeChain) { 778 v8::HandleScope scope; 779 LocalContext env; 780 781 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New(); 782 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New(); 783 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New(); 784 derived->Inherit(base); 785 786 Local<v8::Function> base_function = base->GetFunction(); 787 Local<v8::Function> derived_function = derived->GetFunction(); 788 Local<v8::Function> other_function = other->GetFunction(); 789 790 Local<v8::Object> base_instance = base_function->NewInstance(); 791 Local<v8::Object> derived_instance = derived_function->NewInstance(); 792 Local<v8::Object> derived_instance2 = derived_function->NewInstance(); 793 Local<v8::Object> other_instance = other_function->NewInstance(); 794 derived_instance2->Set(v8_str("__proto__"), derived_instance); 795 other_instance->Set(v8_str("__proto__"), derived_instance2); 796 797 // base_instance is only an instance of base. 798 CHECK_EQ(base_instance, 799 base_instance->FindInstanceInPrototypeChain(base)); 800 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty()); 801 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 802 803 // derived_instance is an instance of base and derived. 804 CHECK_EQ(derived_instance, 805 derived_instance->FindInstanceInPrototypeChain(base)); 806 CHECK_EQ(derived_instance, 807 derived_instance->FindInstanceInPrototypeChain(derived)); 808 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 809 810 // other_instance is an instance of other and its immediate 811 // prototype derived_instance2 is an instance of base and derived. 812 // Note, derived_instance is an instance of base and derived too, 813 // but it comes after derived_instance2 in the prototype chain of 814 // other_instance. 815 CHECK_EQ(derived_instance2, 816 other_instance->FindInstanceInPrototypeChain(base)); 817 CHECK_EQ(derived_instance2, 818 other_instance->FindInstanceInPrototypeChain(derived)); 819 CHECK_EQ(other_instance, 820 other_instance->FindInstanceInPrototypeChain(other)); 821} 822 823 824THREADED_TEST(TinyInteger) { 825 v8::HandleScope scope; 826 LocalContext env; 827 int32_t value = 239; 828 Local<v8::Integer> value_obj = v8::Integer::New(value); 829 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 830} 831 832 833THREADED_TEST(BigSmiInteger) { 834 v8::HandleScope scope; 835 LocalContext env; 836 int32_t value = i::Smi::kMaxValue; 837 // We cannot add one to a Smi::kMaxValue without wrapping. 838 if (i::kSmiValueSize < 32) { 839 CHECK(i::Smi::IsValid(value)); 840 CHECK(!i::Smi::IsValid(value + 1)); 841 Local<v8::Integer> value_obj = v8::Integer::New(value); 842 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 843 } 844} 845 846 847THREADED_TEST(BigInteger) { 848 v8::HandleScope scope; 849 LocalContext env; 850 // We cannot add one to a Smi::kMaxValue without wrapping. 851 if (i::kSmiValueSize < 32) { 852 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1. 853 // The code will not be run in that case, due to the "if" guard. 854 int32_t value = 855 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1); 856 CHECK(value > i::Smi::kMaxValue); 857 CHECK(!i::Smi::IsValid(value)); 858 Local<v8::Integer> value_obj = v8::Integer::New(value); 859 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 860 } 861} 862 863 864THREADED_TEST(TinyUnsignedInteger) { 865 v8::HandleScope scope; 866 LocalContext env; 867 uint32_t value = 239; 868 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 869 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 870} 871 872 873THREADED_TEST(BigUnsignedSmiInteger) { 874 v8::HandleScope scope; 875 LocalContext env; 876 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue); 877 CHECK(i::Smi::IsValid(value)); 878 CHECK(!i::Smi::IsValid(value + 1)); 879 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 880 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 881} 882 883 884THREADED_TEST(BigUnsignedInteger) { 885 v8::HandleScope scope; 886 LocalContext env; 887 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1; 888 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue)); 889 CHECK(!i::Smi::IsValid(value)); 890 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 891 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 892} 893 894 895THREADED_TEST(OutOfSignedRangeUnsignedInteger) { 896 v8::HandleScope scope; 897 LocalContext env; 898 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1; 899 uint32_t value = INT32_MAX_AS_UINT + 1; 900 CHECK(value > INT32_MAX_AS_UINT); // No overflow. 901 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 902 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 903} 904 905 906THREADED_TEST(Number) { 907 v8::HandleScope scope; 908 LocalContext env; 909 double PI = 3.1415926; 910 Local<v8::Number> pi_obj = v8::Number::New(PI); 911 CHECK_EQ(PI, pi_obj->NumberValue()); 912} 913 914 915THREADED_TEST(ToNumber) { 916 v8::HandleScope scope; 917 LocalContext env; 918 Local<String> str = v8_str("3.1415926"); 919 CHECK_EQ(3.1415926, str->NumberValue()); 920 v8::Handle<v8::Boolean> t = v8::True(); 921 CHECK_EQ(1.0, t->NumberValue()); 922 v8::Handle<v8::Boolean> f = v8::False(); 923 CHECK_EQ(0.0, f->NumberValue()); 924} 925 926 927THREADED_TEST(Date) { 928 v8::HandleScope scope; 929 LocalContext env; 930 double PI = 3.1415926; 931 Local<Value> date_obj = v8::Date::New(PI); 932 CHECK_EQ(3.0, date_obj->NumberValue()); 933} 934 935 936THREADED_TEST(Boolean) { 937 v8::HandleScope scope; 938 LocalContext env; 939 v8::Handle<v8::Boolean> t = v8::True(); 940 CHECK(t->Value()); 941 v8::Handle<v8::Boolean> f = v8::False(); 942 CHECK(!f->Value()); 943 v8::Handle<v8::Primitive> u = v8::Undefined(); 944 CHECK(!u->BooleanValue()); 945 v8::Handle<v8::Primitive> n = v8::Null(); 946 CHECK(!n->BooleanValue()); 947 v8::Handle<String> str1 = v8_str(""); 948 CHECK(!str1->BooleanValue()); 949 v8::Handle<String> str2 = v8_str("x"); 950 CHECK(str2->BooleanValue()); 951 CHECK(!v8::Number::New(0)->BooleanValue()); 952 CHECK(v8::Number::New(-1)->BooleanValue()); 953 CHECK(v8::Number::New(1)->BooleanValue()); 954 CHECK(v8::Number::New(42)->BooleanValue()); 955 CHECK(!v8_compile("NaN")->Run()->BooleanValue()); 956} 957 958 959static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) { 960 ApiTestFuzzer::Fuzz(); 961 return v8_num(13.4); 962} 963 964 965static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) { 966 ApiTestFuzzer::Fuzz(); 967 return v8_num(876); 968} 969 970 971THREADED_TEST(GlobalPrototype) { 972 v8::HandleScope scope; 973 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 974 func_templ->PrototypeTemplate()->Set( 975 "dummy", 976 v8::FunctionTemplate::New(DummyCallHandler)); 977 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate(); 978 templ->Set("x", v8_num(200)); 979 templ->SetAccessor(v8_str("m"), GetM); 980 LocalContext env(0, templ); 981 v8::Handle<v8::Object> obj = env->Global(); 982 v8::Handle<Script> script = v8_compile("dummy()"); 983 v8::Handle<Value> result = script->Run(); 984 CHECK_EQ(13.4, result->NumberValue()); 985 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value()); 986 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value()); 987} 988 989 990THREADED_TEST(ObjectTemplate) { 991 v8::HandleScope scope; 992 Local<ObjectTemplate> templ1 = ObjectTemplate::New(); 993 templ1->Set("x", v8_num(10)); 994 templ1->Set("y", v8_num(13)); 995 LocalContext env; 996 Local<v8::Object> instance1 = templ1->NewInstance(); 997 env->Global()->Set(v8_str("p"), instance1); 998 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue()); 999 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue()); 1000 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); 1001 fun->PrototypeTemplate()->Set("nirk", v8_num(123)); 1002 Local<ObjectTemplate> templ2 = fun->InstanceTemplate(); 1003 templ2->Set("a", v8_num(12)); 1004 templ2->Set("b", templ1); 1005 Local<v8::Object> instance2 = templ2->NewInstance(); 1006 env->Global()->Set(v8_str("q"), instance2); 1007 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue()); 1008 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue()); 1009 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue()); 1010 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue()); 1011} 1012 1013 1014static v8::Handle<Value> GetFlabby(const v8::Arguments& args) { 1015 ApiTestFuzzer::Fuzz(); 1016 return v8_num(17.2); 1017} 1018 1019 1020static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) { 1021 ApiTestFuzzer::Fuzz(); 1022 return v8_num(15.2); 1023} 1024 1025 1026THREADED_TEST(DescriptorInheritance) { 1027 v8::HandleScope scope; 1028 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New(); 1029 super->PrototypeTemplate()->Set("flabby", 1030 v8::FunctionTemplate::New(GetFlabby)); 1031 super->PrototypeTemplate()->Set("PI", v8_num(3.14)); 1032 1033 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd); 1034 1035 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New(); 1036 base1->Inherit(super); 1037 base1->PrototypeTemplate()->Set("v1", v8_num(20.1)); 1038 1039 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New(); 1040 base2->Inherit(super); 1041 base2->PrototypeTemplate()->Set("v2", v8_num(10.1)); 1042 1043 LocalContext env; 1044 1045 env->Global()->Set(v8_str("s"), super->GetFunction()); 1046 env->Global()->Set(v8_str("base1"), base1->GetFunction()); 1047 env->Global()->Set(v8_str("base2"), base2->GetFunction()); 1048 1049 // Checks right __proto__ chain. 1050 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue()); 1051 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue()); 1052 1053 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue()); 1054 1055 // Instance accessor should not be visible on function object or its prototype 1056 CHECK(CompileRun("s.knurd == undefined")->BooleanValue()); 1057 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue()); 1058 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue()); 1059 1060 env->Global()->Set(v8_str("obj"), 1061 base1->GetFunction()->NewInstance()); 1062 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue()); 1063 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue()); 1064 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue()); 1065 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue()); 1066 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue()); 1067 1068 env->Global()->Set(v8_str("obj2"), 1069 base2->GetFunction()->NewInstance()); 1070 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue()); 1071 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue()); 1072 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue()); 1073 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue()); 1074 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue()); 1075 1076 // base1 and base2 cannot cross reference to each's prototype 1077 CHECK(v8_compile("obj.v2")->Run()->IsUndefined()); 1078 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined()); 1079} 1080 1081 1082int echo_named_call_count; 1083 1084 1085static v8::Handle<Value> EchoNamedProperty(Local<String> name, 1086 const AccessorInfo& info) { 1087 ApiTestFuzzer::Fuzz(); 1088 CHECK_EQ(v8_str("data"), info.Data()); 1089 echo_named_call_count++; 1090 return name; 1091} 1092 1093 1094THREADED_TEST(NamedPropertyHandlerGetter) { 1095 echo_named_call_count = 0; 1096 v8::HandleScope scope; 1097 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1098 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty, 1099 0, 0, 0, 0, 1100 v8_str("data")); 1101 LocalContext env; 1102 env->Global()->Set(v8_str("obj"), 1103 templ->GetFunction()->NewInstance()); 1104 CHECK_EQ(echo_named_call_count, 0); 1105 v8_compile("obj.x")->Run(); 1106 CHECK_EQ(echo_named_call_count, 1); 1107 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;"; 1108 v8::Handle<Value> str = CompileRun(code); 1109 String::AsciiValue value(str); 1110 CHECK_EQ(*value, "oddlepoddle"); 1111 // Check default behavior 1112 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10); 1113 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue()); 1114 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue()); 1115} 1116 1117 1118int echo_indexed_call_count = 0; 1119 1120 1121static v8::Handle<Value> EchoIndexedProperty(uint32_t index, 1122 const AccessorInfo& info) { 1123 ApiTestFuzzer::Fuzz(); 1124 CHECK_EQ(v8_num(637), info.Data()); 1125 echo_indexed_call_count++; 1126 return v8_num(index); 1127} 1128 1129 1130THREADED_TEST(IndexedPropertyHandlerGetter) { 1131 v8::HandleScope scope; 1132 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1133 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty, 1134 0, 0, 0, 0, 1135 v8_num(637)); 1136 LocalContext env; 1137 env->Global()->Set(v8_str("obj"), 1138 templ->GetFunction()->NewInstance()); 1139 Local<Script> script = v8_compile("obj[900]"); 1140 CHECK_EQ(script->Run()->Int32Value(), 900); 1141} 1142 1143 1144v8::Handle<v8::Object> bottom; 1145 1146static v8::Handle<Value> CheckThisIndexedPropertyHandler( 1147 uint32_t index, 1148 const AccessorInfo& info) { 1149 ApiTestFuzzer::Fuzz(); 1150 CHECK(info.This()->Equals(bottom)); 1151 return v8::Handle<Value>(); 1152} 1153 1154static v8::Handle<Value> CheckThisNamedPropertyHandler( 1155 Local<String> name, 1156 const AccessorInfo& info) { 1157 ApiTestFuzzer::Fuzz(); 1158 CHECK(info.This()->Equals(bottom)); 1159 return v8::Handle<Value>(); 1160} 1161 1162 1163v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index, 1164 Local<Value> value, 1165 const AccessorInfo& info) { 1166 ApiTestFuzzer::Fuzz(); 1167 CHECK(info.This()->Equals(bottom)); 1168 return v8::Handle<Value>(); 1169} 1170 1171 1172v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property, 1173 Local<Value> value, 1174 const AccessorInfo& info) { 1175 ApiTestFuzzer::Fuzz(); 1176 CHECK(info.This()->Equals(bottom)); 1177 return v8::Handle<Value>(); 1178} 1179 1180v8::Handle<v8::Boolean> CheckThisIndexedPropertyQuery( 1181 uint32_t index, 1182 const AccessorInfo& info) { 1183 ApiTestFuzzer::Fuzz(); 1184 CHECK(info.This()->Equals(bottom)); 1185 return v8::Handle<v8::Boolean>(); 1186} 1187 1188 1189v8::Handle<v8::Integer> CheckThisNamedPropertyQuery(Local<String> property, 1190 const AccessorInfo& info) { 1191 ApiTestFuzzer::Fuzz(); 1192 CHECK(info.This()->Equals(bottom)); 1193 return v8::Handle<v8::Integer>(); 1194} 1195 1196 1197v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter( 1198 uint32_t index, 1199 const AccessorInfo& info) { 1200 ApiTestFuzzer::Fuzz(); 1201 CHECK(info.This()->Equals(bottom)); 1202 return v8::Handle<v8::Boolean>(); 1203} 1204 1205 1206v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter( 1207 Local<String> property, 1208 const AccessorInfo& info) { 1209 ApiTestFuzzer::Fuzz(); 1210 CHECK(info.This()->Equals(bottom)); 1211 return v8::Handle<v8::Boolean>(); 1212} 1213 1214 1215v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator( 1216 const AccessorInfo& info) { 1217 ApiTestFuzzer::Fuzz(); 1218 CHECK(info.This()->Equals(bottom)); 1219 return v8::Handle<v8::Array>(); 1220} 1221 1222 1223v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator( 1224 const AccessorInfo& info) { 1225 ApiTestFuzzer::Fuzz(); 1226 CHECK(info.This()->Equals(bottom)); 1227 return v8::Handle<v8::Array>(); 1228} 1229 1230 1231THREADED_TEST(PropertyHandlerInPrototype) { 1232 v8::HandleScope scope; 1233 LocalContext env; 1234 1235 // Set up a prototype chain with three interceptors. 1236 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1237 templ->InstanceTemplate()->SetIndexedPropertyHandler( 1238 CheckThisIndexedPropertyHandler, 1239 CheckThisIndexedPropertySetter, 1240 CheckThisIndexedPropertyQuery, 1241 CheckThisIndexedPropertyDeleter, 1242 CheckThisIndexedPropertyEnumerator); 1243 1244 templ->InstanceTemplate()->SetNamedPropertyHandler( 1245 CheckThisNamedPropertyHandler, 1246 CheckThisNamedPropertySetter, 1247 CheckThisNamedPropertyQuery, 1248 CheckThisNamedPropertyDeleter, 1249 CheckThisNamedPropertyEnumerator); 1250 1251 bottom = templ->GetFunction()->NewInstance(); 1252 Local<v8::Object> top = templ->GetFunction()->NewInstance(); 1253 Local<v8::Object> middle = templ->GetFunction()->NewInstance(); 1254 1255 bottom->Set(v8_str("__proto__"), middle); 1256 middle->Set(v8_str("__proto__"), top); 1257 env->Global()->Set(v8_str("obj"), bottom); 1258 1259 // Indexed and named get. 1260 Script::Compile(v8_str("obj[0]"))->Run(); 1261 Script::Compile(v8_str("obj.x"))->Run(); 1262 1263 // Indexed and named set. 1264 Script::Compile(v8_str("obj[1] = 42"))->Run(); 1265 Script::Compile(v8_str("obj.y = 42"))->Run(); 1266 1267 // Indexed and named query. 1268 Script::Compile(v8_str("0 in obj"))->Run(); 1269 Script::Compile(v8_str("'x' in obj"))->Run(); 1270 1271 // Indexed and named deleter. 1272 Script::Compile(v8_str("delete obj[0]"))->Run(); 1273 Script::Compile(v8_str("delete obj.x"))->Run(); 1274 1275 // Enumerators. 1276 Script::Compile(v8_str("for (var p in obj) ;"))->Run(); 1277} 1278 1279 1280static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key, 1281 const AccessorInfo& info) { 1282 ApiTestFuzzer::Fuzz(); 1283 if (v8_str("pre")->Equals(key)) { 1284 return v8_str("PrePropertyHandler: pre"); 1285 } 1286 return v8::Handle<String>(); 1287} 1288 1289 1290static v8::Handle<v8::Integer> PrePropertyHandlerQuery(Local<String> key, 1291 const AccessorInfo&) { 1292 if (v8_str("pre")->Equals(key)) { 1293 return v8::Integer::New(v8::None); 1294 } 1295 1296 return v8::Handle<v8::Integer>(); // do not intercept the call 1297} 1298 1299 1300THREADED_TEST(PrePropertyHandler) { 1301 v8::HandleScope scope; 1302 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(); 1303 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet, 1304 0, 1305 PrePropertyHandlerQuery); 1306 LocalContext env(NULL, desc->InstanceTemplate()); 1307 Script::Compile(v8_str( 1308 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run(); 1309 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run(); 1310 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre); 1311 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run(); 1312 CHECK_EQ(v8_str("Object: on"), result_on); 1313 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run(); 1314 CHECK(result_post.IsEmpty()); 1315} 1316 1317 1318THREADED_TEST(UndefinedIsNotEnumerable) { 1319 v8::HandleScope scope; 1320 LocalContext env; 1321 v8::Handle<Value> result = Script::Compile(v8_str( 1322 "this.propertyIsEnumerable(undefined)"))->Run(); 1323 CHECK(result->IsFalse()); 1324} 1325 1326 1327v8::Handle<Script> call_recursively_script; 1328static const int kTargetRecursionDepth = 200; // near maximum 1329 1330 1331static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) { 1332 ApiTestFuzzer::Fuzz(); 1333 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1334 if (depth == kTargetRecursionDepth) return v8::Undefined(); 1335 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1336 return call_recursively_script->Run(); 1337} 1338 1339 1340static v8::Handle<Value> CallFunctionRecursivelyCall( 1341 const v8::Arguments& args) { 1342 ApiTestFuzzer::Fuzz(); 1343 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1344 if (depth == kTargetRecursionDepth) { 1345 printf("[depth = %d]\n", depth); 1346 return v8::Undefined(); 1347 } 1348 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1349 v8::Handle<Value> function = 1350 args.This()->Get(v8_str("callFunctionRecursively")); 1351 return function.As<Function>()->Call(args.This(), 0, NULL); 1352} 1353 1354 1355THREADED_TEST(DeepCrossLanguageRecursion) { 1356 v8::HandleScope scope; 1357 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 1358 global->Set(v8_str("callScriptRecursively"), 1359 v8::FunctionTemplate::New(CallScriptRecursivelyCall)); 1360 global->Set(v8_str("callFunctionRecursively"), 1361 v8::FunctionTemplate::New(CallFunctionRecursivelyCall)); 1362 LocalContext env(NULL, global); 1363 1364 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1365 call_recursively_script = v8_compile("callScriptRecursively()"); 1366 v8::Handle<Value> result = call_recursively_script->Run(); 1367 call_recursively_script = v8::Handle<Script>(); 1368 1369 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1370 Script::Compile(v8_str("callFunctionRecursively()"))->Run(); 1371} 1372 1373 1374static v8::Handle<Value> 1375 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) { 1376 ApiTestFuzzer::Fuzz(); 1377 return v8::ThrowException(key); 1378} 1379 1380 1381static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key, 1382 Local<Value>, 1383 const AccessorInfo&) { 1384 v8::ThrowException(key); 1385 return v8::Undefined(); // not the same as v8::Handle<v8::Value>() 1386} 1387 1388 1389THREADED_TEST(CallbackExceptionRegression) { 1390 v8::HandleScope scope; 1391 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 1392 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet, 1393 ThrowingPropertyHandlerSet); 1394 LocalContext env; 1395 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 1396 v8::Handle<Value> otto = Script::Compile(v8_str( 1397 "try { with (obj) { otto; } } catch (e) { e; }"))->Run(); 1398 CHECK_EQ(v8_str("otto"), otto); 1399 v8::Handle<Value> netto = Script::Compile(v8_str( 1400 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run(); 1401 CHECK_EQ(v8_str("netto"), netto); 1402} 1403 1404 1405THREADED_TEST(FunctionPrototype) { 1406 v8::HandleScope scope; 1407 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New(); 1408 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321)); 1409 LocalContext env; 1410 env->Global()->Set(v8_str("Foo"), Foo->GetFunction()); 1411 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak")); 1412 CHECK_EQ(script->Run()->Int32Value(), 321); 1413} 1414 1415 1416THREADED_TEST(InternalFields) { 1417 v8::HandleScope scope; 1418 LocalContext env; 1419 1420 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1421 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1422 instance_templ->SetInternalFieldCount(1); 1423 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1424 CHECK_EQ(1, obj->InternalFieldCount()); 1425 CHECK(obj->GetInternalField(0)->IsUndefined()); 1426 obj->SetInternalField(0, v8_num(17)); 1427 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value()); 1428} 1429 1430 1431THREADED_TEST(GlobalObjectInternalFields) { 1432 v8::HandleScope scope; 1433 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 1434 global_template->SetInternalFieldCount(1); 1435 LocalContext env(NULL, global_template); 1436 v8::Handle<v8::Object> global_proxy = env->Global(); 1437 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); 1438 CHECK_EQ(1, global->InternalFieldCount()); 1439 CHECK(global->GetInternalField(0)->IsUndefined()); 1440 global->SetInternalField(0, v8_num(17)); 1441 CHECK_EQ(17, global->GetInternalField(0)->Int32Value()); 1442} 1443 1444 1445THREADED_TEST(InternalFieldsNativePointers) { 1446 v8::HandleScope scope; 1447 LocalContext env; 1448 1449 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1450 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1451 instance_templ->SetInternalFieldCount(1); 1452 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1453 CHECK_EQ(1, obj->InternalFieldCount()); 1454 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1455 1456 char* data = new char[100]; 1457 1458 void* aligned = data; 1459 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1); 1460 void* unaligned = data + 1; 1461 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1); 1462 1463 // Check reading and writing aligned pointers. 1464 obj->SetPointerInInternalField(0, aligned); 1465 i::Heap::CollectAllGarbage(false); 1466 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1467 1468 // Check reading and writing unaligned pointers. 1469 obj->SetPointerInInternalField(0, unaligned); 1470 i::Heap::CollectAllGarbage(false); 1471 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1472 1473 delete[] data; 1474} 1475 1476 1477THREADED_TEST(InternalFieldsNativePointersAndExternal) { 1478 v8::HandleScope scope; 1479 LocalContext env; 1480 1481 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1482 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1483 instance_templ->SetInternalFieldCount(1); 1484 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1485 CHECK_EQ(1, obj->InternalFieldCount()); 1486 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1487 1488 char* data = new char[100]; 1489 1490 void* aligned = data; 1491 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1); 1492 void* unaligned = data + 1; 1493 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1); 1494 1495 obj->SetPointerInInternalField(0, aligned); 1496 i::Heap::CollectAllGarbage(false); 1497 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0))); 1498 1499 obj->SetPointerInInternalField(0, unaligned); 1500 i::Heap::CollectAllGarbage(false); 1501 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0))); 1502 1503 obj->SetInternalField(0, v8::External::Wrap(aligned)); 1504 i::Heap::CollectAllGarbage(false); 1505 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1506 1507 obj->SetInternalField(0, v8::External::Wrap(unaligned)); 1508 i::Heap::CollectAllGarbage(false); 1509 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1510 1511 delete[] data; 1512} 1513 1514 1515THREADED_TEST(IdentityHash) { 1516 v8::HandleScope scope; 1517 LocalContext env; 1518 1519 // Ensure that the test starts with an fresh heap to test whether the hash 1520 // code is based on the address. 1521 i::Heap::CollectAllGarbage(false); 1522 Local<v8::Object> obj = v8::Object::New(); 1523 int hash = obj->GetIdentityHash(); 1524 int hash1 = obj->GetIdentityHash(); 1525 CHECK_EQ(hash, hash1); 1526 int hash2 = v8::Object::New()->GetIdentityHash(); 1527 // Since the identity hash is essentially a random number two consecutive 1528 // objects should not be assigned the same hash code. If the test below fails 1529 // the random number generator should be evaluated. 1530 CHECK_NE(hash, hash2); 1531 i::Heap::CollectAllGarbage(false); 1532 int hash3 = v8::Object::New()->GetIdentityHash(); 1533 // Make sure that the identity hash is not based on the initial address of 1534 // the object alone. If the test below fails the random number generator 1535 // should be evaluated. 1536 CHECK_NE(hash, hash3); 1537 int hash4 = obj->GetIdentityHash(); 1538 CHECK_EQ(hash, hash4); 1539} 1540 1541 1542THREADED_TEST(HiddenProperties) { 1543 v8::HandleScope scope; 1544 LocalContext env; 1545 1546 v8::Local<v8::Object> obj = v8::Object::New(); 1547 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 1548 v8::Local<v8::String> empty = v8_str(""); 1549 v8::Local<v8::String> prop_name = v8_str("prop_name"); 1550 1551 i::Heap::CollectAllGarbage(false); 1552 1553 // Make sure delete of a non-existent hidden value works 1554 CHECK(obj->DeleteHiddenValue(key)); 1555 1556 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503))); 1557 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value()); 1558 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002))); 1559 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1560 1561 i::Heap::CollectAllGarbage(false); 1562 1563 // Make sure we do not find the hidden property. 1564 CHECK(!obj->Has(empty)); 1565 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1566 CHECK(obj->Get(empty)->IsUndefined()); 1567 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1568 CHECK(obj->Set(empty, v8::Integer::New(2003))); 1569 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1570 CHECK_EQ(2003, obj->Get(empty)->Int32Value()); 1571 1572 i::Heap::CollectAllGarbage(false); 1573 1574 // Add another property and delete it afterwards to force the object in 1575 // slow case. 1576 CHECK(obj->Set(prop_name, v8::Integer::New(2008))); 1577 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1578 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value()); 1579 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1580 CHECK(obj->Delete(prop_name)); 1581 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1582 1583 i::Heap::CollectAllGarbage(false); 1584 1585 CHECK(obj->DeleteHiddenValue(key)); 1586 CHECK(obj->GetHiddenValue(key).IsEmpty()); 1587} 1588 1589 1590static bool interceptor_for_hidden_properties_called; 1591static v8::Handle<Value> InterceptorForHiddenProperties( 1592 Local<String> name, const AccessorInfo& info) { 1593 interceptor_for_hidden_properties_called = true; 1594 return v8::Handle<Value>(); 1595} 1596 1597 1598THREADED_TEST(HiddenPropertiesWithInterceptors) { 1599 v8::HandleScope scope; 1600 LocalContext context; 1601 1602 interceptor_for_hidden_properties_called = false; 1603 1604 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 1605 1606 // Associate an interceptor with an object and start setting hidden values. 1607 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 1608 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 1609 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties); 1610 Local<v8::Function> function = fun_templ->GetFunction(); 1611 Local<v8::Object> obj = function->NewInstance(); 1612 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302))); 1613 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value()); 1614 CHECK(!interceptor_for_hidden_properties_called); 1615} 1616 1617 1618THREADED_TEST(External) { 1619 v8::HandleScope scope; 1620 int x = 3; 1621 Local<v8::External> ext = v8::External::New(&x); 1622 LocalContext env; 1623 env->Global()->Set(v8_str("ext"), ext); 1624 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run(); 1625 v8::Handle<v8::External> reext = reext_obj.As<v8::External>(); 1626 int* ptr = static_cast<int*>(reext->Value()); 1627 CHECK_EQ(x, 3); 1628 *ptr = 10; 1629 CHECK_EQ(x, 10); 1630 1631 // Make sure unaligned pointers are wrapped properly. 1632 char* data = i::StrDup("0123456789"); 1633 Local<v8::Value> zero = v8::External::Wrap(&data[0]); 1634 Local<v8::Value> one = v8::External::Wrap(&data[1]); 1635 Local<v8::Value> two = v8::External::Wrap(&data[2]); 1636 Local<v8::Value> three = v8::External::Wrap(&data[3]); 1637 1638 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero)); 1639 CHECK_EQ('0', *char_ptr); 1640 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one)); 1641 CHECK_EQ('1', *char_ptr); 1642 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two)); 1643 CHECK_EQ('2', *char_ptr); 1644 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three)); 1645 CHECK_EQ('3', *char_ptr); 1646 i::DeleteArray(data); 1647} 1648 1649 1650THREADED_TEST(GlobalHandle) { 1651 v8::Persistent<String> global; 1652 { 1653 v8::HandleScope scope; 1654 Local<String> str = v8_str("str"); 1655 global = v8::Persistent<String>::New(str); 1656 } 1657 CHECK_EQ(global->Length(), 3); 1658 global.Dispose(); 1659} 1660 1661 1662THREADED_TEST(ScriptException) { 1663 v8::HandleScope scope; 1664 LocalContext env; 1665 Local<Script> script = Script::Compile(v8_str("throw 'panama!';")); 1666 v8::TryCatch try_catch; 1667 Local<Value> result = script->Run(); 1668 CHECK(result.IsEmpty()); 1669 CHECK(try_catch.HasCaught()); 1670 String::AsciiValue exception_value(try_catch.Exception()); 1671 CHECK_EQ(*exception_value, "panama!"); 1672} 1673 1674 1675bool message_received; 1676 1677 1678static void check_message(v8::Handle<v8::Message> message, 1679 v8::Handle<Value> data) { 1680 CHECK_EQ(5.76, data->NumberValue()); 1681 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue()); 1682 CHECK_EQ(7.56, message->GetScriptData()->NumberValue()); 1683 message_received = true; 1684} 1685 1686 1687THREADED_TEST(MessageHandlerData) { 1688 message_received = false; 1689 v8::HandleScope scope; 1690 CHECK(!message_received); 1691 v8::V8::AddMessageListener(check_message, v8_num(5.76)); 1692 LocalContext context; 1693 v8::ScriptOrigin origin = 1694 v8::ScriptOrigin(v8_str("6.75")); 1695 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"), 1696 &origin); 1697 script->SetData(v8_str("7.56")); 1698 script->Run(); 1699 CHECK(message_received); 1700 // clear out the message listener 1701 v8::V8::RemoveMessageListeners(check_message); 1702} 1703 1704 1705THREADED_TEST(GetSetProperty) { 1706 v8::HandleScope scope; 1707 LocalContext context; 1708 context->Global()->Set(v8_str("foo"), v8_num(14)); 1709 context->Global()->Set(v8_str("12"), v8_num(92)); 1710 context->Global()->Set(v8::Integer::New(16), v8_num(32)); 1711 context->Global()->Set(v8_num(13), v8_num(56)); 1712 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run(); 1713 CHECK_EQ(14, foo->Int32Value()); 1714 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run(); 1715 CHECK_EQ(92, twelve->Int32Value()); 1716 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run(); 1717 CHECK_EQ(32, sixteen->Int32Value()); 1718 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run(); 1719 CHECK_EQ(56, thirteen->Int32Value()); 1720 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value()); 1721 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value()); 1722 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value()); 1723 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value()); 1724 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value()); 1725 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value()); 1726 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value()); 1727 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value()); 1728 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value()); 1729} 1730 1731 1732THREADED_TEST(PropertyAttributes) { 1733 v8::HandleScope scope; 1734 LocalContext context; 1735 // read-only 1736 Local<String> prop = v8_str("read_only"); 1737 context->Global()->Set(prop, v8_num(7), v8::ReadOnly); 1738 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1739 Script::Compile(v8_str("read_only = 9"))->Run(); 1740 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1741 context->Global()->Set(prop, v8_num(10)); 1742 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1743 // dont-delete 1744 prop = v8_str("dont_delete"); 1745 context->Global()->Set(prop, v8_num(13), v8::DontDelete); 1746 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 1747 Script::Compile(v8_str("delete dont_delete"))->Run(); 1748 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 1749} 1750 1751 1752THREADED_TEST(Array) { 1753 v8::HandleScope scope; 1754 LocalContext context; 1755 Local<v8::Array> array = v8::Array::New(); 1756 CHECK_EQ(0, array->Length()); 1757 CHECK(array->Get(0)->IsUndefined()); 1758 CHECK(!array->Has(0)); 1759 CHECK(array->Get(100)->IsUndefined()); 1760 CHECK(!array->Has(100)); 1761 array->Set(2, v8_num(7)); 1762 CHECK_EQ(3, array->Length()); 1763 CHECK(!array->Has(0)); 1764 CHECK(!array->Has(1)); 1765 CHECK(array->Has(2)); 1766 CHECK_EQ(7, array->Get(2)->Int32Value()); 1767 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run(); 1768 Local<v8::Array> arr = obj.As<v8::Array>(); 1769 CHECK_EQ(3, arr->Length()); 1770 CHECK_EQ(1, arr->Get(0)->Int32Value()); 1771 CHECK_EQ(2, arr->Get(1)->Int32Value()); 1772 CHECK_EQ(3, arr->Get(2)->Int32Value()); 1773} 1774 1775 1776v8::Handle<Value> HandleF(const v8::Arguments& args) { 1777 v8::HandleScope scope; 1778 ApiTestFuzzer::Fuzz(); 1779 Local<v8::Array> result = v8::Array::New(args.Length()); 1780 for (int i = 0; i < args.Length(); i++) 1781 result->Set(i, args[i]); 1782 return scope.Close(result); 1783} 1784 1785 1786THREADED_TEST(Vector) { 1787 v8::HandleScope scope; 1788 Local<ObjectTemplate> global = ObjectTemplate::New(); 1789 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF)); 1790 LocalContext context(0, global); 1791 1792 const char* fun = "f()"; 1793 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>(); 1794 CHECK_EQ(0, a0->Length()); 1795 1796 const char* fun2 = "f(11)"; 1797 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>(); 1798 CHECK_EQ(1, a1->Length()); 1799 CHECK_EQ(11, a1->Get(0)->Int32Value()); 1800 1801 const char* fun3 = "f(12, 13)"; 1802 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>(); 1803 CHECK_EQ(2, a2->Length()); 1804 CHECK_EQ(12, a2->Get(0)->Int32Value()); 1805 CHECK_EQ(13, a2->Get(1)->Int32Value()); 1806 1807 const char* fun4 = "f(14, 15, 16)"; 1808 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>(); 1809 CHECK_EQ(3, a3->Length()); 1810 CHECK_EQ(14, a3->Get(0)->Int32Value()); 1811 CHECK_EQ(15, a3->Get(1)->Int32Value()); 1812 CHECK_EQ(16, a3->Get(2)->Int32Value()); 1813 1814 const char* fun5 = "f(17, 18, 19, 20)"; 1815 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>(); 1816 CHECK_EQ(4, a4->Length()); 1817 CHECK_EQ(17, a4->Get(0)->Int32Value()); 1818 CHECK_EQ(18, a4->Get(1)->Int32Value()); 1819 CHECK_EQ(19, a4->Get(2)->Int32Value()); 1820 CHECK_EQ(20, a4->Get(3)->Int32Value()); 1821} 1822 1823 1824THREADED_TEST(FunctionCall) { 1825 v8::HandleScope scope; 1826 LocalContext context; 1827 CompileRun( 1828 "function Foo() {" 1829 " var result = [];" 1830 " for (var i = 0; i < arguments.length; i++) {" 1831 " result.push(arguments[i]);" 1832 " }" 1833 " return result;" 1834 "}"); 1835 Local<Function> Foo = 1836 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 1837 1838 v8::Handle<Value>* args0 = NULL; 1839 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0)); 1840 CHECK_EQ(0, a0->Length()); 1841 1842 v8::Handle<Value> args1[] = { v8_num(1.1) }; 1843 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1)); 1844 CHECK_EQ(1, a1->Length()); 1845 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 1846 1847 v8::Handle<Value> args2[] = { v8_num(2.2), 1848 v8_num(3.3) }; 1849 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2)); 1850 CHECK_EQ(2, a2->Length()); 1851 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 1852 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 1853 1854 v8::Handle<Value> args3[] = { v8_num(4.4), 1855 v8_num(5.5), 1856 v8_num(6.6) }; 1857 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3)); 1858 CHECK_EQ(3, a3->Length()); 1859 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 1860 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 1861 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 1862 1863 v8::Handle<Value> args4[] = { v8_num(7.7), 1864 v8_num(8.8), 1865 v8_num(9.9), 1866 v8_num(10.11) }; 1867 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4)); 1868 CHECK_EQ(4, a4->Length()); 1869 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 1870 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 1871 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 1872 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 1873} 1874 1875 1876static const char* js_code_causing_out_of_memory = 1877 "var a = new Array(); while(true) a.push(a);"; 1878 1879 1880// These tests run for a long time and prevent us from running tests 1881// that come after them so they cannot run in parallel. 1882TEST(OutOfMemory) { 1883 // It's not possible to read a snapshot into a heap with different dimensions. 1884 if (v8::internal::Snapshot::IsEnabled()) return; 1885 // Set heap limits. 1886 static const int K = 1024; 1887 v8::ResourceConstraints constraints; 1888 constraints.set_max_young_space_size(256 * K); 1889 constraints.set_max_old_space_size(4 * K * K); 1890 v8::SetResourceConstraints(&constraints); 1891 1892 // Execute a script that causes out of memory. 1893 v8::HandleScope scope; 1894 LocalContext context; 1895 v8::V8::IgnoreOutOfMemoryException(); 1896 Local<Script> script = 1897 Script::Compile(String::New(js_code_causing_out_of_memory)); 1898 Local<Value> result = script->Run(); 1899 1900 // Check for out of memory state. 1901 CHECK(result.IsEmpty()); 1902 CHECK(context->HasOutOfMemoryException()); 1903} 1904 1905 1906v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) { 1907 ApiTestFuzzer::Fuzz(); 1908 1909 v8::HandleScope scope; 1910 LocalContext context; 1911 Local<Script> script = 1912 Script::Compile(String::New(js_code_causing_out_of_memory)); 1913 Local<Value> result = script->Run(); 1914 1915 // Check for out of memory state. 1916 CHECK(result.IsEmpty()); 1917 CHECK(context->HasOutOfMemoryException()); 1918 1919 return result; 1920} 1921 1922 1923TEST(OutOfMemoryNested) { 1924 // It's not possible to read a snapshot into a heap with different dimensions. 1925 if (v8::internal::Snapshot::IsEnabled()) return; 1926 // Set heap limits. 1927 static const int K = 1024; 1928 v8::ResourceConstraints constraints; 1929 constraints.set_max_young_space_size(256 * K); 1930 constraints.set_max_old_space_size(4 * K * K); 1931 v8::SetResourceConstraints(&constraints); 1932 1933 v8::HandleScope scope; 1934 Local<ObjectTemplate> templ = ObjectTemplate::New(); 1935 templ->Set(v8_str("ProvokeOutOfMemory"), 1936 v8::FunctionTemplate::New(ProvokeOutOfMemory)); 1937 LocalContext context(0, templ); 1938 v8::V8::IgnoreOutOfMemoryException(); 1939 Local<Value> result = CompileRun( 1940 "var thrown = false;" 1941 "try {" 1942 " ProvokeOutOfMemory();" 1943 "} catch (e) {" 1944 " thrown = true;" 1945 "}"); 1946 // Check for out of memory state. 1947 CHECK(result.IsEmpty()); 1948 CHECK(context->HasOutOfMemoryException()); 1949} 1950 1951 1952TEST(HugeConsStringOutOfMemory) { 1953 // It's not possible to read a snapshot into a heap with different dimensions. 1954 if (v8::internal::Snapshot::IsEnabled()) return; 1955 v8::HandleScope scope; 1956 LocalContext context; 1957 // Set heap limits. 1958 static const int K = 1024; 1959 v8::ResourceConstraints constraints; 1960 constraints.set_max_young_space_size(256 * K); 1961 constraints.set_max_old_space_size(2 * K * K); 1962 v8::SetResourceConstraints(&constraints); 1963 1964 // Execute a script that causes out of memory. 1965 v8::V8::IgnoreOutOfMemoryException(); 1966 1967 // Build huge string. This should fail with out of memory exception. 1968 Local<Value> result = CompileRun( 1969 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();" 1970 "for (var i = 0; i < 22; i++) { str = str + str; }"); 1971 1972 // Check for out of memory state. 1973 CHECK(result.IsEmpty()); 1974 CHECK(context->HasOutOfMemoryException()); 1975} 1976 1977 1978THREADED_TEST(ConstructCall) { 1979 v8::HandleScope scope; 1980 LocalContext context; 1981 CompileRun( 1982 "function Foo() {" 1983 " var result = [];" 1984 " for (var i = 0; i < arguments.length; i++) {" 1985 " result.push(arguments[i]);" 1986 " }" 1987 " return result;" 1988 "}"); 1989 Local<Function> Foo = 1990 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 1991 1992 v8::Handle<Value>* args0 = NULL; 1993 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0)); 1994 CHECK_EQ(0, a0->Length()); 1995 1996 v8::Handle<Value> args1[] = { v8_num(1.1) }; 1997 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1)); 1998 CHECK_EQ(1, a1->Length()); 1999 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 2000 2001 v8::Handle<Value> args2[] = { v8_num(2.2), 2002 v8_num(3.3) }; 2003 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2)); 2004 CHECK_EQ(2, a2->Length()); 2005 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 2006 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 2007 2008 v8::Handle<Value> args3[] = { v8_num(4.4), 2009 v8_num(5.5), 2010 v8_num(6.6) }; 2011 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3)); 2012 CHECK_EQ(3, a3->Length()); 2013 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 2014 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 2015 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 2016 2017 v8::Handle<Value> args4[] = { v8_num(7.7), 2018 v8_num(8.8), 2019 v8_num(9.9), 2020 v8_num(10.11) }; 2021 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4)); 2022 CHECK_EQ(4, a4->Length()); 2023 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 2024 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 2025 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 2026 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 2027} 2028 2029 2030static void CheckUncle(v8::TryCatch* try_catch) { 2031 CHECK(try_catch->HasCaught()); 2032 String::AsciiValue str_value(try_catch->Exception()); 2033 CHECK_EQ(*str_value, "uncle?"); 2034 try_catch->Reset(); 2035} 2036 2037 2038THREADED_TEST(ConversionNumber) { 2039 v8::HandleScope scope; 2040 LocalContext env; 2041 // Very large number. 2042 CompileRun("var obj = Math.pow(2,32) * 1237;"); 2043 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2044 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value()); 2045 CHECK_EQ(0, obj->ToInt32()->Value()); 2046 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned. 2047 // Large number. 2048 CompileRun("var obj = -1234567890123;"); 2049 obj = env->Global()->Get(v8_str("obj")); 2050 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value()); 2051 CHECK_EQ(-1912276171, obj->ToInt32()->Value()); 2052 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT 2053 // Small positive integer. 2054 CompileRun("var obj = 42;"); 2055 obj = env->Global()->Get(v8_str("obj")); 2056 CHECK_EQ(42.0, obj->ToNumber()->Value()); 2057 CHECK_EQ(42, obj->ToInt32()->Value()); 2058 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 2059 // Negative integer. 2060 CompileRun("var obj = -37;"); 2061 obj = env->Global()->Get(v8_str("obj")); 2062 CHECK_EQ(-37.0, obj->ToNumber()->Value()); 2063 CHECK_EQ(-37, obj->ToInt32()->Value()); 2064 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT 2065 // Positive non-int32 integer. 2066 CompileRun("var obj = 0x81234567;"); 2067 obj = env->Global()->Get(v8_str("obj")); 2068 CHECK_EQ(2166572391.0, obj->ToNumber()->Value()); 2069 CHECK_EQ(-2128394905, obj->ToInt32()->Value()); 2070 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT 2071 // Fraction. 2072 CompileRun("var obj = 42.3;"); 2073 obj = env->Global()->Get(v8_str("obj")); 2074 CHECK_EQ(42.3, obj->ToNumber()->Value()); 2075 CHECK_EQ(42, obj->ToInt32()->Value()); 2076 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 2077 // Large negative fraction. 2078 CompileRun("var obj = -5726623061.75;"); 2079 obj = env->Global()->Get(v8_str("obj")); 2080 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value()); 2081 CHECK_EQ(-1431655765, obj->ToInt32()->Value()); 2082 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT 2083} 2084 2085 2086THREADED_TEST(isNumberType) { 2087 v8::HandleScope scope; 2088 LocalContext env; 2089 // Very large number. 2090 CompileRun("var obj = Math.pow(2,32) * 1237;"); 2091 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2092 CHECK(!obj->IsInt32()); 2093 CHECK(!obj->IsUint32()); 2094 // Large negative number. 2095 CompileRun("var obj = -1234567890123;"); 2096 obj = env->Global()->Get(v8_str("obj")); 2097 CHECK(!obj->IsInt32()); 2098 CHECK(!obj->IsUint32()); 2099 // Small positive integer. 2100 CompileRun("var obj = 42;"); 2101 obj = env->Global()->Get(v8_str("obj")); 2102 CHECK(obj->IsInt32()); 2103 CHECK(obj->IsUint32()); 2104 // Negative integer. 2105 CompileRun("var obj = -37;"); 2106 obj = env->Global()->Get(v8_str("obj")); 2107 CHECK(obj->IsInt32()); 2108 CHECK(!obj->IsUint32()); 2109 // Positive non-int32 integer. 2110 CompileRun("var obj = 0x81234567;"); 2111 obj = env->Global()->Get(v8_str("obj")); 2112 CHECK(!obj->IsInt32()); 2113 CHECK(obj->IsUint32()); 2114 // Fraction. 2115 CompileRun("var obj = 42.3;"); 2116 obj = env->Global()->Get(v8_str("obj")); 2117 CHECK(!obj->IsInt32()); 2118 CHECK(!obj->IsUint32()); 2119 // Large negative fraction. 2120 CompileRun("var obj = -5726623061.75;"); 2121 obj = env->Global()->Get(v8_str("obj")); 2122 CHECK(!obj->IsInt32()); 2123 CHECK(!obj->IsUint32()); 2124} 2125 2126 2127THREADED_TEST(ConversionException) { 2128 v8::HandleScope scope; 2129 LocalContext env; 2130 CompileRun( 2131 "function TestClass() { };" 2132 "TestClass.prototype.toString = function () { throw 'uncle?'; };" 2133 "var obj = new TestClass();"); 2134 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2135 2136 v8::TryCatch try_catch; 2137 2138 Local<Value> to_string_result = obj->ToString(); 2139 CHECK(to_string_result.IsEmpty()); 2140 CheckUncle(&try_catch); 2141 2142 Local<Value> to_number_result = obj->ToNumber(); 2143 CHECK(to_number_result.IsEmpty()); 2144 CheckUncle(&try_catch); 2145 2146 Local<Value> to_integer_result = obj->ToInteger(); 2147 CHECK(to_integer_result.IsEmpty()); 2148 CheckUncle(&try_catch); 2149 2150 Local<Value> to_uint32_result = obj->ToUint32(); 2151 CHECK(to_uint32_result.IsEmpty()); 2152 CheckUncle(&try_catch); 2153 2154 Local<Value> to_int32_result = obj->ToInt32(); 2155 CHECK(to_int32_result.IsEmpty()); 2156 CheckUncle(&try_catch); 2157 2158 Local<Value> to_object_result = v8::Undefined()->ToObject(); 2159 CHECK(to_object_result.IsEmpty()); 2160 CHECK(try_catch.HasCaught()); 2161 try_catch.Reset(); 2162 2163 int32_t int32_value = obj->Int32Value(); 2164 CHECK_EQ(0, int32_value); 2165 CheckUncle(&try_catch); 2166 2167 uint32_t uint32_value = obj->Uint32Value(); 2168 CHECK_EQ(0, uint32_value); 2169 CheckUncle(&try_catch); 2170 2171 double number_value = obj->NumberValue(); 2172 CHECK_NE(0, IsNaN(number_value)); 2173 CheckUncle(&try_catch); 2174 2175 int64_t integer_value = obj->IntegerValue(); 2176 CHECK_EQ(0.0, static_cast<double>(integer_value)); 2177 CheckUncle(&try_catch); 2178} 2179 2180 2181v8::Handle<Value> ThrowFromC(const v8::Arguments& args) { 2182 ApiTestFuzzer::Fuzz(); 2183 return v8::ThrowException(v8_str("konto")); 2184} 2185 2186 2187v8::Handle<Value> CCatcher(const v8::Arguments& args) { 2188 if (args.Length() < 1) return v8::Boolean::New(false); 2189 v8::HandleScope scope; 2190 v8::TryCatch try_catch; 2191 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run(); 2192 CHECK(!try_catch.HasCaught() || result.IsEmpty()); 2193 return v8::Boolean::New(try_catch.HasCaught()); 2194} 2195 2196 2197THREADED_TEST(APICatch) { 2198 v8::HandleScope scope; 2199 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2200 templ->Set(v8_str("ThrowFromC"), 2201 v8::FunctionTemplate::New(ThrowFromC)); 2202 LocalContext context(0, templ); 2203 CompileRun( 2204 "var thrown = false;" 2205 "try {" 2206 " ThrowFromC();" 2207 "} catch (e) {" 2208 " thrown = true;" 2209 "}"); 2210 Local<Value> thrown = context->Global()->Get(v8_str("thrown")); 2211 CHECK(thrown->BooleanValue()); 2212} 2213 2214 2215THREADED_TEST(APIThrowTryCatch) { 2216 v8::HandleScope scope; 2217 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2218 templ->Set(v8_str("ThrowFromC"), 2219 v8::FunctionTemplate::New(ThrowFromC)); 2220 LocalContext context(0, templ); 2221 v8::TryCatch try_catch; 2222 CompileRun("ThrowFromC();"); 2223 CHECK(try_catch.HasCaught()); 2224} 2225 2226 2227// Test that a try-finally block doesn't shadow a try-catch block 2228// when setting up an external handler. 2229// 2230// BUG(271): Some of the exception propagation does not work on the 2231// ARM simulator because the simulator separates the C++ stack and the 2232// JS stack. This test therefore fails on the simulator. The test is 2233// not threaded to allow the threading tests to run on the simulator. 2234TEST(TryCatchInTryFinally) { 2235 v8::HandleScope scope; 2236 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2237 templ->Set(v8_str("CCatcher"), 2238 v8::FunctionTemplate::New(CCatcher)); 2239 LocalContext context(0, templ); 2240 Local<Value> result = CompileRun("try {" 2241 " try {" 2242 " CCatcher('throw 7;');" 2243 " } finally {" 2244 " }" 2245 "} catch (e) {" 2246 "}"); 2247 CHECK(result->IsTrue()); 2248} 2249 2250 2251static void receive_message(v8::Handle<v8::Message> message, 2252 v8::Handle<v8::Value> data) { 2253 message->Get(); 2254 message_received = true; 2255} 2256 2257 2258TEST(APIThrowMessage) { 2259 message_received = false; 2260 v8::HandleScope scope; 2261 v8::V8::AddMessageListener(receive_message); 2262 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2263 templ->Set(v8_str("ThrowFromC"), 2264 v8::FunctionTemplate::New(ThrowFromC)); 2265 LocalContext context(0, templ); 2266 CompileRun("ThrowFromC();"); 2267 CHECK(message_received); 2268 v8::V8::RemoveMessageListeners(check_message); 2269} 2270 2271 2272TEST(APIThrowMessageAndVerboseTryCatch) { 2273 message_received = false; 2274 v8::HandleScope scope; 2275 v8::V8::AddMessageListener(receive_message); 2276 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2277 templ->Set(v8_str("ThrowFromC"), 2278 v8::FunctionTemplate::New(ThrowFromC)); 2279 LocalContext context(0, templ); 2280 v8::TryCatch try_catch; 2281 try_catch.SetVerbose(true); 2282 Local<Value> result = CompileRun("ThrowFromC();"); 2283 CHECK(try_catch.HasCaught()); 2284 CHECK(result.IsEmpty()); 2285 CHECK(message_received); 2286 v8::V8::RemoveMessageListeners(check_message); 2287} 2288 2289 2290THREADED_TEST(ExternalScriptException) { 2291 v8::HandleScope scope; 2292 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2293 templ->Set(v8_str("ThrowFromC"), 2294 v8::FunctionTemplate::New(ThrowFromC)); 2295 LocalContext context(0, templ); 2296 2297 v8::TryCatch try_catch; 2298 Local<Script> script 2299 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';")); 2300 Local<Value> result = script->Run(); 2301 CHECK(result.IsEmpty()); 2302 CHECK(try_catch.HasCaught()); 2303 String::AsciiValue exception_value(try_catch.Exception()); 2304 CHECK_EQ("konto", *exception_value); 2305} 2306 2307 2308 2309v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) { 2310 ApiTestFuzzer::Fuzz(); 2311 CHECK_EQ(4, args.Length()); 2312 int count = args[0]->Int32Value(); 2313 int cInterval = args[2]->Int32Value(); 2314 if (count == 0) { 2315 return v8::ThrowException(v8_str("FromC")); 2316 } else { 2317 Local<v8::Object> global = Context::GetCurrent()->Global(); 2318 Local<Value> fun = global->Get(v8_str("JSThrowCountDown")); 2319 v8::Handle<Value> argv[] = { v8_num(count - 1), 2320 args[1], 2321 args[2], 2322 args[3] }; 2323 if (count % cInterval == 0) { 2324 v8::TryCatch try_catch; 2325 Local<Value> result = fun.As<Function>()->Call(global, 4, argv); 2326 int expected = args[3]->Int32Value(); 2327 if (try_catch.HasCaught()) { 2328 CHECK_EQ(expected, count); 2329 CHECK(result.IsEmpty()); 2330 CHECK(!i::Top::has_scheduled_exception()); 2331 } else { 2332 CHECK_NE(expected, count); 2333 } 2334 return result; 2335 } else { 2336 return fun.As<Function>()->Call(global, 4, argv); 2337 } 2338 } 2339} 2340 2341 2342v8::Handle<Value> JSCheck(const v8::Arguments& args) { 2343 ApiTestFuzzer::Fuzz(); 2344 CHECK_EQ(3, args.Length()); 2345 bool equality = args[0]->BooleanValue(); 2346 int count = args[1]->Int32Value(); 2347 int expected = args[2]->Int32Value(); 2348 if (equality) { 2349 CHECK_EQ(count, expected); 2350 } else { 2351 CHECK_NE(count, expected); 2352 } 2353 return v8::Undefined(); 2354} 2355 2356 2357THREADED_TEST(EvalInTryFinally) { 2358 v8::HandleScope scope; 2359 LocalContext context; 2360 v8::TryCatch try_catch; 2361 CompileRun("(function() {" 2362 " try {" 2363 " eval('asldkf (*&^&*^');" 2364 " } finally {" 2365 " return;" 2366 " }" 2367 "})()"); 2368 CHECK(!try_catch.HasCaught()); 2369} 2370 2371 2372// This test works by making a stack of alternating JavaScript and C 2373// activations. These activations set up exception handlers with regular 2374// intervals, one interval for C activations and another for JavaScript 2375// activations. When enough activations have been created an exception is 2376// thrown and we check that the right activation catches the exception and that 2377// no other activations do. The right activation is always the topmost one with 2378// a handler, regardless of whether it is in JavaScript or C. 2379// 2380// The notation used to describe a test case looks like this: 2381// 2382// *JS[4] *C[3] @JS[2] C[1] JS[0] 2383// 2384// Each entry is an activation, either JS or C. The index is the count at that 2385// level. Stars identify activations with exception handlers, the @ identifies 2386// the exception handler that should catch the exception. 2387// 2388// BUG(271): Some of the exception propagation does not work on the 2389// ARM simulator because the simulator separates the C++ stack and the 2390// JS stack. This test therefore fails on the simulator. The test is 2391// not threaded to allow the threading tests to run on the simulator. 2392TEST(ExceptionOrder) { 2393 v8::HandleScope scope; 2394 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2395 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck)); 2396 templ->Set(v8_str("CThrowCountDown"), 2397 v8::FunctionTemplate::New(CThrowCountDown)); 2398 LocalContext context(0, templ); 2399 CompileRun( 2400 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {" 2401 " if (count == 0) throw 'FromJS';" 2402 " if (count % jsInterval == 0) {" 2403 " try {" 2404 " var value = CThrowCountDown(count - 1," 2405 " jsInterval," 2406 " cInterval," 2407 " expected);" 2408 " check(false, count, expected);" 2409 " return value;" 2410 " } catch (e) {" 2411 " check(true, count, expected);" 2412 " }" 2413 " } else {" 2414 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);" 2415 " }" 2416 "}"); 2417 Local<Function> fun = 2418 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown"))); 2419 2420 const int argc = 4; 2421 // count jsInterval cInterval expected 2422 2423 // *JS[4] *C[3] @JS[2] C[1] JS[0] 2424 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) }; 2425 fun->Call(fun, argc, a0); 2426 2427 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0] 2428 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) }; 2429 fun->Call(fun, argc, a1); 2430 2431 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0] 2432 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) }; 2433 fun->Call(fun, argc, a2); 2434 2435 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0] 2436 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) }; 2437 fun->Call(fun, argc, a3); 2438 2439 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0] 2440 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) }; 2441 fun->Call(fun, argc, a4); 2442 2443 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0] 2444 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) }; 2445 fun->Call(fun, argc, a5); 2446} 2447 2448 2449v8::Handle<Value> ThrowValue(const v8::Arguments& args) { 2450 ApiTestFuzzer::Fuzz(); 2451 CHECK_EQ(1, args.Length()); 2452 return v8::ThrowException(args[0]); 2453} 2454 2455 2456THREADED_TEST(ThrowValues) { 2457 v8::HandleScope scope; 2458 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2459 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue)); 2460 LocalContext context(0, templ); 2461 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 2462 "function Run(obj) {" 2463 " try {" 2464 " Throw(obj);" 2465 " } catch (e) {" 2466 " return e;" 2467 " }" 2468 " return 'no exception';" 2469 "}" 2470 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];")); 2471 CHECK_EQ(5, result->Length()); 2472 CHECK(result->Get(v8::Integer::New(0))->IsString()); 2473 CHECK(result->Get(v8::Integer::New(1))->IsNumber()); 2474 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value()); 2475 CHECK(result->Get(v8::Integer::New(2))->IsNumber()); 2476 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value()); 2477 CHECK(result->Get(v8::Integer::New(3))->IsNull()); 2478 CHECK(result->Get(v8::Integer::New(4))->IsUndefined()); 2479} 2480 2481 2482THREADED_TEST(CatchZero) { 2483 v8::HandleScope scope; 2484 LocalContext context; 2485 v8::TryCatch try_catch; 2486 CHECK(!try_catch.HasCaught()); 2487 Script::Compile(v8_str("throw 10"))->Run(); 2488 CHECK(try_catch.HasCaught()); 2489 CHECK_EQ(10, try_catch.Exception()->Int32Value()); 2490 try_catch.Reset(); 2491 CHECK(!try_catch.HasCaught()); 2492 Script::Compile(v8_str("throw 0"))->Run(); 2493 CHECK(try_catch.HasCaught()); 2494 CHECK_EQ(0, try_catch.Exception()->Int32Value()); 2495} 2496 2497 2498THREADED_TEST(CatchExceptionFromWith) { 2499 v8::HandleScope scope; 2500 LocalContext context; 2501 v8::TryCatch try_catch; 2502 CHECK(!try_catch.HasCaught()); 2503 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run(); 2504 CHECK(try_catch.HasCaught()); 2505} 2506 2507 2508THREADED_TEST(Equality) { 2509 v8::HandleScope scope; 2510 LocalContext context; 2511 // Check that equality works at all before relying on CHECK_EQ 2512 CHECK(v8_str("a")->Equals(v8_str("a"))); 2513 CHECK(!v8_str("a")->Equals(v8_str("b"))); 2514 2515 CHECK_EQ(v8_str("a"), v8_str("a")); 2516 CHECK_NE(v8_str("a"), v8_str("b")); 2517 CHECK_EQ(v8_num(1), v8_num(1)); 2518 CHECK_EQ(v8_num(1.00), v8_num(1)); 2519 CHECK_NE(v8_num(1), v8_num(2)); 2520 2521 // Assume String is not symbol. 2522 CHECK(v8_str("a")->StrictEquals(v8_str("a"))); 2523 CHECK(!v8_str("a")->StrictEquals(v8_str("b"))); 2524 CHECK(!v8_str("5")->StrictEquals(v8_num(5))); 2525 CHECK(v8_num(1)->StrictEquals(v8_num(1))); 2526 CHECK(!v8_num(1)->StrictEquals(v8_num(2))); 2527 CHECK(v8_num(0)->StrictEquals(v8_num(-0))); 2528 Local<Value> not_a_number = v8_num(i::OS::nan_value()); 2529 CHECK(!not_a_number->StrictEquals(not_a_number)); 2530 CHECK(v8::False()->StrictEquals(v8::False())); 2531 CHECK(!v8::False()->StrictEquals(v8::Undefined())); 2532 2533 v8::Handle<v8::Object> obj = v8::Object::New(); 2534 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj); 2535 CHECK(alias->StrictEquals(obj)); 2536 alias.Dispose(); 2537} 2538 2539 2540THREADED_TEST(MultiRun) { 2541 v8::HandleScope scope; 2542 LocalContext context; 2543 Local<Script> script = Script::Compile(v8_str("x")); 2544 for (int i = 0; i < 10; i++) 2545 script->Run(); 2546} 2547 2548 2549static v8::Handle<Value> GetXValue(Local<String> name, 2550 const AccessorInfo& info) { 2551 ApiTestFuzzer::Fuzz(); 2552 CHECK_EQ(info.Data(), v8_str("donut")); 2553 CHECK_EQ(name, v8_str("x")); 2554 return name; 2555} 2556 2557 2558THREADED_TEST(SimplePropertyRead) { 2559 v8::HandleScope scope; 2560 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2561 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2562 LocalContext context; 2563 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2564 Local<Script> script = Script::Compile(v8_str("obj.x")); 2565 for (int i = 0; i < 10; i++) { 2566 Local<Value> result = script->Run(); 2567 CHECK_EQ(result, v8_str("x")); 2568 } 2569} 2570 2571THREADED_TEST(DefinePropertyOnAPIAccessor) { 2572 v8::HandleScope scope; 2573 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2574 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2575 LocalContext context; 2576 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2577 2578 // Uses getOwnPropertyDescriptor to check the configurable status 2579 Local<Script> script_desc 2580 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( " 2581 "obj, 'x');" 2582 "prop.configurable;")); 2583 Local<Value> result = script_desc->Run(); 2584 CHECK_EQ(result->BooleanValue(), true); 2585 2586 // Redefine get - but still configurable 2587 Local<Script> script_define 2588 = Script::Compile(v8_str("var desc = { get: function(){return 42; }," 2589 " configurable: true };" 2590 "Object.defineProperty(obj, 'x', desc);" 2591 "obj.x")); 2592 result = script_define->Run(); 2593 CHECK_EQ(result, v8_num(42)); 2594 2595 // Check that the accessor is still configurable 2596 result = script_desc->Run(); 2597 CHECK_EQ(result->BooleanValue(), true); 2598 2599 // Redefine to a non-configurable 2600 script_define 2601 = Script::Compile(v8_str("var desc = { get: function(){return 43; }," 2602 " configurable: false };" 2603 "Object.defineProperty(obj, 'x', desc);" 2604 "obj.x")); 2605 result = script_define->Run(); 2606 CHECK_EQ(result, v8_num(43)); 2607 result = script_desc->Run(); 2608 CHECK_EQ(result->BooleanValue(), false); 2609 2610 // Make sure that it is not possible to redefine again 2611 v8::TryCatch try_catch; 2612 result = script_define->Run(); 2613 CHECK(try_catch.HasCaught()); 2614 String::AsciiValue exception_value(try_catch.Exception()); 2615 CHECK_EQ(*exception_value, 2616 "TypeError: Cannot redefine property: defineProperty"); 2617} 2618 2619THREADED_TEST(DefinePropertyOnDefineGetterSetter) { 2620 v8::HandleScope scope; 2621 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2622 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2623 LocalContext context; 2624 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2625 2626 Local<Script> script_desc = Script::Compile(v8_str("var prop =" 2627 "Object.getOwnPropertyDescriptor( " 2628 "obj, 'x');" 2629 "prop.configurable;")); 2630 Local<Value> result = script_desc->Run(); 2631 CHECK_EQ(result->BooleanValue(), true); 2632 2633 Local<Script> script_define = 2634 Script::Compile(v8_str("var desc = {get: function(){return 42; }," 2635 " configurable: true };" 2636 "Object.defineProperty(obj, 'x', desc);" 2637 "obj.x")); 2638 result = script_define->Run(); 2639 CHECK_EQ(result, v8_num(42)); 2640 2641 2642 result = script_desc->Run(); 2643 CHECK_EQ(result->BooleanValue(), true); 2644 2645 2646 script_define = 2647 Script::Compile(v8_str("var desc = {get: function(){return 43; }," 2648 " configurable: false };" 2649 "Object.defineProperty(obj, 'x', desc);" 2650 "obj.x")); 2651 result = script_define->Run(); 2652 CHECK_EQ(result, v8_num(43)); 2653 result = script_desc->Run(); 2654 2655 CHECK_EQ(result->BooleanValue(), false); 2656 2657 v8::TryCatch try_catch; 2658 result = script_define->Run(); 2659 CHECK(try_catch.HasCaught()); 2660 String::AsciiValue exception_value(try_catch.Exception()); 2661 CHECK_EQ(*exception_value, 2662 "TypeError: Cannot redefine property: defineProperty"); 2663} 2664 2665 2666static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context, 2667 char const* name) { 2668 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name))); 2669} 2670 2671 2672THREADED_TEST(DefineAPIAccessorOnObject) { 2673 v8::HandleScope scope; 2674 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2675 LocalContext context; 2676 2677 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 2678 CompileRun("var obj2 = {};"); 2679 2680 CHECK(CompileRun("obj1.x")->IsUndefined()); 2681 CHECK(CompileRun("obj2.x")->IsUndefined()); 2682 2683 CHECK(GetGlobalProperty(&context, "obj1")-> 2684 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2685 2686 ExpectString("obj1.x", "x"); 2687 CHECK(CompileRun("obj2.x")->IsUndefined()); 2688 2689 CHECK(GetGlobalProperty(&context, "obj2")-> 2690 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2691 2692 ExpectString("obj1.x", "x"); 2693 ExpectString("obj2.x", "x"); 2694 2695 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2696 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2697 2698 CompileRun("Object.defineProperty(obj1, 'x'," 2699 "{ get: function() { return 'y'; }, configurable: true })"); 2700 2701 ExpectString("obj1.x", "y"); 2702 ExpectString("obj2.x", "x"); 2703 2704 CompileRun("Object.defineProperty(obj2, 'x'," 2705 "{ get: function() { return 'y'; }, configurable: true })"); 2706 2707 ExpectString("obj1.x", "y"); 2708 ExpectString("obj2.x", "y"); 2709 2710 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2711 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2712 2713 CHECK(GetGlobalProperty(&context, "obj1")-> 2714 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2715 CHECK(GetGlobalProperty(&context, "obj2")-> 2716 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2717 2718 ExpectString("obj1.x", "x"); 2719 ExpectString("obj2.x", "x"); 2720 2721 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2722 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2723 2724 // Define getters/setters, but now make them not configurable. 2725 CompileRun("Object.defineProperty(obj1, 'x'," 2726 "{ get: function() { return 'z'; }, configurable: false })"); 2727 CompileRun("Object.defineProperty(obj2, 'x'," 2728 "{ get: function() { return 'z'; }, configurable: false })"); 2729 2730 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2731 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2732 2733 ExpectString("obj1.x", "z"); 2734 ExpectString("obj2.x", "z"); 2735 2736 CHECK(!GetGlobalProperty(&context, "obj1")-> 2737 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2738 CHECK(!GetGlobalProperty(&context, "obj2")-> 2739 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2740 2741 ExpectString("obj1.x", "z"); 2742 ExpectString("obj2.x", "z"); 2743} 2744 2745 2746THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) { 2747 v8::HandleScope scope; 2748 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2749 LocalContext context; 2750 2751 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 2752 CompileRun("var obj2 = {};"); 2753 2754 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 2755 v8_str("x"), 2756 GetXValue, NULL, 2757 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 2758 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 2759 v8_str("x"), 2760 GetXValue, NULL, 2761 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 2762 2763 ExpectString("obj1.x", "x"); 2764 ExpectString("obj2.x", "x"); 2765 2766 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2767 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2768 2769 CHECK(!GetGlobalProperty(&context, "obj1")-> 2770 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2771 CHECK(!GetGlobalProperty(&context, "obj2")-> 2772 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2773 2774 { 2775 v8::TryCatch try_catch; 2776 CompileRun("Object.defineProperty(obj1, 'x'," 2777 "{get: function() { return 'func'; }})"); 2778 CHECK(try_catch.HasCaught()); 2779 String::AsciiValue exception_value(try_catch.Exception()); 2780 CHECK_EQ(*exception_value, 2781 "TypeError: Cannot redefine property: defineProperty"); 2782 } 2783 { 2784 v8::TryCatch try_catch; 2785 CompileRun("Object.defineProperty(obj2, 'x'," 2786 "{get: function() { return 'func'; }})"); 2787 CHECK(try_catch.HasCaught()); 2788 String::AsciiValue exception_value(try_catch.Exception()); 2789 CHECK_EQ(*exception_value, 2790 "TypeError: Cannot redefine property: defineProperty"); 2791 } 2792} 2793 2794 2795static v8::Handle<Value> Get239Value(Local<String> name, 2796 const AccessorInfo& info) { 2797 ApiTestFuzzer::Fuzz(); 2798 CHECK_EQ(info.Data(), v8_str("donut")); 2799 CHECK_EQ(name, v8_str("239")); 2800 return name; 2801} 2802 2803 2804THREADED_TEST(ElementAPIAccessor) { 2805 v8::HandleScope scope; 2806 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2807 LocalContext context; 2808 2809 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 2810 CompileRun("var obj2 = {};"); 2811 2812 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 2813 v8_str("239"), 2814 Get239Value, NULL, 2815 v8_str("donut"))); 2816 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 2817 v8_str("239"), 2818 Get239Value, NULL, 2819 v8_str("donut"))); 2820 2821 ExpectString("obj1[239]", "239"); 2822 ExpectString("obj2[239]", "239"); 2823 ExpectString("obj1['239']", "239"); 2824 ExpectString("obj2['239']", "239"); 2825} 2826 2827 2828v8::Persistent<Value> xValue; 2829 2830 2831static void SetXValue(Local<String> name, 2832 Local<Value> value, 2833 const AccessorInfo& info) { 2834 CHECK_EQ(value, v8_num(4)); 2835 CHECK_EQ(info.Data(), v8_str("donut")); 2836 CHECK_EQ(name, v8_str("x")); 2837 CHECK(xValue.IsEmpty()); 2838 xValue = v8::Persistent<Value>::New(value); 2839} 2840 2841 2842THREADED_TEST(SimplePropertyWrite) { 2843 v8::HandleScope scope; 2844 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2845 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut")); 2846 LocalContext context; 2847 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2848 Local<Script> script = Script::Compile(v8_str("obj.x = 4")); 2849 for (int i = 0; i < 10; i++) { 2850 CHECK(xValue.IsEmpty()); 2851 script->Run(); 2852 CHECK_EQ(v8_num(4), xValue); 2853 xValue.Dispose(); 2854 xValue = v8::Persistent<Value>(); 2855 } 2856} 2857 2858 2859static v8::Handle<Value> XPropertyGetter(Local<String> property, 2860 const AccessorInfo& info) { 2861 ApiTestFuzzer::Fuzz(); 2862 CHECK(info.Data()->IsUndefined()); 2863 return property; 2864} 2865 2866 2867THREADED_TEST(NamedInterceptorPropertyRead) { 2868 v8::HandleScope scope; 2869 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2870 templ->SetNamedPropertyHandler(XPropertyGetter); 2871 LocalContext context; 2872 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2873 Local<Script> script = Script::Compile(v8_str("obj.x")); 2874 for (int i = 0; i < 10; i++) { 2875 Local<Value> result = script->Run(); 2876 CHECK_EQ(result, v8_str("x")); 2877 } 2878} 2879 2880 2881THREADED_TEST(NamedInterceptorDictionaryIC) { 2882 v8::HandleScope scope; 2883 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2884 templ->SetNamedPropertyHandler(XPropertyGetter); 2885 LocalContext context; 2886 // Create an object with a named interceptor. 2887 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance()); 2888 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x")); 2889 for (int i = 0; i < 10; i++) { 2890 Local<Value> result = script->Run(); 2891 CHECK_EQ(result, v8_str("x")); 2892 } 2893 // Create a slow case object and a function accessing a property in 2894 // that slow case object (with dictionary probing in generated 2895 // code). Then force object with a named interceptor into slow-case, 2896 // pass it to the function, and check that the interceptor is called 2897 // instead of accessing the local property. 2898 Local<Value> result = 2899 CompileRun("function get_x(o) { return o.x; };" 2900 "var obj = { x : 42, y : 0 };" 2901 "delete obj.y;" 2902 "for (var i = 0; i < 10; i++) get_x(obj);" 2903 "interceptor_obj.x = 42;" 2904 "interceptor_obj.y = 10;" 2905 "delete interceptor_obj.y;" 2906 "get_x(interceptor_obj)"); 2907 CHECK_EQ(result, v8_str("x")); 2908} 2909 2910 2911static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property, 2912 const AccessorInfo& info) { 2913 // Set x on the prototype object and do not handle the get request. 2914 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype(); 2915 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23)); 2916 return v8::Handle<Value>(); 2917} 2918 2919 2920// This is a regression test for http://crbug.com/20104. Map 2921// transitions should not interfere with post interceptor lookup. 2922THREADED_TEST(NamedInterceptorMapTransitionRead) { 2923 v8::HandleScope scope; 2924 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(); 2925 Local<v8::ObjectTemplate> instance_template 2926 = function_template->InstanceTemplate(); 2927 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter); 2928 LocalContext context; 2929 context->Global()->Set(v8_str("F"), function_template->GetFunction()); 2930 // Create an instance of F and introduce a map transition for x. 2931 CompileRun("var o = new F(); o.x = 23;"); 2932 // Create an instance of F and invoke the getter. The result should be 23. 2933 Local<Value> result = CompileRun("o = new F(); o.x"); 2934 CHECK_EQ(result->Int32Value(), 23); 2935} 2936 2937 2938static v8::Handle<Value> IndexedPropertyGetter(uint32_t index, 2939 const AccessorInfo& info) { 2940 ApiTestFuzzer::Fuzz(); 2941 if (index == 37) { 2942 return v8::Handle<Value>(v8_num(625)); 2943 } 2944 return v8::Handle<Value>(); 2945} 2946 2947 2948static v8::Handle<Value> IndexedPropertySetter(uint32_t index, 2949 Local<Value> value, 2950 const AccessorInfo& info) { 2951 ApiTestFuzzer::Fuzz(); 2952 if (index == 39) { 2953 return value; 2954 } 2955 return v8::Handle<Value>(); 2956} 2957 2958 2959THREADED_TEST(IndexedInterceptorWithIndexedAccessor) { 2960 v8::HandleScope scope; 2961 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2962 templ->SetIndexedPropertyHandler(IndexedPropertyGetter, 2963 IndexedPropertySetter); 2964 LocalContext context; 2965 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2966 Local<Script> getter_script = Script::Compile(v8_str( 2967 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];")); 2968 Local<Script> setter_script = Script::Compile(v8_str( 2969 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});" 2970 "obj[17] = 23;" 2971 "obj.foo;")); 2972 Local<Script> interceptor_setter_script = Script::Compile(v8_str( 2973 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});" 2974 "obj[39] = 47;" 2975 "obj.foo;")); // This setter should not run, due to the interceptor. 2976 Local<Script> interceptor_getter_script = Script::Compile(v8_str( 2977 "obj[37];")); 2978 Local<Value> result = getter_script->Run(); 2979 CHECK_EQ(v8_num(5), result); 2980 result = setter_script->Run(); 2981 CHECK_EQ(v8_num(23), result); 2982 result = interceptor_setter_script->Run(); 2983 CHECK_EQ(v8_num(23), result); 2984 result = interceptor_getter_script->Run(); 2985 CHECK_EQ(v8_num(625), result); 2986} 2987 2988 2989static v8::Handle<Value> IdentityIndexedPropertyGetter( 2990 uint32_t index, 2991 const AccessorInfo& info) { 2992 return v8::Integer::New(index); 2993} 2994 2995 2996THREADED_TEST(IndexedInterceptorWithNoSetter) { 2997 v8::HandleScope scope; 2998 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2999 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3000 3001 LocalContext context; 3002 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 3003 3004 const char* code = 3005 "try {" 3006 " obj[0] = 239;" 3007 " for (var i = 0; i < 100; i++) {" 3008 " var v = obj[0];" 3009 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;" 3010 " }" 3011 " 'PASSED'" 3012 "} catch(e) {" 3013 " e" 3014 "}"; 3015 ExpectString(code, "PASSED"); 3016} 3017 3018 3019THREADED_TEST(IndexedInterceptorWithAccessorCheck) { 3020 v8::HandleScope scope; 3021 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3022 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3023 3024 LocalContext context; 3025 Local<v8::Object> obj = templ->NewInstance(); 3026 obj->TurnOnAccessCheck(); 3027 context->Global()->Set(v8_str("obj"), obj); 3028 3029 const char* code = 3030 "try {" 3031 " for (var i = 0; i < 100; i++) {" 3032 " var v = obj[0];" 3033 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;" 3034 " }" 3035 " 'PASSED'" 3036 "} catch(e) {" 3037 " e" 3038 "}"; 3039 ExpectString(code, "PASSED"); 3040} 3041 3042 3043THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) { 3044 i::FLAG_allow_natives_syntax = true; 3045 v8::HandleScope scope; 3046 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3047 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3048 3049 LocalContext context; 3050 Local<v8::Object> obj = templ->NewInstance(); 3051 context->Global()->Set(v8_str("obj"), obj); 3052 3053 const char* code = 3054 "try {" 3055 " for (var i = 0; i < 100; i++) {" 3056 " var expected = i;" 3057 " if (i == 5) {" 3058 " %EnableAccessChecks(obj);" 3059 " expected = undefined;" 3060 " }" 3061 " var v = obj[i];" 3062 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3063 " if (i == 5) %DisableAccessChecks(obj);" 3064 " }" 3065 " 'PASSED'" 3066 "} catch(e) {" 3067 " e" 3068 "}"; 3069 ExpectString(code, "PASSED"); 3070} 3071 3072 3073THREADED_TEST(IndexedInterceptorWithDifferentIndices) { 3074 v8::HandleScope scope; 3075 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3076 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3077 3078 LocalContext context; 3079 Local<v8::Object> obj = templ->NewInstance(); 3080 context->Global()->Set(v8_str("obj"), obj); 3081 3082 const char* code = 3083 "try {" 3084 " for (var i = 0; i < 100; i++) {" 3085 " var v = obj[i];" 3086 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 3087 " }" 3088 " 'PASSED'" 3089 "} catch(e) {" 3090 " e" 3091 "}"; 3092 ExpectString(code, "PASSED"); 3093} 3094 3095 3096THREADED_TEST(IndexedInterceptorWithNotSmiLookup) { 3097 v8::HandleScope scope; 3098 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3099 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3100 3101 LocalContext context; 3102 Local<v8::Object> obj = templ->NewInstance(); 3103 context->Global()->Set(v8_str("obj"), obj); 3104 3105 const char* code = 3106 "try {" 3107 " for (var i = 0; i < 100; i++) {" 3108 " var expected = i;" 3109 " if (i == 50) {" 3110 " i = 'foobar';" 3111 " expected = undefined;" 3112 " }" 3113 " var v = obj[i];" 3114 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3115 " }" 3116 " 'PASSED'" 3117 "} catch(e) {" 3118 " e" 3119 "}"; 3120 ExpectString(code, "PASSED"); 3121} 3122 3123 3124THREADED_TEST(IndexedInterceptorGoingMegamorphic) { 3125 v8::HandleScope scope; 3126 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3127 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3128 3129 LocalContext context; 3130 Local<v8::Object> obj = templ->NewInstance(); 3131 context->Global()->Set(v8_str("obj"), obj); 3132 3133 const char* code = 3134 "var original = obj;" 3135 "try {" 3136 " for (var i = 0; i < 100; i++) {" 3137 " var expected = i;" 3138 " if (i == 50) {" 3139 " obj = {50: 'foobar'};" 3140 " expected = 'foobar';" 3141 " }" 3142 " var v = obj[i];" 3143 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3144 " if (i == 50) obj = original;" 3145 " }" 3146 " 'PASSED'" 3147 "} catch(e) {" 3148 " e" 3149 "}"; 3150 ExpectString(code, "PASSED"); 3151} 3152 3153 3154THREADED_TEST(IndexedInterceptorReceiverTurningSmi) { 3155 v8::HandleScope scope; 3156 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3157 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3158 3159 LocalContext context; 3160 Local<v8::Object> obj = templ->NewInstance(); 3161 context->Global()->Set(v8_str("obj"), obj); 3162 3163 const char* code = 3164 "var original = obj;" 3165 "try {" 3166 " for (var i = 0; i < 100; i++) {" 3167 " var expected = i;" 3168 " if (i == 5) {" 3169 " obj = 239;" 3170 " expected = undefined;" 3171 " }" 3172 " var v = obj[i];" 3173 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3174 " if (i == 5) obj = original;" 3175 " }" 3176 " 'PASSED'" 3177 "} catch(e) {" 3178 " e" 3179 "}"; 3180 ExpectString(code, "PASSED"); 3181} 3182 3183 3184THREADED_TEST(IndexedInterceptorOnProto) { 3185 v8::HandleScope scope; 3186 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3187 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3188 3189 LocalContext context; 3190 Local<v8::Object> obj = templ->NewInstance(); 3191 context->Global()->Set(v8_str("obj"), obj); 3192 3193 const char* code = 3194 "var o = {__proto__: obj};" 3195 "try {" 3196 " for (var i = 0; i < 100; i++) {" 3197 " var v = o[i];" 3198 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 3199 " }" 3200 " 'PASSED'" 3201 "} catch(e) {" 3202 " e" 3203 "}"; 3204 ExpectString(code, "PASSED"); 3205} 3206 3207 3208THREADED_TEST(MultiContexts) { 3209 v8::HandleScope scope; 3210 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New(); 3211 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler)); 3212 3213 Local<String> password = v8_str("Password"); 3214 3215 // Create an environment 3216 LocalContext context0(0, templ); 3217 context0->SetSecurityToken(password); 3218 v8::Handle<v8::Object> global0 = context0->Global(); 3219 global0->Set(v8_str("custom"), v8_num(1234)); 3220 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 3221 3222 // Create an independent environment 3223 LocalContext context1(0, templ); 3224 context1->SetSecurityToken(password); 3225 v8::Handle<v8::Object> global1 = context1->Global(); 3226 global1->Set(v8_str("custom"), v8_num(1234)); 3227 CHECK_NE(global0, global1); 3228 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 3229 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value()); 3230 3231 // Now create a new context with the old global 3232 LocalContext context2(0, templ, global1); 3233 context2->SetSecurityToken(password); 3234 v8::Handle<v8::Object> global2 = context2->Global(); 3235 CHECK_EQ(global1, global2); 3236 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value()); 3237 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value()); 3238} 3239 3240 3241THREADED_TEST(FunctionPrototypeAcrossContexts) { 3242 // Make sure that functions created by cloning boilerplates cannot 3243 // communicate through their __proto__ field. 3244 3245 v8::HandleScope scope; 3246 3247 LocalContext env0; 3248 v8::Handle<v8::Object> global0 = 3249 env0->Global(); 3250 v8::Handle<v8::Object> object0 = 3251 global0->Get(v8_str("Object")).As<v8::Object>(); 3252 v8::Handle<v8::Object> tostring0 = 3253 object0->Get(v8_str("toString")).As<v8::Object>(); 3254 v8::Handle<v8::Object> proto0 = 3255 tostring0->Get(v8_str("__proto__")).As<v8::Object>(); 3256 proto0->Set(v8_str("custom"), v8_num(1234)); 3257 3258 LocalContext env1; 3259 v8::Handle<v8::Object> global1 = 3260 env1->Global(); 3261 v8::Handle<v8::Object> object1 = 3262 global1->Get(v8_str("Object")).As<v8::Object>(); 3263 v8::Handle<v8::Object> tostring1 = 3264 object1->Get(v8_str("toString")).As<v8::Object>(); 3265 v8::Handle<v8::Object> proto1 = 3266 tostring1->Get(v8_str("__proto__")).As<v8::Object>(); 3267 CHECK(!proto1->Has(v8_str("custom"))); 3268} 3269 3270 3271THREADED_TEST(Regress892105) { 3272 // Make sure that object and array literals created by cloning 3273 // boilerplates cannot communicate through their __proto__ 3274 // field. This is rather difficult to check, but we try to add stuff 3275 // to Object.prototype and Array.prototype and create a new 3276 // environment. This should succeed. 3277 3278 v8::HandleScope scope; 3279 3280 Local<String> source = v8_str("Object.prototype.obj = 1234;" 3281 "Array.prototype.arr = 4567;" 3282 "8901"); 3283 3284 LocalContext env0; 3285 Local<Script> script0 = Script::Compile(source); 3286 CHECK_EQ(8901.0, script0->Run()->NumberValue()); 3287 3288 LocalContext env1; 3289 Local<Script> script1 = Script::Compile(source); 3290 CHECK_EQ(8901.0, script1->Run()->NumberValue()); 3291} 3292 3293 3294THREADED_TEST(UndetectableObject) { 3295 v8::HandleScope scope; 3296 LocalContext env; 3297 3298 Local<v8::FunctionTemplate> desc = 3299 v8::FunctionTemplate::New(0, v8::Handle<Value>()); 3300 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 3301 3302 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 3303 env->Global()->Set(v8_str("undetectable"), obj); 3304 3305 ExpectString("undetectable.toString()", "[object Object]"); 3306 ExpectString("typeof undetectable", "undefined"); 3307 ExpectString("typeof(undetectable)", "undefined"); 3308 ExpectBoolean("typeof undetectable == 'undefined'", true); 3309 ExpectBoolean("typeof undetectable == 'object'", false); 3310 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 3311 ExpectBoolean("!undetectable", true); 3312 3313 ExpectObject("true&&undetectable", obj); 3314 ExpectBoolean("false&&undetectable", false); 3315 ExpectBoolean("true||undetectable", true); 3316 ExpectObject("false||undetectable", obj); 3317 3318 ExpectObject("undetectable&&true", obj); 3319 ExpectObject("undetectable&&false", obj); 3320 ExpectBoolean("undetectable||true", true); 3321 ExpectBoolean("undetectable||false", false); 3322 3323 ExpectBoolean("undetectable==null", true); 3324 ExpectBoolean("null==undetectable", true); 3325 ExpectBoolean("undetectable==undefined", true); 3326 ExpectBoolean("undefined==undetectable", true); 3327 ExpectBoolean("undetectable==undetectable", true); 3328 3329 3330 ExpectBoolean("undetectable===null", false); 3331 ExpectBoolean("null===undetectable", false); 3332 ExpectBoolean("undetectable===undefined", false); 3333 ExpectBoolean("undefined===undetectable", false); 3334 ExpectBoolean("undetectable===undetectable", true); 3335} 3336 3337 3338THREADED_TEST(UndetectableString) { 3339 v8::HandleScope scope; 3340 LocalContext env; 3341 3342 Local<String> obj = String::NewUndetectable("foo"); 3343 env->Global()->Set(v8_str("undetectable"), obj); 3344 3345 ExpectString("undetectable", "foo"); 3346 ExpectString("typeof undetectable", "undefined"); 3347 ExpectString("typeof(undetectable)", "undefined"); 3348 ExpectBoolean("typeof undetectable == 'undefined'", true); 3349 ExpectBoolean("typeof undetectable == 'string'", false); 3350 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 3351 ExpectBoolean("!undetectable", true); 3352 3353 ExpectObject("true&&undetectable", obj); 3354 ExpectBoolean("false&&undetectable", false); 3355 ExpectBoolean("true||undetectable", true); 3356 ExpectObject("false||undetectable", obj); 3357 3358 ExpectObject("undetectable&&true", obj); 3359 ExpectObject("undetectable&&false", obj); 3360 ExpectBoolean("undetectable||true", true); 3361 ExpectBoolean("undetectable||false", false); 3362 3363 ExpectBoolean("undetectable==null", true); 3364 ExpectBoolean("null==undetectable", true); 3365 ExpectBoolean("undetectable==undefined", true); 3366 ExpectBoolean("undefined==undetectable", true); 3367 ExpectBoolean("undetectable==undetectable", true); 3368 3369 3370 ExpectBoolean("undetectable===null", false); 3371 ExpectBoolean("null===undetectable", false); 3372 ExpectBoolean("undetectable===undefined", false); 3373 ExpectBoolean("undefined===undetectable", false); 3374 ExpectBoolean("undetectable===undetectable", true); 3375} 3376 3377 3378template <typename T> static void USE(T) { } 3379 3380 3381// This test is not intended to be run, just type checked. 3382static void PersistentHandles() { 3383 USE(PersistentHandles); 3384 Local<String> str = v8_str("foo"); 3385 v8::Persistent<String> p_str = v8::Persistent<String>::New(str); 3386 USE(p_str); 3387 Local<Script> scr = Script::Compile(v8_str("")); 3388 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr); 3389 USE(p_scr); 3390 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3391 v8::Persistent<ObjectTemplate> p_templ = 3392 v8::Persistent<ObjectTemplate>::New(templ); 3393 USE(p_templ); 3394} 3395 3396 3397static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) { 3398 ApiTestFuzzer::Fuzz(); 3399 return v8::Undefined(); 3400} 3401 3402 3403THREADED_TEST(GlobalObjectTemplate) { 3404 v8::HandleScope handle_scope; 3405 Local<ObjectTemplate> global_template = ObjectTemplate::New(); 3406 global_template->Set(v8_str("JSNI_Log"), 3407 v8::FunctionTemplate::New(HandleLogDelegator)); 3408 v8::Persistent<Context> context = Context::New(0, global_template); 3409 Context::Scope context_scope(context); 3410 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run(); 3411 context.Dispose(); 3412} 3413 3414 3415static const char* kSimpleExtensionSource = 3416 "function Foo() {" 3417 " return 4;" 3418 "}"; 3419 3420 3421THREADED_TEST(SimpleExtensions) { 3422 v8::HandleScope handle_scope; 3423 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource)); 3424 const char* extension_names[] = { "simpletest" }; 3425 v8::ExtensionConfiguration extensions(1, extension_names); 3426 v8::Handle<Context> context = Context::New(&extensions); 3427 Context::Scope lock(context); 3428 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 3429 CHECK_EQ(result, v8::Integer::New(4)); 3430} 3431 3432 3433static const char* kEvalExtensionSource1 = 3434 "function UseEval1() {" 3435 " var x = 42;" 3436 " return eval('x');" 3437 "}"; 3438 3439 3440static const char* kEvalExtensionSource2 = 3441 "(function() {" 3442 " var x = 42;" 3443 " function e() {" 3444 " return eval('x');" 3445 " }" 3446 " this.UseEval2 = e;" 3447 "})()"; 3448 3449 3450THREADED_TEST(UseEvalFromExtension) { 3451 v8::HandleScope handle_scope; 3452 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1)); 3453 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2)); 3454 const char* extension_names[] = { "evaltest1", "evaltest2" }; 3455 v8::ExtensionConfiguration extensions(2, extension_names); 3456 v8::Handle<Context> context = Context::New(&extensions); 3457 Context::Scope lock(context); 3458 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run(); 3459 CHECK_EQ(result, v8::Integer::New(42)); 3460 result = Script::Compile(v8_str("UseEval2()"))->Run(); 3461 CHECK_EQ(result, v8::Integer::New(42)); 3462} 3463 3464 3465static const char* kWithExtensionSource1 = 3466 "function UseWith1() {" 3467 " var x = 42;" 3468 " with({x:87}) { return x; }" 3469 "}"; 3470 3471 3472 3473static const char* kWithExtensionSource2 = 3474 "(function() {" 3475 " var x = 42;" 3476 " function e() {" 3477 " with ({x:87}) { return x; }" 3478 " }" 3479 " this.UseWith2 = e;" 3480 "})()"; 3481 3482 3483THREADED_TEST(UseWithFromExtension) { 3484 v8::HandleScope handle_scope; 3485 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1)); 3486 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2)); 3487 const char* extension_names[] = { "withtest1", "withtest2" }; 3488 v8::ExtensionConfiguration extensions(2, extension_names); 3489 v8::Handle<Context> context = Context::New(&extensions); 3490 Context::Scope lock(context); 3491 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run(); 3492 CHECK_EQ(result, v8::Integer::New(87)); 3493 result = Script::Compile(v8_str("UseWith2()"))->Run(); 3494 CHECK_EQ(result, v8::Integer::New(87)); 3495} 3496 3497 3498THREADED_TEST(AutoExtensions) { 3499 v8::HandleScope handle_scope; 3500 Extension* extension = new Extension("autotest", kSimpleExtensionSource); 3501 extension->set_auto_enable(true); 3502 v8::RegisterExtension(extension); 3503 v8::Handle<Context> context = Context::New(); 3504 Context::Scope lock(context); 3505 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 3506 CHECK_EQ(result, v8::Integer::New(4)); 3507} 3508 3509 3510static const char* kSyntaxErrorInExtensionSource = 3511 "["; 3512 3513 3514// Test that a syntax error in an extension does not cause a fatal 3515// error but results in an empty context. 3516THREADED_TEST(SyntaxErrorExtensions) { 3517 v8::HandleScope handle_scope; 3518 v8::RegisterExtension(new Extension("syntaxerror", 3519 kSyntaxErrorInExtensionSource)); 3520 const char* extension_names[] = { "syntaxerror" }; 3521 v8::ExtensionConfiguration extensions(1, extension_names); 3522 v8::Handle<Context> context = Context::New(&extensions); 3523 CHECK(context.IsEmpty()); 3524} 3525 3526 3527static const char* kExceptionInExtensionSource = 3528 "throw 42"; 3529 3530 3531// Test that an exception when installing an extension does not cause 3532// a fatal error but results in an empty context. 3533THREADED_TEST(ExceptionExtensions) { 3534 v8::HandleScope handle_scope; 3535 v8::RegisterExtension(new Extension("exception", 3536 kExceptionInExtensionSource)); 3537 const char* extension_names[] = { "exception" }; 3538 v8::ExtensionConfiguration extensions(1, extension_names); 3539 v8::Handle<Context> context = Context::New(&extensions); 3540 CHECK(context.IsEmpty()); 3541} 3542 3543 3544static void CheckDependencies(const char* name, const char* expected) { 3545 v8::HandleScope handle_scope; 3546 v8::ExtensionConfiguration config(1, &name); 3547 LocalContext context(&config); 3548 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded"))); 3549} 3550 3551 3552/* 3553 * Configuration: 3554 * 3555 * /-- B <--\ 3556 * A <- -- D <-- E 3557 * \-- C <--/ 3558 */ 3559THREADED_TEST(ExtensionDependency) { 3560 static const char* kEDeps[] = { "D" }; 3561 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps)); 3562 static const char* kDDeps[] = { "B", "C" }; 3563 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps)); 3564 static const char* kBCDeps[] = { "A" }; 3565 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps)); 3566 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps)); 3567 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';")); 3568 CheckDependencies("A", "undefinedA"); 3569 CheckDependencies("B", "undefinedAB"); 3570 CheckDependencies("C", "undefinedAC"); 3571 CheckDependencies("D", "undefinedABCD"); 3572 CheckDependencies("E", "undefinedABCDE"); 3573 v8::HandleScope handle_scope; 3574 static const char* exts[2] = { "C", "E" }; 3575 v8::ExtensionConfiguration config(2, exts); 3576 LocalContext context(&config); 3577 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded"))); 3578} 3579 3580 3581static const char* kExtensionTestScript = 3582 "native function A();" 3583 "native function B();" 3584 "native function C();" 3585 "function Foo(i) {" 3586 " if (i == 0) return A();" 3587 " if (i == 1) return B();" 3588 " if (i == 2) return C();" 3589 "}"; 3590 3591 3592static v8::Handle<Value> CallFun(const v8::Arguments& args) { 3593 ApiTestFuzzer::Fuzz(); 3594 if (args.IsConstructCall()) { 3595 args.This()->Set(v8_str("data"), args.Data()); 3596 return v8::Null(); 3597 } 3598 return args.Data(); 3599} 3600 3601 3602class FunctionExtension : public Extension { 3603 public: 3604 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { } 3605 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( 3606 v8::Handle<String> name); 3607}; 3608 3609 3610static int lookup_count = 0; 3611v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction( 3612 v8::Handle<String> name) { 3613 lookup_count++; 3614 if (name->Equals(v8_str("A"))) { 3615 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8)); 3616 } else if (name->Equals(v8_str("B"))) { 3617 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7)); 3618 } else if (name->Equals(v8_str("C"))) { 3619 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6)); 3620 } else { 3621 return v8::Handle<v8::FunctionTemplate>(); 3622 } 3623} 3624 3625 3626THREADED_TEST(FunctionLookup) { 3627 v8::RegisterExtension(new FunctionExtension()); 3628 v8::HandleScope handle_scope; 3629 static const char* exts[1] = { "functiontest" }; 3630 v8::ExtensionConfiguration config(1, exts); 3631 LocalContext context(&config); 3632 CHECK_EQ(3, lookup_count); 3633 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run()); 3634 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run()); 3635 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run()); 3636} 3637 3638 3639THREADED_TEST(NativeFunctionConstructCall) { 3640 v8::RegisterExtension(new FunctionExtension()); 3641 v8::HandleScope handle_scope; 3642 static const char* exts[1] = { "functiontest" }; 3643 v8::ExtensionConfiguration config(1, exts); 3644 LocalContext context(&config); 3645 for (int i = 0; i < 10; i++) { 3646 // Run a few times to ensure that allocation of objects doesn't 3647 // change behavior of a constructor function. 3648 CHECK_EQ(v8::Integer::New(8), 3649 Script::Compile(v8_str("(new A()).data"))->Run()); 3650 CHECK_EQ(v8::Integer::New(7), 3651 Script::Compile(v8_str("(new B()).data"))->Run()); 3652 CHECK_EQ(v8::Integer::New(6), 3653 Script::Compile(v8_str("(new C()).data"))->Run()); 3654 } 3655} 3656 3657 3658static const char* last_location; 3659static const char* last_message; 3660void StoringErrorCallback(const char* location, const char* message) { 3661 if (last_location == NULL) { 3662 last_location = location; 3663 last_message = message; 3664 } 3665} 3666 3667 3668// ErrorReporting creates a circular extensions configuration and 3669// tests that the fatal error handler gets called. This renders V8 3670// unusable and therefore this test cannot be run in parallel. 3671TEST(ErrorReporting) { 3672 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 3673 static const char* aDeps[] = { "B" }; 3674 v8::RegisterExtension(new Extension("A", "", 1, aDeps)); 3675 static const char* bDeps[] = { "A" }; 3676 v8::RegisterExtension(new Extension("B", "", 1, bDeps)); 3677 last_location = NULL; 3678 v8::ExtensionConfiguration config(1, bDeps); 3679 v8::Handle<Context> context = Context::New(&config); 3680 CHECK(context.IsEmpty()); 3681 CHECK_NE(last_location, NULL); 3682} 3683 3684 3685static const char* js_code_causing_huge_string_flattening = 3686 "var str = 'X';" 3687 "for (var i = 0; i < 30; i++) {" 3688 " str = str + str;" 3689 "}" 3690 "str.match(/X/);"; 3691 3692 3693void OOMCallback(const char* location, const char* message) { 3694 exit(0); 3695} 3696 3697 3698TEST(RegexpOutOfMemory) { 3699 // Execute a script that causes out of memory when flattening a string. 3700 v8::HandleScope scope; 3701 v8::V8::SetFatalErrorHandler(OOMCallback); 3702 LocalContext context; 3703 Local<Script> script = 3704 Script::Compile(String::New(js_code_causing_huge_string_flattening)); 3705 last_location = NULL; 3706 Local<Value> result = script->Run(); 3707 3708 CHECK(false); // Should not return. 3709} 3710 3711 3712static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message, 3713 v8::Handle<Value> data) { 3714 CHECK_EQ(v8::Undefined(), data); 3715 CHECK(message->GetScriptResourceName()->IsUndefined()); 3716 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName()); 3717 message->GetLineNumber(); 3718 message->GetSourceLine(); 3719} 3720 3721 3722THREADED_TEST(ErrorWithMissingScriptInfo) { 3723 v8::HandleScope scope; 3724 LocalContext context; 3725 v8::V8::AddMessageListener(MissingScriptInfoMessageListener); 3726 Script::Compile(v8_str("throw Error()"))->Run(); 3727 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener); 3728} 3729 3730 3731int global_index = 0; 3732 3733class Snorkel { 3734 public: 3735 Snorkel() { index_ = global_index++; } 3736 int index_; 3737}; 3738 3739class Whammy { 3740 public: 3741 Whammy() { 3742 cursor_ = 0; 3743 } 3744 ~Whammy() { 3745 script_.Dispose(); 3746 } 3747 v8::Handle<Script> getScript() { 3748 if (script_.IsEmpty()) 3749 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo")); 3750 return Local<Script>(*script_); 3751 } 3752 3753 public: 3754 static const int kObjectCount = 256; 3755 int cursor_; 3756 v8::Persistent<v8::Object> objects_[kObjectCount]; 3757 v8::Persistent<Script> script_; 3758}; 3759 3760static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) { 3761 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data); 3762 delete snorkel; 3763 obj.ClearWeak(); 3764} 3765 3766v8::Handle<Value> WhammyPropertyGetter(Local<String> name, 3767 const AccessorInfo& info) { 3768 Whammy* whammy = 3769 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); 3770 3771 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_]; 3772 3773 v8::Handle<v8::Object> obj = v8::Object::New(); 3774 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj); 3775 if (!prev.IsEmpty()) { 3776 prev->Set(v8_str("next"), obj); 3777 prev.MakeWeak(new Snorkel(), &HandleWeakReference); 3778 whammy->objects_[whammy->cursor_].Clear(); 3779 } 3780 whammy->objects_[whammy->cursor_] = global; 3781 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount; 3782 return whammy->getScript()->Run(); 3783} 3784 3785THREADED_TEST(WeakReference) { 3786 v8::HandleScope handle_scope; 3787 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New(); 3788 templ->SetNamedPropertyHandler(WhammyPropertyGetter, 3789 0, 0, 0, 0, 3790 v8::External::New(new Whammy())); 3791 const char* extension_list[] = { "v8/gc" }; 3792 v8::ExtensionConfiguration extensions(1, extension_list); 3793 v8::Persistent<Context> context = Context::New(&extensions); 3794 Context::Scope context_scope(context); 3795 3796 v8::Handle<v8::Object> interceptor = templ->NewInstance(); 3797 context->Global()->Set(v8_str("whammy"), interceptor); 3798 const char* code = 3799 "var last;" 3800 "for (var i = 0; i < 10000; i++) {" 3801 " var obj = whammy.length;" 3802 " if (last) last.next = obj;" 3803 " last = obj;" 3804 "}" 3805 "gc();" 3806 "4"; 3807 v8::Handle<Value> result = CompileRun(code); 3808 CHECK_EQ(4.0, result->NumberValue()); 3809 3810 context.Dispose(); 3811} 3812 3813 3814static bool in_scavenge = false; 3815static int last = -1; 3816 3817static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) { 3818 CHECK_EQ(-1, last); 3819 last = 0; 3820 obj.Dispose(); 3821 obj.Clear(); 3822 in_scavenge = true; 3823 i::Heap::PerformScavenge(); 3824 in_scavenge = false; 3825 *(reinterpret_cast<bool*>(data)) = true; 3826} 3827 3828static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj, 3829 void* data) { 3830 CHECK_EQ(0, last); 3831 last = 1; 3832 *(reinterpret_cast<bool*>(data)) = in_scavenge; 3833 obj.Dispose(); 3834 obj.Clear(); 3835} 3836 3837THREADED_TEST(NoWeakRefCallbacksInScavenge) { 3838 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks. 3839 // Calling callbacks from scavenges is unsafe as objects held by those 3840 // handlers might have become strongly reachable, but scavenge doesn't 3841 // check that. 3842 v8::Persistent<Context> context = Context::New(); 3843 Context::Scope context_scope(context); 3844 3845 v8::Persistent<v8::Object> object_a; 3846 v8::Persistent<v8::Object> object_b; 3847 3848 { 3849 v8::HandleScope handle_scope; 3850 object_b = v8::Persistent<v8::Object>::New(v8::Object::New()); 3851 object_a = v8::Persistent<v8::Object>::New(v8::Object::New()); 3852 } 3853 3854 bool object_a_disposed = false; 3855 object_a.MakeWeak(&object_a_disposed, &ForceScavenge); 3856 bool released_in_scavenge = false; 3857 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge); 3858 3859 while (!object_a_disposed) { 3860 i::Heap::CollectAllGarbage(false); 3861 } 3862 CHECK(!released_in_scavenge); 3863} 3864 3865 3866v8::Handle<Function> args_fun; 3867 3868 3869static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) { 3870 ApiTestFuzzer::Fuzz(); 3871 CHECK_EQ(args_fun, args.Callee()); 3872 CHECK_EQ(3, args.Length()); 3873 CHECK_EQ(v8::Integer::New(1), args[0]); 3874 CHECK_EQ(v8::Integer::New(2), args[1]); 3875 CHECK_EQ(v8::Integer::New(3), args[2]); 3876 CHECK_EQ(v8::Undefined(), args[3]); 3877 v8::HandleScope scope; 3878 i::Heap::CollectAllGarbage(false); 3879 return v8::Undefined(); 3880} 3881 3882 3883THREADED_TEST(Arguments) { 3884 v8::HandleScope scope; 3885 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 3886 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback)); 3887 LocalContext context(NULL, global); 3888 args_fun = context->Global()->Get(v8_str("f")).As<Function>(); 3889 v8_compile("f(1, 2, 3)")->Run(); 3890} 3891 3892 3893static v8::Handle<Value> NoBlockGetterX(Local<String> name, 3894 const AccessorInfo&) { 3895 return v8::Handle<Value>(); 3896} 3897 3898 3899static v8::Handle<Value> NoBlockGetterI(uint32_t index, 3900 const AccessorInfo&) { 3901 return v8::Handle<Value>(); 3902} 3903 3904 3905static v8::Handle<v8::Boolean> PDeleter(Local<String> name, 3906 const AccessorInfo&) { 3907 if (!name->Equals(v8_str("foo"))) { 3908 return v8::Handle<v8::Boolean>(); // not intercepted 3909 } 3910 3911 return v8::False(); // intercepted, and don't delete the property 3912} 3913 3914 3915static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) { 3916 if (index != 2) { 3917 return v8::Handle<v8::Boolean>(); // not intercepted 3918 } 3919 3920 return v8::False(); // intercepted, and don't delete the property 3921} 3922 3923 3924THREADED_TEST(Deleter) { 3925 v8::HandleScope scope; 3926 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 3927 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL); 3928 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL); 3929 LocalContext context; 3930 context->Global()->Set(v8_str("k"), obj->NewInstance()); 3931 CompileRun( 3932 "k.foo = 'foo';" 3933 "k.bar = 'bar';" 3934 "k[2] = 2;" 3935 "k[4] = 4;"); 3936 CHECK(v8_compile("delete k.foo")->Run()->IsFalse()); 3937 CHECK(v8_compile("delete k.bar")->Run()->IsTrue()); 3938 3939 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo")); 3940 CHECK(v8_compile("k.bar")->Run()->IsUndefined()); 3941 3942 CHECK(v8_compile("delete k[2]")->Run()->IsFalse()); 3943 CHECK(v8_compile("delete k[4]")->Run()->IsTrue()); 3944 3945 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2)); 3946 CHECK(v8_compile("k[4]")->Run()->IsUndefined()); 3947} 3948 3949 3950static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) { 3951 ApiTestFuzzer::Fuzz(); 3952 if (name->Equals(v8_str("foo")) || 3953 name->Equals(v8_str("bar")) || 3954 name->Equals(v8_str("baz"))) { 3955 return v8::Undefined(); 3956 } 3957 return v8::Handle<Value>(); 3958} 3959 3960 3961static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) { 3962 ApiTestFuzzer::Fuzz(); 3963 if (index == 0 || index == 1) return v8::Undefined(); 3964 return v8::Handle<Value>(); 3965} 3966 3967 3968static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) { 3969 ApiTestFuzzer::Fuzz(); 3970 v8::Handle<v8::Array> result = v8::Array::New(3); 3971 result->Set(v8::Integer::New(0), v8_str("foo")); 3972 result->Set(v8::Integer::New(1), v8_str("bar")); 3973 result->Set(v8::Integer::New(2), v8_str("baz")); 3974 return result; 3975} 3976 3977 3978static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) { 3979 ApiTestFuzzer::Fuzz(); 3980 v8::Handle<v8::Array> result = v8::Array::New(2); 3981 result->Set(v8::Integer::New(0), v8_str("0")); 3982 result->Set(v8::Integer::New(1), v8_str("1")); 3983 return result; 3984} 3985 3986 3987THREADED_TEST(Enumerators) { 3988 v8::HandleScope scope; 3989 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 3990 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum); 3991 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum); 3992 LocalContext context; 3993 context->Global()->Set(v8_str("k"), obj->NewInstance()); 3994 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 3995 "k[10] = 0;" 3996 "k.a = 0;" 3997 "k[5] = 0;" 3998 "k.b = 0;" 3999 "k[4294967295] = 0;" 4000 "k.c = 0;" 4001 "k[4294967296] = 0;" 4002 "k.d = 0;" 4003 "k[140000] = 0;" 4004 "k.e = 0;" 4005 "k[30000000000] = 0;" 4006 "k.f = 0;" 4007 "var result = [];" 4008 "for (var prop in k) {" 4009 " result.push(prop);" 4010 "}" 4011 "result")); 4012 // Check that we get all the property names returned including the 4013 // ones from the enumerators in the right order: indexed properties 4014 // in numerical order, indexed interceptor properties, named 4015 // properties in insertion order, named interceptor properties. 4016 // This order is not mandated by the spec, so this test is just 4017 // documenting our behavior. 4018 CHECK_EQ(17, result->Length()); 4019 // Indexed properties in numerical order. 4020 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0))); 4021 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1))); 4022 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2))); 4023 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3))); 4024 // Indexed interceptor properties in the order they are returned 4025 // from the enumerator interceptor. 4026 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4))); 4027 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5))); 4028 // Named properties in insertion order. 4029 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6))); 4030 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7))); 4031 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8))); 4032 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9))); 4033 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10))); 4034 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11))); 4035 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12))); 4036 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13))); 4037 // Named interceptor properties. 4038 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14))); 4039 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15))); 4040 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16))); 4041} 4042 4043 4044int p_getter_count; 4045int p_getter_count2; 4046 4047 4048static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) { 4049 ApiTestFuzzer::Fuzz(); 4050 p_getter_count++; 4051 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 4052 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 4053 if (name->Equals(v8_str("p1"))) { 4054 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 4055 } else if (name->Equals(v8_str("p2"))) { 4056 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 4057 } else if (name->Equals(v8_str("p3"))) { 4058 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 4059 } else if (name->Equals(v8_str("p4"))) { 4060 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 4061 } 4062 return v8::Undefined(); 4063} 4064 4065 4066static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) { 4067 ApiTestFuzzer::Fuzz(); 4068 LocalContext context; 4069 context->Global()->Set(v8_str("o1"), obj->NewInstance()); 4070 CompileRun( 4071 "o1.__proto__ = { };" 4072 "var o2 = { __proto__: o1 };" 4073 "var o3 = { __proto__: o2 };" 4074 "var o4 = { __proto__: o3 };" 4075 "for (var i = 0; i < 10; i++) o4.p4;" 4076 "for (var i = 0; i < 10; i++) o3.p3;" 4077 "for (var i = 0; i < 10; i++) o2.p2;" 4078 "for (var i = 0; i < 10; i++) o1.p1;"); 4079} 4080 4081 4082static v8::Handle<Value> PGetter2(Local<String> name, 4083 const AccessorInfo& info) { 4084 ApiTestFuzzer::Fuzz(); 4085 p_getter_count2++; 4086 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 4087 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 4088 if (name->Equals(v8_str("p1"))) { 4089 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 4090 } else if (name->Equals(v8_str("p2"))) { 4091 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 4092 } else if (name->Equals(v8_str("p3"))) { 4093 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 4094 } else if (name->Equals(v8_str("p4"))) { 4095 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 4096 } 4097 return v8::Undefined(); 4098} 4099 4100 4101THREADED_TEST(GetterHolders) { 4102 v8::HandleScope scope; 4103 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4104 obj->SetAccessor(v8_str("p1"), PGetter); 4105 obj->SetAccessor(v8_str("p2"), PGetter); 4106 obj->SetAccessor(v8_str("p3"), PGetter); 4107 obj->SetAccessor(v8_str("p4"), PGetter); 4108 p_getter_count = 0; 4109 RunHolderTest(obj); 4110 CHECK_EQ(40, p_getter_count); 4111} 4112 4113 4114THREADED_TEST(PreInterceptorHolders) { 4115 v8::HandleScope scope; 4116 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4117 obj->SetNamedPropertyHandler(PGetter2); 4118 p_getter_count2 = 0; 4119 RunHolderTest(obj); 4120 CHECK_EQ(40, p_getter_count2); 4121} 4122 4123 4124THREADED_TEST(ObjectInstantiation) { 4125 v8::HandleScope scope; 4126 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 4127 templ->SetAccessor(v8_str("t"), PGetter2); 4128 LocalContext context; 4129 context->Global()->Set(v8_str("o"), templ->NewInstance()); 4130 for (int i = 0; i < 100; i++) { 4131 v8::HandleScope inner_scope; 4132 v8::Handle<v8::Object> obj = templ->NewInstance(); 4133 CHECK_NE(obj, context->Global()->Get(v8_str("o"))); 4134 context->Global()->Set(v8_str("o2"), obj); 4135 v8::Handle<Value> value = 4136 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run(); 4137 CHECK_EQ(v8::True(), value); 4138 context->Global()->Set(v8_str("o"), obj); 4139 } 4140} 4141 4142 4143THREADED_TEST(StringWrite) { 4144 v8::HandleScope scope; 4145 v8::Handle<String> str = v8_str("abcde"); 4146 4147 char buf[100]; 4148 int len; 4149 4150 memset(buf, 0x1, sizeof(buf)); 4151 len = str->WriteAscii(buf); 4152 CHECK_EQ(len, 5); 4153 CHECK_EQ(strncmp("abcde\0", buf, 6), 0); 4154 4155 memset(buf, 0x1, sizeof(buf)); 4156 len = str->WriteAscii(buf, 0, 4); 4157 CHECK_EQ(len, 4); 4158 CHECK_EQ(strncmp("abcd\1", buf, 5), 0); 4159 4160 memset(buf, 0x1, sizeof(buf)); 4161 len = str->WriteAscii(buf, 0, 5); 4162 CHECK_EQ(len, 5); 4163 CHECK_EQ(strncmp("abcde\1", buf, 6), 0); 4164 4165 memset(buf, 0x1, sizeof(buf)); 4166 len = str->WriteAscii(buf, 0, 6); 4167 CHECK_EQ(len, 5); 4168 CHECK_EQ(strncmp("abcde\0", buf, 6), 0); 4169 4170 memset(buf, 0x1, sizeof(buf)); 4171 len = str->WriteAscii(buf, 4, -1); 4172 CHECK_EQ(len, 1); 4173 CHECK_EQ(strncmp("e\0", buf, 2), 0); 4174 4175 memset(buf, 0x1, sizeof(buf)); 4176 len = str->WriteAscii(buf, 4, 6); 4177 CHECK_EQ(len, 1); 4178 CHECK_EQ(strncmp("e\0", buf, 2), 0); 4179 4180 memset(buf, 0x1, sizeof(buf)); 4181 len = str->WriteAscii(buf, 4, 1); 4182 CHECK_EQ(len, 1); 4183 CHECK_EQ(strncmp("e\1", buf, 2), 0); 4184} 4185 4186 4187THREADED_TEST(ToArrayIndex) { 4188 v8::HandleScope scope; 4189 LocalContext context; 4190 4191 v8::Handle<String> str = v8_str("42"); 4192 v8::Handle<v8::Uint32> index = str->ToArrayIndex(); 4193 CHECK(!index.IsEmpty()); 4194 CHECK_EQ(42.0, index->Uint32Value()); 4195 str = v8_str("42asdf"); 4196 index = str->ToArrayIndex(); 4197 CHECK(index.IsEmpty()); 4198 str = v8_str("-42"); 4199 index = str->ToArrayIndex(); 4200 CHECK(index.IsEmpty()); 4201 str = v8_str("4294967295"); 4202 index = str->ToArrayIndex(); 4203 CHECK(!index.IsEmpty()); 4204 CHECK_EQ(4294967295.0, index->Uint32Value()); 4205 v8::Handle<v8::Number> num = v8::Number::New(1); 4206 index = num->ToArrayIndex(); 4207 CHECK(!index.IsEmpty()); 4208 CHECK_EQ(1.0, index->Uint32Value()); 4209 num = v8::Number::New(-1); 4210 index = num->ToArrayIndex(); 4211 CHECK(index.IsEmpty()); 4212 v8::Handle<v8::Object> obj = v8::Object::New(); 4213 index = obj->ToArrayIndex(); 4214 CHECK(index.IsEmpty()); 4215} 4216 4217 4218THREADED_TEST(ErrorConstruction) { 4219 v8::HandleScope scope; 4220 LocalContext context; 4221 4222 v8::Handle<String> foo = v8_str("foo"); 4223 v8::Handle<String> message = v8_str("message"); 4224 v8::Handle<Value> range_error = v8::Exception::RangeError(foo); 4225 CHECK(range_error->IsObject()); 4226 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>(); 4227 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo)); 4228 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo); 4229 CHECK(reference_error->IsObject()); 4230 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo)); 4231 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo); 4232 CHECK(syntax_error->IsObject()); 4233 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo)); 4234 v8::Handle<Value> type_error = v8::Exception::TypeError(foo); 4235 CHECK(type_error->IsObject()); 4236 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo)); 4237 v8::Handle<Value> error = v8::Exception::Error(foo); 4238 CHECK(error->IsObject()); 4239 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo)); 4240} 4241 4242 4243static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) { 4244 ApiTestFuzzer::Fuzz(); 4245 return v8_num(10); 4246} 4247 4248 4249static void YSetter(Local<String> name, 4250 Local<Value> value, 4251 const AccessorInfo& info) { 4252 if (info.This()->Has(name)) { 4253 info.This()->Delete(name); 4254 } 4255 info.This()->Set(name, value); 4256} 4257 4258 4259THREADED_TEST(DeleteAccessor) { 4260 v8::HandleScope scope; 4261 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4262 obj->SetAccessor(v8_str("y"), YGetter, YSetter); 4263 LocalContext context; 4264 v8::Handle<v8::Object> holder = obj->NewInstance(); 4265 context->Global()->Set(v8_str("holder"), holder); 4266 v8::Handle<Value> result = CompileRun( 4267 "holder.y = 11; holder.y = 12; holder.y"); 4268 CHECK_EQ(12, result->Uint32Value()); 4269} 4270 4271 4272THREADED_TEST(TypeSwitch) { 4273 v8::HandleScope scope; 4274 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New(); 4275 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New(); 4276 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New(); 4277 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 }; 4278 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs); 4279 LocalContext context; 4280 v8::Handle<v8::Object> obj0 = v8::Object::New(); 4281 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance(); 4282 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance(); 4283 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance(); 4284 for (int i = 0; i < 10; i++) { 4285 CHECK_EQ(0, type_switch->match(obj0)); 4286 CHECK_EQ(1, type_switch->match(obj1)); 4287 CHECK_EQ(2, type_switch->match(obj2)); 4288 CHECK_EQ(3, type_switch->match(obj3)); 4289 CHECK_EQ(3, type_switch->match(obj3)); 4290 CHECK_EQ(2, type_switch->match(obj2)); 4291 CHECK_EQ(1, type_switch->match(obj1)); 4292 CHECK_EQ(0, type_switch->match(obj0)); 4293 } 4294} 4295 4296 4297// For use within the TestSecurityHandler() test. 4298static bool g_security_callback_result = false; 4299static bool NamedSecurityTestCallback(Local<v8::Object> global, 4300 Local<Value> name, 4301 v8::AccessType type, 4302 Local<Value> data) { 4303 // Always allow read access. 4304 if (type == v8::ACCESS_GET) 4305 return true; 4306 4307 // Sometimes allow other access. 4308 return g_security_callback_result; 4309} 4310 4311 4312static bool IndexedSecurityTestCallback(Local<v8::Object> global, 4313 uint32_t key, 4314 v8::AccessType type, 4315 Local<Value> data) { 4316 // Always allow read access. 4317 if (type == v8::ACCESS_GET) 4318 return true; 4319 4320 // Sometimes allow other access. 4321 return g_security_callback_result; 4322} 4323 4324 4325static int trouble_nesting = 0; 4326static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) { 4327 ApiTestFuzzer::Fuzz(); 4328 trouble_nesting++; 4329 4330 // Call a JS function that throws an uncaught exception. 4331 Local<v8::Object> arg_this = Context::GetCurrent()->Global(); 4332 Local<Value> trouble_callee = (trouble_nesting == 3) ? 4333 arg_this->Get(v8_str("trouble_callee")) : 4334 arg_this->Get(v8_str("trouble_caller")); 4335 CHECK(trouble_callee->IsFunction()); 4336 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL); 4337} 4338 4339 4340static int report_count = 0; 4341static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>, 4342 v8::Handle<Value>) { 4343 report_count++; 4344} 4345 4346 4347// Counts uncaught exceptions, but other tests running in parallel 4348// also have uncaught exceptions. 4349TEST(ApiUncaughtException) { 4350 report_count = 0; 4351 v8::HandleScope scope; 4352 LocalContext env; 4353 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener); 4354 4355 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 4356 v8::Local<v8::Object> global = env->Global(); 4357 global->Set(v8_str("trouble"), fun->GetFunction()); 4358 4359 Script::Compile(v8_str("function trouble_callee() {" 4360 " var x = null;" 4361 " return x.foo;" 4362 "};" 4363 "function trouble_caller() {" 4364 " trouble();" 4365 "};"))->Run(); 4366 Local<Value> trouble = global->Get(v8_str("trouble")); 4367 CHECK(trouble->IsFunction()); 4368 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee")); 4369 CHECK(trouble_callee->IsFunction()); 4370 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller")); 4371 CHECK(trouble_caller->IsFunction()); 4372 Function::Cast(*trouble_caller)->Call(global, 0, NULL); 4373 CHECK_EQ(1, report_count); 4374 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener); 4375} 4376 4377static const char* script_resource_name = "ExceptionInNativeScript.js"; 4378static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message, 4379 v8::Handle<Value>) { 4380 v8::Handle<v8::Value> name_val = message->GetScriptResourceName(); 4381 CHECK(!name_val.IsEmpty() && name_val->IsString()); 4382 v8::String::AsciiValue name(message->GetScriptResourceName()); 4383 CHECK_EQ(script_resource_name, *name); 4384 CHECK_EQ(3, message->GetLineNumber()); 4385 v8::String::AsciiValue source_line(message->GetSourceLine()); 4386 CHECK_EQ(" new o.foo();", *source_line); 4387} 4388 4389TEST(ExceptionInNativeScript) { 4390 v8::HandleScope scope; 4391 LocalContext env; 4392 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener); 4393 4394 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 4395 v8::Local<v8::Object> global = env->Global(); 4396 global->Set(v8_str("trouble"), fun->GetFunction()); 4397 4398 Script::Compile(v8_str("function trouble() {\n" 4399 " var o = {};\n" 4400 " new o.foo();\n" 4401 "};"), v8::String::New(script_resource_name))->Run(); 4402 Local<Value> trouble = global->Get(v8_str("trouble")); 4403 CHECK(trouble->IsFunction()); 4404 Function::Cast(*trouble)->Call(global, 0, NULL); 4405 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener); 4406} 4407 4408 4409TEST(CompilationErrorUsingTryCatchHandler) { 4410 v8::HandleScope scope; 4411 LocalContext env; 4412 v8::TryCatch try_catch; 4413 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile.")); 4414 CHECK_NE(NULL, *try_catch.Exception()); 4415 CHECK(try_catch.HasCaught()); 4416} 4417 4418 4419TEST(TryCatchFinallyUsingTryCatchHandler) { 4420 v8::HandleScope scope; 4421 LocalContext env; 4422 v8::TryCatch try_catch; 4423 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run(); 4424 CHECK(!try_catch.HasCaught()); 4425 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run(); 4426 CHECK(try_catch.HasCaught()); 4427 try_catch.Reset(); 4428 Script::Compile(v8_str("(function() {" 4429 "try { throw ''; } finally { return; }" 4430 "})()"))->Run(); 4431 CHECK(!try_catch.HasCaught()); 4432 Script::Compile(v8_str("(function()" 4433 " { try { throw ''; } finally { throw 0; }" 4434 "})()"))->Run(); 4435 CHECK(try_catch.HasCaught()); 4436} 4437 4438 4439// SecurityHandler can't be run twice 4440TEST(SecurityHandler) { 4441 v8::HandleScope scope0; 4442 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 4443 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback, 4444 IndexedSecurityTestCallback); 4445 // Create an environment 4446 v8::Persistent<Context> context0 = 4447 Context::New(NULL, global_template); 4448 context0->Enter(); 4449 4450 v8::Handle<v8::Object> global0 = context0->Global(); 4451 v8::Handle<Script> script0 = v8_compile("foo = 111"); 4452 script0->Run(); 4453 global0->Set(v8_str("0"), v8_num(999)); 4454 v8::Handle<Value> foo0 = global0->Get(v8_str("foo")); 4455 CHECK_EQ(111, foo0->Int32Value()); 4456 v8::Handle<Value> z0 = global0->Get(v8_str("0")); 4457 CHECK_EQ(999, z0->Int32Value()); 4458 4459 // Create another environment, should fail security checks. 4460 v8::HandleScope scope1; 4461 4462 v8::Persistent<Context> context1 = 4463 Context::New(NULL, global_template); 4464 context1->Enter(); 4465 4466 v8::Handle<v8::Object> global1 = context1->Global(); 4467 global1->Set(v8_str("othercontext"), global0); 4468 // This set will fail the security check. 4469 v8::Handle<Script> script1 = 4470 v8_compile("othercontext.foo = 222; othercontext[0] = 888;"); 4471 script1->Run(); 4472 // This read will pass the security check. 4473 v8::Handle<Value> foo1 = global0->Get(v8_str("foo")); 4474 CHECK_EQ(111, foo1->Int32Value()); 4475 // This read will pass the security check. 4476 v8::Handle<Value> z1 = global0->Get(v8_str("0")); 4477 CHECK_EQ(999, z1->Int32Value()); 4478 4479 // Create another environment, should pass security checks. 4480 { g_security_callback_result = true; // allow security handler to pass. 4481 v8::HandleScope scope2; 4482 LocalContext context2; 4483 v8::Handle<v8::Object> global2 = context2->Global(); 4484 global2->Set(v8_str("othercontext"), global0); 4485 v8::Handle<Script> script2 = 4486 v8_compile("othercontext.foo = 333; othercontext[0] = 888;"); 4487 script2->Run(); 4488 v8::Handle<Value> foo2 = global0->Get(v8_str("foo")); 4489 CHECK_EQ(333, foo2->Int32Value()); 4490 v8::Handle<Value> z2 = global0->Get(v8_str("0")); 4491 CHECK_EQ(888, z2->Int32Value()); 4492 } 4493 4494 context1->Exit(); 4495 context1.Dispose(); 4496 4497 context0->Exit(); 4498 context0.Dispose(); 4499} 4500 4501 4502THREADED_TEST(SecurityChecks) { 4503 v8::HandleScope handle_scope; 4504 LocalContext env1; 4505 v8::Persistent<Context> env2 = Context::New(); 4506 4507 Local<Value> foo = v8_str("foo"); 4508 Local<Value> bar = v8_str("bar"); 4509 4510 // Set to the same domain. 4511 env1->SetSecurityToken(foo); 4512 4513 // Create a function in env1. 4514 Script::Compile(v8_str("spy=function(){return spy;}"))->Run(); 4515 Local<Value> spy = env1->Global()->Get(v8_str("spy")); 4516 CHECK(spy->IsFunction()); 4517 4518 // Create another function accessing global objects. 4519 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run(); 4520 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2")); 4521 CHECK(spy2->IsFunction()); 4522 4523 // Switch to env2 in the same domain and invoke spy on env2. 4524 { 4525 env2->SetSecurityToken(foo); 4526 // Enter env2 4527 Context::Scope scope_env2(env2); 4528 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL); 4529 CHECK(result->IsFunction()); 4530 } 4531 4532 { 4533 env2->SetSecurityToken(bar); 4534 Context::Scope scope_env2(env2); 4535 4536 // Call cross_domain_call, it should throw an exception 4537 v8::TryCatch try_catch; 4538 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL); 4539 CHECK(try_catch.HasCaught()); 4540 } 4541 4542 env2.Dispose(); 4543} 4544 4545 4546// Regression test case for issue 1183439. 4547THREADED_TEST(SecurityChecksForPrototypeChain) { 4548 v8::HandleScope scope; 4549 LocalContext current; 4550 v8::Persistent<Context> other = Context::New(); 4551 4552 // Change context to be able to get to the Object function in the 4553 // other context without hitting the security checks. 4554 v8::Local<Value> other_object; 4555 { Context::Scope scope(other); 4556 other_object = other->Global()->Get(v8_str("Object")); 4557 other->Global()->Set(v8_num(42), v8_num(87)); 4558 } 4559 4560 current->Global()->Set(v8_str("other"), other->Global()); 4561 CHECK(v8_compile("other")->Run()->Equals(other->Global())); 4562 4563 // Make sure the security check fails here and we get an undefined 4564 // result instead of getting the Object function. Repeat in a loop 4565 // to make sure to exercise the IC code. 4566 v8::Local<Script> access_other0 = v8_compile("other.Object"); 4567 v8::Local<Script> access_other1 = v8_compile("other[42]"); 4568 for (int i = 0; i < 5; i++) { 4569 CHECK(!access_other0->Run()->Equals(other_object)); 4570 CHECK(access_other0->Run()->IsUndefined()); 4571 CHECK(!access_other1->Run()->Equals(v8_num(87))); 4572 CHECK(access_other1->Run()->IsUndefined()); 4573 } 4574 4575 // Create an object that has 'other' in its prototype chain and make 4576 // sure we cannot access the Object function indirectly through 4577 // that. Repeat in a loop to make sure to exercise the IC code. 4578 v8_compile("function F() { };" 4579 "F.prototype = other;" 4580 "var f = new F();")->Run(); 4581 v8::Local<Script> access_f0 = v8_compile("f.Object"); 4582 v8::Local<Script> access_f1 = v8_compile("f[42]"); 4583 for (int j = 0; j < 5; j++) { 4584 CHECK(!access_f0->Run()->Equals(other_object)); 4585 CHECK(access_f0->Run()->IsUndefined()); 4586 CHECK(!access_f1->Run()->Equals(v8_num(87))); 4587 CHECK(access_f1->Run()->IsUndefined()); 4588 } 4589 4590 // Now it gets hairy: Set the prototype for the other global object 4591 // to be the current global object. The prototype chain for 'f' now 4592 // goes through 'other' but ends up in the current global object. 4593 { Context::Scope scope(other); 4594 other->Global()->Set(v8_str("__proto__"), current->Global()); 4595 } 4596 // Set a named and an index property on the current global 4597 // object. To force the lookup to go through the other global object, 4598 // the properties must not exist in the other global object. 4599 current->Global()->Set(v8_str("foo"), v8_num(100)); 4600 current->Global()->Set(v8_num(99), v8_num(101)); 4601 // Try to read the properties from f and make sure that the access 4602 // gets stopped by the security checks on the other global object. 4603 Local<Script> access_f2 = v8_compile("f.foo"); 4604 Local<Script> access_f3 = v8_compile("f[99]"); 4605 for (int k = 0; k < 5; k++) { 4606 CHECK(!access_f2->Run()->Equals(v8_num(100))); 4607 CHECK(access_f2->Run()->IsUndefined()); 4608 CHECK(!access_f3->Run()->Equals(v8_num(101))); 4609 CHECK(access_f3->Run()->IsUndefined()); 4610 } 4611 other.Dispose(); 4612} 4613 4614 4615THREADED_TEST(CrossDomainDelete) { 4616 v8::HandleScope handle_scope; 4617 LocalContext env1; 4618 v8::Persistent<Context> env2 = Context::New(); 4619 4620 Local<Value> foo = v8_str("foo"); 4621 Local<Value> bar = v8_str("bar"); 4622 4623 // Set to the same domain. 4624 env1->SetSecurityToken(foo); 4625 env2->SetSecurityToken(foo); 4626 4627 env1->Global()->Set(v8_str("prop"), v8_num(3)); 4628 env2->Global()->Set(v8_str("env1"), env1->Global()); 4629 4630 // Change env2 to a different domain and delete env1.prop. 4631 env2->SetSecurityToken(bar); 4632 { 4633 Context::Scope scope_env2(env2); 4634 Local<Value> result = 4635 Script::Compile(v8_str("delete env1.prop"))->Run(); 4636 CHECK(result->IsFalse()); 4637 } 4638 4639 // Check that env1.prop still exists. 4640 Local<Value> v = env1->Global()->Get(v8_str("prop")); 4641 CHECK(v->IsNumber()); 4642 CHECK_EQ(3, v->Int32Value()); 4643 4644 env2.Dispose(); 4645} 4646 4647 4648THREADED_TEST(CrossDomainIsPropertyEnumerable) { 4649 v8::HandleScope handle_scope; 4650 LocalContext env1; 4651 v8::Persistent<Context> env2 = Context::New(); 4652 4653 Local<Value> foo = v8_str("foo"); 4654 Local<Value> bar = v8_str("bar"); 4655 4656 // Set to the same domain. 4657 env1->SetSecurityToken(foo); 4658 env2->SetSecurityToken(foo); 4659 4660 env1->Global()->Set(v8_str("prop"), v8_num(3)); 4661 env2->Global()->Set(v8_str("env1"), env1->Global()); 4662 4663 // env1.prop is enumerable in env2. 4664 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')"); 4665 { 4666 Context::Scope scope_env2(env2); 4667 Local<Value> result = Script::Compile(test)->Run(); 4668 CHECK(result->IsTrue()); 4669 } 4670 4671 // Change env2 to a different domain and test again. 4672 env2->SetSecurityToken(bar); 4673 { 4674 Context::Scope scope_env2(env2); 4675 Local<Value> result = Script::Compile(test)->Run(); 4676 CHECK(result->IsFalse()); 4677 } 4678 4679 env2.Dispose(); 4680} 4681 4682 4683THREADED_TEST(CrossDomainForIn) { 4684 v8::HandleScope handle_scope; 4685 LocalContext env1; 4686 v8::Persistent<Context> env2 = Context::New(); 4687 4688 Local<Value> foo = v8_str("foo"); 4689 Local<Value> bar = v8_str("bar"); 4690 4691 // Set to the same domain. 4692 env1->SetSecurityToken(foo); 4693 env2->SetSecurityToken(foo); 4694 4695 env1->Global()->Set(v8_str("prop"), v8_num(3)); 4696 env2->Global()->Set(v8_str("env1"), env1->Global()); 4697 4698 // Change env2 to a different domain and set env1's global object 4699 // as the __proto__ of an object in env2 and enumerate properties 4700 // in for-in. It shouldn't enumerate properties on env1's global 4701 // object. 4702 env2->SetSecurityToken(bar); 4703 { 4704 Context::Scope scope_env2(env2); 4705 Local<Value> result = 4706 CompileRun("(function(){var obj = {'__proto__':env1};" 4707 "for (var p in obj)" 4708 " if (p == 'prop') return false;" 4709 "return true;})()"); 4710 CHECK(result->IsTrue()); 4711 } 4712 env2.Dispose(); 4713} 4714 4715 4716TEST(ContextDetachGlobal) { 4717 v8::HandleScope handle_scope; 4718 LocalContext env1; 4719 v8::Persistent<Context> env2 = Context::New(); 4720 4721 Local<v8::Object> global1 = env1->Global(); 4722 4723 Local<Value> foo = v8_str("foo"); 4724 4725 // Set to the same domain. 4726 env1->SetSecurityToken(foo); 4727 env2->SetSecurityToken(foo); 4728 4729 // Enter env2 4730 env2->Enter(); 4731 4732 // Create a function in env2 and add a reference to it in env1. 4733 Local<v8::Object> global2 = env2->Global(); 4734 global2->Set(v8_str("prop"), v8::Integer::New(1)); 4735 CompileRun("function getProp() {return prop;}"); 4736 4737 env1->Global()->Set(v8_str("getProp"), 4738 global2->Get(v8_str("getProp"))); 4739 4740 // Detach env2's global, and reuse the global object of env2 4741 env2->Exit(); 4742 env2->DetachGlobal(); 4743 // env2 has a new global object. 4744 CHECK(!env2->Global()->Equals(global2)); 4745 4746 v8::Persistent<Context> env3 = 4747 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 4748 env3->SetSecurityToken(v8_str("bar")); 4749 env3->Enter(); 4750 4751 Local<v8::Object> global3 = env3->Global(); 4752 CHECK_EQ(global2, global3); 4753 CHECK(global3->Get(v8_str("prop"))->IsUndefined()); 4754 CHECK(global3->Get(v8_str("getProp"))->IsUndefined()); 4755 global3->Set(v8_str("prop"), v8::Integer::New(-1)); 4756 global3->Set(v8_str("prop2"), v8::Integer::New(2)); 4757 env3->Exit(); 4758 4759 // Call getProp in env1, and it should return the value 1 4760 { 4761 Local<Value> get_prop = global1->Get(v8_str("getProp")); 4762 CHECK(get_prop->IsFunction()); 4763 v8::TryCatch try_catch; 4764 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL); 4765 CHECK(!try_catch.HasCaught()); 4766 CHECK_EQ(1, r->Int32Value()); 4767 } 4768 4769 // Check that env3 is not accessible from env1 4770 { 4771 Local<Value> r = global3->Get(v8_str("prop2")); 4772 CHECK(r->IsUndefined()); 4773 } 4774 4775 env2.Dispose(); 4776 env3.Dispose(); 4777} 4778 4779 4780TEST(DetachAndReattachGlobal) { 4781 v8::HandleScope scope; 4782 LocalContext env1; 4783 4784 // Create second environment. 4785 v8::Persistent<Context> env2 = Context::New(); 4786 4787 Local<Value> foo = v8_str("foo"); 4788 4789 // Set same security token for env1 and env2. 4790 env1->SetSecurityToken(foo); 4791 env2->SetSecurityToken(foo); 4792 4793 // Create a property on the global object in env2. 4794 { 4795 v8::Context::Scope scope(env2); 4796 env2->Global()->Set(v8_str("p"), v8::Integer::New(42)); 4797 } 4798 4799 // Create a reference to env2 global from env1 global. 4800 env1->Global()->Set(v8_str("other"), env2->Global()); 4801 4802 // Check that we have access to other.p in env2 from env1. 4803 Local<Value> result = CompileRun("other.p"); 4804 CHECK(result->IsInt32()); 4805 CHECK_EQ(42, result->Int32Value()); 4806 4807 // Hold on to global from env2 and detach global from env2. 4808 Local<v8::Object> global2 = env2->Global(); 4809 env2->DetachGlobal(); 4810 4811 // Check that the global has been detached. No other.p property can 4812 // be found. 4813 result = CompileRun("other.p"); 4814 CHECK(result->IsUndefined()); 4815 4816 // Reuse global2 for env3. 4817 v8::Persistent<Context> env3 = 4818 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 4819 CHECK_EQ(global2, env3->Global()); 4820 4821 // Start by using the same security token for env3 as for env1 and env2. 4822 env3->SetSecurityToken(foo); 4823 4824 // Create a property on the global object in env3. 4825 { 4826 v8::Context::Scope scope(env3); 4827 env3->Global()->Set(v8_str("p"), v8::Integer::New(24)); 4828 } 4829 4830 // Check that other.p is now the property in env3 and that we have access. 4831 result = CompileRun("other.p"); 4832 CHECK(result->IsInt32()); 4833 CHECK_EQ(24, result->Int32Value()); 4834 4835 // Change security token for env3 to something different from env1 and env2. 4836 env3->SetSecurityToken(v8_str("bar")); 4837 4838 // Check that we do not have access to other.p in env1. |other| is now 4839 // the global object for env3 which has a different security token, 4840 // so access should be blocked. 4841 result = CompileRun("other.p"); 4842 CHECK(result->IsUndefined()); 4843 4844 // Detach the global for env3 and reattach it to env2. 4845 env3->DetachGlobal(); 4846 env2->ReattachGlobal(global2); 4847 4848 // Check that we have access to other.p again in env1. |other| is now 4849 // the global object for env2 which has the same security token as env1. 4850 result = CompileRun("other.p"); 4851 CHECK(result->IsInt32()); 4852 CHECK_EQ(42, result->Int32Value()); 4853 4854 env2.Dispose(); 4855 env3.Dispose(); 4856} 4857 4858 4859static bool NamedAccessBlocker(Local<v8::Object> global, 4860 Local<Value> name, 4861 v8::AccessType type, 4862 Local<Value> data) { 4863 return Context::GetCurrent()->Global()->Equals(global); 4864} 4865 4866 4867static bool IndexedAccessBlocker(Local<v8::Object> global, 4868 uint32_t key, 4869 v8::AccessType type, 4870 Local<Value> data) { 4871 return Context::GetCurrent()->Global()->Equals(global); 4872} 4873 4874 4875static int g_echo_value = -1; 4876static v8::Handle<Value> EchoGetter(Local<String> name, 4877 const AccessorInfo& info) { 4878 return v8_num(g_echo_value); 4879} 4880 4881 4882static void EchoSetter(Local<String> name, 4883 Local<Value> value, 4884 const AccessorInfo&) { 4885 if (value->IsNumber()) 4886 g_echo_value = value->Int32Value(); 4887} 4888 4889 4890static v8::Handle<Value> UnreachableGetter(Local<String> name, 4891 const AccessorInfo& info) { 4892 CHECK(false); // This function should not be called.. 4893 return v8::Undefined(); 4894} 4895 4896 4897static void UnreachableSetter(Local<String>, Local<Value>, 4898 const AccessorInfo&) { 4899 CHECK(false); // This function should nto be called. 4900} 4901 4902 4903THREADED_TEST(AccessControl) { 4904 v8::HandleScope handle_scope; 4905 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 4906 4907 global_template->SetAccessCheckCallbacks(NamedAccessBlocker, 4908 IndexedAccessBlocker); 4909 4910 // Add an accessor accessible by cross-domain JS code. 4911 global_template->SetAccessor( 4912 v8_str("accessible_prop"), 4913 EchoGetter, EchoSetter, 4914 v8::Handle<Value>(), 4915 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); 4916 4917 // Add an accessor that is not accessible by cross-domain JS code. 4918 global_template->SetAccessor(v8_str("blocked_prop"), 4919 UnreachableGetter, UnreachableSetter, 4920 v8::Handle<Value>(), 4921 v8::DEFAULT); 4922 4923 // Create an environment 4924 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 4925 context0->Enter(); 4926 4927 v8::Handle<v8::Object> global0 = context0->Global(); 4928 4929 v8::HandleScope scope1; 4930 4931 v8::Persistent<Context> context1 = Context::New(); 4932 context1->Enter(); 4933 4934 v8::Handle<v8::Object> global1 = context1->Global(); 4935 global1->Set(v8_str("other"), global0); 4936 4937 v8::Handle<Value> value; 4938 4939 // Access blocked property 4940 value = v8_compile("other.blocked_prop = 1")->Run(); 4941 value = v8_compile("other.blocked_prop")->Run(); 4942 CHECK(value->IsUndefined()); 4943 4944 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run(); 4945 CHECK(value->IsFalse()); 4946 4947 // Access accessible property 4948 value = v8_compile("other.accessible_prop = 3")->Run(); 4949 CHECK(value->IsNumber()); 4950 CHECK_EQ(3, value->Int32Value()); 4951 CHECK_EQ(3, g_echo_value); 4952 4953 value = v8_compile("other.accessible_prop")->Run(); 4954 CHECK(value->IsNumber()); 4955 CHECK_EQ(3, value->Int32Value()); 4956 4957 value = 4958 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run(); 4959 CHECK(value->IsTrue()); 4960 4961 // Enumeration doesn't enumerate accessors from inaccessible objects in 4962 // the prototype chain even if the accessors are in themselves accessible. 4963 Local<Value> result = 4964 CompileRun("(function(){var obj = {'__proto__':other};" 4965 "for (var p in obj)" 4966 " if (p == 'accessible_prop' || p == 'blocked_prop') {" 4967 " return false;" 4968 " }" 4969 "return true;})()"); 4970 CHECK(result->IsTrue()); 4971 4972 context1->Exit(); 4973 context0->Exit(); 4974 context1.Dispose(); 4975 context0.Dispose(); 4976} 4977 4978 4979static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global, 4980 Local<Value> name, 4981 v8::AccessType type, 4982 Local<Value> data) { 4983 return false; 4984} 4985 4986 4987static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global, 4988 uint32_t key, 4989 v8::AccessType type, 4990 Local<Value> data) { 4991 return false; 4992} 4993 4994 4995THREADED_TEST(AccessControlGetOwnPropertyNames) { 4996 v8::HandleScope handle_scope; 4997 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(); 4998 4999 obj_template->Set(v8_str("x"), v8::Integer::New(42)); 5000 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker, 5001 GetOwnPropertyNamesIndexedBlocker); 5002 5003 // Create an environment 5004 v8::Persistent<Context> context0 = Context::New(NULL, obj_template); 5005 context0->Enter(); 5006 5007 v8::Handle<v8::Object> global0 = context0->Global(); 5008 5009 v8::HandleScope scope1; 5010 5011 v8::Persistent<Context> context1 = Context::New(); 5012 context1->Enter(); 5013 5014 v8::Handle<v8::Object> global1 = context1->Global(); 5015 global1->Set(v8_str("other"), global0); 5016 global1->Set(v8_str("object"), obj_template->NewInstance()); 5017 5018 v8::Handle<Value> value; 5019 5020 // Attempt to get the property names of the other global object and 5021 // of an object that requires access checks. Accessing the other 5022 // global object should be blocked by access checks on the global 5023 // proxy object. Accessing the object that requires access checks 5024 // is blocked by the access checks on the object itself. 5025 value = CompileRun("Object.getOwnPropertyNames(other).length == 0"); 5026 CHECK(value->IsTrue()); 5027 5028 value = CompileRun("Object.getOwnPropertyNames(object).length == 0"); 5029 CHECK(value->IsTrue()); 5030 5031 context1->Exit(); 5032 context0->Exit(); 5033 context1.Dispose(); 5034 context0.Dispose(); 5035} 5036 5037 5038static v8::Handle<Value> ConstTenGetter(Local<String> name, 5039 const AccessorInfo& info) { 5040 return v8_num(10); 5041} 5042 5043 5044THREADED_TEST(CrossDomainAccessors) { 5045 v8::HandleScope handle_scope; 5046 5047 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(); 5048 5049 v8::Handle<v8::ObjectTemplate> global_template = 5050 func_template->InstanceTemplate(); 5051 5052 v8::Handle<v8::ObjectTemplate> proto_template = 5053 func_template->PrototypeTemplate(); 5054 5055 // Add an accessor to proto that's accessible by cross-domain JS code. 5056 proto_template->SetAccessor(v8_str("accessible"), 5057 ConstTenGetter, 0, 5058 v8::Handle<Value>(), 5059 v8::ALL_CAN_READ); 5060 5061 // Add an accessor that is not accessible by cross-domain JS code. 5062 global_template->SetAccessor(v8_str("unreachable"), 5063 UnreachableGetter, 0, 5064 v8::Handle<Value>(), 5065 v8::DEFAULT); 5066 5067 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 5068 context0->Enter(); 5069 5070 Local<v8::Object> global = context0->Global(); 5071 // Add a normal property that shadows 'accessible' 5072 global->Set(v8_str("accessible"), v8_num(11)); 5073 5074 // Enter a new context. 5075 v8::HandleScope scope1; 5076 v8::Persistent<Context> context1 = Context::New(); 5077 context1->Enter(); 5078 5079 v8::Handle<v8::Object> global1 = context1->Global(); 5080 global1->Set(v8_str("other"), global); 5081 5082 // Should return 10, instead of 11 5083 v8::Handle<Value> value = v8_compile("other.accessible")->Run(); 5084 CHECK(value->IsNumber()); 5085 CHECK_EQ(10, value->Int32Value()); 5086 5087 value = v8_compile("other.unreachable")->Run(); 5088 CHECK(value->IsUndefined()); 5089 5090 context1->Exit(); 5091 context0->Exit(); 5092 context1.Dispose(); 5093 context0.Dispose(); 5094} 5095 5096 5097static int named_access_count = 0; 5098static int indexed_access_count = 0; 5099 5100static bool NamedAccessCounter(Local<v8::Object> global, 5101 Local<Value> name, 5102 v8::AccessType type, 5103 Local<Value> data) { 5104 named_access_count++; 5105 return true; 5106} 5107 5108 5109static bool IndexedAccessCounter(Local<v8::Object> global, 5110 uint32_t key, 5111 v8::AccessType type, 5112 Local<Value> data) { 5113 indexed_access_count++; 5114 return true; 5115} 5116 5117 5118// This one is too easily disturbed by other tests. 5119TEST(AccessControlIC) { 5120 named_access_count = 0; 5121 indexed_access_count = 0; 5122 5123 v8::HandleScope handle_scope; 5124 5125 // Create an environment. 5126 v8::Persistent<Context> context0 = Context::New(); 5127 context0->Enter(); 5128 5129 // Create an object that requires access-check functions to be 5130 // called for cross-domain access. 5131 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 5132 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 5133 IndexedAccessCounter); 5134 Local<v8::Object> object = object_template->NewInstance(); 5135 5136 v8::HandleScope scope1; 5137 5138 // Create another environment. 5139 v8::Persistent<Context> context1 = Context::New(); 5140 context1->Enter(); 5141 5142 // Make easy access to the object from the other environment. 5143 v8::Handle<v8::Object> global1 = context1->Global(); 5144 global1->Set(v8_str("obj"), object); 5145 5146 v8::Handle<Value> value; 5147 5148 // Check that the named access-control function is called every time. 5149 CompileRun("function testProp(obj) {" 5150 " for (var i = 0; i < 10; i++) obj.prop = 1;" 5151 " for (var j = 0; j < 10; j++) obj.prop;" 5152 " return obj.prop" 5153 "}"); 5154 value = CompileRun("testProp(obj)"); 5155 CHECK(value->IsNumber()); 5156 CHECK_EQ(1, value->Int32Value()); 5157 CHECK_EQ(21, named_access_count); 5158 5159 // Check that the named access-control function is called every time. 5160 CompileRun("var p = 'prop';" 5161 "function testKeyed(obj) {" 5162 " for (var i = 0; i < 10; i++) obj[p] = 1;" 5163 " for (var j = 0; j < 10; j++) obj[p];" 5164 " return obj[p];" 5165 "}"); 5166 // Use obj which requires access checks. No inline caching is used 5167 // in that case. 5168 value = CompileRun("testKeyed(obj)"); 5169 CHECK(value->IsNumber()); 5170 CHECK_EQ(1, value->Int32Value()); 5171 CHECK_EQ(42, named_access_count); 5172 // Force the inline caches into generic state and try again. 5173 CompileRun("testKeyed({ a: 0 })"); 5174 CompileRun("testKeyed({ b: 0 })"); 5175 value = CompileRun("testKeyed(obj)"); 5176 CHECK(value->IsNumber()); 5177 CHECK_EQ(1, value->Int32Value()); 5178 CHECK_EQ(63, named_access_count); 5179 5180 // Check that the indexed access-control function is called every time. 5181 CompileRun("function testIndexed(obj) {" 5182 " for (var i = 0; i < 10; i++) obj[0] = 1;" 5183 " for (var j = 0; j < 10; j++) obj[0];" 5184 " return obj[0]" 5185 "}"); 5186 value = CompileRun("testIndexed(obj)"); 5187 CHECK(value->IsNumber()); 5188 CHECK_EQ(1, value->Int32Value()); 5189 CHECK_EQ(21, indexed_access_count); 5190 // Force the inline caches into generic state. 5191 CompileRun("testIndexed(new Array(1))"); 5192 // Test that the indexed access check is called. 5193 value = CompileRun("testIndexed(obj)"); 5194 CHECK(value->IsNumber()); 5195 CHECK_EQ(1, value->Int32Value()); 5196 CHECK_EQ(42, indexed_access_count); 5197 5198 // Check that the named access check is called when invoking 5199 // functions on an object that requires access checks. 5200 CompileRun("obj.f = function() {}"); 5201 CompileRun("function testCallNormal(obj) {" 5202 " for (var i = 0; i < 10; i++) obj.f();" 5203 "}"); 5204 CompileRun("testCallNormal(obj)"); 5205 CHECK_EQ(74, named_access_count); 5206 5207 // Force obj into slow case. 5208 value = CompileRun("delete obj.prop"); 5209 CHECK(value->BooleanValue()); 5210 // Force inline caches into dictionary probing mode. 5211 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);"); 5212 // Test that the named access check is called. 5213 value = CompileRun("testProp(obj);"); 5214 CHECK(value->IsNumber()); 5215 CHECK_EQ(1, value->Int32Value()); 5216 CHECK_EQ(96, named_access_count); 5217 5218 // Force the call inline cache into dictionary probing mode. 5219 CompileRun("o.f = function() {}; testCallNormal(o)"); 5220 // Test that the named access check is still called for each 5221 // invocation of the function. 5222 value = CompileRun("testCallNormal(obj)"); 5223 CHECK_EQ(106, named_access_count); 5224 5225 context1->Exit(); 5226 context0->Exit(); 5227 context1.Dispose(); 5228 context0.Dispose(); 5229} 5230 5231 5232static bool NamedAccessFlatten(Local<v8::Object> global, 5233 Local<Value> name, 5234 v8::AccessType type, 5235 Local<Value> data) { 5236 char buf[100]; 5237 int len; 5238 5239 CHECK(name->IsString()); 5240 5241 memset(buf, 0x1, sizeof(buf)); 5242 len = name.As<String>()->WriteAscii(buf); 5243 CHECK_EQ(4, len); 5244 5245 uint16_t buf2[100]; 5246 5247 memset(buf, 0x1, sizeof(buf)); 5248 len = name.As<String>()->Write(buf2); 5249 CHECK_EQ(4, len); 5250 5251 return true; 5252} 5253 5254 5255static bool IndexedAccessFlatten(Local<v8::Object> global, 5256 uint32_t key, 5257 v8::AccessType type, 5258 Local<Value> data) { 5259 return true; 5260} 5261 5262 5263// Regression test. In access checks, operations that may cause 5264// garbage collection are not allowed. It used to be the case that 5265// using the Write operation on a string could cause a garbage 5266// collection due to flattening of the string. This is no longer the 5267// case. 5268THREADED_TEST(AccessControlFlatten) { 5269 named_access_count = 0; 5270 indexed_access_count = 0; 5271 5272 v8::HandleScope handle_scope; 5273 5274 // Create an environment. 5275 v8::Persistent<Context> context0 = Context::New(); 5276 context0->Enter(); 5277 5278 // Create an object that requires access-check functions to be 5279 // called for cross-domain access. 5280 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 5281 object_template->SetAccessCheckCallbacks(NamedAccessFlatten, 5282 IndexedAccessFlatten); 5283 Local<v8::Object> object = object_template->NewInstance(); 5284 5285 v8::HandleScope scope1; 5286 5287 // Create another environment. 5288 v8::Persistent<Context> context1 = Context::New(); 5289 context1->Enter(); 5290 5291 // Make easy access to the object from the other environment. 5292 v8::Handle<v8::Object> global1 = context1->Global(); 5293 global1->Set(v8_str("obj"), object); 5294 5295 v8::Handle<Value> value; 5296 5297 value = v8_compile("var p = 'as' + 'df';")->Run(); 5298 value = v8_compile("obj[p];")->Run(); 5299 5300 context1->Exit(); 5301 context0->Exit(); 5302 context1.Dispose(); 5303 context0.Dispose(); 5304} 5305 5306 5307static v8::Handle<Value> AccessControlNamedGetter( 5308 Local<String>, const AccessorInfo&) { 5309 return v8::Integer::New(42); 5310} 5311 5312 5313static v8::Handle<Value> AccessControlNamedSetter( 5314 Local<String>, Local<Value> value, const AccessorInfo&) { 5315 return value; 5316} 5317 5318 5319static v8::Handle<Value> AccessControlIndexedGetter( 5320 uint32_t index, 5321 const AccessorInfo& info) { 5322 return v8_num(42); 5323} 5324 5325 5326static v8::Handle<Value> AccessControlIndexedSetter( 5327 uint32_t, Local<Value> value, const AccessorInfo&) { 5328 return value; 5329} 5330 5331 5332THREADED_TEST(AccessControlInterceptorIC) { 5333 named_access_count = 0; 5334 indexed_access_count = 0; 5335 5336 v8::HandleScope handle_scope; 5337 5338 // Create an environment. 5339 v8::Persistent<Context> context0 = Context::New(); 5340 context0->Enter(); 5341 5342 // Create an object that requires access-check functions to be 5343 // called for cross-domain access. The object also has interceptors 5344 // interceptor. 5345 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 5346 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 5347 IndexedAccessCounter); 5348 object_template->SetNamedPropertyHandler(AccessControlNamedGetter, 5349 AccessControlNamedSetter); 5350 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter, 5351 AccessControlIndexedSetter); 5352 Local<v8::Object> object = object_template->NewInstance(); 5353 5354 v8::HandleScope scope1; 5355 5356 // Create another environment. 5357 v8::Persistent<Context> context1 = Context::New(); 5358 context1->Enter(); 5359 5360 // Make easy access to the object from the other environment. 5361 v8::Handle<v8::Object> global1 = context1->Global(); 5362 global1->Set(v8_str("obj"), object); 5363 5364 v8::Handle<Value> value; 5365 5366 // Check that the named access-control function is called every time 5367 // eventhough there is an interceptor on the object. 5368 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run(); 5369 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;" 5370 "obj.x")->Run(); 5371 CHECK(value->IsNumber()); 5372 CHECK_EQ(42, value->Int32Value()); 5373 CHECK_EQ(21, named_access_count); 5374 5375 value = v8_compile("var p = 'x';")->Run(); 5376 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run(); 5377 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];" 5378 "obj[p]")->Run(); 5379 CHECK(value->IsNumber()); 5380 CHECK_EQ(42, value->Int32Value()); 5381 CHECK_EQ(42, named_access_count); 5382 5383 // Check that the indexed access-control function is called every 5384 // time eventhough there is an interceptor on the object. 5385 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run(); 5386 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];" 5387 "obj[0]")->Run(); 5388 CHECK(value->IsNumber()); 5389 CHECK_EQ(42, value->Int32Value()); 5390 CHECK_EQ(21, indexed_access_count); 5391 5392 context1->Exit(); 5393 context0->Exit(); 5394 context1.Dispose(); 5395 context0.Dispose(); 5396} 5397 5398 5399THREADED_TEST(Version) { 5400 v8::V8::GetVersion(); 5401} 5402 5403 5404static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) { 5405 ApiTestFuzzer::Fuzz(); 5406 return v8_num(12); 5407} 5408 5409 5410THREADED_TEST(InstanceProperties) { 5411 v8::HandleScope handle_scope; 5412 LocalContext context; 5413 5414 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5415 Local<ObjectTemplate> instance = t->InstanceTemplate(); 5416 5417 instance->Set(v8_str("x"), v8_num(42)); 5418 instance->Set(v8_str("f"), 5419 v8::FunctionTemplate::New(InstanceFunctionCallback)); 5420 5421 Local<Value> o = t->GetFunction()->NewInstance(); 5422 5423 context->Global()->Set(v8_str("i"), o); 5424 Local<Value> value = Script::Compile(v8_str("i.x"))->Run(); 5425 CHECK_EQ(42, value->Int32Value()); 5426 5427 value = Script::Compile(v8_str("i.f()"))->Run(); 5428 CHECK_EQ(12, value->Int32Value()); 5429} 5430 5431 5432static v8::Handle<Value> 5433GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) { 5434 ApiTestFuzzer::Fuzz(); 5435 return v8::Handle<Value>(); 5436} 5437 5438 5439THREADED_TEST(GlobalObjectInstanceProperties) { 5440 v8::HandleScope handle_scope; 5441 5442 Local<Value> global_object; 5443 5444 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5445 t->InstanceTemplate()->SetNamedPropertyHandler( 5446 GlobalObjectInstancePropertiesGet); 5447 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 5448 instance_template->Set(v8_str("x"), v8_num(42)); 5449 instance_template->Set(v8_str("f"), 5450 v8::FunctionTemplate::New(InstanceFunctionCallback)); 5451 5452 { 5453 LocalContext env(NULL, instance_template); 5454 // Hold on to the global object so it can be used again in another 5455 // environment initialization. 5456 global_object = env->Global(); 5457 5458 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 5459 CHECK_EQ(42, value->Int32Value()); 5460 value = Script::Compile(v8_str("f()"))->Run(); 5461 CHECK_EQ(12, value->Int32Value()); 5462 } 5463 5464 { 5465 // Create new environment reusing the global object. 5466 LocalContext env(NULL, instance_template, global_object); 5467 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 5468 CHECK_EQ(42, value->Int32Value()); 5469 value = Script::Compile(v8_str("f()"))->Run(); 5470 CHECK_EQ(12, value->Int32Value()); 5471 } 5472} 5473 5474 5475static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) { 5476 ApiTestFuzzer::Fuzz(); 5477 return v8_num(42); 5478} 5479 5480 5481static int shadow_y; 5482static int shadow_y_setter_call_count; 5483static int shadow_y_getter_call_count; 5484 5485 5486static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) { 5487 shadow_y_setter_call_count++; 5488 shadow_y = 42; 5489} 5490 5491 5492static v8::Handle<Value> ShadowYGetter(Local<String> name, 5493 const AccessorInfo& info) { 5494 ApiTestFuzzer::Fuzz(); 5495 shadow_y_getter_call_count++; 5496 return v8_num(shadow_y); 5497} 5498 5499 5500static v8::Handle<Value> ShadowIndexedGet(uint32_t index, 5501 const AccessorInfo& info) { 5502 return v8::Handle<Value>(); 5503} 5504 5505 5506static v8::Handle<Value> ShadowNamedGet(Local<String> key, 5507 const AccessorInfo&) { 5508 return v8::Handle<Value>(); 5509} 5510 5511 5512THREADED_TEST(ShadowObject) { 5513 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0; 5514 v8::HandleScope handle_scope; 5515 5516 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New(); 5517 LocalContext context(NULL, global_template); 5518 5519 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5520 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet); 5521 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet); 5522 Local<ObjectTemplate> proto = t->PrototypeTemplate(); 5523 Local<ObjectTemplate> instance = t->InstanceTemplate(); 5524 5525 // Only allow calls of f on instances of t. 5526 Local<v8::Signature> signature = v8::Signature::New(t); 5527 proto->Set(v8_str("f"), 5528 v8::FunctionTemplate::New(ShadowFunctionCallback, 5529 Local<Value>(), 5530 signature)); 5531 proto->Set(v8_str("x"), v8_num(12)); 5532 5533 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter); 5534 5535 Local<Value> o = t->GetFunction()->NewInstance(); 5536 context->Global()->Set(v8_str("__proto__"), o); 5537 5538 Local<Value> value = 5539 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run(); 5540 CHECK(value->IsBoolean()); 5541 CHECK(!value->BooleanValue()); 5542 5543 value = Script::Compile(v8_str("x"))->Run(); 5544 CHECK_EQ(12, value->Int32Value()); 5545 5546 value = Script::Compile(v8_str("f()"))->Run(); 5547 CHECK_EQ(42, value->Int32Value()); 5548 5549 Script::Compile(v8_str("y = 42"))->Run(); 5550 CHECK_EQ(1, shadow_y_setter_call_count); 5551 value = Script::Compile(v8_str("y"))->Run(); 5552 CHECK_EQ(1, shadow_y_getter_call_count); 5553 CHECK_EQ(42, value->Int32Value()); 5554} 5555 5556 5557THREADED_TEST(HiddenPrototype) { 5558 v8::HandleScope handle_scope; 5559 LocalContext context; 5560 5561 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 5562 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 5563 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 5564 t1->SetHiddenPrototype(true); 5565 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 5566 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 5567 t2->SetHiddenPrototype(true); 5568 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 5569 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 5570 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 5571 5572 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 5573 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 5574 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 5575 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 5576 5577 // Setting the prototype on an object skips hidden prototypes. 5578 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5579 o0->Set(v8_str("__proto__"), o1); 5580 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5581 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5582 o0->Set(v8_str("__proto__"), o2); 5583 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5584 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5585 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5586 o0->Set(v8_str("__proto__"), o3); 5587 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5588 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5589 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5590 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 5591 5592 // Getting the prototype of o0 should get the first visible one 5593 // which is o3. Therefore, z should not be defined on the prototype 5594 // object. 5595 Local<Value> proto = o0->Get(v8_str("__proto__")); 5596 CHECK(proto->IsObject()); 5597 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined()); 5598} 5599 5600 5601THREADED_TEST(SetPrototype) { 5602 v8::HandleScope handle_scope; 5603 LocalContext context; 5604 5605 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 5606 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 5607 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 5608 t1->SetHiddenPrototype(true); 5609 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 5610 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 5611 t2->SetHiddenPrototype(true); 5612 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 5613 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 5614 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 5615 5616 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 5617 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 5618 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 5619 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 5620 5621 // Setting the prototype on an object does not skip hidden prototypes. 5622 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5623 CHECK(o0->SetPrototype(o1)); 5624 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5625 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5626 CHECK(o1->SetPrototype(o2)); 5627 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5628 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5629 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5630 CHECK(o2->SetPrototype(o3)); 5631 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5632 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5633 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5634 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 5635 5636 // Getting the prototype of o0 should get the first visible one 5637 // which is o3. Therefore, z should not be defined on the prototype 5638 // object. 5639 Local<Value> proto = o0->Get(v8_str("__proto__")); 5640 CHECK(proto->IsObject()); 5641 CHECK_EQ(proto.As<v8::Object>(), o3); 5642 5643 // However, Object::GetPrototype ignores hidden prototype. 5644 Local<Value> proto0 = o0->GetPrototype(); 5645 CHECK(proto0->IsObject()); 5646 CHECK_EQ(proto0.As<v8::Object>(), o1); 5647 5648 Local<Value> proto1 = o1->GetPrototype(); 5649 CHECK(proto1->IsObject()); 5650 CHECK_EQ(proto1.As<v8::Object>(), o2); 5651 5652 Local<Value> proto2 = o2->GetPrototype(); 5653 CHECK(proto2->IsObject()); 5654 CHECK_EQ(proto2.As<v8::Object>(), o3); 5655} 5656 5657 5658THREADED_TEST(SetPrototypeThrows) { 5659 v8::HandleScope handle_scope; 5660 LocalContext context; 5661 5662 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5663 5664 Local<v8::Object> o0 = t->GetFunction()->NewInstance(); 5665 Local<v8::Object> o1 = t->GetFunction()->NewInstance(); 5666 5667 CHECK(o0->SetPrototype(o1)); 5668 // If setting the prototype leads to the cycle, SetPrototype should 5669 // return false and keep VM in sane state. 5670 v8::TryCatch try_catch; 5671 CHECK(!o1->SetPrototype(o0)); 5672 CHECK(!try_catch.HasCaught()); 5673 ASSERT(!i::Top::has_pending_exception()); 5674 5675 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value()); 5676} 5677 5678 5679THREADED_TEST(GetterSetterExceptions) { 5680 v8::HandleScope handle_scope; 5681 LocalContext context; 5682 CompileRun( 5683 "function Foo() { };" 5684 "function Throw() { throw 5; };" 5685 "var x = { };" 5686 "x.__defineSetter__('set', Throw);" 5687 "x.__defineGetter__('get', Throw);"); 5688 Local<v8::Object> x = 5689 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x"))); 5690 v8::TryCatch try_catch; 5691 x->Set(v8_str("set"), v8::Integer::New(8)); 5692 x->Get(v8_str("get")); 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} 5700 5701 5702THREADED_TEST(Constructor) { 5703 v8::HandleScope handle_scope; 5704 LocalContext context; 5705 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 5706 templ->SetClassName(v8_str("Fun")); 5707 Local<Function> cons = templ->GetFunction(); 5708 context->Global()->Set(v8_str("Fun"), cons); 5709 Local<v8::Object> inst = cons->NewInstance(); 5710 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst); 5711 Local<Value> value = CompileRun("(new Fun()).constructor === Fun"); 5712 CHECK(value->BooleanValue()); 5713} 5714 5715THREADED_TEST(FunctionDescriptorException) { 5716 v8::HandleScope handle_scope; 5717 LocalContext context; 5718 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 5719 templ->SetClassName(v8_str("Fun")); 5720 Local<Function> cons = templ->GetFunction(); 5721 context->Global()->Set(v8_str("Fun"), cons); 5722 Local<Value> value = CompileRun( 5723 "function test() {" 5724 " try {" 5725 " (new Fun()).blah()" 5726 " } catch (e) {" 5727 " var str = String(e);" 5728 " if (str.indexOf('TypeError') == -1) return 1;" 5729 " if (str.indexOf('[object Fun]') != -1) return 2;" 5730 " if (str.indexOf('#<a Fun>') == -1) return 3;" 5731 " return 0;" 5732 " }" 5733 " return 4;" 5734 "}" 5735 "test();"); 5736 CHECK_EQ(0, value->Int32Value()); 5737} 5738 5739 5740THREADED_TEST(EvalAliasedDynamic) { 5741 v8::HandleScope scope; 5742 LocalContext current; 5743 5744 // Tests where aliased eval can only be resolved dynamically. 5745 Local<Script> script = 5746 Script::Compile(v8_str("function f(x) { " 5747 " var foo = 2;" 5748 " with (x) { return eval('foo'); }" 5749 "}" 5750 "foo = 0;" 5751 "result1 = f(new Object());" 5752 "result2 = f(this);" 5753 "var x = new Object();" 5754 "x.eval = function(x) { return 1; };" 5755 "result3 = f(x);")); 5756 script->Run(); 5757 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value()); 5758 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value()); 5759 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value()); 5760 5761 v8::TryCatch try_catch; 5762 script = 5763 Script::Compile(v8_str("function f(x) { " 5764 " var bar = 2;" 5765 " with (x) { return eval('bar'); }" 5766 "}" 5767 "f(this)")); 5768 script->Run(); 5769 CHECK(try_catch.HasCaught()); 5770 try_catch.Reset(); 5771} 5772 5773 5774THREADED_TEST(CrossEval) { 5775 v8::HandleScope scope; 5776 LocalContext other; 5777 LocalContext current; 5778 5779 Local<String> token = v8_str("<security token>"); 5780 other->SetSecurityToken(token); 5781 current->SetSecurityToken(token); 5782 5783 // Setup reference from current to other. 5784 current->Global()->Set(v8_str("other"), other->Global()); 5785 5786 // Check that new variables are introduced in other context. 5787 Local<Script> script = 5788 Script::Compile(v8_str("other.eval('var foo = 1234')")); 5789 script->Run(); 5790 Local<Value> foo = other->Global()->Get(v8_str("foo")); 5791 CHECK_EQ(1234, foo->Int32Value()); 5792 CHECK(!current->Global()->Has(v8_str("foo"))); 5793 5794 // Check that writing to non-existing properties introduces them in 5795 // the other context. 5796 script = 5797 Script::Compile(v8_str("other.eval('na = 1234')")); 5798 script->Run(); 5799 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value()); 5800 CHECK(!current->Global()->Has(v8_str("na"))); 5801 5802 // Check that global variables in current context are not visible in other 5803 // context. 5804 v8::TryCatch try_catch; 5805 script = 5806 Script::Compile(v8_str("var bar = 42; other.eval('bar');")); 5807 Local<Value> result = script->Run(); 5808 CHECK(try_catch.HasCaught()); 5809 try_catch.Reset(); 5810 5811 // Check that local variables in current context are not visible in other 5812 // context. 5813 script = 5814 Script::Compile(v8_str("(function() { " 5815 " var baz = 87;" 5816 " return other.eval('baz');" 5817 "})();")); 5818 result = script->Run(); 5819 CHECK(try_catch.HasCaught()); 5820 try_catch.Reset(); 5821 5822 // Check that global variables in the other environment are visible 5823 // when evaluting code. 5824 other->Global()->Set(v8_str("bis"), v8_num(1234)); 5825 script = Script::Compile(v8_str("other.eval('bis')")); 5826 CHECK_EQ(1234, script->Run()->Int32Value()); 5827 CHECK(!try_catch.HasCaught()); 5828 5829 // Check that the 'this' pointer points to the global object evaluating 5830 // code. 5831 other->Global()->Set(v8_str("t"), other->Global()); 5832 script = Script::Compile(v8_str("other.eval('this == t')")); 5833 result = script->Run(); 5834 CHECK(result->IsTrue()); 5835 CHECK(!try_catch.HasCaught()); 5836 5837 // Check that variables introduced in with-statement are not visible in 5838 // other context. 5839 script = 5840 Script::Compile(v8_str("with({x:2}){other.eval('x')}")); 5841 result = script->Run(); 5842 CHECK(try_catch.HasCaught()); 5843 try_catch.Reset(); 5844 5845 // Check that you cannot use 'eval.call' with another object than the 5846 // current global object. 5847 script = 5848 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')")); 5849 result = script->Run(); 5850 CHECK(try_catch.HasCaught()); 5851} 5852 5853 5854// Test that calling eval in a context which has been detached from 5855// its global throws an exception. This behavior is consistent with 5856// other JavaScript implementations. 5857THREADED_TEST(EvalInDetachedGlobal) { 5858 v8::HandleScope scope; 5859 5860 v8::Persistent<Context> context0 = Context::New(); 5861 v8::Persistent<Context> context1 = Context::New(); 5862 5863 // Setup function in context0 that uses eval from context0. 5864 context0->Enter(); 5865 v8::Handle<v8::Value> fun = 5866 CompileRun("var x = 42;" 5867 "(function() {" 5868 " var e = eval;" 5869 " return function(s) { return e(s); }" 5870 "})()"); 5871 context0->Exit(); 5872 5873 // Put the function into context1 and call it before and after 5874 // detaching the global. Before detaching, the call succeeds and 5875 // after detaching and exception is thrown. 5876 context1->Enter(); 5877 context1->Global()->Set(v8_str("fun"), fun); 5878 v8::Handle<v8::Value> x_value = CompileRun("fun('x')"); 5879 CHECK_EQ(42, x_value->Int32Value()); 5880 context0->DetachGlobal(); 5881 v8::TryCatch catcher; 5882 x_value = CompileRun("fun('x')"); 5883 CHECK(x_value.IsEmpty()); 5884 CHECK(catcher.HasCaught()); 5885 context1->Exit(); 5886 5887 context1.Dispose(); 5888 context0.Dispose(); 5889} 5890 5891 5892THREADED_TEST(CrossLazyLoad) { 5893 v8::HandleScope scope; 5894 LocalContext other; 5895 LocalContext current; 5896 5897 Local<String> token = v8_str("<security token>"); 5898 other->SetSecurityToken(token); 5899 current->SetSecurityToken(token); 5900 5901 // Setup reference from current to other. 5902 current->Global()->Set(v8_str("other"), other->Global()); 5903 5904 // Trigger lazy loading in other context. 5905 Local<Script> script = 5906 Script::Compile(v8_str("other.eval('new Date(42)')")); 5907 Local<Value> value = script->Run(); 5908 CHECK_EQ(42.0, value->NumberValue()); 5909} 5910 5911 5912static v8::Handle<Value> call_as_function(const v8::Arguments& args) { 5913 ApiTestFuzzer::Fuzz(); 5914 if (args.IsConstructCall()) { 5915 if (args[0]->IsInt32()) { 5916 return v8_num(-args[0]->Int32Value()); 5917 } 5918 } 5919 5920 return args[0]; 5921} 5922 5923 5924// Test that a call handler can be set for objects which will allow 5925// non-function objects created through the API to be called as 5926// functions. 5927THREADED_TEST(CallAsFunction) { 5928 v8::HandleScope scope; 5929 LocalContext context; 5930 5931 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5932 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 5933 instance_template->SetCallAsFunctionHandler(call_as_function); 5934 Local<v8::Object> instance = t->GetFunction()->NewInstance(); 5935 context->Global()->Set(v8_str("obj"), instance); 5936 v8::TryCatch try_catch; 5937 Local<Value> value; 5938 CHECK(!try_catch.HasCaught()); 5939 5940 value = CompileRun("obj(42)"); 5941 CHECK(!try_catch.HasCaught()); 5942 CHECK_EQ(42, value->Int32Value()); 5943 5944 value = CompileRun("(function(o){return o(49)})(obj)"); 5945 CHECK(!try_catch.HasCaught()); 5946 CHECK_EQ(49, value->Int32Value()); 5947 5948 // test special case of call as function 5949 value = CompileRun("[obj]['0'](45)"); 5950 CHECK(!try_catch.HasCaught()); 5951 CHECK_EQ(45, value->Int32Value()); 5952 5953 value = CompileRun("obj.call = Function.prototype.call;" 5954 "obj.call(null, 87)"); 5955 CHECK(!try_catch.HasCaught()); 5956 CHECK_EQ(87, value->Int32Value()); 5957 5958 // Regression tests for bug #1116356: Calling call through call/apply 5959 // must work for non-function receivers. 5960 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])"; 5961 value = CompileRun(apply_99); 5962 CHECK(!try_catch.HasCaught()); 5963 CHECK_EQ(99, value->Int32Value()); 5964 5965 const char* call_17 = "Function.prototype.call.call(obj, this, 17)"; 5966 value = CompileRun(call_17); 5967 CHECK(!try_catch.HasCaught()); 5968 CHECK_EQ(17, value->Int32Value()); 5969 5970 // Check that the call-as-function handler can be called through 5971 // new. 5972 value = CompileRun("new obj(43)"); 5973 CHECK(!try_catch.HasCaught()); 5974 CHECK_EQ(-43, value->Int32Value()); 5975} 5976 5977 5978static int CountHandles() { 5979 return v8::HandleScope::NumberOfHandles(); 5980} 5981 5982 5983static int Recurse(int depth, int iterations) { 5984 v8::HandleScope scope; 5985 if (depth == 0) return CountHandles(); 5986 for (int i = 0; i < iterations; i++) { 5987 Local<v8::Number> n = v8::Integer::New(42); 5988 } 5989 return Recurse(depth - 1, iterations); 5990} 5991 5992 5993THREADED_TEST(HandleIteration) { 5994 static const int kIterations = 500; 5995 static const int kNesting = 200; 5996 CHECK_EQ(0, CountHandles()); 5997 { 5998 v8::HandleScope scope1; 5999 CHECK_EQ(0, CountHandles()); 6000 for (int i = 0; i < kIterations; i++) { 6001 Local<v8::Number> n = v8::Integer::New(42); 6002 CHECK_EQ(i + 1, CountHandles()); 6003 } 6004 6005 CHECK_EQ(kIterations, CountHandles()); 6006 { 6007 v8::HandleScope scope2; 6008 for (int j = 0; j < kIterations; j++) { 6009 Local<v8::Number> n = v8::Integer::New(42); 6010 CHECK_EQ(j + 1 + kIterations, CountHandles()); 6011 } 6012 } 6013 CHECK_EQ(kIterations, CountHandles()); 6014 } 6015 CHECK_EQ(0, CountHandles()); 6016 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations)); 6017} 6018 6019 6020static v8::Handle<Value> InterceptorHasOwnPropertyGetter( 6021 Local<String> name, 6022 const AccessorInfo& info) { 6023 ApiTestFuzzer::Fuzz(); 6024 return v8::Handle<Value>(); 6025} 6026 6027 6028THREADED_TEST(InterceptorHasOwnProperty) { 6029 v8::HandleScope scope; 6030 LocalContext context; 6031 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6032 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 6033 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter); 6034 Local<Function> function = fun_templ->GetFunction(); 6035 context->Global()->Set(v8_str("constructor"), function); 6036 v8::Handle<Value> value = CompileRun( 6037 "var o = new constructor();" 6038 "o.hasOwnProperty('ostehaps');"); 6039 CHECK_EQ(false, value->BooleanValue()); 6040 value = CompileRun( 6041 "o.ostehaps = 42;" 6042 "o.hasOwnProperty('ostehaps');"); 6043 CHECK_EQ(true, value->BooleanValue()); 6044 value = CompileRun( 6045 "var p = new constructor();" 6046 "p.hasOwnProperty('ostehaps');"); 6047 CHECK_EQ(false, value->BooleanValue()); 6048} 6049 6050 6051static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC( 6052 Local<String> name, 6053 const AccessorInfo& info) { 6054 ApiTestFuzzer::Fuzz(); 6055 i::Heap::CollectAllGarbage(false); 6056 return v8::Handle<Value>(); 6057} 6058 6059 6060THREADED_TEST(InterceptorHasOwnPropertyCausingGC) { 6061 v8::HandleScope scope; 6062 LocalContext context; 6063 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6064 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 6065 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC); 6066 Local<Function> function = fun_templ->GetFunction(); 6067 context->Global()->Set(v8_str("constructor"), function); 6068 // Let's first make some stuff so we can be sure to get a good GC. 6069 CompileRun( 6070 "function makestr(size) {" 6071 " switch (size) {" 6072 " case 1: return 'f';" 6073 " case 2: return 'fo';" 6074 " case 3: return 'foo';" 6075 " }" 6076 " return makestr(size >> 1) + makestr((size + 1) >> 1);" 6077 "}" 6078 "var x = makestr(12345);" 6079 "x = makestr(31415);" 6080 "x = makestr(23456);"); 6081 v8::Handle<Value> value = CompileRun( 6082 "var o = new constructor();" 6083 "o.__proto__ = new String(x);" 6084 "o.hasOwnProperty('ostehaps');"); 6085 CHECK_EQ(false, value->BooleanValue()); 6086} 6087 6088 6089typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property, 6090 const AccessorInfo& info); 6091 6092 6093static void CheckInterceptorLoadIC(NamedPropertyGetter getter, 6094 const char* source, 6095 int expected) { 6096 v8::HandleScope scope; 6097 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6098 templ->SetNamedPropertyHandler(getter); 6099 LocalContext context; 6100 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6101 v8::Handle<Value> value = CompileRun(source); 6102 CHECK_EQ(expected, value->Int32Value()); 6103} 6104 6105 6106static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name, 6107 const AccessorInfo& info) { 6108 ApiTestFuzzer::Fuzz(); 6109 CHECK(v8_str("x")->Equals(name)); 6110 return v8::Integer::New(42); 6111} 6112 6113 6114// This test should hit the load IC for the interceptor case. 6115THREADED_TEST(InterceptorLoadIC) { 6116 CheckInterceptorLoadIC(InterceptorLoadICGetter, 6117 "var result = 0;" 6118 "for (var i = 0; i < 1000; i++) {" 6119 " result = o.x;" 6120 "}", 6121 42); 6122} 6123 6124 6125// Below go several tests which verify that JITing for various 6126// configurations of interceptor and explicit fields works fine 6127// (those cases are special cased to get better performance). 6128 6129static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name, 6130 const AccessorInfo& info) { 6131 ApiTestFuzzer::Fuzz(); 6132 return v8_str("x")->Equals(name) 6133 ? v8::Integer::New(42) : v8::Handle<v8::Value>(); 6134} 6135 6136 6137THREADED_TEST(InterceptorLoadICWithFieldOnHolder) { 6138 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6139 "var result = 0;" 6140 "o.y = 239;" 6141 "for (var i = 0; i < 1000; i++) {" 6142 " result = o.y;" 6143 "}", 6144 239); 6145} 6146 6147 6148THREADED_TEST(InterceptorLoadICWithSubstitutedProto) { 6149 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6150 "var result = 0;" 6151 "o.__proto__ = { 'y': 239 };" 6152 "for (var i = 0; i < 1000; i++) {" 6153 " result = o.y + o.x;" 6154 "}", 6155 239 + 42); 6156} 6157 6158 6159THREADED_TEST(InterceptorLoadICWithPropertyOnProto) { 6160 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6161 "var result = 0;" 6162 "o.__proto__.y = 239;" 6163 "for (var i = 0; i < 1000; i++) {" 6164 " result = o.y + o.x;" 6165 "}", 6166 239 + 42); 6167} 6168 6169 6170THREADED_TEST(InterceptorLoadICUndefined) { 6171 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6172 "var result = 0;" 6173 "for (var i = 0; i < 1000; i++) {" 6174 " result = (o.y == undefined) ? 239 : 42;" 6175 "}", 6176 239); 6177} 6178 6179 6180THREADED_TEST(InterceptorLoadICWithOverride) { 6181 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6182 "fst = new Object(); fst.__proto__ = o;" 6183 "snd = new Object(); snd.__proto__ = fst;" 6184 "var result1 = 0;" 6185 "for (var i = 0; i < 1000; i++) {" 6186 " result1 = snd.x;" 6187 "}" 6188 "fst.x = 239;" 6189 "var result = 0;" 6190 "for (var i = 0; i < 1000; i++) {" 6191 " result = snd.x;" 6192 "}" 6193 "result + result1", 6194 239 + 42); 6195} 6196 6197 6198// Test the case when we stored field into 6199// a stub, but interceptor produced value on its own. 6200THREADED_TEST(InterceptorLoadICFieldNotNeeded) { 6201 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6202 "proto = new Object();" 6203 "o.__proto__ = proto;" 6204 "proto.x = 239;" 6205 "for (var i = 0; i < 1000; i++) {" 6206 " o.x;" 6207 // Now it should be ICed and keep a reference to x defined on proto 6208 "}" 6209 "var result = 0;" 6210 "for (var i = 0; i < 1000; i++) {" 6211 " result += o.x;" 6212 "}" 6213 "result;", 6214 42 * 1000); 6215} 6216 6217 6218// Test the case when we stored field into 6219// a stub, but it got invalidated later on. 6220THREADED_TEST(InterceptorLoadICInvalidatedField) { 6221 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6222 "proto1 = new Object();" 6223 "proto2 = new Object();" 6224 "o.__proto__ = proto1;" 6225 "proto1.__proto__ = proto2;" 6226 "proto2.y = 239;" 6227 "for (var i = 0; i < 1000; i++) {" 6228 " o.y;" 6229 // Now it should be ICed and keep a reference to y defined on proto2 6230 "}" 6231 "proto1.y = 42;" 6232 "var result = 0;" 6233 "for (var i = 0; i < 1000; i++) {" 6234 " result += o.y;" 6235 "}" 6236 "result;", 6237 42 * 1000); 6238} 6239 6240 6241static int interceptor_load_not_handled_calls = 0; 6242static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name, 6243 const AccessorInfo& info) { 6244 ++interceptor_load_not_handled_calls; 6245 return v8::Handle<v8::Value>(); 6246} 6247 6248 6249// Test how post-interceptor lookups are done in the non-cacheable 6250// case: the interceptor should not be invoked during this lookup. 6251THREADED_TEST(InterceptorLoadICPostInterceptor) { 6252 interceptor_load_not_handled_calls = 0; 6253 CheckInterceptorLoadIC(InterceptorLoadNotHandled, 6254 "receiver = new Object();" 6255 "receiver.__proto__ = o;" 6256 "proto = new Object();" 6257 "/* Make proto a slow-case object. */" 6258 "for (var i = 0; i < 1000; i++) {" 6259 " proto[\"xxxxxxxx\" + i] = [];" 6260 "}" 6261 "proto.x = 17;" 6262 "o.__proto__ = proto;" 6263 "var result = 0;" 6264 "for (var i = 0; i < 1000; i++) {" 6265 " result += receiver.x;" 6266 "}" 6267 "result;", 6268 17 * 1000); 6269 CHECK_EQ(1000, interceptor_load_not_handled_calls); 6270} 6271 6272 6273// Test the case when we stored field into 6274// a stub, but it got invalidated later on due to override on 6275// global object which is between interceptor and fields' holders. 6276THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) { 6277 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6278 "o.__proto__ = this;" // set a global to be a proto of o. 6279 "this.__proto__.y = 239;" 6280 "for (var i = 0; i < 10; i++) {" 6281 " if (o.y != 239) throw 'oops: ' + o.y;" 6282 // Now it should be ICed and keep a reference to y defined on field_holder. 6283 "}" 6284 "this.y = 42;" // Assign on a global. 6285 "var result = 0;" 6286 "for (var i = 0; i < 10; i++) {" 6287 " result += o.y;" 6288 "}" 6289 "result;", 6290 42 * 10); 6291} 6292 6293 6294static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) { 6295 ApiTestFuzzer::Fuzz(); 6296 return v8_num(239); 6297} 6298 6299 6300static void SetOnThis(Local<String> name, 6301 Local<Value> value, 6302 const AccessorInfo& info) { 6303 info.This()->ForceSet(name, value); 6304} 6305 6306 6307THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) { 6308 v8::HandleScope scope; 6309 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6310 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6311 templ->SetAccessor(v8_str("y"), Return239); 6312 LocalContext context; 6313 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6314 6315 // Check the case when receiver and interceptor's holder 6316 // are the same objects. 6317 v8::Handle<Value> value = CompileRun( 6318 "var result = 0;" 6319 "for (var i = 0; i < 7; i++) {" 6320 " result = o.y;" 6321 "}"); 6322 CHECK_EQ(239, value->Int32Value()); 6323 6324 // Check the case when interceptor's holder is in proto chain 6325 // of receiver. 6326 value = CompileRun( 6327 "r = { __proto__: o };" 6328 "var result = 0;" 6329 "for (var i = 0; i < 7; i++) {" 6330 " result = r.y;" 6331 "}"); 6332 CHECK_EQ(239, value->Int32Value()); 6333} 6334 6335 6336THREADED_TEST(InterceptorLoadICWithCallbackOnProto) { 6337 v8::HandleScope scope; 6338 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6339 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6340 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6341 templ_p->SetAccessor(v8_str("y"), Return239); 6342 6343 LocalContext context; 6344 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6345 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6346 6347 // Check the case when receiver and interceptor's holder 6348 // are the same objects. 6349 v8::Handle<Value> value = CompileRun( 6350 "o.__proto__ = p;" 6351 "var result = 0;" 6352 "for (var i = 0; i < 7; i++) {" 6353 " result = o.x + o.y;" 6354 "}"); 6355 CHECK_EQ(239 + 42, value->Int32Value()); 6356 6357 // Check the case when interceptor's holder is in proto chain 6358 // of receiver. 6359 value = CompileRun( 6360 "r = { __proto__: o };" 6361 "var result = 0;" 6362 "for (var i = 0; i < 7; i++) {" 6363 " result = r.x + r.y;" 6364 "}"); 6365 CHECK_EQ(239 + 42, value->Int32Value()); 6366} 6367 6368 6369THREADED_TEST(InterceptorLoadICForCallbackWithOverride) { 6370 v8::HandleScope scope; 6371 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6372 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6373 templ->SetAccessor(v8_str("y"), Return239); 6374 6375 LocalContext context; 6376 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6377 6378 v8::Handle<Value> value = CompileRun( 6379 "fst = new Object(); fst.__proto__ = o;" 6380 "snd = new Object(); snd.__proto__ = fst;" 6381 "var result1 = 0;" 6382 "for (var i = 0; i < 7; i++) {" 6383 " result1 = snd.x;" 6384 "}" 6385 "fst.x = 239;" 6386 "var result = 0;" 6387 "for (var i = 0; i < 7; i++) {" 6388 " result = snd.x;" 6389 "}" 6390 "result + result1"); 6391 CHECK_EQ(239 + 42, value->Int32Value()); 6392} 6393 6394 6395// Test the case when we stored callback into 6396// a stub, but interceptor produced value on its own. 6397THREADED_TEST(InterceptorLoadICCallbackNotNeeded) { 6398 v8::HandleScope scope; 6399 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6400 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6401 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6402 templ_p->SetAccessor(v8_str("y"), Return239); 6403 6404 LocalContext context; 6405 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6406 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6407 6408 v8::Handle<Value> value = CompileRun( 6409 "o.__proto__ = p;" 6410 "for (var i = 0; i < 7; i++) {" 6411 " o.x;" 6412 // Now it should be ICed and keep a reference to x defined on p 6413 "}" 6414 "var result = 0;" 6415 "for (var i = 0; i < 7; i++) {" 6416 " result += o.x;" 6417 "}" 6418 "result"); 6419 CHECK_EQ(42 * 7, value->Int32Value()); 6420} 6421 6422 6423// Test the case when we stored callback into 6424// a stub, but it got invalidated later on. 6425THREADED_TEST(InterceptorLoadICInvalidatedCallback) { 6426 v8::HandleScope scope; 6427 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6428 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6429 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6430 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 6431 6432 LocalContext context; 6433 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6434 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6435 6436 v8::Handle<Value> value = CompileRun( 6437 "inbetween = new Object();" 6438 "o.__proto__ = inbetween;" 6439 "inbetween.__proto__ = p;" 6440 "for (var i = 0; i < 10; i++) {" 6441 " o.y;" 6442 // Now it should be ICed and keep a reference to y defined on p 6443 "}" 6444 "inbetween.y = 42;" 6445 "var result = 0;" 6446 "for (var i = 0; i < 10; i++) {" 6447 " result += o.y;" 6448 "}" 6449 "result"); 6450 CHECK_EQ(42 * 10, value->Int32Value()); 6451} 6452 6453 6454// Test the case when we stored callback into 6455// a stub, but it got invalidated later on due to override on 6456// global object which is between interceptor and callbacks' holders. 6457THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) { 6458 v8::HandleScope scope; 6459 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6460 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6461 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6462 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 6463 6464 LocalContext context; 6465 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6466 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6467 6468 v8::Handle<Value> value = CompileRun( 6469 "o.__proto__ = this;" 6470 "this.__proto__ = p;" 6471 "for (var i = 0; i < 10; i++) {" 6472 " if (o.y != 239) throw 'oops: ' + o.y;" 6473 // Now it should be ICed and keep a reference to y defined on p 6474 "}" 6475 "this.y = 42;" 6476 "var result = 0;" 6477 "for (var i = 0; i < 10; i++) {" 6478 " result += o.y;" 6479 "}" 6480 "result"); 6481 CHECK_EQ(42 * 10, value->Int32Value()); 6482} 6483 6484 6485static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name, 6486 const AccessorInfo& info) { 6487 ApiTestFuzzer::Fuzz(); 6488 CHECK(v8_str("x")->Equals(name)); 6489 return v8::Integer::New(0); 6490} 6491 6492 6493THREADED_TEST(InterceptorReturningZero) { 6494 CheckInterceptorLoadIC(InterceptorLoadICGetter0, 6495 "o.x == undefined ? 1 : 0", 6496 0); 6497} 6498 6499 6500static v8::Handle<Value> InterceptorStoreICSetter( 6501 Local<String> key, Local<Value> value, const AccessorInfo&) { 6502 CHECK(v8_str("x")->Equals(key)); 6503 CHECK_EQ(42, value->Int32Value()); 6504 return value; 6505} 6506 6507 6508// This test should hit the store IC for the interceptor case. 6509THREADED_TEST(InterceptorStoreIC) { 6510 v8::HandleScope scope; 6511 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6512 templ->SetNamedPropertyHandler(InterceptorLoadICGetter, 6513 InterceptorStoreICSetter); 6514 LocalContext context; 6515 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6516 v8::Handle<Value> value = CompileRun( 6517 "for (var i = 0; i < 1000; i++) {" 6518 " o.x = 42;" 6519 "}"); 6520} 6521 6522 6523THREADED_TEST(InterceptorStoreICWithNoSetter) { 6524 v8::HandleScope scope; 6525 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6526 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6527 LocalContext context; 6528 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6529 v8::Handle<Value> value = CompileRun( 6530 "for (var i = 0; i < 1000; i++) {" 6531 " o.y = 239;" 6532 "}" 6533 "42 + o.y"); 6534 CHECK_EQ(239 + 42, value->Int32Value()); 6535} 6536 6537 6538 6539 6540v8::Handle<Value> call_ic_function; 6541v8::Handle<Value> call_ic_function2; 6542v8::Handle<Value> call_ic_function3; 6543 6544static v8::Handle<Value> InterceptorCallICGetter(Local<String> name, 6545 const AccessorInfo& info) { 6546 ApiTestFuzzer::Fuzz(); 6547 CHECK(v8_str("x")->Equals(name)); 6548 return call_ic_function; 6549} 6550 6551 6552// This test should hit the call IC for the interceptor case. 6553THREADED_TEST(InterceptorCallIC) { 6554 v8::HandleScope scope; 6555 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6556 templ->SetNamedPropertyHandler(InterceptorCallICGetter); 6557 LocalContext context; 6558 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6559 call_ic_function = 6560 v8_compile("function f(x) { return x + 1; }; f")->Run(); 6561 v8::Handle<Value> value = CompileRun( 6562 "var result = 0;" 6563 "for (var i = 0; i < 1000; i++) {" 6564 " result = o.x(41);" 6565 "}"); 6566 CHECK_EQ(42, value->Int32Value()); 6567} 6568 6569 6570// This test checks that if interceptor doesn't provide 6571// a value, we can fetch regular value. 6572THREADED_TEST(InterceptorCallICSeesOthers) { 6573 v8::HandleScope scope; 6574 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6575 templ->SetNamedPropertyHandler(NoBlockGetterX); 6576 LocalContext context; 6577 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6578 v8::Handle<Value> value = CompileRun( 6579 "o.x = function f(x) { return x + 1; };" 6580 "var result = 0;" 6581 "for (var i = 0; i < 7; i++) {" 6582 " result = o.x(41);" 6583 "}"); 6584 CHECK_EQ(42, value->Int32Value()); 6585} 6586 6587 6588static v8::Handle<Value> call_ic_function4; 6589static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name, 6590 const AccessorInfo& info) { 6591 ApiTestFuzzer::Fuzz(); 6592 CHECK(v8_str("x")->Equals(name)); 6593 return call_ic_function4; 6594} 6595 6596 6597// This test checks that if interceptor provides a function, 6598// even if we cached shadowed variant, interceptor's function 6599// is invoked 6600THREADED_TEST(InterceptorCallICCacheableNotNeeded) { 6601 v8::HandleScope scope; 6602 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6603 templ->SetNamedPropertyHandler(InterceptorCallICGetter4); 6604 LocalContext context; 6605 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6606 call_ic_function4 = 6607 v8_compile("function f(x) { return x - 1; }; f")->Run(); 6608 v8::Handle<Value> value = CompileRun( 6609 "o.__proto__.x = function(x) { return x + 1; };" 6610 "var result = 0;" 6611 "for (var i = 0; i < 1000; i++) {" 6612 " result = o.x(42);" 6613 "}"); 6614 CHECK_EQ(41, value->Int32Value()); 6615} 6616 6617 6618// Test the case when we stored cacheable lookup into 6619// a stub, but it got invalidated later on 6620THREADED_TEST(InterceptorCallICInvalidatedCacheable) { 6621 v8::HandleScope scope; 6622 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6623 templ->SetNamedPropertyHandler(NoBlockGetterX); 6624 LocalContext context; 6625 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6626 v8::Handle<Value> value = CompileRun( 6627 "proto1 = new Object();" 6628 "proto2 = new Object();" 6629 "o.__proto__ = proto1;" 6630 "proto1.__proto__ = proto2;" 6631 "proto2.y = function(x) { return x + 1; };" 6632 // Invoke it many times to compile a stub 6633 "for (var i = 0; i < 7; i++) {" 6634 " o.y(42);" 6635 "}" 6636 "proto1.y = function(x) { return x - 1; };" 6637 "var result = 0;" 6638 "for (var i = 0; i < 7; i++) {" 6639 " result += o.y(42);" 6640 "}"); 6641 CHECK_EQ(41 * 7, value->Int32Value()); 6642} 6643 6644 6645static v8::Handle<Value> call_ic_function5; 6646static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name, 6647 const AccessorInfo& info) { 6648 ApiTestFuzzer::Fuzz(); 6649 if (v8_str("x")->Equals(name)) 6650 return call_ic_function5; 6651 else 6652 return Local<Value>(); 6653} 6654 6655 6656// This test checks that if interceptor doesn't provide a function, 6657// cached constant function is used 6658THREADED_TEST(InterceptorCallICConstantFunctionUsed) { 6659 v8::HandleScope scope; 6660 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6661 templ->SetNamedPropertyHandler(NoBlockGetterX); 6662 LocalContext context; 6663 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6664 v8::Handle<Value> value = CompileRun( 6665 "function inc(x) { return x + 1; };" 6666 "inc(1);" 6667 "o.x = inc;" 6668 "var result = 0;" 6669 "for (var i = 0; i < 1000; i++) {" 6670 " result = o.x(42);" 6671 "}"); 6672 CHECK_EQ(43, value->Int32Value()); 6673} 6674 6675 6676// This test checks that if interceptor provides a function, 6677// even if we cached constant function, interceptor's function 6678// is invoked 6679THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) { 6680 v8::HandleScope scope; 6681 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6682 templ->SetNamedPropertyHandler(InterceptorCallICGetter5); 6683 LocalContext context; 6684 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6685 call_ic_function5 = 6686 v8_compile("function f(x) { return x - 1; }; f")->Run(); 6687 v8::Handle<Value> value = CompileRun( 6688 "function inc(x) { return x + 1; };" 6689 "inc(1);" 6690 "o.x = inc;" 6691 "var result = 0;" 6692 "for (var i = 0; i < 1000; i++) {" 6693 " result = o.x(42);" 6694 "}"); 6695 CHECK_EQ(41, value->Int32Value()); 6696} 6697 6698 6699// Test the case when we stored constant function into 6700// a stub, but it got invalidated later on 6701THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) { 6702 v8::HandleScope scope; 6703 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6704 templ->SetNamedPropertyHandler(NoBlockGetterX); 6705 LocalContext context; 6706 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6707 v8::Handle<Value> value = CompileRun( 6708 "function inc(x) { return x + 1; };" 6709 "inc(1);" 6710 "proto1 = new Object();" 6711 "proto2 = new Object();" 6712 "o.__proto__ = proto1;" 6713 "proto1.__proto__ = proto2;" 6714 "proto2.y = inc;" 6715 // Invoke it many times to compile a stub 6716 "for (var i = 0; i < 7; i++) {" 6717 " o.y(42);" 6718 "}" 6719 "proto1.y = function(x) { return x - 1; };" 6720 "var result = 0;" 6721 "for (var i = 0; i < 7; i++) {" 6722 " result += o.y(42);" 6723 "}"); 6724 CHECK_EQ(41 * 7, value->Int32Value()); 6725} 6726 6727 6728// Test the case when we stored constant function into 6729// a stub, but it got invalidated later on due to override on 6730// global object which is between interceptor and constant function' holders. 6731THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) { 6732 v8::HandleScope scope; 6733 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6734 templ->SetNamedPropertyHandler(NoBlockGetterX); 6735 LocalContext context; 6736 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6737 v8::Handle<Value> value = CompileRun( 6738 "function inc(x) { return x + 1; };" 6739 "inc(1);" 6740 "o.__proto__ = this;" 6741 "this.__proto__.y = inc;" 6742 // Invoke it many times to compile a stub 6743 "for (var i = 0; i < 7; i++) {" 6744 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);" 6745 "}" 6746 "this.y = function(x) { return x - 1; };" 6747 "var result = 0;" 6748 "for (var i = 0; i < 7; i++) {" 6749 " result += o.y(42);" 6750 "}"); 6751 CHECK_EQ(41 * 7, value->Int32Value()); 6752} 6753 6754 6755// Test the case when actual function to call sits on global object. 6756THREADED_TEST(InterceptorCallICCachedFromGlobal) { 6757 v8::HandleScope scope; 6758 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6759 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 6760 6761 LocalContext context; 6762 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6763 6764 v8::Handle<Value> value = CompileRun( 6765 "try {" 6766 " o.__proto__ = this;" 6767 " for (var i = 0; i < 10; i++) {" 6768 " var v = o.parseFloat('239');" 6769 " if (v != 239) throw v;" 6770 // Now it should be ICed and keep a reference to parseFloat. 6771 " }" 6772 " var result = 0;" 6773 " for (var i = 0; i < 10; i++) {" 6774 " result += o.parseFloat('239');" 6775 " }" 6776 " result" 6777 "} catch(e) {" 6778 " e" 6779 "};"); 6780 CHECK_EQ(239 * 10, value->Int32Value()); 6781} 6782 6783static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name, 6784 const AccessorInfo& info) { 6785 ApiTestFuzzer::Fuzz(); 6786 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data())); 6787 ++(*call_count); 6788 if ((*call_count) % 20 == 0) { 6789 v8::internal::Heap::CollectAllGarbage(true); 6790 } 6791 return v8::Handle<Value>(); 6792} 6793 6794static v8::Handle<Value> FastApiCallback_TrivialSignature( 6795 const v8::Arguments& args) { 6796 ApiTestFuzzer::Fuzz(); 6797 CHECK_EQ(args.This(), args.Holder()); 6798 CHECK(args.Data()->Equals(v8_str("method_data"))); 6799 return v8::Integer::New(args[0]->Int32Value() + 1); 6800} 6801 6802static v8::Handle<Value> FastApiCallback_SimpleSignature( 6803 const v8::Arguments& args) { 6804 ApiTestFuzzer::Fuzz(); 6805 CHECK_EQ(args.This()->GetPrototype(), args.Holder()); 6806 CHECK(args.Data()->Equals(v8_str("method_data"))); 6807 // Note, we're using HasRealNamedProperty instead of Has to avoid 6808 // invoking the interceptor again. 6809 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo"))); 6810 return v8::Integer::New(args[0]->Int32Value() + 1); 6811} 6812 6813// Helper to maximize the odds of object moving. 6814static void GenerateSomeGarbage() { 6815 CompileRun( 6816 "var garbage;" 6817 "for (var i = 0; i < 1000; i++) {" 6818 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];" 6819 "}" 6820 "garbage = undefined;"); 6821} 6822 6823THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) { 6824 int interceptor_call_count = 0; 6825 v8::HandleScope scope; 6826 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6827 v8::Handle<v8::FunctionTemplate> method_templ = 6828 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 6829 v8_str("method_data"), 6830 v8::Handle<v8::Signature>()); 6831 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6832 proto_templ->Set(v8_str("method"), method_templ); 6833 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6834 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6835 NULL, NULL, NULL, NULL, 6836 v8::External::Wrap(&interceptor_call_count)); 6837 LocalContext context; 6838 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6839 GenerateSomeGarbage(); 6840 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6841 v8::Handle<Value> value = CompileRun( 6842 "var result = 0;" 6843 "for (var i = 0; i < 100; i++) {" 6844 " result = o.method(41);" 6845 "}"); 6846 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 6847 CHECK_EQ(100, interceptor_call_count); 6848} 6849 6850THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) { 6851 int interceptor_call_count = 0; 6852 v8::HandleScope scope; 6853 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6854 v8::Handle<v8::FunctionTemplate> method_templ = 6855 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6856 v8_str("method_data"), 6857 v8::Signature::New(fun_templ)); 6858 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6859 proto_templ->Set(v8_str("method"), method_templ); 6860 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6861 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6862 NULL, NULL, NULL, NULL, 6863 v8::External::Wrap(&interceptor_call_count)); 6864 LocalContext context; 6865 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6866 GenerateSomeGarbage(); 6867 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6868 v8::Handle<Value> value = CompileRun( 6869 "o.foo = 17;" 6870 "var receiver = {};" 6871 "receiver.__proto__ = o;" 6872 "var result = 0;" 6873 "for (var i = 0; i < 100; i++) {" 6874 " result = receiver.method(41);" 6875 "}"); 6876 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 6877 CHECK_EQ(100, interceptor_call_count); 6878} 6879 6880THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) { 6881 int interceptor_call_count = 0; 6882 v8::HandleScope scope; 6883 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6884 v8::Handle<v8::FunctionTemplate> method_templ = 6885 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6886 v8_str("method_data"), 6887 v8::Signature::New(fun_templ)); 6888 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6889 proto_templ->Set(v8_str("method"), method_templ); 6890 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6891 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6892 NULL, NULL, NULL, NULL, 6893 v8::External::Wrap(&interceptor_call_count)); 6894 LocalContext context; 6895 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6896 GenerateSomeGarbage(); 6897 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6898 v8::Handle<Value> value = CompileRun( 6899 "o.foo = 17;" 6900 "var receiver = {};" 6901 "receiver.__proto__ = o;" 6902 "var result = 0;" 6903 "var saved_result = 0;" 6904 "for (var i = 0; i < 100; i++) {" 6905 " result = receiver.method(41);" 6906 " if (i == 50) {" 6907 " saved_result = result;" 6908 " receiver = {method: function(x) { return x - 1 }};" 6909 " }" 6910 "}"); 6911 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 6912 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 6913 CHECK_GE(interceptor_call_count, 50); 6914} 6915 6916THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) { 6917 int interceptor_call_count = 0; 6918 v8::HandleScope scope; 6919 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6920 v8::Handle<v8::FunctionTemplate> method_templ = 6921 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6922 v8_str("method_data"), 6923 v8::Signature::New(fun_templ)); 6924 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6925 proto_templ->Set(v8_str("method"), method_templ); 6926 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6927 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6928 NULL, NULL, NULL, NULL, 6929 v8::External::Wrap(&interceptor_call_count)); 6930 LocalContext context; 6931 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6932 GenerateSomeGarbage(); 6933 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6934 v8::Handle<Value> value = CompileRun( 6935 "o.foo = 17;" 6936 "var receiver = {};" 6937 "receiver.__proto__ = o;" 6938 "var result = 0;" 6939 "var saved_result = 0;" 6940 "for (var i = 0; i < 100; i++) {" 6941 " result = receiver.method(41);" 6942 " if (i == 50) {" 6943 " saved_result = result;" 6944 " o.method = function(x) { return x - 1 };" 6945 " }" 6946 "}"); 6947 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 6948 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 6949 CHECK_GE(interceptor_call_count, 50); 6950} 6951 6952THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) { 6953 int interceptor_call_count = 0; 6954 v8::HandleScope scope; 6955 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6956 v8::Handle<v8::FunctionTemplate> method_templ = 6957 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6958 v8_str("method_data"), 6959 v8::Signature::New(fun_templ)); 6960 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6961 proto_templ->Set(v8_str("method"), method_templ); 6962 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6963 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6964 NULL, NULL, NULL, NULL, 6965 v8::External::Wrap(&interceptor_call_count)); 6966 LocalContext context; 6967 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6968 GenerateSomeGarbage(); 6969 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6970 v8::TryCatch try_catch; 6971 v8::Handle<Value> value = CompileRun( 6972 "o.foo = 17;" 6973 "var receiver = {};" 6974 "receiver.__proto__ = o;" 6975 "var result = 0;" 6976 "var saved_result = 0;" 6977 "for (var i = 0; i < 100; i++) {" 6978 " result = receiver.method(41);" 6979 " if (i == 50) {" 6980 " saved_result = result;" 6981 " receiver = 333;" 6982 " }" 6983 "}"); 6984 CHECK(try_catch.HasCaught()); 6985 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), 6986 try_catch.Exception()->ToString()); 6987 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 6988 CHECK_GE(interceptor_call_count, 50); 6989} 6990 6991THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) { 6992 int interceptor_call_count = 0; 6993 v8::HandleScope scope; 6994 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6995 v8::Handle<v8::FunctionTemplate> method_templ = 6996 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6997 v8_str("method_data"), 6998 v8::Signature::New(fun_templ)); 6999 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7000 proto_templ->Set(v8_str("method"), method_templ); 7001 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7002 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 7003 NULL, NULL, NULL, NULL, 7004 v8::External::Wrap(&interceptor_call_count)); 7005 LocalContext context; 7006 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7007 GenerateSomeGarbage(); 7008 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7009 v8::TryCatch try_catch; 7010 v8::Handle<Value> value = CompileRun( 7011 "o.foo = 17;" 7012 "var receiver = {};" 7013 "receiver.__proto__ = o;" 7014 "var result = 0;" 7015 "var saved_result = 0;" 7016 "for (var i = 0; i < 100; i++) {" 7017 " result = receiver.method(41);" 7018 " if (i == 50) {" 7019 " saved_result = result;" 7020 " receiver = {method: receiver.method};" 7021 " }" 7022 "}"); 7023 CHECK(try_catch.HasCaught()); 7024 CHECK_EQ(v8_str("TypeError: Illegal invocation"), 7025 try_catch.Exception()->ToString()); 7026 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7027 CHECK_GE(interceptor_call_count, 50); 7028} 7029 7030THREADED_TEST(CallICFastApi_TrivialSignature) { 7031 v8::HandleScope scope; 7032 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7033 v8::Handle<v8::FunctionTemplate> method_templ = 7034 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 7035 v8_str("method_data"), 7036 v8::Handle<v8::Signature>()); 7037 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7038 proto_templ->Set(v8_str("method"), method_templ); 7039 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7040 LocalContext context; 7041 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7042 GenerateSomeGarbage(); 7043 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7044 v8::Handle<Value> value = CompileRun( 7045 "var result = 0;" 7046 "for (var i = 0; i < 100; i++) {" 7047 " result = o.method(41);" 7048 "}"); 7049 7050 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 7051} 7052 7053THREADED_TEST(CallICFastApi_SimpleSignature) { 7054 v8::HandleScope scope; 7055 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7056 v8::Handle<v8::FunctionTemplate> method_templ = 7057 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7058 v8_str("method_data"), 7059 v8::Signature::New(fun_templ)); 7060 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7061 proto_templ->Set(v8_str("method"), method_templ); 7062 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7063 LocalContext context; 7064 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7065 GenerateSomeGarbage(); 7066 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7067 v8::Handle<Value> value = CompileRun( 7068 "o.foo = 17;" 7069 "var receiver = {};" 7070 "receiver.__proto__ = o;" 7071 "var result = 0;" 7072 "for (var i = 0; i < 100; i++) {" 7073 " result = receiver.method(41);" 7074 "}"); 7075 7076 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 7077} 7078 7079THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) { 7080 v8::HandleScope scope; 7081 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7082 v8::Handle<v8::FunctionTemplate> method_templ = 7083 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7084 v8_str("method_data"), 7085 v8::Signature::New(fun_templ)); 7086 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7087 proto_templ->Set(v8_str("method"), method_templ); 7088 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7089 LocalContext context; 7090 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7091 GenerateSomeGarbage(); 7092 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7093 v8::Handle<Value> value = CompileRun( 7094 "o.foo = 17;" 7095 "var receiver = {};" 7096 "receiver.__proto__ = o;" 7097 "var result = 0;" 7098 "var saved_result = 0;" 7099 "for (var i = 0; i < 100; i++) {" 7100 " result = receiver.method(41);" 7101 " if (i == 50) {" 7102 " saved_result = result;" 7103 " receiver = {method: function(x) { return x - 1 }};" 7104 " }" 7105 "}"); 7106 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 7107 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7108} 7109 7110THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) { 7111 v8::HandleScope scope; 7112 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7113 v8::Handle<v8::FunctionTemplate> method_templ = 7114 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7115 v8_str("method_data"), 7116 v8::Signature::New(fun_templ)); 7117 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7118 proto_templ->Set(v8_str("method"), method_templ); 7119 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7120 LocalContext context; 7121 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7122 GenerateSomeGarbage(); 7123 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7124 v8::TryCatch try_catch; 7125 v8::Handle<Value> value = CompileRun( 7126 "o.foo = 17;" 7127 "var receiver = {};" 7128 "receiver.__proto__ = o;" 7129 "var result = 0;" 7130 "var saved_result = 0;" 7131 "for (var i = 0; i < 100; i++) {" 7132 " result = receiver.method(41);" 7133 " if (i == 50) {" 7134 " saved_result = result;" 7135 " receiver = 333;" 7136 " }" 7137 "}"); 7138 CHECK(try_catch.HasCaught()); 7139 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), 7140 try_catch.Exception()->ToString()); 7141 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7142} 7143 7144 7145v8::Handle<Value> keyed_call_ic_function; 7146 7147static v8::Handle<Value> InterceptorKeyedCallICGetter( 7148 Local<String> name, const AccessorInfo& info) { 7149 ApiTestFuzzer::Fuzz(); 7150 if (v8_str("x")->Equals(name)) { 7151 return keyed_call_ic_function; 7152 } 7153 return v8::Handle<Value>(); 7154} 7155 7156 7157// Test the case when we stored cacheable lookup into 7158// a stub, but the function name changed (to another cacheable function). 7159THREADED_TEST(InterceptorKeyedCallICKeyChange1) { 7160 v8::HandleScope scope; 7161 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7162 templ->SetNamedPropertyHandler(NoBlockGetterX); 7163 LocalContext context; 7164 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7165 v8::Handle<Value> value = CompileRun( 7166 "proto = new Object();" 7167 "proto.y = function(x) { return x + 1; };" 7168 "proto.z = function(x) { return x - 1; };" 7169 "o.__proto__ = proto;" 7170 "var result = 0;" 7171 "var method = 'y';" 7172 "for (var i = 0; i < 10; i++) {" 7173 " if (i == 5) { method = 'z'; };" 7174 " result += o[method](41);" 7175 "}"); 7176 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7177} 7178 7179 7180// Test the case when we stored cacheable lookup into 7181// a stub, but the function name changed (and the new function is present 7182// both before and after the interceptor in the prototype chain). 7183THREADED_TEST(InterceptorKeyedCallICKeyChange2) { 7184 v8::HandleScope scope; 7185 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7186 templ->SetNamedPropertyHandler(InterceptorKeyedCallICGetter); 7187 LocalContext context; 7188 context->Global()->Set(v8_str("proto1"), templ->NewInstance()); 7189 keyed_call_ic_function = 7190 v8_compile("function f(x) { return x - 1; }; f")->Run(); 7191 v8::Handle<Value> value = CompileRun( 7192 "o = new Object();" 7193 "proto2 = new Object();" 7194 "o.y = function(x) { return x + 1; };" 7195 "proto2.y = function(x) { return x + 2; };" 7196 "o.__proto__ = proto1;" 7197 "proto1.__proto__ = proto2;" 7198 "var result = 0;" 7199 "var method = 'x';" 7200 "for (var i = 0; i < 10; i++) {" 7201 " if (i == 5) { method = 'y'; };" 7202 " result += o[method](41);" 7203 "}"); 7204 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7205} 7206 7207 7208// Same as InterceptorKeyedCallICKeyChange1 only the cacheable function sit 7209// on the global object. 7210THREADED_TEST(InterceptorKeyedCallICKeyChangeOnGlobal) { 7211 v8::HandleScope scope; 7212 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7213 templ->SetNamedPropertyHandler(NoBlockGetterX); 7214 LocalContext context; 7215 context->Global()->Set(v8_str("o"), templ->NewInstance()); 7216 v8::Handle<Value> value = CompileRun( 7217 "function inc(x) { return x + 1; };" 7218 "inc(1);" 7219 "function dec(x) { return x - 1; };" 7220 "dec(1);" 7221 "o.__proto__ = this;" 7222 "this.__proto__.x = inc;" 7223 "this.__proto__.y = dec;" 7224 "var result = 0;" 7225 "var method = 'x';" 7226 "for (var i = 0; i < 10; i++) {" 7227 " if (i == 5) { method = 'y'; };" 7228 " result += o[method](41);" 7229 "}"); 7230 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7231} 7232 7233 7234// Test the case when actual function to call sits on global object. 7235THREADED_TEST(InterceptorKeyedCallICFromGlobal) { 7236 v8::HandleScope scope; 7237 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7238 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 7239 LocalContext context; 7240 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 7241 7242 v8::Handle<Value> value = CompileRun( 7243 "function len(x) { return x.length; };" 7244 "o.__proto__ = this;" 7245 "var m = 'parseFloat';" 7246 "var result = 0;" 7247 "for (var i = 0; i < 10; i++) {" 7248 " if (i == 5) {" 7249 " m = 'len';" 7250 " saved_result = result;" 7251 " };" 7252 " result = o[m]('239');" 7253 "}"); 7254 CHECK_EQ(3, context->Global()->Get(v8_str("result"))->Int32Value()); 7255 CHECK_EQ(239, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7256} 7257 7258// Test the map transition before the interceptor. 7259THREADED_TEST(InterceptorKeyedCallICMapChangeBefore) { 7260 v8::HandleScope scope; 7261 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7262 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 7263 LocalContext context; 7264 context->Global()->Set(v8_str("proto"), templ_o->NewInstance()); 7265 7266 v8::Handle<Value> value = CompileRun( 7267 "var o = new Object();" 7268 "o.__proto__ = proto;" 7269 "o.method = function(x) { return x + 1; };" 7270 "var m = 'method';" 7271 "var result = 0;" 7272 "for (var i = 0; i < 10; i++) {" 7273 " if (i == 5) { o.method = function(x) { return x - 1; }; };" 7274 " result += o[m](41);" 7275 "}"); 7276 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7277} 7278 7279 7280// Test the map transition after the interceptor. 7281THREADED_TEST(InterceptorKeyedCallICMapChangeAfter) { 7282 v8::HandleScope scope; 7283 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 7284 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 7285 LocalContext context; 7286 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 7287 7288 v8::Handle<Value> value = CompileRun( 7289 "var proto = new Object();" 7290 "o.__proto__ = proto;" 7291 "proto.method = function(x) { return x + 1; };" 7292 "var m = 'method';" 7293 "var result = 0;" 7294 "for (var i = 0; i < 10; i++) {" 7295 " if (i == 5) { proto.method = function(x) { return x - 1; }; };" 7296 " result += o[m](41);" 7297 "}"); 7298 CHECK_EQ(42*5 + 40*5, context->Global()->Get(v8_str("result"))->Int32Value()); 7299} 7300 7301 7302static int interceptor_call_count = 0; 7303 7304static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name, 7305 const AccessorInfo& info) { 7306 ApiTestFuzzer::Fuzz(); 7307 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) { 7308 return call_ic_function2; 7309 } 7310 return v8::Handle<Value>(); 7311} 7312 7313 7314// This test should hit load and call ICs for the interceptor case. 7315// Once in a while, the interceptor will reply that a property was not 7316// found in which case we should get a reference error. 7317THREADED_TEST(InterceptorICReferenceErrors) { 7318 v8::HandleScope scope; 7319 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7320 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter); 7321 LocalContext context(0, templ, v8::Handle<Value>()); 7322 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run(); 7323 v8::Handle<Value> value = CompileRun( 7324 "function f() {" 7325 " for (var i = 0; i < 1000; i++) {" 7326 " try { x; } catch(e) { return true; }" 7327 " }" 7328 " return false;" 7329 "};" 7330 "f();"); 7331 CHECK_EQ(true, value->BooleanValue()); 7332 interceptor_call_count = 0; 7333 value = CompileRun( 7334 "function g() {" 7335 " for (var i = 0; i < 1000; i++) {" 7336 " try { x(42); } catch(e) { return true; }" 7337 " }" 7338 " return false;" 7339 "};" 7340 "g();"); 7341 CHECK_EQ(true, value->BooleanValue()); 7342} 7343 7344 7345static int interceptor_ic_exception_get_count = 0; 7346 7347static v8::Handle<Value> InterceptorICExceptionGetter( 7348 Local<String> name, 7349 const AccessorInfo& info) { 7350 ApiTestFuzzer::Fuzz(); 7351 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) { 7352 return call_ic_function3; 7353 } 7354 if (interceptor_ic_exception_get_count == 20) { 7355 return v8::ThrowException(v8_num(42)); 7356 } 7357 // Do not handle get for properties other than x. 7358 return v8::Handle<Value>(); 7359} 7360 7361// Test interceptor load/call IC where the interceptor throws an 7362// exception once in a while. 7363THREADED_TEST(InterceptorICGetterExceptions) { 7364 interceptor_ic_exception_get_count = 0; 7365 v8::HandleScope scope; 7366 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7367 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter); 7368 LocalContext context(0, templ, v8::Handle<Value>()); 7369 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run(); 7370 v8::Handle<Value> value = CompileRun( 7371 "function f() {" 7372 " for (var i = 0; i < 100; i++) {" 7373 " try { x; } catch(e) { return true; }" 7374 " }" 7375 " return false;" 7376 "};" 7377 "f();"); 7378 CHECK_EQ(true, value->BooleanValue()); 7379 interceptor_ic_exception_get_count = 0; 7380 value = CompileRun( 7381 "function f() {" 7382 " for (var i = 0; i < 100; i++) {" 7383 " try { x(42); } catch(e) { return true; }" 7384 " }" 7385 " return false;" 7386 "};" 7387 "f();"); 7388 CHECK_EQ(true, value->BooleanValue()); 7389} 7390 7391 7392static int interceptor_ic_exception_set_count = 0; 7393 7394static v8::Handle<Value> InterceptorICExceptionSetter( 7395 Local<String> key, Local<Value> value, const AccessorInfo&) { 7396 ApiTestFuzzer::Fuzz(); 7397 if (++interceptor_ic_exception_set_count > 20) { 7398 return v8::ThrowException(v8_num(42)); 7399 } 7400 // Do not actually handle setting. 7401 return v8::Handle<Value>(); 7402} 7403 7404// Test interceptor store IC where the interceptor throws an exception 7405// once in a while. 7406THREADED_TEST(InterceptorICSetterExceptions) { 7407 interceptor_ic_exception_set_count = 0; 7408 v8::HandleScope scope; 7409 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7410 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter); 7411 LocalContext context(0, templ, v8::Handle<Value>()); 7412 v8::Handle<Value> value = CompileRun( 7413 "function f() {" 7414 " for (var i = 0; i < 100; i++) {" 7415 " try { x = 42; } catch(e) { return true; }" 7416 " }" 7417 " return false;" 7418 "};" 7419 "f();"); 7420 CHECK_EQ(true, value->BooleanValue()); 7421} 7422 7423 7424// Test that we ignore null interceptors. 7425THREADED_TEST(NullNamedInterceptor) { 7426 v8::HandleScope scope; 7427 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7428 templ->SetNamedPropertyHandler(0); 7429 LocalContext context; 7430 templ->Set("x", v8_num(42)); 7431 v8::Handle<v8::Object> obj = templ->NewInstance(); 7432 context->Global()->Set(v8_str("obj"), obj); 7433 v8::Handle<Value> value = CompileRun("obj.x"); 7434 CHECK(value->IsInt32()); 7435 CHECK_EQ(42, value->Int32Value()); 7436} 7437 7438 7439// Test that we ignore null interceptors. 7440THREADED_TEST(NullIndexedInterceptor) { 7441 v8::HandleScope scope; 7442 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7443 templ->SetIndexedPropertyHandler(0); 7444 LocalContext context; 7445 templ->Set("42", v8_num(42)); 7446 v8::Handle<v8::Object> obj = templ->NewInstance(); 7447 context->Global()->Set(v8_str("obj"), obj); 7448 v8::Handle<Value> value = CompileRun("obj[42]"); 7449 CHECK(value->IsInt32()); 7450 CHECK_EQ(42, value->Int32Value()); 7451} 7452 7453 7454THREADED_TEST(NamedPropertyHandlerGetterAttributes) { 7455 v8::HandleScope scope; 7456 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 7457 templ->InstanceTemplate()->SetNamedPropertyHandler(InterceptorLoadXICGetter); 7458 LocalContext env; 7459 env->Global()->Set(v8_str("obj"), 7460 templ->GetFunction()->NewInstance()); 7461 ExpectTrue("obj.x === 42"); 7462 ExpectTrue("!obj.propertyIsEnumerable('x')"); 7463} 7464 7465 7466static v8::Handle<Value> ParentGetter(Local<String> name, 7467 const AccessorInfo& info) { 7468 ApiTestFuzzer::Fuzz(); 7469 return v8_num(1); 7470} 7471 7472 7473static v8::Handle<Value> ChildGetter(Local<String> name, 7474 const AccessorInfo& info) { 7475 ApiTestFuzzer::Fuzz(); 7476 return v8_num(42); 7477} 7478 7479 7480THREADED_TEST(Overriding) { 7481 v8::HandleScope scope; 7482 LocalContext context; 7483 7484 // Parent template. 7485 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New(); 7486 Local<ObjectTemplate> parent_instance_templ = 7487 parent_templ->InstanceTemplate(); 7488 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter); 7489 7490 // Template that inherits from the parent template. 7491 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New(); 7492 Local<ObjectTemplate> child_instance_templ = 7493 child_templ->InstanceTemplate(); 7494 child_templ->Inherit(parent_templ); 7495 // Override 'f'. The child version of 'f' should get called for child 7496 // instances. 7497 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter); 7498 // Add 'g' twice. The 'g' added last should get called for instances. 7499 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter); 7500 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter); 7501 7502 // Add 'h' as an accessor to the proto template with ReadOnly attributes 7503 // so 'h' can be shadowed on the instance object. 7504 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate(); 7505 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0, 7506 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 7507 7508 // Add 'i' as an accessor to the instance template with ReadOnly attributes 7509 // but the attribute does not have effect because it is duplicated with 7510 // NULL setter. 7511 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0, 7512 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 7513 7514 7515 7516 // Instantiate the child template. 7517 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance(); 7518 7519 // Check that the child function overrides the parent one. 7520 context->Global()->Set(v8_str("o"), instance); 7521 Local<Value> value = v8_compile("o.f")->Run(); 7522 // Check that the 'g' that was added last is hit. 7523 CHECK_EQ(42, value->Int32Value()); 7524 value = v8_compile("o.g")->Run(); 7525 CHECK_EQ(42, value->Int32Value()); 7526 7527 // Check 'h' can be shadowed. 7528 value = v8_compile("o.h = 3; o.h")->Run(); 7529 CHECK_EQ(3, value->Int32Value()); 7530 7531 // Check 'i' is cannot be shadowed or changed. 7532 value = v8_compile("o.i = 3; o.i")->Run(); 7533 CHECK_EQ(42, value->Int32Value()); 7534} 7535 7536 7537static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) { 7538 ApiTestFuzzer::Fuzz(); 7539 if (args.IsConstructCall()) { 7540 return v8::Boolean::New(true); 7541 } 7542 return v8::Boolean::New(false); 7543} 7544 7545 7546THREADED_TEST(IsConstructCall) { 7547 v8::HandleScope scope; 7548 7549 // Function template with call handler. 7550 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 7551 templ->SetCallHandler(IsConstructHandler); 7552 7553 LocalContext context; 7554 7555 context->Global()->Set(v8_str("f"), templ->GetFunction()); 7556 Local<Value> value = v8_compile("f()")->Run(); 7557 CHECK(!value->BooleanValue()); 7558 value = v8_compile("new f()")->Run(); 7559 CHECK(value->BooleanValue()); 7560} 7561 7562 7563THREADED_TEST(ObjectProtoToString) { 7564 v8::HandleScope scope; 7565 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 7566 templ->SetClassName(v8_str("MyClass")); 7567 7568 LocalContext context; 7569 7570 Local<String> customized_tostring = v8_str("customized toString"); 7571 7572 // Replace Object.prototype.toString 7573 v8_compile("Object.prototype.toString = function() {" 7574 " return 'customized toString';" 7575 "}")->Run(); 7576 7577 // Normal ToString call should call replaced Object.prototype.toString 7578 Local<v8::Object> instance = templ->GetFunction()->NewInstance(); 7579 Local<String> value = instance->ToString(); 7580 CHECK(value->IsString() && value->Equals(customized_tostring)); 7581 7582 // ObjectProtoToString should not call replace toString function. 7583 value = instance->ObjectProtoToString(); 7584 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]"))); 7585 7586 // Check global 7587 value = context->Global()->ObjectProtoToString(); 7588 CHECK(value->IsString() && value->Equals(v8_str("[object global]"))); 7589 7590 // Check ordinary object 7591 Local<Value> object = v8_compile("new Object()")->Run(); 7592 value = object.As<v8::Object>()->ObjectProtoToString(); 7593 CHECK(value->IsString() && value->Equals(v8_str("[object Object]"))); 7594} 7595 7596 7597bool ApiTestFuzzer::fuzzing_ = false; 7598v8::internal::Semaphore* ApiTestFuzzer::all_tests_done_= 7599 v8::internal::OS::CreateSemaphore(0); 7600int ApiTestFuzzer::active_tests_; 7601int ApiTestFuzzer::tests_being_run_; 7602int ApiTestFuzzer::current_; 7603 7604 7605// We are in a callback and want to switch to another thread (if we 7606// are currently running the thread fuzzing test). 7607void ApiTestFuzzer::Fuzz() { 7608 if (!fuzzing_) return; 7609 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_; 7610 test->ContextSwitch(); 7611} 7612 7613 7614// Let the next thread go. Since it is also waiting on the V8 lock it may 7615// not start immediately. 7616bool ApiTestFuzzer::NextThread() { 7617 int test_position = GetNextTestNumber(); 7618 const char* test_name = RegisterThreadedTest::nth(current_)->name(); 7619 if (test_position == current_) { 7620 if (kLogThreading) 7621 printf("Stay with %s\n", test_name); 7622 return false; 7623 } 7624 if (kLogThreading) { 7625 printf("Switch from %s to %s\n", 7626 test_name, 7627 RegisterThreadedTest::nth(test_position)->name()); 7628 } 7629 current_ = test_position; 7630 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal(); 7631 return true; 7632} 7633 7634 7635void ApiTestFuzzer::Run() { 7636 // When it is our turn... 7637 gate_->Wait(); 7638 { 7639 // ... get the V8 lock and start running the test. 7640 v8::Locker locker; 7641 CallTest(); 7642 } 7643 // This test finished. 7644 active_ = false; 7645 active_tests_--; 7646 // If it was the last then signal that fact. 7647 if (active_tests_ == 0) { 7648 all_tests_done_->Signal(); 7649 } else { 7650 // Otherwise select a new test and start that. 7651 NextThread(); 7652 } 7653} 7654 7655 7656static unsigned linear_congruential_generator; 7657 7658 7659void ApiTestFuzzer::Setup(PartOfTest part) { 7660 linear_congruential_generator = i::FLAG_testing_prng_seed; 7661 fuzzing_ = true; 7662 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1); 7663 int end = (part == FIRST_PART) 7664 ? (RegisterThreadedTest::count() >> 1) 7665 : RegisterThreadedTest::count(); 7666 active_tests_ = tests_being_run_ = end - start; 7667 for (int i = 0; i < tests_being_run_; i++) { 7668 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start); 7669 } 7670 for (int i = 0; i < active_tests_; i++) { 7671 RegisterThreadedTest::nth(i)->fuzzer_->Start(); 7672 } 7673} 7674 7675 7676static void CallTestNumber(int test_number) { 7677 (RegisterThreadedTest::nth(test_number)->callback())(); 7678} 7679 7680 7681void ApiTestFuzzer::RunAllTests() { 7682 // Set off the first test. 7683 current_ = -1; 7684 NextThread(); 7685 // Wait till they are all done. 7686 all_tests_done_->Wait(); 7687} 7688 7689 7690int ApiTestFuzzer::GetNextTestNumber() { 7691 int next_test; 7692 do { 7693 next_test = (linear_congruential_generator >> 16) % tests_being_run_; 7694 linear_congruential_generator *= 1664525u; 7695 linear_congruential_generator += 1013904223u; 7696 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_); 7697 return next_test; 7698} 7699 7700 7701void ApiTestFuzzer::ContextSwitch() { 7702 // If the new thread is the same as the current thread there is nothing to do. 7703 if (NextThread()) { 7704 // Now it can start. 7705 v8::Unlocker unlocker; 7706 // Wait till someone starts us again. 7707 gate_->Wait(); 7708 // And we're off. 7709 } 7710} 7711 7712 7713void ApiTestFuzzer::TearDown() { 7714 fuzzing_ = false; 7715 for (int i = 0; i < RegisterThreadedTest::count(); i++) { 7716 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_; 7717 if (fuzzer != NULL) fuzzer->Join(); 7718 } 7719} 7720 7721 7722// Lets not be needlessly self-referential. 7723TEST(Threading) { 7724 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART); 7725 ApiTestFuzzer::RunAllTests(); 7726 ApiTestFuzzer::TearDown(); 7727} 7728 7729TEST(Threading2) { 7730 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART); 7731 ApiTestFuzzer::RunAllTests(); 7732 ApiTestFuzzer::TearDown(); 7733} 7734 7735 7736void ApiTestFuzzer::CallTest() { 7737 if (kLogThreading) 7738 printf("Start test %d\n", test_number_); 7739 CallTestNumber(test_number_); 7740 if (kLogThreading) 7741 printf("End test %d\n", test_number_); 7742} 7743 7744 7745static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) { 7746 CHECK(v8::Locker::IsLocked()); 7747 ApiTestFuzzer::Fuzz(); 7748 v8::Unlocker unlocker; 7749 const char* code = "throw 7;"; 7750 { 7751 v8::Locker nested_locker; 7752 v8::HandleScope scope; 7753 v8::Handle<Value> exception; 7754 { v8::TryCatch try_catch; 7755 v8::Handle<Value> value = CompileRun(code); 7756 CHECK(value.IsEmpty()); 7757 CHECK(try_catch.HasCaught()); 7758 // Make sure to wrap the exception in a new handle because 7759 // the handle returned from the TryCatch is destroyed 7760 // when the TryCatch is destroyed. 7761 exception = Local<Value>::New(try_catch.Exception()); 7762 } 7763 return v8::ThrowException(exception); 7764 } 7765} 7766 7767 7768static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) { 7769 CHECK(v8::Locker::IsLocked()); 7770 ApiTestFuzzer::Fuzz(); 7771 v8::Unlocker unlocker; 7772 const char* code = "throw 7;"; 7773 { 7774 v8::Locker nested_locker; 7775 v8::HandleScope scope; 7776 v8::Handle<Value> value = CompileRun(code); 7777 CHECK(value.IsEmpty()); 7778 return v8_str("foo"); 7779 } 7780} 7781 7782 7783// These are locking tests that don't need to be run again 7784// as part of the locking aggregation tests. 7785TEST(NestedLockers) { 7786 v8::Locker locker; 7787 CHECK(v8::Locker::IsLocked()); 7788 v8::HandleScope scope; 7789 LocalContext env; 7790 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS); 7791 Local<Function> fun = fun_templ->GetFunction(); 7792 env->Global()->Set(v8_str("throw_in_js"), fun); 7793 Local<Script> script = v8_compile("(function () {" 7794 " try {" 7795 " throw_in_js();" 7796 " return 42;" 7797 " } catch (e) {" 7798 " return e * 13;" 7799 " }" 7800 "})();"); 7801 CHECK_EQ(91, script->Run()->Int32Value()); 7802} 7803 7804 7805// These are locking tests that don't need to be run again 7806// as part of the locking aggregation tests. 7807TEST(NestedLockersNoTryCatch) { 7808 v8::Locker locker; 7809 v8::HandleScope scope; 7810 LocalContext env; 7811 Local<v8::FunctionTemplate> fun_templ = 7812 v8::FunctionTemplate::New(ThrowInJSNoCatch); 7813 Local<Function> fun = fun_templ->GetFunction(); 7814 env->Global()->Set(v8_str("throw_in_js"), fun); 7815 Local<Script> script = v8_compile("(function () {" 7816 " try {" 7817 " throw_in_js();" 7818 " return 42;" 7819 " } catch (e) {" 7820 " return e * 13;" 7821 " }" 7822 "})();"); 7823 CHECK_EQ(91, script->Run()->Int32Value()); 7824} 7825 7826 7827THREADED_TEST(RecursiveLocking) { 7828 v8::Locker locker; 7829 { 7830 v8::Locker locker2; 7831 CHECK(v8::Locker::IsLocked()); 7832 } 7833} 7834 7835 7836static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) { 7837 ApiTestFuzzer::Fuzz(); 7838 v8::Unlocker unlocker; 7839 return v8::Undefined(); 7840} 7841 7842 7843THREADED_TEST(LockUnlockLock) { 7844 { 7845 v8::Locker locker; 7846 v8::HandleScope scope; 7847 LocalContext env; 7848 Local<v8::FunctionTemplate> fun_templ = 7849 v8::FunctionTemplate::New(UnlockForAMoment); 7850 Local<Function> fun = fun_templ->GetFunction(); 7851 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 7852 Local<Script> script = v8_compile("(function () {" 7853 " unlock_for_a_moment();" 7854 " return 42;" 7855 "})();"); 7856 CHECK_EQ(42, script->Run()->Int32Value()); 7857 } 7858 { 7859 v8::Locker locker; 7860 v8::HandleScope scope; 7861 LocalContext env; 7862 Local<v8::FunctionTemplate> fun_templ = 7863 v8::FunctionTemplate::New(UnlockForAMoment); 7864 Local<Function> fun = fun_templ->GetFunction(); 7865 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 7866 Local<Script> script = v8_compile("(function () {" 7867 " unlock_for_a_moment();" 7868 " return 42;" 7869 "})();"); 7870 CHECK_EQ(42, script->Run()->Int32Value()); 7871 } 7872} 7873 7874 7875static int GetGlobalObjectsCount() { 7876 int count = 0; 7877 v8::internal::HeapIterator it; 7878 for (i::HeapObject* object = it.next(); object != NULL; object = it.next()) 7879 if (object->IsJSGlobalObject()) count++; 7880 return count; 7881} 7882 7883 7884static int GetSurvivingGlobalObjectsCount() { 7885 // We need to collect all garbage twice to be sure that everything 7886 // has been collected. This is because inline caches are cleared in 7887 // the first garbage collection but some of the maps have already 7888 // been marked at that point. Therefore some of the maps are not 7889 // collected until the second garbage collection. 7890 v8::internal::Heap::CollectAllGarbage(false); 7891 v8::internal::Heap::CollectAllGarbage(false); 7892 int count = GetGlobalObjectsCount(); 7893#ifdef DEBUG 7894 if (count > 0) v8::internal::Heap::TracePathToGlobal(); 7895#endif 7896 return count; 7897} 7898 7899 7900TEST(DontLeakGlobalObjects) { 7901 // Regression test for issues 1139850 and 1174891. 7902 7903 v8::V8::Initialize(); 7904 7905 int count = GetSurvivingGlobalObjectsCount(); 7906 7907 for (int i = 0; i < 5; i++) { 7908 { v8::HandleScope scope; 7909 LocalContext context; 7910 } 7911 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 7912 7913 { v8::HandleScope scope; 7914 LocalContext context; 7915 v8_compile("Date")->Run(); 7916 } 7917 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 7918 7919 { v8::HandleScope scope; 7920 LocalContext context; 7921 v8_compile("/aaa/")->Run(); 7922 } 7923 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 7924 7925 { v8::HandleScope scope; 7926 const char* extension_list[] = { "v8/gc" }; 7927 v8::ExtensionConfiguration extensions(1, extension_list); 7928 LocalContext context(&extensions); 7929 v8_compile("gc();")->Run(); 7930 } 7931 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 7932 } 7933} 7934 7935 7936v8::Persistent<v8::Object> some_object; 7937v8::Persistent<v8::Object> bad_handle; 7938 7939void NewPersistentHandleCallback(v8::Persistent<v8::Value>, void*) { 7940 v8::HandleScope scope; 7941 bad_handle = v8::Persistent<v8::Object>::New(some_object); 7942} 7943 7944 7945THREADED_TEST(NewPersistentHandleFromWeakCallback) { 7946 LocalContext context; 7947 7948 v8::Persistent<v8::Object> handle1, handle2; 7949 { 7950 v8::HandleScope scope; 7951 some_object = v8::Persistent<v8::Object>::New(v8::Object::New()); 7952 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7953 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7954 } 7955 // Note: order is implementation dependent alas: currently 7956 // global handle nodes are processed by PostGarbageCollectionProcessing 7957 // in reverse allocation order, so if second allocated handle is deleted, 7958 // weak callback of the first handle would be able to 'reallocate' it. 7959 handle1.MakeWeak(NULL, NewPersistentHandleCallback); 7960 handle2.Dispose(); 7961 i::Heap::CollectAllGarbage(false); 7962} 7963 7964 7965v8::Persistent<v8::Object> to_be_disposed; 7966 7967void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) { 7968 to_be_disposed.Dispose(); 7969 i::Heap::CollectAllGarbage(false); 7970} 7971 7972 7973THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) { 7974 LocalContext context; 7975 7976 v8::Persistent<v8::Object> handle1, handle2; 7977 { 7978 v8::HandleScope scope; 7979 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7980 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7981 } 7982 handle1.MakeWeak(NULL, DisposeAndForceGcCallback); 7983 to_be_disposed = handle2; 7984 i::Heap::CollectAllGarbage(false); 7985} 7986 7987void DisposingCallback(v8::Persistent<v8::Value> handle, void*) { 7988 handle.Dispose(); 7989} 7990 7991void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) { 7992 v8::HandleScope scope; 7993 v8::Persistent<v8::Object>::New(v8::Object::New()); 7994} 7995 7996 7997THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) { 7998 LocalContext context; 7999 8000 v8::Persistent<v8::Object> handle1, handle2, handle3; 8001 { 8002 v8::HandleScope scope; 8003 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8004 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8005 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 8006 } 8007 handle2.MakeWeak(NULL, DisposingCallback); 8008 handle3.MakeWeak(NULL, HandleCreatingCallback); 8009 i::Heap::CollectAllGarbage(false); 8010} 8011 8012 8013THREADED_TEST(CheckForCrossContextObjectLiterals) { 8014 v8::V8::Initialize(); 8015 8016 const int nof = 2; 8017 const char* sources[nof] = { 8018 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }", 8019 "Object()" 8020 }; 8021 8022 for (int i = 0; i < nof; i++) { 8023 const char* source = sources[i]; 8024 { v8::HandleScope scope; 8025 LocalContext context; 8026 CompileRun(source); 8027 } 8028 { v8::HandleScope scope; 8029 LocalContext context; 8030 CompileRun(source); 8031 } 8032 } 8033} 8034 8035 8036static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) { 8037 v8::HandleScope inner; 8038 env->Enter(); 8039 v8::Handle<Value> three = v8_num(3); 8040 v8::Handle<Value> value = inner.Close(three); 8041 env->Exit(); 8042 return value; 8043} 8044 8045 8046THREADED_TEST(NestedHandleScopeAndContexts) { 8047 v8::HandleScope outer; 8048 v8::Persistent<Context> env = Context::New(); 8049 env->Enter(); 8050 v8::Handle<Value> value = NestedScope(env); 8051 v8::Handle<String> str = value->ToString(); 8052 env->Exit(); 8053 env.Dispose(); 8054} 8055 8056 8057THREADED_TEST(ExternalAllocatedMemory) { 8058 v8::HandleScope outer; 8059 v8::Persistent<Context> env = Context::New(); 8060 const int kSize = 1024*1024; 8061 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize); 8062 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0); 8063} 8064 8065 8066THREADED_TEST(DisposeEnteredContext) { 8067 v8::HandleScope scope; 8068 LocalContext outer; 8069 { v8::Persistent<v8::Context> inner = v8::Context::New(); 8070 inner->Enter(); 8071 inner.Dispose(); 8072 inner.Clear(); 8073 inner->Exit(); 8074 } 8075} 8076 8077 8078// Regression test for issue 54, object templates with internal fields 8079// but no accessors or interceptors did not get their internal field 8080// count set on instances. 8081THREADED_TEST(Regress54) { 8082 v8::HandleScope outer; 8083 LocalContext context; 8084 static v8::Persistent<v8::ObjectTemplate> templ; 8085 if (templ.IsEmpty()) { 8086 v8::HandleScope inner; 8087 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New(); 8088 local->SetInternalFieldCount(1); 8089 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local)); 8090 } 8091 v8::Handle<v8::Object> result = templ->NewInstance(); 8092 CHECK_EQ(1, result->InternalFieldCount()); 8093} 8094 8095 8096// If part of the threaded tests, this test makes ThreadingTest fail 8097// on mac. 8098TEST(CatchStackOverflow) { 8099 v8::HandleScope scope; 8100 LocalContext context; 8101 v8::TryCatch try_catch; 8102 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New( 8103 "function f() {" 8104 " return f();" 8105 "}" 8106 "" 8107 "f();")); 8108 v8::Handle<v8::Value> result = script->Run(); 8109 CHECK(result.IsEmpty()); 8110} 8111 8112 8113static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script, 8114 const char* resource_name, 8115 int line_offset) { 8116 v8::HandleScope scope; 8117 v8::TryCatch try_catch; 8118 v8::Handle<v8::Value> result = script->Run(); 8119 CHECK(result.IsEmpty()); 8120 CHECK(try_catch.HasCaught()); 8121 v8::Handle<v8::Message> message = try_catch.Message(); 8122 CHECK(!message.IsEmpty()); 8123 CHECK_EQ(10 + line_offset, message->GetLineNumber()); 8124 CHECK_EQ(91, message->GetStartPosition()); 8125 CHECK_EQ(92, message->GetEndPosition()); 8126 CHECK_EQ(2, message->GetStartColumn()); 8127 CHECK_EQ(3, message->GetEndColumn()); 8128 v8::String::AsciiValue line(message->GetSourceLine()); 8129 CHECK_EQ(" throw 'nirk';", *line); 8130 v8::String::AsciiValue name(message->GetScriptResourceName()); 8131 CHECK_EQ(resource_name, *name); 8132} 8133 8134 8135THREADED_TEST(TryCatchSourceInfo) { 8136 v8::HandleScope scope; 8137 LocalContext context; 8138 v8::Handle<v8::String> source = v8::String::New( 8139 "function Foo() {\n" 8140 " return Bar();\n" 8141 "}\n" 8142 "\n" 8143 "function Bar() {\n" 8144 " return Baz();\n" 8145 "}\n" 8146 "\n" 8147 "function Baz() {\n" 8148 " throw 'nirk';\n" 8149 "}\n" 8150 "\n" 8151 "Foo();\n"); 8152 8153 const char* resource_name; 8154 v8::Handle<v8::Script> script; 8155 resource_name = "test.js"; 8156 script = v8::Script::Compile(source, v8::String::New(resource_name)); 8157 CheckTryCatchSourceInfo(script, resource_name, 0); 8158 8159 resource_name = "test1.js"; 8160 v8::ScriptOrigin origin1(v8::String::New(resource_name)); 8161 script = v8::Script::Compile(source, &origin1); 8162 CheckTryCatchSourceInfo(script, resource_name, 0); 8163 8164 resource_name = "test2.js"; 8165 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7)); 8166 script = v8::Script::Compile(source, &origin2); 8167 CheckTryCatchSourceInfo(script, resource_name, 7); 8168} 8169 8170 8171THREADED_TEST(CompilationCache) { 8172 v8::HandleScope scope; 8173 LocalContext context; 8174 v8::Handle<v8::String> source0 = v8::String::New("1234"); 8175 v8::Handle<v8::String> source1 = v8::String::New("1234"); 8176 v8::Handle<v8::Script> script0 = 8177 v8::Script::Compile(source0, v8::String::New("test.js")); 8178 v8::Handle<v8::Script> script1 = 8179 v8::Script::Compile(source1, v8::String::New("test.js")); 8180 v8::Handle<v8::Script> script2 = 8181 v8::Script::Compile(source0); // different origin 8182 CHECK_EQ(1234, script0->Run()->Int32Value()); 8183 CHECK_EQ(1234, script1->Run()->Int32Value()); 8184 CHECK_EQ(1234, script2->Run()->Int32Value()); 8185} 8186 8187 8188static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) { 8189 ApiTestFuzzer::Fuzz(); 8190 return v8_num(42); 8191} 8192 8193 8194THREADED_TEST(CallbackFunctionName) { 8195 v8::HandleScope scope; 8196 LocalContext context; 8197 Local<ObjectTemplate> t = ObjectTemplate::New(); 8198 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback)); 8199 context->Global()->Set(v8_str("obj"), t->NewInstance()); 8200 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name"); 8201 CHECK(value->IsString()); 8202 v8::String::AsciiValue name(value); 8203 CHECK_EQ("asdf", *name); 8204} 8205 8206 8207THREADED_TEST(DateAccess) { 8208 v8::HandleScope scope; 8209 LocalContext context; 8210 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0); 8211 CHECK(date->IsDate()); 8212 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue()); 8213} 8214 8215 8216void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) { 8217 v8::Handle<v8::Object> obj = val.As<v8::Object>(); 8218 v8::Handle<v8::Array> props = obj->GetPropertyNames(); 8219 CHECK_EQ(elmc, props->Length()); 8220 for (int i = 0; i < elmc; i++) { 8221 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i))); 8222 CHECK_EQ(elmv[i], *elm); 8223 } 8224} 8225 8226 8227THREADED_TEST(PropertyEnumeration) { 8228 v8::HandleScope scope; 8229 LocalContext context; 8230 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New( 8231 "var result = [];" 8232 "result[0] = {};" 8233 "result[1] = {a: 1, b: 2};" 8234 "result[2] = [1, 2, 3];" 8235 "var proto = {x: 1, y: 2, z: 3};" 8236 "var x = { __proto__: proto, w: 0, z: 1 };" 8237 "result[3] = x;" 8238 "result;"))->Run(); 8239 v8::Handle<v8::Array> elms = obj.As<v8::Array>(); 8240 CHECK_EQ(4, elms->Length()); 8241 int elmc0 = 0; 8242 const char** elmv0 = NULL; 8243 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0); 8244 int elmc1 = 2; 8245 const char* elmv1[] = {"a", "b"}; 8246 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1); 8247 int elmc2 = 3; 8248 const char* elmv2[] = {"0", "1", "2"}; 8249 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2); 8250 int elmc3 = 4; 8251 const char* elmv3[] = {"w", "z", "x", "y"}; 8252 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3); 8253} 8254 8255 8256static bool NamedSetAccessBlocker(Local<v8::Object> obj, 8257 Local<Value> name, 8258 v8::AccessType type, 8259 Local<Value> data) { 8260 return type != v8::ACCESS_SET; 8261} 8262 8263 8264static bool IndexedSetAccessBlocker(Local<v8::Object> obj, 8265 uint32_t key, 8266 v8::AccessType type, 8267 Local<Value> data) { 8268 return type != v8::ACCESS_SET; 8269} 8270 8271 8272THREADED_TEST(DisableAccessChecksWhileConfiguring) { 8273 v8::HandleScope scope; 8274 LocalContext context; 8275 Local<ObjectTemplate> templ = ObjectTemplate::New(); 8276 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker, 8277 IndexedSetAccessBlocker); 8278 templ->Set(v8_str("x"), v8::True()); 8279 Local<v8::Object> instance = templ->NewInstance(); 8280 context->Global()->Set(v8_str("obj"), instance); 8281 Local<Value> value = CompileRun("obj.x"); 8282 CHECK(value->BooleanValue()); 8283} 8284 8285 8286static bool NamedGetAccessBlocker(Local<v8::Object> obj, 8287 Local<Value> name, 8288 v8::AccessType type, 8289 Local<Value> data) { 8290 return false; 8291} 8292 8293 8294static bool IndexedGetAccessBlocker(Local<v8::Object> obj, 8295 uint32_t key, 8296 v8::AccessType type, 8297 Local<Value> data) { 8298 return false; 8299} 8300 8301 8302 8303THREADED_TEST(AccessChecksReenabledCorrectly) { 8304 v8::HandleScope scope; 8305 LocalContext context; 8306 Local<ObjectTemplate> templ = ObjectTemplate::New(); 8307 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker, 8308 IndexedGetAccessBlocker); 8309 templ->Set(v8_str("a"), v8_str("a")); 8310 // Add more than 8 (see kMaxFastProperties) properties 8311 // so that the constructor will force copying map. 8312 // Cannot sprintf, gcc complains unsafety. 8313 char buf[4]; 8314 for (char i = '0'; i <= '9' ; i++) { 8315 buf[0] = i; 8316 for (char j = '0'; j <= '9'; j++) { 8317 buf[1] = j; 8318 for (char k = '0'; k <= '9'; k++) { 8319 buf[2] = k; 8320 buf[3] = 0; 8321 templ->Set(v8_str(buf), v8::Number::New(k)); 8322 } 8323 } 8324 } 8325 8326 Local<v8::Object> instance_1 = templ->NewInstance(); 8327 context->Global()->Set(v8_str("obj_1"), instance_1); 8328 8329 Local<Value> value_1 = CompileRun("obj_1.a"); 8330 CHECK(value_1->IsUndefined()); 8331 8332 Local<v8::Object> instance_2 = templ->NewInstance(); 8333 context->Global()->Set(v8_str("obj_2"), instance_2); 8334 8335 Local<Value> value_2 = CompileRun("obj_2.a"); 8336 CHECK(value_2->IsUndefined()); 8337} 8338 8339 8340// This tests that access check information remains on the global 8341// object template when creating contexts. 8342THREADED_TEST(AccessControlRepeatedContextCreation) { 8343 v8::HandleScope handle_scope; 8344 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 8345 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker, 8346 IndexedSetAccessBlocker); 8347 i::Handle<i::ObjectTemplateInfo> internal_template = 8348 v8::Utils::OpenHandle(*global_template); 8349 CHECK(!internal_template->constructor()->IsUndefined()); 8350 i::Handle<i::FunctionTemplateInfo> constructor( 8351 i::FunctionTemplateInfo::cast(internal_template->constructor())); 8352 CHECK(!constructor->access_check_info()->IsUndefined()); 8353 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 8354 CHECK(!constructor->access_check_info()->IsUndefined()); 8355} 8356 8357 8358THREADED_TEST(TurnOnAccessCheck) { 8359 v8::HandleScope handle_scope; 8360 8361 // Create an environment with access check to the global object disabled by 8362 // default. 8363 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 8364 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker, 8365 IndexedGetAccessBlocker, 8366 v8::Handle<v8::Value>(), 8367 false); 8368 v8::Persistent<Context> context = Context::New(NULL, global_template); 8369 Context::Scope context_scope(context); 8370 8371 // Set up a property and a number of functions. 8372 context->Global()->Set(v8_str("a"), v8_num(1)); 8373 CompileRun("function f1() {return a;}" 8374 "function f2() {return a;}" 8375 "function g1() {return h();}" 8376 "function g2() {return h();}" 8377 "function h() {return 1;}"); 8378 Local<Function> f1 = 8379 Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); 8380 Local<Function> f2 = 8381 Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); 8382 Local<Function> g1 = 8383 Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); 8384 Local<Function> g2 = 8385 Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); 8386 Local<Function> h = 8387 Local<Function>::Cast(context->Global()->Get(v8_str("h"))); 8388 8389 // Get the global object. 8390 v8::Handle<v8::Object> global = context->Global(); 8391 8392 // Call f1 one time and f2 a number of times. This will ensure that f1 still 8393 // uses the runtime system to retreive property a whereas f2 uses global load 8394 // inline cache. 8395 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1))); 8396 for (int i = 0; i < 4; i++) { 8397 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1))); 8398 } 8399 8400 // Same for g1 and g2. 8401 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1))); 8402 for (int i = 0; i < 4; i++) { 8403 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1))); 8404 } 8405 8406 // Detach the global and turn on access check. 8407 context->DetachGlobal(); 8408 context->Global()->TurnOnAccessCheck(); 8409 8410 // Failing access check to property get results in undefined. 8411 CHECK(f1->Call(global, 0, NULL)->IsUndefined()); 8412 CHECK(f2->Call(global, 0, NULL)->IsUndefined()); 8413 8414 // Failing access check to function call results in exception. 8415 CHECK(g1->Call(global, 0, NULL).IsEmpty()); 8416 CHECK(g2->Call(global, 0, NULL).IsEmpty()); 8417 8418 // No failing access check when just returning a constant. 8419 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1))); 8420} 8421 8422 8423// This test verifies that pre-compilation (aka preparsing) can be called 8424// without initializing the whole VM. Thus we cannot run this test in a 8425// multi-threaded setup. 8426TEST(PreCompile) { 8427 // TODO(155): This test would break without the initialization of V8. This is 8428 // a workaround for now to make this test not fail. 8429 v8::V8::Initialize(); 8430 const char* script = "function foo(a) { return a+1; }"; 8431 v8::ScriptData* sd = 8432 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8433 CHECK_NE(sd->Length(), 0); 8434 CHECK_NE(sd->Data(), NULL); 8435 CHECK(!sd->HasError()); 8436 delete sd; 8437} 8438 8439 8440TEST(PreCompileWithError) { 8441 v8::V8::Initialize(); 8442 const char* script = "function foo(a) { return 1 * * 2; }"; 8443 v8::ScriptData* sd = 8444 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8445 CHECK(sd->HasError()); 8446 delete sd; 8447} 8448 8449 8450TEST(Regress31661) { 8451 v8::V8::Initialize(); 8452 const char* script = " The Definintive Guide"; 8453 v8::ScriptData* sd = 8454 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8455 CHECK(sd->HasError()); 8456 delete sd; 8457} 8458 8459 8460// Tests that ScriptData can be serialized and deserialized. 8461TEST(PreCompileSerialization) { 8462 v8::V8::Initialize(); 8463 const char* script = "function foo(a) { return a+1; }"; 8464 v8::ScriptData* sd = 8465 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8466 8467 // Serialize. 8468 int serialized_data_length = sd->Length(); 8469 char* serialized_data = i::NewArray<char>(serialized_data_length); 8470 memcpy(serialized_data, sd->Data(), serialized_data_length); 8471 8472 // Deserialize. 8473 v8::ScriptData* deserialized_sd = 8474 v8::ScriptData::New(serialized_data, serialized_data_length); 8475 8476 // Verify that the original is the same as the deserialized. 8477 CHECK_EQ(sd->Length(), deserialized_sd->Length()); 8478 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length())); 8479 CHECK_EQ(sd->HasError(), deserialized_sd->HasError()); 8480 8481 delete sd; 8482 delete deserialized_sd; 8483} 8484 8485 8486// Attempts to deserialize bad data. 8487TEST(PreCompileDeserializationError) { 8488 v8::V8::Initialize(); 8489 const char* data = "DONT CARE"; 8490 int invalid_size = 3; 8491 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size); 8492 8493 CHECK_EQ(0, sd->Length()); 8494 8495 delete sd; 8496} 8497 8498 8499// Verifies that the Handle<String> and const char* versions of the API produce 8500// the same results (at least for one trivial case). 8501TEST(PreCompileAPIVariationsAreSame) { 8502 v8::V8::Initialize(); 8503 v8::HandleScope scope; 8504 8505 const char* cstring = "function foo(a) { return a+1; }"; 8506 v8::ScriptData* sd_from_cstring = 8507 v8::ScriptData::PreCompile(cstring, i::StrLength(cstring)); 8508 8509 TestAsciiResource* resource = new TestAsciiResource(cstring); 8510 v8::ScriptData* sd_from_istring = v8::ScriptData::PreCompile( 8511 v8::String::NewExternal(resource)); 8512 8513 CHECK_EQ(sd_from_cstring->Length(), sd_from_istring->Length()); 8514 CHECK_EQ(0, memcmp(sd_from_cstring->Data(), 8515 sd_from_istring->Data(), 8516 sd_from_cstring->Length())); 8517 8518 delete sd_from_cstring; 8519 delete sd_from_istring; 8520} 8521 8522 8523// This tests that we do not allow dictionary load/call inline caches 8524// to use functions that have not yet been compiled. The potential 8525// problem of loading a function that has not yet been compiled can 8526// arise because we share code between contexts via the compilation 8527// cache. 8528THREADED_TEST(DictionaryICLoadedFunction) { 8529 v8::HandleScope scope; 8530 // Test LoadIC. 8531 for (int i = 0; i < 2; i++) { 8532 LocalContext context; 8533 context->Global()->Set(v8_str("tmp"), v8::True()); 8534 context->Global()->Delete(v8_str("tmp")); 8535 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');"); 8536 } 8537 // Test CallIC. 8538 for (int i = 0; i < 2; i++) { 8539 LocalContext context; 8540 context->Global()->Set(v8_str("tmp"), v8::True()); 8541 context->Global()->Delete(v8_str("tmp")); 8542 CompileRun("for (var j = 0; j < 10; j++) RegExp('')"); 8543 } 8544} 8545 8546 8547// Test that cross-context new calls use the context of the callee to 8548// create the new JavaScript object. 8549THREADED_TEST(CrossContextNew) { 8550 v8::HandleScope scope; 8551 v8::Persistent<Context> context0 = Context::New(); 8552 v8::Persistent<Context> context1 = Context::New(); 8553 8554 // Allow cross-domain access. 8555 Local<String> token = v8_str("<security token>"); 8556 context0->SetSecurityToken(token); 8557 context1->SetSecurityToken(token); 8558 8559 // Set an 'x' property on the Object prototype and define a 8560 // constructor function in context0. 8561 context0->Enter(); 8562 CompileRun("Object.prototype.x = 42; function C() {};"); 8563 context0->Exit(); 8564 8565 // Call the constructor function from context0 and check that the 8566 // result has the 'x' property. 8567 context1->Enter(); 8568 context1->Global()->Set(v8_str("other"), context0->Global()); 8569 Local<Value> value = CompileRun("var instance = new other.C(); instance.x"); 8570 CHECK(value->IsInt32()); 8571 CHECK_EQ(42, value->Int32Value()); 8572 context1->Exit(); 8573 8574 // Dispose the contexts to allow them to be garbage collected. 8575 context0.Dispose(); 8576 context1.Dispose(); 8577} 8578 8579 8580class RegExpInterruptTest { 8581 public: 8582 RegExpInterruptTest() : block_(NULL) {} 8583 ~RegExpInterruptTest() { delete block_; } 8584 void RunTest() { 8585 block_ = i::OS::CreateSemaphore(0); 8586 gc_count_ = 0; 8587 gc_during_regexp_ = 0; 8588 regexp_success_ = false; 8589 gc_success_ = false; 8590 GCThread gc_thread(this); 8591 gc_thread.Start(); 8592 v8::Locker::StartPreemption(1); 8593 8594 LongRunningRegExp(); 8595 { 8596 v8::Unlocker unlock; 8597 gc_thread.Join(); 8598 } 8599 v8::Locker::StopPreemption(); 8600 CHECK(regexp_success_); 8601 CHECK(gc_success_); 8602 } 8603 private: 8604 // Number of garbage collections required. 8605 static const int kRequiredGCs = 5; 8606 8607 class GCThread : public i::Thread { 8608 public: 8609 explicit GCThread(RegExpInterruptTest* test) 8610 : test_(test) {} 8611 virtual void Run() { 8612 test_->CollectGarbage(); 8613 } 8614 private: 8615 RegExpInterruptTest* test_; 8616 }; 8617 8618 void CollectGarbage() { 8619 block_->Wait(); 8620 while (gc_during_regexp_ < kRequiredGCs) { 8621 { 8622 v8::Locker lock; 8623 // TODO(lrn): Perhaps create some garbage before collecting. 8624 i::Heap::CollectAllGarbage(false); 8625 gc_count_++; 8626 } 8627 i::OS::Sleep(1); 8628 } 8629 gc_success_ = true; 8630 } 8631 8632 void LongRunningRegExp() { 8633 block_->Signal(); // Enable garbage collection thread on next preemption. 8634 int rounds = 0; 8635 while (gc_during_regexp_ < kRequiredGCs) { 8636 int gc_before = gc_count_; 8637 { 8638 // Match 15-30 "a"'s against 14 and a "b". 8639 const char* c_source = 8640 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 8641 ".exec('aaaaaaaaaaaaaaab') === null"; 8642 Local<String> source = String::New(c_source); 8643 Local<Script> script = Script::Compile(source); 8644 Local<Value> result = script->Run(); 8645 if (!result->BooleanValue()) { 8646 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit. 8647 return; 8648 } 8649 } 8650 { 8651 // Match 15-30 "a"'s against 15 and a "b". 8652 const char* c_source = 8653 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 8654 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'"; 8655 Local<String> source = String::New(c_source); 8656 Local<Script> script = Script::Compile(source); 8657 Local<Value> result = script->Run(); 8658 if (!result->BooleanValue()) { 8659 gc_during_regexp_ = kRequiredGCs; 8660 return; 8661 } 8662 } 8663 int gc_after = gc_count_; 8664 gc_during_regexp_ += gc_after - gc_before; 8665 rounds++; 8666 i::OS::Sleep(1); 8667 } 8668 regexp_success_ = true; 8669 } 8670 8671 i::Semaphore* block_; 8672 int gc_count_; 8673 int gc_during_regexp_; 8674 bool regexp_success_; 8675 bool gc_success_; 8676}; 8677 8678 8679// Test that a regular expression execution can be interrupted and 8680// survive a garbage collection. 8681TEST(RegExpInterruption) { 8682 v8::Locker lock; 8683 v8::V8::Initialize(); 8684 v8::HandleScope scope; 8685 Local<Context> local_env; 8686 { 8687 LocalContext env; 8688 local_env = env.local(); 8689 } 8690 8691 // Local context should still be live. 8692 CHECK(!local_env.IsEmpty()); 8693 local_env->Enter(); 8694 8695 // Should complete without problems. 8696 RegExpInterruptTest().RunTest(); 8697 8698 local_env->Exit(); 8699} 8700 8701 8702class ApplyInterruptTest { 8703 public: 8704 ApplyInterruptTest() : block_(NULL) {} 8705 ~ApplyInterruptTest() { delete block_; } 8706 void RunTest() { 8707 block_ = i::OS::CreateSemaphore(0); 8708 gc_count_ = 0; 8709 gc_during_apply_ = 0; 8710 apply_success_ = false; 8711 gc_success_ = false; 8712 GCThread gc_thread(this); 8713 gc_thread.Start(); 8714 v8::Locker::StartPreemption(1); 8715 8716 LongRunningApply(); 8717 { 8718 v8::Unlocker unlock; 8719 gc_thread.Join(); 8720 } 8721 v8::Locker::StopPreemption(); 8722 CHECK(apply_success_); 8723 CHECK(gc_success_); 8724 } 8725 private: 8726 // Number of garbage collections required. 8727 static const int kRequiredGCs = 2; 8728 8729 class GCThread : public i::Thread { 8730 public: 8731 explicit GCThread(ApplyInterruptTest* test) 8732 : test_(test) {} 8733 virtual void Run() { 8734 test_->CollectGarbage(); 8735 } 8736 private: 8737 ApplyInterruptTest* test_; 8738 }; 8739 8740 void CollectGarbage() { 8741 block_->Wait(); 8742 while (gc_during_apply_ < kRequiredGCs) { 8743 { 8744 v8::Locker lock; 8745 i::Heap::CollectAllGarbage(false); 8746 gc_count_++; 8747 } 8748 i::OS::Sleep(1); 8749 } 8750 gc_success_ = true; 8751 } 8752 8753 void LongRunningApply() { 8754 block_->Signal(); 8755 int rounds = 0; 8756 while (gc_during_apply_ < kRequiredGCs) { 8757 int gc_before = gc_count_; 8758 { 8759 const char* c_source = 8760 "function do_very_little(bar) {" 8761 " this.foo = bar;" 8762 "}" 8763 "for (var i = 0; i < 100000; i++) {" 8764 " do_very_little.apply(this, ['bar']);" 8765 "}"; 8766 Local<String> source = String::New(c_source); 8767 Local<Script> script = Script::Compile(source); 8768 Local<Value> result = script->Run(); 8769 // Check that no exception was thrown. 8770 CHECK(!result.IsEmpty()); 8771 } 8772 int gc_after = gc_count_; 8773 gc_during_apply_ += gc_after - gc_before; 8774 rounds++; 8775 } 8776 apply_success_ = true; 8777 } 8778 8779 i::Semaphore* block_; 8780 int gc_count_; 8781 int gc_during_apply_; 8782 bool apply_success_; 8783 bool gc_success_; 8784}; 8785 8786 8787// Test that nothing bad happens if we get a preemption just when we were 8788// about to do an apply(). 8789TEST(ApplyInterruption) { 8790 v8::Locker lock; 8791 v8::V8::Initialize(); 8792 v8::HandleScope scope; 8793 Local<Context> local_env; 8794 { 8795 LocalContext env; 8796 local_env = env.local(); 8797 } 8798 8799 // Local context should still be live. 8800 CHECK(!local_env.IsEmpty()); 8801 local_env->Enter(); 8802 8803 // Should complete without problems. 8804 ApplyInterruptTest().RunTest(); 8805 8806 local_env->Exit(); 8807} 8808 8809 8810// Verify that we can clone an object 8811TEST(ObjectClone) { 8812 v8::HandleScope scope; 8813 LocalContext env; 8814 8815 const char* sample = 8816 "var rv = {};" \ 8817 "rv.alpha = 'hello';" \ 8818 "rv.beta = 123;" \ 8819 "rv;"; 8820 8821 // Create an object, verify basics. 8822 Local<Value> val = CompileRun(sample); 8823 CHECK(val->IsObject()); 8824 Local<v8::Object> obj = val.As<v8::Object>(); 8825 obj->Set(v8_str("gamma"), v8_str("cloneme")); 8826 8827 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha"))); 8828 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 8829 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma"))); 8830 8831 // Clone it. 8832 Local<v8::Object> clone = obj->Clone(); 8833 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha"))); 8834 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta"))); 8835 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma"))); 8836 8837 // Set a property on the clone, verify each object. 8838 clone->Set(v8_str("beta"), v8::Integer::New(456)); 8839 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 8840 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta"))); 8841} 8842 8843 8844class AsciiVectorResource : public v8::String::ExternalAsciiStringResource { 8845 public: 8846 explicit AsciiVectorResource(i::Vector<const char> vector) 8847 : data_(vector) {} 8848 virtual ~AsciiVectorResource() {} 8849 virtual size_t length() const { return data_.length(); } 8850 virtual const char* data() const { return data_.start(); } 8851 private: 8852 i::Vector<const char> data_; 8853}; 8854 8855 8856class UC16VectorResource : public v8::String::ExternalStringResource { 8857 public: 8858 explicit UC16VectorResource(i::Vector<const i::uc16> vector) 8859 : data_(vector) {} 8860 virtual ~UC16VectorResource() {} 8861 virtual size_t length() const { return data_.length(); } 8862 virtual const i::uc16* data() const { return data_.start(); } 8863 private: 8864 i::Vector<const i::uc16> data_; 8865}; 8866 8867 8868static void MorphAString(i::String* string, 8869 AsciiVectorResource* ascii_resource, 8870 UC16VectorResource* uc16_resource) { 8871 CHECK(i::StringShape(string).IsExternal()); 8872 if (string->IsAsciiRepresentation()) { 8873 // Check old map is not symbol or long. 8874 CHECK(string->map() == i::Heap::external_ascii_string_map()); 8875 // Morph external string to be TwoByte string. 8876 string->set_map(i::Heap::external_string_map()); 8877 i::ExternalTwoByteString* morphed = 8878 i::ExternalTwoByteString::cast(string); 8879 morphed->set_resource(uc16_resource); 8880 } else { 8881 // Check old map is not symbol or long. 8882 CHECK(string->map() == i::Heap::external_string_map()); 8883 // Morph external string to be ASCII string. 8884 string->set_map(i::Heap::external_ascii_string_map()); 8885 i::ExternalAsciiString* morphed = 8886 i::ExternalAsciiString::cast(string); 8887 morphed->set_resource(ascii_resource); 8888 } 8889} 8890 8891 8892// Test that we can still flatten a string if the components it is built up 8893// from have been turned into 16 bit strings in the mean time. 8894THREADED_TEST(MorphCompositeStringTest) { 8895 const char* c_string = "Now is the time for all good men" 8896 " to come to the aid of the party"; 8897 uint16_t* two_byte_string = AsciiToTwoByteString(c_string); 8898 { 8899 v8::HandleScope scope; 8900 LocalContext env; 8901 AsciiVectorResource ascii_resource( 8902 i::Vector<const char>(c_string, i::StrLength(c_string))); 8903 UC16VectorResource uc16_resource( 8904 i::Vector<const uint16_t>(two_byte_string, 8905 i::StrLength(c_string))); 8906 8907 Local<String> lhs(v8::Utils::ToLocal( 8908 i::Factory::NewExternalStringFromAscii(&ascii_resource))); 8909 Local<String> rhs(v8::Utils::ToLocal( 8910 i::Factory::NewExternalStringFromAscii(&ascii_resource))); 8911 8912 env->Global()->Set(v8_str("lhs"), lhs); 8913 env->Global()->Set(v8_str("rhs"), rhs); 8914 8915 CompileRun( 8916 "var cons = lhs + rhs;" 8917 "var slice = lhs.substring(1, lhs.length - 1);" 8918 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);"); 8919 8920 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource); 8921 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource); 8922 8923 // Now do some stuff to make sure the strings are flattened, etc. 8924 CompileRun( 8925 "/[^a-z]/.test(cons);" 8926 "/[^a-z]/.test(slice);" 8927 "/[^a-z]/.test(slice_on_cons);"); 8928 const char* expected_cons = 8929 "Now is the time for all good men to come to the aid of the party" 8930 "Now is the time for all good men to come to the aid of the party"; 8931 const char* expected_slice = 8932 "ow is the time for all good men to come to the aid of the part"; 8933 const char* expected_slice_on_cons = 8934 "ow is the time for all good men to come to the aid of the party" 8935 "Now is the time for all good men to come to the aid of the part"; 8936 CHECK_EQ(String::New(expected_cons), 8937 env->Global()->Get(v8_str("cons"))); 8938 CHECK_EQ(String::New(expected_slice), 8939 env->Global()->Get(v8_str("slice"))); 8940 CHECK_EQ(String::New(expected_slice_on_cons), 8941 env->Global()->Get(v8_str("slice_on_cons"))); 8942 } 8943} 8944 8945 8946TEST(CompileExternalTwoByteSource) { 8947 v8::HandleScope scope; 8948 LocalContext context; 8949 8950 // This is a very short list of sources, which currently is to check for a 8951 // regression caused by r2703. 8952 const char* ascii_sources[] = { 8953 "0.5", 8954 "-0.5", // This mainly testes PushBack in the Scanner. 8955 "--0.5", // This mainly testes PushBack in the Scanner. 8956 NULL 8957 }; 8958 8959 // Compile the sources as external two byte strings. 8960 for (int i = 0; ascii_sources[i] != NULL; i++) { 8961 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]); 8962 UC16VectorResource uc16_resource( 8963 i::Vector<const uint16_t>(two_byte_string, 8964 i::StrLength(ascii_sources[i]))); 8965 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource); 8966 v8::Script::Compile(source); 8967 } 8968} 8969 8970 8971class RegExpStringModificationTest { 8972 public: 8973 RegExpStringModificationTest() 8974 : block_(i::OS::CreateSemaphore(0)), 8975 morphs_(0), 8976 morphs_during_regexp_(0), 8977 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)), 8978 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {} 8979 ~RegExpStringModificationTest() { delete block_; } 8980 void RunTest() { 8981 regexp_success_ = false; 8982 morph_success_ = false; 8983 8984 // Initialize the contents of two_byte_content_ to be a uc16 representation 8985 // of "aaaaaaaaaaaaaab". 8986 for (int i = 0; i < 14; i++) { 8987 two_byte_content_[i] = 'a'; 8988 } 8989 two_byte_content_[14] = 'b'; 8990 8991 // Create the input string for the regexp - the one we are going to change 8992 // properties of. 8993 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_); 8994 8995 // Inject the input as a global variable. 8996 i::Handle<i::String> input_name = 8997 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5)); 8998 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE); 8999 9000 9001 MorphThread morph_thread(this); 9002 morph_thread.Start(); 9003 v8::Locker::StartPreemption(1); 9004 LongRunningRegExp(); 9005 { 9006 v8::Unlocker unlock; 9007 morph_thread.Join(); 9008 } 9009 v8::Locker::StopPreemption(); 9010 CHECK(regexp_success_); 9011 CHECK(morph_success_); 9012 } 9013 private: 9014 9015 // Number of string modifications required. 9016 static const int kRequiredModifications = 5; 9017 static const int kMaxModifications = 100; 9018 9019 class MorphThread : public i::Thread { 9020 public: 9021 explicit MorphThread(RegExpStringModificationTest* test) 9022 : test_(test) {} 9023 virtual void Run() { 9024 test_->MorphString(); 9025 } 9026 private: 9027 RegExpStringModificationTest* test_; 9028 }; 9029 9030 void MorphString() { 9031 block_->Wait(); 9032 while (morphs_during_regexp_ < kRequiredModifications && 9033 morphs_ < kMaxModifications) { 9034 { 9035 v8::Locker lock; 9036 // Swap string between ascii and two-byte representation. 9037 i::String* string = *input_; 9038 MorphAString(string, &ascii_resource_, &uc16_resource_); 9039 morphs_++; 9040 } 9041 i::OS::Sleep(1); 9042 } 9043 morph_success_ = true; 9044 } 9045 9046 void LongRunningRegExp() { 9047 block_->Signal(); // Enable morphing thread on next preemption. 9048 while (morphs_during_regexp_ < kRequiredModifications && 9049 morphs_ < kMaxModifications) { 9050 int morphs_before = morphs_; 9051 { 9052 // Match 15-30 "a"'s against 14 and a "b". 9053 const char* c_source = 9054 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 9055 ".exec(input) === null"; 9056 Local<String> source = String::New(c_source); 9057 Local<Script> script = Script::Compile(source); 9058 Local<Value> result = script->Run(); 9059 CHECK(result->IsTrue()); 9060 } 9061 int morphs_after = morphs_; 9062 morphs_during_regexp_ += morphs_after - morphs_before; 9063 } 9064 regexp_success_ = true; 9065 } 9066 9067 i::uc16 two_byte_content_[15]; 9068 i::Semaphore* block_; 9069 int morphs_; 9070 int morphs_during_regexp_; 9071 bool regexp_success_; 9072 bool morph_success_; 9073 i::Handle<i::String> input_; 9074 AsciiVectorResource ascii_resource_; 9075 UC16VectorResource uc16_resource_; 9076}; 9077 9078 9079// Test that a regular expression execution can be interrupted and 9080// the string changed without failing. 9081TEST(RegExpStringModification) { 9082 v8::Locker lock; 9083 v8::V8::Initialize(); 9084 v8::HandleScope scope; 9085 Local<Context> local_env; 9086 { 9087 LocalContext env; 9088 local_env = env.local(); 9089 } 9090 9091 // Local context should still be live. 9092 CHECK(!local_env.IsEmpty()); 9093 local_env->Enter(); 9094 9095 // Should complete without problems. 9096 RegExpStringModificationTest().RunTest(); 9097 9098 local_env->Exit(); 9099} 9100 9101 9102// Test that we can set a property on the global object even if there 9103// is a read-only property in the prototype chain. 9104TEST(ReadOnlyPropertyInGlobalProto) { 9105 v8::HandleScope scope; 9106 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9107 LocalContext context(0, templ); 9108 v8::Handle<v8::Object> global = context->Global(); 9109 v8::Handle<v8::Object> global_proto = 9110 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__"))); 9111 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly); 9112 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly); 9113 // Check without 'eval' or 'with'. 9114 v8::Handle<v8::Value> res = 9115 CompileRun("function f() { x = 42; return x; }; f()"); 9116 // Check with 'eval'. 9117 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()"); 9118 CHECK_EQ(v8::Integer::New(42), res); 9119 // Check with 'with'. 9120 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()"); 9121 CHECK_EQ(v8::Integer::New(42), res); 9122} 9123 9124static int force_set_set_count = 0; 9125static int force_set_get_count = 0; 9126bool pass_on_get = false; 9127 9128static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name, 9129 const v8::AccessorInfo& info) { 9130 force_set_get_count++; 9131 if (pass_on_get) { 9132 return v8::Handle<v8::Value>(); 9133 } else { 9134 return v8::Int32::New(3); 9135 } 9136} 9137 9138static void ForceSetSetter(v8::Local<v8::String> name, 9139 v8::Local<v8::Value> value, 9140 const v8::AccessorInfo& info) { 9141 force_set_set_count++; 9142} 9143 9144static v8::Handle<v8::Value> ForceSetInterceptSetter( 9145 v8::Local<v8::String> name, 9146 v8::Local<v8::Value> value, 9147 const v8::AccessorInfo& info) { 9148 force_set_set_count++; 9149 return v8::Undefined(); 9150} 9151 9152TEST(ForceSet) { 9153 force_set_get_count = 0; 9154 force_set_set_count = 0; 9155 pass_on_get = false; 9156 9157 v8::HandleScope scope; 9158 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9159 v8::Handle<v8::String> access_property = v8::String::New("a"); 9160 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter); 9161 LocalContext context(NULL, templ); 9162 v8::Handle<v8::Object> global = context->Global(); 9163 9164 // Ordinary properties 9165 v8::Handle<v8::String> simple_property = v8::String::New("p"); 9166 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly); 9167 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9168 // This should fail because the property is read-only 9169 global->Set(simple_property, v8::Int32::New(5)); 9170 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9171 // This should succeed even though the property is read-only 9172 global->ForceSet(simple_property, v8::Int32::New(6)); 9173 CHECK_EQ(6, global->Get(simple_property)->Int32Value()); 9174 9175 // Accessors 9176 CHECK_EQ(0, force_set_set_count); 9177 CHECK_EQ(0, force_set_get_count); 9178 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 9179 // CHECK_EQ the property shouldn't override it, just call the setter 9180 // which in this case does nothing. 9181 global->Set(access_property, v8::Int32::New(7)); 9182 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 9183 CHECK_EQ(1, force_set_set_count); 9184 CHECK_EQ(2, force_set_get_count); 9185 // Forcing the property to be set should override the accessor without 9186 // calling it 9187 global->ForceSet(access_property, v8::Int32::New(8)); 9188 CHECK_EQ(8, global->Get(access_property)->Int32Value()); 9189 CHECK_EQ(1, force_set_set_count); 9190 CHECK_EQ(2, force_set_get_count); 9191} 9192 9193TEST(ForceSetWithInterceptor) { 9194 force_set_get_count = 0; 9195 force_set_set_count = 0; 9196 pass_on_get = false; 9197 9198 v8::HandleScope scope; 9199 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9200 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter); 9201 LocalContext context(NULL, templ); 9202 v8::Handle<v8::Object> global = context->Global(); 9203 9204 v8::Handle<v8::String> some_property = v8::String::New("a"); 9205 CHECK_EQ(0, force_set_set_count); 9206 CHECK_EQ(0, force_set_get_count); 9207 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 9208 // Setting the property shouldn't override it, just call the setter 9209 // which in this case does nothing. 9210 global->Set(some_property, v8::Int32::New(7)); 9211 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 9212 CHECK_EQ(1, force_set_set_count); 9213 CHECK_EQ(2, force_set_get_count); 9214 // Getting the property when the interceptor returns an empty handle 9215 // should yield undefined, since the property isn't present on the 9216 // object itself yet. 9217 pass_on_get = true; 9218 CHECK(global->Get(some_property)->IsUndefined()); 9219 CHECK_EQ(1, force_set_set_count); 9220 CHECK_EQ(3, force_set_get_count); 9221 // Forcing the property to be set should cause the value to be 9222 // set locally without calling the interceptor. 9223 global->ForceSet(some_property, v8::Int32::New(8)); 9224 CHECK_EQ(8, global->Get(some_property)->Int32Value()); 9225 CHECK_EQ(1, force_set_set_count); 9226 CHECK_EQ(4, force_set_get_count); 9227 // Reenabling the interceptor should cause it to take precedence over 9228 // the property 9229 pass_on_get = false; 9230 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 9231 CHECK_EQ(1, force_set_set_count); 9232 CHECK_EQ(5, force_set_get_count); 9233 // The interceptor should also work for other properties 9234 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value()); 9235 CHECK_EQ(1, force_set_set_count); 9236 CHECK_EQ(6, force_set_get_count); 9237} 9238 9239 9240THREADED_TEST(ForceDelete) { 9241 v8::HandleScope scope; 9242 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9243 LocalContext context(NULL, templ); 9244 v8::Handle<v8::Object> global = context->Global(); 9245 9246 // Ordinary properties 9247 v8::Handle<v8::String> simple_property = v8::String::New("p"); 9248 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete); 9249 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9250 // This should fail because the property is dont-delete. 9251 CHECK(!global->Delete(simple_property)); 9252 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 9253 // This should succeed even though the property is dont-delete. 9254 CHECK(global->ForceDelete(simple_property)); 9255 CHECK(global->Get(simple_property)->IsUndefined()); 9256} 9257 9258 9259static int force_delete_interceptor_count = 0; 9260static bool pass_on_delete = false; 9261 9262 9263static v8::Handle<v8::Boolean> ForceDeleteDeleter( 9264 v8::Local<v8::String> name, 9265 const v8::AccessorInfo& info) { 9266 force_delete_interceptor_count++; 9267 if (pass_on_delete) { 9268 return v8::Handle<v8::Boolean>(); 9269 } else { 9270 return v8::True(); 9271 } 9272} 9273 9274 9275THREADED_TEST(ForceDeleteWithInterceptor) { 9276 force_delete_interceptor_count = 0; 9277 pass_on_delete = false; 9278 9279 v8::HandleScope scope; 9280 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 9281 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter); 9282 LocalContext context(NULL, templ); 9283 v8::Handle<v8::Object> global = context->Global(); 9284 9285 v8::Handle<v8::String> some_property = v8::String::New("a"); 9286 global->Set(some_property, v8::Integer::New(42), v8::DontDelete); 9287 9288 // Deleting a property should get intercepted and nothing should 9289 // happen. 9290 CHECK_EQ(0, force_delete_interceptor_count); 9291 CHECK(global->Delete(some_property)); 9292 CHECK_EQ(1, force_delete_interceptor_count); 9293 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 9294 // Deleting the property when the interceptor returns an empty 9295 // handle should not delete the property since it is DontDelete. 9296 pass_on_delete = true; 9297 CHECK(!global->Delete(some_property)); 9298 CHECK_EQ(2, force_delete_interceptor_count); 9299 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 9300 // Forcing the property to be deleted should delete the value 9301 // without calling the interceptor. 9302 CHECK(global->ForceDelete(some_property)); 9303 CHECK(global->Get(some_property)->IsUndefined()); 9304 CHECK_EQ(2, force_delete_interceptor_count); 9305} 9306 9307 9308// Make sure that forcing a delete invalidates any IC stubs, so we 9309// don't read the hole value. 9310THREADED_TEST(ForceDeleteIC) { 9311 v8::HandleScope scope; 9312 LocalContext context; 9313 // Create a DontDelete variable on the global object. 9314 CompileRun("this.__proto__ = { foo: 'horse' };" 9315 "var foo = 'fish';" 9316 "function f() { return foo.length; }"); 9317 // Initialize the IC for foo in f. 9318 CompileRun("for (var i = 0; i < 4; i++) f();"); 9319 // Make sure the value of foo is correct before the deletion. 9320 CHECK_EQ(4, CompileRun("f()")->Int32Value()); 9321 // Force the deletion of foo. 9322 CHECK(context->Global()->ForceDelete(v8_str("foo"))); 9323 // Make sure the value for foo is read from the prototype, and that 9324 // we don't get in trouble with reading the deleted cell value 9325 // sentinel. 9326 CHECK_EQ(5, CompileRun("f()")->Int32Value()); 9327} 9328 9329 9330v8::Persistent<Context> calling_context0; 9331v8::Persistent<Context> calling_context1; 9332v8::Persistent<Context> calling_context2; 9333 9334 9335// Check that the call to the callback is initiated in 9336// calling_context2, the directly calling context is calling_context1 9337// and the callback itself is in calling_context0. 9338static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) { 9339 ApiTestFuzzer::Fuzz(); 9340 CHECK(Context::GetCurrent() == calling_context0); 9341 CHECK(Context::GetCalling() == calling_context1); 9342 CHECK(Context::GetEntered() == calling_context2); 9343 return v8::Integer::New(42); 9344} 9345 9346 9347THREADED_TEST(GetCallingContext) { 9348 v8::HandleScope scope; 9349 9350 calling_context0 = Context::New(); 9351 calling_context1 = Context::New(); 9352 calling_context2 = Context::New(); 9353 9354 // Allow cross-domain access. 9355 Local<String> token = v8_str("<security token>"); 9356 calling_context0->SetSecurityToken(token); 9357 calling_context1->SetSecurityToken(token); 9358 calling_context2->SetSecurityToken(token); 9359 9360 // Create an object with a C++ callback in context0. 9361 calling_context0->Enter(); 9362 Local<v8::FunctionTemplate> callback_templ = 9363 v8::FunctionTemplate::New(GetCallingContextCallback); 9364 calling_context0->Global()->Set(v8_str("callback"), 9365 callback_templ->GetFunction()); 9366 calling_context0->Exit(); 9367 9368 // Expose context0 in context1 and setup a function that calls the 9369 // callback function. 9370 calling_context1->Enter(); 9371 calling_context1->Global()->Set(v8_str("context0"), 9372 calling_context0->Global()); 9373 CompileRun("function f() { context0.callback() }"); 9374 calling_context1->Exit(); 9375 9376 // Expose context1 in context2 and call the callback function in 9377 // context0 indirectly through f in context1. 9378 calling_context2->Enter(); 9379 calling_context2->Global()->Set(v8_str("context1"), 9380 calling_context1->Global()); 9381 CompileRun("context1.f()"); 9382 calling_context2->Exit(); 9383 9384 // Dispose the contexts to allow them to be garbage collected. 9385 calling_context0.Dispose(); 9386 calling_context1.Dispose(); 9387 calling_context2.Dispose(); 9388 calling_context0.Clear(); 9389 calling_context1.Clear(); 9390 calling_context2.Clear(); 9391} 9392 9393 9394// Check that a variable declaration with no explicit initialization 9395// value does not shadow an existing property in the prototype chain. 9396// 9397// This is consistent with Firefox and Safari. 9398// 9399// See http://crbug.com/12548. 9400THREADED_TEST(InitGlobalVarInProtoChain) { 9401 v8::HandleScope scope; 9402 LocalContext context; 9403 // Introduce a variable in the prototype chain. 9404 CompileRun("__proto__.x = 42"); 9405 v8::Handle<v8::Value> result = CompileRun("var x; x"); 9406 CHECK(!result->IsUndefined()); 9407 CHECK_EQ(42, result->Int32Value()); 9408} 9409 9410 9411// Regression test for issue 398. 9412// If a function is added to an object, creating a constant function 9413// field, and the result is cloned, replacing the constant function on the 9414// original should not affect the clone. 9415// See http://code.google.com/p/v8/issues/detail?id=398 9416THREADED_TEST(ReplaceConstantFunction) { 9417 v8::HandleScope scope; 9418 LocalContext context; 9419 v8::Handle<v8::Object> obj = v8::Object::New(); 9420 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 9421 v8::Handle<v8::String> foo_string = v8::String::New("foo"); 9422 obj->Set(foo_string, func_templ->GetFunction()); 9423 v8::Handle<v8::Object> obj_clone = obj->Clone(); 9424 obj_clone->Set(foo_string, v8::String::New("Hello")); 9425 CHECK(!obj->Get(foo_string)->IsUndefined()); 9426} 9427 9428 9429// Regression test for http://crbug.com/16276. 9430THREADED_TEST(Regress16276) { 9431 v8::HandleScope scope; 9432 LocalContext context; 9433 // Force the IC in f to be a dictionary load IC. 9434 CompileRun("function f(obj) { return obj.x; }\n" 9435 "var obj = { x: { foo: 42 }, y: 87 };\n" 9436 "var x = obj.x;\n" 9437 "delete obj.y;\n" 9438 "for (var i = 0; i < 5; i++) f(obj);"); 9439 // Detach the global object to make 'this' refer directly to the 9440 // global object (not the proxy), and make sure that the dictionary 9441 // load IC doesn't mess up loading directly from the global object. 9442 context->DetachGlobal(); 9443 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value()); 9444} 9445 9446 9447THREADED_TEST(PixelArray) { 9448 v8::HandleScope scope; 9449 LocalContext context; 9450 const int kElementCount = 260; 9451 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); 9452 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount, 9453 pixel_data); 9454 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9455 for (int i = 0; i < kElementCount; i++) { 9456 pixels->set(i, i % 256); 9457 } 9458 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9459 for (int i = 0; i < kElementCount; i++) { 9460 CHECK_EQ(i % 256, pixels->get(i)); 9461 CHECK_EQ(i % 256, pixel_data[i]); 9462 } 9463 9464 v8::Handle<v8::Object> obj = v8::Object::New(); 9465 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 9466 // Set the elements to be the pixels. 9467 // jsobj->set_elements(*pixels); 9468 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount); 9469 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value()); 9470 obj->Set(v8_str("field"), v8::Int32::New(1503)); 9471 context->Global()->Set(v8_str("pixels"), obj); 9472 v8::Handle<v8::Value> result = CompileRun("pixels.field"); 9473 CHECK_EQ(1503, result->Int32Value()); 9474 result = CompileRun("pixels[1]"); 9475 CHECK_EQ(1, result->Int32Value()); 9476 9477 result = CompileRun("var sum = 0;" 9478 "for (var i = 0; i < 8; i++) {" 9479 " sum += pixels[i] = pixels[i] = -i;" 9480 "}" 9481 "sum;"); 9482 CHECK_EQ(-28, result->Int32Value()); 9483 9484 result = CompileRun("var sum = 0;" 9485 "for (var i = 0; i < 8; i++) {" 9486 " sum += pixels[i] = pixels[i] = 0;" 9487 "}" 9488 "sum;"); 9489 CHECK_EQ(0, result->Int32Value()); 9490 9491 result = CompileRun("var sum = 0;" 9492 "for (var i = 0; i < 8; i++) {" 9493 " sum += pixels[i] = pixels[i] = 255;" 9494 "}" 9495 "sum;"); 9496 CHECK_EQ(8 * 255, result->Int32Value()); 9497 9498 result = CompileRun("var sum = 0;" 9499 "for (var i = 0; i < 8; i++) {" 9500 " sum += pixels[i] = pixels[i] = 256 + i;" 9501 "}" 9502 "sum;"); 9503 CHECK_EQ(2076, result->Int32Value()); 9504 9505 result = CompileRun("var sum = 0;" 9506 "for (var i = 0; i < 8; i++) {" 9507 " sum += pixels[i] = pixels[i] = i;" 9508 "}" 9509 "sum;"); 9510 CHECK_EQ(28, result->Int32Value()); 9511 9512 result = CompileRun("var sum = 0;" 9513 "for (var i = 0; i < 8; i++) {" 9514 " sum += pixels[i];" 9515 "}" 9516 "sum;"); 9517 CHECK_EQ(28, result->Int32Value()); 9518 9519 i::Handle<i::Smi> value(i::Smi::FromInt(2)); 9520 i::SetElement(jsobj, 1, value); 9521 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value()); 9522 *value.location() = i::Smi::FromInt(256); 9523 i::SetElement(jsobj, 1, value); 9524 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value()); 9525 *value.location() = i::Smi::FromInt(-1); 9526 i::SetElement(jsobj, 1, value); 9527 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value()); 9528 9529 result = CompileRun("for (var i = 0; i < 8; i++) {" 9530 " pixels[i] = (i * 65) - 109;" 9531 "}" 9532 "pixels[1] + pixels[6];"); 9533 CHECK_EQ(255, result->Int32Value()); 9534 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value()); 9535 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value()); 9536 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value()); 9537 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value()); 9538 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value()); 9539 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value()); 9540 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value()); 9541 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value()); 9542 result = CompileRun("var sum = 0;" 9543 "for (var i = 0; i < 8; i++) {" 9544 " sum += pixels[i];" 9545 "}" 9546 "sum;"); 9547 CHECK_EQ(984, result->Int32Value()); 9548 9549 result = CompileRun("for (var i = 0; i < 8; i++) {" 9550 " pixels[i] = (i * 1.1);" 9551 "}" 9552 "pixels[1] + pixels[6];"); 9553 CHECK_EQ(8, result->Int32Value()); 9554 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value()); 9555 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value()); 9556 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value()); 9557 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value()); 9558 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value()); 9559 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value()); 9560 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value()); 9561 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value()); 9562 9563 result = CompileRun("for (var i = 0; i < 8; i++) {" 9564 " pixels[7] = undefined;" 9565 "}" 9566 "pixels[7];"); 9567 CHECK_EQ(0, result->Int32Value()); 9568 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value()); 9569 9570 result = CompileRun("for (var i = 0; i < 8; i++) {" 9571 " pixels[6] = '2.3';" 9572 "}" 9573 "pixels[6];"); 9574 CHECK_EQ(2, result->Int32Value()); 9575 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value()); 9576 9577 result = CompileRun("for (var i = 0; i < 8; i++) {" 9578 " pixels[5] = NaN;" 9579 "}" 9580 "pixels[5];"); 9581 CHECK_EQ(0, result->Int32Value()); 9582 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 9583 9584 result = CompileRun("for (var i = 0; i < 8; i++) {" 9585 " pixels[8] = Infinity;" 9586 "}" 9587 "pixels[8];"); 9588 CHECK_EQ(255, result->Int32Value()); 9589 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value()); 9590 9591 result = CompileRun("for (var i = 0; i < 8; i++) {" 9592 " pixels[9] = -Infinity;" 9593 "}" 9594 "pixels[9];"); 9595 CHECK_EQ(0, result->Int32Value()); 9596 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value()); 9597 9598 result = CompileRun("pixels[3] = 33;" 9599 "delete pixels[3];" 9600 "pixels[3];"); 9601 CHECK_EQ(33, result->Int32Value()); 9602 9603 result = CompileRun("pixels[0] = 10; pixels[1] = 11;" 9604 "pixels[2] = 12; pixels[3] = 13;" 9605 "pixels.__defineGetter__('2'," 9606 "function() { return 120; });" 9607 "pixels[2];"); 9608 CHECK_EQ(12, result->Int32Value()); 9609 9610 result = CompileRun("var js_array = new Array(40);" 9611 "js_array[0] = 77;" 9612 "js_array;"); 9613 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 9614 9615 result = CompileRun("pixels[1] = 23;" 9616 "pixels.__proto__ = [];" 9617 "js_array.__proto__ = pixels;" 9618 "js_array.concat(pixels);"); 9619 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 9620 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 9621 9622 result = CompileRun("pixels[1] = 23;"); 9623 CHECK_EQ(23, result->Int32Value()); 9624 9625 // Test for index greater than 255. Regression test for: 9626 // http://code.google.com/p/chromium/issues/detail?id=26337. 9627 result = CompileRun("pixels[256] = 255;"); 9628 CHECK_EQ(255, result->Int32Value()); 9629 result = CompileRun("var i = 0;" 9630 "for (var j = 0; j < 8; j++) { i = pixels[256]; }" 9631 "i"); 9632 CHECK_EQ(255, result->Int32Value()); 9633 9634 free(pixel_data); 9635} 9636 9637 9638THREADED_TEST(PixelArrayInfo) { 9639 v8::HandleScope scope; 9640 LocalContext context; 9641 for (int size = 0; size < 100; size += 10) { 9642 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(size)); 9643 v8::Handle<v8::Object> obj = v8::Object::New(); 9644 obj->SetIndexedPropertiesToPixelData(pixel_data, size); 9645 CHECK(obj->HasIndexedPropertiesInPixelData()); 9646 CHECK_EQ(pixel_data, obj->GetIndexedPropertiesPixelData()); 9647 CHECK_EQ(size, obj->GetIndexedPropertiesPixelDataLength()); 9648 free(pixel_data); 9649 } 9650} 9651 9652 9653static int ExternalArrayElementSize(v8::ExternalArrayType array_type) { 9654 switch (array_type) { 9655 case v8::kExternalByteArray: 9656 case v8::kExternalUnsignedByteArray: 9657 return 1; 9658 break; 9659 case v8::kExternalShortArray: 9660 case v8::kExternalUnsignedShortArray: 9661 return 2; 9662 break; 9663 case v8::kExternalIntArray: 9664 case v8::kExternalUnsignedIntArray: 9665 case v8::kExternalFloatArray: 9666 return 4; 9667 break; 9668 default: 9669 UNREACHABLE(); 9670 return -1; 9671 } 9672 UNREACHABLE(); 9673 return -1; 9674} 9675 9676 9677template <class ExternalArrayClass, class ElementType> 9678static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, 9679 int64_t low, 9680 int64_t high) { 9681 v8::HandleScope scope; 9682 LocalContext context; 9683 const int kElementCount = 40; 9684 int element_size = ExternalArrayElementSize(array_type); 9685 ElementType* array_data = 9686 static_cast<ElementType*>(malloc(kElementCount * element_size)); 9687 i::Handle<ExternalArrayClass> array = 9688 i::Handle<ExternalArrayClass>::cast( 9689 i::Factory::NewExternalArray(kElementCount, array_type, array_data)); 9690 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9691 for (int i = 0; i < kElementCount; i++) { 9692 array->set(i, static_cast<ElementType>(i)); 9693 } 9694 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9695 for (int i = 0; i < kElementCount; i++) { 9696 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i))); 9697 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i])); 9698 } 9699 9700 v8::Handle<v8::Object> obj = v8::Object::New(); 9701 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 9702 // Set the elements to be the external array. 9703 obj->SetIndexedPropertiesToExternalArrayData(array_data, 9704 array_type, 9705 kElementCount); 9706 CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number())); 9707 obj->Set(v8_str("field"), v8::Int32::New(1503)); 9708 context->Global()->Set(v8_str("ext_array"), obj); 9709 v8::Handle<v8::Value> result = CompileRun("ext_array.field"); 9710 CHECK_EQ(1503, result->Int32Value()); 9711 result = CompileRun("ext_array[1]"); 9712 CHECK_EQ(1, result->Int32Value()); 9713 9714 // Check pass through of assigned smis 9715 result = CompileRun("var sum = 0;" 9716 "for (var i = 0; i < 8; i++) {" 9717 " sum += ext_array[i] = ext_array[i] = -i;" 9718 "}" 9719 "sum;"); 9720 CHECK_EQ(-28, result->Int32Value()); 9721 9722 // Check assigned smis 9723 result = CompileRun("for (var i = 0; i < 8; i++) {" 9724 " ext_array[i] = i;" 9725 "}" 9726 "var sum = 0;" 9727 "for (var i = 0; i < 8; i++) {" 9728 " sum += ext_array[i];" 9729 "}" 9730 "sum;"); 9731 CHECK_EQ(28, result->Int32Value()); 9732 9733 // Check assigned smis in reverse order 9734 result = CompileRun("for (var i = 8; --i >= 0; ) {" 9735 " ext_array[i] = i;" 9736 "}" 9737 "var sum = 0;" 9738 "for (var i = 0; i < 8; i++) {" 9739 " sum += ext_array[i];" 9740 "}" 9741 "sum;"); 9742 CHECK_EQ(28, result->Int32Value()); 9743 9744 // Check pass through of assigned HeapNumbers 9745 result = CompileRun("var sum = 0;" 9746 "for (var i = 0; i < 16; i+=2) {" 9747 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);" 9748 "}" 9749 "sum;"); 9750 CHECK_EQ(-28, result->Int32Value()); 9751 9752 // Check assigned HeapNumbers 9753 result = CompileRun("for (var i = 0; i < 16; i+=2) {" 9754 " ext_array[i] = (i * 0.5);" 9755 "}" 9756 "var sum = 0;" 9757 "for (var i = 0; i < 16; i+=2) {" 9758 " sum += ext_array[i];" 9759 "}" 9760 "sum;"); 9761 CHECK_EQ(28, result->Int32Value()); 9762 9763 // Check assigned HeapNumbers in reverse order 9764 result = CompileRun("for (var i = 14; i >= 0; i-=2) {" 9765 " ext_array[i] = (i * 0.5);" 9766 "}" 9767 "var sum = 0;" 9768 "for (var i = 0; i < 16; i+=2) {" 9769 " sum += ext_array[i];" 9770 "}" 9771 "sum;"); 9772 CHECK_EQ(28, result->Int32Value()); 9773 9774 i::ScopedVector<char> test_buf(1024); 9775 9776 // Check legal boundary conditions. 9777 // The repeated loads and stores ensure the ICs are exercised. 9778 const char* boundary_program = 9779 "var res = 0;" 9780 "for (var i = 0; i < 16; i++) {" 9781 " ext_array[i] = %lld;" 9782 " if (i > 8) {" 9783 " res = ext_array[i];" 9784 " }" 9785 "}" 9786 "res;"; 9787 i::OS::SNPrintF(test_buf, 9788 boundary_program, 9789 low); 9790 result = CompileRun(test_buf.start()); 9791 CHECK_EQ(low, result->IntegerValue()); 9792 9793 i::OS::SNPrintF(test_buf, 9794 boundary_program, 9795 high); 9796 result = CompileRun(test_buf.start()); 9797 CHECK_EQ(high, result->IntegerValue()); 9798 9799 // Check misprediction of type in IC. 9800 result = CompileRun("var tmp_array = ext_array;" 9801 "var sum = 0;" 9802 "for (var i = 0; i < 8; i++) {" 9803 " tmp_array[i] = i;" 9804 " sum += tmp_array[i];" 9805 " if (i == 4) {" 9806 " tmp_array = {};" 9807 " }" 9808 "}" 9809 "sum;"); 9810 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9811 CHECK_EQ(28, result->Int32Value()); 9812 9813 // Make sure out-of-range loads do not throw. 9814 i::OS::SNPrintF(test_buf, 9815 "var caught_exception = false;" 9816 "try {" 9817 " ext_array[%d];" 9818 "} catch (e) {" 9819 " caught_exception = true;" 9820 "}" 9821 "caught_exception;", 9822 kElementCount); 9823 result = CompileRun(test_buf.start()); 9824 CHECK_EQ(false, result->BooleanValue()); 9825 9826 // Make sure out-of-range stores do not throw. 9827 i::OS::SNPrintF(test_buf, 9828 "var caught_exception = false;" 9829 "try {" 9830 " ext_array[%d] = 1;" 9831 "} catch (e) {" 9832 " caught_exception = true;" 9833 "}" 9834 "caught_exception;", 9835 kElementCount); 9836 result = CompileRun(test_buf.start()); 9837 CHECK_EQ(false, result->BooleanValue()); 9838 9839 // Check other boundary conditions, values and operations. 9840 result = CompileRun("for (var i = 0; i < 8; i++) {" 9841 " ext_array[7] = undefined;" 9842 "}" 9843 "ext_array[7];"); 9844 CHECK_EQ(0, result->Int32Value()); 9845 CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number())); 9846 9847 result = CompileRun("for (var i = 0; i < 8; i++) {" 9848 " ext_array[6] = '2.3';" 9849 "}" 9850 "ext_array[6];"); 9851 CHECK_EQ(2, result->Int32Value()); 9852 CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number())); 9853 9854 if (array_type != v8::kExternalFloatArray) { 9855 // Though the specification doesn't state it, be explicit about 9856 // converting NaNs and +/-Infinity to zero. 9857 result = CompileRun("for (var i = 0; i < 8; i++) {" 9858 " ext_array[i] = 5;" 9859 "}" 9860 "for (var i = 0; i < 8; i++) {" 9861 " ext_array[i] = NaN;" 9862 "}" 9863 "ext_array[5];"); 9864 CHECK_EQ(0, result->Int32Value()); 9865 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 9866 9867 result = CompileRun("for (var i = 0; i < 8; i++) {" 9868 " ext_array[i] = 5;" 9869 "}" 9870 "for (var i = 0; i < 8; i++) {" 9871 " ext_array[i] = Infinity;" 9872 "}" 9873 "ext_array[5];"); 9874 CHECK_EQ(0, result->Int32Value()); 9875 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 9876 9877 result = CompileRun("for (var i = 0; i < 8; i++) {" 9878 " ext_array[i] = 5;" 9879 "}" 9880 "for (var i = 0; i < 8; i++) {" 9881 " ext_array[i] = -Infinity;" 9882 "}" 9883 "ext_array[5];"); 9884 CHECK_EQ(0, result->Int32Value()); 9885 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 9886 } 9887 9888 result = CompileRun("ext_array[3] = 33;" 9889 "delete ext_array[3];" 9890 "ext_array[3];"); 9891 CHECK_EQ(33, result->Int32Value()); 9892 9893 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;" 9894 "ext_array[2] = 12; ext_array[3] = 13;" 9895 "ext_array.__defineGetter__('2'," 9896 "function() { return 120; });" 9897 "ext_array[2];"); 9898 CHECK_EQ(12, result->Int32Value()); 9899 9900 result = CompileRun("var js_array = new Array(40);" 9901 "js_array[0] = 77;" 9902 "js_array;"); 9903 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 9904 9905 result = CompileRun("ext_array[1] = 23;" 9906 "ext_array.__proto__ = [];" 9907 "js_array.__proto__ = ext_array;" 9908 "js_array.concat(ext_array);"); 9909 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 9910 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 9911 9912 result = CompileRun("ext_array[1] = 23;"); 9913 CHECK_EQ(23, result->Int32Value()); 9914 9915 // Test more complex manipulations which cause eax to contain values 9916 // that won't be completely overwritten by loads from the arrays. 9917 // This catches bugs in the instructions used for the KeyedLoadIC 9918 // for byte and word types. 9919 { 9920 const int kXSize = 300; 9921 const int kYSize = 300; 9922 const int kLargeElementCount = kXSize * kYSize * 4; 9923 ElementType* large_array_data = 9924 static_cast<ElementType*>(malloc(kLargeElementCount * element_size)); 9925 i::Handle<ExternalArrayClass> large_array = 9926 i::Handle<ExternalArrayClass>::cast( 9927 i::Factory::NewExternalArray(kLargeElementCount, 9928 array_type, 9929 array_data)); 9930 v8::Handle<v8::Object> large_obj = v8::Object::New(); 9931 // Set the elements to be the external array. 9932 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data, 9933 array_type, 9934 kLargeElementCount); 9935 context->Global()->Set(v8_str("large_array"), large_obj); 9936 // Initialize contents of a few rows. 9937 for (int x = 0; x < 300; x++) { 9938 int row = 0; 9939 int offset = row * 300 * 4; 9940 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 9941 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 9942 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 9943 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 9944 row = 150; 9945 offset = row * 300 * 4; 9946 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 9947 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 9948 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 9949 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 9950 row = 298; 9951 offset = row * 300 * 4; 9952 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 9953 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 9954 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 9955 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 9956 } 9957 // The goal of the code below is to make "offset" large enough 9958 // that the computation of the index (which goes into eax) has 9959 // high bits set which will not be overwritten by a byte or short 9960 // load. 9961 result = CompileRun("var failed = false;" 9962 "var offset = 0;" 9963 "for (var i = 0; i < 300; i++) {" 9964 " if (large_array[4 * i] != 127 ||" 9965 " large_array[4 * i + 1] != 0 ||" 9966 " large_array[4 * i + 2] != 0 ||" 9967 " large_array[4 * i + 3] != 127) {" 9968 " failed = true;" 9969 " }" 9970 "}" 9971 "offset = 150 * 300 * 4;" 9972 "for (var i = 0; i < 300; i++) {" 9973 " if (large_array[offset + 4 * i] != 127 ||" 9974 " large_array[offset + 4 * i + 1] != 0 ||" 9975 " large_array[offset + 4 * i + 2] != 0 ||" 9976 " large_array[offset + 4 * i + 3] != 127) {" 9977 " failed = true;" 9978 " }" 9979 "}" 9980 "offset = 298 * 300 * 4;" 9981 "for (var i = 0; i < 300; i++) {" 9982 " if (large_array[offset + 4 * i] != 127 ||" 9983 " large_array[offset + 4 * i + 1] != 0 ||" 9984 " large_array[offset + 4 * i + 2] != 0 ||" 9985 " large_array[offset + 4 * i + 3] != 127) {" 9986 " failed = true;" 9987 " }" 9988 "}" 9989 "!failed;"); 9990 CHECK_EQ(true, result->BooleanValue()); 9991 free(large_array_data); 9992 } 9993 9994 free(array_data); 9995} 9996 9997 9998THREADED_TEST(ExternalByteArray) { 9999 ExternalArrayTestHelper<v8::internal::ExternalByteArray, int8_t>( 10000 v8::kExternalByteArray, 10001 -128, 10002 127); 10003} 10004 10005 10006THREADED_TEST(ExternalUnsignedByteArray) { 10007 ExternalArrayTestHelper<v8::internal::ExternalUnsignedByteArray, uint8_t>( 10008 v8::kExternalUnsignedByteArray, 10009 0, 10010 255); 10011} 10012 10013 10014THREADED_TEST(ExternalShortArray) { 10015 ExternalArrayTestHelper<v8::internal::ExternalShortArray, int16_t>( 10016 v8::kExternalShortArray, 10017 -32768, 10018 32767); 10019} 10020 10021 10022THREADED_TEST(ExternalUnsignedShortArray) { 10023 ExternalArrayTestHelper<v8::internal::ExternalUnsignedShortArray, uint16_t>( 10024 v8::kExternalUnsignedShortArray, 10025 0, 10026 65535); 10027} 10028 10029 10030THREADED_TEST(ExternalIntArray) { 10031 ExternalArrayTestHelper<v8::internal::ExternalIntArray, int32_t>( 10032 v8::kExternalIntArray, 10033 INT_MIN, // -2147483648 10034 INT_MAX); // 2147483647 10035} 10036 10037 10038THREADED_TEST(ExternalUnsignedIntArray) { 10039 ExternalArrayTestHelper<v8::internal::ExternalUnsignedIntArray, uint32_t>( 10040 v8::kExternalUnsignedIntArray, 10041 0, 10042 UINT_MAX); // 4294967295 10043} 10044 10045 10046THREADED_TEST(ExternalFloatArray) { 10047 ExternalArrayTestHelper<v8::internal::ExternalFloatArray, float>( 10048 v8::kExternalFloatArray, 10049 -500, 10050 500); 10051} 10052 10053 10054THREADED_TEST(ExternalArrays) { 10055 TestExternalByteArray(); 10056 TestExternalUnsignedByteArray(); 10057 TestExternalShortArray(); 10058 TestExternalUnsignedShortArray(); 10059 TestExternalIntArray(); 10060 TestExternalUnsignedIntArray(); 10061 TestExternalFloatArray(); 10062} 10063 10064 10065void ExternalArrayInfoTestHelper(v8::ExternalArrayType array_type) { 10066 v8::HandleScope scope; 10067 LocalContext context; 10068 for (int size = 0; size < 100; size += 10) { 10069 int element_size = ExternalArrayElementSize(array_type); 10070 void* external_data = malloc(size * element_size); 10071 v8::Handle<v8::Object> obj = v8::Object::New(); 10072 obj->SetIndexedPropertiesToExternalArrayData( 10073 external_data, array_type, size); 10074 CHECK(obj->HasIndexedPropertiesInExternalArrayData()); 10075 CHECK_EQ(external_data, obj->GetIndexedPropertiesExternalArrayData()); 10076 CHECK_EQ(array_type, obj->GetIndexedPropertiesExternalArrayDataType()); 10077 CHECK_EQ(size, obj->GetIndexedPropertiesExternalArrayDataLength()); 10078 free(external_data); 10079 } 10080} 10081 10082 10083THREADED_TEST(ExternalArrayInfo) { 10084 ExternalArrayInfoTestHelper(v8::kExternalByteArray); 10085 ExternalArrayInfoTestHelper(v8::kExternalUnsignedByteArray); 10086 ExternalArrayInfoTestHelper(v8::kExternalShortArray); 10087 ExternalArrayInfoTestHelper(v8::kExternalUnsignedShortArray); 10088 ExternalArrayInfoTestHelper(v8::kExternalIntArray); 10089 ExternalArrayInfoTestHelper(v8::kExternalUnsignedIntArray); 10090 ExternalArrayInfoTestHelper(v8::kExternalFloatArray); 10091} 10092 10093 10094THREADED_TEST(ScriptContextDependence) { 10095 v8::HandleScope scope; 10096 LocalContext c1; 10097 const char *source = "foo"; 10098 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source)); 10099 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source)); 10100 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100)); 10101 CHECK_EQ(dep->Run()->Int32Value(), 100); 10102 CHECK_EQ(indep->Run()->Int32Value(), 100); 10103 LocalContext c2; 10104 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101)); 10105 CHECK_EQ(dep->Run()->Int32Value(), 100); 10106 CHECK_EQ(indep->Run()->Int32Value(), 101); 10107} 10108 10109 10110THREADED_TEST(StackTrace) { 10111 v8::HandleScope scope; 10112 LocalContext context; 10113 v8::TryCatch try_catch; 10114 const char *source = "function foo() { FAIL.FAIL; }; foo();"; 10115 v8::Handle<v8::String> src = v8::String::New(source); 10116 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test"); 10117 v8::Script::New(src, origin)->Run(); 10118 CHECK(try_catch.HasCaught()); 10119 v8::String::Utf8Value stack(try_catch.StackTrace()); 10120 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL); 10121} 10122 10123 10124// Checks that a StackFrame has certain expected values. 10125void checkStackFrame(const char* expected_script_name, 10126 const char* expected_func_name, int expected_line_number, 10127 int expected_column, bool is_eval, bool is_constructor, 10128 v8::Handle<v8::StackFrame> frame) { 10129 v8::HandleScope scope; 10130 v8::String::Utf8Value func_name(frame->GetFunctionName()); 10131 v8::String::Utf8Value script_name(frame->GetScriptName()); 10132 if (*script_name == NULL) { 10133 // The situation where there is no associated script, like for evals. 10134 CHECK(expected_script_name == NULL); 10135 } else { 10136 CHECK(strstr(*script_name, expected_script_name) != NULL); 10137 } 10138 CHECK(strstr(*func_name, expected_func_name) != NULL); 10139 CHECK_EQ(expected_line_number, frame->GetLineNumber()); 10140 CHECK_EQ(expected_column, frame->GetColumn()); 10141 CHECK_EQ(is_eval, frame->IsEval()); 10142 CHECK_EQ(is_constructor, frame->IsConstructor()); 10143} 10144 10145 10146v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) { 10147 v8::HandleScope scope; 10148 const char* origin = "capture-stack-trace-test"; 10149 const int kOverviewTest = 1; 10150 const int kDetailedTest = 2; 10151 10152 ASSERT(args.Length() == 1); 10153 10154 int testGroup = args[0]->Int32Value(); 10155 if (testGroup == kOverviewTest) { 10156 v8::Handle<v8::StackTrace> stackTrace = 10157 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview); 10158 CHECK_EQ(4, stackTrace->GetFrameCount()); 10159 checkStackFrame(origin, "bar", 2, 10, false, false, 10160 stackTrace->GetFrame(0)); 10161 checkStackFrame(origin, "foo", 6, 3, false, false, 10162 stackTrace->GetFrame(1)); 10163 checkStackFrame(NULL, "", 1, 1, false, false, 10164 stackTrace->GetFrame(2)); 10165 // The last frame is an anonymous function that has the initial call. 10166 checkStackFrame(origin, "", 8, 7, false, false, 10167 stackTrace->GetFrame(3)); 10168 10169 CHECK(stackTrace->AsArray()->IsArray()); 10170 } else if (testGroup == kDetailedTest) { 10171 v8::Handle<v8::StackTrace> stackTrace = 10172 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); 10173 CHECK_EQ(4, stackTrace->GetFrameCount()); 10174 checkStackFrame(origin, "bat", 4, 22, false, false, 10175 stackTrace->GetFrame(0)); 10176 checkStackFrame(origin, "baz", 8, 3, false, true, 10177 stackTrace->GetFrame(1)); 10178#ifdef ENABLE_DEBUGGER_SUPPORT 10179 bool is_eval = true; 10180#else // ENABLE_DEBUGGER_SUPPORT 10181 bool is_eval = false; 10182#endif // ENABLE_DEBUGGER_SUPPORT 10183 10184 checkStackFrame(NULL, "", 1, 1, is_eval, false, 10185 stackTrace->GetFrame(2)); 10186 // The last frame is an anonymous function that has the initial call to foo. 10187 checkStackFrame(origin, "", 10, 1, false, false, 10188 stackTrace->GetFrame(3)); 10189 10190 CHECK(stackTrace->AsArray()->IsArray()); 10191 } 10192 return v8::Undefined(); 10193} 10194 10195 10196// Tests the C++ StackTrace API. 10197THREADED_TEST(CaptureStackTrace) { 10198 v8::HandleScope scope; 10199 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test"); 10200 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10201 templ->Set(v8_str("AnalyzeStackInNativeCode"), 10202 v8::FunctionTemplate::New(AnalyzeStackInNativeCode)); 10203 LocalContext context(0, templ); 10204 10205 // Test getting OVERVIEW information. Should ignore information that is not 10206 // script name, function name, line number, and column offset. 10207 const char *overview_source = 10208 "function bar() {\n" 10209 " var y; AnalyzeStackInNativeCode(1);\n" 10210 "}\n" 10211 "function foo() {\n" 10212 "\n" 10213 " bar();\n" 10214 "}\n" 10215 "var x;eval('new foo();');"; 10216 v8::Handle<v8::String> overview_src = v8::String::New(overview_source); 10217 v8::Handle<Value> overview_result = 10218 v8::Script::New(overview_src, origin)->Run(); 10219 ASSERT(!overview_result.IsEmpty()); 10220 ASSERT(overview_result->IsObject()); 10221 10222 // Test getting DETAILED information. 10223 const char *detailed_source = 10224 "function bat() {AnalyzeStackInNativeCode(2);\n" 10225 "}\n" 10226 "\n" 10227 "function baz() {\n" 10228 " bat();\n" 10229 "}\n" 10230 "eval('new baz();');"; 10231 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source); 10232 // Make the script using a non-zero line and column offset. 10233 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3); 10234 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5); 10235 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset); 10236 v8::Handle<v8::Script> detailed_script( 10237 v8::Script::New(detailed_src, &detailed_origin)); 10238 v8::Handle<Value> detailed_result = detailed_script->Run(); 10239 ASSERT(!detailed_result.IsEmpty()); 10240 ASSERT(detailed_result->IsObject()); 10241} 10242 10243 10244// Test that idle notification can be handled and eventually returns true. 10245THREADED_TEST(IdleNotification) { 10246 bool rv = false; 10247 for (int i = 0; i < 100; i++) { 10248 rv = v8::V8::IdleNotification(); 10249 if (rv) 10250 break; 10251 } 10252 CHECK(rv == true); 10253} 10254 10255 10256static uint32_t* stack_limit; 10257 10258static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) { 10259 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit()); 10260 return v8::Undefined(); 10261} 10262 10263 10264// Uses the address of a local variable to determine the stack top now. 10265// Given a size, returns an address that is that far from the current 10266// top of stack. 10267static uint32_t* ComputeStackLimit(uint32_t size) { 10268 uint32_t* answer = &size - (size / sizeof(size)); 10269 // If the size is very large and the stack is very near the bottom of 10270 // memory then the calculation above may wrap around and give an address 10271 // that is above the (downwards-growing) stack. In that case we return 10272 // a very low address. 10273 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size)); 10274 return answer; 10275} 10276 10277 10278TEST(SetResourceConstraints) { 10279 static const int K = 1024; 10280 uint32_t* set_limit = ComputeStackLimit(128 * K); 10281 10282 // Set stack limit. 10283 v8::ResourceConstraints constraints; 10284 constraints.set_stack_limit(set_limit); 10285 CHECK(v8::SetResourceConstraints(&constraints)); 10286 10287 // Execute a script. 10288 v8::HandleScope scope; 10289 LocalContext env; 10290 Local<v8::FunctionTemplate> fun_templ = 10291 v8::FunctionTemplate::New(GetStackLimitCallback); 10292 Local<Function> fun = fun_templ->GetFunction(); 10293 env->Global()->Set(v8_str("get_stack_limit"), fun); 10294 CompileRun("get_stack_limit();"); 10295 10296 CHECK(stack_limit == set_limit); 10297} 10298 10299 10300TEST(SetResourceConstraintsInThread) { 10301 uint32_t* set_limit; 10302 { 10303 v8::Locker locker; 10304 static const int K = 1024; 10305 set_limit = ComputeStackLimit(128 * K); 10306 10307 // Set stack limit. 10308 v8::ResourceConstraints constraints; 10309 constraints.set_stack_limit(set_limit); 10310 CHECK(v8::SetResourceConstraints(&constraints)); 10311 10312 // Execute a script. 10313 v8::HandleScope scope; 10314 LocalContext env; 10315 Local<v8::FunctionTemplate> fun_templ = 10316 v8::FunctionTemplate::New(GetStackLimitCallback); 10317 Local<Function> fun = fun_templ->GetFunction(); 10318 env->Global()->Set(v8_str("get_stack_limit"), fun); 10319 CompileRun("get_stack_limit();"); 10320 10321 CHECK(stack_limit == set_limit); 10322 } 10323 { 10324 v8::Locker locker; 10325 CHECK(stack_limit == set_limit); 10326 } 10327} 10328 10329 10330THREADED_TEST(GetHeapStatistics) { 10331 v8::HandleScope scope; 10332 LocalContext c1; 10333 v8::HeapStatistics heap_statistics; 10334 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0); 10335 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0); 10336 v8::V8::GetHeapStatistics(&heap_statistics); 10337 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0); 10338 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0); 10339} 10340 10341 10342static double DoubleFromBits(uint64_t value) { 10343 double target; 10344#ifdef BIG_ENDIAN_FLOATING_POINT 10345 const int kIntSize = 4; 10346 // Somebody swapped the lower and higher half of doubles. 10347 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); 10348 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); 10349#else 10350 memcpy(&target, &value, sizeof(target)); 10351#endif 10352 return target; 10353} 10354 10355 10356static uint64_t DoubleToBits(double value) { 10357 uint64_t target; 10358#ifdef BIG_ENDIAN_FLOATING_POINT 10359 const int kIntSize = 4; 10360 // Somebody swapped the lower and higher half of doubles. 10361 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); 10362 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); 10363#else 10364 memcpy(&target, &value, sizeof(target)); 10365#endif 10366 return target; 10367} 10368 10369 10370static double DoubleToDateTime(double input) { 10371 double date_limit = 864e13; 10372 if (IsNaN(input) || input < -date_limit || input > date_limit) { 10373 return i::OS::nan_value(); 10374 } 10375 return (input < 0) ? -(floor(-input)) : floor(input); 10376} 10377 10378// We don't have a consistent way to write 64-bit constants syntactically, so we 10379// split them into two 32-bit constants and combine them programmatically. 10380static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) { 10381 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits); 10382} 10383 10384 10385THREADED_TEST(QuietSignalingNaNs) { 10386 v8::HandleScope scope; 10387 LocalContext context; 10388 v8::TryCatch try_catch; 10389 10390 // Special double values. 10391 double snan = DoubleFromBits(0x7ff00000, 0x00000001); 10392 double qnan = DoubleFromBits(0x7ff80000, 0x00000000); 10393 double infinity = DoubleFromBits(0x7ff00000, 0x00000000); 10394 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu); 10395 double min_normal = DoubleFromBits(0x00100000, 0x00000000); 10396 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu); 10397 double min_denormal = DoubleFromBits(0x00000000, 0x00000001); 10398 10399 // Date values are capped at +/-100000000 days (times 864e5 ms per day) 10400 // on either side of the epoch. 10401 double date_limit = 864e13; 10402 10403 double test_values[] = { 10404 snan, 10405 qnan, 10406 infinity, 10407 max_normal, 10408 date_limit + 1, 10409 date_limit, 10410 min_normal, 10411 max_denormal, 10412 min_denormal, 10413 0, 10414 -0, 10415 -min_denormal, 10416 -max_denormal, 10417 -min_normal, 10418 -date_limit, 10419 -date_limit - 1, 10420 -max_normal, 10421 -infinity, 10422 -qnan, 10423 -snan 10424 }; 10425 int num_test_values = 20; 10426 10427 for (int i = 0; i < num_test_values; i++) { 10428 double test_value = test_values[i]; 10429 10430 // Check that Number::New preserves non-NaNs and quiets SNaNs. 10431 v8::Handle<v8::Value> number = v8::Number::New(test_value); 10432 double stored_number = number->NumberValue(); 10433 if (!IsNaN(test_value)) { 10434 CHECK_EQ(test_value, stored_number); 10435 } else { 10436 uint64_t stored_bits = DoubleToBits(stored_number); 10437 // Check if quiet nan (bits 51..62 all set). 10438 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 10439 } 10440 10441 // Check that Date::New preserves non-NaNs in the date range and 10442 // quiets SNaNs. 10443 v8::Handle<v8::Value> date = v8::Date::New(test_value); 10444 double expected_stored_date = DoubleToDateTime(test_value); 10445 double stored_date = date->NumberValue(); 10446 if (!IsNaN(expected_stored_date)) { 10447 CHECK_EQ(expected_stored_date, stored_date); 10448 } else { 10449 uint64_t stored_bits = DoubleToBits(stored_date); 10450 // Check if quiet nan (bits 51..62 all set). 10451 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 10452 } 10453 } 10454} 10455 10456 10457static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) { 10458 v8::HandleScope scope; 10459 v8::TryCatch tc; 10460 v8::Handle<v8::String> str = args[0]->ToString(); 10461 if (tc.HasCaught()) 10462 return tc.ReThrow(); 10463 return v8::Undefined(); 10464} 10465 10466 10467// Test that an exception can be propagated down through a spaghetti 10468// stack using ReThrow. 10469THREADED_TEST(SpaghettiStackReThrow) { 10470 v8::HandleScope scope; 10471 LocalContext context; 10472 context->Global()->Set( 10473 v8::String::New("s"), 10474 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction()); 10475 v8::TryCatch try_catch; 10476 CompileRun( 10477 "var i = 0;" 10478 "var o = {" 10479 " toString: function () {" 10480 " if (i == 10) {" 10481 " throw 'Hey!';" 10482 " } else {" 10483 " i++;" 10484 " return s(o);" 10485 " }" 10486 " }" 10487 "};" 10488 "s(o);"); 10489 CHECK(try_catch.HasCaught()); 10490 v8::String::Utf8Value value(try_catch.Exception()); 10491 CHECK_EQ(0, strcmp(*value, "Hey!")); 10492} 10493 10494 10495TEST(Regress528) { 10496 v8::V8::Initialize(); 10497 10498 v8::HandleScope scope; 10499 v8::Persistent<Context> context; 10500 v8::Persistent<Context> other_context; 10501 int gc_count; 10502 10503 // Create a context used to keep the code from aging in the compilation 10504 // cache. 10505 other_context = Context::New(); 10506 10507 // Context-dependent context data creates reference from the compilation 10508 // cache to the global object. 10509 const char* source_simple = "1"; 10510 context = Context::New(); 10511 { 10512 v8::HandleScope scope; 10513 10514 context->Enter(); 10515 Local<v8::String> obj = v8::String::New(""); 10516 context->SetData(obj); 10517 CompileRun(source_simple); 10518 context->Exit(); 10519 } 10520 context.Dispose(); 10521 for (gc_count = 1; gc_count < 10; gc_count++) { 10522 other_context->Enter(); 10523 CompileRun(source_simple); 10524 other_context->Exit(); 10525 v8::internal::Heap::CollectAllGarbage(false); 10526 if (GetGlobalObjectsCount() == 1) break; 10527 } 10528 CHECK_GE(2, gc_count); 10529 CHECK_EQ(1, GetGlobalObjectsCount()); 10530 10531 // Eval in a function creates reference from the compilation cache to the 10532 // global object. 10533 const char* source_eval = "function f(){eval('1')}; f()"; 10534 context = Context::New(); 10535 { 10536 v8::HandleScope scope; 10537 10538 context->Enter(); 10539 CompileRun(source_eval); 10540 context->Exit(); 10541 } 10542 context.Dispose(); 10543 for (gc_count = 1; gc_count < 10; gc_count++) { 10544 other_context->Enter(); 10545 CompileRun(source_eval); 10546 other_context->Exit(); 10547 v8::internal::Heap::CollectAllGarbage(false); 10548 if (GetGlobalObjectsCount() == 1) break; 10549 } 10550 CHECK_GE(2, gc_count); 10551 CHECK_EQ(1, GetGlobalObjectsCount()); 10552 10553 // Looking up the line number for an exception creates reference from the 10554 // compilation cache to the global object. 10555 const char* source_exception = "function f(){throw 1;} f()"; 10556 context = Context::New(); 10557 { 10558 v8::HandleScope scope; 10559 10560 context->Enter(); 10561 v8::TryCatch try_catch; 10562 CompileRun(source_exception); 10563 CHECK(try_catch.HasCaught()); 10564 v8::Handle<v8::Message> message = try_catch.Message(); 10565 CHECK(!message.IsEmpty()); 10566 CHECK_EQ(1, message->GetLineNumber()); 10567 context->Exit(); 10568 } 10569 context.Dispose(); 10570 for (gc_count = 1; gc_count < 10; gc_count++) { 10571 other_context->Enter(); 10572 CompileRun(source_exception); 10573 other_context->Exit(); 10574 v8::internal::Heap::CollectAllGarbage(false); 10575 if (GetGlobalObjectsCount() == 1) break; 10576 } 10577 CHECK_GE(2, gc_count); 10578 CHECK_EQ(1, GetGlobalObjectsCount()); 10579 10580 other_context.Dispose(); 10581} 10582 10583 10584THREADED_TEST(ScriptOrigin) { 10585 v8::HandleScope scope; 10586 LocalContext env; 10587 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 10588 v8::Handle<v8::String> script = v8::String::New( 10589 "function f() {}\n\nfunction g() {}"); 10590 v8::Script::Compile(script, &origin)->Run(); 10591 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 10592 env->Global()->Get(v8::String::New("f"))); 10593 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 10594 env->Global()->Get(v8::String::New("g"))); 10595 10596 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin(); 10597 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName())); 10598 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value()); 10599 10600 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin(); 10601 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName())); 10602 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value()); 10603} 10604 10605 10606THREADED_TEST(ScriptLineNumber) { 10607 v8::HandleScope scope; 10608 LocalContext env; 10609 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 10610 v8::Handle<v8::String> script = v8::String::New( 10611 "function f() {}\n\nfunction g() {}"); 10612 v8::Script::Compile(script, &origin)->Run(); 10613 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 10614 env->Global()->Get(v8::String::New("f"))); 10615 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 10616 env->Global()->Get(v8::String::New("g"))); 10617 CHECK_EQ(0, f->GetScriptLineNumber()); 10618 CHECK_EQ(2, g->GetScriptLineNumber()); 10619} 10620 10621 10622static v8::Handle<Value> GetterWhichReturns42(Local<String> name, 10623 const AccessorInfo& info) { 10624 return v8_num(42); 10625} 10626 10627 10628static void SetterWhichSetsYOnThisTo23(Local<String> name, 10629 Local<Value> value, 10630 const AccessorInfo& info) { 10631 info.This()->Set(v8_str("y"), v8_num(23)); 10632} 10633 10634 10635TEST(SetterOnConstructorPrototype) { 10636 v8::HandleScope scope; 10637 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10638 templ->SetAccessor(v8_str("x"), 10639 GetterWhichReturns42, 10640 SetterWhichSetsYOnThisTo23); 10641 LocalContext context; 10642 context->Global()->Set(v8_str("P"), templ->NewInstance()); 10643 CompileRun("function C1() {" 10644 " this.x = 23;" 10645 "};" 10646 "C1.prototype = P;" 10647 "function C2() {" 10648 " this.x = 23" 10649 "};" 10650 "C2.prototype = { };" 10651 "C2.prototype.__proto__ = P;"); 10652 10653 v8::Local<v8::Script> script; 10654 script = v8::Script::Compile(v8_str("new C1();")); 10655 for (int i = 0; i < 10; i++) { 10656 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 10657 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); 10658 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); 10659 } 10660 10661 script = v8::Script::Compile(v8_str("new C2();")); 10662 for (int i = 0; i < 10; i++) { 10663 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 10664 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value()); 10665 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value()); 10666 } 10667} 10668 10669 10670static v8::Handle<Value> NamedPropertyGetterWhichReturns42( 10671 Local<String> name, const AccessorInfo& info) { 10672 return v8_num(42); 10673} 10674 10675 10676static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23( 10677 Local<String> name, Local<Value> value, const AccessorInfo& info) { 10678 if (name->Equals(v8_str("x"))) { 10679 info.This()->Set(v8_str("y"), v8_num(23)); 10680 } 10681 return v8::Handle<Value>(); 10682} 10683 10684 10685THREADED_TEST(InterceptorOnConstructorPrototype) { 10686 v8::HandleScope scope; 10687 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10688 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42, 10689 NamedPropertySetterWhichSetsYOnThisTo23); 10690 LocalContext context; 10691 context->Global()->Set(v8_str("P"), templ->NewInstance()); 10692 CompileRun("function C1() {" 10693 " this.x = 23;" 10694 "};" 10695 "C1.prototype = P;" 10696 "function C2() {" 10697 " this.x = 23" 10698 "};" 10699 "C2.prototype = { };" 10700 "C2.prototype.__proto__ = P;"); 10701 10702 v8::Local<v8::Script> script; 10703 script = v8::Script::Compile(v8_str("new C1();")); 10704 for (int i = 0; i < 10; i++) { 10705 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 10706 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); 10707 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); 10708 } 10709 10710 script = v8::Script::Compile(v8_str("new C2();")); 10711 for (int i = 0; i < 10; i++) { 10712 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 10713 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value()); 10714 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value()); 10715 } 10716} 10717 10718 10719TEST(Bug618) { 10720 const char* source = "function C1() {" 10721 " this.x = 23;" 10722 "};" 10723 "C1.prototype = P;"; 10724 10725 v8::HandleScope scope; 10726 LocalContext context; 10727 v8::Local<v8::Script> script; 10728 10729 // Use a simple object as prototype. 10730 v8::Local<v8::Object> prototype = v8::Object::New(); 10731 prototype->Set(v8_str("y"), v8_num(42)); 10732 context->Global()->Set(v8_str("P"), prototype); 10733 10734 // This compile will add the code to the compilation cache. 10735 CompileRun(source); 10736 10737 script = v8::Script::Compile(v8_str("new C1();")); 10738 for (int i = 0; i < 10; i++) { 10739 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 10740 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); 10741 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); 10742 } 10743 10744 // Use an API object with accessors as prototype. 10745 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10746 templ->SetAccessor(v8_str("x"), 10747 GetterWhichReturns42, 10748 SetterWhichSetsYOnThisTo23); 10749 context->Global()->Set(v8_str("P"), templ->NewInstance()); 10750 10751 // This compile will get the code from the compilation cache. 10752 CompileRun(source); 10753 10754 script = v8::Script::Compile(v8_str("new C1();")); 10755 for (int i = 0; i < 10; i++) { 10756 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 10757 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); 10758 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); 10759 } 10760} 10761 10762int prologue_call_count = 0; 10763int epilogue_call_count = 0; 10764int prologue_call_count_second = 0; 10765int epilogue_call_count_second = 0; 10766 10767void PrologueCallback(v8::GCType, v8::GCCallbackFlags) { 10768 ++prologue_call_count; 10769} 10770 10771void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) { 10772 ++epilogue_call_count; 10773} 10774 10775void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { 10776 ++prologue_call_count_second; 10777} 10778 10779void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { 10780 ++epilogue_call_count_second; 10781} 10782 10783TEST(GCCallbacks) { 10784 LocalContext context; 10785 10786 v8::V8::AddGCPrologueCallback(PrologueCallback); 10787 v8::V8::AddGCEpilogueCallback(EpilogueCallback); 10788 CHECK_EQ(0, prologue_call_count); 10789 CHECK_EQ(0, epilogue_call_count); 10790 i::Heap::CollectAllGarbage(false); 10791 CHECK_EQ(1, prologue_call_count); 10792 CHECK_EQ(1, epilogue_call_count); 10793 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond); 10794 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond); 10795 i::Heap::CollectAllGarbage(false); 10796 CHECK_EQ(2, prologue_call_count); 10797 CHECK_EQ(2, epilogue_call_count); 10798 CHECK_EQ(1, prologue_call_count_second); 10799 CHECK_EQ(1, epilogue_call_count_second); 10800 v8::V8::RemoveGCPrologueCallback(PrologueCallback); 10801 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback); 10802 i::Heap::CollectAllGarbage(false); 10803 CHECK_EQ(2, prologue_call_count); 10804 CHECK_EQ(2, epilogue_call_count); 10805 CHECK_EQ(2, prologue_call_count_second); 10806 CHECK_EQ(2, epilogue_call_count_second); 10807 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond); 10808 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond); 10809 i::Heap::CollectAllGarbage(false); 10810 CHECK_EQ(2, prologue_call_count); 10811 CHECK_EQ(2, epilogue_call_count); 10812 CHECK_EQ(2, prologue_call_count_second); 10813 CHECK_EQ(2, epilogue_call_count_second); 10814} 10815 10816 10817THREADED_TEST(AddToJSFunctionResultCache) { 10818 i::FLAG_allow_natives_syntax = true; 10819 v8::HandleScope scope; 10820 10821 LocalContext context; 10822 10823 const char* code = 10824 "(function() {" 10825 " var key0 = 'a';" 10826 " var key1 = 'b';" 10827 " var r0 = %_GetFromCache(0, key0);" 10828 " var r1 = %_GetFromCache(0, key1);" 10829 " var r0_ = %_GetFromCache(0, key0);" 10830 " if (r0 !== r0_)" 10831 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;" 10832 " var r1_ = %_GetFromCache(0, key1);" 10833 " if (r1 !== r1_)" 10834 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;" 10835 " return 'PASSED';" 10836 "})()"; 10837 v8::internal::Heap::ClearJSFunctionResultCaches(); 10838 ExpectString(code, "PASSED"); 10839} 10840 10841 10842static const int k0CacheSize = 16; 10843 10844THREADED_TEST(FillJSFunctionResultCache) { 10845 i::FLAG_allow_natives_syntax = true; 10846 v8::HandleScope scope; 10847 10848 LocalContext context; 10849 10850 const char* code = 10851 "(function() {" 10852 " var k = 'a';" 10853 " var r = %_GetFromCache(0, k);" 10854 " for (var i = 0; i < 16; i++) {" 10855 " %_GetFromCache(0, 'a' + i);" 10856 " };" 10857 " if (r === %_GetFromCache(0, k))" 10858 " return 'FAILED: k0CacheSize is too small';" 10859 " return 'PASSED';" 10860 "})()"; 10861 v8::internal::Heap::ClearJSFunctionResultCaches(); 10862 ExpectString(code, "PASSED"); 10863} 10864 10865 10866THREADED_TEST(RoundRobinGetFromCache) { 10867 i::FLAG_allow_natives_syntax = true; 10868 v8::HandleScope scope; 10869 10870 LocalContext context; 10871 10872 const char* code = 10873 "(function() {" 10874 " var keys = [];" 10875 " for (var i = 0; i < 16; i++) keys.push(i);" 10876 " var values = [];" 10877 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" 10878 " for (var i = 0; i < 16; i++) {" 10879 " var v = %_GetFromCache(0, keys[i]);" 10880 " if (v !== values[i])" 10881 " return 'Wrong value for ' + " 10882 " keys[i] + ': ' + v + ' vs. ' + values[i];" 10883 " };" 10884 " return 'PASSED';" 10885 "})()"; 10886 v8::internal::Heap::ClearJSFunctionResultCaches(); 10887 ExpectString(code, "PASSED"); 10888} 10889 10890 10891THREADED_TEST(ReverseGetFromCache) { 10892 i::FLAG_allow_natives_syntax = true; 10893 v8::HandleScope scope; 10894 10895 LocalContext context; 10896 10897 const char* code = 10898 "(function() {" 10899 " var keys = [];" 10900 " for (var i = 0; i < 16; i++) keys.push(i);" 10901 " var values = [];" 10902 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" 10903 " for (var i = 15; i >= 16; i--) {" 10904 " var v = %_GetFromCache(0, keys[i]);" 10905 " if (v !== values[i])" 10906 " return 'Wrong value for ' + " 10907 " keys[i] + ': ' + v + ' vs. ' + values[i];" 10908 " };" 10909 " return 'PASSED';" 10910 "})()"; 10911 v8::internal::Heap::ClearJSFunctionResultCaches(); 10912 ExpectString(code, "PASSED"); 10913} 10914 10915 10916THREADED_TEST(TestEviction) { 10917 i::FLAG_allow_natives_syntax = true; 10918 v8::HandleScope scope; 10919 10920 LocalContext context; 10921 10922 const char* code = 10923 "(function() {" 10924 " for (var i = 0; i < 2*16; i++) {" 10925 " %_GetFromCache(0, 'a' + i);" 10926 " };" 10927 " return 'PASSED';" 10928 "})()"; 10929 v8::internal::Heap::ClearJSFunctionResultCaches(); 10930 ExpectString(code, "PASSED"); 10931} 10932