test-api.cc revision f7060e27768c550ace7ec48ad8c093466db52dfa
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 613THREADED_TEST(StringConcat) { 614 { 615 v8::HandleScope scope; 616 LocalContext env; 617 const char* one_byte_string_1 = "function a_times_t"; 618 const char* two_byte_string_1 = "wo_plus_b(a, b) {return "; 619 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + "; 620 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + "; 621 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 622 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 623 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);"; 624 Local<String> left = v8_str(one_byte_string_1); 625 Local<String> right = String::New(AsciiToTwoByteString(two_byte_string_1)); 626 Local<String> source = String::Concat(left, right); 627 right = String::NewExternal( 628 new TestAsciiResource(i::StrDup(one_byte_extern_1))); 629 source = String::Concat(source, right); 630 right = String::NewExternal( 631 new TestResource(AsciiToTwoByteString(two_byte_extern_1))); 632 source = String::Concat(source, right); 633 right = v8_str(one_byte_string_2); 634 source = String::Concat(source, right); 635 right = String::New(AsciiToTwoByteString(two_byte_string_2)); 636 source = String::Concat(source, right); 637 right = String::NewExternal( 638 new TestResource(AsciiToTwoByteString(two_byte_extern_2))); 639 source = String::Concat(source, right); 640 Local<Script> script = Script::Compile(source); 641 Local<Value> value = script->Run(); 642 CHECK(value->IsNumber()); 643 CHECK_EQ(68, value->Int32Value()); 644 } 645 v8::internal::CompilationCache::Clear(); 646 i::Heap::CollectAllGarbage(false); 647 i::Heap::CollectAllGarbage(false); 648} 649 650 651THREADED_TEST(GlobalProperties) { 652 v8::HandleScope scope; 653 LocalContext env; 654 v8::Handle<v8::Object> global = env->Global(); 655 global->Set(v8_str("pi"), v8_num(3.1415926)); 656 Local<Value> pi = global->Get(v8_str("pi")); 657 CHECK_EQ(3.1415926, pi->NumberValue()); 658} 659 660 661static v8::Handle<Value> handle_call(const v8::Arguments& args) { 662 ApiTestFuzzer::Fuzz(); 663 return v8_num(102); 664} 665 666 667static v8::Handle<Value> construct_call(const v8::Arguments& args) { 668 ApiTestFuzzer::Fuzz(); 669 args.This()->Set(v8_str("x"), v8_num(1)); 670 args.This()->Set(v8_str("y"), v8_num(2)); 671 return args.This(); 672} 673 674THREADED_TEST(FunctionTemplate) { 675 v8::HandleScope scope; 676 LocalContext env; 677 { 678 Local<v8::FunctionTemplate> fun_templ = 679 v8::FunctionTemplate::New(handle_call); 680 Local<Function> fun = fun_templ->GetFunction(); 681 env->Global()->Set(v8_str("obj"), fun); 682 Local<Script> script = v8_compile("obj()"); 683 CHECK_EQ(102, script->Run()->Int32Value()); 684 } 685 // Use SetCallHandler to initialize a function template, should work like the 686 // previous one. 687 { 688 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 689 fun_templ->SetCallHandler(handle_call); 690 Local<Function> fun = fun_templ->GetFunction(); 691 env->Global()->Set(v8_str("obj"), fun); 692 Local<Script> script = v8_compile("obj()"); 693 CHECK_EQ(102, script->Run()->Int32Value()); 694 } 695 // Test constructor calls. 696 { 697 Local<v8::FunctionTemplate> fun_templ = 698 v8::FunctionTemplate::New(construct_call); 699 fun_templ->SetClassName(v8_str("funky")); 700 Local<Function> fun = fun_templ->GetFunction(); 701 env->Global()->Set(v8_str("obj"), fun); 702 Local<Script> script = v8_compile("var s = new obj(); s.x"); 703 CHECK_EQ(1, script->Run()->Int32Value()); 704 705 Local<Value> result = v8_compile("(new obj()).toString()")->Run(); 706 CHECK_EQ(v8_str("[object funky]"), result); 707 } 708} 709 710 711THREADED_TEST(FindInstanceInPrototypeChain) { 712 v8::HandleScope scope; 713 LocalContext env; 714 715 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New(); 716 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New(); 717 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New(); 718 derived->Inherit(base); 719 720 Local<v8::Function> base_function = base->GetFunction(); 721 Local<v8::Function> derived_function = derived->GetFunction(); 722 Local<v8::Function> other_function = other->GetFunction(); 723 724 Local<v8::Object> base_instance = base_function->NewInstance(); 725 Local<v8::Object> derived_instance = derived_function->NewInstance(); 726 Local<v8::Object> derived_instance2 = derived_function->NewInstance(); 727 Local<v8::Object> other_instance = other_function->NewInstance(); 728 derived_instance2->Set(v8_str("__proto__"), derived_instance); 729 other_instance->Set(v8_str("__proto__"), derived_instance2); 730 731 // base_instance is only an instance of base. 732 CHECK_EQ(base_instance, 733 base_instance->FindInstanceInPrototypeChain(base)); 734 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty()); 735 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 736 737 // derived_instance is an instance of base and derived. 738 CHECK_EQ(derived_instance, 739 derived_instance->FindInstanceInPrototypeChain(base)); 740 CHECK_EQ(derived_instance, 741 derived_instance->FindInstanceInPrototypeChain(derived)); 742 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 743 744 // other_instance is an instance of other and its immediate 745 // prototype derived_instance2 is an instance of base and derived. 746 // Note, derived_instance is an instance of base and derived too, 747 // but it comes after derived_instance2 in the prototype chain of 748 // other_instance. 749 CHECK_EQ(derived_instance2, 750 other_instance->FindInstanceInPrototypeChain(base)); 751 CHECK_EQ(derived_instance2, 752 other_instance->FindInstanceInPrototypeChain(derived)); 753 CHECK_EQ(other_instance, 754 other_instance->FindInstanceInPrototypeChain(other)); 755} 756 757 758THREADED_TEST(TinyInteger) { 759 v8::HandleScope scope; 760 LocalContext env; 761 int32_t value = 239; 762 Local<v8::Integer> value_obj = v8::Integer::New(value); 763 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 764} 765 766 767THREADED_TEST(BigSmiInteger) { 768 v8::HandleScope scope; 769 LocalContext env; 770 int32_t value = i::Smi::kMaxValue; 771 // We cannot add one to a Smi::kMaxValue without wrapping. 772 if (i::kSmiValueSize < 32) { 773 CHECK(i::Smi::IsValid(value)); 774 CHECK(!i::Smi::IsValid(value + 1)); 775 Local<v8::Integer> value_obj = v8::Integer::New(value); 776 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 777 } 778} 779 780 781THREADED_TEST(BigInteger) { 782 v8::HandleScope scope; 783 LocalContext env; 784 // We cannot add one to a Smi::kMaxValue without wrapping. 785 if (i::kSmiValueSize < 32) { 786 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1. 787 // The code will not be run in that case, due to the "if" guard. 788 int32_t value = 789 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1); 790 CHECK(value > i::Smi::kMaxValue); 791 CHECK(!i::Smi::IsValid(value)); 792 Local<v8::Integer> value_obj = v8::Integer::New(value); 793 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 794 } 795} 796 797 798THREADED_TEST(TinyUnsignedInteger) { 799 v8::HandleScope scope; 800 LocalContext env; 801 uint32_t value = 239; 802 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 803 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 804} 805 806 807THREADED_TEST(BigUnsignedSmiInteger) { 808 v8::HandleScope scope; 809 LocalContext env; 810 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue); 811 CHECK(i::Smi::IsValid(value)); 812 CHECK(!i::Smi::IsValid(value + 1)); 813 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 814 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 815} 816 817 818THREADED_TEST(BigUnsignedInteger) { 819 v8::HandleScope scope; 820 LocalContext env; 821 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1; 822 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue)); 823 CHECK(!i::Smi::IsValid(value)); 824 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 825 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 826} 827 828 829THREADED_TEST(OutOfSignedRangeUnsignedInteger) { 830 v8::HandleScope scope; 831 LocalContext env; 832 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1; 833 uint32_t value = INT32_MAX_AS_UINT + 1; 834 CHECK(value > INT32_MAX_AS_UINT); // No overflow. 835 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 836 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 837} 838 839 840THREADED_TEST(Number) { 841 v8::HandleScope scope; 842 LocalContext env; 843 double PI = 3.1415926; 844 Local<v8::Number> pi_obj = v8::Number::New(PI); 845 CHECK_EQ(PI, pi_obj->NumberValue()); 846} 847 848 849THREADED_TEST(ToNumber) { 850 v8::HandleScope scope; 851 LocalContext env; 852 Local<String> str = v8_str("3.1415926"); 853 CHECK_EQ(3.1415926, str->NumberValue()); 854 v8::Handle<v8::Boolean> t = v8::True(); 855 CHECK_EQ(1.0, t->NumberValue()); 856 v8::Handle<v8::Boolean> f = v8::False(); 857 CHECK_EQ(0.0, f->NumberValue()); 858} 859 860 861THREADED_TEST(Date) { 862 v8::HandleScope scope; 863 LocalContext env; 864 double PI = 3.1415926; 865 Local<Value> date_obj = v8::Date::New(PI); 866 CHECK_EQ(3.0, date_obj->NumberValue()); 867} 868 869 870THREADED_TEST(Boolean) { 871 v8::HandleScope scope; 872 LocalContext env; 873 v8::Handle<v8::Boolean> t = v8::True(); 874 CHECK(t->Value()); 875 v8::Handle<v8::Boolean> f = v8::False(); 876 CHECK(!f->Value()); 877 v8::Handle<v8::Primitive> u = v8::Undefined(); 878 CHECK(!u->BooleanValue()); 879 v8::Handle<v8::Primitive> n = v8::Null(); 880 CHECK(!n->BooleanValue()); 881 v8::Handle<String> str1 = v8_str(""); 882 CHECK(!str1->BooleanValue()); 883 v8::Handle<String> str2 = v8_str("x"); 884 CHECK(str2->BooleanValue()); 885 CHECK(!v8::Number::New(0)->BooleanValue()); 886 CHECK(v8::Number::New(-1)->BooleanValue()); 887 CHECK(v8::Number::New(1)->BooleanValue()); 888 CHECK(v8::Number::New(42)->BooleanValue()); 889 CHECK(!v8_compile("NaN")->Run()->BooleanValue()); 890} 891 892 893static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) { 894 ApiTestFuzzer::Fuzz(); 895 return v8_num(13.4); 896} 897 898 899static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) { 900 ApiTestFuzzer::Fuzz(); 901 return v8_num(876); 902} 903 904 905THREADED_TEST(GlobalPrototype) { 906 v8::HandleScope scope; 907 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 908 func_templ->PrototypeTemplate()->Set( 909 "dummy", 910 v8::FunctionTemplate::New(DummyCallHandler)); 911 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate(); 912 templ->Set("x", v8_num(200)); 913 templ->SetAccessor(v8_str("m"), GetM); 914 LocalContext env(0, templ); 915 v8::Handle<v8::Object> obj = env->Global(); 916 v8::Handle<Script> script = v8_compile("dummy()"); 917 v8::Handle<Value> result = script->Run(); 918 CHECK_EQ(13.4, result->NumberValue()); 919 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value()); 920 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value()); 921} 922 923 924THREADED_TEST(ObjectTemplate) { 925 v8::HandleScope scope; 926 Local<ObjectTemplate> templ1 = ObjectTemplate::New(); 927 templ1->Set("x", v8_num(10)); 928 templ1->Set("y", v8_num(13)); 929 LocalContext env; 930 Local<v8::Object> instance1 = templ1->NewInstance(); 931 env->Global()->Set(v8_str("p"), instance1); 932 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue()); 933 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue()); 934 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); 935 fun->PrototypeTemplate()->Set("nirk", v8_num(123)); 936 Local<ObjectTemplate> templ2 = fun->InstanceTemplate(); 937 templ2->Set("a", v8_num(12)); 938 templ2->Set("b", templ1); 939 Local<v8::Object> instance2 = templ2->NewInstance(); 940 env->Global()->Set(v8_str("q"), instance2); 941 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue()); 942 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue()); 943 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue()); 944 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue()); 945} 946 947 948static v8::Handle<Value> GetFlabby(const v8::Arguments& args) { 949 ApiTestFuzzer::Fuzz(); 950 return v8_num(17.2); 951} 952 953 954static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) { 955 ApiTestFuzzer::Fuzz(); 956 return v8_num(15.2); 957} 958 959 960THREADED_TEST(DescriptorInheritance) { 961 v8::HandleScope scope; 962 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New(); 963 super->PrototypeTemplate()->Set("flabby", 964 v8::FunctionTemplate::New(GetFlabby)); 965 super->PrototypeTemplate()->Set("PI", v8_num(3.14)); 966 967 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd); 968 969 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New(); 970 base1->Inherit(super); 971 base1->PrototypeTemplate()->Set("v1", v8_num(20.1)); 972 973 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New(); 974 base2->Inherit(super); 975 base2->PrototypeTemplate()->Set("v2", v8_num(10.1)); 976 977 LocalContext env; 978 979 env->Global()->Set(v8_str("s"), super->GetFunction()); 980 env->Global()->Set(v8_str("base1"), base1->GetFunction()); 981 env->Global()->Set(v8_str("base2"), base2->GetFunction()); 982 983 // Checks right __proto__ chain. 984 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue()); 985 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue()); 986 987 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue()); 988 989 // Instance accessor should not be visible on function object or its prototype 990 CHECK(CompileRun("s.knurd == undefined")->BooleanValue()); 991 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue()); 992 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue()); 993 994 env->Global()->Set(v8_str("obj"), 995 base1->GetFunction()->NewInstance()); 996 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue()); 997 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue()); 998 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue()); 999 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue()); 1000 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue()); 1001 1002 env->Global()->Set(v8_str("obj2"), 1003 base2->GetFunction()->NewInstance()); 1004 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue()); 1005 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue()); 1006 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue()); 1007 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue()); 1008 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue()); 1009 1010 // base1 and base2 cannot cross reference to each's prototype 1011 CHECK(v8_compile("obj.v2")->Run()->IsUndefined()); 1012 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined()); 1013} 1014 1015 1016int echo_named_call_count; 1017 1018 1019static v8::Handle<Value> EchoNamedProperty(Local<String> name, 1020 const AccessorInfo& info) { 1021 ApiTestFuzzer::Fuzz(); 1022 CHECK_EQ(v8_str("data"), info.Data()); 1023 echo_named_call_count++; 1024 return name; 1025} 1026 1027 1028THREADED_TEST(NamedPropertyHandlerGetter) { 1029 echo_named_call_count = 0; 1030 v8::HandleScope scope; 1031 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1032 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty, 1033 0, 0, 0, 0, 1034 v8_str("data")); 1035 LocalContext env; 1036 env->Global()->Set(v8_str("obj"), 1037 templ->GetFunction()->NewInstance()); 1038 CHECK_EQ(echo_named_call_count, 0); 1039 v8_compile("obj.x")->Run(); 1040 CHECK_EQ(echo_named_call_count, 1); 1041 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;"; 1042 v8::Handle<Value> str = CompileRun(code); 1043 String::AsciiValue value(str); 1044 CHECK_EQ(*value, "oddlepoddle"); 1045 // Check default behavior 1046 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10); 1047 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue()); 1048 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue()); 1049} 1050 1051 1052int echo_indexed_call_count = 0; 1053 1054 1055static v8::Handle<Value> EchoIndexedProperty(uint32_t index, 1056 const AccessorInfo& info) { 1057 ApiTestFuzzer::Fuzz(); 1058 CHECK_EQ(v8_num(637), info.Data()); 1059 echo_indexed_call_count++; 1060 return v8_num(index); 1061} 1062 1063 1064THREADED_TEST(IndexedPropertyHandlerGetter) { 1065 v8::HandleScope scope; 1066 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1067 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty, 1068 0, 0, 0, 0, 1069 v8_num(637)); 1070 LocalContext env; 1071 env->Global()->Set(v8_str("obj"), 1072 templ->GetFunction()->NewInstance()); 1073 Local<Script> script = v8_compile("obj[900]"); 1074 CHECK_EQ(script->Run()->Int32Value(), 900); 1075} 1076 1077 1078v8::Handle<v8::Object> bottom; 1079 1080static v8::Handle<Value> CheckThisIndexedPropertyHandler( 1081 uint32_t index, 1082 const AccessorInfo& info) { 1083 ApiTestFuzzer::Fuzz(); 1084 CHECK(info.This()->Equals(bottom)); 1085 return v8::Handle<Value>(); 1086} 1087 1088static v8::Handle<Value> CheckThisNamedPropertyHandler( 1089 Local<String> name, 1090 const AccessorInfo& info) { 1091 ApiTestFuzzer::Fuzz(); 1092 CHECK(info.This()->Equals(bottom)); 1093 return v8::Handle<Value>(); 1094} 1095 1096 1097v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index, 1098 Local<Value> value, 1099 const AccessorInfo& info) { 1100 ApiTestFuzzer::Fuzz(); 1101 CHECK(info.This()->Equals(bottom)); 1102 return v8::Handle<Value>(); 1103} 1104 1105 1106v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property, 1107 Local<Value> value, 1108 const AccessorInfo& info) { 1109 ApiTestFuzzer::Fuzz(); 1110 CHECK(info.This()->Equals(bottom)); 1111 return v8::Handle<Value>(); 1112} 1113 1114v8::Handle<v8::Boolean> CheckThisIndexedPropertyQuery( 1115 uint32_t index, 1116 const AccessorInfo& info) { 1117 ApiTestFuzzer::Fuzz(); 1118 CHECK(info.This()->Equals(bottom)); 1119 return v8::Handle<v8::Boolean>(); 1120} 1121 1122 1123v8::Handle<v8::Boolean> CheckThisNamedPropertyQuery(Local<String> property, 1124 const AccessorInfo& info) { 1125 ApiTestFuzzer::Fuzz(); 1126 CHECK(info.This()->Equals(bottom)); 1127 return v8::Handle<v8::Boolean>(); 1128} 1129 1130 1131v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter( 1132 uint32_t index, 1133 const AccessorInfo& info) { 1134 ApiTestFuzzer::Fuzz(); 1135 CHECK(info.This()->Equals(bottom)); 1136 return v8::Handle<v8::Boolean>(); 1137} 1138 1139 1140v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter( 1141 Local<String> property, 1142 const AccessorInfo& info) { 1143 ApiTestFuzzer::Fuzz(); 1144 CHECK(info.This()->Equals(bottom)); 1145 return v8::Handle<v8::Boolean>(); 1146} 1147 1148 1149v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator( 1150 const AccessorInfo& info) { 1151 ApiTestFuzzer::Fuzz(); 1152 CHECK(info.This()->Equals(bottom)); 1153 return v8::Handle<v8::Array>(); 1154} 1155 1156 1157v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator( 1158 const AccessorInfo& info) { 1159 ApiTestFuzzer::Fuzz(); 1160 CHECK(info.This()->Equals(bottom)); 1161 return v8::Handle<v8::Array>(); 1162} 1163 1164 1165THREADED_TEST(PropertyHandlerInPrototype) { 1166 v8::HandleScope scope; 1167 LocalContext env; 1168 1169 // Set up a prototype chain with three interceptors. 1170 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1171 templ->InstanceTemplate()->SetIndexedPropertyHandler( 1172 CheckThisIndexedPropertyHandler, 1173 CheckThisIndexedPropertySetter, 1174 CheckThisIndexedPropertyQuery, 1175 CheckThisIndexedPropertyDeleter, 1176 CheckThisIndexedPropertyEnumerator); 1177 1178 templ->InstanceTemplate()->SetNamedPropertyHandler( 1179 CheckThisNamedPropertyHandler, 1180 CheckThisNamedPropertySetter, 1181 CheckThisNamedPropertyQuery, 1182 CheckThisNamedPropertyDeleter, 1183 CheckThisNamedPropertyEnumerator); 1184 1185 bottom = templ->GetFunction()->NewInstance(); 1186 Local<v8::Object> top = templ->GetFunction()->NewInstance(); 1187 Local<v8::Object> middle = templ->GetFunction()->NewInstance(); 1188 1189 bottom->Set(v8_str("__proto__"), middle); 1190 middle->Set(v8_str("__proto__"), top); 1191 env->Global()->Set(v8_str("obj"), bottom); 1192 1193 // Indexed and named get. 1194 Script::Compile(v8_str("obj[0]"))->Run(); 1195 Script::Compile(v8_str("obj.x"))->Run(); 1196 1197 // Indexed and named set. 1198 Script::Compile(v8_str("obj[1] = 42"))->Run(); 1199 Script::Compile(v8_str("obj.y = 42"))->Run(); 1200 1201 // Indexed and named query. 1202 Script::Compile(v8_str("0 in obj"))->Run(); 1203 Script::Compile(v8_str("'x' in obj"))->Run(); 1204 1205 // Indexed and named deleter. 1206 Script::Compile(v8_str("delete obj[0]"))->Run(); 1207 Script::Compile(v8_str("delete obj.x"))->Run(); 1208 1209 // Enumerators. 1210 Script::Compile(v8_str("for (var p in obj) ;"))->Run(); 1211} 1212 1213 1214static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key, 1215 const AccessorInfo& info) { 1216 ApiTestFuzzer::Fuzz(); 1217 if (v8_str("pre")->Equals(key)) { 1218 return v8_str("PrePropertyHandler: pre"); 1219 } 1220 return v8::Handle<String>(); 1221} 1222 1223 1224static v8::Handle<v8::Boolean> PrePropertyHandlerHas(Local<String> key, 1225 const AccessorInfo&) { 1226 if (v8_str("pre")->Equals(key)) { 1227 return v8::True(); 1228 } 1229 1230 return v8::Handle<v8::Boolean>(); // do not intercept the call 1231} 1232 1233 1234THREADED_TEST(PrePropertyHandler) { 1235 v8::HandleScope scope; 1236 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(); 1237 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet, 1238 0, 1239 PrePropertyHandlerHas); 1240 LocalContext env(NULL, desc->InstanceTemplate()); 1241 Script::Compile(v8_str( 1242 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run(); 1243 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run(); 1244 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre); 1245 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run(); 1246 CHECK_EQ(v8_str("Object: on"), result_on); 1247 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run(); 1248 CHECK(result_post.IsEmpty()); 1249} 1250 1251 1252THREADED_TEST(UndefinedIsNotEnumerable) { 1253 v8::HandleScope scope; 1254 LocalContext env; 1255 v8::Handle<Value> result = Script::Compile(v8_str( 1256 "this.propertyIsEnumerable(undefined)"))->Run(); 1257 CHECK(result->IsFalse()); 1258} 1259 1260 1261v8::Handle<Script> call_recursively_script; 1262static const int kTargetRecursionDepth = 200; // near maximum 1263 1264 1265static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) { 1266 ApiTestFuzzer::Fuzz(); 1267 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1268 if (depth == kTargetRecursionDepth) return v8::Undefined(); 1269 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1270 return call_recursively_script->Run(); 1271} 1272 1273 1274static v8::Handle<Value> CallFunctionRecursivelyCall( 1275 const v8::Arguments& args) { 1276 ApiTestFuzzer::Fuzz(); 1277 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1278 if (depth == kTargetRecursionDepth) { 1279 printf("[depth = %d]\n", depth); 1280 return v8::Undefined(); 1281 } 1282 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1283 v8::Handle<Value> function = 1284 args.This()->Get(v8_str("callFunctionRecursively")); 1285 return function.As<Function>()->Call(args.This(), 0, NULL); 1286} 1287 1288 1289THREADED_TEST(DeepCrossLanguageRecursion) { 1290 v8::HandleScope scope; 1291 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 1292 global->Set(v8_str("callScriptRecursively"), 1293 v8::FunctionTemplate::New(CallScriptRecursivelyCall)); 1294 global->Set(v8_str("callFunctionRecursively"), 1295 v8::FunctionTemplate::New(CallFunctionRecursivelyCall)); 1296 LocalContext env(NULL, global); 1297 1298 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1299 call_recursively_script = v8_compile("callScriptRecursively()"); 1300 v8::Handle<Value> result = call_recursively_script->Run(); 1301 call_recursively_script = v8::Handle<Script>(); 1302 1303 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1304 Script::Compile(v8_str("callFunctionRecursively()"))->Run(); 1305} 1306 1307 1308static v8::Handle<Value> 1309 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) { 1310 ApiTestFuzzer::Fuzz(); 1311 return v8::ThrowException(key); 1312} 1313 1314 1315static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key, 1316 Local<Value>, 1317 const AccessorInfo&) { 1318 v8::ThrowException(key); 1319 return v8::Undefined(); // not the same as v8::Handle<v8::Value>() 1320} 1321 1322 1323THREADED_TEST(CallbackExceptionRegression) { 1324 v8::HandleScope scope; 1325 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 1326 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet, 1327 ThrowingPropertyHandlerSet); 1328 LocalContext env; 1329 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 1330 v8::Handle<Value> otto = Script::Compile(v8_str( 1331 "try { with (obj) { otto; } } catch (e) { e; }"))->Run(); 1332 CHECK_EQ(v8_str("otto"), otto); 1333 v8::Handle<Value> netto = Script::Compile(v8_str( 1334 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run(); 1335 CHECK_EQ(v8_str("netto"), netto); 1336} 1337 1338 1339THREADED_TEST(FunctionPrototype) { 1340 v8::HandleScope scope; 1341 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New(); 1342 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321)); 1343 LocalContext env; 1344 env->Global()->Set(v8_str("Foo"), Foo->GetFunction()); 1345 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak")); 1346 CHECK_EQ(script->Run()->Int32Value(), 321); 1347} 1348 1349 1350THREADED_TEST(InternalFields) { 1351 v8::HandleScope scope; 1352 LocalContext env; 1353 1354 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1355 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1356 instance_templ->SetInternalFieldCount(1); 1357 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1358 CHECK_EQ(1, obj->InternalFieldCount()); 1359 CHECK(obj->GetInternalField(0)->IsUndefined()); 1360 obj->SetInternalField(0, v8_num(17)); 1361 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value()); 1362} 1363 1364 1365THREADED_TEST(GlobalObjectInternalFields) { 1366 v8::HandleScope scope; 1367 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 1368 global_template->SetInternalFieldCount(1); 1369 LocalContext env(NULL, global_template); 1370 v8::Handle<v8::Object> global_proxy = env->Global(); 1371 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); 1372 CHECK_EQ(1, global->InternalFieldCount()); 1373 CHECK(global->GetInternalField(0)->IsUndefined()); 1374 global->SetInternalField(0, v8_num(17)); 1375 CHECK_EQ(17, global->GetInternalField(0)->Int32Value()); 1376} 1377 1378 1379THREADED_TEST(InternalFieldsNativePointers) { 1380 v8::HandleScope scope; 1381 LocalContext env; 1382 1383 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1384 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1385 instance_templ->SetInternalFieldCount(1); 1386 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1387 CHECK_EQ(1, obj->InternalFieldCount()); 1388 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1389 1390 char* data = new char[100]; 1391 1392 void* aligned = data; 1393 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1); 1394 void* unaligned = data + 1; 1395 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1); 1396 1397 // Check reading and writing aligned pointers. 1398 obj->SetPointerInInternalField(0, aligned); 1399 i::Heap::CollectAllGarbage(false); 1400 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1401 1402 // Check reading and writing unaligned pointers. 1403 obj->SetPointerInInternalField(0, unaligned); 1404 i::Heap::CollectAllGarbage(false); 1405 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1406 1407 delete[] data; 1408} 1409 1410 1411THREADED_TEST(InternalFieldsNativePointersAndExternal) { 1412 v8::HandleScope scope; 1413 LocalContext env; 1414 1415 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1416 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1417 instance_templ->SetInternalFieldCount(1); 1418 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1419 CHECK_EQ(1, obj->InternalFieldCount()); 1420 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1421 1422 char* data = new char[100]; 1423 1424 void* aligned = data; 1425 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1); 1426 void* unaligned = data + 1; 1427 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1); 1428 1429 obj->SetPointerInInternalField(0, aligned); 1430 i::Heap::CollectAllGarbage(false); 1431 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0))); 1432 1433 obj->SetPointerInInternalField(0, unaligned); 1434 i::Heap::CollectAllGarbage(false); 1435 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0))); 1436 1437 obj->SetInternalField(0, v8::External::Wrap(aligned)); 1438 i::Heap::CollectAllGarbage(false); 1439 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1440 1441 obj->SetInternalField(0, v8::External::Wrap(unaligned)); 1442 i::Heap::CollectAllGarbage(false); 1443 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1444 1445 delete[] data; 1446} 1447 1448 1449THREADED_TEST(IdentityHash) { 1450 v8::HandleScope scope; 1451 LocalContext env; 1452 1453 // Ensure that the test starts with an fresh heap to test whether the hash 1454 // code is based on the address. 1455 i::Heap::CollectAllGarbage(false); 1456 Local<v8::Object> obj = v8::Object::New(); 1457 int hash = obj->GetIdentityHash(); 1458 int hash1 = obj->GetIdentityHash(); 1459 CHECK_EQ(hash, hash1); 1460 int hash2 = v8::Object::New()->GetIdentityHash(); 1461 // Since the identity hash is essentially a random number two consecutive 1462 // objects should not be assigned the same hash code. If the test below fails 1463 // the random number generator should be evaluated. 1464 CHECK_NE(hash, hash2); 1465 i::Heap::CollectAllGarbage(false); 1466 int hash3 = v8::Object::New()->GetIdentityHash(); 1467 // Make sure that the identity hash is not based on the initial address of 1468 // the object alone. If the test below fails the random number generator 1469 // should be evaluated. 1470 CHECK_NE(hash, hash3); 1471 int hash4 = obj->GetIdentityHash(); 1472 CHECK_EQ(hash, hash4); 1473} 1474 1475 1476THREADED_TEST(HiddenProperties) { 1477 v8::HandleScope scope; 1478 LocalContext env; 1479 1480 v8::Local<v8::Object> obj = v8::Object::New(); 1481 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 1482 v8::Local<v8::String> empty = v8_str(""); 1483 v8::Local<v8::String> prop_name = v8_str("prop_name"); 1484 1485 i::Heap::CollectAllGarbage(false); 1486 1487 // Make sure delete of a non-existent hidden value works 1488 CHECK(obj->DeleteHiddenValue(key)); 1489 1490 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503))); 1491 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value()); 1492 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002))); 1493 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1494 1495 i::Heap::CollectAllGarbage(false); 1496 1497 // Make sure we do not find the hidden property. 1498 CHECK(!obj->Has(empty)); 1499 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1500 CHECK(obj->Get(empty)->IsUndefined()); 1501 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1502 CHECK(obj->Set(empty, v8::Integer::New(2003))); 1503 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1504 CHECK_EQ(2003, obj->Get(empty)->Int32Value()); 1505 1506 i::Heap::CollectAllGarbage(false); 1507 1508 // Add another property and delete it afterwards to force the object in 1509 // slow case. 1510 CHECK(obj->Set(prop_name, v8::Integer::New(2008))); 1511 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1512 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value()); 1513 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1514 CHECK(obj->Delete(prop_name)); 1515 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1516 1517 i::Heap::CollectAllGarbage(false); 1518 1519 CHECK(obj->DeleteHiddenValue(key)); 1520 CHECK(obj->GetHiddenValue(key).IsEmpty()); 1521} 1522 1523 1524static bool interceptor_for_hidden_properties_called; 1525static v8::Handle<Value> InterceptorForHiddenProperties( 1526 Local<String> name, const AccessorInfo& info) { 1527 interceptor_for_hidden_properties_called = true; 1528 return v8::Handle<Value>(); 1529} 1530 1531 1532THREADED_TEST(HiddenPropertiesWithInterceptors) { 1533 v8::HandleScope scope; 1534 LocalContext context; 1535 1536 interceptor_for_hidden_properties_called = false; 1537 1538 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 1539 1540 // Associate an interceptor with an object and start setting hidden values. 1541 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 1542 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 1543 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties); 1544 Local<v8::Function> function = fun_templ->GetFunction(); 1545 Local<v8::Object> obj = function->NewInstance(); 1546 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302))); 1547 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value()); 1548 CHECK(!interceptor_for_hidden_properties_called); 1549} 1550 1551 1552THREADED_TEST(External) { 1553 v8::HandleScope scope; 1554 int x = 3; 1555 Local<v8::External> ext = v8::External::New(&x); 1556 LocalContext env; 1557 env->Global()->Set(v8_str("ext"), ext); 1558 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run(); 1559 v8::Handle<v8::External> reext = reext_obj.As<v8::External>(); 1560 int* ptr = static_cast<int*>(reext->Value()); 1561 CHECK_EQ(x, 3); 1562 *ptr = 10; 1563 CHECK_EQ(x, 10); 1564 1565 // Make sure unaligned pointers are wrapped properly. 1566 char* data = i::StrDup("0123456789"); 1567 Local<v8::Value> zero = v8::External::Wrap(&data[0]); 1568 Local<v8::Value> one = v8::External::Wrap(&data[1]); 1569 Local<v8::Value> two = v8::External::Wrap(&data[2]); 1570 Local<v8::Value> three = v8::External::Wrap(&data[3]); 1571 1572 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero)); 1573 CHECK_EQ('0', *char_ptr); 1574 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one)); 1575 CHECK_EQ('1', *char_ptr); 1576 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two)); 1577 CHECK_EQ('2', *char_ptr); 1578 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three)); 1579 CHECK_EQ('3', *char_ptr); 1580 i::DeleteArray(data); 1581} 1582 1583 1584THREADED_TEST(GlobalHandle) { 1585 v8::Persistent<String> global; 1586 { 1587 v8::HandleScope scope; 1588 Local<String> str = v8_str("str"); 1589 global = v8::Persistent<String>::New(str); 1590 } 1591 CHECK_EQ(global->Length(), 3); 1592 global.Dispose(); 1593} 1594 1595 1596THREADED_TEST(ScriptException) { 1597 v8::HandleScope scope; 1598 LocalContext env; 1599 Local<Script> script = Script::Compile(v8_str("throw 'panama!';")); 1600 v8::TryCatch try_catch; 1601 Local<Value> result = script->Run(); 1602 CHECK(result.IsEmpty()); 1603 CHECK(try_catch.HasCaught()); 1604 String::AsciiValue exception_value(try_catch.Exception()); 1605 CHECK_EQ(*exception_value, "panama!"); 1606} 1607 1608 1609bool message_received; 1610 1611 1612static void check_message(v8::Handle<v8::Message> message, 1613 v8::Handle<Value> data) { 1614 CHECK_EQ(5.76, data->NumberValue()); 1615 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue()); 1616 CHECK_EQ(7.56, message->GetScriptData()->NumberValue()); 1617 message_received = true; 1618} 1619 1620 1621THREADED_TEST(MessageHandlerData) { 1622 message_received = false; 1623 v8::HandleScope scope; 1624 CHECK(!message_received); 1625 v8::V8::AddMessageListener(check_message, v8_num(5.76)); 1626 LocalContext context; 1627 v8::ScriptOrigin origin = 1628 v8::ScriptOrigin(v8_str("6.75")); 1629 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"), 1630 &origin); 1631 script->SetData(v8_str("7.56")); 1632 script->Run(); 1633 CHECK(message_received); 1634 // clear out the message listener 1635 v8::V8::RemoveMessageListeners(check_message); 1636} 1637 1638 1639THREADED_TEST(GetSetProperty) { 1640 v8::HandleScope scope; 1641 LocalContext context; 1642 context->Global()->Set(v8_str("foo"), v8_num(14)); 1643 context->Global()->Set(v8_str("12"), v8_num(92)); 1644 context->Global()->Set(v8::Integer::New(16), v8_num(32)); 1645 context->Global()->Set(v8_num(13), v8_num(56)); 1646 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run(); 1647 CHECK_EQ(14, foo->Int32Value()); 1648 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run(); 1649 CHECK_EQ(92, twelve->Int32Value()); 1650 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run(); 1651 CHECK_EQ(32, sixteen->Int32Value()); 1652 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run(); 1653 CHECK_EQ(56, thirteen->Int32Value()); 1654 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value()); 1655 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value()); 1656 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value()); 1657 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value()); 1658 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value()); 1659 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value()); 1660 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value()); 1661 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value()); 1662 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value()); 1663} 1664 1665 1666THREADED_TEST(PropertyAttributes) { 1667 v8::HandleScope scope; 1668 LocalContext context; 1669 // read-only 1670 Local<String> prop = v8_str("read_only"); 1671 context->Global()->Set(prop, v8_num(7), v8::ReadOnly); 1672 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1673 Script::Compile(v8_str("read_only = 9"))->Run(); 1674 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1675 context->Global()->Set(prop, v8_num(10)); 1676 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1677 // dont-delete 1678 prop = v8_str("dont_delete"); 1679 context->Global()->Set(prop, v8_num(13), v8::DontDelete); 1680 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 1681 Script::Compile(v8_str("delete dont_delete"))->Run(); 1682 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 1683} 1684 1685 1686THREADED_TEST(Array) { 1687 v8::HandleScope scope; 1688 LocalContext context; 1689 Local<v8::Array> array = v8::Array::New(); 1690 CHECK_EQ(0, array->Length()); 1691 CHECK(array->Get(0)->IsUndefined()); 1692 CHECK(!array->Has(0)); 1693 CHECK(array->Get(100)->IsUndefined()); 1694 CHECK(!array->Has(100)); 1695 array->Set(2, v8_num(7)); 1696 CHECK_EQ(3, array->Length()); 1697 CHECK(!array->Has(0)); 1698 CHECK(!array->Has(1)); 1699 CHECK(array->Has(2)); 1700 CHECK_EQ(7, array->Get(2)->Int32Value()); 1701 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run(); 1702 Local<v8::Array> arr = obj.As<v8::Array>(); 1703 CHECK_EQ(3, arr->Length()); 1704 CHECK_EQ(1, arr->Get(0)->Int32Value()); 1705 CHECK_EQ(2, arr->Get(1)->Int32Value()); 1706 CHECK_EQ(3, arr->Get(2)->Int32Value()); 1707} 1708 1709 1710v8::Handle<Value> HandleF(const v8::Arguments& args) { 1711 v8::HandleScope scope; 1712 ApiTestFuzzer::Fuzz(); 1713 Local<v8::Array> result = v8::Array::New(args.Length()); 1714 for (int i = 0; i < args.Length(); i++) 1715 result->Set(i, args[i]); 1716 return scope.Close(result); 1717} 1718 1719 1720THREADED_TEST(Vector) { 1721 v8::HandleScope scope; 1722 Local<ObjectTemplate> global = ObjectTemplate::New(); 1723 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF)); 1724 LocalContext context(0, global); 1725 1726 const char* fun = "f()"; 1727 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>(); 1728 CHECK_EQ(0, a0->Length()); 1729 1730 const char* fun2 = "f(11)"; 1731 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>(); 1732 CHECK_EQ(1, a1->Length()); 1733 CHECK_EQ(11, a1->Get(0)->Int32Value()); 1734 1735 const char* fun3 = "f(12, 13)"; 1736 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>(); 1737 CHECK_EQ(2, a2->Length()); 1738 CHECK_EQ(12, a2->Get(0)->Int32Value()); 1739 CHECK_EQ(13, a2->Get(1)->Int32Value()); 1740 1741 const char* fun4 = "f(14, 15, 16)"; 1742 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>(); 1743 CHECK_EQ(3, a3->Length()); 1744 CHECK_EQ(14, a3->Get(0)->Int32Value()); 1745 CHECK_EQ(15, a3->Get(1)->Int32Value()); 1746 CHECK_EQ(16, a3->Get(2)->Int32Value()); 1747 1748 const char* fun5 = "f(17, 18, 19, 20)"; 1749 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>(); 1750 CHECK_EQ(4, a4->Length()); 1751 CHECK_EQ(17, a4->Get(0)->Int32Value()); 1752 CHECK_EQ(18, a4->Get(1)->Int32Value()); 1753 CHECK_EQ(19, a4->Get(2)->Int32Value()); 1754 CHECK_EQ(20, a4->Get(3)->Int32Value()); 1755} 1756 1757 1758THREADED_TEST(FunctionCall) { 1759 v8::HandleScope scope; 1760 LocalContext context; 1761 CompileRun( 1762 "function Foo() {" 1763 " var result = [];" 1764 " for (var i = 0; i < arguments.length; i++) {" 1765 " result.push(arguments[i]);" 1766 " }" 1767 " return result;" 1768 "}"); 1769 Local<Function> Foo = 1770 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 1771 1772 v8::Handle<Value>* args0 = NULL; 1773 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0)); 1774 CHECK_EQ(0, a0->Length()); 1775 1776 v8::Handle<Value> args1[] = { v8_num(1.1) }; 1777 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1)); 1778 CHECK_EQ(1, a1->Length()); 1779 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 1780 1781 v8::Handle<Value> args2[] = { v8_num(2.2), 1782 v8_num(3.3) }; 1783 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2)); 1784 CHECK_EQ(2, a2->Length()); 1785 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 1786 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 1787 1788 v8::Handle<Value> args3[] = { v8_num(4.4), 1789 v8_num(5.5), 1790 v8_num(6.6) }; 1791 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3)); 1792 CHECK_EQ(3, a3->Length()); 1793 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 1794 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 1795 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 1796 1797 v8::Handle<Value> args4[] = { v8_num(7.7), 1798 v8_num(8.8), 1799 v8_num(9.9), 1800 v8_num(10.11) }; 1801 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4)); 1802 CHECK_EQ(4, a4->Length()); 1803 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 1804 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 1805 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 1806 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 1807} 1808 1809 1810static const char* js_code_causing_out_of_memory = 1811 "var a = new Array(); while(true) a.push(a);"; 1812 1813 1814// These tests run for a long time and prevent us from running tests 1815// that come after them so they cannot run in parallel. 1816TEST(OutOfMemory) { 1817 // It's not possible to read a snapshot into a heap with different dimensions. 1818 if (v8::internal::Snapshot::IsEnabled()) return; 1819 // Set heap limits. 1820 static const int K = 1024; 1821 v8::ResourceConstraints constraints; 1822 constraints.set_max_young_space_size(256 * K); 1823 constraints.set_max_old_space_size(4 * K * K); 1824 v8::SetResourceConstraints(&constraints); 1825 1826 // Execute a script that causes out of memory. 1827 v8::HandleScope scope; 1828 LocalContext context; 1829 v8::V8::IgnoreOutOfMemoryException(); 1830 Local<Script> script = 1831 Script::Compile(String::New(js_code_causing_out_of_memory)); 1832 Local<Value> result = script->Run(); 1833 1834 // Check for out of memory state. 1835 CHECK(result.IsEmpty()); 1836 CHECK(context->HasOutOfMemoryException()); 1837} 1838 1839 1840v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) { 1841 ApiTestFuzzer::Fuzz(); 1842 1843 v8::HandleScope scope; 1844 LocalContext context; 1845 Local<Script> script = 1846 Script::Compile(String::New(js_code_causing_out_of_memory)); 1847 Local<Value> result = script->Run(); 1848 1849 // Check for out of memory state. 1850 CHECK(result.IsEmpty()); 1851 CHECK(context->HasOutOfMemoryException()); 1852 1853 return result; 1854} 1855 1856 1857TEST(OutOfMemoryNested) { 1858 // It's not possible to read a snapshot into a heap with different dimensions. 1859 if (v8::internal::Snapshot::IsEnabled()) return; 1860 // Set heap limits. 1861 static const int K = 1024; 1862 v8::ResourceConstraints constraints; 1863 constraints.set_max_young_space_size(256 * K); 1864 constraints.set_max_old_space_size(4 * K * K); 1865 v8::SetResourceConstraints(&constraints); 1866 1867 v8::HandleScope scope; 1868 Local<ObjectTemplate> templ = ObjectTemplate::New(); 1869 templ->Set(v8_str("ProvokeOutOfMemory"), 1870 v8::FunctionTemplate::New(ProvokeOutOfMemory)); 1871 LocalContext context(0, templ); 1872 v8::V8::IgnoreOutOfMemoryException(); 1873 Local<Value> result = CompileRun( 1874 "var thrown = false;" 1875 "try {" 1876 " ProvokeOutOfMemory();" 1877 "} catch (e) {" 1878 " thrown = true;" 1879 "}"); 1880 // Check for out of memory state. 1881 CHECK(result.IsEmpty()); 1882 CHECK(context->HasOutOfMemoryException()); 1883} 1884 1885 1886TEST(HugeConsStringOutOfMemory) { 1887 // It's not possible to read a snapshot into a heap with different dimensions. 1888 if (v8::internal::Snapshot::IsEnabled()) return; 1889 v8::HandleScope scope; 1890 LocalContext context; 1891 // Set heap limits. 1892 static const int K = 1024; 1893 v8::ResourceConstraints constraints; 1894 constraints.set_max_young_space_size(256 * K); 1895 constraints.set_max_old_space_size(2 * K * K); 1896 v8::SetResourceConstraints(&constraints); 1897 1898 // Execute a script that causes out of memory. 1899 v8::V8::IgnoreOutOfMemoryException(); 1900 1901 // Build huge string. This should fail with out of memory exception. 1902 Local<Value> result = CompileRun( 1903 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();" 1904 "for (var i = 0; i < 22; i++) { str = str + str; }"); 1905 1906 // Check for out of memory state. 1907 CHECK(result.IsEmpty()); 1908 CHECK(context->HasOutOfMemoryException()); 1909} 1910 1911 1912THREADED_TEST(ConstructCall) { 1913 v8::HandleScope scope; 1914 LocalContext context; 1915 CompileRun( 1916 "function Foo() {" 1917 " var result = [];" 1918 " for (var i = 0; i < arguments.length; i++) {" 1919 " result.push(arguments[i]);" 1920 " }" 1921 " return result;" 1922 "}"); 1923 Local<Function> Foo = 1924 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 1925 1926 v8::Handle<Value>* args0 = NULL; 1927 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0)); 1928 CHECK_EQ(0, a0->Length()); 1929 1930 v8::Handle<Value> args1[] = { v8_num(1.1) }; 1931 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1)); 1932 CHECK_EQ(1, a1->Length()); 1933 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 1934 1935 v8::Handle<Value> args2[] = { v8_num(2.2), 1936 v8_num(3.3) }; 1937 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2)); 1938 CHECK_EQ(2, a2->Length()); 1939 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 1940 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 1941 1942 v8::Handle<Value> args3[] = { v8_num(4.4), 1943 v8_num(5.5), 1944 v8_num(6.6) }; 1945 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3)); 1946 CHECK_EQ(3, a3->Length()); 1947 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 1948 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 1949 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 1950 1951 v8::Handle<Value> args4[] = { v8_num(7.7), 1952 v8_num(8.8), 1953 v8_num(9.9), 1954 v8_num(10.11) }; 1955 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4)); 1956 CHECK_EQ(4, a4->Length()); 1957 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 1958 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 1959 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 1960 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 1961} 1962 1963 1964static void CheckUncle(v8::TryCatch* try_catch) { 1965 CHECK(try_catch->HasCaught()); 1966 String::AsciiValue str_value(try_catch->Exception()); 1967 CHECK_EQ(*str_value, "uncle?"); 1968 try_catch->Reset(); 1969} 1970 1971 1972THREADED_TEST(ConversionNumber) { 1973 v8::HandleScope scope; 1974 LocalContext env; 1975 // Very large number. 1976 CompileRun("var obj = Math.pow(2,32) * 1237;"); 1977 Local<Value> obj = env->Global()->Get(v8_str("obj")); 1978 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value()); 1979 CHECK_EQ(0, obj->ToInt32()->Value()); 1980 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned. 1981 // Large number. 1982 CompileRun("var obj = -1234567890123;"); 1983 obj = env->Global()->Get(v8_str("obj")); 1984 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value()); 1985 CHECK_EQ(-1912276171, obj->ToInt32()->Value()); 1986 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT 1987 // Small positive integer. 1988 CompileRun("var obj = 42;"); 1989 obj = env->Global()->Get(v8_str("obj")); 1990 CHECK_EQ(42.0, obj->ToNumber()->Value()); 1991 CHECK_EQ(42, obj->ToInt32()->Value()); 1992 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 1993 // Negative integer. 1994 CompileRun("var obj = -37;"); 1995 obj = env->Global()->Get(v8_str("obj")); 1996 CHECK_EQ(-37.0, obj->ToNumber()->Value()); 1997 CHECK_EQ(-37, obj->ToInt32()->Value()); 1998 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT 1999 // Positive non-int32 integer. 2000 CompileRun("var obj = 0x81234567;"); 2001 obj = env->Global()->Get(v8_str("obj")); 2002 CHECK_EQ(2166572391.0, obj->ToNumber()->Value()); 2003 CHECK_EQ(-2128394905, obj->ToInt32()->Value()); 2004 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT 2005 // Fraction. 2006 CompileRun("var obj = 42.3;"); 2007 obj = env->Global()->Get(v8_str("obj")); 2008 CHECK_EQ(42.3, obj->ToNumber()->Value()); 2009 CHECK_EQ(42, obj->ToInt32()->Value()); 2010 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 2011 // Large negative fraction. 2012 CompileRun("var obj = -5726623061.75;"); 2013 obj = env->Global()->Get(v8_str("obj")); 2014 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value()); 2015 CHECK_EQ(-1431655765, obj->ToInt32()->Value()); 2016 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT 2017} 2018 2019 2020THREADED_TEST(isNumberType) { 2021 v8::HandleScope scope; 2022 LocalContext env; 2023 // Very large number. 2024 CompileRun("var obj = Math.pow(2,32) * 1237;"); 2025 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2026 CHECK(!obj->IsInt32()); 2027 CHECK(!obj->IsUint32()); 2028 // Large negative number. 2029 CompileRun("var obj = -1234567890123;"); 2030 obj = env->Global()->Get(v8_str("obj")); 2031 CHECK(!obj->IsInt32()); 2032 CHECK(!obj->IsUint32()); 2033 // Small positive integer. 2034 CompileRun("var obj = 42;"); 2035 obj = env->Global()->Get(v8_str("obj")); 2036 CHECK(obj->IsInt32()); 2037 CHECK(obj->IsUint32()); 2038 // Negative integer. 2039 CompileRun("var obj = -37;"); 2040 obj = env->Global()->Get(v8_str("obj")); 2041 CHECK(obj->IsInt32()); 2042 CHECK(!obj->IsUint32()); 2043 // Positive non-int32 integer. 2044 CompileRun("var obj = 0x81234567;"); 2045 obj = env->Global()->Get(v8_str("obj")); 2046 CHECK(!obj->IsInt32()); 2047 CHECK(obj->IsUint32()); 2048 // Fraction. 2049 CompileRun("var obj = 42.3;"); 2050 obj = env->Global()->Get(v8_str("obj")); 2051 CHECK(!obj->IsInt32()); 2052 CHECK(!obj->IsUint32()); 2053 // Large negative fraction. 2054 CompileRun("var obj = -5726623061.75;"); 2055 obj = env->Global()->Get(v8_str("obj")); 2056 CHECK(!obj->IsInt32()); 2057 CHECK(!obj->IsUint32()); 2058} 2059 2060 2061THREADED_TEST(ConversionException) { 2062 v8::HandleScope scope; 2063 LocalContext env; 2064 CompileRun( 2065 "function TestClass() { };" 2066 "TestClass.prototype.toString = function () { throw 'uncle?'; };" 2067 "var obj = new TestClass();"); 2068 Local<Value> obj = env->Global()->Get(v8_str("obj")); 2069 2070 v8::TryCatch try_catch; 2071 2072 Local<Value> to_string_result = obj->ToString(); 2073 CHECK(to_string_result.IsEmpty()); 2074 CheckUncle(&try_catch); 2075 2076 Local<Value> to_number_result = obj->ToNumber(); 2077 CHECK(to_number_result.IsEmpty()); 2078 CheckUncle(&try_catch); 2079 2080 Local<Value> to_integer_result = obj->ToInteger(); 2081 CHECK(to_integer_result.IsEmpty()); 2082 CheckUncle(&try_catch); 2083 2084 Local<Value> to_uint32_result = obj->ToUint32(); 2085 CHECK(to_uint32_result.IsEmpty()); 2086 CheckUncle(&try_catch); 2087 2088 Local<Value> to_int32_result = obj->ToInt32(); 2089 CHECK(to_int32_result.IsEmpty()); 2090 CheckUncle(&try_catch); 2091 2092 Local<Value> to_object_result = v8::Undefined()->ToObject(); 2093 CHECK(to_object_result.IsEmpty()); 2094 CHECK(try_catch.HasCaught()); 2095 try_catch.Reset(); 2096 2097 int32_t int32_value = obj->Int32Value(); 2098 CHECK_EQ(0, int32_value); 2099 CheckUncle(&try_catch); 2100 2101 uint32_t uint32_value = obj->Uint32Value(); 2102 CHECK_EQ(0, uint32_value); 2103 CheckUncle(&try_catch); 2104 2105 double number_value = obj->NumberValue(); 2106 CHECK_NE(0, IsNaN(number_value)); 2107 CheckUncle(&try_catch); 2108 2109 int64_t integer_value = obj->IntegerValue(); 2110 CHECK_EQ(0.0, static_cast<double>(integer_value)); 2111 CheckUncle(&try_catch); 2112} 2113 2114 2115v8::Handle<Value> ThrowFromC(const v8::Arguments& args) { 2116 ApiTestFuzzer::Fuzz(); 2117 return v8::ThrowException(v8_str("konto")); 2118} 2119 2120 2121v8::Handle<Value> CCatcher(const v8::Arguments& args) { 2122 if (args.Length() < 1) return v8::Boolean::New(false); 2123 v8::HandleScope scope; 2124 v8::TryCatch try_catch; 2125 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run(); 2126 CHECK(!try_catch.HasCaught() || result.IsEmpty()); 2127 return v8::Boolean::New(try_catch.HasCaught()); 2128} 2129 2130 2131THREADED_TEST(APICatch) { 2132 v8::HandleScope scope; 2133 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2134 templ->Set(v8_str("ThrowFromC"), 2135 v8::FunctionTemplate::New(ThrowFromC)); 2136 LocalContext context(0, templ); 2137 CompileRun( 2138 "var thrown = false;" 2139 "try {" 2140 " ThrowFromC();" 2141 "} catch (e) {" 2142 " thrown = true;" 2143 "}"); 2144 Local<Value> thrown = context->Global()->Get(v8_str("thrown")); 2145 CHECK(thrown->BooleanValue()); 2146} 2147 2148 2149THREADED_TEST(APIThrowTryCatch) { 2150 v8::HandleScope scope; 2151 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2152 templ->Set(v8_str("ThrowFromC"), 2153 v8::FunctionTemplate::New(ThrowFromC)); 2154 LocalContext context(0, templ); 2155 v8::TryCatch try_catch; 2156 CompileRun("ThrowFromC();"); 2157 CHECK(try_catch.HasCaught()); 2158} 2159 2160 2161// Test that a try-finally block doesn't shadow a try-catch block 2162// when setting up an external handler. 2163// 2164// BUG(271): Some of the exception propagation does not work on the 2165// ARM simulator because the simulator separates the C++ stack and the 2166// JS stack. This test therefore fails on the simulator. The test is 2167// not threaded to allow the threading tests to run on the simulator. 2168TEST(TryCatchInTryFinally) { 2169 v8::HandleScope scope; 2170 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2171 templ->Set(v8_str("CCatcher"), 2172 v8::FunctionTemplate::New(CCatcher)); 2173 LocalContext context(0, templ); 2174 Local<Value> result = CompileRun("try {" 2175 " try {" 2176 " CCatcher('throw 7;');" 2177 " } finally {" 2178 " }" 2179 "} catch (e) {" 2180 "}"); 2181 CHECK(result->IsTrue()); 2182} 2183 2184 2185static void receive_message(v8::Handle<v8::Message> message, 2186 v8::Handle<v8::Value> data) { 2187 message->Get(); 2188 message_received = true; 2189} 2190 2191 2192TEST(APIThrowMessage) { 2193 message_received = false; 2194 v8::HandleScope scope; 2195 v8::V8::AddMessageListener(receive_message); 2196 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2197 templ->Set(v8_str("ThrowFromC"), 2198 v8::FunctionTemplate::New(ThrowFromC)); 2199 LocalContext context(0, templ); 2200 CompileRun("ThrowFromC();"); 2201 CHECK(message_received); 2202 v8::V8::RemoveMessageListeners(check_message); 2203} 2204 2205 2206TEST(APIThrowMessageAndVerboseTryCatch) { 2207 message_received = false; 2208 v8::HandleScope scope; 2209 v8::V8::AddMessageListener(receive_message); 2210 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2211 templ->Set(v8_str("ThrowFromC"), 2212 v8::FunctionTemplate::New(ThrowFromC)); 2213 LocalContext context(0, templ); 2214 v8::TryCatch try_catch; 2215 try_catch.SetVerbose(true); 2216 Local<Value> result = CompileRun("ThrowFromC();"); 2217 CHECK(try_catch.HasCaught()); 2218 CHECK(result.IsEmpty()); 2219 CHECK(message_received); 2220 v8::V8::RemoveMessageListeners(check_message); 2221} 2222 2223 2224THREADED_TEST(ExternalScriptException) { 2225 v8::HandleScope scope; 2226 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2227 templ->Set(v8_str("ThrowFromC"), 2228 v8::FunctionTemplate::New(ThrowFromC)); 2229 LocalContext context(0, templ); 2230 2231 v8::TryCatch try_catch; 2232 Local<Script> script 2233 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';")); 2234 Local<Value> result = script->Run(); 2235 CHECK(result.IsEmpty()); 2236 CHECK(try_catch.HasCaught()); 2237 String::AsciiValue exception_value(try_catch.Exception()); 2238 CHECK_EQ("konto", *exception_value); 2239} 2240 2241 2242 2243v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) { 2244 ApiTestFuzzer::Fuzz(); 2245 CHECK_EQ(4, args.Length()); 2246 int count = args[0]->Int32Value(); 2247 int cInterval = args[2]->Int32Value(); 2248 if (count == 0) { 2249 return v8::ThrowException(v8_str("FromC")); 2250 } else { 2251 Local<v8::Object> global = Context::GetCurrent()->Global(); 2252 Local<Value> fun = global->Get(v8_str("JSThrowCountDown")); 2253 v8::Handle<Value> argv[] = { v8_num(count - 1), 2254 args[1], 2255 args[2], 2256 args[3] }; 2257 if (count % cInterval == 0) { 2258 v8::TryCatch try_catch; 2259 Local<Value> result = fun.As<Function>()->Call(global, 4, argv); 2260 int expected = args[3]->Int32Value(); 2261 if (try_catch.HasCaught()) { 2262 CHECK_EQ(expected, count); 2263 CHECK(result.IsEmpty()); 2264 CHECK(!i::Top::has_scheduled_exception()); 2265 } else { 2266 CHECK_NE(expected, count); 2267 } 2268 return result; 2269 } else { 2270 return fun.As<Function>()->Call(global, 4, argv); 2271 } 2272 } 2273} 2274 2275 2276v8::Handle<Value> JSCheck(const v8::Arguments& args) { 2277 ApiTestFuzzer::Fuzz(); 2278 CHECK_EQ(3, args.Length()); 2279 bool equality = args[0]->BooleanValue(); 2280 int count = args[1]->Int32Value(); 2281 int expected = args[2]->Int32Value(); 2282 if (equality) { 2283 CHECK_EQ(count, expected); 2284 } else { 2285 CHECK_NE(count, expected); 2286 } 2287 return v8::Undefined(); 2288} 2289 2290 2291THREADED_TEST(EvalInTryFinally) { 2292 v8::HandleScope scope; 2293 LocalContext context; 2294 v8::TryCatch try_catch; 2295 CompileRun("(function() {" 2296 " try {" 2297 " eval('asldkf (*&^&*^');" 2298 " } finally {" 2299 " return;" 2300 " }" 2301 "})()"); 2302 CHECK(!try_catch.HasCaught()); 2303} 2304 2305 2306// This test works by making a stack of alternating JavaScript and C 2307// activations. These activations set up exception handlers with regular 2308// intervals, one interval for C activations and another for JavaScript 2309// activations. When enough activations have been created an exception is 2310// thrown and we check that the right activation catches the exception and that 2311// no other activations do. The right activation is always the topmost one with 2312// a handler, regardless of whether it is in JavaScript or C. 2313// 2314// The notation used to describe a test case looks like this: 2315// 2316// *JS[4] *C[3] @JS[2] C[1] JS[0] 2317// 2318// Each entry is an activation, either JS or C. The index is the count at that 2319// level. Stars identify activations with exception handlers, the @ identifies 2320// the exception handler that should catch the exception. 2321// 2322// BUG(271): Some of the exception propagation does not work on the 2323// ARM simulator because the simulator separates the C++ stack and the 2324// JS stack. This test therefore fails on the simulator. The test is 2325// not threaded to allow the threading tests to run on the simulator. 2326TEST(ExceptionOrder) { 2327 v8::HandleScope scope; 2328 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2329 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck)); 2330 templ->Set(v8_str("CThrowCountDown"), 2331 v8::FunctionTemplate::New(CThrowCountDown)); 2332 LocalContext context(0, templ); 2333 CompileRun( 2334 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {" 2335 " if (count == 0) throw 'FromJS';" 2336 " if (count % jsInterval == 0) {" 2337 " try {" 2338 " var value = CThrowCountDown(count - 1," 2339 " jsInterval," 2340 " cInterval," 2341 " expected);" 2342 " check(false, count, expected);" 2343 " return value;" 2344 " } catch (e) {" 2345 " check(true, count, expected);" 2346 " }" 2347 " } else {" 2348 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);" 2349 " }" 2350 "}"); 2351 Local<Function> fun = 2352 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown"))); 2353 2354 const int argc = 4; 2355 // count jsInterval cInterval expected 2356 2357 // *JS[4] *C[3] @JS[2] C[1] JS[0] 2358 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) }; 2359 fun->Call(fun, argc, a0); 2360 2361 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0] 2362 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) }; 2363 fun->Call(fun, argc, a1); 2364 2365 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0] 2366 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) }; 2367 fun->Call(fun, argc, a2); 2368 2369 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0] 2370 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) }; 2371 fun->Call(fun, argc, a3); 2372 2373 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0] 2374 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) }; 2375 fun->Call(fun, argc, a4); 2376 2377 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0] 2378 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) }; 2379 fun->Call(fun, argc, a5); 2380} 2381 2382 2383v8::Handle<Value> ThrowValue(const v8::Arguments& args) { 2384 ApiTestFuzzer::Fuzz(); 2385 CHECK_EQ(1, args.Length()); 2386 return v8::ThrowException(args[0]); 2387} 2388 2389 2390THREADED_TEST(ThrowValues) { 2391 v8::HandleScope scope; 2392 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2393 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue)); 2394 LocalContext context(0, templ); 2395 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 2396 "function Run(obj) {" 2397 " try {" 2398 " Throw(obj);" 2399 " } catch (e) {" 2400 " return e;" 2401 " }" 2402 " return 'no exception';" 2403 "}" 2404 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];")); 2405 CHECK_EQ(5, result->Length()); 2406 CHECK(result->Get(v8::Integer::New(0))->IsString()); 2407 CHECK(result->Get(v8::Integer::New(1))->IsNumber()); 2408 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value()); 2409 CHECK(result->Get(v8::Integer::New(2))->IsNumber()); 2410 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value()); 2411 CHECK(result->Get(v8::Integer::New(3))->IsNull()); 2412 CHECK(result->Get(v8::Integer::New(4))->IsUndefined()); 2413} 2414 2415 2416THREADED_TEST(CatchZero) { 2417 v8::HandleScope scope; 2418 LocalContext context; 2419 v8::TryCatch try_catch; 2420 CHECK(!try_catch.HasCaught()); 2421 Script::Compile(v8_str("throw 10"))->Run(); 2422 CHECK(try_catch.HasCaught()); 2423 CHECK_EQ(10, try_catch.Exception()->Int32Value()); 2424 try_catch.Reset(); 2425 CHECK(!try_catch.HasCaught()); 2426 Script::Compile(v8_str("throw 0"))->Run(); 2427 CHECK(try_catch.HasCaught()); 2428 CHECK_EQ(0, try_catch.Exception()->Int32Value()); 2429} 2430 2431 2432THREADED_TEST(CatchExceptionFromWith) { 2433 v8::HandleScope scope; 2434 LocalContext context; 2435 v8::TryCatch try_catch; 2436 CHECK(!try_catch.HasCaught()); 2437 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run(); 2438 CHECK(try_catch.HasCaught()); 2439} 2440 2441 2442THREADED_TEST(Equality) { 2443 v8::HandleScope scope; 2444 LocalContext context; 2445 // Check that equality works at all before relying on CHECK_EQ 2446 CHECK(v8_str("a")->Equals(v8_str("a"))); 2447 CHECK(!v8_str("a")->Equals(v8_str("b"))); 2448 2449 CHECK_EQ(v8_str("a"), v8_str("a")); 2450 CHECK_NE(v8_str("a"), v8_str("b")); 2451 CHECK_EQ(v8_num(1), v8_num(1)); 2452 CHECK_EQ(v8_num(1.00), v8_num(1)); 2453 CHECK_NE(v8_num(1), v8_num(2)); 2454 2455 // Assume String is not symbol. 2456 CHECK(v8_str("a")->StrictEquals(v8_str("a"))); 2457 CHECK(!v8_str("a")->StrictEquals(v8_str("b"))); 2458 CHECK(!v8_str("5")->StrictEquals(v8_num(5))); 2459 CHECK(v8_num(1)->StrictEquals(v8_num(1))); 2460 CHECK(!v8_num(1)->StrictEquals(v8_num(2))); 2461 CHECK(v8_num(0)->StrictEquals(v8_num(-0))); 2462 Local<Value> not_a_number = v8_num(i::OS::nan_value()); 2463 CHECK(!not_a_number->StrictEquals(not_a_number)); 2464 CHECK(v8::False()->StrictEquals(v8::False())); 2465 CHECK(!v8::False()->StrictEquals(v8::Undefined())); 2466 2467 v8::Handle<v8::Object> obj = v8::Object::New(); 2468 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj); 2469 CHECK(alias->StrictEquals(obj)); 2470 alias.Dispose(); 2471} 2472 2473 2474THREADED_TEST(MultiRun) { 2475 v8::HandleScope scope; 2476 LocalContext context; 2477 Local<Script> script = Script::Compile(v8_str("x")); 2478 for (int i = 0; i < 10; i++) 2479 script->Run(); 2480} 2481 2482 2483static v8::Handle<Value> GetXValue(Local<String> name, 2484 const AccessorInfo& info) { 2485 ApiTestFuzzer::Fuzz(); 2486 CHECK_EQ(info.Data(), v8_str("donut")); 2487 CHECK_EQ(name, v8_str("x")); 2488 return name; 2489} 2490 2491 2492THREADED_TEST(SimplePropertyRead) { 2493 v8::HandleScope scope; 2494 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2495 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2496 LocalContext context; 2497 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2498 Local<Script> script = Script::Compile(v8_str("obj.x")); 2499 for (int i = 0; i < 10; i++) { 2500 Local<Value> result = script->Run(); 2501 CHECK_EQ(result, v8_str("x")); 2502 } 2503} 2504 2505THREADED_TEST(DefinePropertyOnAPIAccessor) { 2506 v8::HandleScope scope; 2507 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2508 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2509 LocalContext context; 2510 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2511 2512 // Uses getOwnPropertyDescriptor to check the configurable status 2513 Local<Script> script_desc 2514 = Script::Compile(v8_str("var prop = Object.getOwnPropertyDescriptor( " 2515 "obj, 'x');" 2516 "prop.configurable;")); 2517 Local<Value> result = script_desc->Run(); 2518 CHECK_EQ(result->BooleanValue(), true); 2519 2520 // Redefine get - but still configurable 2521 Local<Script> script_define 2522 = Script::Compile(v8_str("var desc = { get: function(){return 42; }," 2523 " configurable: true };" 2524 "Object.defineProperty(obj, 'x', desc);" 2525 "obj.x")); 2526 result = script_define->Run(); 2527 CHECK_EQ(result, v8_num(42)); 2528 2529 // Check that the accessor is still configurable 2530 result = script_desc->Run(); 2531 CHECK_EQ(result->BooleanValue(), true); 2532 2533 // Redefine to a non-configurable 2534 script_define 2535 = Script::Compile(v8_str("var desc = { get: function(){return 43; }," 2536 " configurable: false };" 2537 "Object.defineProperty(obj, 'x', desc);" 2538 "obj.x")); 2539 result = script_define->Run(); 2540 CHECK_EQ(result, v8_num(43)); 2541 result = script_desc->Run(); 2542 CHECK_EQ(result->BooleanValue(), false); 2543 2544 // Make sure that it is not possible to redefine again 2545 v8::TryCatch try_catch; 2546 result = script_define->Run(); 2547 CHECK(try_catch.HasCaught()); 2548 String::AsciiValue exception_value(try_catch.Exception()); 2549 CHECK_EQ(*exception_value, 2550 "TypeError: Cannot redefine property: defineProperty"); 2551} 2552 2553THREADED_TEST(DefinePropertyOnDefineGetterSetter) { 2554 v8::HandleScope scope; 2555 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2556 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2557 LocalContext context; 2558 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2559 2560 Local<Script> script_desc = Script::Compile(v8_str("var prop =" 2561 "Object.getOwnPropertyDescriptor( " 2562 "obj, 'x');" 2563 "prop.configurable;")); 2564 Local<Value> result = script_desc->Run(); 2565 CHECK_EQ(result->BooleanValue(), true); 2566 2567 Local<Script> script_define = 2568 Script::Compile(v8_str("var desc = {get: function(){return 42; }," 2569 " configurable: true };" 2570 "Object.defineProperty(obj, 'x', desc);" 2571 "obj.x")); 2572 result = script_define->Run(); 2573 CHECK_EQ(result, v8_num(42)); 2574 2575 2576 result = script_desc->Run(); 2577 CHECK_EQ(result->BooleanValue(), true); 2578 2579 2580 script_define = 2581 Script::Compile(v8_str("var desc = {get: function(){return 43; }," 2582 " configurable: false };" 2583 "Object.defineProperty(obj, 'x', desc);" 2584 "obj.x")); 2585 result = script_define->Run(); 2586 CHECK_EQ(result, v8_num(43)); 2587 result = script_desc->Run(); 2588 2589 CHECK_EQ(result->BooleanValue(), false); 2590 2591 v8::TryCatch try_catch; 2592 result = script_define->Run(); 2593 CHECK(try_catch.HasCaught()); 2594 String::AsciiValue exception_value(try_catch.Exception()); 2595 CHECK_EQ(*exception_value, 2596 "TypeError: Cannot redefine property: defineProperty"); 2597} 2598 2599 2600static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context, 2601 char const* name) { 2602 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name))); 2603} 2604 2605 2606THREADED_TEST(DefineAPIAccessorOnObject) { 2607 v8::HandleScope scope; 2608 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2609 LocalContext context; 2610 2611 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 2612 CompileRun("var obj2 = {};"); 2613 2614 CHECK(CompileRun("obj1.x")->IsUndefined()); 2615 CHECK(CompileRun("obj2.x")->IsUndefined()); 2616 2617 CHECK(GetGlobalProperty(&context, "obj1")-> 2618 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2619 2620 ExpectString("obj1.x", "x"); 2621 CHECK(CompileRun("obj2.x")->IsUndefined()); 2622 2623 CHECK(GetGlobalProperty(&context, "obj2")-> 2624 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2625 2626 ExpectString("obj1.x", "x"); 2627 ExpectString("obj2.x", "x"); 2628 2629 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2630 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2631 2632 CompileRun("Object.defineProperty(obj1, 'x'," 2633 "{ get: function() { return 'y'; }, configurable: true })"); 2634 2635 ExpectString("obj1.x", "y"); 2636 ExpectString("obj2.x", "x"); 2637 2638 CompileRun("Object.defineProperty(obj2, 'x'," 2639 "{ get: function() { return 'y'; }, configurable: true })"); 2640 2641 ExpectString("obj1.x", "y"); 2642 ExpectString("obj2.x", "y"); 2643 2644 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2645 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2646 2647 CHECK(GetGlobalProperty(&context, "obj1")-> 2648 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2649 CHECK(GetGlobalProperty(&context, "obj2")-> 2650 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2651 2652 ExpectString("obj1.x", "x"); 2653 ExpectString("obj2.x", "x"); 2654 2655 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2656 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2657 2658 // Define getters/setters, but now make them not configurable. 2659 CompileRun("Object.defineProperty(obj1, 'x'," 2660 "{ get: function() { return 'z'; }, configurable: false })"); 2661 CompileRun("Object.defineProperty(obj2, 'x'," 2662 "{ get: function() { return 'z'; }, configurable: false })"); 2663 2664 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2665 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2666 2667 ExpectString("obj1.x", "z"); 2668 ExpectString("obj2.x", "z"); 2669 2670 CHECK(!GetGlobalProperty(&context, "obj1")-> 2671 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2672 CHECK(!GetGlobalProperty(&context, "obj2")-> 2673 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2674 2675 ExpectString("obj1.x", "z"); 2676 ExpectString("obj2.x", "z"); 2677} 2678 2679 2680THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) { 2681 v8::HandleScope scope; 2682 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2683 LocalContext context; 2684 2685 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 2686 CompileRun("var obj2 = {};"); 2687 2688 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 2689 v8_str("x"), 2690 GetXValue, NULL, 2691 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 2692 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 2693 v8_str("x"), 2694 GetXValue, NULL, 2695 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 2696 2697 ExpectString("obj1.x", "x"); 2698 ExpectString("obj2.x", "x"); 2699 2700 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 2701 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 2702 2703 CHECK(!GetGlobalProperty(&context, "obj1")-> 2704 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2705 CHECK(!GetGlobalProperty(&context, "obj2")-> 2706 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 2707 2708 { 2709 v8::TryCatch try_catch; 2710 CompileRun("Object.defineProperty(obj1, 'x'," 2711 "{get: function() { return 'func'; }})"); 2712 CHECK(try_catch.HasCaught()); 2713 String::AsciiValue exception_value(try_catch.Exception()); 2714 CHECK_EQ(*exception_value, 2715 "TypeError: Cannot redefine property: defineProperty"); 2716 } 2717 { 2718 v8::TryCatch try_catch; 2719 CompileRun("Object.defineProperty(obj2, 'x'," 2720 "{get: function() { return 'func'; }})"); 2721 CHECK(try_catch.HasCaught()); 2722 String::AsciiValue exception_value(try_catch.Exception()); 2723 CHECK_EQ(*exception_value, 2724 "TypeError: Cannot redefine property: defineProperty"); 2725 } 2726} 2727 2728 2729static v8::Handle<Value> Get239Value(Local<String> name, 2730 const AccessorInfo& info) { 2731 ApiTestFuzzer::Fuzz(); 2732 CHECK_EQ(info.Data(), v8_str("donut")); 2733 CHECK_EQ(name, v8_str("239")); 2734 return name; 2735} 2736 2737 2738THREADED_TEST(ElementAPIAccessor) { 2739 v8::HandleScope scope; 2740 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2741 LocalContext context; 2742 2743 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 2744 CompileRun("var obj2 = {};"); 2745 2746 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 2747 v8_str("239"), 2748 Get239Value, NULL, 2749 v8_str("donut"))); 2750 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 2751 v8_str("239"), 2752 Get239Value, NULL, 2753 v8_str("donut"))); 2754 2755 ExpectString("obj1[239]", "239"); 2756 ExpectString("obj2[239]", "239"); 2757 ExpectString("obj1['239']", "239"); 2758 ExpectString("obj2['239']", "239"); 2759} 2760 2761 2762v8::Persistent<Value> xValue; 2763 2764 2765static void SetXValue(Local<String> name, 2766 Local<Value> value, 2767 const AccessorInfo& info) { 2768 CHECK_EQ(value, v8_num(4)); 2769 CHECK_EQ(info.Data(), v8_str("donut")); 2770 CHECK_EQ(name, v8_str("x")); 2771 CHECK(xValue.IsEmpty()); 2772 xValue = v8::Persistent<Value>::New(value); 2773} 2774 2775 2776THREADED_TEST(SimplePropertyWrite) { 2777 v8::HandleScope scope; 2778 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2779 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut")); 2780 LocalContext context; 2781 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2782 Local<Script> script = Script::Compile(v8_str("obj.x = 4")); 2783 for (int i = 0; i < 10; i++) { 2784 CHECK(xValue.IsEmpty()); 2785 script->Run(); 2786 CHECK_EQ(v8_num(4), xValue); 2787 xValue.Dispose(); 2788 xValue = v8::Persistent<Value>(); 2789 } 2790} 2791 2792 2793static v8::Handle<Value> XPropertyGetter(Local<String> property, 2794 const AccessorInfo& info) { 2795 ApiTestFuzzer::Fuzz(); 2796 CHECK(info.Data()->IsUndefined()); 2797 return property; 2798} 2799 2800 2801THREADED_TEST(NamedInterceptorPropertyRead) { 2802 v8::HandleScope scope; 2803 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2804 templ->SetNamedPropertyHandler(XPropertyGetter); 2805 LocalContext context; 2806 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2807 Local<Script> script = Script::Compile(v8_str("obj.x")); 2808 for (int i = 0; i < 10; i++) { 2809 Local<Value> result = script->Run(); 2810 CHECK_EQ(result, v8_str("x")); 2811 } 2812} 2813 2814 2815THREADED_TEST(NamedInterceptorDictionaryIC) { 2816 v8::HandleScope scope; 2817 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2818 templ->SetNamedPropertyHandler(XPropertyGetter); 2819 LocalContext context; 2820 // Create an object with a named interceptor. 2821 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance()); 2822 Local<Script> script = Script::Compile(v8_str("interceptor_obj.x")); 2823 for (int i = 0; i < 10; i++) { 2824 Local<Value> result = script->Run(); 2825 CHECK_EQ(result, v8_str("x")); 2826 } 2827 // Create a slow case object and a function accessing a property in 2828 // that slow case object (with dictionary probing in generated 2829 // code). Then force object with a named interceptor into slow-case, 2830 // pass it to the function, and check that the interceptor is called 2831 // instead of accessing the local property. 2832 Local<Value> result = 2833 CompileRun("function get_x(o) { return o.x; };" 2834 "var obj = { x : 42, y : 0 };" 2835 "delete obj.y;" 2836 "for (var i = 0; i < 10; i++) get_x(obj);" 2837 "interceptor_obj.x = 42;" 2838 "interceptor_obj.y = 10;" 2839 "delete interceptor_obj.y;" 2840 "get_x(interceptor_obj)"); 2841 CHECK_EQ(result, v8_str("x")); 2842} 2843 2844 2845static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property, 2846 const AccessorInfo& info) { 2847 // Set x on the prototype object and do not handle the get request. 2848 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype(); 2849 proto.As<v8::Object>()->Set(v8_str("x"), v8::Integer::New(23)); 2850 return v8::Handle<Value>(); 2851} 2852 2853 2854// This is a regression test for http://crbug.com/20104. Map 2855// transitions should not interfere with post interceptor lookup. 2856THREADED_TEST(NamedInterceptorMapTransitionRead) { 2857 v8::HandleScope scope; 2858 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(); 2859 Local<v8::ObjectTemplate> instance_template 2860 = function_template->InstanceTemplate(); 2861 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter); 2862 LocalContext context; 2863 context->Global()->Set(v8_str("F"), function_template->GetFunction()); 2864 // Create an instance of F and introduce a map transition for x. 2865 CompileRun("var o = new F(); o.x = 23;"); 2866 // Create an instance of F and invoke the getter. The result should be 23. 2867 Local<Value> result = CompileRun("o = new F(); o.x"); 2868 CHECK_EQ(result->Int32Value(), 23); 2869} 2870 2871 2872static v8::Handle<Value> IndexedPropertyGetter(uint32_t index, 2873 const AccessorInfo& info) { 2874 ApiTestFuzzer::Fuzz(); 2875 if (index == 37) { 2876 return v8::Handle<Value>(v8_num(625)); 2877 } 2878 return v8::Handle<Value>(); 2879} 2880 2881 2882static v8::Handle<Value> IndexedPropertySetter(uint32_t index, 2883 Local<Value> value, 2884 const AccessorInfo& info) { 2885 ApiTestFuzzer::Fuzz(); 2886 if (index == 39) { 2887 return value; 2888 } 2889 return v8::Handle<Value>(); 2890} 2891 2892 2893THREADED_TEST(IndexedInterceptorWithIndexedAccessor) { 2894 v8::HandleScope scope; 2895 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2896 templ->SetIndexedPropertyHandler(IndexedPropertyGetter, 2897 IndexedPropertySetter); 2898 LocalContext context; 2899 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2900 Local<Script> getter_script = Script::Compile(v8_str( 2901 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];")); 2902 Local<Script> setter_script = Script::Compile(v8_str( 2903 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});" 2904 "obj[17] = 23;" 2905 "obj.foo;")); 2906 Local<Script> interceptor_setter_script = Script::Compile(v8_str( 2907 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});" 2908 "obj[39] = 47;" 2909 "obj.foo;")); // This setter should not run, due to the interceptor. 2910 Local<Script> interceptor_getter_script = Script::Compile(v8_str( 2911 "obj[37];")); 2912 Local<Value> result = getter_script->Run(); 2913 CHECK_EQ(v8_num(5), result); 2914 result = setter_script->Run(); 2915 CHECK_EQ(v8_num(23), result); 2916 result = interceptor_setter_script->Run(); 2917 CHECK_EQ(v8_num(23), result); 2918 result = interceptor_getter_script->Run(); 2919 CHECK_EQ(v8_num(625), result); 2920} 2921 2922 2923static v8::Handle<Value> IdentityIndexedPropertyGetter( 2924 uint32_t index, 2925 const AccessorInfo& info) { 2926 return v8::Integer::New(index); 2927} 2928 2929 2930THREADED_TEST(IndexedInterceptorWithNoSetter) { 2931 v8::HandleScope scope; 2932 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2933 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 2934 2935 LocalContext context; 2936 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2937 2938 const char* code = 2939 "try {" 2940 " obj[0] = 239;" 2941 " for (var i = 0; i < 100; i++) {" 2942 " var v = obj[0];" 2943 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;" 2944 " }" 2945 " 'PASSED'" 2946 "} catch(e) {" 2947 " e" 2948 "}"; 2949 ExpectString(code, "PASSED"); 2950} 2951 2952 2953THREADED_TEST(IndexedInterceptorWithAccessorCheck) { 2954 v8::HandleScope scope; 2955 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2956 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 2957 2958 LocalContext context; 2959 Local<v8::Object> obj = templ->NewInstance(); 2960 obj->TurnOnAccessCheck(); 2961 context->Global()->Set(v8_str("obj"), obj); 2962 2963 const char* code = 2964 "try {" 2965 " for (var i = 0; i < 100; i++) {" 2966 " var v = obj[0];" 2967 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;" 2968 " }" 2969 " 'PASSED'" 2970 "} catch(e) {" 2971 " e" 2972 "}"; 2973 ExpectString(code, "PASSED"); 2974} 2975 2976 2977THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) { 2978 i::FLAG_allow_natives_syntax = true; 2979 v8::HandleScope scope; 2980 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2981 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 2982 2983 LocalContext context; 2984 Local<v8::Object> obj = templ->NewInstance(); 2985 context->Global()->Set(v8_str("obj"), obj); 2986 2987 const char* code = 2988 "try {" 2989 " for (var i = 0; i < 100; i++) {" 2990 " var expected = i;" 2991 " if (i == 5) {" 2992 " %EnableAccessChecks(obj);" 2993 " expected = undefined;" 2994 " }" 2995 " var v = obj[i];" 2996 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 2997 " if (i == 5) %DisableAccessChecks(obj);" 2998 " }" 2999 " 'PASSED'" 3000 "} catch(e) {" 3001 " e" 3002 "}"; 3003 ExpectString(code, "PASSED"); 3004} 3005 3006 3007THREADED_TEST(IndexedInterceptorWithDifferentIndices) { 3008 v8::HandleScope scope; 3009 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3010 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3011 3012 LocalContext context; 3013 Local<v8::Object> obj = templ->NewInstance(); 3014 context->Global()->Set(v8_str("obj"), obj); 3015 3016 const char* code = 3017 "try {" 3018 " for (var i = 0; i < 100; i++) {" 3019 " var v = obj[i];" 3020 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 3021 " }" 3022 " 'PASSED'" 3023 "} catch(e) {" 3024 " e" 3025 "}"; 3026 ExpectString(code, "PASSED"); 3027} 3028 3029 3030THREADED_TEST(IndexedInterceptorWithNotSmiLookup) { 3031 v8::HandleScope scope; 3032 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3033 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3034 3035 LocalContext context; 3036 Local<v8::Object> obj = templ->NewInstance(); 3037 context->Global()->Set(v8_str("obj"), obj); 3038 3039 const char* code = 3040 "try {" 3041 " for (var i = 0; i < 100; i++) {" 3042 " var expected = i;" 3043 " if (i == 50) {" 3044 " i = 'foobar';" 3045 " expected = undefined;" 3046 " }" 3047 " var v = obj[i];" 3048 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3049 " }" 3050 " 'PASSED'" 3051 "} catch(e) {" 3052 " e" 3053 "}"; 3054 ExpectString(code, "PASSED"); 3055} 3056 3057 3058THREADED_TEST(IndexedInterceptorGoingMegamorphic) { 3059 v8::HandleScope scope; 3060 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3061 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3062 3063 LocalContext context; 3064 Local<v8::Object> obj = templ->NewInstance(); 3065 context->Global()->Set(v8_str("obj"), obj); 3066 3067 const char* code = 3068 "var original = obj;" 3069 "try {" 3070 " for (var i = 0; i < 100; i++) {" 3071 " var expected = i;" 3072 " if (i == 50) {" 3073 " obj = {50: 'foobar'};" 3074 " expected = 'foobar';" 3075 " }" 3076 " var v = obj[i];" 3077 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3078 " if (i == 50) obj = original;" 3079 " }" 3080 " 'PASSED'" 3081 "} catch(e) {" 3082 " e" 3083 "}"; 3084 ExpectString(code, "PASSED"); 3085} 3086 3087 3088THREADED_TEST(IndexedInterceptorReceiverTurningSmi) { 3089 v8::HandleScope scope; 3090 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3091 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3092 3093 LocalContext context; 3094 Local<v8::Object> obj = templ->NewInstance(); 3095 context->Global()->Set(v8_str("obj"), obj); 3096 3097 const char* code = 3098 "var original = obj;" 3099 "try {" 3100 " for (var i = 0; i < 100; i++) {" 3101 " var expected = i;" 3102 " if (i == 5) {" 3103 " obj = 239;" 3104 " expected = undefined;" 3105 " }" 3106 " var v = obj[i];" 3107 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 3108 " if (i == 5) obj = original;" 3109 " }" 3110 " 'PASSED'" 3111 "} catch(e) {" 3112 " e" 3113 "}"; 3114 ExpectString(code, "PASSED"); 3115} 3116 3117 3118THREADED_TEST(IndexedInterceptorOnProto) { 3119 v8::HandleScope scope; 3120 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3121 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 3122 3123 LocalContext context; 3124 Local<v8::Object> obj = templ->NewInstance(); 3125 context->Global()->Set(v8_str("obj"), obj); 3126 3127 const char* code = 3128 "var o = {__proto__: obj};" 3129 "try {" 3130 " for (var i = 0; i < 100; i++) {" 3131 " var v = o[i];" 3132 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 3133 " }" 3134 " 'PASSED'" 3135 "} catch(e) {" 3136 " e" 3137 "}"; 3138 ExpectString(code, "PASSED"); 3139} 3140 3141 3142THREADED_TEST(MultiContexts) { 3143 v8::HandleScope scope; 3144 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New(); 3145 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler)); 3146 3147 Local<String> password = v8_str("Password"); 3148 3149 // Create an environment 3150 LocalContext context0(0, templ); 3151 context0->SetSecurityToken(password); 3152 v8::Handle<v8::Object> global0 = context0->Global(); 3153 global0->Set(v8_str("custom"), v8_num(1234)); 3154 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 3155 3156 // Create an independent environment 3157 LocalContext context1(0, templ); 3158 context1->SetSecurityToken(password); 3159 v8::Handle<v8::Object> global1 = context1->Global(); 3160 global1->Set(v8_str("custom"), v8_num(1234)); 3161 CHECK_NE(global0, global1); 3162 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 3163 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value()); 3164 3165 // Now create a new context with the old global 3166 LocalContext context2(0, templ, global1); 3167 context2->SetSecurityToken(password); 3168 v8::Handle<v8::Object> global2 = context2->Global(); 3169 CHECK_EQ(global1, global2); 3170 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value()); 3171 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value()); 3172} 3173 3174 3175THREADED_TEST(FunctionPrototypeAcrossContexts) { 3176 // Make sure that functions created by cloning boilerplates cannot 3177 // communicate through their __proto__ field. 3178 3179 v8::HandleScope scope; 3180 3181 LocalContext env0; 3182 v8::Handle<v8::Object> global0 = 3183 env0->Global(); 3184 v8::Handle<v8::Object> object0 = 3185 global0->Get(v8_str("Object")).As<v8::Object>(); 3186 v8::Handle<v8::Object> tostring0 = 3187 object0->Get(v8_str("toString")).As<v8::Object>(); 3188 v8::Handle<v8::Object> proto0 = 3189 tostring0->Get(v8_str("__proto__")).As<v8::Object>(); 3190 proto0->Set(v8_str("custom"), v8_num(1234)); 3191 3192 LocalContext env1; 3193 v8::Handle<v8::Object> global1 = 3194 env1->Global(); 3195 v8::Handle<v8::Object> object1 = 3196 global1->Get(v8_str("Object")).As<v8::Object>(); 3197 v8::Handle<v8::Object> tostring1 = 3198 object1->Get(v8_str("toString")).As<v8::Object>(); 3199 v8::Handle<v8::Object> proto1 = 3200 tostring1->Get(v8_str("__proto__")).As<v8::Object>(); 3201 CHECK(!proto1->Has(v8_str("custom"))); 3202} 3203 3204 3205THREADED_TEST(Regress892105) { 3206 // Make sure that object and array literals created by cloning 3207 // boilerplates cannot communicate through their __proto__ 3208 // field. This is rather difficult to check, but we try to add stuff 3209 // to Object.prototype and Array.prototype and create a new 3210 // environment. This should succeed. 3211 3212 v8::HandleScope scope; 3213 3214 Local<String> source = v8_str("Object.prototype.obj = 1234;" 3215 "Array.prototype.arr = 4567;" 3216 "8901"); 3217 3218 LocalContext env0; 3219 Local<Script> script0 = Script::Compile(source); 3220 CHECK_EQ(8901.0, script0->Run()->NumberValue()); 3221 3222 LocalContext env1; 3223 Local<Script> script1 = Script::Compile(source); 3224 CHECK_EQ(8901.0, script1->Run()->NumberValue()); 3225} 3226 3227 3228THREADED_TEST(UndetectableObject) { 3229 v8::HandleScope scope; 3230 LocalContext env; 3231 3232 Local<v8::FunctionTemplate> desc = 3233 v8::FunctionTemplate::New(0, v8::Handle<Value>()); 3234 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 3235 3236 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 3237 env->Global()->Set(v8_str("undetectable"), obj); 3238 3239 ExpectString("undetectable.toString()", "[object Object]"); 3240 ExpectString("typeof undetectable", "undefined"); 3241 ExpectString("typeof(undetectable)", "undefined"); 3242 ExpectBoolean("typeof undetectable == 'undefined'", true); 3243 ExpectBoolean("typeof undetectable == 'object'", false); 3244 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 3245 ExpectBoolean("!undetectable", true); 3246 3247 ExpectObject("true&&undetectable", obj); 3248 ExpectBoolean("false&&undetectable", false); 3249 ExpectBoolean("true||undetectable", true); 3250 ExpectObject("false||undetectable", obj); 3251 3252 ExpectObject("undetectable&&true", obj); 3253 ExpectObject("undetectable&&false", obj); 3254 ExpectBoolean("undetectable||true", true); 3255 ExpectBoolean("undetectable||false", false); 3256 3257 ExpectBoolean("undetectable==null", true); 3258 ExpectBoolean("null==undetectable", true); 3259 ExpectBoolean("undetectable==undefined", true); 3260 ExpectBoolean("undefined==undetectable", true); 3261 ExpectBoolean("undetectable==undetectable", true); 3262 3263 3264 ExpectBoolean("undetectable===null", false); 3265 ExpectBoolean("null===undetectable", false); 3266 ExpectBoolean("undetectable===undefined", false); 3267 ExpectBoolean("undefined===undetectable", false); 3268 ExpectBoolean("undetectable===undetectable", true); 3269} 3270 3271 3272THREADED_TEST(UndetectableString) { 3273 v8::HandleScope scope; 3274 LocalContext env; 3275 3276 Local<String> obj = String::NewUndetectable("foo"); 3277 env->Global()->Set(v8_str("undetectable"), obj); 3278 3279 ExpectString("undetectable", "foo"); 3280 ExpectString("typeof undetectable", "undefined"); 3281 ExpectString("typeof(undetectable)", "undefined"); 3282 ExpectBoolean("typeof undetectable == 'undefined'", true); 3283 ExpectBoolean("typeof undetectable == 'string'", false); 3284 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 3285 ExpectBoolean("!undetectable", true); 3286 3287 ExpectObject("true&&undetectable", obj); 3288 ExpectBoolean("false&&undetectable", false); 3289 ExpectBoolean("true||undetectable", true); 3290 ExpectObject("false||undetectable", obj); 3291 3292 ExpectObject("undetectable&&true", obj); 3293 ExpectObject("undetectable&&false", obj); 3294 ExpectBoolean("undetectable||true", true); 3295 ExpectBoolean("undetectable||false", false); 3296 3297 ExpectBoolean("undetectable==null", true); 3298 ExpectBoolean("null==undetectable", true); 3299 ExpectBoolean("undetectable==undefined", true); 3300 ExpectBoolean("undefined==undetectable", true); 3301 ExpectBoolean("undetectable==undetectable", true); 3302 3303 3304 ExpectBoolean("undetectable===null", false); 3305 ExpectBoolean("null===undetectable", false); 3306 ExpectBoolean("undetectable===undefined", false); 3307 ExpectBoolean("undefined===undetectable", false); 3308 ExpectBoolean("undetectable===undetectable", true); 3309} 3310 3311 3312template <typename T> static void USE(T) { } 3313 3314 3315// This test is not intended to be run, just type checked. 3316static void PersistentHandles() { 3317 USE(PersistentHandles); 3318 Local<String> str = v8_str("foo"); 3319 v8::Persistent<String> p_str = v8::Persistent<String>::New(str); 3320 USE(p_str); 3321 Local<Script> scr = Script::Compile(v8_str("")); 3322 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr); 3323 USE(p_scr); 3324 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3325 v8::Persistent<ObjectTemplate> p_templ = 3326 v8::Persistent<ObjectTemplate>::New(templ); 3327 USE(p_templ); 3328} 3329 3330 3331static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) { 3332 ApiTestFuzzer::Fuzz(); 3333 return v8::Undefined(); 3334} 3335 3336 3337THREADED_TEST(GlobalObjectTemplate) { 3338 v8::HandleScope handle_scope; 3339 Local<ObjectTemplate> global_template = ObjectTemplate::New(); 3340 global_template->Set(v8_str("JSNI_Log"), 3341 v8::FunctionTemplate::New(HandleLogDelegator)); 3342 v8::Persistent<Context> context = Context::New(0, global_template); 3343 Context::Scope context_scope(context); 3344 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run(); 3345 context.Dispose(); 3346} 3347 3348 3349static const char* kSimpleExtensionSource = 3350 "function Foo() {" 3351 " return 4;" 3352 "}"; 3353 3354 3355THREADED_TEST(SimpleExtensions) { 3356 v8::HandleScope handle_scope; 3357 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource)); 3358 const char* extension_names[] = { "simpletest" }; 3359 v8::ExtensionConfiguration extensions(1, extension_names); 3360 v8::Handle<Context> context = Context::New(&extensions); 3361 Context::Scope lock(context); 3362 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 3363 CHECK_EQ(result, v8::Integer::New(4)); 3364} 3365 3366 3367static const char* kEvalExtensionSource1 = 3368 "function UseEval1() {" 3369 " var x = 42;" 3370 " return eval('x');" 3371 "}"; 3372 3373 3374static const char* kEvalExtensionSource2 = 3375 "(function() {" 3376 " var x = 42;" 3377 " function e() {" 3378 " return eval('x');" 3379 " }" 3380 " this.UseEval2 = e;" 3381 "})()"; 3382 3383 3384THREADED_TEST(UseEvalFromExtension) { 3385 v8::HandleScope handle_scope; 3386 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1)); 3387 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2)); 3388 const char* extension_names[] = { "evaltest1", "evaltest2" }; 3389 v8::ExtensionConfiguration extensions(2, extension_names); 3390 v8::Handle<Context> context = Context::New(&extensions); 3391 Context::Scope lock(context); 3392 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run(); 3393 CHECK_EQ(result, v8::Integer::New(42)); 3394 result = Script::Compile(v8_str("UseEval2()"))->Run(); 3395 CHECK_EQ(result, v8::Integer::New(42)); 3396} 3397 3398 3399static const char* kWithExtensionSource1 = 3400 "function UseWith1() {" 3401 " var x = 42;" 3402 " with({x:87}) { return x; }" 3403 "}"; 3404 3405 3406 3407static const char* kWithExtensionSource2 = 3408 "(function() {" 3409 " var x = 42;" 3410 " function e() {" 3411 " with ({x:87}) { return x; }" 3412 " }" 3413 " this.UseWith2 = e;" 3414 "})()"; 3415 3416 3417THREADED_TEST(UseWithFromExtension) { 3418 v8::HandleScope handle_scope; 3419 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1)); 3420 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2)); 3421 const char* extension_names[] = { "withtest1", "withtest2" }; 3422 v8::ExtensionConfiguration extensions(2, extension_names); 3423 v8::Handle<Context> context = Context::New(&extensions); 3424 Context::Scope lock(context); 3425 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run(); 3426 CHECK_EQ(result, v8::Integer::New(87)); 3427 result = Script::Compile(v8_str("UseWith2()"))->Run(); 3428 CHECK_EQ(result, v8::Integer::New(87)); 3429} 3430 3431 3432THREADED_TEST(AutoExtensions) { 3433 v8::HandleScope handle_scope; 3434 Extension* extension = new Extension("autotest", kSimpleExtensionSource); 3435 extension->set_auto_enable(true); 3436 v8::RegisterExtension(extension); 3437 v8::Handle<Context> context = Context::New(); 3438 Context::Scope lock(context); 3439 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 3440 CHECK_EQ(result, v8::Integer::New(4)); 3441} 3442 3443 3444static const char* kSyntaxErrorInExtensionSource = 3445 "["; 3446 3447 3448// Test that a syntax error in an extension does not cause a fatal 3449// error but results in an empty context. 3450THREADED_TEST(SyntaxErrorExtensions) { 3451 v8::HandleScope handle_scope; 3452 v8::RegisterExtension(new Extension("syntaxerror", 3453 kSyntaxErrorInExtensionSource)); 3454 const char* extension_names[] = { "syntaxerror" }; 3455 v8::ExtensionConfiguration extensions(1, extension_names); 3456 v8::Handle<Context> context = Context::New(&extensions); 3457 CHECK(context.IsEmpty()); 3458} 3459 3460 3461static const char* kExceptionInExtensionSource = 3462 "throw 42"; 3463 3464 3465// Test that an exception when installing an extension does not cause 3466// a fatal error but results in an empty context. 3467THREADED_TEST(ExceptionExtensions) { 3468 v8::HandleScope handle_scope; 3469 v8::RegisterExtension(new Extension("exception", 3470 kExceptionInExtensionSource)); 3471 const char* extension_names[] = { "exception" }; 3472 v8::ExtensionConfiguration extensions(1, extension_names); 3473 v8::Handle<Context> context = Context::New(&extensions); 3474 CHECK(context.IsEmpty()); 3475} 3476 3477 3478static void CheckDependencies(const char* name, const char* expected) { 3479 v8::HandleScope handle_scope; 3480 v8::ExtensionConfiguration config(1, &name); 3481 LocalContext context(&config); 3482 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded"))); 3483} 3484 3485 3486/* 3487 * Configuration: 3488 * 3489 * /-- B <--\ 3490 * A <- -- D <-- E 3491 * \-- C <--/ 3492 */ 3493THREADED_TEST(ExtensionDependency) { 3494 static const char* kEDeps[] = { "D" }; 3495 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps)); 3496 static const char* kDDeps[] = { "B", "C" }; 3497 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps)); 3498 static const char* kBCDeps[] = { "A" }; 3499 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps)); 3500 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps)); 3501 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';")); 3502 CheckDependencies("A", "undefinedA"); 3503 CheckDependencies("B", "undefinedAB"); 3504 CheckDependencies("C", "undefinedAC"); 3505 CheckDependencies("D", "undefinedABCD"); 3506 CheckDependencies("E", "undefinedABCDE"); 3507 v8::HandleScope handle_scope; 3508 static const char* exts[2] = { "C", "E" }; 3509 v8::ExtensionConfiguration config(2, exts); 3510 LocalContext context(&config); 3511 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded"))); 3512} 3513 3514 3515static const char* kExtensionTestScript = 3516 "native function A();" 3517 "native function B();" 3518 "native function C();" 3519 "function Foo(i) {" 3520 " if (i == 0) return A();" 3521 " if (i == 1) return B();" 3522 " if (i == 2) return C();" 3523 "}"; 3524 3525 3526static v8::Handle<Value> CallFun(const v8::Arguments& args) { 3527 ApiTestFuzzer::Fuzz(); 3528 if (args.IsConstructCall()) { 3529 args.This()->Set(v8_str("data"), args.Data()); 3530 return v8::Null(); 3531 } 3532 return args.Data(); 3533} 3534 3535 3536class FunctionExtension : public Extension { 3537 public: 3538 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { } 3539 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( 3540 v8::Handle<String> name); 3541}; 3542 3543 3544static int lookup_count = 0; 3545v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction( 3546 v8::Handle<String> name) { 3547 lookup_count++; 3548 if (name->Equals(v8_str("A"))) { 3549 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8)); 3550 } else if (name->Equals(v8_str("B"))) { 3551 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7)); 3552 } else if (name->Equals(v8_str("C"))) { 3553 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6)); 3554 } else { 3555 return v8::Handle<v8::FunctionTemplate>(); 3556 } 3557} 3558 3559 3560THREADED_TEST(FunctionLookup) { 3561 v8::RegisterExtension(new FunctionExtension()); 3562 v8::HandleScope handle_scope; 3563 static const char* exts[1] = { "functiontest" }; 3564 v8::ExtensionConfiguration config(1, exts); 3565 LocalContext context(&config); 3566 CHECK_EQ(3, lookup_count); 3567 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run()); 3568 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run()); 3569 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run()); 3570} 3571 3572 3573THREADED_TEST(NativeFunctionConstructCall) { 3574 v8::RegisterExtension(new FunctionExtension()); 3575 v8::HandleScope handle_scope; 3576 static const char* exts[1] = { "functiontest" }; 3577 v8::ExtensionConfiguration config(1, exts); 3578 LocalContext context(&config); 3579 for (int i = 0; i < 10; i++) { 3580 // Run a few times to ensure that allocation of objects doesn't 3581 // change behavior of a constructor function. 3582 CHECK_EQ(v8::Integer::New(8), 3583 Script::Compile(v8_str("(new A()).data"))->Run()); 3584 CHECK_EQ(v8::Integer::New(7), 3585 Script::Compile(v8_str("(new B()).data"))->Run()); 3586 CHECK_EQ(v8::Integer::New(6), 3587 Script::Compile(v8_str("(new C()).data"))->Run()); 3588 } 3589} 3590 3591 3592static const char* last_location; 3593static const char* last_message; 3594void StoringErrorCallback(const char* location, const char* message) { 3595 if (last_location == NULL) { 3596 last_location = location; 3597 last_message = message; 3598 } 3599} 3600 3601 3602// ErrorReporting creates a circular extensions configuration and 3603// tests that the fatal error handler gets called. This renders V8 3604// unusable and therefore this test cannot be run in parallel. 3605TEST(ErrorReporting) { 3606 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 3607 static const char* aDeps[] = { "B" }; 3608 v8::RegisterExtension(new Extension("A", "", 1, aDeps)); 3609 static const char* bDeps[] = { "A" }; 3610 v8::RegisterExtension(new Extension("B", "", 1, bDeps)); 3611 last_location = NULL; 3612 v8::ExtensionConfiguration config(1, bDeps); 3613 v8::Handle<Context> context = Context::New(&config); 3614 CHECK(context.IsEmpty()); 3615 CHECK_NE(last_location, NULL); 3616} 3617 3618 3619static const char* js_code_causing_huge_string_flattening = 3620 "var str = 'X';" 3621 "for (var i = 0; i < 30; i++) {" 3622 " str = str + str;" 3623 "}" 3624 "str.match(/X/);"; 3625 3626 3627void OOMCallback(const char* location, const char* message) { 3628 exit(0); 3629} 3630 3631 3632TEST(RegexpOutOfMemory) { 3633 // Execute a script that causes out of memory when flattening a string. 3634 v8::HandleScope scope; 3635 v8::V8::SetFatalErrorHandler(OOMCallback); 3636 LocalContext context; 3637 Local<Script> script = 3638 Script::Compile(String::New(js_code_causing_huge_string_flattening)); 3639 last_location = NULL; 3640 Local<Value> result = script->Run(); 3641 3642 CHECK(false); // Should not return. 3643} 3644 3645 3646static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message, 3647 v8::Handle<Value> data) { 3648 CHECK_EQ(v8::Undefined(), data); 3649 CHECK(message->GetScriptResourceName()->IsUndefined()); 3650 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName()); 3651 message->GetLineNumber(); 3652 message->GetSourceLine(); 3653} 3654 3655 3656THREADED_TEST(ErrorWithMissingScriptInfo) { 3657 v8::HandleScope scope; 3658 LocalContext context; 3659 v8::V8::AddMessageListener(MissingScriptInfoMessageListener); 3660 Script::Compile(v8_str("throw Error()"))->Run(); 3661 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener); 3662} 3663 3664 3665int global_index = 0; 3666 3667class Snorkel { 3668 public: 3669 Snorkel() { index_ = global_index++; } 3670 int index_; 3671}; 3672 3673class Whammy { 3674 public: 3675 Whammy() { 3676 cursor_ = 0; 3677 } 3678 ~Whammy() { 3679 script_.Dispose(); 3680 } 3681 v8::Handle<Script> getScript() { 3682 if (script_.IsEmpty()) 3683 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo")); 3684 return Local<Script>(*script_); 3685 } 3686 3687 public: 3688 static const int kObjectCount = 256; 3689 int cursor_; 3690 v8::Persistent<v8::Object> objects_[kObjectCount]; 3691 v8::Persistent<Script> script_; 3692}; 3693 3694static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) { 3695 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data); 3696 delete snorkel; 3697 obj.ClearWeak(); 3698} 3699 3700v8::Handle<Value> WhammyPropertyGetter(Local<String> name, 3701 const AccessorInfo& info) { 3702 Whammy* whammy = 3703 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); 3704 3705 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_]; 3706 3707 v8::Handle<v8::Object> obj = v8::Object::New(); 3708 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj); 3709 if (!prev.IsEmpty()) { 3710 prev->Set(v8_str("next"), obj); 3711 prev.MakeWeak(new Snorkel(), &HandleWeakReference); 3712 whammy->objects_[whammy->cursor_].Clear(); 3713 } 3714 whammy->objects_[whammy->cursor_] = global; 3715 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount; 3716 return whammy->getScript()->Run(); 3717} 3718 3719THREADED_TEST(WeakReference) { 3720 v8::HandleScope handle_scope; 3721 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New(); 3722 templ->SetNamedPropertyHandler(WhammyPropertyGetter, 3723 0, 0, 0, 0, 3724 v8::External::New(new Whammy())); 3725 const char* extension_list[] = { "v8/gc" }; 3726 v8::ExtensionConfiguration extensions(1, extension_list); 3727 v8::Persistent<Context> context = Context::New(&extensions); 3728 Context::Scope context_scope(context); 3729 3730 v8::Handle<v8::Object> interceptor = templ->NewInstance(); 3731 context->Global()->Set(v8_str("whammy"), interceptor); 3732 const char* code = 3733 "var last;" 3734 "for (var i = 0; i < 10000; i++) {" 3735 " var obj = whammy.length;" 3736 " if (last) last.next = obj;" 3737 " last = obj;" 3738 "}" 3739 "gc();" 3740 "4"; 3741 v8::Handle<Value> result = CompileRun(code); 3742 CHECK_EQ(4.0, result->NumberValue()); 3743 3744 context.Dispose(); 3745} 3746 3747 3748static bool in_scavenge = false; 3749static int last = -1; 3750 3751static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) { 3752 CHECK_EQ(-1, last); 3753 last = 0; 3754 obj.Dispose(); 3755 obj.Clear(); 3756 in_scavenge = true; 3757 i::Heap::PerformScavenge(); 3758 in_scavenge = false; 3759 *(reinterpret_cast<bool*>(data)) = true; 3760} 3761 3762static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj, 3763 void* data) { 3764 CHECK_EQ(0, last); 3765 last = 1; 3766 *(reinterpret_cast<bool*>(data)) = in_scavenge; 3767 obj.Dispose(); 3768 obj.Clear(); 3769} 3770 3771THREADED_TEST(NoWeakRefCallbacksInScavenge) { 3772 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks. 3773 // Calling callbacks from scavenges is unsafe as objects held by those 3774 // handlers might have become strongly reachable, but scavenge doesn't 3775 // check that. 3776 v8::Persistent<Context> context = Context::New(); 3777 Context::Scope context_scope(context); 3778 3779 v8::Persistent<v8::Object> object_a; 3780 v8::Persistent<v8::Object> object_b; 3781 3782 { 3783 v8::HandleScope handle_scope; 3784 object_b = v8::Persistent<v8::Object>::New(v8::Object::New()); 3785 object_a = v8::Persistent<v8::Object>::New(v8::Object::New()); 3786 } 3787 3788 bool object_a_disposed = false; 3789 object_a.MakeWeak(&object_a_disposed, &ForceScavenge); 3790 bool released_in_scavenge = false; 3791 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge); 3792 3793 while (!object_a_disposed) { 3794 i::Heap::CollectAllGarbage(false); 3795 } 3796 CHECK(!released_in_scavenge); 3797} 3798 3799 3800v8::Handle<Function> args_fun; 3801 3802 3803static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) { 3804 ApiTestFuzzer::Fuzz(); 3805 CHECK_EQ(args_fun, args.Callee()); 3806 CHECK_EQ(3, args.Length()); 3807 CHECK_EQ(v8::Integer::New(1), args[0]); 3808 CHECK_EQ(v8::Integer::New(2), args[1]); 3809 CHECK_EQ(v8::Integer::New(3), args[2]); 3810 CHECK_EQ(v8::Undefined(), args[3]); 3811 v8::HandleScope scope; 3812 i::Heap::CollectAllGarbage(false); 3813 return v8::Undefined(); 3814} 3815 3816 3817THREADED_TEST(Arguments) { 3818 v8::HandleScope scope; 3819 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 3820 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback)); 3821 LocalContext context(NULL, global); 3822 args_fun = context->Global()->Get(v8_str("f")).As<Function>(); 3823 v8_compile("f(1, 2, 3)")->Run(); 3824} 3825 3826 3827static v8::Handle<Value> NoBlockGetterX(Local<String> name, 3828 const AccessorInfo&) { 3829 return v8::Handle<Value>(); 3830} 3831 3832 3833static v8::Handle<Value> NoBlockGetterI(uint32_t index, 3834 const AccessorInfo&) { 3835 return v8::Handle<Value>(); 3836} 3837 3838 3839static v8::Handle<v8::Boolean> PDeleter(Local<String> name, 3840 const AccessorInfo&) { 3841 if (!name->Equals(v8_str("foo"))) { 3842 return v8::Handle<v8::Boolean>(); // not intercepted 3843 } 3844 3845 return v8::False(); // intercepted, and don't delete the property 3846} 3847 3848 3849static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) { 3850 if (index != 2) { 3851 return v8::Handle<v8::Boolean>(); // not intercepted 3852 } 3853 3854 return v8::False(); // intercepted, and don't delete the property 3855} 3856 3857 3858THREADED_TEST(Deleter) { 3859 v8::HandleScope scope; 3860 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 3861 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL); 3862 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL); 3863 LocalContext context; 3864 context->Global()->Set(v8_str("k"), obj->NewInstance()); 3865 CompileRun( 3866 "k.foo = 'foo';" 3867 "k.bar = 'bar';" 3868 "k[2] = 2;" 3869 "k[4] = 4;"); 3870 CHECK(v8_compile("delete k.foo")->Run()->IsFalse()); 3871 CHECK(v8_compile("delete k.bar")->Run()->IsTrue()); 3872 3873 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo")); 3874 CHECK(v8_compile("k.bar")->Run()->IsUndefined()); 3875 3876 CHECK(v8_compile("delete k[2]")->Run()->IsFalse()); 3877 CHECK(v8_compile("delete k[4]")->Run()->IsTrue()); 3878 3879 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2)); 3880 CHECK(v8_compile("k[4]")->Run()->IsUndefined()); 3881} 3882 3883 3884static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) { 3885 ApiTestFuzzer::Fuzz(); 3886 if (name->Equals(v8_str("foo")) || 3887 name->Equals(v8_str("bar")) || 3888 name->Equals(v8_str("baz"))) { 3889 return v8::Undefined(); 3890 } 3891 return v8::Handle<Value>(); 3892} 3893 3894 3895static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) { 3896 ApiTestFuzzer::Fuzz(); 3897 if (index == 0 || index == 1) return v8::Undefined(); 3898 return v8::Handle<Value>(); 3899} 3900 3901 3902static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) { 3903 ApiTestFuzzer::Fuzz(); 3904 v8::Handle<v8::Array> result = v8::Array::New(3); 3905 result->Set(v8::Integer::New(0), v8_str("foo")); 3906 result->Set(v8::Integer::New(1), v8_str("bar")); 3907 result->Set(v8::Integer::New(2), v8_str("baz")); 3908 return result; 3909} 3910 3911 3912static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) { 3913 ApiTestFuzzer::Fuzz(); 3914 v8::Handle<v8::Array> result = v8::Array::New(2); 3915 result->Set(v8::Integer::New(0), v8_str("0")); 3916 result->Set(v8::Integer::New(1), v8_str("1")); 3917 return result; 3918} 3919 3920 3921THREADED_TEST(Enumerators) { 3922 v8::HandleScope scope; 3923 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 3924 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum); 3925 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum); 3926 LocalContext context; 3927 context->Global()->Set(v8_str("k"), obj->NewInstance()); 3928 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 3929 "k[10] = 0;" 3930 "k.a = 0;" 3931 "k[5] = 0;" 3932 "k.b = 0;" 3933 "k[4294967295] = 0;" 3934 "k.c = 0;" 3935 "k[4294967296] = 0;" 3936 "k.d = 0;" 3937 "k[140000] = 0;" 3938 "k.e = 0;" 3939 "k[30000000000] = 0;" 3940 "k.f = 0;" 3941 "var result = [];" 3942 "for (var prop in k) {" 3943 " result.push(prop);" 3944 "}" 3945 "result")); 3946 // Check that we get all the property names returned including the 3947 // ones from the enumerators in the right order: indexed properties 3948 // in numerical order, indexed interceptor properties, named 3949 // properties in insertion order, named interceptor properties. 3950 // This order is not mandated by the spec, so this test is just 3951 // documenting our behavior. 3952 CHECK_EQ(17, result->Length()); 3953 // Indexed properties in numerical order. 3954 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0))); 3955 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1))); 3956 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2))); 3957 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3))); 3958 // Indexed interceptor properties in the order they are returned 3959 // from the enumerator interceptor. 3960 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4))); 3961 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5))); 3962 // Named properties in insertion order. 3963 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6))); 3964 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7))); 3965 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8))); 3966 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9))); 3967 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10))); 3968 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11))); 3969 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12))); 3970 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13))); 3971 // Named interceptor properties. 3972 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14))); 3973 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15))); 3974 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16))); 3975} 3976 3977 3978int p_getter_count; 3979int p_getter_count2; 3980 3981 3982static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) { 3983 ApiTestFuzzer::Fuzz(); 3984 p_getter_count++; 3985 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 3986 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 3987 if (name->Equals(v8_str("p1"))) { 3988 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 3989 } else if (name->Equals(v8_str("p2"))) { 3990 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 3991 } else if (name->Equals(v8_str("p3"))) { 3992 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 3993 } else if (name->Equals(v8_str("p4"))) { 3994 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 3995 } 3996 return v8::Undefined(); 3997} 3998 3999 4000static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) { 4001 ApiTestFuzzer::Fuzz(); 4002 LocalContext context; 4003 context->Global()->Set(v8_str("o1"), obj->NewInstance()); 4004 CompileRun( 4005 "o1.__proto__ = { };" 4006 "var o2 = { __proto__: o1 };" 4007 "var o3 = { __proto__: o2 };" 4008 "var o4 = { __proto__: o3 };" 4009 "for (var i = 0; i < 10; i++) o4.p4;" 4010 "for (var i = 0; i < 10; i++) o3.p3;" 4011 "for (var i = 0; i < 10; i++) o2.p2;" 4012 "for (var i = 0; i < 10; i++) o1.p1;"); 4013} 4014 4015 4016static v8::Handle<Value> PGetter2(Local<String> name, 4017 const AccessorInfo& info) { 4018 ApiTestFuzzer::Fuzz(); 4019 p_getter_count2++; 4020 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 4021 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 4022 if (name->Equals(v8_str("p1"))) { 4023 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 4024 } else if (name->Equals(v8_str("p2"))) { 4025 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 4026 } else if (name->Equals(v8_str("p3"))) { 4027 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 4028 } else if (name->Equals(v8_str("p4"))) { 4029 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 4030 } 4031 return v8::Undefined(); 4032} 4033 4034 4035THREADED_TEST(GetterHolders) { 4036 v8::HandleScope scope; 4037 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4038 obj->SetAccessor(v8_str("p1"), PGetter); 4039 obj->SetAccessor(v8_str("p2"), PGetter); 4040 obj->SetAccessor(v8_str("p3"), PGetter); 4041 obj->SetAccessor(v8_str("p4"), PGetter); 4042 p_getter_count = 0; 4043 RunHolderTest(obj); 4044 CHECK_EQ(40, p_getter_count); 4045} 4046 4047 4048THREADED_TEST(PreInterceptorHolders) { 4049 v8::HandleScope scope; 4050 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4051 obj->SetNamedPropertyHandler(PGetter2); 4052 p_getter_count2 = 0; 4053 RunHolderTest(obj); 4054 CHECK_EQ(40, p_getter_count2); 4055} 4056 4057 4058THREADED_TEST(ObjectInstantiation) { 4059 v8::HandleScope scope; 4060 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 4061 templ->SetAccessor(v8_str("t"), PGetter2); 4062 LocalContext context; 4063 context->Global()->Set(v8_str("o"), templ->NewInstance()); 4064 for (int i = 0; i < 100; i++) { 4065 v8::HandleScope inner_scope; 4066 v8::Handle<v8::Object> obj = templ->NewInstance(); 4067 CHECK_NE(obj, context->Global()->Get(v8_str("o"))); 4068 context->Global()->Set(v8_str("o2"), obj); 4069 v8::Handle<Value> value = 4070 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run(); 4071 CHECK_EQ(v8::True(), value); 4072 context->Global()->Set(v8_str("o"), obj); 4073 } 4074} 4075 4076 4077THREADED_TEST(StringWrite) { 4078 v8::HandleScope scope; 4079 v8::Handle<String> str = v8_str("abcde"); 4080 4081 char buf[100]; 4082 int len; 4083 4084 memset(buf, 0x1, sizeof(buf)); 4085 len = str->WriteAscii(buf); 4086 CHECK_EQ(len, 5); 4087 CHECK_EQ(strncmp("abcde\0", buf, 6), 0); 4088 4089 memset(buf, 0x1, sizeof(buf)); 4090 len = str->WriteAscii(buf, 0, 4); 4091 CHECK_EQ(len, 4); 4092 CHECK_EQ(strncmp("abcd\1", buf, 5), 0); 4093 4094 memset(buf, 0x1, sizeof(buf)); 4095 len = str->WriteAscii(buf, 0, 5); 4096 CHECK_EQ(len, 5); 4097 CHECK_EQ(strncmp("abcde\1", buf, 6), 0); 4098 4099 memset(buf, 0x1, sizeof(buf)); 4100 len = str->WriteAscii(buf, 0, 6); 4101 CHECK_EQ(len, 5); 4102 CHECK_EQ(strncmp("abcde\0", buf, 6), 0); 4103 4104 memset(buf, 0x1, sizeof(buf)); 4105 len = str->WriteAscii(buf, 4, -1); 4106 CHECK_EQ(len, 1); 4107 CHECK_EQ(strncmp("e\0", buf, 2), 0); 4108 4109 memset(buf, 0x1, sizeof(buf)); 4110 len = str->WriteAscii(buf, 4, 6); 4111 CHECK_EQ(len, 1); 4112 CHECK_EQ(strncmp("e\0", buf, 2), 0); 4113 4114 memset(buf, 0x1, sizeof(buf)); 4115 len = str->WriteAscii(buf, 4, 1); 4116 CHECK_EQ(len, 1); 4117 CHECK_EQ(strncmp("e\1", buf, 2), 0); 4118} 4119 4120 4121THREADED_TEST(ToArrayIndex) { 4122 v8::HandleScope scope; 4123 LocalContext context; 4124 4125 v8::Handle<String> str = v8_str("42"); 4126 v8::Handle<v8::Uint32> index = str->ToArrayIndex(); 4127 CHECK(!index.IsEmpty()); 4128 CHECK_EQ(42.0, index->Uint32Value()); 4129 str = v8_str("42asdf"); 4130 index = str->ToArrayIndex(); 4131 CHECK(index.IsEmpty()); 4132 str = v8_str("-42"); 4133 index = str->ToArrayIndex(); 4134 CHECK(index.IsEmpty()); 4135 str = v8_str("4294967295"); 4136 index = str->ToArrayIndex(); 4137 CHECK(!index.IsEmpty()); 4138 CHECK_EQ(4294967295.0, index->Uint32Value()); 4139 v8::Handle<v8::Number> num = v8::Number::New(1); 4140 index = num->ToArrayIndex(); 4141 CHECK(!index.IsEmpty()); 4142 CHECK_EQ(1.0, index->Uint32Value()); 4143 num = v8::Number::New(-1); 4144 index = num->ToArrayIndex(); 4145 CHECK(index.IsEmpty()); 4146 v8::Handle<v8::Object> obj = v8::Object::New(); 4147 index = obj->ToArrayIndex(); 4148 CHECK(index.IsEmpty()); 4149} 4150 4151 4152THREADED_TEST(ErrorConstruction) { 4153 v8::HandleScope scope; 4154 LocalContext context; 4155 4156 v8::Handle<String> foo = v8_str("foo"); 4157 v8::Handle<String> message = v8_str("message"); 4158 v8::Handle<Value> range_error = v8::Exception::RangeError(foo); 4159 CHECK(range_error->IsObject()); 4160 v8::Handle<v8::Object> range_obj = range_error.As<v8::Object>(); 4161 CHECK(range_error.As<v8::Object>()->Get(message)->Equals(foo)); 4162 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo); 4163 CHECK(reference_error->IsObject()); 4164 CHECK(reference_error.As<v8::Object>()->Get(message)->Equals(foo)); 4165 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo); 4166 CHECK(syntax_error->IsObject()); 4167 CHECK(syntax_error.As<v8::Object>()->Get(message)->Equals(foo)); 4168 v8::Handle<Value> type_error = v8::Exception::TypeError(foo); 4169 CHECK(type_error->IsObject()); 4170 CHECK(type_error.As<v8::Object>()->Get(message)->Equals(foo)); 4171 v8::Handle<Value> error = v8::Exception::Error(foo); 4172 CHECK(error->IsObject()); 4173 CHECK(error.As<v8::Object>()->Get(message)->Equals(foo)); 4174} 4175 4176 4177static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) { 4178 ApiTestFuzzer::Fuzz(); 4179 return v8_num(10); 4180} 4181 4182 4183static void YSetter(Local<String> name, 4184 Local<Value> value, 4185 const AccessorInfo& info) { 4186 if (info.This()->Has(name)) { 4187 info.This()->Delete(name); 4188 } 4189 info.This()->Set(name, value); 4190} 4191 4192 4193THREADED_TEST(DeleteAccessor) { 4194 v8::HandleScope scope; 4195 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 4196 obj->SetAccessor(v8_str("y"), YGetter, YSetter); 4197 LocalContext context; 4198 v8::Handle<v8::Object> holder = obj->NewInstance(); 4199 context->Global()->Set(v8_str("holder"), holder); 4200 v8::Handle<Value> result = CompileRun( 4201 "holder.y = 11; holder.y = 12; holder.y"); 4202 CHECK_EQ(12, result->Uint32Value()); 4203} 4204 4205 4206THREADED_TEST(TypeSwitch) { 4207 v8::HandleScope scope; 4208 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New(); 4209 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New(); 4210 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New(); 4211 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 }; 4212 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs); 4213 LocalContext context; 4214 v8::Handle<v8::Object> obj0 = v8::Object::New(); 4215 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance(); 4216 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance(); 4217 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance(); 4218 for (int i = 0; i < 10; i++) { 4219 CHECK_EQ(0, type_switch->match(obj0)); 4220 CHECK_EQ(1, type_switch->match(obj1)); 4221 CHECK_EQ(2, type_switch->match(obj2)); 4222 CHECK_EQ(3, type_switch->match(obj3)); 4223 CHECK_EQ(3, type_switch->match(obj3)); 4224 CHECK_EQ(2, type_switch->match(obj2)); 4225 CHECK_EQ(1, type_switch->match(obj1)); 4226 CHECK_EQ(0, type_switch->match(obj0)); 4227 } 4228} 4229 4230 4231// For use within the TestSecurityHandler() test. 4232static bool g_security_callback_result = false; 4233static bool NamedSecurityTestCallback(Local<v8::Object> global, 4234 Local<Value> name, 4235 v8::AccessType type, 4236 Local<Value> data) { 4237 // Always allow read access. 4238 if (type == v8::ACCESS_GET) 4239 return true; 4240 4241 // Sometimes allow other access. 4242 return g_security_callback_result; 4243} 4244 4245 4246static bool IndexedSecurityTestCallback(Local<v8::Object> global, 4247 uint32_t key, 4248 v8::AccessType type, 4249 Local<Value> data) { 4250 // Always allow read access. 4251 if (type == v8::ACCESS_GET) 4252 return true; 4253 4254 // Sometimes allow other access. 4255 return g_security_callback_result; 4256} 4257 4258 4259static int trouble_nesting = 0; 4260static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) { 4261 ApiTestFuzzer::Fuzz(); 4262 trouble_nesting++; 4263 4264 // Call a JS function that throws an uncaught exception. 4265 Local<v8::Object> arg_this = Context::GetCurrent()->Global(); 4266 Local<Value> trouble_callee = (trouble_nesting == 3) ? 4267 arg_this->Get(v8_str("trouble_callee")) : 4268 arg_this->Get(v8_str("trouble_caller")); 4269 CHECK(trouble_callee->IsFunction()); 4270 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL); 4271} 4272 4273 4274static int report_count = 0; 4275static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>, 4276 v8::Handle<Value>) { 4277 report_count++; 4278} 4279 4280 4281// Counts uncaught exceptions, but other tests running in parallel 4282// also have uncaught exceptions. 4283TEST(ApiUncaughtException) { 4284 report_count = 0; 4285 v8::HandleScope scope; 4286 LocalContext env; 4287 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener); 4288 4289 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 4290 v8::Local<v8::Object> global = env->Global(); 4291 global->Set(v8_str("trouble"), fun->GetFunction()); 4292 4293 Script::Compile(v8_str("function trouble_callee() {" 4294 " var x = null;" 4295 " return x.foo;" 4296 "};" 4297 "function trouble_caller() {" 4298 " trouble();" 4299 "};"))->Run(); 4300 Local<Value> trouble = global->Get(v8_str("trouble")); 4301 CHECK(trouble->IsFunction()); 4302 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee")); 4303 CHECK(trouble_callee->IsFunction()); 4304 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller")); 4305 CHECK(trouble_caller->IsFunction()); 4306 Function::Cast(*trouble_caller)->Call(global, 0, NULL); 4307 CHECK_EQ(1, report_count); 4308 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener); 4309} 4310 4311static const char* script_resource_name = "ExceptionInNativeScript.js"; 4312static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message, 4313 v8::Handle<Value>) { 4314 v8::Handle<v8::Value> name_val = message->GetScriptResourceName(); 4315 CHECK(!name_val.IsEmpty() && name_val->IsString()); 4316 v8::String::AsciiValue name(message->GetScriptResourceName()); 4317 CHECK_EQ(script_resource_name, *name); 4318 CHECK_EQ(3, message->GetLineNumber()); 4319 v8::String::AsciiValue source_line(message->GetSourceLine()); 4320 CHECK_EQ(" new o.foo();", *source_line); 4321} 4322 4323TEST(ExceptionInNativeScript) { 4324 v8::HandleScope scope; 4325 LocalContext env; 4326 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener); 4327 4328 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 4329 v8::Local<v8::Object> global = env->Global(); 4330 global->Set(v8_str("trouble"), fun->GetFunction()); 4331 4332 Script::Compile(v8_str("function trouble() {\n" 4333 " var o = {};\n" 4334 " new o.foo();\n" 4335 "};"), v8::String::New(script_resource_name))->Run(); 4336 Local<Value> trouble = global->Get(v8_str("trouble")); 4337 CHECK(trouble->IsFunction()); 4338 Function::Cast(*trouble)->Call(global, 0, NULL); 4339 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener); 4340} 4341 4342 4343TEST(CompilationErrorUsingTryCatchHandler) { 4344 v8::HandleScope scope; 4345 LocalContext env; 4346 v8::TryCatch try_catch; 4347 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile.")); 4348 CHECK_NE(NULL, *try_catch.Exception()); 4349 CHECK(try_catch.HasCaught()); 4350} 4351 4352 4353TEST(TryCatchFinallyUsingTryCatchHandler) { 4354 v8::HandleScope scope; 4355 LocalContext env; 4356 v8::TryCatch try_catch; 4357 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run(); 4358 CHECK(!try_catch.HasCaught()); 4359 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run(); 4360 CHECK(try_catch.HasCaught()); 4361 try_catch.Reset(); 4362 Script::Compile(v8_str("(function() {" 4363 "try { throw ''; } finally { return; }" 4364 "})()"))->Run(); 4365 CHECK(!try_catch.HasCaught()); 4366 Script::Compile(v8_str("(function()" 4367 " { try { throw ''; } finally { throw 0; }" 4368 "})()"))->Run(); 4369 CHECK(try_catch.HasCaught()); 4370} 4371 4372 4373// SecurityHandler can't be run twice 4374TEST(SecurityHandler) { 4375 v8::HandleScope scope0; 4376 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 4377 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback, 4378 IndexedSecurityTestCallback); 4379 // Create an environment 4380 v8::Persistent<Context> context0 = 4381 Context::New(NULL, global_template); 4382 context0->Enter(); 4383 4384 v8::Handle<v8::Object> global0 = context0->Global(); 4385 v8::Handle<Script> script0 = v8_compile("foo = 111"); 4386 script0->Run(); 4387 global0->Set(v8_str("0"), v8_num(999)); 4388 v8::Handle<Value> foo0 = global0->Get(v8_str("foo")); 4389 CHECK_EQ(111, foo0->Int32Value()); 4390 v8::Handle<Value> z0 = global0->Get(v8_str("0")); 4391 CHECK_EQ(999, z0->Int32Value()); 4392 4393 // Create another environment, should fail security checks. 4394 v8::HandleScope scope1; 4395 4396 v8::Persistent<Context> context1 = 4397 Context::New(NULL, global_template); 4398 context1->Enter(); 4399 4400 v8::Handle<v8::Object> global1 = context1->Global(); 4401 global1->Set(v8_str("othercontext"), global0); 4402 // This set will fail the security check. 4403 v8::Handle<Script> script1 = 4404 v8_compile("othercontext.foo = 222; othercontext[0] = 888;"); 4405 script1->Run(); 4406 // This read will pass the security check. 4407 v8::Handle<Value> foo1 = global0->Get(v8_str("foo")); 4408 CHECK_EQ(111, foo1->Int32Value()); 4409 // This read will pass the security check. 4410 v8::Handle<Value> z1 = global0->Get(v8_str("0")); 4411 CHECK_EQ(999, z1->Int32Value()); 4412 4413 // Create another environment, should pass security checks. 4414 { g_security_callback_result = true; // allow security handler to pass. 4415 v8::HandleScope scope2; 4416 LocalContext context2; 4417 v8::Handle<v8::Object> global2 = context2->Global(); 4418 global2->Set(v8_str("othercontext"), global0); 4419 v8::Handle<Script> script2 = 4420 v8_compile("othercontext.foo = 333; othercontext[0] = 888;"); 4421 script2->Run(); 4422 v8::Handle<Value> foo2 = global0->Get(v8_str("foo")); 4423 CHECK_EQ(333, foo2->Int32Value()); 4424 v8::Handle<Value> z2 = global0->Get(v8_str("0")); 4425 CHECK_EQ(888, z2->Int32Value()); 4426 } 4427 4428 context1->Exit(); 4429 context1.Dispose(); 4430 4431 context0->Exit(); 4432 context0.Dispose(); 4433} 4434 4435 4436THREADED_TEST(SecurityChecks) { 4437 v8::HandleScope handle_scope; 4438 LocalContext env1; 4439 v8::Persistent<Context> env2 = Context::New(); 4440 4441 Local<Value> foo = v8_str("foo"); 4442 Local<Value> bar = v8_str("bar"); 4443 4444 // Set to the same domain. 4445 env1->SetSecurityToken(foo); 4446 4447 // Create a function in env1. 4448 Script::Compile(v8_str("spy=function(){return spy;}"))->Run(); 4449 Local<Value> spy = env1->Global()->Get(v8_str("spy")); 4450 CHECK(spy->IsFunction()); 4451 4452 // Create another function accessing global objects. 4453 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run(); 4454 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2")); 4455 CHECK(spy2->IsFunction()); 4456 4457 // Switch to env2 in the same domain and invoke spy on env2. 4458 { 4459 env2->SetSecurityToken(foo); 4460 // Enter env2 4461 Context::Scope scope_env2(env2); 4462 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL); 4463 CHECK(result->IsFunction()); 4464 } 4465 4466 { 4467 env2->SetSecurityToken(bar); 4468 Context::Scope scope_env2(env2); 4469 4470 // Call cross_domain_call, it should throw an exception 4471 v8::TryCatch try_catch; 4472 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL); 4473 CHECK(try_catch.HasCaught()); 4474 } 4475 4476 env2.Dispose(); 4477} 4478 4479 4480// Regression test case for issue 1183439. 4481THREADED_TEST(SecurityChecksForPrototypeChain) { 4482 v8::HandleScope scope; 4483 LocalContext current; 4484 v8::Persistent<Context> other = Context::New(); 4485 4486 // Change context to be able to get to the Object function in the 4487 // other context without hitting the security checks. 4488 v8::Local<Value> other_object; 4489 { Context::Scope scope(other); 4490 other_object = other->Global()->Get(v8_str("Object")); 4491 other->Global()->Set(v8_num(42), v8_num(87)); 4492 } 4493 4494 current->Global()->Set(v8_str("other"), other->Global()); 4495 CHECK(v8_compile("other")->Run()->Equals(other->Global())); 4496 4497 // Make sure the security check fails here and we get an undefined 4498 // result instead of getting the Object function. Repeat in a loop 4499 // to make sure to exercise the IC code. 4500 v8::Local<Script> access_other0 = v8_compile("other.Object"); 4501 v8::Local<Script> access_other1 = v8_compile("other[42]"); 4502 for (int i = 0; i < 5; i++) { 4503 CHECK(!access_other0->Run()->Equals(other_object)); 4504 CHECK(access_other0->Run()->IsUndefined()); 4505 CHECK(!access_other1->Run()->Equals(v8_num(87))); 4506 CHECK(access_other1->Run()->IsUndefined()); 4507 } 4508 4509 // Create an object that has 'other' in its prototype chain and make 4510 // sure we cannot access the Object function indirectly through 4511 // that. Repeat in a loop to make sure to exercise the IC code. 4512 v8_compile("function F() { };" 4513 "F.prototype = other;" 4514 "var f = new F();")->Run(); 4515 v8::Local<Script> access_f0 = v8_compile("f.Object"); 4516 v8::Local<Script> access_f1 = v8_compile("f[42]"); 4517 for (int j = 0; j < 5; j++) { 4518 CHECK(!access_f0->Run()->Equals(other_object)); 4519 CHECK(access_f0->Run()->IsUndefined()); 4520 CHECK(!access_f1->Run()->Equals(v8_num(87))); 4521 CHECK(access_f1->Run()->IsUndefined()); 4522 } 4523 4524 // Now it gets hairy: Set the prototype for the other global object 4525 // to be the current global object. The prototype chain for 'f' now 4526 // goes through 'other' but ends up in the current global object. 4527 { Context::Scope scope(other); 4528 other->Global()->Set(v8_str("__proto__"), current->Global()); 4529 } 4530 // Set a named and an index property on the current global 4531 // object. To force the lookup to go through the other global object, 4532 // the properties must not exist in the other global object. 4533 current->Global()->Set(v8_str("foo"), v8_num(100)); 4534 current->Global()->Set(v8_num(99), v8_num(101)); 4535 // Try to read the properties from f and make sure that the access 4536 // gets stopped by the security checks on the other global object. 4537 Local<Script> access_f2 = v8_compile("f.foo"); 4538 Local<Script> access_f3 = v8_compile("f[99]"); 4539 for (int k = 0; k < 5; k++) { 4540 CHECK(!access_f2->Run()->Equals(v8_num(100))); 4541 CHECK(access_f2->Run()->IsUndefined()); 4542 CHECK(!access_f3->Run()->Equals(v8_num(101))); 4543 CHECK(access_f3->Run()->IsUndefined()); 4544 } 4545 other.Dispose(); 4546} 4547 4548 4549THREADED_TEST(CrossDomainDelete) { 4550 v8::HandleScope handle_scope; 4551 LocalContext env1; 4552 v8::Persistent<Context> env2 = Context::New(); 4553 4554 Local<Value> foo = v8_str("foo"); 4555 Local<Value> bar = v8_str("bar"); 4556 4557 // Set to the same domain. 4558 env1->SetSecurityToken(foo); 4559 env2->SetSecurityToken(foo); 4560 4561 env1->Global()->Set(v8_str("prop"), v8_num(3)); 4562 env2->Global()->Set(v8_str("env1"), env1->Global()); 4563 4564 // Change env2 to a different domain and delete env1.prop. 4565 env2->SetSecurityToken(bar); 4566 { 4567 Context::Scope scope_env2(env2); 4568 Local<Value> result = 4569 Script::Compile(v8_str("delete env1.prop"))->Run(); 4570 CHECK(result->IsFalse()); 4571 } 4572 4573 // Check that env1.prop still exists. 4574 Local<Value> v = env1->Global()->Get(v8_str("prop")); 4575 CHECK(v->IsNumber()); 4576 CHECK_EQ(3, v->Int32Value()); 4577 4578 env2.Dispose(); 4579} 4580 4581 4582THREADED_TEST(CrossDomainIsPropertyEnumerable) { 4583 v8::HandleScope handle_scope; 4584 LocalContext env1; 4585 v8::Persistent<Context> env2 = Context::New(); 4586 4587 Local<Value> foo = v8_str("foo"); 4588 Local<Value> bar = v8_str("bar"); 4589 4590 // Set to the same domain. 4591 env1->SetSecurityToken(foo); 4592 env2->SetSecurityToken(foo); 4593 4594 env1->Global()->Set(v8_str("prop"), v8_num(3)); 4595 env2->Global()->Set(v8_str("env1"), env1->Global()); 4596 4597 // env1.prop is enumerable in env2. 4598 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')"); 4599 { 4600 Context::Scope scope_env2(env2); 4601 Local<Value> result = Script::Compile(test)->Run(); 4602 CHECK(result->IsTrue()); 4603 } 4604 4605 // Change env2 to a different domain and test again. 4606 env2->SetSecurityToken(bar); 4607 { 4608 Context::Scope scope_env2(env2); 4609 Local<Value> result = Script::Compile(test)->Run(); 4610 CHECK(result->IsFalse()); 4611 } 4612 4613 env2.Dispose(); 4614} 4615 4616 4617THREADED_TEST(CrossDomainForIn) { 4618 v8::HandleScope handle_scope; 4619 LocalContext env1; 4620 v8::Persistent<Context> env2 = Context::New(); 4621 4622 Local<Value> foo = v8_str("foo"); 4623 Local<Value> bar = v8_str("bar"); 4624 4625 // Set to the same domain. 4626 env1->SetSecurityToken(foo); 4627 env2->SetSecurityToken(foo); 4628 4629 env1->Global()->Set(v8_str("prop"), v8_num(3)); 4630 env2->Global()->Set(v8_str("env1"), env1->Global()); 4631 4632 // Change env2 to a different domain and set env1's global object 4633 // as the __proto__ of an object in env2 and enumerate properties 4634 // in for-in. It shouldn't enumerate properties on env1's global 4635 // object. 4636 env2->SetSecurityToken(bar); 4637 { 4638 Context::Scope scope_env2(env2); 4639 Local<Value> result = 4640 CompileRun("(function(){var obj = {'__proto__':env1};" 4641 "for (var p in obj)" 4642 " if (p == 'prop') return false;" 4643 "return true;})()"); 4644 CHECK(result->IsTrue()); 4645 } 4646 env2.Dispose(); 4647} 4648 4649 4650TEST(ContextDetachGlobal) { 4651 v8::HandleScope handle_scope; 4652 LocalContext env1; 4653 v8::Persistent<Context> env2 = Context::New(); 4654 4655 Local<v8::Object> global1 = env1->Global(); 4656 4657 Local<Value> foo = v8_str("foo"); 4658 4659 // Set to the same domain. 4660 env1->SetSecurityToken(foo); 4661 env2->SetSecurityToken(foo); 4662 4663 // Enter env2 4664 env2->Enter(); 4665 4666 // Create a function in env2 and add a reference to it in env1. 4667 Local<v8::Object> global2 = env2->Global(); 4668 global2->Set(v8_str("prop"), v8::Integer::New(1)); 4669 CompileRun("function getProp() {return prop;}"); 4670 4671 env1->Global()->Set(v8_str("getProp"), 4672 global2->Get(v8_str("getProp"))); 4673 4674 // Detach env2's global, and reuse the global object of env2 4675 env2->Exit(); 4676 env2->DetachGlobal(); 4677 // env2 has a new global object. 4678 CHECK(!env2->Global()->Equals(global2)); 4679 4680 v8::Persistent<Context> env3 = 4681 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 4682 env3->SetSecurityToken(v8_str("bar")); 4683 env3->Enter(); 4684 4685 Local<v8::Object> global3 = env3->Global(); 4686 CHECK_EQ(global2, global3); 4687 CHECK(global3->Get(v8_str("prop"))->IsUndefined()); 4688 CHECK(global3->Get(v8_str("getProp"))->IsUndefined()); 4689 global3->Set(v8_str("prop"), v8::Integer::New(-1)); 4690 global3->Set(v8_str("prop2"), v8::Integer::New(2)); 4691 env3->Exit(); 4692 4693 // Call getProp in env1, and it should return the value 1 4694 { 4695 Local<Value> get_prop = global1->Get(v8_str("getProp")); 4696 CHECK(get_prop->IsFunction()); 4697 v8::TryCatch try_catch; 4698 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL); 4699 CHECK(!try_catch.HasCaught()); 4700 CHECK_EQ(1, r->Int32Value()); 4701 } 4702 4703 // Check that env3 is not accessible from env1 4704 { 4705 Local<Value> r = global3->Get(v8_str("prop2")); 4706 CHECK(r->IsUndefined()); 4707 } 4708 4709 env2.Dispose(); 4710 env3.Dispose(); 4711} 4712 4713 4714TEST(DetachAndReattachGlobal) { 4715 v8::HandleScope scope; 4716 LocalContext env1; 4717 4718 // Create second environment. 4719 v8::Persistent<Context> env2 = Context::New(); 4720 4721 Local<Value> foo = v8_str("foo"); 4722 4723 // Set same security token for env1 and env2. 4724 env1->SetSecurityToken(foo); 4725 env2->SetSecurityToken(foo); 4726 4727 // Create a property on the global object in env2. 4728 { 4729 v8::Context::Scope scope(env2); 4730 env2->Global()->Set(v8_str("p"), v8::Integer::New(42)); 4731 } 4732 4733 // Create a reference to env2 global from env1 global. 4734 env1->Global()->Set(v8_str("other"), env2->Global()); 4735 4736 // Check that we have access to other.p in env2 from env1. 4737 Local<Value> result = CompileRun("other.p"); 4738 CHECK(result->IsInt32()); 4739 CHECK_EQ(42, result->Int32Value()); 4740 4741 // Hold on to global from env2 and detach global from env2. 4742 Local<v8::Object> global2 = env2->Global(); 4743 env2->DetachGlobal(); 4744 4745 // Check that the global has been detached. No other.p property can 4746 // be found. 4747 result = CompileRun("other.p"); 4748 CHECK(result->IsUndefined()); 4749 4750 // Reuse global2 for env3. 4751 v8::Persistent<Context> env3 = 4752 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 4753 CHECK_EQ(global2, env3->Global()); 4754 4755 // Start by using the same security token for env3 as for env1 and env2. 4756 env3->SetSecurityToken(foo); 4757 4758 // Create a property on the global object in env3. 4759 { 4760 v8::Context::Scope scope(env3); 4761 env3->Global()->Set(v8_str("p"), v8::Integer::New(24)); 4762 } 4763 4764 // Check that other.p is now the property in env3 and that we have access. 4765 result = CompileRun("other.p"); 4766 CHECK(result->IsInt32()); 4767 CHECK_EQ(24, result->Int32Value()); 4768 4769 // Change security token for env3 to something different from env1 and env2. 4770 env3->SetSecurityToken(v8_str("bar")); 4771 4772 // Check that we do not have access to other.p in env1. |other| is now 4773 // the global object for env3 which has a different security token, 4774 // so access should be blocked. 4775 result = CompileRun("other.p"); 4776 CHECK(result->IsUndefined()); 4777 4778 // Detach the global for env3 and reattach it to env2. 4779 env3->DetachGlobal(); 4780 env2->ReattachGlobal(global2); 4781 4782 // Check that we have access to other.p again in env1. |other| is now 4783 // the global object for env2 which has the same security token as env1. 4784 result = CompileRun("other.p"); 4785 CHECK(result->IsInt32()); 4786 CHECK_EQ(42, result->Int32Value()); 4787 4788 env2.Dispose(); 4789 env3.Dispose(); 4790} 4791 4792 4793static bool NamedAccessBlocker(Local<v8::Object> global, 4794 Local<Value> name, 4795 v8::AccessType type, 4796 Local<Value> data) { 4797 return Context::GetCurrent()->Global()->Equals(global); 4798} 4799 4800 4801static bool IndexedAccessBlocker(Local<v8::Object> global, 4802 uint32_t key, 4803 v8::AccessType type, 4804 Local<Value> data) { 4805 return Context::GetCurrent()->Global()->Equals(global); 4806} 4807 4808 4809static int g_echo_value = -1; 4810static v8::Handle<Value> EchoGetter(Local<String> name, 4811 const AccessorInfo& info) { 4812 return v8_num(g_echo_value); 4813} 4814 4815 4816static void EchoSetter(Local<String> name, 4817 Local<Value> value, 4818 const AccessorInfo&) { 4819 if (value->IsNumber()) 4820 g_echo_value = value->Int32Value(); 4821} 4822 4823 4824static v8::Handle<Value> UnreachableGetter(Local<String> name, 4825 const AccessorInfo& info) { 4826 CHECK(false); // This function should not be called.. 4827 return v8::Undefined(); 4828} 4829 4830 4831static void UnreachableSetter(Local<String>, Local<Value>, 4832 const AccessorInfo&) { 4833 CHECK(false); // This function should nto be called. 4834} 4835 4836 4837THREADED_TEST(AccessControl) { 4838 v8::HandleScope handle_scope; 4839 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 4840 4841 global_template->SetAccessCheckCallbacks(NamedAccessBlocker, 4842 IndexedAccessBlocker); 4843 4844 // Add an accessor accessible by cross-domain JS code. 4845 global_template->SetAccessor( 4846 v8_str("accessible_prop"), 4847 EchoGetter, EchoSetter, 4848 v8::Handle<Value>(), 4849 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); 4850 4851 // Add an accessor that is not accessible by cross-domain JS code. 4852 global_template->SetAccessor(v8_str("blocked_prop"), 4853 UnreachableGetter, UnreachableSetter, 4854 v8::Handle<Value>(), 4855 v8::DEFAULT); 4856 4857 // Create an environment 4858 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 4859 context0->Enter(); 4860 4861 v8::Handle<v8::Object> global0 = context0->Global(); 4862 4863 v8::HandleScope scope1; 4864 4865 v8::Persistent<Context> context1 = Context::New(); 4866 context1->Enter(); 4867 4868 v8::Handle<v8::Object> global1 = context1->Global(); 4869 global1->Set(v8_str("other"), global0); 4870 4871 v8::Handle<Value> value; 4872 4873 // Access blocked property 4874 value = v8_compile("other.blocked_prop = 1")->Run(); 4875 value = v8_compile("other.blocked_prop")->Run(); 4876 CHECK(value->IsUndefined()); 4877 4878 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run(); 4879 CHECK(value->IsFalse()); 4880 4881 // Access accessible property 4882 value = v8_compile("other.accessible_prop = 3")->Run(); 4883 CHECK(value->IsNumber()); 4884 CHECK_EQ(3, value->Int32Value()); 4885 CHECK_EQ(3, g_echo_value); 4886 4887 value = v8_compile("other.accessible_prop")->Run(); 4888 CHECK(value->IsNumber()); 4889 CHECK_EQ(3, value->Int32Value()); 4890 4891 value = 4892 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run(); 4893 CHECK(value->IsTrue()); 4894 4895 // Enumeration doesn't enumerate accessors from inaccessible objects in 4896 // the prototype chain even if the accessors are in themselves accessible. 4897 Local<Value> result = 4898 CompileRun("(function(){var obj = {'__proto__':other};" 4899 "for (var p in obj)" 4900 " if (p == 'accessible_prop' || p == 'blocked_prop') {" 4901 " return false;" 4902 " }" 4903 "return true;})()"); 4904 CHECK(result->IsTrue()); 4905 4906 context1->Exit(); 4907 context0->Exit(); 4908 context1.Dispose(); 4909 context0.Dispose(); 4910} 4911 4912 4913static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global, 4914 Local<Value> name, 4915 v8::AccessType type, 4916 Local<Value> data) { 4917 return false; 4918} 4919 4920 4921static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global, 4922 uint32_t key, 4923 v8::AccessType type, 4924 Local<Value> data) { 4925 return false; 4926} 4927 4928 4929THREADED_TEST(AccessControlGetOwnPropertyNames) { 4930 v8::HandleScope handle_scope; 4931 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(); 4932 4933 obj_template->Set(v8_str("x"), v8::Integer::New(42)); 4934 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker, 4935 GetOwnPropertyNamesIndexedBlocker); 4936 4937 // Create an environment 4938 v8::Persistent<Context> context0 = Context::New(NULL, obj_template); 4939 context0->Enter(); 4940 4941 v8::Handle<v8::Object> global0 = context0->Global(); 4942 4943 v8::HandleScope scope1; 4944 4945 v8::Persistent<Context> context1 = Context::New(); 4946 context1->Enter(); 4947 4948 v8::Handle<v8::Object> global1 = context1->Global(); 4949 global1->Set(v8_str("other"), global0); 4950 global1->Set(v8_str("object"), obj_template->NewInstance()); 4951 4952 v8::Handle<Value> value; 4953 4954 // Attempt to get the property names of the other global object and 4955 // of an object that requires access checks. Accessing the other 4956 // global object should be blocked by access checks on the global 4957 // proxy object. Accessing the object that requires access checks 4958 // is blocked by the access checks on the object itself. 4959 value = CompileRun("Object.getOwnPropertyNames(other).length == 0"); 4960 CHECK(value->IsTrue()); 4961 4962 value = CompileRun("Object.getOwnPropertyNames(object).length == 0"); 4963 CHECK(value->IsTrue()); 4964 4965 context1->Exit(); 4966 context0->Exit(); 4967 context1.Dispose(); 4968 context0.Dispose(); 4969} 4970 4971 4972static v8::Handle<Value> ConstTenGetter(Local<String> name, 4973 const AccessorInfo& info) { 4974 return v8_num(10); 4975} 4976 4977 4978THREADED_TEST(CrossDomainAccessors) { 4979 v8::HandleScope handle_scope; 4980 4981 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(); 4982 4983 v8::Handle<v8::ObjectTemplate> global_template = 4984 func_template->InstanceTemplate(); 4985 4986 v8::Handle<v8::ObjectTemplate> proto_template = 4987 func_template->PrototypeTemplate(); 4988 4989 // Add an accessor to proto that's accessible by cross-domain JS code. 4990 proto_template->SetAccessor(v8_str("accessible"), 4991 ConstTenGetter, 0, 4992 v8::Handle<Value>(), 4993 v8::ALL_CAN_READ); 4994 4995 // Add an accessor that is not accessible by cross-domain JS code. 4996 global_template->SetAccessor(v8_str("unreachable"), 4997 UnreachableGetter, 0, 4998 v8::Handle<Value>(), 4999 v8::DEFAULT); 5000 5001 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 5002 context0->Enter(); 5003 5004 Local<v8::Object> global = context0->Global(); 5005 // Add a normal property that shadows 'accessible' 5006 global->Set(v8_str("accessible"), v8_num(11)); 5007 5008 // Enter a new context. 5009 v8::HandleScope scope1; 5010 v8::Persistent<Context> context1 = Context::New(); 5011 context1->Enter(); 5012 5013 v8::Handle<v8::Object> global1 = context1->Global(); 5014 global1->Set(v8_str("other"), global); 5015 5016 // Should return 10, instead of 11 5017 v8::Handle<Value> value = v8_compile("other.accessible")->Run(); 5018 CHECK(value->IsNumber()); 5019 CHECK_EQ(10, value->Int32Value()); 5020 5021 value = v8_compile("other.unreachable")->Run(); 5022 CHECK(value->IsUndefined()); 5023 5024 context1->Exit(); 5025 context0->Exit(); 5026 context1.Dispose(); 5027 context0.Dispose(); 5028} 5029 5030 5031static int named_access_count = 0; 5032static int indexed_access_count = 0; 5033 5034static bool NamedAccessCounter(Local<v8::Object> global, 5035 Local<Value> name, 5036 v8::AccessType type, 5037 Local<Value> data) { 5038 named_access_count++; 5039 return true; 5040} 5041 5042 5043static bool IndexedAccessCounter(Local<v8::Object> global, 5044 uint32_t key, 5045 v8::AccessType type, 5046 Local<Value> data) { 5047 indexed_access_count++; 5048 return true; 5049} 5050 5051 5052// This one is too easily disturbed by other tests. 5053TEST(AccessControlIC) { 5054 named_access_count = 0; 5055 indexed_access_count = 0; 5056 5057 v8::HandleScope handle_scope; 5058 5059 // Create an environment. 5060 v8::Persistent<Context> context0 = Context::New(); 5061 context0->Enter(); 5062 5063 // Create an object that requires access-check functions to be 5064 // called for cross-domain access. 5065 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 5066 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 5067 IndexedAccessCounter); 5068 Local<v8::Object> object = object_template->NewInstance(); 5069 5070 v8::HandleScope scope1; 5071 5072 // Create another environment. 5073 v8::Persistent<Context> context1 = Context::New(); 5074 context1->Enter(); 5075 5076 // Make easy access to the object from the other environment. 5077 v8::Handle<v8::Object> global1 = context1->Global(); 5078 global1->Set(v8_str("obj"), object); 5079 5080 v8::Handle<Value> value; 5081 5082 // Check that the named access-control function is called every time. 5083 CompileRun("function testProp(obj) {" 5084 " for (var i = 0; i < 10; i++) obj.prop = 1;" 5085 " for (var j = 0; j < 10; j++) obj.prop;" 5086 " return obj.prop" 5087 "}"); 5088 value = CompileRun("testProp(obj)"); 5089 CHECK(value->IsNumber()); 5090 CHECK_EQ(1, value->Int32Value()); 5091 CHECK_EQ(21, named_access_count); 5092 5093 // Check that the named access-control function is called every time. 5094 CompileRun("var p = 'prop';" 5095 "function testKeyed(obj) {" 5096 " for (var i = 0; i < 10; i++) obj[p] = 1;" 5097 " for (var j = 0; j < 10; j++) obj[p];" 5098 " return obj[p];" 5099 "}"); 5100 // Use obj which requires access checks. No inline caching is used 5101 // in that case. 5102 value = CompileRun("testKeyed(obj)"); 5103 CHECK(value->IsNumber()); 5104 CHECK_EQ(1, value->Int32Value()); 5105 CHECK_EQ(42, named_access_count); 5106 // Force the inline caches into generic state and try again. 5107 CompileRun("testKeyed({ a: 0 })"); 5108 CompileRun("testKeyed({ b: 0 })"); 5109 value = CompileRun("testKeyed(obj)"); 5110 CHECK(value->IsNumber()); 5111 CHECK_EQ(1, value->Int32Value()); 5112 CHECK_EQ(63, named_access_count); 5113 5114 // Check that the indexed access-control function is called every time. 5115 CompileRun("function testIndexed(obj) {" 5116 " for (var i = 0; i < 10; i++) obj[0] = 1;" 5117 " for (var j = 0; j < 10; j++) obj[0];" 5118 " return obj[0]" 5119 "}"); 5120 value = CompileRun("testIndexed(obj)"); 5121 CHECK(value->IsNumber()); 5122 CHECK_EQ(1, value->Int32Value()); 5123 CHECK_EQ(21, indexed_access_count); 5124 // Force the inline caches into generic state. 5125 CompileRun("testIndexed(new Array(1))"); 5126 // Test that the indexed access check is called. 5127 value = CompileRun("testIndexed(obj)"); 5128 CHECK(value->IsNumber()); 5129 CHECK_EQ(1, value->Int32Value()); 5130 CHECK_EQ(42, indexed_access_count); 5131 5132 // Check that the named access check is called when invoking 5133 // functions on an object that requires access checks. 5134 CompileRun("obj.f = function() {}"); 5135 CompileRun("function testCallNormal(obj) {" 5136 " for (var i = 0; i < 10; i++) obj.f();" 5137 "}"); 5138 CompileRun("testCallNormal(obj)"); 5139 CHECK_EQ(74, named_access_count); 5140 5141 // Force obj into slow case. 5142 value = CompileRun("delete obj.prop"); 5143 CHECK(value->BooleanValue()); 5144 // Force inline caches into dictionary probing mode. 5145 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);"); 5146 // Test that the named access check is called. 5147 value = CompileRun("testProp(obj);"); 5148 CHECK(value->IsNumber()); 5149 CHECK_EQ(1, value->Int32Value()); 5150 CHECK_EQ(96, named_access_count); 5151 5152 // Force the call inline cache into dictionary probing mode. 5153 CompileRun("o.f = function() {}; testCallNormal(o)"); 5154 // Test that the named access check is still called for each 5155 // invocation of the function. 5156 value = CompileRun("testCallNormal(obj)"); 5157 CHECK_EQ(106, named_access_count); 5158 5159 context1->Exit(); 5160 context0->Exit(); 5161 context1.Dispose(); 5162 context0.Dispose(); 5163} 5164 5165 5166static bool NamedAccessFlatten(Local<v8::Object> global, 5167 Local<Value> name, 5168 v8::AccessType type, 5169 Local<Value> data) { 5170 char buf[100]; 5171 int len; 5172 5173 CHECK(name->IsString()); 5174 5175 memset(buf, 0x1, sizeof(buf)); 5176 len = name.As<String>()->WriteAscii(buf); 5177 CHECK_EQ(4, len); 5178 5179 uint16_t buf2[100]; 5180 5181 memset(buf, 0x1, sizeof(buf)); 5182 len = name.As<String>()->Write(buf2); 5183 CHECK_EQ(4, len); 5184 5185 return true; 5186} 5187 5188 5189static bool IndexedAccessFlatten(Local<v8::Object> global, 5190 uint32_t key, 5191 v8::AccessType type, 5192 Local<Value> data) { 5193 return true; 5194} 5195 5196 5197// Regression test. In access checks, operations that may cause 5198// garbage collection are not allowed. It used to be the case that 5199// using the Write operation on a string could cause a garbage 5200// collection due to flattening of the string. This is no longer the 5201// case. 5202THREADED_TEST(AccessControlFlatten) { 5203 named_access_count = 0; 5204 indexed_access_count = 0; 5205 5206 v8::HandleScope handle_scope; 5207 5208 // Create an environment. 5209 v8::Persistent<Context> context0 = Context::New(); 5210 context0->Enter(); 5211 5212 // Create an object that requires access-check functions to be 5213 // called for cross-domain access. 5214 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 5215 object_template->SetAccessCheckCallbacks(NamedAccessFlatten, 5216 IndexedAccessFlatten); 5217 Local<v8::Object> object = object_template->NewInstance(); 5218 5219 v8::HandleScope scope1; 5220 5221 // Create another environment. 5222 v8::Persistent<Context> context1 = Context::New(); 5223 context1->Enter(); 5224 5225 // Make easy access to the object from the other environment. 5226 v8::Handle<v8::Object> global1 = context1->Global(); 5227 global1->Set(v8_str("obj"), object); 5228 5229 v8::Handle<Value> value; 5230 5231 value = v8_compile("var p = 'as' + 'df';")->Run(); 5232 value = v8_compile("obj[p];")->Run(); 5233 5234 context1->Exit(); 5235 context0->Exit(); 5236 context1.Dispose(); 5237 context0.Dispose(); 5238} 5239 5240 5241static v8::Handle<Value> AccessControlNamedGetter( 5242 Local<String>, const AccessorInfo&) { 5243 return v8::Integer::New(42); 5244} 5245 5246 5247static v8::Handle<Value> AccessControlNamedSetter( 5248 Local<String>, Local<Value> value, const AccessorInfo&) { 5249 return value; 5250} 5251 5252 5253static v8::Handle<Value> AccessControlIndexedGetter( 5254 uint32_t index, 5255 const AccessorInfo& info) { 5256 return v8_num(42); 5257} 5258 5259 5260static v8::Handle<Value> AccessControlIndexedSetter( 5261 uint32_t, Local<Value> value, const AccessorInfo&) { 5262 return value; 5263} 5264 5265 5266THREADED_TEST(AccessControlInterceptorIC) { 5267 named_access_count = 0; 5268 indexed_access_count = 0; 5269 5270 v8::HandleScope handle_scope; 5271 5272 // Create an environment. 5273 v8::Persistent<Context> context0 = Context::New(); 5274 context0->Enter(); 5275 5276 // Create an object that requires access-check functions to be 5277 // called for cross-domain access. The object also has interceptors 5278 // interceptor. 5279 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 5280 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 5281 IndexedAccessCounter); 5282 object_template->SetNamedPropertyHandler(AccessControlNamedGetter, 5283 AccessControlNamedSetter); 5284 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter, 5285 AccessControlIndexedSetter); 5286 Local<v8::Object> object = object_template->NewInstance(); 5287 5288 v8::HandleScope scope1; 5289 5290 // Create another environment. 5291 v8::Persistent<Context> context1 = Context::New(); 5292 context1->Enter(); 5293 5294 // Make easy access to the object from the other environment. 5295 v8::Handle<v8::Object> global1 = context1->Global(); 5296 global1->Set(v8_str("obj"), object); 5297 5298 v8::Handle<Value> value; 5299 5300 // Check that the named access-control function is called every time 5301 // eventhough there is an interceptor on the object. 5302 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run(); 5303 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;" 5304 "obj.x")->Run(); 5305 CHECK(value->IsNumber()); 5306 CHECK_EQ(42, value->Int32Value()); 5307 CHECK_EQ(21, named_access_count); 5308 5309 value = v8_compile("var p = 'x';")->Run(); 5310 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run(); 5311 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];" 5312 "obj[p]")->Run(); 5313 CHECK(value->IsNumber()); 5314 CHECK_EQ(42, value->Int32Value()); 5315 CHECK_EQ(42, named_access_count); 5316 5317 // Check that the indexed access-control function is called every 5318 // time eventhough there is an interceptor on the object. 5319 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run(); 5320 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];" 5321 "obj[0]")->Run(); 5322 CHECK(value->IsNumber()); 5323 CHECK_EQ(42, value->Int32Value()); 5324 CHECK_EQ(21, indexed_access_count); 5325 5326 context1->Exit(); 5327 context0->Exit(); 5328 context1.Dispose(); 5329 context0.Dispose(); 5330} 5331 5332 5333THREADED_TEST(Version) { 5334 v8::V8::GetVersion(); 5335} 5336 5337 5338static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) { 5339 ApiTestFuzzer::Fuzz(); 5340 return v8_num(12); 5341} 5342 5343 5344THREADED_TEST(InstanceProperties) { 5345 v8::HandleScope handle_scope; 5346 LocalContext context; 5347 5348 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5349 Local<ObjectTemplate> instance = t->InstanceTemplate(); 5350 5351 instance->Set(v8_str("x"), v8_num(42)); 5352 instance->Set(v8_str("f"), 5353 v8::FunctionTemplate::New(InstanceFunctionCallback)); 5354 5355 Local<Value> o = t->GetFunction()->NewInstance(); 5356 5357 context->Global()->Set(v8_str("i"), o); 5358 Local<Value> value = Script::Compile(v8_str("i.x"))->Run(); 5359 CHECK_EQ(42, value->Int32Value()); 5360 5361 value = Script::Compile(v8_str("i.f()"))->Run(); 5362 CHECK_EQ(12, value->Int32Value()); 5363} 5364 5365 5366static v8::Handle<Value> 5367GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) { 5368 ApiTestFuzzer::Fuzz(); 5369 return v8::Handle<Value>(); 5370} 5371 5372 5373THREADED_TEST(GlobalObjectInstanceProperties) { 5374 v8::HandleScope handle_scope; 5375 5376 Local<Value> global_object; 5377 5378 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5379 t->InstanceTemplate()->SetNamedPropertyHandler( 5380 GlobalObjectInstancePropertiesGet); 5381 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 5382 instance_template->Set(v8_str("x"), v8_num(42)); 5383 instance_template->Set(v8_str("f"), 5384 v8::FunctionTemplate::New(InstanceFunctionCallback)); 5385 5386 { 5387 LocalContext env(NULL, instance_template); 5388 // Hold on to the global object so it can be used again in another 5389 // environment initialization. 5390 global_object = env->Global(); 5391 5392 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 5393 CHECK_EQ(42, value->Int32Value()); 5394 value = Script::Compile(v8_str("f()"))->Run(); 5395 CHECK_EQ(12, value->Int32Value()); 5396 } 5397 5398 { 5399 // Create new environment reusing the global object. 5400 LocalContext env(NULL, instance_template, global_object); 5401 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 5402 CHECK_EQ(42, value->Int32Value()); 5403 value = Script::Compile(v8_str("f()"))->Run(); 5404 CHECK_EQ(12, value->Int32Value()); 5405 } 5406} 5407 5408 5409static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) { 5410 ApiTestFuzzer::Fuzz(); 5411 return v8_num(42); 5412} 5413 5414 5415static int shadow_y; 5416static int shadow_y_setter_call_count; 5417static int shadow_y_getter_call_count; 5418 5419 5420static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) { 5421 shadow_y_setter_call_count++; 5422 shadow_y = 42; 5423} 5424 5425 5426static v8::Handle<Value> ShadowYGetter(Local<String> name, 5427 const AccessorInfo& info) { 5428 ApiTestFuzzer::Fuzz(); 5429 shadow_y_getter_call_count++; 5430 return v8_num(shadow_y); 5431} 5432 5433 5434static v8::Handle<Value> ShadowIndexedGet(uint32_t index, 5435 const AccessorInfo& info) { 5436 return v8::Handle<Value>(); 5437} 5438 5439 5440static v8::Handle<Value> ShadowNamedGet(Local<String> key, 5441 const AccessorInfo&) { 5442 return v8::Handle<Value>(); 5443} 5444 5445 5446THREADED_TEST(ShadowObject) { 5447 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0; 5448 v8::HandleScope handle_scope; 5449 5450 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New(); 5451 LocalContext context(NULL, global_template); 5452 5453 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5454 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet); 5455 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet); 5456 Local<ObjectTemplate> proto = t->PrototypeTemplate(); 5457 Local<ObjectTemplate> instance = t->InstanceTemplate(); 5458 5459 // Only allow calls of f on instances of t. 5460 Local<v8::Signature> signature = v8::Signature::New(t); 5461 proto->Set(v8_str("f"), 5462 v8::FunctionTemplate::New(ShadowFunctionCallback, 5463 Local<Value>(), 5464 signature)); 5465 proto->Set(v8_str("x"), v8_num(12)); 5466 5467 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter); 5468 5469 Local<Value> o = t->GetFunction()->NewInstance(); 5470 context->Global()->Set(v8_str("__proto__"), o); 5471 5472 Local<Value> value = 5473 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run(); 5474 CHECK(value->IsBoolean()); 5475 CHECK(!value->BooleanValue()); 5476 5477 value = Script::Compile(v8_str("x"))->Run(); 5478 CHECK_EQ(12, value->Int32Value()); 5479 5480 value = Script::Compile(v8_str("f()"))->Run(); 5481 CHECK_EQ(42, value->Int32Value()); 5482 5483 Script::Compile(v8_str("y = 42"))->Run(); 5484 CHECK_EQ(1, shadow_y_setter_call_count); 5485 value = Script::Compile(v8_str("y"))->Run(); 5486 CHECK_EQ(1, shadow_y_getter_call_count); 5487 CHECK_EQ(42, value->Int32Value()); 5488} 5489 5490 5491THREADED_TEST(HiddenPrototype) { 5492 v8::HandleScope handle_scope; 5493 LocalContext context; 5494 5495 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 5496 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 5497 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 5498 t1->SetHiddenPrototype(true); 5499 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 5500 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 5501 t2->SetHiddenPrototype(true); 5502 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 5503 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 5504 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 5505 5506 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 5507 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 5508 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 5509 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 5510 5511 // Setting the prototype on an object skips hidden prototypes. 5512 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5513 o0->Set(v8_str("__proto__"), o1); 5514 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5515 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5516 o0->Set(v8_str("__proto__"), o2); 5517 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5518 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5519 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5520 o0->Set(v8_str("__proto__"), o3); 5521 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5522 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5523 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5524 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 5525 5526 // Getting the prototype of o0 should get the first visible one 5527 // which is o3. Therefore, z should not be defined on the prototype 5528 // object. 5529 Local<Value> proto = o0->Get(v8_str("__proto__")); 5530 CHECK(proto->IsObject()); 5531 CHECK(proto.As<v8::Object>()->Get(v8_str("z"))->IsUndefined()); 5532} 5533 5534 5535THREADED_TEST(SetPrototype) { 5536 v8::HandleScope handle_scope; 5537 LocalContext context; 5538 5539 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 5540 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 5541 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 5542 t1->SetHiddenPrototype(true); 5543 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 5544 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 5545 t2->SetHiddenPrototype(true); 5546 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 5547 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 5548 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 5549 5550 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 5551 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 5552 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 5553 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 5554 5555 // Setting the prototype on an object does not skip hidden prototypes. 5556 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5557 CHECK(o0->SetPrototype(o1)); 5558 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5559 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5560 CHECK(o1->SetPrototype(o2)); 5561 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5562 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5563 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5564 CHECK(o2->SetPrototype(o3)); 5565 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5566 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5567 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5568 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 5569 5570 // Getting the prototype of o0 should get the first visible one 5571 // which is o3. Therefore, z should not be defined on the prototype 5572 // object. 5573 Local<Value> proto = o0->Get(v8_str("__proto__")); 5574 CHECK(proto->IsObject()); 5575 CHECK_EQ(proto.As<v8::Object>(), o3); 5576 5577 // However, Object::GetPrototype ignores hidden prototype. 5578 Local<Value> proto0 = o0->GetPrototype(); 5579 CHECK(proto0->IsObject()); 5580 CHECK_EQ(proto0.As<v8::Object>(), o1); 5581 5582 Local<Value> proto1 = o1->GetPrototype(); 5583 CHECK(proto1->IsObject()); 5584 CHECK_EQ(proto1.As<v8::Object>(), o2); 5585 5586 Local<Value> proto2 = o2->GetPrototype(); 5587 CHECK(proto2->IsObject()); 5588 CHECK_EQ(proto2.As<v8::Object>(), o3); 5589} 5590 5591 5592THREADED_TEST(SetPrototypeThrows) { 5593 v8::HandleScope handle_scope; 5594 LocalContext context; 5595 5596 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5597 5598 Local<v8::Object> o0 = t->GetFunction()->NewInstance(); 5599 Local<v8::Object> o1 = t->GetFunction()->NewInstance(); 5600 5601 CHECK(o0->SetPrototype(o1)); 5602 // If setting the prototype leads to the cycle, SetPrototype should 5603 // return false and keep VM in sane state. 5604 v8::TryCatch try_catch; 5605 CHECK(!o1->SetPrototype(o0)); 5606 CHECK(!try_catch.HasCaught()); 5607 ASSERT(!i::Top::has_pending_exception()); 5608 5609 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value()); 5610} 5611 5612 5613THREADED_TEST(GetterSetterExceptions) { 5614 v8::HandleScope handle_scope; 5615 LocalContext context; 5616 CompileRun( 5617 "function Foo() { };" 5618 "function Throw() { throw 5; };" 5619 "var x = { };" 5620 "x.__defineSetter__('set', Throw);" 5621 "x.__defineGetter__('get', Throw);"); 5622 Local<v8::Object> x = 5623 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x"))); 5624 v8::TryCatch try_catch; 5625 x->Set(v8_str("set"), v8::Integer::New(8)); 5626 x->Get(v8_str("get")); 5627 x->Set(v8_str("set"), v8::Integer::New(8)); 5628 x->Get(v8_str("get")); 5629 x->Set(v8_str("set"), v8::Integer::New(8)); 5630 x->Get(v8_str("get")); 5631 x->Set(v8_str("set"), v8::Integer::New(8)); 5632 x->Get(v8_str("get")); 5633} 5634 5635 5636THREADED_TEST(Constructor) { 5637 v8::HandleScope handle_scope; 5638 LocalContext context; 5639 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 5640 templ->SetClassName(v8_str("Fun")); 5641 Local<Function> cons = templ->GetFunction(); 5642 context->Global()->Set(v8_str("Fun"), cons); 5643 Local<v8::Object> inst = cons->NewInstance(); 5644 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst); 5645 Local<Value> value = CompileRun("(new Fun()).constructor === Fun"); 5646 CHECK(value->BooleanValue()); 5647} 5648 5649THREADED_TEST(FunctionDescriptorException) { 5650 v8::HandleScope handle_scope; 5651 LocalContext context; 5652 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 5653 templ->SetClassName(v8_str("Fun")); 5654 Local<Function> cons = templ->GetFunction(); 5655 context->Global()->Set(v8_str("Fun"), cons); 5656 Local<Value> value = CompileRun( 5657 "function test() {" 5658 " try {" 5659 " (new Fun()).blah()" 5660 " } catch (e) {" 5661 " var str = String(e);" 5662 " if (str.indexOf('TypeError') == -1) return 1;" 5663 " if (str.indexOf('[object Fun]') != -1) return 2;" 5664 " if (str.indexOf('#<a Fun>') == -1) return 3;" 5665 " return 0;" 5666 " }" 5667 " return 4;" 5668 "}" 5669 "test();"); 5670 CHECK_EQ(0, value->Int32Value()); 5671} 5672 5673 5674THREADED_TEST(EvalAliasedDynamic) { 5675 v8::HandleScope scope; 5676 LocalContext current; 5677 5678 // Tests where aliased eval can only be resolved dynamically. 5679 Local<Script> script = 5680 Script::Compile(v8_str("function f(x) { " 5681 " var foo = 2;" 5682 " with (x) { return eval('foo'); }" 5683 "}" 5684 "foo = 0;" 5685 "result1 = f(new Object());" 5686 "result2 = f(this);" 5687 "var x = new Object();" 5688 "x.eval = function(x) { return 1; };" 5689 "result3 = f(x);")); 5690 script->Run(); 5691 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value()); 5692 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value()); 5693 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value()); 5694 5695 v8::TryCatch try_catch; 5696 script = 5697 Script::Compile(v8_str("function f(x) { " 5698 " var bar = 2;" 5699 " with (x) { return eval('bar'); }" 5700 "}" 5701 "f(this)")); 5702 script->Run(); 5703 CHECK(try_catch.HasCaught()); 5704 try_catch.Reset(); 5705} 5706 5707 5708THREADED_TEST(CrossEval) { 5709 v8::HandleScope scope; 5710 LocalContext other; 5711 LocalContext current; 5712 5713 Local<String> token = v8_str("<security token>"); 5714 other->SetSecurityToken(token); 5715 current->SetSecurityToken(token); 5716 5717 // Setup reference from current to other. 5718 current->Global()->Set(v8_str("other"), other->Global()); 5719 5720 // Check that new variables are introduced in other context. 5721 Local<Script> script = 5722 Script::Compile(v8_str("other.eval('var foo = 1234')")); 5723 script->Run(); 5724 Local<Value> foo = other->Global()->Get(v8_str("foo")); 5725 CHECK_EQ(1234, foo->Int32Value()); 5726 CHECK(!current->Global()->Has(v8_str("foo"))); 5727 5728 // Check that writing to non-existing properties introduces them in 5729 // the other context. 5730 script = 5731 Script::Compile(v8_str("other.eval('na = 1234')")); 5732 script->Run(); 5733 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value()); 5734 CHECK(!current->Global()->Has(v8_str("na"))); 5735 5736 // Check that global variables in current context are not visible in other 5737 // context. 5738 v8::TryCatch try_catch; 5739 script = 5740 Script::Compile(v8_str("var bar = 42; other.eval('bar');")); 5741 Local<Value> result = script->Run(); 5742 CHECK(try_catch.HasCaught()); 5743 try_catch.Reset(); 5744 5745 // Check that local variables in current context are not visible in other 5746 // context. 5747 script = 5748 Script::Compile(v8_str("(function() { " 5749 " var baz = 87;" 5750 " return other.eval('baz');" 5751 "})();")); 5752 result = script->Run(); 5753 CHECK(try_catch.HasCaught()); 5754 try_catch.Reset(); 5755 5756 // Check that global variables in the other environment are visible 5757 // when evaluting code. 5758 other->Global()->Set(v8_str("bis"), v8_num(1234)); 5759 script = Script::Compile(v8_str("other.eval('bis')")); 5760 CHECK_EQ(1234, script->Run()->Int32Value()); 5761 CHECK(!try_catch.HasCaught()); 5762 5763 // Check that the 'this' pointer points to the global object evaluating 5764 // code. 5765 other->Global()->Set(v8_str("t"), other->Global()); 5766 script = Script::Compile(v8_str("other.eval('this == t')")); 5767 result = script->Run(); 5768 CHECK(result->IsTrue()); 5769 CHECK(!try_catch.HasCaught()); 5770 5771 // Check that variables introduced in with-statement are not visible in 5772 // other context. 5773 script = 5774 Script::Compile(v8_str("with({x:2}){other.eval('x')}")); 5775 result = script->Run(); 5776 CHECK(try_catch.HasCaught()); 5777 try_catch.Reset(); 5778 5779 // Check that you cannot use 'eval.call' with another object than the 5780 // current global object. 5781 script = 5782 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')")); 5783 result = script->Run(); 5784 CHECK(try_catch.HasCaught()); 5785} 5786 5787 5788// Test that calling eval in a context which has been detached from 5789// its global throws an exception. This behavior is consistent with 5790// other JavaScript implementations. 5791THREADED_TEST(EvalInDetachedGlobal) { 5792 v8::HandleScope scope; 5793 5794 v8::Persistent<Context> context0 = Context::New(); 5795 v8::Persistent<Context> context1 = Context::New(); 5796 5797 // Setup function in context0 that uses eval from context0. 5798 context0->Enter(); 5799 v8::Handle<v8::Value> fun = 5800 CompileRun("var x = 42;" 5801 "(function() {" 5802 " var e = eval;" 5803 " return function(s) { return e(s); }" 5804 "})()"); 5805 context0->Exit(); 5806 5807 // Put the function into context1 and call it before and after 5808 // detaching the global. Before detaching, the call succeeds and 5809 // after detaching and exception is thrown. 5810 context1->Enter(); 5811 context1->Global()->Set(v8_str("fun"), fun); 5812 v8::Handle<v8::Value> x_value = CompileRun("fun('x')"); 5813 CHECK_EQ(42, x_value->Int32Value()); 5814 context0->DetachGlobal(); 5815 v8::TryCatch catcher; 5816 x_value = CompileRun("fun('x')"); 5817 CHECK(x_value.IsEmpty()); 5818 CHECK(catcher.HasCaught()); 5819 context1->Exit(); 5820 5821 context1.Dispose(); 5822 context0.Dispose(); 5823} 5824 5825 5826THREADED_TEST(CrossLazyLoad) { 5827 v8::HandleScope scope; 5828 LocalContext other; 5829 LocalContext current; 5830 5831 Local<String> token = v8_str("<security token>"); 5832 other->SetSecurityToken(token); 5833 current->SetSecurityToken(token); 5834 5835 // Setup reference from current to other. 5836 current->Global()->Set(v8_str("other"), other->Global()); 5837 5838 // Trigger lazy loading in other context. 5839 Local<Script> script = 5840 Script::Compile(v8_str("other.eval('new Date(42)')")); 5841 Local<Value> value = script->Run(); 5842 CHECK_EQ(42.0, value->NumberValue()); 5843} 5844 5845 5846static v8::Handle<Value> call_as_function(const v8::Arguments& args) { 5847 ApiTestFuzzer::Fuzz(); 5848 if (args.IsConstructCall()) { 5849 if (args[0]->IsInt32()) { 5850 return v8_num(-args[0]->Int32Value()); 5851 } 5852 } 5853 5854 return args[0]; 5855} 5856 5857 5858// Test that a call handler can be set for objects which will allow 5859// non-function objects created through the API to be called as 5860// functions. 5861THREADED_TEST(CallAsFunction) { 5862 v8::HandleScope scope; 5863 LocalContext context; 5864 5865 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5866 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 5867 instance_template->SetCallAsFunctionHandler(call_as_function); 5868 Local<v8::Object> instance = t->GetFunction()->NewInstance(); 5869 context->Global()->Set(v8_str("obj"), instance); 5870 v8::TryCatch try_catch; 5871 Local<Value> value; 5872 CHECK(!try_catch.HasCaught()); 5873 5874 value = CompileRun("obj(42)"); 5875 CHECK(!try_catch.HasCaught()); 5876 CHECK_EQ(42, value->Int32Value()); 5877 5878 value = CompileRun("(function(o){return o(49)})(obj)"); 5879 CHECK(!try_catch.HasCaught()); 5880 CHECK_EQ(49, value->Int32Value()); 5881 5882 // test special case of call as function 5883 value = CompileRun("[obj]['0'](45)"); 5884 CHECK(!try_catch.HasCaught()); 5885 CHECK_EQ(45, value->Int32Value()); 5886 5887 value = CompileRun("obj.call = Function.prototype.call;" 5888 "obj.call(null, 87)"); 5889 CHECK(!try_catch.HasCaught()); 5890 CHECK_EQ(87, value->Int32Value()); 5891 5892 // Regression tests for bug #1116356: Calling call through call/apply 5893 // must work for non-function receivers. 5894 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])"; 5895 value = CompileRun(apply_99); 5896 CHECK(!try_catch.HasCaught()); 5897 CHECK_EQ(99, value->Int32Value()); 5898 5899 const char* call_17 = "Function.prototype.call.call(obj, this, 17)"; 5900 value = CompileRun(call_17); 5901 CHECK(!try_catch.HasCaught()); 5902 CHECK_EQ(17, value->Int32Value()); 5903 5904 // Check that the call-as-function handler can be called through 5905 // new. 5906 value = CompileRun("new obj(43)"); 5907 CHECK(!try_catch.HasCaught()); 5908 CHECK_EQ(-43, value->Int32Value()); 5909} 5910 5911 5912static int CountHandles() { 5913 return v8::HandleScope::NumberOfHandles(); 5914} 5915 5916 5917static int Recurse(int depth, int iterations) { 5918 v8::HandleScope scope; 5919 if (depth == 0) return CountHandles(); 5920 for (int i = 0; i < iterations; i++) { 5921 Local<v8::Number> n = v8::Integer::New(42); 5922 } 5923 return Recurse(depth - 1, iterations); 5924} 5925 5926 5927THREADED_TEST(HandleIteration) { 5928 static const int kIterations = 500; 5929 static const int kNesting = 200; 5930 CHECK_EQ(0, CountHandles()); 5931 { 5932 v8::HandleScope scope1; 5933 CHECK_EQ(0, CountHandles()); 5934 for (int i = 0; i < kIterations; i++) { 5935 Local<v8::Number> n = v8::Integer::New(42); 5936 CHECK_EQ(i + 1, CountHandles()); 5937 } 5938 5939 CHECK_EQ(kIterations, CountHandles()); 5940 { 5941 v8::HandleScope scope2; 5942 for (int j = 0; j < kIterations; j++) { 5943 Local<v8::Number> n = v8::Integer::New(42); 5944 CHECK_EQ(j + 1 + kIterations, CountHandles()); 5945 } 5946 } 5947 CHECK_EQ(kIterations, CountHandles()); 5948 } 5949 CHECK_EQ(0, CountHandles()); 5950 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations)); 5951} 5952 5953 5954static v8::Handle<Value> InterceptorHasOwnPropertyGetter( 5955 Local<String> name, 5956 const AccessorInfo& info) { 5957 ApiTestFuzzer::Fuzz(); 5958 return v8::Handle<Value>(); 5959} 5960 5961 5962THREADED_TEST(InterceptorHasOwnProperty) { 5963 v8::HandleScope scope; 5964 LocalContext context; 5965 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 5966 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 5967 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter); 5968 Local<Function> function = fun_templ->GetFunction(); 5969 context->Global()->Set(v8_str("constructor"), function); 5970 v8::Handle<Value> value = CompileRun( 5971 "var o = new constructor();" 5972 "o.hasOwnProperty('ostehaps');"); 5973 CHECK_EQ(false, value->BooleanValue()); 5974 value = CompileRun( 5975 "o.ostehaps = 42;" 5976 "o.hasOwnProperty('ostehaps');"); 5977 CHECK_EQ(true, value->BooleanValue()); 5978 value = CompileRun( 5979 "var p = new constructor();" 5980 "p.hasOwnProperty('ostehaps');"); 5981 CHECK_EQ(false, value->BooleanValue()); 5982} 5983 5984 5985static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC( 5986 Local<String> name, 5987 const AccessorInfo& info) { 5988 ApiTestFuzzer::Fuzz(); 5989 i::Heap::CollectAllGarbage(false); 5990 return v8::Handle<Value>(); 5991} 5992 5993 5994THREADED_TEST(InterceptorHasOwnPropertyCausingGC) { 5995 v8::HandleScope scope; 5996 LocalContext context; 5997 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 5998 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 5999 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC); 6000 Local<Function> function = fun_templ->GetFunction(); 6001 context->Global()->Set(v8_str("constructor"), function); 6002 // Let's first make some stuff so we can be sure to get a good GC. 6003 CompileRun( 6004 "function makestr(size) {" 6005 " switch (size) {" 6006 " case 1: return 'f';" 6007 " case 2: return 'fo';" 6008 " case 3: return 'foo';" 6009 " }" 6010 " return makestr(size >> 1) + makestr((size + 1) >> 1);" 6011 "}" 6012 "var x = makestr(12345);" 6013 "x = makestr(31415);" 6014 "x = makestr(23456);"); 6015 v8::Handle<Value> value = CompileRun( 6016 "var o = new constructor();" 6017 "o.__proto__ = new String(x);" 6018 "o.hasOwnProperty('ostehaps');"); 6019 CHECK_EQ(false, value->BooleanValue()); 6020} 6021 6022 6023typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property, 6024 const AccessorInfo& info); 6025 6026 6027static void CheckInterceptorLoadIC(NamedPropertyGetter getter, 6028 const char* source, 6029 int expected) { 6030 v8::HandleScope scope; 6031 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6032 templ->SetNamedPropertyHandler(getter); 6033 LocalContext context; 6034 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6035 v8::Handle<Value> value = CompileRun(source); 6036 CHECK_EQ(expected, value->Int32Value()); 6037} 6038 6039 6040static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name, 6041 const AccessorInfo& info) { 6042 ApiTestFuzzer::Fuzz(); 6043 CHECK(v8_str("x")->Equals(name)); 6044 return v8::Integer::New(42); 6045} 6046 6047 6048// This test should hit the load IC for the interceptor case. 6049THREADED_TEST(InterceptorLoadIC) { 6050 CheckInterceptorLoadIC(InterceptorLoadICGetter, 6051 "var result = 0;" 6052 "for (var i = 0; i < 1000; i++) {" 6053 " result = o.x;" 6054 "}", 6055 42); 6056} 6057 6058 6059// Below go several tests which verify that JITing for various 6060// configurations of interceptor and explicit fields works fine 6061// (those cases are special cased to get better performance). 6062 6063static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name, 6064 const AccessorInfo& info) { 6065 ApiTestFuzzer::Fuzz(); 6066 return v8_str("x")->Equals(name) 6067 ? v8::Integer::New(42) : v8::Handle<v8::Value>(); 6068} 6069 6070 6071THREADED_TEST(InterceptorLoadICWithFieldOnHolder) { 6072 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6073 "var result = 0;" 6074 "o.y = 239;" 6075 "for (var i = 0; i < 1000; i++) {" 6076 " result = o.y;" 6077 "}", 6078 239); 6079} 6080 6081 6082THREADED_TEST(InterceptorLoadICWithSubstitutedProto) { 6083 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6084 "var result = 0;" 6085 "o.__proto__ = { 'y': 239 };" 6086 "for (var i = 0; i < 1000; i++) {" 6087 " result = o.y + o.x;" 6088 "}", 6089 239 + 42); 6090} 6091 6092 6093THREADED_TEST(InterceptorLoadICWithPropertyOnProto) { 6094 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6095 "var result = 0;" 6096 "o.__proto__.y = 239;" 6097 "for (var i = 0; i < 1000; i++) {" 6098 " result = o.y + o.x;" 6099 "}", 6100 239 + 42); 6101} 6102 6103 6104THREADED_TEST(InterceptorLoadICUndefined) { 6105 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6106 "var result = 0;" 6107 "for (var i = 0; i < 1000; i++) {" 6108 " result = (o.y == undefined) ? 239 : 42;" 6109 "}", 6110 239); 6111} 6112 6113 6114THREADED_TEST(InterceptorLoadICWithOverride) { 6115 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6116 "fst = new Object(); fst.__proto__ = o;" 6117 "snd = new Object(); snd.__proto__ = fst;" 6118 "var result1 = 0;" 6119 "for (var i = 0; i < 1000; i++) {" 6120 " result1 = snd.x;" 6121 "}" 6122 "fst.x = 239;" 6123 "var result = 0;" 6124 "for (var i = 0; i < 1000; i++) {" 6125 " result = snd.x;" 6126 "}" 6127 "result + result1", 6128 239 + 42); 6129} 6130 6131 6132// Test the case when we stored field into 6133// a stub, but interceptor produced value on its own. 6134THREADED_TEST(InterceptorLoadICFieldNotNeeded) { 6135 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6136 "proto = new Object();" 6137 "o.__proto__ = proto;" 6138 "proto.x = 239;" 6139 "for (var i = 0; i < 1000; i++) {" 6140 " o.x;" 6141 // Now it should be ICed and keep a reference to x defined on proto 6142 "}" 6143 "var result = 0;" 6144 "for (var i = 0; i < 1000; i++) {" 6145 " result += o.x;" 6146 "}" 6147 "result;", 6148 42 * 1000); 6149} 6150 6151 6152// Test the case when we stored field into 6153// a stub, but it got invalidated later on. 6154THREADED_TEST(InterceptorLoadICInvalidatedField) { 6155 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6156 "proto1 = new Object();" 6157 "proto2 = new Object();" 6158 "o.__proto__ = proto1;" 6159 "proto1.__proto__ = proto2;" 6160 "proto2.y = 239;" 6161 "for (var i = 0; i < 1000; i++) {" 6162 " o.y;" 6163 // Now it should be ICed and keep a reference to y defined on proto2 6164 "}" 6165 "proto1.y = 42;" 6166 "var result = 0;" 6167 "for (var i = 0; i < 1000; i++) {" 6168 " result += o.y;" 6169 "}" 6170 "result;", 6171 42 * 1000); 6172} 6173 6174 6175static int interceptor_load_not_handled_calls = 0; 6176static v8::Handle<Value> InterceptorLoadNotHandled(Local<String> name, 6177 const AccessorInfo& info) { 6178 ++interceptor_load_not_handled_calls; 6179 return v8::Handle<v8::Value>(); 6180} 6181 6182 6183// Test how post-interceptor lookups are done in the non-cacheable 6184// case: the interceptor should not be invoked during this lookup. 6185THREADED_TEST(InterceptorLoadICPostInterceptor) { 6186 interceptor_load_not_handled_calls = 0; 6187 CheckInterceptorLoadIC(InterceptorLoadNotHandled, 6188 "receiver = new Object();" 6189 "receiver.__proto__ = o;" 6190 "proto = new Object();" 6191 "/* Make proto a slow-case object. */" 6192 "for (var i = 0; i < 1000; i++) {" 6193 " proto[\"xxxxxxxx\" + i] = [];" 6194 "}" 6195 "proto.x = 17;" 6196 "o.__proto__ = proto;" 6197 "var result = 0;" 6198 "for (var i = 0; i < 1000; i++) {" 6199 " result += receiver.x;" 6200 "}" 6201 "result;", 6202 17 * 1000); 6203 CHECK_EQ(1000, interceptor_load_not_handled_calls); 6204} 6205 6206 6207// Test the case when we stored field into 6208// a stub, but it got invalidated later on due to override on 6209// global object which is between interceptor and fields' holders. 6210THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) { 6211 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 6212 "o.__proto__ = this;" // set a global to be a proto of o. 6213 "this.__proto__.y = 239;" 6214 "for (var i = 0; i < 10; i++) {" 6215 " if (o.y != 239) throw 'oops: ' + o.y;" 6216 // Now it should be ICed and keep a reference to y defined on field_holder. 6217 "}" 6218 "this.y = 42;" // Assign on a global. 6219 "var result = 0;" 6220 "for (var i = 0; i < 10; i++) {" 6221 " result += o.y;" 6222 "}" 6223 "result;", 6224 42 * 10); 6225} 6226 6227 6228static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) { 6229 ApiTestFuzzer::Fuzz(); 6230 return v8_num(239); 6231} 6232 6233 6234static void SetOnThis(Local<String> name, 6235 Local<Value> value, 6236 const AccessorInfo& info) { 6237 info.This()->ForceSet(name, value); 6238} 6239 6240 6241THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) { 6242 v8::HandleScope scope; 6243 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6244 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6245 templ->SetAccessor(v8_str("y"), Return239); 6246 LocalContext context; 6247 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6248 v8::Handle<Value> value = CompileRun( 6249 "var result = 0;" 6250 "for (var i = 0; i < 7; i++) {" 6251 " result = o.y;" 6252 "}"); 6253 CHECK_EQ(239, value->Int32Value()); 6254} 6255 6256 6257THREADED_TEST(InterceptorLoadICWithCallbackOnProto) { 6258 v8::HandleScope scope; 6259 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6260 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6261 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6262 templ_p->SetAccessor(v8_str("y"), Return239); 6263 6264 LocalContext context; 6265 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6266 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6267 6268 v8::Handle<Value> value = CompileRun( 6269 "o.__proto__ = p;" 6270 "var result = 0;" 6271 "for (var i = 0; i < 7; i++) {" 6272 " result = o.x + o.y;" 6273 "}"); 6274 CHECK_EQ(239 + 42, value->Int32Value()); 6275} 6276 6277 6278THREADED_TEST(InterceptorLoadICForCallbackWithOverride) { 6279 v8::HandleScope scope; 6280 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6281 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6282 templ->SetAccessor(v8_str("y"), Return239); 6283 6284 LocalContext context; 6285 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6286 6287 v8::Handle<Value> value = CompileRun( 6288 "fst = new Object(); fst.__proto__ = o;" 6289 "snd = new Object(); snd.__proto__ = fst;" 6290 "var result1 = 0;" 6291 "for (var i = 0; i < 7; i++) {" 6292 " result1 = snd.x;" 6293 "}" 6294 "fst.x = 239;" 6295 "var result = 0;" 6296 "for (var i = 0; i < 7; i++) {" 6297 " result = snd.x;" 6298 "}" 6299 "result + result1"); 6300 CHECK_EQ(239 + 42, value->Int32Value()); 6301} 6302 6303 6304// Test the case when we stored callback into 6305// a stub, but interceptor produced value on its own. 6306THREADED_TEST(InterceptorLoadICCallbackNotNeeded) { 6307 v8::HandleScope scope; 6308 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6309 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6310 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6311 templ_p->SetAccessor(v8_str("y"), Return239); 6312 6313 LocalContext context; 6314 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6315 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6316 6317 v8::Handle<Value> value = CompileRun( 6318 "o.__proto__ = p;" 6319 "for (var i = 0; i < 7; i++) {" 6320 " o.x;" 6321 // Now it should be ICed and keep a reference to x defined on p 6322 "}" 6323 "var result = 0;" 6324 "for (var i = 0; i < 7; i++) {" 6325 " result += o.x;" 6326 "}" 6327 "result"); 6328 CHECK_EQ(42 * 7, value->Int32Value()); 6329} 6330 6331 6332// Test the case when we stored callback into 6333// a stub, but it got invalidated later on. 6334THREADED_TEST(InterceptorLoadICInvalidatedCallback) { 6335 v8::HandleScope scope; 6336 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6337 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6338 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6339 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 6340 6341 LocalContext context; 6342 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6343 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6344 6345 v8::Handle<Value> value = CompileRun( 6346 "inbetween = new Object();" 6347 "o.__proto__ = inbetween;" 6348 "inbetween.__proto__ = p;" 6349 "for (var i = 0; i < 10; i++) {" 6350 " o.y;" 6351 // Now it should be ICed and keep a reference to y defined on p 6352 "}" 6353 "inbetween.y = 42;" 6354 "var result = 0;" 6355 "for (var i = 0; i < 10; i++) {" 6356 " result += o.y;" 6357 "}" 6358 "result"); 6359 CHECK_EQ(42 * 10, value->Int32Value()); 6360} 6361 6362 6363// Test the case when we stored callback into 6364// a stub, but it got invalidated later on due to override on 6365// global object which is between interceptor and callbacks' holders. 6366THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) { 6367 v8::HandleScope scope; 6368 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6369 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6370 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6371 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 6372 6373 LocalContext context; 6374 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6375 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6376 6377 v8::Handle<Value> value = CompileRun( 6378 "o.__proto__ = this;" 6379 "this.__proto__ = p;" 6380 "for (var i = 0; i < 10; i++) {" 6381 " if (o.y != 239) throw 'oops: ' + o.y;" 6382 // Now it should be ICed and keep a reference to y defined on p 6383 "}" 6384 "this.y = 42;" 6385 "var result = 0;" 6386 "for (var i = 0; i < 10; i++) {" 6387 " result += o.y;" 6388 "}" 6389 "result"); 6390 CHECK_EQ(42 * 10, value->Int32Value()); 6391} 6392 6393 6394static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name, 6395 const AccessorInfo& info) { 6396 ApiTestFuzzer::Fuzz(); 6397 CHECK(v8_str("x")->Equals(name)); 6398 return v8::Integer::New(0); 6399} 6400 6401 6402THREADED_TEST(InterceptorReturningZero) { 6403 CheckInterceptorLoadIC(InterceptorLoadICGetter0, 6404 "o.x == undefined ? 1 : 0", 6405 0); 6406} 6407 6408 6409static v8::Handle<Value> InterceptorStoreICSetter( 6410 Local<String> key, Local<Value> value, const AccessorInfo&) { 6411 CHECK(v8_str("x")->Equals(key)); 6412 CHECK_EQ(42, value->Int32Value()); 6413 return value; 6414} 6415 6416 6417// This test should hit the store IC for the interceptor case. 6418THREADED_TEST(InterceptorStoreIC) { 6419 v8::HandleScope scope; 6420 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6421 templ->SetNamedPropertyHandler(InterceptorLoadICGetter, 6422 InterceptorStoreICSetter); 6423 LocalContext context; 6424 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6425 v8::Handle<Value> value = CompileRun( 6426 "for (var i = 0; i < 1000; i++) {" 6427 " o.x = 42;" 6428 "}"); 6429} 6430 6431 6432THREADED_TEST(InterceptorStoreICWithNoSetter) { 6433 v8::HandleScope scope; 6434 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6435 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6436 LocalContext context; 6437 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6438 v8::Handle<Value> value = CompileRun( 6439 "for (var i = 0; i < 1000; i++) {" 6440 " o.y = 239;" 6441 "}" 6442 "42 + o.y"); 6443 CHECK_EQ(239 + 42, value->Int32Value()); 6444} 6445 6446 6447 6448 6449v8::Handle<Value> call_ic_function; 6450v8::Handle<Value> call_ic_function2; 6451v8::Handle<Value> call_ic_function3; 6452 6453static v8::Handle<Value> InterceptorCallICGetter(Local<String> name, 6454 const AccessorInfo& info) { 6455 ApiTestFuzzer::Fuzz(); 6456 CHECK(v8_str("x")->Equals(name)); 6457 return call_ic_function; 6458} 6459 6460 6461// This test should hit the call IC for the interceptor case. 6462THREADED_TEST(InterceptorCallIC) { 6463 v8::HandleScope scope; 6464 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6465 templ->SetNamedPropertyHandler(InterceptorCallICGetter); 6466 LocalContext context; 6467 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6468 call_ic_function = 6469 v8_compile("function f(x) { return x + 1; }; f")->Run(); 6470 v8::Handle<Value> value = CompileRun( 6471 "var result = 0;" 6472 "for (var i = 0; i < 1000; i++) {" 6473 " result = o.x(41);" 6474 "}"); 6475 CHECK_EQ(42, value->Int32Value()); 6476} 6477 6478 6479// This test checks that if interceptor doesn't provide 6480// a value, we can fetch regular value. 6481THREADED_TEST(InterceptorCallICSeesOthers) { 6482 v8::HandleScope scope; 6483 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6484 templ->SetNamedPropertyHandler(NoBlockGetterX); 6485 LocalContext context; 6486 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6487 v8::Handle<Value> value = CompileRun( 6488 "o.x = function f(x) { return x + 1; };" 6489 "var result = 0;" 6490 "for (var i = 0; i < 7; i++) {" 6491 " result = o.x(41);" 6492 "}"); 6493 CHECK_EQ(42, value->Int32Value()); 6494} 6495 6496 6497static v8::Handle<Value> call_ic_function4; 6498static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name, 6499 const AccessorInfo& info) { 6500 ApiTestFuzzer::Fuzz(); 6501 CHECK(v8_str("x")->Equals(name)); 6502 return call_ic_function4; 6503} 6504 6505 6506// This test checks that if interceptor provides a function, 6507// even if we cached shadowed variant, interceptor's function 6508// is invoked 6509THREADED_TEST(InterceptorCallICCacheableNotNeeded) { 6510 v8::HandleScope scope; 6511 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6512 templ->SetNamedPropertyHandler(InterceptorCallICGetter4); 6513 LocalContext context; 6514 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6515 call_ic_function4 = 6516 v8_compile("function f(x) { return x - 1; }; f")->Run(); 6517 v8::Handle<Value> value = CompileRun( 6518 "o.__proto__.x = function(x) { return x + 1; };" 6519 "var result = 0;" 6520 "for (var i = 0; i < 1000; i++) {" 6521 " result = o.x(42);" 6522 "}"); 6523 CHECK_EQ(41, value->Int32Value()); 6524} 6525 6526 6527// Test the case when we stored cacheable lookup into 6528// a stub, but it got invalidated later on 6529THREADED_TEST(InterceptorCallICInvalidatedCacheable) { 6530 v8::HandleScope scope; 6531 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6532 templ->SetNamedPropertyHandler(NoBlockGetterX); 6533 LocalContext context; 6534 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6535 v8::Handle<Value> value = CompileRun( 6536 "proto1 = new Object();" 6537 "proto2 = new Object();" 6538 "o.__proto__ = proto1;" 6539 "proto1.__proto__ = proto2;" 6540 "proto2.y = function(x) { return x + 1; };" 6541 // Invoke it many times to compile a stub 6542 "for (var i = 0; i < 7; i++) {" 6543 " o.y(42);" 6544 "}" 6545 "proto1.y = function(x) { return x - 1; };" 6546 "var result = 0;" 6547 "for (var i = 0; i < 7; i++) {" 6548 " result += o.y(42);" 6549 "}"); 6550 CHECK_EQ(41 * 7, value->Int32Value()); 6551} 6552 6553 6554static v8::Handle<Value> call_ic_function5; 6555static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name, 6556 const AccessorInfo& info) { 6557 ApiTestFuzzer::Fuzz(); 6558 if (v8_str("x")->Equals(name)) 6559 return call_ic_function5; 6560 else 6561 return Local<Value>(); 6562} 6563 6564 6565// This test checks that if interceptor doesn't provide a function, 6566// cached constant function is used 6567THREADED_TEST(InterceptorCallICConstantFunctionUsed) { 6568 v8::HandleScope scope; 6569 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6570 templ->SetNamedPropertyHandler(NoBlockGetterX); 6571 LocalContext context; 6572 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6573 v8::Handle<Value> value = CompileRun( 6574 "function inc(x) { return x + 1; };" 6575 "inc(1);" 6576 "o.x = inc;" 6577 "var result = 0;" 6578 "for (var i = 0; i < 1000; i++) {" 6579 " result = o.x(42);" 6580 "}"); 6581 CHECK_EQ(43, value->Int32Value()); 6582} 6583 6584 6585// This test checks that if interceptor provides a function, 6586// even if we cached constant function, interceptor's function 6587// is invoked 6588THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) { 6589 v8::HandleScope scope; 6590 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6591 templ->SetNamedPropertyHandler(InterceptorCallICGetter5); 6592 LocalContext context; 6593 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6594 call_ic_function5 = 6595 v8_compile("function f(x) { return x - 1; }; f")->Run(); 6596 v8::Handle<Value> value = CompileRun( 6597 "function inc(x) { return x + 1; };" 6598 "inc(1);" 6599 "o.x = inc;" 6600 "var result = 0;" 6601 "for (var i = 0; i < 1000; i++) {" 6602 " result = o.x(42);" 6603 "}"); 6604 CHECK_EQ(41, value->Int32Value()); 6605} 6606 6607 6608// Test the case when we stored constant function into 6609// a stub, but it got invalidated later on 6610THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) { 6611 v8::HandleScope scope; 6612 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6613 templ->SetNamedPropertyHandler(NoBlockGetterX); 6614 LocalContext context; 6615 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6616 v8::Handle<Value> value = CompileRun( 6617 "function inc(x) { return x + 1; };" 6618 "inc(1);" 6619 "proto1 = new Object();" 6620 "proto2 = new Object();" 6621 "o.__proto__ = proto1;" 6622 "proto1.__proto__ = proto2;" 6623 "proto2.y = inc;" 6624 // Invoke it many times to compile a stub 6625 "for (var i = 0; i < 7; i++) {" 6626 " o.y(42);" 6627 "}" 6628 "proto1.y = function(x) { return x - 1; };" 6629 "var result = 0;" 6630 "for (var i = 0; i < 7; i++) {" 6631 " result += o.y(42);" 6632 "}"); 6633 CHECK_EQ(41 * 7, value->Int32Value()); 6634} 6635 6636 6637// Test the case when we stored constant function into 6638// a stub, but it got invalidated later on due to override on 6639// global object which is between interceptor and constant function' holders. 6640THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) { 6641 v8::HandleScope scope; 6642 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6643 templ->SetNamedPropertyHandler(NoBlockGetterX); 6644 LocalContext context; 6645 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6646 v8::Handle<Value> value = CompileRun( 6647 "function inc(x) { return x + 1; };" 6648 "inc(1);" 6649 "o.__proto__ = this;" 6650 "this.__proto__.y = inc;" 6651 // Invoke it many times to compile a stub 6652 "for (var i = 0; i < 7; i++) {" 6653 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);" 6654 "}" 6655 "this.y = function(x) { return x - 1; };" 6656 "var result = 0;" 6657 "for (var i = 0; i < 7; i++) {" 6658 " result += o.y(42);" 6659 "}"); 6660 CHECK_EQ(41 * 7, value->Int32Value()); 6661} 6662 6663 6664// Test the case when actual function to call sits on global object. 6665THREADED_TEST(InterceptorCallICCachedFromGlobal) { 6666 v8::HandleScope scope; 6667 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6668 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 6669 6670 LocalContext context; 6671 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6672 6673 v8::Handle<Value> value = CompileRun( 6674 "try {" 6675 " o.__proto__ = this;" 6676 " for (var i = 0; i < 10; i++) {" 6677 " var v = o.parseFloat('239');" 6678 " if (v != 239) throw v;" 6679 // Now it should be ICed and keep a reference to parseFloat. 6680 " }" 6681 " var result = 0;" 6682 " for (var i = 0; i < 10; i++) {" 6683 " result += o.parseFloat('239');" 6684 " }" 6685 " result" 6686 "} catch(e) {" 6687 " e" 6688 "};"); 6689 CHECK_EQ(239 * 10, value->Int32Value()); 6690} 6691 6692static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name, 6693 const AccessorInfo& info) { 6694 ApiTestFuzzer::Fuzz(); 6695 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data())); 6696 ++(*call_count); 6697 if ((*call_count) % 20 == 0) { 6698 v8::internal::Heap::CollectAllGarbage(true); 6699 } 6700 return v8::Handle<Value>(); 6701} 6702 6703static v8::Handle<Value> FastApiCallback_TrivialSignature( 6704 const v8::Arguments& args) { 6705 ApiTestFuzzer::Fuzz(); 6706 CHECK_EQ(args.This(), args.Holder()); 6707 CHECK(args.Data()->Equals(v8_str("method_data"))); 6708 return v8::Integer::New(args[0]->Int32Value() + 1); 6709} 6710 6711static v8::Handle<Value> FastApiCallback_SimpleSignature( 6712 const v8::Arguments& args) { 6713 ApiTestFuzzer::Fuzz(); 6714 CHECK_EQ(args.This()->GetPrototype(), args.Holder()); 6715 CHECK(args.Data()->Equals(v8_str("method_data"))); 6716 // Note, we're using HasRealNamedProperty instead of Has to avoid 6717 // invoking the interceptor again. 6718 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo"))); 6719 return v8::Integer::New(args[0]->Int32Value() + 1); 6720} 6721 6722// Helper to maximize the odds of object moving. 6723static void GenerateSomeGarbage() { 6724 CompileRun( 6725 "var garbage;" 6726 "for (var i = 0; i < 1000; i++) {" 6727 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];" 6728 "}" 6729 "garbage = undefined;"); 6730} 6731 6732THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) { 6733 int interceptor_call_count = 0; 6734 v8::HandleScope scope; 6735 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6736 v8::Handle<v8::FunctionTemplate> method_templ = 6737 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 6738 v8_str("method_data"), 6739 v8::Handle<v8::Signature>()); 6740 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6741 proto_templ->Set(v8_str("method"), method_templ); 6742 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6743 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6744 NULL, NULL, NULL, NULL, 6745 v8::External::Wrap(&interceptor_call_count)); 6746 LocalContext context; 6747 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6748 GenerateSomeGarbage(); 6749 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6750 v8::Handle<Value> value = CompileRun( 6751 "var result = 0;" 6752 "for (var i = 0; i < 100; i++) {" 6753 " result = o.method(41);" 6754 "}"); 6755 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 6756 CHECK_EQ(100, interceptor_call_count); 6757} 6758 6759THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) { 6760 int interceptor_call_count = 0; 6761 v8::HandleScope scope; 6762 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6763 v8::Handle<v8::FunctionTemplate> method_templ = 6764 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6765 v8_str("method_data"), 6766 v8::Signature::New(fun_templ)); 6767 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6768 proto_templ->Set(v8_str("method"), method_templ); 6769 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6770 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6771 NULL, NULL, NULL, NULL, 6772 v8::External::Wrap(&interceptor_call_count)); 6773 LocalContext context; 6774 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6775 GenerateSomeGarbage(); 6776 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6777 v8::Handle<Value> value = CompileRun( 6778 "o.foo = 17;" 6779 "var receiver = {};" 6780 "receiver.__proto__ = o;" 6781 "var result = 0;" 6782 "for (var i = 0; i < 100; i++) {" 6783 " result = receiver.method(41);" 6784 "}"); 6785 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 6786 CHECK_EQ(100, interceptor_call_count); 6787} 6788 6789THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) { 6790 int interceptor_call_count = 0; 6791 v8::HandleScope scope; 6792 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6793 v8::Handle<v8::FunctionTemplate> method_templ = 6794 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6795 v8_str("method_data"), 6796 v8::Signature::New(fun_templ)); 6797 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6798 proto_templ->Set(v8_str("method"), method_templ); 6799 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6800 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6801 NULL, NULL, NULL, NULL, 6802 v8::External::Wrap(&interceptor_call_count)); 6803 LocalContext context; 6804 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6805 GenerateSomeGarbage(); 6806 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6807 v8::Handle<Value> value = CompileRun( 6808 "o.foo = 17;" 6809 "var receiver = {};" 6810 "receiver.__proto__ = o;" 6811 "var result = 0;" 6812 "var saved_result = 0;" 6813 "for (var i = 0; i < 100; i++) {" 6814 " result = receiver.method(41);" 6815 " if (i == 50) {" 6816 " saved_result = result;" 6817 " receiver = {method: function(x) { return x - 1 }};" 6818 " }" 6819 "}"); 6820 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 6821 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 6822 CHECK_GE(interceptor_call_count, 50); 6823} 6824 6825THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) { 6826 int interceptor_call_count = 0; 6827 v8::HandleScope scope; 6828 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6829 v8::Handle<v8::FunctionTemplate> method_templ = 6830 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6831 v8_str("method_data"), 6832 v8::Signature::New(fun_templ)); 6833 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6834 proto_templ->Set(v8_str("method"), method_templ); 6835 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6836 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6837 NULL, NULL, NULL, NULL, 6838 v8::External::Wrap(&interceptor_call_count)); 6839 LocalContext context; 6840 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6841 GenerateSomeGarbage(); 6842 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6843 v8::Handle<Value> value = CompileRun( 6844 "o.foo = 17;" 6845 "var receiver = {};" 6846 "receiver.__proto__ = o;" 6847 "var result = 0;" 6848 "var saved_result = 0;" 6849 "for (var i = 0; i < 100; i++) {" 6850 " result = receiver.method(41);" 6851 " if (i == 50) {" 6852 " saved_result = result;" 6853 " o.method = function(x) { return x - 1 };" 6854 " }" 6855 "}"); 6856 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 6857 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 6858 CHECK_GE(interceptor_call_count, 50); 6859} 6860 6861THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss3) { 6862 int interceptor_call_count = 0; 6863 v8::HandleScope scope; 6864 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6865 v8::Handle<v8::FunctionTemplate> method_templ = 6866 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6867 v8_str("method_data"), 6868 v8::Signature::New(fun_templ)); 6869 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6870 proto_templ->Set(v8_str("method"), method_templ); 6871 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6872 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6873 NULL, NULL, NULL, NULL, 6874 v8::External::Wrap(&interceptor_call_count)); 6875 LocalContext context; 6876 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6877 GenerateSomeGarbage(); 6878 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6879 v8::TryCatch try_catch; 6880 v8::Handle<Value> value = CompileRun( 6881 "o.foo = 17;" 6882 "var receiver = {};" 6883 "receiver.__proto__ = o;" 6884 "var result = 0;" 6885 "var saved_result = 0;" 6886 "for (var i = 0; i < 100; i++) {" 6887 " result = receiver.method(41);" 6888 " if (i == 50) {" 6889 " saved_result = result;" 6890 " receiver = 333;" 6891 " }" 6892 "}"); 6893 CHECK(try_catch.HasCaught()); 6894 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), 6895 try_catch.Exception()->ToString()); 6896 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 6897 CHECK_GE(interceptor_call_count, 50); 6898} 6899 6900THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) { 6901 int interceptor_call_count = 0; 6902 v8::HandleScope scope; 6903 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6904 v8::Handle<v8::FunctionTemplate> method_templ = 6905 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6906 v8_str("method_data"), 6907 v8::Signature::New(fun_templ)); 6908 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6909 proto_templ->Set(v8_str("method"), method_templ); 6910 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6911 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6912 NULL, NULL, NULL, NULL, 6913 v8::External::Wrap(&interceptor_call_count)); 6914 LocalContext context; 6915 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6916 GenerateSomeGarbage(); 6917 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6918 v8::TryCatch try_catch; 6919 v8::Handle<Value> value = CompileRun( 6920 "o.foo = 17;" 6921 "var receiver = {};" 6922 "receiver.__proto__ = o;" 6923 "var result = 0;" 6924 "var saved_result = 0;" 6925 "for (var i = 0; i < 100; i++) {" 6926 " result = receiver.method(41);" 6927 " if (i == 50) {" 6928 " saved_result = result;" 6929 " receiver = {method: receiver.method};" 6930 " }" 6931 "}"); 6932 CHECK(try_catch.HasCaught()); 6933 CHECK_EQ(v8_str("TypeError: Illegal invocation"), 6934 try_catch.Exception()->ToString()); 6935 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 6936 CHECK_GE(interceptor_call_count, 50); 6937} 6938 6939THREADED_TEST(CallICFastApi_TrivialSignature) { 6940 v8::HandleScope scope; 6941 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6942 v8::Handle<v8::FunctionTemplate> method_templ = 6943 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 6944 v8_str("method_data"), 6945 v8::Handle<v8::Signature>()); 6946 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6947 proto_templ->Set(v8_str("method"), method_templ); 6948 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6949 LocalContext context; 6950 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6951 GenerateSomeGarbage(); 6952 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6953 v8::Handle<Value> value = CompileRun( 6954 "var result = 0;" 6955 "for (var i = 0; i < 100; i++) {" 6956 " result = o.method(41);" 6957 "}"); 6958 6959 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 6960} 6961 6962THREADED_TEST(CallICFastApi_SimpleSignature) { 6963 v8::HandleScope scope; 6964 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6965 v8::Handle<v8::FunctionTemplate> method_templ = 6966 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6967 v8_str("method_data"), 6968 v8::Signature::New(fun_templ)); 6969 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6970 proto_templ->Set(v8_str("method"), method_templ); 6971 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6972 LocalContext context; 6973 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6974 GenerateSomeGarbage(); 6975 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6976 v8::Handle<Value> value = CompileRun( 6977 "o.foo = 17;" 6978 "var receiver = {};" 6979 "receiver.__proto__ = o;" 6980 "var result = 0;" 6981 "for (var i = 0; i < 100; i++) {" 6982 " result = receiver.method(41);" 6983 "}"); 6984 6985 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 6986} 6987 6988THREADED_TEST(CallICFastApi_SimpleSignature_Miss1) { 6989 v8::HandleScope scope; 6990 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6991 v8::Handle<v8::FunctionTemplate> method_templ = 6992 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6993 v8_str("method_data"), 6994 v8::Signature::New(fun_templ)); 6995 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6996 proto_templ->Set(v8_str("method"), method_templ); 6997 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6998 LocalContext context; 6999 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7000 GenerateSomeGarbage(); 7001 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7002 v8::Handle<Value> value = CompileRun( 7003 "o.foo = 17;" 7004 "var receiver = {};" 7005 "receiver.__proto__ = o;" 7006 "var result = 0;" 7007 "var saved_result = 0;" 7008 "for (var i = 0; i < 100; i++) {" 7009 " result = receiver.method(41);" 7010 " if (i == 50) {" 7011 " saved_result = result;" 7012 " receiver = {method: function(x) { return x - 1 }};" 7013 " }" 7014 "}"); 7015 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 7016 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7017} 7018 7019THREADED_TEST(CallICFastApi_SimpleSignature_Miss2) { 7020 v8::HandleScope scope; 7021 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 7022 v8::Handle<v8::FunctionTemplate> method_templ = 7023 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 7024 v8_str("method_data"), 7025 v8::Signature::New(fun_templ)); 7026 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 7027 proto_templ->Set(v8_str("method"), method_templ); 7028 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 7029 LocalContext context; 7030 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 7031 GenerateSomeGarbage(); 7032 context->Global()->Set(v8_str("o"), fun->NewInstance()); 7033 v8::TryCatch try_catch; 7034 v8::Handle<Value> value = CompileRun( 7035 "o.foo = 17;" 7036 "var receiver = {};" 7037 "receiver.__proto__ = o;" 7038 "var result = 0;" 7039 "var saved_result = 0;" 7040 "for (var i = 0; i < 100; i++) {" 7041 " result = receiver.method(41);" 7042 " if (i == 50) {" 7043 " saved_result = result;" 7044 " receiver = 333;" 7045 " }" 7046 "}"); 7047 CHECK(try_catch.HasCaught()); 7048 CHECK_EQ(v8_str("TypeError: Object 333 has no method 'method'"), 7049 try_catch.Exception()->ToString()); 7050 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 7051} 7052 7053 7054static int interceptor_call_count = 0; 7055 7056static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name, 7057 const AccessorInfo& info) { 7058 ApiTestFuzzer::Fuzz(); 7059 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) { 7060 return call_ic_function2; 7061 } 7062 return v8::Handle<Value>(); 7063} 7064 7065 7066// This test should hit load and call ICs for the interceptor case. 7067// Once in a while, the interceptor will reply that a property was not 7068// found in which case we should get a reference error. 7069THREADED_TEST(InterceptorICReferenceErrors) { 7070 v8::HandleScope scope; 7071 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7072 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter); 7073 LocalContext context(0, templ, v8::Handle<Value>()); 7074 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run(); 7075 v8::Handle<Value> value = CompileRun( 7076 "function f() {" 7077 " for (var i = 0; i < 1000; i++) {" 7078 " try { x; } catch(e) { return true; }" 7079 " }" 7080 " return false;" 7081 "};" 7082 "f();"); 7083 CHECK_EQ(true, value->BooleanValue()); 7084 interceptor_call_count = 0; 7085 value = CompileRun( 7086 "function g() {" 7087 " for (var i = 0; i < 1000; i++) {" 7088 " try { x(42); } catch(e) { return true; }" 7089 " }" 7090 " return false;" 7091 "};" 7092 "g();"); 7093 CHECK_EQ(true, value->BooleanValue()); 7094} 7095 7096 7097static int interceptor_ic_exception_get_count = 0; 7098 7099static v8::Handle<Value> InterceptorICExceptionGetter( 7100 Local<String> name, 7101 const AccessorInfo& info) { 7102 ApiTestFuzzer::Fuzz(); 7103 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) { 7104 return call_ic_function3; 7105 } 7106 if (interceptor_ic_exception_get_count == 20) { 7107 return v8::ThrowException(v8_num(42)); 7108 } 7109 // Do not handle get for properties other than x. 7110 return v8::Handle<Value>(); 7111} 7112 7113// Test interceptor load/call IC where the interceptor throws an 7114// exception once in a while. 7115THREADED_TEST(InterceptorICGetterExceptions) { 7116 interceptor_ic_exception_get_count = 0; 7117 v8::HandleScope scope; 7118 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7119 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter); 7120 LocalContext context(0, templ, v8::Handle<Value>()); 7121 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run(); 7122 v8::Handle<Value> value = CompileRun( 7123 "function f() {" 7124 " for (var i = 0; i < 100; i++) {" 7125 " try { x; } catch(e) { return true; }" 7126 " }" 7127 " return false;" 7128 "};" 7129 "f();"); 7130 CHECK_EQ(true, value->BooleanValue()); 7131 interceptor_ic_exception_get_count = 0; 7132 value = CompileRun( 7133 "function f() {" 7134 " for (var i = 0; i < 100; i++) {" 7135 " try { x(42); } catch(e) { return true; }" 7136 " }" 7137 " return false;" 7138 "};" 7139 "f();"); 7140 CHECK_EQ(true, value->BooleanValue()); 7141} 7142 7143 7144static int interceptor_ic_exception_set_count = 0; 7145 7146static v8::Handle<Value> InterceptorICExceptionSetter( 7147 Local<String> key, Local<Value> value, const AccessorInfo&) { 7148 ApiTestFuzzer::Fuzz(); 7149 if (++interceptor_ic_exception_set_count > 20) { 7150 return v8::ThrowException(v8_num(42)); 7151 } 7152 // Do not actually handle setting. 7153 return v8::Handle<Value>(); 7154} 7155 7156// Test interceptor store IC where the interceptor throws an exception 7157// once in a while. 7158THREADED_TEST(InterceptorICSetterExceptions) { 7159 interceptor_ic_exception_set_count = 0; 7160 v8::HandleScope scope; 7161 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7162 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter); 7163 LocalContext context(0, templ, v8::Handle<Value>()); 7164 v8::Handle<Value> value = CompileRun( 7165 "function f() {" 7166 " for (var i = 0; i < 100; i++) {" 7167 " try { x = 42; } catch(e) { return true; }" 7168 " }" 7169 " return false;" 7170 "};" 7171 "f();"); 7172 CHECK_EQ(true, value->BooleanValue()); 7173} 7174 7175 7176// Test that we ignore null interceptors. 7177THREADED_TEST(NullNamedInterceptor) { 7178 v8::HandleScope scope; 7179 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7180 templ->SetNamedPropertyHandler(0); 7181 LocalContext context; 7182 templ->Set("x", v8_num(42)); 7183 v8::Handle<v8::Object> obj = templ->NewInstance(); 7184 context->Global()->Set(v8_str("obj"), obj); 7185 v8::Handle<Value> value = CompileRun("obj.x"); 7186 CHECK(value->IsInt32()); 7187 CHECK_EQ(42, value->Int32Value()); 7188} 7189 7190 7191// Test that we ignore null interceptors. 7192THREADED_TEST(NullIndexedInterceptor) { 7193 v8::HandleScope scope; 7194 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 7195 templ->SetIndexedPropertyHandler(0); 7196 LocalContext context; 7197 templ->Set("42", v8_num(42)); 7198 v8::Handle<v8::Object> obj = templ->NewInstance(); 7199 context->Global()->Set(v8_str("obj"), obj); 7200 v8::Handle<Value> value = CompileRun("obj[42]"); 7201 CHECK(value->IsInt32()); 7202 CHECK_EQ(42, value->Int32Value()); 7203} 7204 7205 7206static v8::Handle<Value> ParentGetter(Local<String> name, 7207 const AccessorInfo& info) { 7208 ApiTestFuzzer::Fuzz(); 7209 return v8_num(1); 7210} 7211 7212 7213static v8::Handle<Value> ChildGetter(Local<String> name, 7214 const AccessorInfo& info) { 7215 ApiTestFuzzer::Fuzz(); 7216 return v8_num(42); 7217} 7218 7219 7220THREADED_TEST(Overriding) { 7221 v8::HandleScope scope; 7222 LocalContext context; 7223 7224 // Parent template. 7225 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New(); 7226 Local<ObjectTemplate> parent_instance_templ = 7227 parent_templ->InstanceTemplate(); 7228 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter); 7229 7230 // Template that inherits from the parent template. 7231 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New(); 7232 Local<ObjectTemplate> child_instance_templ = 7233 child_templ->InstanceTemplate(); 7234 child_templ->Inherit(parent_templ); 7235 // Override 'f'. The child version of 'f' should get called for child 7236 // instances. 7237 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter); 7238 // Add 'g' twice. The 'g' added last should get called for instances. 7239 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter); 7240 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter); 7241 7242 // Add 'h' as an accessor to the proto template with ReadOnly attributes 7243 // so 'h' can be shadowed on the instance object. 7244 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate(); 7245 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0, 7246 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 7247 7248 // Add 'i' as an accessor to the instance template with ReadOnly attributes 7249 // but the attribute does not have effect because it is duplicated with 7250 // NULL setter. 7251 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0, 7252 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 7253 7254 7255 7256 // Instantiate the child template. 7257 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance(); 7258 7259 // Check that the child function overrides the parent one. 7260 context->Global()->Set(v8_str("o"), instance); 7261 Local<Value> value = v8_compile("o.f")->Run(); 7262 // Check that the 'g' that was added last is hit. 7263 CHECK_EQ(42, value->Int32Value()); 7264 value = v8_compile("o.g")->Run(); 7265 CHECK_EQ(42, value->Int32Value()); 7266 7267 // Check 'h' can be shadowed. 7268 value = v8_compile("o.h = 3; o.h")->Run(); 7269 CHECK_EQ(3, value->Int32Value()); 7270 7271 // Check 'i' is cannot be shadowed or changed. 7272 value = v8_compile("o.i = 3; o.i")->Run(); 7273 CHECK_EQ(42, value->Int32Value()); 7274} 7275 7276 7277static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) { 7278 ApiTestFuzzer::Fuzz(); 7279 if (args.IsConstructCall()) { 7280 return v8::Boolean::New(true); 7281 } 7282 return v8::Boolean::New(false); 7283} 7284 7285 7286THREADED_TEST(IsConstructCall) { 7287 v8::HandleScope scope; 7288 7289 // Function template with call handler. 7290 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 7291 templ->SetCallHandler(IsConstructHandler); 7292 7293 LocalContext context; 7294 7295 context->Global()->Set(v8_str("f"), templ->GetFunction()); 7296 Local<Value> value = v8_compile("f()")->Run(); 7297 CHECK(!value->BooleanValue()); 7298 value = v8_compile("new f()")->Run(); 7299 CHECK(value->BooleanValue()); 7300} 7301 7302 7303THREADED_TEST(ObjectProtoToString) { 7304 v8::HandleScope scope; 7305 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 7306 templ->SetClassName(v8_str("MyClass")); 7307 7308 LocalContext context; 7309 7310 Local<String> customized_tostring = v8_str("customized toString"); 7311 7312 // Replace Object.prototype.toString 7313 v8_compile("Object.prototype.toString = function() {" 7314 " return 'customized toString';" 7315 "}")->Run(); 7316 7317 // Normal ToString call should call replaced Object.prototype.toString 7318 Local<v8::Object> instance = templ->GetFunction()->NewInstance(); 7319 Local<String> value = instance->ToString(); 7320 CHECK(value->IsString() && value->Equals(customized_tostring)); 7321 7322 // ObjectProtoToString should not call replace toString function. 7323 value = instance->ObjectProtoToString(); 7324 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]"))); 7325 7326 // Check global 7327 value = context->Global()->ObjectProtoToString(); 7328 CHECK(value->IsString() && value->Equals(v8_str("[object global]"))); 7329 7330 // Check ordinary object 7331 Local<Value> object = v8_compile("new Object()")->Run(); 7332 value = object.As<v8::Object>()->ObjectProtoToString(); 7333 CHECK(value->IsString() && value->Equals(v8_str("[object Object]"))); 7334} 7335 7336 7337bool ApiTestFuzzer::fuzzing_ = false; 7338v8::internal::Semaphore* ApiTestFuzzer::all_tests_done_= 7339 v8::internal::OS::CreateSemaphore(0); 7340int ApiTestFuzzer::active_tests_; 7341int ApiTestFuzzer::tests_being_run_; 7342int ApiTestFuzzer::current_; 7343 7344 7345// We are in a callback and want to switch to another thread (if we 7346// are currently running the thread fuzzing test). 7347void ApiTestFuzzer::Fuzz() { 7348 if (!fuzzing_) return; 7349 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_; 7350 test->ContextSwitch(); 7351} 7352 7353 7354// Let the next thread go. Since it is also waiting on the V8 lock it may 7355// not start immediately. 7356bool ApiTestFuzzer::NextThread() { 7357 int test_position = GetNextTestNumber(); 7358 const char* test_name = RegisterThreadedTest::nth(current_)->name(); 7359 if (test_position == current_) { 7360 if (kLogThreading) 7361 printf("Stay with %s\n", test_name); 7362 return false; 7363 } 7364 if (kLogThreading) { 7365 printf("Switch from %s to %s\n", 7366 test_name, 7367 RegisterThreadedTest::nth(test_position)->name()); 7368 } 7369 current_ = test_position; 7370 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal(); 7371 return true; 7372} 7373 7374 7375void ApiTestFuzzer::Run() { 7376 // When it is our turn... 7377 gate_->Wait(); 7378 { 7379 // ... get the V8 lock and start running the test. 7380 v8::Locker locker; 7381 CallTest(); 7382 } 7383 // This test finished. 7384 active_ = false; 7385 active_tests_--; 7386 // If it was the last then signal that fact. 7387 if (active_tests_ == 0) { 7388 all_tests_done_->Signal(); 7389 } else { 7390 // Otherwise select a new test and start that. 7391 NextThread(); 7392 } 7393} 7394 7395 7396static unsigned linear_congruential_generator; 7397 7398 7399void ApiTestFuzzer::Setup(PartOfTest part) { 7400 linear_congruential_generator = i::FLAG_testing_prng_seed; 7401 fuzzing_ = true; 7402 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1); 7403 int end = (part == FIRST_PART) 7404 ? (RegisterThreadedTest::count() >> 1) 7405 : RegisterThreadedTest::count(); 7406 active_tests_ = tests_being_run_ = end - start; 7407 for (int i = 0; i < tests_being_run_; i++) { 7408 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start); 7409 } 7410 for (int i = 0; i < active_tests_; i++) { 7411 RegisterThreadedTest::nth(i)->fuzzer_->Start(); 7412 } 7413} 7414 7415 7416static void CallTestNumber(int test_number) { 7417 (RegisterThreadedTest::nth(test_number)->callback())(); 7418} 7419 7420 7421void ApiTestFuzzer::RunAllTests() { 7422 // Set off the first test. 7423 current_ = -1; 7424 NextThread(); 7425 // Wait till they are all done. 7426 all_tests_done_->Wait(); 7427} 7428 7429 7430int ApiTestFuzzer::GetNextTestNumber() { 7431 int next_test; 7432 do { 7433 next_test = (linear_congruential_generator >> 16) % tests_being_run_; 7434 linear_congruential_generator *= 1664525u; 7435 linear_congruential_generator += 1013904223u; 7436 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_); 7437 return next_test; 7438} 7439 7440 7441void ApiTestFuzzer::ContextSwitch() { 7442 // If the new thread is the same as the current thread there is nothing to do. 7443 if (NextThread()) { 7444 // Now it can start. 7445 v8::Unlocker unlocker; 7446 // Wait till someone starts us again. 7447 gate_->Wait(); 7448 // And we're off. 7449 } 7450} 7451 7452 7453void ApiTestFuzzer::TearDown() { 7454 fuzzing_ = false; 7455 for (int i = 0; i < RegisterThreadedTest::count(); i++) { 7456 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_; 7457 if (fuzzer != NULL) fuzzer->Join(); 7458 } 7459} 7460 7461 7462// Lets not be needlessly self-referential. 7463TEST(Threading) { 7464 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART); 7465 ApiTestFuzzer::RunAllTests(); 7466 ApiTestFuzzer::TearDown(); 7467} 7468 7469TEST(Threading2) { 7470 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART); 7471 ApiTestFuzzer::RunAllTests(); 7472 ApiTestFuzzer::TearDown(); 7473} 7474 7475 7476void ApiTestFuzzer::CallTest() { 7477 if (kLogThreading) 7478 printf("Start test %d\n", test_number_); 7479 CallTestNumber(test_number_); 7480 if (kLogThreading) 7481 printf("End test %d\n", test_number_); 7482} 7483 7484 7485static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) { 7486 CHECK(v8::Locker::IsLocked()); 7487 ApiTestFuzzer::Fuzz(); 7488 v8::Unlocker unlocker; 7489 const char* code = "throw 7;"; 7490 { 7491 v8::Locker nested_locker; 7492 v8::HandleScope scope; 7493 v8::Handle<Value> exception; 7494 { v8::TryCatch try_catch; 7495 v8::Handle<Value> value = CompileRun(code); 7496 CHECK(value.IsEmpty()); 7497 CHECK(try_catch.HasCaught()); 7498 // Make sure to wrap the exception in a new handle because 7499 // the handle returned from the TryCatch is destroyed 7500 // when the TryCatch is destroyed. 7501 exception = Local<Value>::New(try_catch.Exception()); 7502 } 7503 return v8::ThrowException(exception); 7504 } 7505} 7506 7507 7508static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) { 7509 CHECK(v8::Locker::IsLocked()); 7510 ApiTestFuzzer::Fuzz(); 7511 v8::Unlocker unlocker; 7512 const char* code = "throw 7;"; 7513 { 7514 v8::Locker nested_locker; 7515 v8::HandleScope scope; 7516 v8::Handle<Value> value = CompileRun(code); 7517 CHECK(value.IsEmpty()); 7518 return v8_str("foo"); 7519 } 7520} 7521 7522 7523// These are locking tests that don't need to be run again 7524// as part of the locking aggregation tests. 7525TEST(NestedLockers) { 7526 v8::Locker locker; 7527 CHECK(v8::Locker::IsLocked()); 7528 v8::HandleScope scope; 7529 LocalContext env; 7530 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS); 7531 Local<Function> fun = fun_templ->GetFunction(); 7532 env->Global()->Set(v8_str("throw_in_js"), fun); 7533 Local<Script> script = v8_compile("(function () {" 7534 " try {" 7535 " throw_in_js();" 7536 " return 42;" 7537 " } catch (e) {" 7538 " return e * 13;" 7539 " }" 7540 "})();"); 7541 CHECK_EQ(91, script->Run()->Int32Value()); 7542} 7543 7544 7545// These are locking tests that don't need to be run again 7546// as part of the locking aggregation tests. 7547TEST(NestedLockersNoTryCatch) { 7548 v8::Locker locker; 7549 v8::HandleScope scope; 7550 LocalContext env; 7551 Local<v8::FunctionTemplate> fun_templ = 7552 v8::FunctionTemplate::New(ThrowInJSNoCatch); 7553 Local<Function> fun = fun_templ->GetFunction(); 7554 env->Global()->Set(v8_str("throw_in_js"), fun); 7555 Local<Script> script = v8_compile("(function () {" 7556 " try {" 7557 " throw_in_js();" 7558 " return 42;" 7559 " } catch (e) {" 7560 " return e * 13;" 7561 " }" 7562 "})();"); 7563 CHECK_EQ(91, script->Run()->Int32Value()); 7564} 7565 7566 7567THREADED_TEST(RecursiveLocking) { 7568 v8::Locker locker; 7569 { 7570 v8::Locker locker2; 7571 CHECK(v8::Locker::IsLocked()); 7572 } 7573} 7574 7575 7576static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) { 7577 ApiTestFuzzer::Fuzz(); 7578 v8::Unlocker unlocker; 7579 return v8::Undefined(); 7580} 7581 7582 7583THREADED_TEST(LockUnlockLock) { 7584 { 7585 v8::Locker locker; 7586 v8::HandleScope scope; 7587 LocalContext env; 7588 Local<v8::FunctionTemplate> fun_templ = 7589 v8::FunctionTemplate::New(UnlockForAMoment); 7590 Local<Function> fun = fun_templ->GetFunction(); 7591 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 7592 Local<Script> script = v8_compile("(function () {" 7593 " unlock_for_a_moment();" 7594 " return 42;" 7595 "})();"); 7596 CHECK_EQ(42, script->Run()->Int32Value()); 7597 } 7598 { 7599 v8::Locker locker; 7600 v8::HandleScope scope; 7601 LocalContext env; 7602 Local<v8::FunctionTemplate> fun_templ = 7603 v8::FunctionTemplate::New(UnlockForAMoment); 7604 Local<Function> fun = fun_templ->GetFunction(); 7605 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 7606 Local<Script> script = v8_compile("(function () {" 7607 " unlock_for_a_moment();" 7608 " return 42;" 7609 "})();"); 7610 CHECK_EQ(42, script->Run()->Int32Value()); 7611 } 7612} 7613 7614 7615static int GetGlobalObjectsCount() { 7616 int count = 0; 7617 v8::internal::HeapIterator it; 7618 for (i::HeapObject* object = it.next(); object != NULL; object = it.next()) 7619 if (object->IsJSGlobalObject()) count++; 7620 return count; 7621} 7622 7623 7624static int GetSurvivingGlobalObjectsCount() { 7625 // We need to collect all garbage twice to be sure that everything 7626 // has been collected. This is because inline caches are cleared in 7627 // the first garbage collection but some of the maps have already 7628 // been marked at that point. Therefore some of the maps are not 7629 // collected until the second garbage collection. 7630 v8::internal::Heap::CollectAllGarbage(false); 7631 v8::internal::Heap::CollectAllGarbage(false); 7632 int count = GetGlobalObjectsCount(); 7633#ifdef DEBUG 7634 if (count > 0) v8::internal::Heap::TracePathToGlobal(); 7635#endif 7636 return count; 7637} 7638 7639 7640TEST(DontLeakGlobalObjects) { 7641 // Regression test for issues 1139850 and 1174891. 7642 7643 v8::V8::Initialize(); 7644 7645 int count = GetSurvivingGlobalObjectsCount(); 7646 7647 for (int i = 0; i < 5; i++) { 7648 { v8::HandleScope scope; 7649 LocalContext context; 7650 } 7651 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 7652 7653 { v8::HandleScope scope; 7654 LocalContext context; 7655 v8_compile("Date")->Run(); 7656 } 7657 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 7658 7659 { v8::HandleScope scope; 7660 LocalContext context; 7661 v8_compile("/aaa/")->Run(); 7662 } 7663 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 7664 7665 { v8::HandleScope scope; 7666 const char* extension_list[] = { "v8/gc" }; 7667 v8::ExtensionConfiguration extensions(1, extension_list); 7668 LocalContext context(&extensions); 7669 v8_compile("gc();")->Run(); 7670 } 7671 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 7672 } 7673} 7674 7675 7676v8::Persistent<v8::Object> some_object; 7677v8::Persistent<v8::Object> bad_handle; 7678 7679void NewPersistentHandleCallback(v8::Persistent<v8::Value>, void*) { 7680 v8::HandleScope scope; 7681 bad_handle = v8::Persistent<v8::Object>::New(some_object); 7682} 7683 7684 7685THREADED_TEST(NewPersistentHandleFromWeakCallback) { 7686 LocalContext context; 7687 7688 v8::Persistent<v8::Object> handle1, handle2; 7689 { 7690 v8::HandleScope scope; 7691 some_object = v8::Persistent<v8::Object>::New(v8::Object::New()); 7692 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7693 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7694 } 7695 // Note: order is implementation dependent alas: currently 7696 // global handle nodes are processed by PostGarbageCollectionProcessing 7697 // in reverse allocation order, so if second allocated handle is deleted, 7698 // weak callback of the first handle would be able to 'reallocate' it. 7699 handle1.MakeWeak(NULL, NewPersistentHandleCallback); 7700 handle2.Dispose(); 7701 i::Heap::CollectAllGarbage(false); 7702} 7703 7704 7705v8::Persistent<v8::Object> to_be_disposed; 7706 7707void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) { 7708 to_be_disposed.Dispose(); 7709 i::Heap::CollectAllGarbage(false); 7710} 7711 7712 7713THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) { 7714 LocalContext context; 7715 7716 v8::Persistent<v8::Object> handle1, handle2; 7717 { 7718 v8::HandleScope scope; 7719 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7720 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7721 } 7722 handle1.MakeWeak(NULL, DisposeAndForceGcCallback); 7723 to_be_disposed = handle2; 7724 i::Heap::CollectAllGarbage(false); 7725} 7726 7727void DisposingCallback(v8::Persistent<v8::Value> handle, void*) { 7728 handle.Dispose(); 7729} 7730 7731void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) { 7732 v8::HandleScope scope; 7733 v8::Persistent<v8::Object>::New(v8::Object::New()); 7734} 7735 7736 7737THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) { 7738 LocalContext context; 7739 7740 v8::Persistent<v8::Object> handle1, handle2, handle3; 7741 { 7742 v8::HandleScope scope; 7743 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7744 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7745 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7746 } 7747 handle2.MakeWeak(NULL, DisposingCallback); 7748 handle3.MakeWeak(NULL, HandleCreatingCallback); 7749 i::Heap::CollectAllGarbage(false); 7750} 7751 7752 7753THREADED_TEST(CheckForCrossContextObjectLiterals) { 7754 v8::V8::Initialize(); 7755 7756 const int nof = 2; 7757 const char* sources[nof] = { 7758 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }", 7759 "Object()" 7760 }; 7761 7762 for (int i = 0; i < nof; i++) { 7763 const char* source = sources[i]; 7764 { v8::HandleScope scope; 7765 LocalContext context; 7766 CompileRun(source); 7767 } 7768 { v8::HandleScope scope; 7769 LocalContext context; 7770 CompileRun(source); 7771 } 7772 } 7773} 7774 7775 7776static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) { 7777 v8::HandleScope inner; 7778 env->Enter(); 7779 v8::Handle<Value> three = v8_num(3); 7780 v8::Handle<Value> value = inner.Close(three); 7781 env->Exit(); 7782 return value; 7783} 7784 7785 7786THREADED_TEST(NestedHandleScopeAndContexts) { 7787 v8::HandleScope outer; 7788 v8::Persistent<Context> env = Context::New(); 7789 env->Enter(); 7790 v8::Handle<Value> value = NestedScope(env); 7791 v8::Handle<String> str = value->ToString(); 7792 env->Exit(); 7793 env.Dispose(); 7794} 7795 7796 7797THREADED_TEST(ExternalAllocatedMemory) { 7798 v8::HandleScope outer; 7799 v8::Persistent<Context> env = Context::New(); 7800 const int kSize = 1024*1024; 7801 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize); 7802 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0); 7803} 7804 7805 7806THREADED_TEST(DisposeEnteredContext) { 7807 v8::HandleScope scope; 7808 LocalContext outer; 7809 { v8::Persistent<v8::Context> inner = v8::Context::New(); 7810 inner->Enter(); 7811 inner.Dispose(); 7812 inner.Clear(); 7813 inner->Exit(); 7814 } 7815} 7816 7817 7818// Regression test for issue 54, object templates with internal fields 7819// but no accessors or interceptors did not get their internal field 7820// count set on instances. 7821THREADED_TEST(Regress54) { 7822 v8::HandleScope outer; 7823 LocalContext context; 7824 static v8::Persistent<v8::ObjectTemplate> templ; 7825 if (templ.IsEmpty()) { 7826 v8::HandleScope inner; 7827 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New(); 7828 local->SetInternalFieldCount(1); 7829 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local)); 7830 } 7831 v8::Handle<v8::Object> result = templ->NewInstance(); 7832 CHECK_EQ(1, result->InternalFieldCount()); 7833} 7834 7835 7836// If part of the threaded tests, this test makes ThreadingTest fail 7837// on mac. 7838TEST(CatchStackOverflow) { 7839 v8::HandleScope scope; 7840 LocalContext context; 7841 v8::TryCatch try_catch; 7842 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New( 7843 "function f() {" 7844 " return f();" 7845 "}" 7846 "" 7847 "f();")); 7848 v8::Handle<v8::Value> result = script->Run(); 7849 CHECK(result.IsEmpty()); 7850} 7851 7852 7853static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script, 7854 const char* resource_name, 7855 int line_offset) { 7856 v8::HandleScope scope; 7857 v8::TryCatch try_catch; 7858 v8::Handle<v8::Value> result = script->Run(); 7859 CHECK(result.IsEmpty()); 7860 CHECK(try_catch.HasCaught()); 7861 v8::Handle<v8::Message> message = try_catch.Message(); 7862 CHECK(!message.IsEmpty()); 7863 CHECK_EQ(10 + line_offset, message->GetLineNumber()); 7864 CHECK_EQ(91, message->GetStartPosition()); 7865 CHECK_EQ(92, message->GetEndPosition()); 7866 CHECK_EQ(2, message->GetStartColumn()); 7867 CHECK_EQ(3, message->GetEndColumn()); 7868 v8::String::AsciiValue line(message->GetSourceLine()); 7869 CHECK_EQ(" throw 'nirk';", *line); 7870 v8::String::AsciiValue name(message->GetScriptResourceName()); 7871 CHECK_EQ(resource_name, *name); 7872} 7873 7874 7875THREADED_TEST(TryCatchSourceInfo) { 7876 v8::HandleScope scope; 7877 LocalContext context; 7878 v8::Handle<v8::String> source = v8::String::New( 7879 "function Foo() {\n" 7880 " return Bar();\n" 7881 "}\n" 7882 "\n" 7883 "function Bar() {\n" 7884 " return Baz();\n" 7885 "}\n" 7886 "\n" 7887 "function Baz() {\n" 7888 " throw 'nirk';\n" 7889 "}\n" 7890 "\n" 7891 "Foo();\n"); 7892 7893 const char* resource_name; 7894 v8::Handle<v8::Script> script; 7895 resource_name = "test.js"; 7896 script = v8::Script::Compile(source, v8::String::New(resource_name)); 7897 CheckTryCatchSourceInfo(script, resource_name, 0); 7898 7899 resource_name = "test1.js"; 7900 v8::ScriptOrigin origin1(v8::String::New(resource_name)); 7901 script = v8::Script::Compile(source, &origin1); 7902 CheckTryCatchSourceInfo(script, resource_name, 0); 7903 7904 resource_name = "test2.js"; 7905 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7)); 7906 script = v8::Script::Compile(source, &origin2); 7907 CheckTryCatchSourceInfo(script, resource_name, 7); 7908} 7909 7910 7911THREADED_TEST(CompilationCache) { 7912 v8::HandleScope scope; 7913 LocalContext context; 7914 v8::Handle<v8::String> source0 = v8::String::New("1234"); 7915 v8::Handle<v8::String> source1 = v8::String::New("1234"); 7916 v8::Handle<v8::Script> script0 = 7917 v8::Script::Compile(source0, v8::String::New("test.js")); 7918 v8::Handle<v8::Script> script1 = 7919 v8::Script::Compile(source1, v8::String::New("test.js")); 7920 v8::Handle<v8::Script> script2 = 7921 v8::Script::Compile(source0); // different origin 7922 CHECK_EQ(1234, script0->Run()->Int32Value()); 7923 CHECK_EQ(1234, script1->Run()->Int32Value()); 7924 CHECK_EQ(1234, script2->Run()->Int32Value()); 7925} 7926 7927 7928static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) { 7929 ApiTestFuzzer::Fuzz(); 7930 return v8_num(42); 7931} 7932 7933 7934THREADED_TEST(CallbackFunctionName) { 7935 v8::HandleScope scope; 7936 LocalContext context; 7937 Local<ObjectTemplate> t = ObjectTemplate::New(); 7938 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback)); 7939 context->Global()->Set(v8_str("obj"), t->NewInstance()); 7940 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name"); 7941 CHECK(value->IsString()); 7942 v8::String::AsciiValue name(value); 7943 CHECK_EQ("asdf", *name); 7944} 7945 7946 7947THREADED_TEST(DateAccess) { 7948 v8::HandleScope scope; 7949 LocalContext context; 7950 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0); 7951 CHECK(date->IsDate()); 7952 CHECK_EQ(1224744689038.0, date.As<v8::Date>()->NumberValue()); 7953} 7954 7955 7956void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) { 7957 v8::Handle<v8::Object> obj = val.As<v8::Object>(); 7958 v8::Handle<v8::Array> props = obj->GetPropertyNames(); 7959 CHECK_EQ(elmc, props->Length()); 7960 for (int i = 0; i < elmc; i++) { 7961 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i))); 7962 CHECK_EQ(elmv[i], *elm); 7963 } 7964} 7965 7966 7967THREADED_TEST(PropertyEnumeration) { 7968 v8::HandleScope scope; 7969 LocalContext context; 7970 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New( 7971 "var result = [];" 7972 "result[0] = {};" 7973 "result[1] = {a: 1, b: 2};" 7974 "result[2] = [1, 2, 3];" 7975 "var proto = {x: 1, y: 2, z: 3};" 7976 "var x = { __proto__: proto, w: 0, z: 1 };" 7977 "result[3] = x;" 7978 "result;"))->Run(); 7979 v8::Handle<v8::Array> elms = obj.As<v8::Array>(); 7980 CHECK_EQ(4, elms->Length()); 7981 int elmc0 = 0; 7982 const char** elmv0 = NULL; 7983 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0); 7984 int elmc1 = 2; 7985 const char* elmv1[] = {"a", "b"}; 7986 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1); 7987 int elmc2 = 3; 7988 const char* elmv2[] = {"0", "1", "2"}; 7989 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2); 7990 int elmc3 = 4; 7991 const char* elmv3[] = {"w", "z", "x", "y"}; 7992 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3); 7993} 7994 7995 7996static bool NamedSetAccessBlocker(Local<v8::Object> obj, 7997 Local<Value> name, 7998 v8::AccessType type, 7999 Local<Value> data) { 8000 return type != v8::ACCESS_SET; 8001} 8002 8003 8004static bool IndexedSetAccessBlocker(Local<v8::Object> obj, 8005 uint32_t key, 8006 v8::AccessType type, 8007 Local<Value> data) { 8008 return type != v8::ACCESS_SET; 8009} 8010 8011 8012THREADED_TEST(DisableAccessChecksWhileConfiguring) { 8013 v8::HandleScope scope; 8014 LocalContext context; 8015 Local<ObjectTemplate> templ = ObjectTemplate::New(); 8016 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker, 8017 IndexedSetAccessBlocker); 8018 templ->Set(v8_str("x"), v8::True()); 8019 Local<v8::Object> instance = templ->NewInstance(); 8020 context->Global()->Set(v8_str("obj"), instance); 8021 Local<Value> value = CompileRun("obj.x"); 8022 CHECK(value->BooleanValue()); 8023} 8024 8025 8026static bool NamedGetAccessBlocker(Local<v8::Object> obj, 8027 Local<Value> name, 8028 v8::AccessType type, 8029 Local<Value> data) { 8030 return false; 8031} 8032 8033 8034static bool IndexedGetAccessBlocker(Local<v8::Object> obj, 8035 uint32_t key, 8036 v8::AccessType type, 8037 Local<Value> data) { 8038 return false; 8039} 8040 8041 8042 8043THREADED_TEST(AccessChecksReenabledCorrectly) { 8044 v8::HandleScope scope; 8045 LocalContext context; 8046 Local<ObjectTemplate> templ = ObjectTemplate::New(); 8047 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker, 8048 IndexedGetAccessBlocker); 8049 templ->Set(v8_str("a"), v8_str("a")); 8050 // Add more than 8 (see kMaxFastProperties) properties 8051 // so that the constructor will force copying map. 8052 // Cannot sprintf, gcc complains unsafety. 8053 char buf[4]; 8054 for (char i = '0'; i <= '9' ; i++) { 8055 buf[0] = i; 8056 for (char j = '0'; j <= '9'; j++) { 8057 buf[1] = j; 8058 for (char k = '0'; k <= '9'; k++) { 8059 buf[2] = k; 8060 buf[3] = 0; 8061 templ->Set(v8_str(buf), v8::Number::New(k)); 8062 } 8063 } 8064 } 8065 8066 Local<v8::Object> instance_1 = templ->NewInstance(); 8067 context->Global()->Set(v8_str("obj_1"), instance_1); 8068 8069 Local<Value> value_1 = CompileRun("obj_1.a"); 8070 CHECK(value_1->IsUndefined()); 8071 8072 Local<v8::Object> instance_2 = templ->NewInstance(); 8073 context->Global()->Set(v8_str("obj_2"), instance_2); 8074 8075 Local<Value> value_2 = CompileRun("obj_2.a"); 8076 CHECK(value_2->IsUndefined()); 8077} 8078 8079 8080// This tests that access check information remains on the global 8081// object template when creating contexts. 8082THREADED_TEST(AccessControlRepeatedContextCreation) { 8083 v8::HandleScope handle_scope; 8084 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 8085 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker, 8086 IndexedSetAccessBlocker); 8087 i::Handle<i::ObjectTemplateInfo> internal_template = 8088 v8::Utils::OpenHandle(*global_template); 8089 CHECK(!internal_template->constructor()->IsUndefined()); 8090 i::Handle<i::FunctionTemplateInfo> constructor( 8091 i::FunctionTemplateInfo::cast(internal_template->constructor())); 8092 CHECK(!constructor->access_check_info()->IsUndefined()); 8093 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 8094 CHECK(!constructor->access_check_info()->IsUndefined()); 8095} 8096 8097 8098THREADED_TEST(TurnOnAccessCheck) { 8099 v8::HandleScope handle_scope; 8100 8101 // Create an environment with access check to the global object disabled by 8102 // default. 8103 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 8104 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker, 8105 IndexedGetAccessBlocker, 8106 v8::Handle<v8::Value>(), 8107 false); 8108 v8::Persistent<Context> context = Context::New(NULL, global_template); 8109 Context::Scope context_scope(context); 8110 8111 // Set up a property and a number of functions. 8112 context->Global()->Set(v8_str("a"), v8_num(1)); 8113 CompileRun("function f1() {return a;}" 8114 "function f2() {return a;}" 8115 "function g1() {return h();}" 8116 "function g2() {return h();}" 8117 "function h() {return 1;}"); 8118 Local<Function> f1 = 8119 Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); 8120 Local<Function> f2 = 8121 Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); 8122 Local<Function> g1 = 8123 Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); 8124 Local<Function> g2 = 8125 Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); 8126 Local<Function> h = 8127 Local<Function>::Cast(context->Global()->Get(v8_str("h"))); 8128 8129 // Get the global object. 8130 v8::Handle<v8::Object> global = context->Global(); 8131 8132 // Call f1 one time and f2 a number of times. This will ensure that f1 still 8133 // uses the runtime system to retreive property a whereas f2 uses global load 8134 // inline cache. 8135 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1))); 8136 for (int i = 0; i < 4; i++) { 8137 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1))); 8138 } 8139 8140 // Same for g1 and g2. 8141 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1))); 8142 for (int i = 0; i < 4; i++) { 8143 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1))); 8144 } 8145 8146 // Detach the global and turn on access check. 8147 context->DetachGlobal(); 8148 context->Global()->TurnOnAccessCheck(); 8149 8150 // Failing access check to property get results in undefined. 8151 CHECK(f1->Call(global, 0, NULL)->IsUndefined()); 8152 CHECK(f2->Call(global, 0, NULL)->IsUndefined()); 8153 8154 // Failing access check to function call results in exception. 8155 CHECK(g1->Call(global, 0, NULL).IsEmpty()); 8156 CHECK(g2->Call(global, 0, NULL).IsEmpty()); 8157 8158 // No failing access check when just returning a constant. 8159 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1))); 8160} 8161 8162 8163// This test verifies that pre-compilation (aka preparsing) can be called 8164// without initializing the whole VM. Thus we cannot run this test in a 8165// multi-threaded setup. 8166TEST(PreCompile) { 8167 // TODO(155): This test would break without the initialization of V8. This is 8168 // a workaround for now to make this test not fail. 8169 v8::V8::Initialize(); 8170 const char* script = "function foo(a) { return a+1; }"; 8171 v8::ScriptData* sd = 8172 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8173 CHECK_NE(sd->Length(), 0); 8174 CHECK_NE(sd->Data(), NULL); 8175 CHECK(!sd->HasError()); 8176 delete sd; 8177} 8178 8179 8180TEST(PreCompileWithError) { 8181 v8::V8::Initialize(); 8182 const char* script = "function foo(a) { return 1 * * 2; }"; 8183 v8::ScriptData* sd = 8184 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8185 CHECK(sd->HasError()); 8186 delete sd; 8187} 8188 8189 8190TEST(Regress31661) { 8191 v8::V8::Initialize(); 8192 const char* script = " The Definintive Guide"; 8193 v8::ScriptData* sd = 8194 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8195 CHECK(sd->HasError()); 8196 delete sd; 8197} 8198 8199 8200// Tests that ScriptData can be serialized and deserialized. 8201TEST(PreCompileSerialization) { 8202 v8::V8::Initialize(); 8203 const char* script = "function foo(a) { return a+1; }"; 8204 v8::ScriptData* sd = 8205 v8::ScriptData::PreCompile(script, i::StrLength(script)); 8206 8207 // Serialize. 8208 int serialized_data_length = sd->Length(); 8209 char* serialized_data = i::NewArray<char>(serialized_data_length); 8210 memcpy(serialized_data, sd->Data(), serialized_data_length); 8211 8212 // Deserialize. 8213 v8::ScriptData* deserialized_sd = 8214 v8::ScriptData::New(serialized_data, serialized_data_length); 8215 8216 // Verify that the original is the same as the deserialized. 8217 CHECK_EQ(sd->Length(), deserialized_sd->Length()); 8218 CHECK_EQ(0, memcmp(sd->Data(), deserialized_sd->Data(), sd->Length())); 8219 CHECK_EQ(sd->HasError(), deserialized_sd->HasError()); 8220 8221 delete sd; 8222 delete deserialized_sd; 8223} 8224 8225 8226// Attempts to deserialize bad data. 8227TEST(PreCompileDeserializationError) { 8228 v8::V8::Initialize(); 8229 const char* data = "DONT CARE"; 8230 int invalid_size = 3; 8231 v8::ScriptData* sd = v8::ScriptData::New(data, invalid_size); 8232 8233 CHECK_EQ(0, sd->Length()); 8234 8235 delete sd; 8236} 8237 8238 8239// This tests that we do not allow dictionary load/call inline caches 8240// to use functions that have not yet been compiled. The potential 8241// problem of loading a function that has not yet been compiled can 8242// arise because we share code between contexts via the compilation 8243// cache. 8244THREADED_TEST(DictionaryICLoadedFunction) { 8245 v8::HandleScope scope; 8246 // Test LoadIC. 8247 for (int i = 0; i < 2; i++) { 8248 LocalContext context; 8249 context->Global()->Set(v8_str("tmp"), v8::True()); 8250 context->Global()->Delete(v8_str("tmp")); 8251 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');"); 8252 } 8253 // Test CallIC. 8254 for (int i = 0; i < 2; i++) { 8255 LocalContext context; 8256 context->Global()->Set(v8_str("tmp"), v8::True()); 8257 context->Global()->Delete(v8_str("tmp")); 8258 CompileRun("for (var j = 0; j < 10; j++) RegExp('')"); 8259 } 8260} 8261 8262 8263// Test that cross-context new calls use the context of the callee to 8264// create the new JavaScript object. 8265THREADED_TEST(CrossContextNew) { 8266 v8::HandleScope scope; 8267 v8::Persistent<Context> context0 = Context::New(); 8268 v8::Persistent<Context> context1 = Context::New(); 8269 8270 // Allow cross-domain access. 8271 Local<String> token = v8_str("<security token>"); 8272 context0->SetSecurityToken(token); 8273 context1->SetSecurityToken(token); 8274 8275 // Set an 'x' property on the Object prototype and define a 8276 // constructor function in context0. 8277 context0->Enter(); 8278 CompileRun("Object.prototype.x = 42; function C() {};"); 8279 context0->Exit(); 8280 8281 // Call the constructor function from context0 and check that the 8282 // result has the 'x' property. 8283 context1->Enter(); 8284 context1->Global()->Set(v8_str("other"), context0->Global()); 8285 Local<Value> value = CompileRun("var instance = new other.C(); instance.x"); 8286 CHECK(value->IsInt32()); 8287 CHECK_EQ(42, value->Int32Value()); 8288 context1->Exit(); 8289 8290 // Dispose the contexts to allow them to be garbage collected. 8291 context0.Dispose(); 8292 context1.Dispose(); 8293} 8294 8295 8296class RegExpInterruptTest { 8297 public: 8298 RegExpInterruptTest() : block_(NULL) {} 8299 ~RegExpInterruptTest() { delete block_; } 8300 void RunTest() { 8301 block_ = i::OS::CreateSemaphore(0); 8302 gc_count_ = 0; 8303 gc_during_regexp_ = 0; 8304 regexp_success_ = false; 8305 gc_success_ = false; 8306 GCThread gc_thread(this); 8307 gc_thread.Start(); 8308 v8::Locker::StartPreemption(1); 8309 8310 LongRunningRegExp(); 8311 { 8312 v8::Unlocker unlock; 8313 gc_thread.Join(); 8314 } 8315 v8::Locker::StopPreemption(); 8316 CHECK(regexp_success_); 8317 CHECK(gc_success_); 8318 } 8319 private: 8320 // Number of garbage collections required. 8321 static const int kRequiredGCs = 5; 8322 8323 class GCThread : public i::Thread { 8324 public: 8325 explicit GCThread(RegExpInterruptTest* test) 8326 : test_(test) {} 8327 virtual void Run() { 8328 test_->CollectGarbage(); 8329 } 8330 private: 8331 RegExpInterruptTest* test_; 8332 }; 8333 8334 void CollectGarbage() { 8335 block_->Wait(); 8336 while (gc_during_regexp_ < kRequiredGCs) { 8337 { 8338 v8::Locker lock; 8339 // TODO(lrn): Perhaps create some garbage before collecting. 8340 i::Heap::CollectAllGarbage(false); 8341 gc_count_++; 8342 } 8343 i::OS::Sleep(1); 8344 } 8345 gc_success_ = true; 8346 } 8347 8348 void LongRunningRegExp() { 8349 block_->Signal(); // Enable garbage collection thread on next preemption. 8350 int rounds = 0; 8351 while (gc_during_regexp_ < kRequiredGCs) { 8352 int gc_before = gc_count_; 8353 { 8354 // Match 15-30 "a"'s against 14 and a "b". 8355 const char* c_source = 8356 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 8357 ".exec('aaaaaaaaaaaaaaab') === null"; 8358 Local<String> source = String::New(c_source); 8359 Local<Script> script = Script::Compile(source); 8360 Local<Value> result = script->Run(); 8361 if (!result->BooleanValue()) { 8362 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit. 8363 return; 8364 } 8365 } 8366 { 8367 // Match 15-30 "a"'s against 15 and a "b". 8368 const char* c_source = 8369 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 8370 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'"; 8371 Local<String> source = String::New(c_source); 8372 Local<Script> script = Script::Compile(source); 8373 Local<Value> result = script->Run(); 8374 if (!result->BooleanValue()) { 8375 gc_during_regexp_ = kRequiredGCs; 8376 return; 8377 } 8378 } 8379 int gc_after = gc_count_; 8380 gc_during_regexp_ += gc_after - gc_before; 8381 rounds++; 8382 i::OS::Sleep(1); 8383 } 8384 regexp_success_ = true; 8385 } 8386 8387 i::Semaphore* block_; 8388 int gc_count_; 8389 int gc_during_regexp_; 8390 bool regexp_success_; 8391 bool gc_success_; 8392}; 8393 8394 8395// Test that a regular expression execution can be interrupted and 8396// survive a garbage collection. 8397TEST(RegExpInterruption) { 8398 v8::Locker lock; 8399 v8::V8::Initialize(); 8400 v8::HandleScope scope; 8401 Local<Context> local_env; 8402 { 8403 LocalContext env; 8404 local_env = env.local(); 8405 } 8406 8407 // Local context should still be live. 8408 CHECK(!local_env.IsEmpty()); 8409 local_env->Enter(); 8410 8411 // Should complete without problems. 8412 RegExpInterruptTest().RunTest(); 8413 8414 local_env->Exit(); 8415} 8416 8417 8418class ApplyInterruptTest { 8419 public: 8420 ApplyInterruptTest() : block_(NULL) {} 8421 ~ApplyInterruptTest() { delete block_; } 8422 void RunTest() { 8423 block_ = i::OS::CreateSemaphore(0); 8424 gc_count_ = 0; 8425 gc_during_apply_ = 0; 8426 apply_success_ = false; 8427 gc_success_ = false; 8428 GCThread gc_thread(this); 8429 gc_thread.Start(); 8430 v8::Locker::StartPreemption(1); 8431 8432 LongRunningApply(); 8433 { 8434 v8::Unlocker unlock; 8435 gc_thread.Join(); 8436 } 8437 v8::Locker::StopPreemption(); 8438 CHECK(apply_success_); 8439 CHECK(gc_success_); 8440 } 8441 private: 8442 // Number of garbage collections required. 8443 static const int kRequiredGCs = 2; 8444 8445 class GCThread : public i::Thread { 8446 public: 8447 explicit GCThread(ApplyInterruptTest* test) 8448 : test_(test) {} 8449 virtual void Run() { 8450 test_->CollectGarbage(); 8451 } 8452 private: 8453 ApplyInterruptTest* test_; 8454 }; 8455 8456 void CollectGarbage() { 8457 block_->Wait(); 8458 while (gc_during_apply_ < kRequiredGCs) { 8459 { 8460 v8::Locker lock; 8461 i::Heap::CollectAllGarbage(false); 8462 gc_count_++; 8463 } 8464 i::OS::Sleep(1); 8465 } 8466 gc_success_ = true; 8467 } 8468 8469 void LongRunningApply() { 8470 block_->Signal(); 8471 int rounds = 0; 8472 while (gc_during_apply_ < kRequiredGCs) { 8473 int gc_before = gc_count_; 8474 { 8475 const char* c_source = 8476 "function do_very_little(bar) {" 8477 " this.foo = bar;" 8478 "}" 8479 "for (var i = 0; i < 100000; i++) {" 8480 " do_very_little.apply(this, ['bar']);" 8481 "}"; 8482 Local<String> source = String::New(c_source); 8483 Local<Script> script = Script::Compile(source); 8484 Local<Value> result = script->Run(); 8485 // Check that no exception was thrown. 8486 CHECK(!result.IsEmpty()); 8487 } 8488 int gc_after = gc_count_; 8489 gc_during_apply_ += gc_after - gc_before; 8490 rounds++; 8491 } 8492 apply_success_ = true; 8493 } 8494 8495 i::Semaphore* block_; 8496 int gc_count_; 8497 int gc_during_apply_; 8498 bool apply_success_; 8499 bool gc_success_; 8500}; 8501 8502 8503// Test that nothing bad happens if we get a preemption just when we were 8504// about to do an apply(). 8505TEST(ApplyInterruption) { 8506 v8::Locker lock; 8507 v8::V8::Initialize(); 8508 v8::HandleScope scope; 8509 Local<Context> local_env; 8510 { 8511 LocalContext env; 8512 local_env = env.local(); 8513 } 8514 8515 // Local context should still be live. 8516 CHECK(!local_env.IsEmpty()); 8517 local_env->Enter(); 8518 8519 // Should complete without problems. 8520 ApplyInterruptTest().RunTest(); 8521 8522 local_env->Exit(); 8523} 8524 8525 8526// Verify that we can clone an object 8527TEST(ObjectClone) { 8528 v8::HandleScope scope; 8529 LocalContext env; 8530 8531 const char* sample = 8532 "var rv = {};" \ 8533 "rv.alpha = 'hello';" \ 8534 "rv.beta = 123;" \ 8535 "rv;"; 8536 8537 // Create an object, verify basics. 8538 Local<Value> val = CompileRun(sample); 8539 CHECK(val->IsObject()); 8540 Local<v8::Object> obj = val.As<v8::Object>(); 8541 obj->Set(v8_str("gamma"), v8_str("cloneme")); 8542 8543 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha"))); 8544 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 8545 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma"))); 8546 8547 // Clone it. 8548 Local<v8::Object> clone = obj->Clone(); 8549 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha"))); 8550 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta"))); 8551 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma"))); 8552 8553 // Set a property on the clone, verify each object. 8554 clone->Set(v8_str("beta"), v8::Integer::New(456)); 8555 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 8556 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta"))); 8557} 8558 8559 8560class AsciiVectorResource : public v8::String::ExternalAsciiStringResource { 8561 public: 8562 explicit AsciiVectorResource(i::Vector<const char> vector) 8563 : data_(vector) {} 8564 virtual ~AsciiVectorResource() {} 8565 virtual size_t length() const { return data_.length(); } 8566 virtual const char* data() const { return data_.start(); } 8567 private: 8568 i::Vector<const char> data_; 8569}; 8570 8571 8572class UC16VectorResource : public v8::String::ExternalStringResource { 8573 public: 8574 explicit UC16VectorResource(i::Vector<const i::uc16> vector) 8575 : data_(vector) {} 8576 virtual ~UC16VectorResource() {} 8577 virtual size_t length() const { return data_.length(); } 8578 virtual const i::uc16* data() const { return data_.start(); } 8579 private: 8580 i::Vector<const i::uc16> data_; 8581}; 8582 8583 8584static void MorphAString(i::String* string, 8585 AsciiVectorResource* ascii_resource, 8586 UC16VectorResource* uc16_resource) { 8587 CHECK(i::StringShape(string).IsExternal()); 8588 if (string->IsAsciiRepresentation()) { 8589 // Check old map is not symbol or long. 8590 CHECK(string->map() == i::Heap::external_ascii_string_map()); 8591 // Morph external string to be TwoByte string. 8592 string->set_map(i::Heap::external_string_map()); 8593 i::ExternalTwoByteString* morphed = 8594 i::ExternalTwoByteString::cast(string); 8595 morphed->set_resource(uc16_resource); 8596 } else { 8597 // Check old map is not symbol or long. 8598 CHECK(string->map() == i::Heap::external_string_map()); 8599 // Morph external string to be ASCII string. 8600 string->set_map(i::Heap::external_ascii_string_map()); 8601 i::ExternalAsciiString* morphed = 8602 i::ExternalAsciiString::cast(string); 8603 morphed->set_resource(ascii_resource); 8604 } 8605} 8606 8607 8608// Test that we can still flatten a string if the components it is built up 8609// from have been turned into 16 bit strings in the mean time. 8610THREADED_TEST(MorphCompositeStringTest) { 8611 const char* c_string = "Now is the time for all good men" 8612 " to come to the aid of the party"; 8613 uint16_t* two_byte_string = AsciiToTwoByteString(c_string); 8614 { 8615 v8::HandleScope scope; 8616 LocalContext env; 8617 AsciiVectorResource ascii_resource( 8618 i::Vector<const char>(c_string, i::StrLength(c_string))); 8619 UC16VectorResource uc16_resource( 8620 i::Vector<const uint16_t>(two_byte_string, 8621 i::StrLength(c_string))); 8622 8623 Local<String> lhs(v8::Utils::ToLocal( 8624 i::Factory::NewExternalStringFromAscii(&ascii_resource))); 8625 Local<String> rhs(v8::Utils::ToLocal( 8626 i::Factory::NewExternalStringFromAscii(&ascii_resource))); 8627 8628 env->Global()->Set(v8_str("lhs"), lhs); 8629 env->Global()->Set(v8_str("rhs"), rhs); 8630 8631 CompileRun( 8632 "var cons = lhs + rhs;" 8633 "var slice = lhs.substring(1, lhs.length - 1);" 8634 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);"); 8635 8636 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource); 8637 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource); 8638 8639 // Now do some stuff to make sure the strings are flattened, etc. 8640 CompileRun( 8641 "/[^a-z]/.test(cons);" 8642 "/[^a-z]/.test(slice);" 8643 "/[^a-z]/.test(slice_on_cons);"); 8644 const char* expected_cons = 8645 "Now is the time for all good men to come to the aid of the party" 8646 "Now is the time for all good men to come to the aid of the party"; 8647 const char* expected_slice = 8648 "ow is the time for all good men to come to the aid of the part"; 8649 const char* expected_slice_on_cons = 8650 "ow is the time for all good men to come to the aid of the party" 8651 "Now is the time for all good men to come to the aid of the part"; 8652 CHECK_EQ(String::New(expected_cons), 8653 env->Global()->Get(v8_str("cons"))); 8654 CHECK_EQ(String::New(expected_slice), 8655 env->Global()->Get(v8_str("slice"))); 8656 CHECK_EQ(String::New(expected_slice_on_cons), 8657 env->Global()->Get(v8_str("slice_on_cons"))); 8658 } 8659} 8660 8661 8662TEST(CompileExternalTwoByteSource) { 8663 v8::HandleScope scope; 8664 LocalContext context; 8665 8666 // This is a very short list of sources, which currently is to check for a 8667 // regression caused by r2703. 8668 const char* ascii_sources[] = { 8669 "0.5", 8670 "-0.5", // This mainly testes PushBack in the Scanner. 8671 "--0.5", // This mainly testes PushBack in the Scanner. 8672 NULL 8673 }; 8674 8675 // Compile the sources as external two byte strings. 8676 for (int i = 0; ascii_sources[i] != NULL; i++) { 8677 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]); 8678 UC16VectorResource uc16_resource( 8679 i::Vector<const uint16_t>(two_byte_string, 8680 i::StrLength(ascii_sources[i]))); 8681 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource); 8682 v8::Script::Compile(source); 8683 } 8684} 8685 8686 8687class RegExpStringModificationTest { 8688 public: 8689 RegExpStringModificationTest() 8690 : block_(i::OS::CreateSemaphore(0)), 8691 morphs_(0), 8692 morphs_during_regexp_(0), 8693 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)), 8694 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {} 8695 ~RegExpStringModificationTest() { delete block_; } 8696 void RunTest() { 8697 regexp_success_ = false; 8698 morph_success_ = false; 8699 8700 // Initialize the contents of two_byte_content_ to be a uc16 representation 8701 // of "aaaaaaaaaaaaaab". 8702 for (int i = 0; i < 14; i++) { 8703 two_byte_content_[i] = 'a'; 8704 } 8705 two_byte_content_[14] = 'b'; 8706 8707 // Create the input string for the regexp - the one we are going to change 8708 // properties of. 8709 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_); 8710 8711 // Inject the input as a global variable. 8712 i::Handle<i::String> input_name = 8713 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5)); 8714 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE); 8715 8716 8717 MorphThread morph_thread(this); 8718 morph_thread.Start(); 8719 v8::Locker::StartPreemption(1); 8720 LongRunningRegExp(); 8721 { 8722 v8::Unlocker unlock; 8723 morph_thread.Join(); 8724 } 8725 v8::Locker::StopPreemption(); 8726 CHECK(regexp_success_); 8727 CHECK(morph_success_); 8728 } 8729 private: 8730 8731 // Number of string modifications required. 8732 static const int kRequiredModifications = 5; 8733 static const int kMaxModifications = 100; 8734 8735 class MorphThread : public i::Thread { 8736 public: 8737 explicit MorphThread(RegExpStringModificationTest* test) 8738 : test_(test) {} 8739 virtual void Run() { 8740 test_->MorphString(); 8741 } 8742 private: 8743 RegExpStringModificationTest* test_; 8744 }; 8745 8746 void MorphString() { 8747 block_->Wait(); 8748 while (morphs_during_regexp_ < kRequiredModifications && 8749 morphs_ < kMaxModifications) { 8750 { 8751 v8::Locker lock; 8752 // Swap string between ascii and two-byte representation. 8753 i::String* string = *input_; 8754 MorphAString(string, &ascii_resource_, &uc16_resource_); 8755 morphs_++; 8756 } 8757 i::OS::Sleep(1); 8758 } 8759 morph_success_ = true; 8760 } 8761 8762 void LongRunningRegExp() { 8763 block_->Signal(); // Enable morphing thread on next preemption. 8764 while (morphs_during_regexp_ < kRequiredModifications && 8765 morphs_ < kMaxModifications) { 8766 int morphs_before = morphs_; 8767 { 8768 // Match 15-30 "a"'s against 14 and a "b". 8769 const char* c_source = 8770 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 8771 ".exec(input) === null"; 8772 Local<String> source = String::New(c_source); 8773 Local<Script> script = Script::Compile(source); 8774 Local<Value> result = script->Run(); 8775 CHECK(result->IsTrue()); 8776 } 8777 int morphs_after = morphs_; 8778 morphs_during_regexp_ += morphs_after - morphs_before; 8779 } 8780 regexp_success_ = true; 8781 } 8782 8783 i::uc16 two_byte_content_[15]; 8784 i::Semaphore* block_; 8785 int morphs_; 8786 int morphs_during_regexp_; 8787 bool regexp_success_; 8788 bool morph_success_; 8789 i::Handle<i::String> input_; 8790 AsciiVectorResource ascii_resource_; 8791 UC16VectorResource uc16_resource_; 8792}; 8793 8794 8795// Test that a regular expression execution can be interrupted and 8796// the string changed without failing. 8797TEST(RegExpStringModification) { 8798 v8::Locker lock; 8799 v8::V8::Initialize(); 8800 v8::HandleScope scope; 8801 Local<Context> local_env; 8802 { 8803 LocalContext env; 8804 local_env = env.local(); 8805 } 8806 8807 // Local context should still be live. 8808 CHECK(!local_env.IsEmpty()); 8809 local_env->Enter(); 8810 8811 // Should complete without problems. 8812 RegExpStringModificationTest().RunTest(); 8813 8814 local_env->Exit(); 8815} 8816 8817 8818// Test that we can set a property on the global object even if there 8819// is a read-only property in the prototype chain. 8820TEST(ReadOnlyPropertyInGlobalProto) { 8821 v8::HandleScope scope; 8822 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 8823 LocalContext context(0, templ); 8824 v8::Handle<v8::Object> global = context->Global(); 8825 v8::Handle<v8::Object> global_proto = 8826 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__"))); 8827 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly); 8828 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly); 8829 // Check without 'eval' or 'with'. 8830 v8::Handle<v8::Value> res = 8831 CompileRun("function f() { x = 42; return x; }; f()"); 8832 // Check with 'eval'. 8833 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()"); 8834 CHECK_EQ(v8::Integer::New(42), res); 8835 // Check with 'with'. 8836 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()"); 8837 CHECK_EQ(v8::Integer::New(42), res); 8838} 8839 8840static int force_set_set_count = 0; 8841static int force_set_get_count = 0; 8842bool pass_on_get = false; 8843 8844static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name, 8845 const v8::AccessorInfo& info) { 8846 force_set_get_count++; 8847 if (pass_on_get) { 8848 return v8::Handle<v8::Value>(); 8849 } else { 8850 return v8::Int32::New(3); 8851 } 8852} 8853 8854static void ForceSetSetter(v8::Local<v8::String> name, 8855 v8::Local<v8::Value> value, 8856 const v8::AccessorInfo& info) { 8857 force_set_set_count++; 8858} 8859 8860static v8::Handle<v8::Value> ForceSetInterceptSetter( 8861 v8::Local<v8::String> name, 8862 v8::Local<v8::Value> value, 8863 const v8::AccessorInfo& info) { 8864 force_set_set_count++; 8865 return v8::Undefined(); 8866} 8867 8868TEST(ForceSet) { 8869 force_set_get_count = 0; 8870 force_set_set_count = 0; 8871 pass_on_get = false; 8872 8873 v8::HandleScope scope; 8874 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 8875 v8::Handle<v8::String> access_property = v8::String::New("a"); 8876 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter); 8877 LocalContext context(NULL, templ); 8878 v8::Handle<v8::Object> global = context->Global(); 8879 8880 // Ordinary properties 8881 v8::Handle<v8::String> simple_property = v8::String::New("p"); 8882 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly); 8883 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 8884 // This should fail because the property is read-only 8885 global->Set(simple_property, v8::Int32::New(5)); 8886 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 8887 // This should succeed even though the property is read-only 8888 global->ForceSet(simple_property, v8::Int32::New(6)); 8889 CHECK_EQ(6, global->Get(simple_property)->Int32Value()); 8890 8891 // Accessors 8892 CHECK_EQ(0, force_set_set_count); 8893 CHECK_EQ(0, force_set_get_count); 8894 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 8895 // CHECK_EQ the property shouldn't override it, just call the setter 8896 // which in this case does nothing. 8897 global->Set(access_property, v8::Int32::New(7)); 8898 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 8899 CHECK_EQ(1, force_set_set_count); 8900 CHECK_EQ(2, force_set_get_count); 8901 // Forcing the property to be set should override the accessor without 8902 // calling it 8903 global->ForceSet(access_property, v8::Int32::New(8)); 8904 CHECK_EQ(8, global->Get(access_property)->Int32Value()); 8905 CHECK_EQ(1, force_set_set_count); 8906 CHECK_EQ(2, force_set_get_count); 8907} 8908 8909TEST(ForceSetWithInterceptor) { 8910 force_set_get_count = 0; 8911 force_set_set_count = 0; 8912 pass_on_get = false; 8913 8914 v8::HandleScope scope; 8915 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 8916 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter); 8917 LocalContext context(NULL, templ); 8918 v8::Handle<v8::Object> global = context->Global(); 8919 8920 v8::Handle<v8::String> some_property = v8::String::New("a"); 8921 CHECK_EQ(0, force_set_set_count); 8922 CHECK_EQ(0, force_set_get_count); 8923 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 8924 // Setting the property shouldn't override it, just call the setter 8925 // which in this case does nothing. 8926 global->Set(some_property, v8::Int32::New(7)); 8927 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 8928 CHECK_EQ(1, force_set_set_count); 8929 CHECK_EQ(2, force_set_get_count); 8930 // Getting the property when the interceptor returns an empty handle 8931 // should yield undefined, since the property isn't present on the 8932 // object itself yet. 8933 pass_on_get = true; 8934 CHECK(global->Get(some_property)->IsUndefined()); 8935 CHECK_EQ(1, force_set_set_count); 8936 CHECK_EQ(3, force_set_get_count); 8937 // Forcing the property to be set should cause the value to be 8938 // set locally without calling the interceptor. 8939 global->ForceSet(some_property, v8::Int32::New(8)); 8940 CHECK_EQ(8, global->Get(some_property)->Int32Value()); 8941 CHECK_EQ(1, force_set_set_count); 8942 CHECK_EQ(4, force_set_get_count); 8943 // Reenabling the interceptor should cause it to take precedence over 8944 // the property 8945 pass_on_get = false; 8946 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 8947 CHECK_EQ(1, force_set_set_count); 8948 CHECK_EQ(5, force_set_get_count); 8949 // The interceptor should also work for other properties 8950 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value()); 8951 CHECK_EQ(1, force_set_set_count); 8952 CHECK_EQ(6, force_set_get_count); 8953} 8954 8955 8956THREADED_TEST(ForceDelete) { 8957 v8::HandleScope scope; 8958 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 8959 LocalContext context(NULL, templ); 8960 v8::Handle<v8::Object> global = context->Global(); 8961 8962 // Ordinary properties 8963 v8::Handle<v8::String> simple_property = v8::String::New("p"); 8964 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete); 8965 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 8966 // This should fail because the property is dont-delete. 8967 CHECK(!global->Delete(simple_property)); 8968 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 8969 // This should succeed even though the property is dont-delete. 8970 CHECK(global->ForceDelete(simple_property)); 8971 CHECK(global->Get(simple_property)->IsUndefined()); 8972} 8973 8974 8975static int force_delete_interceptor_count = 0; 8976static bool pass_on_delete = false; 8977 8978 8979static v8::Handle<v8::Boolean> ForceDeleteDeleter( 8980 v8::Local<v8::String> name, 8981 const v8::AccessorInfo& info) { 8982 force_delete_interceptor_count++; 8983 if (pass_on_delete) { 8984 return v8::Handle<v8::Boolean>(); 8985 } else { 8986 return v8::True(); 8987 } 8988} 8989 8990 8991THREADED_TEST(ForceDeleteWithInterceptor) { 8992 force_delete_interceptor_count = 0; 8993 pass_on_delete = false; 8994 8995 v8::HandleScope scope; 8996 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 8997 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter); 8998 LocalContext context(NULL, templ); 8999 v8::Handle<v8::Object> global = context->Global(); 9000 9001 v8::Handle<v8::String> some_property = v8::String::New("a"); 9002 global->Set(some_property, v8::Integer::New(42), v8::DontDelete); 9003 9004 // Deleting a property should get intercepted and nothing should 9005 // happen. 9006 CHECK_EQ(0, force_delete_interceptor_count); 9007 CHECK(global->Delete(some_property)); 9008 CHECK_EQ(1, force_delete_interceptor_count); 9009 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 9010 // Deleting the property when the interceptor returns an empty 9011 // handle should not delete the property since it is DontDelete. 9012 pass_on_delete = true; 9013 CHECK(!global->Delete(some_property)); 9014 CHECK_EQ(2, force_delete_interceptor_count); 9015 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 9016 // Forcing the property to be deleted should delete the value 9017 // without calling the interceptor. 9018 CHECK(global->ForceDelete(some_property)); 9019 CHECK(global->Get(some_property)->IsUndefined()); 9020 CHECK_EQ(2, force_delete_interceptor_count); 9021} 9022 9023 9024// Make sure that forcing a delete invalidates any IC stubs, so we 9025// don't read the hole value. 9026THREADED_TEST(ForceDeleteIC) { 9027 v8::HandleScope scope; 9028 LocalContext context; 9029 // Create a DontDelete variable on the global object. 9030 CompileRun("this.__proto__ = { foo: 'horse' };" 9031 "var foo = 'fish';" 9032 "function f() { return foo.length; }"); 9033 // Initialize the IC for foo in f. 9034 CompileRun("for (var i = 0; i < 4; i++) f();"); 9035 // Make sure the value of foo is correct before the deletion. 9036 CHECK_EQ(4, CompileRun("f()")->Int32Value()); 9037 // Force the deletion of foo. 9038 CHECK(context->Global()->ForceDelete(v8_str("foo"))); 9039 // Make sure the value for foo is read from the prototype, and that 9040 // we don't get in trouble with reading the deleted cell value 9041 // sentinel. 9042 CHECK_EQ(5, CompileRun("f()")->Int32Value()); 9043} 9044 9045 9046v8::Persistent<Context> calling_context0; 9047v8::Persistent<Context> calling_context1; 9048v8::Persistent<Context> calling_context2; 9049 9050 9051// Check that the call to the callback is initiated in 9052// calling_context2, the directly calling context is calling_context1 9053// and the callback itself is in calling_context0. 9054static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) { 9055 ApiTestFuzzer::Fuzz(); 9056 CHECK(Context::GetCurrent() == calling_context0); 9057 CHECK(Context::GetCalling() == calling_context1); 9058 CHECK(Context::GetEntered() == calling_context2); 9059 return v8::Integer::New(42); 9060} 9061 9062 9063THREADED_TEST(GetCallingContext) { 9064 v8::HandleScope scope; 9065 9066 calling_context0 = Context::New(); 9067 calling_context1 = Context::New(); 9068 calling_context2 = Context::New(); 9069 9070 // Allow cross-domain access. 9071 Local<String> token = v8_str("<security token>"); 9072 calling_context0->SetSecurityToken(token); 9073 calling_context1->SetSecurityToken(token); 9074 calling_context2->SetSecurityToken(token); 9075 9076 // Create an object with a C++ callback in context0. 9077 calling_context0->Enter(); 9078 Local<v8::FunctionTemplate> callback_templ = 9079 v8::FunctionTemplate::New(GetCallingContextCallback); 9080 calling_context0->Global()->Set(v8_str("callback"), 9081 callback_templ->GetFunction()); 9082 calling_context0->Exit(); 9083 9084 // Expose context0 in context1 and setup a function that calls the 9085 // callback function. 9086 calling_context1->Enter(); 9087 calling_context1->Global()->Set(v8_str("context0"), 9088 calling_context0->Global()); 9089 CompileRun("function f() { context0.callback() }"); 9090 calling_context1->Exit(); 9091 9092 // Expose context1 in context2 and call the callback function in 9093 // context0 indirectly through f in context1. 9094 calling_context2->Enter(); 9095 calling_context2->Global()->Set(v8_str("context1"), 9096 calling_context1->Global()); 9097 CompileRun("context1.f()"); 9098 calling_context2->Exit(); 9099 9100 // Dispose the contexts to allow them to be garbage collected. 9101 calling_context0.Dispose(); 9102 calling_context1.Dispose(); 9103 calling_context2.Dispose(); 9104 calling_context0.Clear(); 9105 calling_context1.Clear(); 9106 calling_context2.Clear(); 9107} 9108 9109 9110// Check that a variable declaration with no explicit initialization 9111// value does not shadow an existing property in the prototype chain. 9112// 9113// This is consistent with Firefox and Safari. 9114// 9115// See http://crbug.com/12548. 9116THREADED_TEST(InitGlobalVarInProtoChain) { 9117 v8::HandleScope scope; 9118 LocalContext context; 9119 // Introduce a variable in the prototype chain. 9120 CompileRun("__proto__.x = 42"); 9121 v8::Handle<v8::Value> result = CompileRun("var x; x"); 9122 CHECK(!result->IsUndefined()); 9123 CHECK_EQ(42, result->Int32Value()); 9124} 9125 9126 9127// Regression test for issue 398. 9128// If a function is added to an object, creating a constant function 9129// field, and the result is cloned, replacing the constant function on the 9130// original should not affect the clone. 9131// See http://code.google.com/p/v8/issues/detail?id=398 9132THREADED_TEST(ReplaceConstantFunction) { 9133 v8::HandleScope scope; 9134 LocalContext context; 9135 v8::Handle<v8::Object> obj = v8::Object::New(); 9136 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 9137 v8::Handle<v8::String> foo_string = v8::String::New("foo"); 9138 obj->Set(foo_string, func_templ->GetFunction()); 9139 v8::Handle<v8::Object> obj_clone = obj->Clone(); 9140 obj_clone->Set(foo_string, v8::String::New("Hello")); 9141 CHECK(!obj->Get(foo_string)->IsUndefined()); 9142} 9143 9144 9145// Regression test for http://crbug.com/16276. 9146THREADED_TEST(Regress16276) { 9147 v8::HandleScope scope; 9148 LocalContext context; 9149 // Force the IC in f to be a dictionary load IC. 9150 CompileRun("function f(obj) { return obj.x; }\n" 9151 "var obj = { x: { foo: 42 }, y: 87 };\n" 9152 "var x = obj.x;\n" 9153 "delete obj.y;\n" 9154 "for (var i = 0; i < 5; i++) f(obj);"); 9155 // Detach the global object to make 'this' refer directly to the 9156 // global object (not the proxy), and make sure that the dictionary 9157 // load IC doesn't mess up loading directly from the global object. 9158 context->DetachGlobal(); 9159 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value()); 9160} 9161 9162 9163THREADED_TEST(PixelArray) { 9164 v8::HandleScope scope; 9165 LocalContext context; 9166 const int kElementCount = 260; 9167 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); 9168 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount, 9169 pixel_data); 9170 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9171 for (int i = 0; i < kElementCount; i++) { 9172 pixels->set(i, i % 256); 9173 } 9174 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9175 for (int i = 0; i < kElementCount; i++) { 9176 CHECK_EQ(i % 256, pixels->get(i)); 9177 CHECK_EQ(i % 256, pixel_data[i]); 9178 } 9179 9180 v8::Handle<v8::Object> obj = v8::Object::New(); 9181 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 9182 // Set the elements to be the pixels. 9183 // jsobj->set_elements(*pixels); 9184 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount); 9185 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value()); 9186 obj->Set(v8_str("field"), v8::Int32::New(1503)); 9187 context->Global()->Set(v8_str("pixels"), obj); 9188 v8::Handle<v8::Value> result = CompileRun("pixels.field"); 9189 CHECK_EQ(1503, result->Int32Value()); 9190 result = CompileRun("pixels[1]"); 9191 CHECK_EQ(1, result->Int32Value()); 9192 9193 result = CompileRun("var sum = 0;" 9194 "for (var i = 0; i < 8; i++) {" 9195 " sum += pixels[i] = pixels[i] = -i;" 9196 "}" 9197 "sum;"); 9198 CHECK_EQ(-28, result->Int32Value()); 9199 9200 result = CompileRun("var sum = 0;" 9201 "for (var i = 0; i < 8; i++) {" 9202 " sum += pixels[i] = pixels[i] = 0;" 9203 "}" 9204 "sum;"); 9205 CHECK_EQ(0, result->Int32Value()); 9206 9207 result = CompileRun("var sum = 0;" 9208 "for (var i = 0; i < 8; i++) {" 9209 " sum += pixels[i] = pixels[i] = 255;" 9210 "}" 9211 "sum;"); 9212 CHECK_EQ(8 * 255, result->Int32Value()); 9213 9214 result = CompileRun("var sum = 0;" 9215 "for (var i = 0; i < 8; i++) {" 9216 " sum += pixels[i] = pixels[i] = 256 + i;" 9217 "}" 9218 "sum;"); 9219 CHECK_EQ(2076, result->Int32Value()); 9220 9221 result = CompileRun("var sum = 0;" 9222 "for (var i = 0; i < 8; i++) {" 9223 " sum += pixels[i] = pixels[i] = i;" 9224 "}" 9225 "sum;"); 9226 CHECK_EQ(28, result->Int32Value()); 9227 9228 result = CompileRun("var sum = 0;" 9229 "for (var i = 0; i < 8; i++) {" 9230 " sum += pixels[i];" 9231 "}" 9232 "sum;"); 9233 CHECK_EQ(28, result->Int32Value()); 9234 9235 i::Handle<i::Smi> value(i::Smi::FromInt(2)); 9236 i::SetElement(jsobj, 1, value); 9237 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value()); 9238 *value.location() = i::Smi::FromInt(256); 9239 i::SetElement(jsobj, 1, value); 9240 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value()); 9241 *value.location() = i::Smi::FromInt(-1); 9242 i::SetElement(jsobj, 1, value); 9243 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value()); 9244 9245 result = CompileRun("for (var i = 0; i < 8; i++) {" 9246 " pixels[i] = (i * 65) - 109;" 9247 "}" 9248 "pixels[1] + pixels[6];"); 9249 CHECK_EQ(255, result->Int32Value()); 9250 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value()); 9251 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value()); 9252 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value()); 9253 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value()); 9254 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value()); 9255 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value()); 9256 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value()); 9257 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value()); 9258 result = CompileRun("var sum = 0;" 9259 "for (var i = 0; i < 8; i++) {" 9260 " sum += pixels[i];" 9261 "}" 9262 "sum;"); 9263 CHECK_EQ(984, result->Int32Value()); 9264 9265 result = CompileRun("for (var i = 0; i < 8; i++) {" 9266 " pixels[i] = (i * 1.1);" 9267 "}" 9268 "pixels[1] + pixels[6];"); 9269 CHECK_EQ(8, result->Int32Value()); 9270 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value()); 9271 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value()); 9272 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value()); 9273 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value()); 9274 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value()); 9275 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value()); 9276 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value()); 9277 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value()); 9278 9279 result = CompileRun("for (var i = 0; i < 8; i++) {" 9280 " pixels[7] = undefined;" 9281 "}" 9282 "pixels[7];"); 9283 CHECK_EQ(0, result->Int32Value()); 9284 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value()); 9285 9286 result = CompileRun("for (var i = 0; i < 8; i++) {" 9287 " pixels[6] = '2.3';" 9288 "}" 9289 "pixels[6];"); 9290 CHECK_EQ(2, result->Int32Value()); 9291 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value()); 9292 9293 result = CompileRun("for (var i = 0; i < 8; i++) {" 9294 " pixels[5] = NaN;" 9295 "}" 9296 "pixels[5];"); 9297 CHECK_EQ(0, result->Int32Value()); 9298 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 9299 9300 result = CompileRun("for (var i = 0; i < 8; i++) {" 9301 " pixels[8] = Infinity;" 9302 "}" 9303 "pixels[8];"); 9304 CHECK_EQ(255, result->Int32Value()); 9305 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value()); 9306 9307 result = CompileRun("for (var i = 0; i < 8; i++) {" 9308 " pixels[9] = -Infinity;" 9309 "}" 9310 "pixels[9];"); 9311 CHECK_EQ(0, result->Int32Value()); 9312 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value()); 9313 9314 result = CompileRun("pixels[3] = 33;" 9315 "delete pixels[3];" 9316 "pixels[3];"); 9317 CHECK_EQ(33, result->Int32Value()); 9318 9319 result = CompileRun("pixels[0] = 10; pixels[1] = 11;" 9320 "pixels[2] = 12; pixels[3] = 13;" 9321 "pixels.__defineGetter__('2'," 9322 "function() { return 120; });" 9323 "pixels[2];"); 9324 CHECK_EQ(12, result->Int32Value()); 9325 9326 result = CompileRun("var js_array = new Array(40);" 9327 "js_array[0] = 77;" 9328 "js_array;"); 9329 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 9330 9331 result = CompileRun("pixels[1] = 23;" 9332 "pixels.__proto__ = [];" 9333 "js_array.__proto__ = pixels;" 9334 "js_array.concat(pixels);"); 9335 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 9336 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 9337 9338 result = CompileRun("pixels[1] = 23;"); 9339 CHECK_EQ(23, result->Int32Value()); 9340 9341 // Test for index greater than 255. Regression test for: 9342 // http://code.google.com/p/chromium/issues/detail?id=26337. 9343 result = CompileRun("pixels[256] = 255;"); 9344 CHECK_EQ(255, result->Int32Value()); 9345 result = CompileRun("var i = 0;" 9346 "for (var j = 0; j < 8; j++) { i = pixels[256]; }" 9347 "i"); 9348 CHECK_EQ(255, result->Int32Value()); 9349 9350 free(pixel_data); 9351} 9352 9353 9354template <class ExternalArrayClass, class ElementType> 9355static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, 9356 int64_t low, 9357 int64_t high) { 9358 v8::HandleScope scope; 9359 LocalContext context; 9360 const int kElementCount = 40; 9361 int element_size = 0; 9362 switch (array_type) { 9363 case v8::kExternalByteArray: 9364 case v8::kExternalUnsignedByteArray: 9365 element_size = 1; 9366 break; 9367 case v8::kExternalShortArray: 9368 case v8::kExternalUnsignedShortArray: 9369 element_size = 2; 9370 break; 9371 case v8::kExternalIntArray: 9372 case v8::kExternalUnsignedIntArray: 9373 case v8::kExternalFloatArray: 9374 element_size = 4; 9375 break; 9376 default: 9377 UNREACHABLE(); 9378 break; 9379 } 9380 ElementType* array_data = 9381 static_cast<ElementType*>(malloc(kElementCount * element_size)); 9382 i::Handle<ExternalArrayClass> array = 9383 i::Handle<ExternalArrayClass>::cast( 9384 i::Factory::NewExternalArray(kElementCount, array_type, array_data)); 9385 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9386 for (int i = 0; i < kElementCount; i++) { 9387 array->set(i, static_cast<ElementType>(i)); 9388 } 9389 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9390 for (int i = 0; i < kElementCount; i++) { 9391 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i))); 9392 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i])); 9393 } 9394 9395 v8::Handle<v8::Object> obj = v8::Object::New(); 9396 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 9397 // Set the elements to be the external array. 9398 obj->SetIndexedPropertiesToExternalArrayData(array_data, 9399 array_type, 9400 kElementCount); 9401 CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number())); 9402 obj->Set(v8_str("field"), v8::Int32::New(1503)); 9403 context->Global()->Set(v8_str("ext_array"), obj); 9404 v8::Handle<v8::Value> result = CompileRun("ext_array.field"); 9405 CHECK_EQ(1503, result->Int32Value()); 9406 result = CompileRun("ext_array[1]"); 9407 CHECK_EQ(1, result->Int32Value()); 9408 9409 // Check pass through of assigned smis 9410 result = CompileRun("var sum = 0;" 9411 "for (var i = 0; i < 8; i++) {" 9412 " sum += ext_array[i] = ext_array[i] = -i;" 9413 "}" 9414 "sum;"); 9415 CHECK_EQ(-28, result->Int32Value()); 9416 9417 // Check assigned smis 9418 result = CompileRun("for (var i = 0; i < 8; i++) {" 9419 " ext_array[i] = i;" 9420 "}" 9421 "var sum = 0;" 9422 "for (var i = 0; i < 8; i++) {" 9423 " sum += ext_array[i];" 9424 "}" 9425 "sum;"); 9426 CHECK_EQ(28, result->Int32Value()); 9427 9428 // Check assigned smis in reverse order 9429 result = CompileRun("for (var i = 8; --i >= 0; ) {" 9430 " ext_array[i] = i;" 9431 "}" 9432 "var sum = 0;" 9433 "for (var i = 0; i < 8; i++) {" 9434 " sum += ext_array[i];" 9435 "}" 9436 "sum;"); 9437 CHECK_EQ(28, result->Int32Value()); 9438 9439 // Check pass through of assigned HeapNumbers 9440 result = CompileRun("var sum = 0;" 9441 "for (var i = 0; i < 16; i+=2) {" 9442 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);" 9443 "}" 9444 "sum;"); 9445 CHECK_EQ(-28, result->Int32Value()); 9446 9447 // Check assigned HeapNumbers 9448 result = CompileRun("for (var i = 0; i < 16; i+=2) {" 9449 " ext_array[i] = (i * 0.5);" 9450 "}" 9451 "var sum = 0;" 9452 "for (var i = 0; i < 16; i+=2) {" 9453 " sum += ext_array[i];" 9454 "}" 9455 "sum;"); 9456 CHECK_EQ(28, result->Int32Value()); 9457 9458 // Check assigned HeapNumbers in reverse order 9459 result = CompileRun("for (var i = 14; i >= 0; i-=2) {" 9460 " ext_array[i] = (i * 0.5);" 9461 "}" 9462 "var sum = 0;" 9463 "for (var i = 0; i < 16; i+=2) {" 9464 " sum += ext_array[i];" 9465 "}" 9466 "sum;"); 9467 CHECK_EQ(28, result->Int32Value()); 9468 9469 i::ScopedVector<char> test_buf(1024); 9470 9471 // Check legal boundary conditions. 9472 // The repeated loads and stores ensure the ICs are exercised. 9473 const char* boundary_program = 9474 "var res = 0;" 9475 "for (var i = 0; i < 16; i++) {" 9476 " ext_array[i] = %lld;" 9477 " if (i > 8) {" 9478 " res = ext_array[i];" 9479 " }" 9480 "}" 9481 "res;"; 9482 i::OS::SNPrintF(test_buf, 9483 boundary_program, 9484 low); 9485 result = CompileRun(test_buf.start()); 9486 CHECK_EQ(low, result->IntegerValue()); 9487 9488 i::OS::SNPrintF(test_buf, 9489 boundary_program, 9490 high); 9491 result = CompileRun(test_buf.start()); 9492 CHECK_EQ(high, result->IntegerValue()); 9493 9494 // Check misprediction of type in IC. 9495 result = CompileRun("var tmp_array = ext_array;" 9496 "var sum = 0;" 9497 "for (var i = 0; i < 8; i++) {" 9498 " tmp_array[i] = i;" 9499 " sum += tmp_array[i];" 9500 " if (i == 4) {" 9501 " tmp_array = {};" 9502 " }" 9503 "}" 9504 "sum;"); 9505 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9506 CHECK_EQ(28, result->Int32Value()); 9507 9508 // Make sure out-of-range loads do not throw. 9509 i::OS::SNPrintF(test_buf, 9510 "var caught_exception = false;" 9511 "try {" 9512 " ext_array[%d];" 9513 "} catch (e) {" 9514 " caught_exception = true;" 9515 "}" 9516 "caught_exception;", 9517 kElementCount); 9518 result = CompileRun(test_buf.start()); 9519 CHECK_EQ(false, result->BooleanValue()); 9520 9521 // Make sure out-of-range stores do not throw. 9522 i::OS::SNPrintF(test_buf, 9523 "var caught_exception = false;" 9524 "try {" 9525 " ext_array[%d] = 1;" 9526 "} catch (e) {" 9527 " caught_exception = true;" 9528 "}" 9529 "caught_exception;", 9530 kElementCount); 9531 result = CompileRun(test_buf.start()); 9532 CHECK_EQ(false, result->BooleanValue()); 9533 9534 // Check other boundary conditions, values and operations. 9535 result = CompileRun("for (var i = 0; i < 8; i++) {" 9536 " ext_array[7] = undefined;" 9537 "}" 9538 "ext_array[7];"); 9539 CHECK_EQ(0, result->Int32Value()); 9540 CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number())); 9541 9542 result = CompileRun("for (var i = 0; i < 8; i++) {" 9543 " ext_array[6] = '2.3';" 9544 "}" 9545 "ext_array[6];"); 9546 CHECK_EQ(2, result->Int32Value()); 9547 CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number())); 9548 9549 if (array_type != v8::kExternalFloatArray) { 9550 // Though the specification doesn't state it, be explicit about 9551 // converting NaNs and +/-Infinity to zero. 9552 result = CompileRun("for (var i = 0; i < 8; i++) {" 9553 " ext_array[i] = 5;" 9554 "}" 9555 "for (var i = 0; i < 8; i++) {" 9556 " ext_array[i] = NaN;" 9557 "}" 9558 "ext_array[5];"); 9559 CHECK_EQ(0, result->Int32Value()); 9560 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 9561 9562 result = CompileRun("for (var i = 0; i < 8; i++) {" 9563 " ext_array[i] = 5;" 9564 "}" 9565 "for (var i = 0; i < 8; i++) {" 9566 " ext_array[i] = Infinity;" 9567 "}" 9568 "ext_array[5];"); 9569 CHECK_EQ(0, result->Int32Value()); 9570 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 9571 9572 result = CompileRun("for (var i = 0; i < 8; i++) {" 9573 " ext_array[i] = 5;" 9574 "}" 9575 "for (var i = 0; i < 8; i++) {" 9576 " ext_array[i] = -Infinity;" 9577 "}" 9578 "ext_array[5];"); 9579 CHECK_EQ(0, result->Int32Value()); 9580 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 9581 } 9582 9583 result = CompileRun("ext_array[3] = 33;" 9584 "delete ext_array[3];" 9585 "ext_array[3];"); 9586 CHECK_EQ(33, result->Int32Value()); 9587 9588 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;" 9589 "ext_array[2] = 12; ext_array[3] = 13;" 9590 "ext_array.__defineGetter__('2'," 9591 "function() { return 120; });" 9592 "ext_array[2];"); 9593 CHECK_EQ(12, result->Int32Value()); 9594 9595 result = CompileRun("var js_array = new Array(40);" 9596 "js_array[0] = 77;" 9597 "js_array;"); 9598 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 9599 9600 result = CompileRun("ext_array[1] = 23;" 9601 "ext_array.__proto__ = [];" 9602 "js_array.__proto__ = ext_array;" 9603 "js_array.concat(ext_array);"); 9604 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 9605 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 9606 9607 result = CompileRun("ext_array[1] = 23;"); 9608 CHECK_EQ(23, result->Int32Value()); 9609 9610 // Test more complex manipulations which cause eax to contain values 9611 // that won't be completely overwritten by loads from the arrays. 9612 // This catches bugs in the instructions used for the KeyedLoadIC 9613 // for byte and word types. 9614 { 9615 const int kXSize = 300; 9616 const int kYSize = 300; 9617 const int kLargeElementCount = kXSize * kYSize * 4; 9618 ElementType* large_array_data = 9619 static_cast<ElementType*>(malloc(kLargeElementCount * element_size)); 9620 i::Handle<ExternalArrayClass> large_array = 9621 i::Handle<ExternalArrayClass>::cast( 9622 i::Factory::NewExternalArray(kLargeElementCount, 9623 array_type, 9624 array_data)); 9625 v8::Handle<v8::Object> large_obj = v8::Object::New(); 9626 // Set the elements to be the external array. 9627 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data, 9628 array_type, 9629 kLargeElementCount); 9630 context->Global()->Set(v8_str("large_array"), large_obj); 9631 // Initialize contents of a few rows. 9632 for (int x = 0; x < 300; x++) { 9633 int row = 0; 9634 int offset = row * 300 * 4; 9635 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 9636 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 9637 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 9638 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 9639 row = 150; 9640 offset = row * 300 * 4; 9641 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 9642 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 9643 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 9644 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 9645 row = 298; 9646 offset = row * 300 * 4; 9647 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 9648 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 9649 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 9650 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 9651 } 9652 // The goal of the code below is to make "offset" large enough 9653 // that the computation of the index (which goes into eax) has 9654 // high bits set which will not be overwritten by a byte or short 9655 // load. 9656 result = CompileRun("var failed = false;" 9657 "var offset = 0;" 9658 "for (var i = 0; i < 300; i++) {" 9659 " if (large_array[4 * i] != 127 ||" 9660 " large_array[4 * i + 1] != 0 ||" 9661 " large_array[4 * i + 2] != 0 ||" 9662 " large_array[4 * i + 3] != 127) {" 9663 " failed = true;" 9664 " }" 9665 "}" 9666 "offset = 150 * 300 * 4;" 9667 "for (var i = 0; i < 300; i++) {" 9668 " if (large_array[offset + 4 * i] != 127 ||" 9669 " large_array[offset + 4 * i + 1] != 0 ||" 9670 " large_array[offset + 4 * i + 2] != 0 ||" 9671 " large_array[offset + 4 * i + 3] != 127) {" 9672 " failed = true;" 9673 " }" 9674 "}" 9675 "offset = 298 * 300 * 4;" 9676 "for (var i = 0; i < 300; i++) {" 9677 " if (large_array[offset + 4 * i] != 127 ||" 9678 " large_array[offset + 4 * i + 1] != 0 ||" 9679 " large_array[offset + 4 * i + 2] != 0 ||" 9680 " large_array[offset + 4 * i + 3] != 127) {" 9681 " failed = true;" 9682 " }" 9683 "}" 9684 "!failed;"); 9685 CHECK_EQ(true, result->BooleanValue()); 9686 free(large_array_data); 9687 } 9688 9689 free(array_data); 9690} 9691 9692 9693THREADED_TEST(ExternalByteArray) { 9694 ExternalArrayTestHelper<v8::internal::ExternalByteArray, int8_t>( 9695 v8::kExternalByteArray, 9696 -128, 9697 127); 9698} 9699 9700 9701THREADED_TEST(ExternalUnsignedByteArray) { 9702 ExternalArrayTestHelper<v8::internal::ExternalUnsignedByteArray, uint8_t>( 9703 v8::kExternalUnsignedByteArray, 9704 0, 9705 255); 9706} 9707 9708 9709THREADED_TEST(ExternalShortArray) { 9710 ExternalArrayTestHelper<v8::internal::ExternalShortArray, int16_t>( 9711 v8::kExternalShortArray, 9712 -32768, 9713 32767); 9714} 9715 9716 9717THREADED_TEST(ExternalUnsignedShortArray) { 9718 ExternalArrayTestHelper<v8::internal::ExternalUnsignedShortArray, uint16_t>( 9719 v8::kExternalUnsignedShortArray, 9720 0, 9721 65535); 9722} 9723 9724 9725THREADED_TEST(ExternalIntArray) { 9726 ExternalArrayTestHelper<v8::internal::ExternalIntArray, int32_t>( 9727 v8::kExternalIntArray, 9728 INT_MIN, // -2147483648 9729 INT_MAX); // 2147483647 9730} 9731 9732 9733THREADED_TEST(ExternalUnsignedIntArray) { 9734 ExternalArrayTestHelper<v8::internal::ExternalUnsignedIntArray, uint32_t>( 9735 v8::kExternalUnsignedIntArray, 9736 0, 9737 UINT_MAX); // 4294967295 9738} 9739 9740 9741THREADED_TEST(ExternalFloatArray) { 9742 ExternalArrayTestHelper<v8::internal::ExternalFloatArray, float>( 9743 v8::kExternalFloatArray, 9744 -500, 9745 500); 9746} 9747 9748 9749THREADED_TEST(ExternalArrays) { 9750 TestExternalByteArray(); 9751 TestExternalUnsignedByteArray(); 9752 TestExternalShortArray(); 9753 TestExternalUnsignedShortArray(); 9754 TestExternalIntArray(); 9755 TestExternalUnsignedIntArray(); 9756 TestExternalFloatArray(); 9757} 9758 9759 9760THREADED_TEST(ScriptContextDependence) { 9761 v8::HandleScope scope; 9762 LocalContext c1; 9763 const char *source = "foo"; 9764 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source)); 9765 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source)); 9766 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100)); 9767 CHECK_EQ(dep->Run()->Int32Value(), 100); 9768 CHECK_EQ(indep->Run()->Int32Value(), 100); 9769 LocalContext c2; 9770 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101)); 9771 CHECK_EQ(dep->Run()->Int32Value(), 100); 9772 CHECK_EQ(indep->Run()->Int32Value(), 101); 9773} 9774 9775 9776THREADED_TEST(StackTrace) { 9777 v8::HandleScope scope; 9778 LocalContext context; 9779 v8::TryCatch try_catch; 9780 const char *source = "function foo() { FAIL.FAIL; }; foo();"; 9781 v8::Handle<v8::String> src = v8::String::New(source); 9782 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test"); 9783 v8::Script::New(src, origin)->Run(); 9784 CHECK(try_catch.HasCaught()); 9785 v8::String::Utf8Value stack(try_catch.StackTrace()); 9786 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL); 9787} 9788 9789 9790// Checks that a StackFrame has certain expected values. 9791void checkStackFrame(const char* expected_script_name, 9792 const char* expected_func_name, int expected_line_number, 9793 int expected_column, bool is_eval, bool is_constructor, 9794 v8::Handle<v8::StackFrame> frame) { 9795 v8::HandleScope scope; 9796 v8::String::Utf8Value func_name(frame->GetFunctionName()); 9797 v8::String::Utf8Value script_name(frame->GetScriptName()); 9798 if (*script_name == NULL) { 9799 // The situation where there is no associated script, like for evals. 9800 CHECK(expected_script_name == NULL); 9801 } else { 9802 CHECK(strstr(*script_name, expected_script_name) != NULL); 9803 } 9804 CHECK(strstr(*func_name, expected_func_name) != NULL); 9805 CHECK_EQ(expected_line_number, frame->GetLineNumber()); 9806 CHECK_EQ(expected_column, frame->GetColumn()); 9807 CHECK_EQ(is_eval, frame->IsEval()); 9808 CHECK_EQ(is_constructor, frame->IsConstructor()); 9809} 9810 9811 9812v8::Handle<Value> AnalyzeStackInNativeCode(const v8::Arguments& args) { 9813 v8::HandleScope scope; 9814 const char* origin = "capture-stack-trace-test"; 9815 const int kOverviewTest = 1; 9816 const int kDetailedTest = 2; 9817 9818 ASSERT(args.Length() == 1); 9819 9820 int testGroup = args[0]->Int32Value(); 9821 if (testGroup == kOverviewTest) { 9822 v8::Handle<v8::StackTrace> stackTrace = 9823 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview); 9824 CHECK_EQ(4, stackTrace->GetFrameCount()); 9825 checkStackFrame(origin, "bar", 2, 10, false, false, 9826 stackTrace->GetFrame(0)); 9827 checkStackFrame(origin, "foo", 6, 3, false, false, 9828 stackTrace->GetFrame(1)); 9829 checkStackFrame(NULL, "", 1, 1, false, false, 9830 stackTrace->GetFrame(2)); 9831 // The last frame is an anonymous function that has the initial call. 9832 checkStackFrame(origin, "", 8, 7, false, false, 9833 stackTrace->GetFrame(3)); 9834 9835 CHECK(stackTrace->AsArray()->IsArray()); 9836 } else if (testGroup == kDetailedTest) { 9837 v8::Handle<v8::StackTrace> stackTrace = 9838 v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kDetailed); 9839 CHECK_EQ(4, stackTrace->GetFrameCount()); 9840 checkStackFrame(origin, "bat", 4, 22, false, false, 9841 stackTrace->GetFrame(0)); 9842 checkStackFrame(origin, "baz", 8, 3, false, true, 9843 stackTrace->GetFrame(1)); 9844 checkStackFrame(NULL, "", 1, 1, true, false, 9845 stackTrace->GetFrame(2)); 9846 // The last frame is an anonymous function that has the initial call to foo. 9847 checkStackFrame(origin, "", 10, 1, false, false, 9848 stackTrace->GetFrame(3)); 9849 9850 CHECK(stackTrace->AsArray()->IsArray()); 9851 } 9852 return v8::Undefined(); 9853} 9854 9855 9856// Tests the C++ StackTrace API. 9857THREADED_TEST(CaptureStackTrace) { 9858 v8::HandleScope scope; 9859 v8::Handle<v8::String> origin = v8::String::New("capture-stack-trace-test"); 9860 Local<ObjectTemplate> templ = ObjectTemplate::New(); 9861 templ->Set(v8_str("AnalyzeStackInNativeCode"), 9862 v8::FunctionTemplate::New(AnalyzeStackInNativeCode)); 9863 LocalContext context(0, templ); 9864 9865 // Test getting OVERVIEW information. Should ignore information that is not 9866 // script name, function name, line number, and column offset. 9867 const char *overview_source = 9868 "function bar() {\n" 9869 " var y; AnalyzeStackInNativeCode(1);\n" 9870 "}\n" 9871 "function foo() {\n" 9872 "\n" 9873 " bar();\n" 9874 "}\n" 9875 "var x;eval('new foo();');"; 9876 v8::Handle<v8::String> overview_src = v8::String::New(overview_source); 9877 v8::Handle<Value> overview_result = 9878 v8::Script::New(overview_src, origin)->Run(); 9879 ASSERT(!overview_result.IsEmpty()); 9880 ASSERT(overview_result->IsObject()); 9881 9882 // Test getting DETAILED information. 9883 const char *detailed_source = 9884 "function bat() {AnalyzeStackInNativeCode(2);\n" 9885 "}\n" 9886 "\n" 9887 "function baz() {\n" 9888 " bat();\n" 9889 "}\n" 9890 "eval('new baz();');"; 9891 v8::Handle<v8::String> detailed_src = v8::String::New(detailed_source); 9892 // Make the script using a non-zero line and column offset. 9893 v8::Handle<v8::Integer> line_offset = v8::Integer::New(3); 9894 v8::Handle<v8::Integer> column_offset = v8::Integer::New(5); 9895 v8::ScriptOrigin detailed_origin(origin, line_offset, column_offset); 9896 v8::Handle<v8::Script> detailed_script( 9897 v8::Script::New(detailed_src, &detailed_origin)); 9898 v8::Handle<Value> detailed_result = detailed_script->Run(); 9899 ASSERT(!detailed_result.IsEmpty()); 9900 ASSERT(detailed_result->IsObject()); 9901} 9902 9903 9904// Test that idle notification can be handled and eventually returns true. 9905THREADED_TEST(IdleNotification) { 9906 bool rv = false; 9907 for (int i = 0; i < 100; i++) { 9908 rv = v8::V8::IdleNotification(); 9909 if (rv) 9910 break; 9911 } 9912 CHECK(rv == true); 9913} 9914 9915 9916static uint32_t* stack_limit; 9917 9918static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) { 9919 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit()); 9920 return v8::Undefined(); 9921} 9922 9923 9924// Uses the address of a local variable to determine the stack top now. 9925// Given a size, returns an address that is that far from the current 9926// top of stack. 9927static uint32_t* ComputeStackLimit(uint32_t size) { 9928 uint32_t* answer = &size - (size / sizeof(size)); 9929 // If the size is very large and the stack is very near the bottom of 9930 // memory then the calculation above may wrap around and give an address 9931 // that is above the (downwards-growing) stack. In that case we return 9932 // a very low address. 9933 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size)); 9934 return answer; 9935} 9936 9937 9938TEST(SetResourceConstraints) { 9939 static const int K = 1024; 9940 uint32_t* set_limit = ComputeStackLimit(128 * K); 9941 9942 // Set stack limit. 9943 v8::ResourceConstraints constraints; 9944 constraints.set_stack_limit(set_limit); 9945 CHECK(v8::SetResourceConstraints(&constraints)); 9946 9947 // Execute a script. 9948 v8::HandleScope scope; 9949 LocalContext env; 9950 Local<v8::FunctionTemplate> fun_templ = 9951 v8::FunctionTemplate::New(GetStackLimitCallback); 9952 Local<Function> fun = fun_templ->GetFunction(); 9953 env->Global()->Set(v8_str("get_stack_limit"), fun); 9954 CompileRun("get_stack_limit();"); 9955 9956 CHECK(stack_limit == set_limit); 9957} 9958 9959 9960TEST(SetResourceConstraintsInThread) { 9961 uint32_t* set_limit; 9962 { 9963 v8::Locker locker; 9964 static const int K = 1024; 9965 set_limit = ComputeStackLimit(128 * K); 9966 9967 // Set stack limit. 9968 v8::ResourceConstraints constraints; 9969 constraints.set_stack_limit(set_limit); 9970 CHECK(v8::SetResourceConstraints(&constraints)); 9971 9972 // Execute a script. 9973 v8::HandleScope scope; 9974 LocalContext env; 9975 Local<v8::FunctionTemplate> fun_templ = 9976 v8::FunctionTemplate::New(GetStackLimitCallback); 9977 Local<Function> fun = fun_templ->GetFunction(); 9978 env->Global()->Set(v8_str("get_stack_limit"), fun); 9979 CompileRun("get_stack_limit();"); 9980 9981 CHECK(stack_limit == set_limit); 9982 } 9983 { 9984 v8::Locker locker; 9985 CHECK(stack_limit == set_limit); 9986 } 9987} 9988 9989 9990THREADED_TEST(GetHeapStatistics) { 9991 v8::HandleScope scope; 9992 LocalContext c1; 9993 v8::HeapStatistics heap_statistics; 9994 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0); 9995 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0); 9996 v8::V8::GetHeapStatistics(&heap_statistics); 9997 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0); 9998 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0); 9999} 10000 10001 10002static double DoubleFromBits(uint64_t value) { 10003 double target; 10004#ifdef BIG_ENDIAN_FLOATING_POINT 10005 const int kIntSize = 4; 10006 // Somebody swapped the lower and higher half of doubles. 10007 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); 10008 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); 10009#else 10010 memcpy(&target, &value, sizeof(target)); 10011#endif 10012 return target; 10013} 10014 10015 10016static uint64_t DoubleToBits(double value) { 10017 uint64_t target; 10018#ifdef BIG_ENDIAN_FLOATING_POINT 10019 const int kIntSize = 4; 10020 // Somebody swapped the lower and higher half of doubles. 10021 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); 10022 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); 10023#else 10024 memcpy(&target, &value, sizeof(target)); 10025#endif 10026 return target; 10027} 10028 10029 10030static double DoubleToDateTime(double input) { 10031 double date_limit = 864e13; 10032 if (IsNaN(input) || input < -date_limit || input > date_limit) { 10033 return i::OS::nan_value(); 10034 } 10035 return (input < 0) ? -(floor(-input)) : floor(input); 10036} 10037 10038// We don't have a consistent way to write 64-bit constants syntactically, so we 10039// split them into two 32-bit constants and combine them programmatically. 10040static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) { 10041 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits); 10042} 10043 10044 10045THREADED_TEST(QuietSignalingNaNs) { 10046 v8::HandleScope scope; 10047 LocalContext context; 10048 v8::TryCatch try_catch; 10049 10050 // Special double values. 10051 double snan = DoubleFromBits(0x7ff00000, 0x00000001); 10052 double qnan = DoubleFromBits(0x7ff80000, 0x00000000); 10053 double infinity = DoubleFromBits(0x7ff00000, 0x00000000); 10054 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu); 10055 double min_normal = DoubleFromBits(0x00100000, 0x00000000); 10056 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu); 10057 double min_denormal = DoubleFromBits(0x00000000, 0x00000001); 10058 10059 // Date values are capped at +/-100000000 days (times 864e5 ms per day) 10060 // on either side of the epoch. 10061 double date_limit = 864e13; 10062 10063 double test_values[] = { 10064 snan, 10065 qnan, 10066 infinity, 10067 max_normal, 10068 date_limit + 1, 10069 date_limit, 10070 min_normal, 10071 max_denormal, 10072 min_denormal, 10073 0, 10074 -0, 10075 -min_denormal, 10076 -max_denormal, 10077 -min_normal, 10078 -date_limit, 10079 -date_limit - 1, 10080 -max_normal, 10081 -infinity, 10082 -qnan, 10083 -snan 10084 }; 10085 int num_test_values = 20; 10086 10087 for (int i = 0; i < num_test_values; i++) { 10088 double test_value = test_values[i]; 10089 10090 // Check that Number::New preserves non-NaNs and quiets SNaNs. 10091 v8::Handle<v8::Value> number = v8::Number::New(test_value); 10092 double stored_number = number->NumberValue(); 10093 if (!IsNaN(test_value)) { 10094 CHECK_EQ(test_value, stored_number); 10095 } else { 10096 uint64_t stored_bits = DoubleToBits(stored_number); 10097 // Check if quiet nan (bits 51..62 all set). 10098 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 10099 } 10100 10101 // Check that Date::New preserves non-NaNs in the date range and 10102 // quiets SNaNs. 10103 v8::Handle<v8::Value> date = v8::Date::New(test_value); 10104 double expected_stored_date = DoubleToDateTime(test_value); 10105 double stored_date = date->NumberValue(); 10106 if (!IsNaN(expected_stored_date)) { 10107 CHECK_EQ(expected_stored_date, stored_date); 10108 } else { 10109 uint64_t stored_bits = DoubleToBits(stored_date); 10110 // Check if quiet nan (bits 51..62 all set). 10111 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 10112 } 10113 } 10114} 10115 10116 10117static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) { 10118 v8::HandleScope scope; 10119 v8::TryCatch tc; 10120 v8::Handle<v8::String> str = args[0]->ToString(); 10121 if (tc.HasCaught()) 10122 return tc.ReThrow(); 10123 return v8::Undefined(); 10124} 10125 10126 10127// Test that an exception can be propagated down through a spaghetti 10128// stack using ReThrow. 10129THREADED_TEST(SpaghettiStackReThrow) { 10130 v8::HandleScope scope; 10131 LocalContext context; 10132 context->Global()->Set( 10133 v8::String::New("s"), 10134 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction()); 10135 v8::TryCatch try_catch; 10136 CompileRun( 10137 "var i = 0;" 10138 "var o = {" 10139 " toString: function () {" 10140 " if (i == 10) {" 10141 " throw 'Hey!';" 10142 " } else {" 10143 " i++;" 10144 " return s(o);" 10145 " }" 10146 " }" 10147 "};" 10148 "s(o);"); 10149 CHECK(try_catch.HasCaught()); 10150 v8::String::Utf8Value value(try_catch.Exception()); 10151 CHECK_EQ(0, strcmp(*value, "Hey!")); 10152} 10153 10154 10155TEST(Regress528) { 10156 v8::V8::Initialize(); 10157 10158 v8::HandleScope scope; 10159 v8::Persistent<Context> context; 10160 v8::Persistent<Context> other_context; 10161 int gc_count; 10162 10163 // Create a context used to keep the code from aging in the compilation 10164 // cache. 10165 other_context = Context::New(); 10166 10167 // Context-dependent context data creates reference from the compilation 10168 // cache to the global object. 10169 const char* source_simple = "1"; 10170 context = Context::New(); 10171 { 10172 v8::HandleScope scope; 10173 10174 context->Enter(); 10175 Local<v8::String> obj = v8::String::New(""); 10176 context->SetData(obj); 10177 CompileRun(source_simple); 10178 context->Exit(); 10179 } 10180 context.Dispose(); 10181 for (gc_count = 1; gc_count < 10; gc_count++) { 10182 other_context->Enter(); 10183 CompileRun(source_simple); 10184 other_context->Exit(); 10185 v8::internal::Heap::CollectAllGarbage(false); 10186 if (GetGlobalObjectsCount() == 1) break; 10187 } 10188 CHECK_GE(2, gc_count); 10189 CHECK_EQ(1, GetGlobalObjectsCount()); 10190 10191 // Eval in a function creates reference from the compilation cache to the 10192 // global object. 10193 const char* source_eval = "function f(){eval('1')}; f()"; 10194 context = Context::New(); 10195 { 10196 v8::HandleScope scope; 10197 10198 context->Enter(); 10199 CompileRun(source_eval); 10200 context->Exit(); 10201 } 10202 context.Dispose(); 10203 for (gc_count = 1; gc_count < 10; gc_count++) { 10204 other_context->Enter(); 10205 CompileRun(source_eval); 10206 other_context->Exit(); 10207 v8::internal::Heap::CollectAllGarbage(false); 10208 if (GetGlobalObjectsCount() == 1) break; 10209 } 10210 CHECK_GE(2, gc_count); 10211 CHECK_EQ(1, GetGlobalObjectsCount()); 10212 10213 // Looking up the line number for an exception creates reference from the 10214 // compilation cache to the global object. 10215 const char* source_exception = "function f(){throw 1;} f()"; 10216 context = Context::New(); 10217 { 10218 v8::HandleScope scope; 10219 10220 context->Enter(); 10221 v8::TryCatch try_catch; 10222 CompileRun(source_exception); 10223 CHECK(try_catch.HasCaught()); 10224 v8::Handle<v8::Message> message = try_catch.Message(); 10225 CHECK(!message.IsEmpty()); 10226 CHECK_EQ(1, message->GetLineNumber()); 10227 context->Exit(); 10228 } 10229 context.Dispose(); 10230 for (gc_count = 1; gc_count < 10; gc_count++) { 10231 other_context->Enter(); 10232 CompileRun(source_exception); 10233 other_context->Exit(); 10234 v8::internal::Heap::CollectAllGarbage(false); 10235 if (GetGlobalObjectsCount() == 1) break; 10236 } 10237 CHECK_GE(2, gc_count); 10238 CHECK_EQ(1, GetGlobalObjectsCount()); 10239 10240 other_context.Dispose(); 10241} 10242 10243 10244THREADED_TEST(ScriptOrigin) { 10245 v8::HandleScope scope; 10246 LocalContext env; 10247 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 10248 v8::Handle<v8::String> script = v8::String::New( 10249 "function f() {}\n\nfunction g() {}"); 10250 v8::Script::Compile(script, &origin)->Run(); 10251 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 10252 env->Global()->Get(v8::String::New("f"))); 10253 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 10254 env->Global()->Get(v8::String::New("g"))); 10255 10256 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin(); 10257 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName())); 10258 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value()); 10259 10260 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin(); 10261 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName())); 10262 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value()); 10263} 10264 10265 10266THREADED_TEST(ScriptLineNumber) { 10267 v8::HandleScope scope; 10268 LocalContext env; 10269 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 10270 v8::Handle<v8::String> script = v8::String::New( 10271 "function f() {}\n\nfunction g() {}"); 10272 v8::Script::Compile(script, &origin)->Run(); 10273 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 10274 env->Global()->Get(v8::String::New("f"))); 10275 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 10276 env->Global()->Get(v8::String::New("g"))); 10277 CHECK_EQ(0, f->GetScriptLineNumber()); 10278 CHECK_EQ(2, g->GetScriptLineNumber()); 10279} 10280 10281 10282static v8::Handle<Value> GetterWhichReturns42(Local<String> name, 10283 const AccessorInfo& info) { 10284 return v8_num(42); 10285} 10286 10287 10288static void SetterWhichSetsYOnThisTo23(Local<String> name, 10289 Local<Value> value, 10290 const AccessorInfo& info) { 10291 info.This()->Set(v8_str("y"), v8_num(23)); 10292} 10293 10294 10295TEST(SetterOnConstructorPrototype) { 10296 v8::HandleScope scope; 10297 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10298 templ->SetAccessor(v8_str("x"), 10299 GetterWhichReturns42, 10300 SetterWhichSetsYOnThisTo23); 10301 LocalContext context; 10302 context->Global()->Set(v8_str("P"), templ->NewInstance()); 10303 CompileRun("function C1() {" 10304 " this.x = 23;" 10305 "};" 10306 "C1.prototype = P;" 10307 "function C2() {" 10308 " this.x = 23" 10309 "};" 10310 "C2.prototype = { };" 10311 "C2.prototype.__proto__ = P;"); 10312 10313 v8::Local<v8::Script> script; 10314 script = v8::Script::Compile(v8_str("new C1();")); 10315 for (int i = 0; i < 10; i++) { 10316 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 10317 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); 10318 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); 10319 } 10320 10321 script = v8::Script::Compile(v8_str("new C2();")); 10322 for (int i = 0; i < 10; i++) { 10323 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 10324 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value()); 10325 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value()); 10326 } 10327} 10328 10329 10330static v8::Handle<Value> NamedPropertyGetterWhichReturns42( 10331 Local<String> name, const AccessorInfo& info) { 10332 return v8_num(42); 10333} 10334 10335 10336static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23( 10337 Local<String> name, Local<Value> value, const AccessorInfo& info) { 10338 if (name->Equals(v8_str("x"))) { 10339 info.This()->Set(v8_str("y"), v8_num(23)); 10340 } 10341 return v8::Handle<Value>(); 10342} 10343 10344 10345THREADED_TEST(InterceptorOnConstructorPrototype) { 10346 v8::HandleScope scope; 10347 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10348 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42, 10349 NamedPropertySetterWhichSetsYOnThisTo23); 10350 LocalContext context; 10351 context->Global()->Set(v8_str("P"), templ->NewInstance()); 10352 CompileRun("function C1() {" 10353 " this.x = 23;" 10354 "};" 10355 "C1.prototype = P;" 10356 "function C2() {" 10357 " this.x = 23" 10358 "};" 10359 "C2.prototype = { };" 10360 "C2.prototype.__proto__ = P;"); 10361 10362 v8::Local<v8::Script> script; 10363 script = v8::Script::Compile(v8_str("new C1();")); 10364 for (int i = 0; i < 10; i++) { 10365 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 10366 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); 10367 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); 10368 } 10369 10370 script = v8::Script::Compile(v8_str("new C2();")); 10371 for (int i = 0; i < 10; i++) { 10372 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 10373 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value()); 10374 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value()); 10375 } 10376} 10377 10378 10379TEST(Bug618) { 10380 const char* source = "function C1() {" 10381 " this.x = 23;" 10382 "};" 10383 "C1.prototype = P;"; 10384 10385 v8::HandleScope scope; 10386 LocalContext context; 10387 v8::Local<v8::Script> script; 10388 10389 // Use a simple object as prototype. 10390 v8::Local<v8::Object> prototype = v8::Object::New(); 10391 prototype->Set(v8_str("y"), v8_num(42)); 10392 context->Global()->Set(v8_str("P"), prototype); 10393 10394 // This compile will add the code to the compilation cache. 10395 CompileRun(source); 10396 10397 script = v8::Script::Compile(v8_str("new C1();")); 10398 for (int i = 0; i < 10; i++) { 10399 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 10400 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); 10401 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); 10402 } 10403 10404 // Use an API object with accessors as prototype. 10405 Local<ObjectTemplate> templ = ObjectTemplate::New(); 10406 templ->SetAccessor(v8_str("x"), 10407 GetterWhichReturns42, 10408 SetterWhichSetsYOnThisTo23); 10409 context->Global()->Set(v8_str("P"), templ->NewInstance()); 10410 10411 // This compile will get the code from the compilation cache. 10412 CompileRun(source); 10413 10414 script = v8::Script::Compile(v8_str("new C1();")); 10415 for (int i = 0; i < 10; i++) { 10416 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 10417 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); 10418 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); 10419 } 10420} 10421 10422int prologue_call_count = 0; 10423int epilogue_call_count = 0; 10424int prologue_call_count_second = 0; 10425int epilogue_call_count_second = 0; 10426 10427void PrologueCallback(v8::GCType, v8::GCCallbackFlags) { 10428 ++prologue_call_count; 10429} 10430 10431void EpilogueCallback(v8::GCType, v8::GCCallbackFlags) { 10432 ++epilogue_call_count; 10433} 10434 10435void PrologueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { 10436 ++prologue_call_count_second; 10437} 10438 10439void EpilogueCallbackSecond(v8::GCType, v8::GCCallbackFlags) { 10440 ++epilogue_call_count_second; 10441} 10442 10443TEST(GCCallbacks) { 10444 LocalContext context; 10445 10446 v8::V8::AddGCPrologueCallback(PrologueCallback); 10447 v8::V8::AddGCEpilogueCallback(EpilogueCallback); 10448 CHECK_EQ(0, prologue_call_count); 10449 CHECK_EQ(0, epilogue_call_count); 10450 i::Heap::CollectAllGarbage(false); 10451 CHECK_EQ(1, prologue_call_count); 10452 CHECK_EQ(1, epilogue_call_count); 10453 v8::V8::AddGCPrologueCallback(PrologueCallbackSecond); 10454 v8::V8::AddGCEpilogueCallback(EpilogueCallbackSecond); 10455 i::Heap::CollectAllGarbage(false); 10456 CHECK_EQ(2, prologue_call_count); 10457 CHECK_EQ(2, epilogue_call_count); 10458 CHECK_EQ(1, prologue_call_count_second); 10459 CHECK_EQ(1, epilogue_call_count_second); 10460 v8::V8::RemoveGCPrologueCallback(PrologueCallback); 10461 v8::V8::RemoveGCEpilogueCallback(EpilogueCallback); 10462 i::Heap::CollectAllGarbage(false); 10463 CHECK_EQ(2, prologue_call_count); 10464 CHECK_EQ(2, epilogue_call_count); 10465 CHECK_EQ(2, prologue_call_count_second); 10466 CHECK_EQ(2, epilogue_call_count_second); 10467 v8::V8::RemoveGCPrologueCallback(PrologueCallbackSecond); 10468 v8::V8::RemoveGCEpilogueCallback(EpilogueCallbackSecond); 10469 i::Heap::CollectAllGarbage(false); 10470 CHECK_EQ(2, prologue_call_count); 10471 CHECK_EQ(2, epilogue_call_count); 10472 CHECK_EQ(2, prologue_call_count_second); 10473 CHECK_EQ(2, epilogue_call_count_second); 10474} 10475 10476 10477THREADED_TEST(AddToJSFunctionResultCache) { 10478 i::FLAG_allow_natives_syntax = true; 10479 v8::HandleScope scope; 10480 10481 LocalContext context; 10482 10483 const char* code = 10484 "(function() {" 10485 " var key0 = 'a';" 10486 " var key1 = 'b';" 10487 " var r0 = %_GetFromCache(0, key0);" 10488 " var r1 = %_GetFromCache(0, key1);" 10489 " var r0_ = %_GetFromCache(0, key0);" 10490 " if (r0 !== r0_)" 10491 " return 'Different results for ' + key0 + ': ' + r0 + ' vs. ' + r0_;" 10492 " var r1_ = %_GetFromCache(0, key1);" 10493 " if (r1 !== r1_)" 10494 " return 'Different results for ' + key1 + ': ' + r1 + ' vs. ' + r1_;" 10495 " return 'PASSED';" 10496 "})()"; 10497 v8::internal::Heap::ClearJSFunctionResultCaches(); 10498 ExpectString(code, "PASSED"); 10499} 10500 10501 10502static const int k0CacheSize = 16; 10503 10504THREADED_TEST(FillJSFunctionResultCache) { 10505 i::FLAG_allow_natives_syntax = true; 10506 v8::HandleScope scope; 10507 10508 LocalContext context; 10509 10510 const char* code = 10511 "(function() {" 10512 " var k = 'a';" 10513 " var r = %_GetFromCache(0, k);" 10514 " for (var i = 0; i < 16; i++) {" 10515 " %_GetFromCache(0, 'a' + i);" 10516 " };" 10517 " if (r === %_GetFromCache(0, k))" 10518 " return 'FAILED: k0CacheSize is too small';" 10519 " return 'PASSED';" 10520 "})()"; 10521 v8::internal::Heap::ClearJSFunctionResultCaches(); 10522 ExpectString(code, "PASSED"); 10523} 10524 10525 10526THREADED_TEST(RoundRobinGetFromCache) { 10527 i::FLAG_allow_natives_syntax = true; 10528 v8::HandleScope scope; 10529 10530 LocalContext context; 10531 10532 const char* code = 10533 "(function() {" 10534 " var keys = [];" 10535 " for (var i = 0; i < 16; i++) keys.push(i);" 10536 " var values = [];" 10537 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" 10538 " for (var i = 0; i < 16; i++) {" 10539 " var v = %_GetFromCache(0, keys[i]);" 10540 " if (v !== values[i])" 10541 " return 'Wrong value for ' + " 10542 " keys[i] + ': ' + v + ' vs. ' + values[i];" 10543 " };" 10544 " return 'PASSED';" 10545 "})()"; 10546 v8::internal::Heap::ClearJSFunctionResultCaches(); 10547 ExpectString(code, "PASSED"); 10548} 10549 10550 10551THREADED_TEST(ReverseGetFromCache) { 10552 i::FLAG_allow_natives_syntax = true; 10553 v8::HandleScope scope; 10554 10555 LocalContext context; 10556 10557 const char* code = 10558 "(function() {" 10559 " var keys = [];" 10560 " for (var i = 0; i < 16; i++) keys.push(i);" 10561 " var values = [];" 10562 " for (var i = 0; i < 16; i++) values[i] = %_GetFromCache(0, keys[i]);" 10563 " for (var i = 15; i >= 16; i--) {" 10564 " var v = %_GetFromCache(0, keys[i]);" 10565 " if (v !== values[i])" 10566 " return 'Wrong value for ' + " 10567 " keys[i] + ': ' + v + ' vs. ' + values[i];" 10568 " };" 10569 " return 'PASSED';" 10570 "})()"; 10571 v8::internal::Heap::ClearJSFunctionResultCaches(); 10572 ExpectString(code, "PASSED"); 10573} 10574 10575 10576THREADED_TEST(TestEviction) { 10577 i::FLAG_allow_natives_syntax = true; 10578 v8::HandleScope scope; 10579 10580 LocalContext context; 10581 10582 const char* code = 10583 "(function() {" 10584 " for (var i = 0; i < 2*16; i++) {" 10585 " %_GetFromCache(0, 'a' + i);" 10586 " };" 10587 " return 'PASSED';" 10588 "})()"; 10589 v8::internal::Heap::ClearJSFunctionResultCaches(); 10590 ExpectString(code, "PASSED"); 10591} 10592