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 41 static const bool kLogThreading = true; 42 43 static bool IsNaN(double x) { 44 #ifdef WIN32 45 return _isnan(x); 46 #else 47 return isnan(x); 48 #endif 49 } 50 51 using ::v8::ObjectTemplate; 52 using ::v8::Value; 53 using ::v8::Context; 54 using ::v8::Local; 55 using ::v8::String; 56 using ::v8::Script; 57 using ::v8::Function; 58 using ::v8::AccessorInfo; 59 using ::v8::Extension; 60 61 namespace i = ::v8::internal; 62 63 64 static 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 72 static 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 79 static void ExpectObject(const char* code, Local<Value> expected) { 80 Local<Value> result = CompileRun(code); 81 CHECK(result->Equals(expected)); 82 } 83 84 85 static int signature_callback_count; 86 static v8::Handle<Value> IncrementingSignatureCallback( 87 const v8::Arguments& args) { 88 ApiTestFuzzer::Fuzz(); 89 signature_callback_count++; 90 v8::Handle<v8::Array> result = v8::Array::New(args.Length()); 91 for (int i = 0; i < args.Length(); i++) 92 result->Set(v8::Integer::New(i), args[i]); 93 return result; 94 } 95 96 97 static v8::Handle<Value> SignatureCallback(const v8::Arguments& args) { 98 ApiTestFuzzer::Fuzz(); 99 v8::Handle<v8::Array> result = v8::Array::New(args.Length()); 100 for (int i = 0; i < args.Length(); i++) { 101 result->Set(v8::Integer::New(i), args[i]); 102 } 103 return result; 104 } 105 106 107 THREADED_TEST(Handles) { 108 v8::HandleScope scope; 109 Local<Context> local_env; 110 { 111 LocalContext env; 112 local_env = env.local(); 113 } 114 115 // Local context should still be live. 116 CHECK(!local_env.IsEmpty()); 117 local_env->Enter(); 118 119 v8::Handle<v8::Primitive> undef = v8::Undefined(); 120 CHECK(!undef.IsEmpty()); 121 CHECK(undef->IsUndefined()); 122 123 const char* c_source = "1 + 2 + 3"; 124 Local<String> source = String::New(c_source); 125 Local<Script> script = Script::Compile(source); 126 CHECK_EQ(6, script->Run()->Int32Value()); 127 128 local_env->Exit(); 129 } 130 131 132 THREADED_TEST(ReceiverSignature) { 133 v8::HandleScope scope; 134 LocalContext env; 135 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); 136 v8::Handle<v8::Signature> sig = v8::Signature::New(fun); 137 fun->PrototypeTemplate()->Set( 138 v8_str("m"), 139 v8::FunctionTemplate::New(IncrementingSignatureCallback, 140 v8::Handle<Value>(), 141 sig)); 142 env->Global()->Set(v8_str("Fun"), fun->GetFunction()); 143 signature_callback_count = 0; 144 CompileRun( 145 "var o = new Fun();" 146 "o.m();"); 147 CHECK_EQ(1, signature_callback_count); 148 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New(); 149 sub_fun->Inherit(fun); 150 env->Global()->Set(v8_str("SubFun"), sub_fun->GetFunction()); 151 CompileRun( 152 "var o = new SubFun();" 153 "o.m();"); 154 CHECK_EQ(2, signature_callback_count); 155 156 v8::TryCatch try_catch; 157 CompileRun( 158 "var o = { };" 159 "o.m = Fun.prototype.m;" 160 "o.m();"); 161 CHECK_EQ(2, signature_callback_count); 162 CHECK(try_catch.HasCaught()); 163 try_catch.Reset(); 164 v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New(); 165 sub_fun->Inherit(fun); 166 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction()); 167 CompileRun( 168 "var o = new UnrelFun();" 169 "o.m = Fun.prototype.m;" 170 "o.m();"); 171 CHECK_EQ(2, signature_callback_count); 172 CHECK(try_catch.HasCaught()); 173 } 174 175 176 177 178 THREADED_TEST(ArgumentSignature) { 179 v8::HandleScope scope; 180 LocalContext env; 181 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New(); 182 cons->SetClassName(v8_str("Cons")); 183 v8::Handle<v8::Signature> sig = 184 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 1, &cons); 185 v8::Handle<v8::FunctionTemplate> fun = 186 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), sig); 187 env->Global()->Set(v8_str("Cons"), cons->GetFunction()); 188 env->Global()->Set(v8_str("Fun1"), fun->GetFunction()); 189 190 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';"); 191 CHECK(value1->IsTrue()); 192 193 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';"); 194 CHECK(value2->IsTrue()); 195 196 v8::Handle<Value> value3 = CompileRun("Fun1() == '';"); 197 CHECK(value3->IsTrue()); 198 199 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New(); 200 cons1->SetClassName(v8_str("Cons1")); 201 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New(); 202 cons2->SetClassName(v8_str("Cons2")); 203 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New(); 204 cons3->SetClassName(v8_str("Cons3")); 205 206 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 }; 207 v8::Handle<v8::Signature> wsig = 208 v8::Signature::New(v8::Handle<v8::FunctionTemplate>(), 3, args); 209 v8::Handle<v8::FunctionTemplate> fun2 = 210 v8::FunctionTemplate::New(SignatureCallback, v8::Handle<Value>(), wsig); 211 212 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction()); 213 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction()); 214 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction()); 215 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction()); 216 v8::Handle<Value> value4 = CompileRun( 217 "Fun2(new Cons1(), new Cons2(), new Cons3()) ==" 218 "'[object Cons1],[object Cons2],[object Cons3]'"); 219 CHECK(value4->IsTrue()); 220 221 v8::Handle<Value> value5 = CompileRun( 222 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'"); 223 CHECK(value5->IsTrue()); 224 225 v8::Handle<Value> value6 = CompileRun( 226 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'"); 227 CHECK(value6->IsTrue()); 228 229 v8::Handle<Value> value7 = CompileRun( 230 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == " 231 "'[object Cons1],[object Cons2],[object Cons3],d';"); 232 CHECK(value7->IsTrue()); 233 234 v8::Handle<Value> value8 = CompileRun( 235 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'"); 236 CHECK(value8->IsTrue()); 237 } 238 239 240 THREADED_TEST(HulIgennem) { 241 v8::HandleScope scope; 242 LocalContext env; 243 v8::Handle<v8::Primitive> undef = v8::Undefined(); 244 Local<String> undef_str = undef->ToString(); 245 char* value = i::NewArray<char>(undef_str->Length() + 1); 246 undef_str->WriteAscii(value); 247 CHECK_EQ(0, strcmp(value, "undefined")); 248 i::DeleteArray(value); 249 } 250 251 252 THREADED_TEST(Access) { 253 v8::HandleScope scope; 254 LocalContext env; 255 Local<v8::Object> obj = v8::Object::New(); 256 Local<Value> foo_before = obj->Get(v8_str("foo")); 257 CHECK(foo_before->IsUndefined()); 258 Local<String> bar_str = v8_str("bar"); 259 obj->Set(v8_str("foo"), bar_str); 260 Local<Value> foo_after = obj->Get(v8_str("foo")); 261 CHECK(!foo_after->IsUndefined()); 262 CHECK(foo_after->IsString()); 263 CHECK_EQ(bar_str, foo_after); 264 } 265 266 267 THREADED_TEST(Script) { 268 v8::HandleScope scope; 269 LocalContext env; 270 const char* c_source = "1 + 2 + 3"; 271 Local<String> source = String::New(c_source); 272 Local<Script> script = Script::Compile(source); 273 CHECK_EQ(6, script->Run()->Int32Value()); 274 } 275 276 277 static uint16_t* AsciiToTwoByteString(const char* source) { 278 int array_length = i::StrLength(source) + 1; 279 uint16_t* converted = i::NewArray<uint16_t>(array_length); 280 for (int i = 0; i < array_length; i++) converted[i] = source[i]; 281 return converted; 282 } 283 284 285 class TestResource: public String::ExternalStringResource { 286 public: 287 static int dispose_count; 288 289 explicit TestResource(uint16_t* data) 290 : data_(data), length_(0) { 291 while (data[length_]) ++length_; 292 } 293 294 ~TestResource() { 295 i::DeleteArray(data_); 296 ++dispose_count; 297 } 298 299 const uint16_t* data() const { 300 return data_; 301 } 302 303 size_t length() const { 304 return length_; 305 } 306 private: 307 uint16_t* data_; 308 size_t length_; 309 }; 310 311 312 int TestResource::dispose_count = 0; 313 314 315 class TestAsciiResource: public String::ExternalAsciiStringResource { 316 public: 317 static int dispose_count; 318 319 explicit TestAsciiResource(const char* data) 320 : data_(data), 321 length_(strlen(data)) { } 322 323 ~TestAsciiResource() { 324 i::DeleteArray(data_); 325 ++dispose_count; 326 } 327 328 const char* data() const { 329 return data_; 330 } 331 332 size_t length() const { 333 return length_; 334 } 335 private: 336 const char* data_; 337 size_t length_; 338 }; 339 340 341 int TestAsciiResource::dispose_count = 0; 342 343 344 THREADED_TEST(ScriptUsingStringResource) { 345 TestResource::dispose_count = 0; 346 const char* c_source = "1 + 2 * 3"; 347 uint16_t* two_byte_source = AsciiToTwoByteString(c_source); 348 { 349 v8::HandleScope scope; 350 LocalContext env; 351 TestResource* resource = new TestResource(two_byte_source); 352 Local<String> source = String::NewExternal(resource); 353 Local<Script> script = Script::Compile(source); 354 Local<Value> value = script->Run(); 355 CHECK(value->IsNumber()); 356 CHECK_EQ(7, value->Int32Value()); 357 CHECK(source->IsExternal()); 358 CHECK_EQ(resource, 359 static_cast<TestResource*>(source->GetExternalStringResource())); 360 v8::internal::Heap::CollectAllGarbage(false); 361 CHECK_EQ(0, TestResource::dispose_count); 362 } 363 v8::internal::CompilationCache::Clear(); 364 v8::internal::Heap::CollectAllGarbage(false); 365 CHECK_EQ(1, TestResource::dispose_count); 366 } 367 368 369 THREADED_TEST(ScriptUsingAsciiStringResource) { 370 TestAsciiResource::dispose_count = 0; 371 const char* c_source = "1 + 2 * 3"; 372 { 373 v8::HandleScope scope; 374 LocalContext env; 375 Local<String> source = 376 String::NewExternal(new TestAsciiResource(i::StrDup(c_source))); 377 Local<Script> script = Script::Compile(source); 378 Local<Value> value = script->Run(); 379 CHECK(value->IsNumber()); 380 CHECK_EQ(7, value->Int32Value()); 381 v8::internal::Heap::CollectAllGarbage(false); 382 CHECK_EQ(0, TestAsciiResource::dispose_count); 383 } 384 v8::internal::CompilationCache::Clear(); 385 v8::internal::Heap::CollectAllGarbage(false); 386 CHECK_EQ(1, TestAsciiResource::dispose_count); 387 } 388 389 390 THREADED_TEST(ScriptMakingExternalString) { 391 TestResource::dispose_count = 0; 392 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3"); 393 { 394 v8::HandleScope scope; 395 LocalContext env; 396 Local<String> source = String::New(two_byte_source); 397 // Trigger GCs so that the newly allocated string moves to old gen. 398 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 399 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 400 bool success = source->MakeExternal(new TestResource(two_byte_source)); 401 CHECK(success); 402 Local<Script> script = Script::Compile(source); 403 Local<Value> value = script->Run(); 404 CHECK(value->IsNumber()); 405 CHECK_EQ(7, value->Int32Value()); 406 v8::internal::Heap::CollectAllGarbage(false); 407 CHECK_EQ(0, TestResource::dispose_count); 408 } 409 v8::internal::CompilationCache::Clear(); 410 v8::internal::Heap::CollectAllGarbage(false); 411 CHECK_EQ(1, TestResource::dispose_count); 412 } 413 414 415 THREADED_TEST(ScriptMakingExternalAsciiString) { 416 TestAsciiResource::dispose_count = 0; 417 const char* c_source = "1 + 2 * 3"; 418 { 419 v8::HandleScope scope; 420 LocalContext env; 421 Local<String> source = v8_str(c_source); 422 // Trigger GCs so that the newly allocated string moves to old gen. 423 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 424 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 425 bool success = source->MakeExternal( 426 new TestAsciiResource(i::StrDup(c_source))); 427 CHECK(success); 428 Local<Script> script = Script::Compile(source); 429 Local<Value> value = script->Run(); 430 CHECK(value->IsNumber()); 431 CHECK_EQ(7, value->Int32Value()); 432 v8::internal::Heap::CollectAllGarbage(false); 433 CHECK_EQ(0, TestAsciiResource::dispose_count); 434 } 435 v8::internal::CompilationCache::Clear(); 436 v8::internal::Heap::CollectAllGarbage(false); 437 CHECK_EQ(1, TestAsciiResource::dispose_count); 438 } 439 440 441 TEST(MakingExternalStringConditions) { 442 v8::HandleScope scope; 443 LocalContext env; 444 445 // Free some space in the new space so that we can check freshness. 446 i::Heap::CollectGarbage(0, i::NEW_SPACE); 447 i::Heap::CollectGarbage(0, i::NEW_SPACE); 448 449 Local<String> small_string = String::New(AsciiToTwoByteString("small")); 450 // We should refuse to externalize newly created small string. 451 CHECK(!small_string->CanMakeExternal()); 452 // Trigger GCs so that the newly allocated string moves to old gen. 453 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 454 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 455 // Old space strings should be accepted. 456 CHECK(small_string->CanMakeExternal()); 457 458 small_string = String::New(AsciiToTwoByteString("small 2")); 459 // We should refuse externalizing newly created small string. 460 CHECK(!small_string->CanMakeExternal()); 461 for (int i = 0; i < 100; i++) { 462 String::Value value(small_string); 463 } 464 // Frequently used strings should be accepted. 465 CHECK(small_string->CanMakeExternal()); 466 467 const int buf_size = 10 * 1024; 468 char* buf = i::NewArray<char>(buf_size); 469 memset(buf, 'a', buf_size); 470 buf[buf_size - 1] = '\0'; 471 Local<String> large_string = String::New(AsciiToTwoByteString(buf)); 472 i::DeleteArray(buf); 473 // Large strings should be immediately accepted. 474 CHECK(large_string->CanMakeExternal()); 475 } 476 477 478 TEST(MakingExternalAsciiStringConditions) { 479 v8::HandleScope scope; 480 LocalContext env; 481 482 // Free some space in the new space so that we can check freshness. 483 i::Heap::CollectGarbage(0, i::NEW_SPACE); 484 i::Heap::CollectGarbage(0, i::NEW_SPACE); 485 486 Local<String> small_string = String::New("small"); 487 // We should refuse to externalize newly created small string. 488 CHECK(!small_string->CanMakeExternal()); 489 // Trigger GCs so that the newly allocated string moves to old gen. 490 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 491 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 492 // Old space strings should be accepted. 493 CHECK(small_string->CanMakeExternal()); 494 495 small_string = String::New("small 2"); 496 // We should refuse externalizing newly created small string. 497 CHECK(!small_string->CanMakeExternal()); 498 for (int i = 0; i < 100; i++) { 499 String::Value value(small_string); 500 } 501 // Frequently used strings should be accepted. 502 CHECK(small_string->CanMakeExternal()); 503 504 const int buf_size = 10 * 1024; 505 char* buf = i::NewArray<char>(buf_size); 506 memset(buf, 'a', buf_size); 507 buf[buf_size - 1] = '\0'; 508 Local<String> large_string = String::New(buf); 509 i::DeleteArray(buf); 510 // Large strings should be immediately accepted. 511 CHECK(large_string->CanMakeExternal()); 512 } 513 514 515 THREADED_TEST(UsingExternalString) { 516 { 517 v8::HandleScope scope; 518 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 519 Local<String> string = 520 String::NewExternal(new TestResource(two_byte_string)); 521 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 522 // Trigger GCs so that the newly allocated string moves to old gen. 523 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 524 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 525 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring); 526 CHECK(isymbol->IsSymbol()); 527 } 528 i::Heap::CollectAllGarbage(false); 529 i::Heap::CollectAllGarbage(false); 530 } 531 532 533 THREADED_TEST(UsingExternalAsciiString) { 534 { 535 v8::HandleScope scope; 536 const char* one_byte_string = "test string"; 537 Local<String> string = String::NewExternal( 538 new TestAsciiResource(i::StrDup(one_byte_string))); 539 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 540 // Trigger GCs so that the newly allocated string moves to old gen. 541 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now 542 i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now 543 i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring); 544 CHECK(isymbol->IsSymbol()); 545 } 546 i::Heap::CollectAllGarbage(false); 547 i::Heap::CollectAllGarbage(false); 548 } 549 550 551 THREADED_TEST(ScavengeExternalString) { 552 TestResource::dispose_count = 0; 553 { 554 v8::HandleScope scope; 555 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 556 Local<String> string = 557 String::NewExternal(new TestResource(two_byte_string)); 558 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 559 i::Heap::CollectGarbage(0, i::NEW_SPACE); 560 CHECK(i::Heap::InNewSpace(*istring)); 561 CHECK_EQ(0, TestResource::dispose_count); 562 } 563 i::Heap::CollectGarbage(0, i::NEW_SPACE); 564 CHECK_EQ(1, TestResource::dispose_count); 565 } 566 567 568 THREADED_TEST(ScavengeExternalAsciiString) { 569 TestAsciiResource::dispose_count = 0; 570 { 571 v8::HandleScope scope; 572 const char* one_byte_string = "test string"; 573 Local<String> string = String::NewExternal( 574 new TestAsciiResource(i::StrDup(one_byte_string))); 575 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 576 i::Heap::CollectGarbage(0, i::NEW_SPACE); 577 CHECK(i::Heap::InNewSpace(*istring)); 578 CHECK_EQ(0, TestAsciiResource::dispose_count); 579 } 580 i::Heap::CollectGarbage(0, i::NEW_SPACE); 581 CHECK_EQ(1, TestAsciiResource::dispose_count); 582 } 583 584 585 THREADED_TEST(StringConcat) { 586 { 587 v8::HandleScope scope; 588 LocalContext env; 589 const char* one_byte_string_1 = "function a_times_t"; 590 const char* two_byte_string_1 = "wo_plus_b(a, b) {return "; 591 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + "; 592 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + "; 593 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 594 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 595 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);"; 596 Local<String> left = v8_str(one_byte_string_1); 597 Local<String> right = String::New(AsciiToTwoByteString(two_byte_string_1)); 598 Local<String> source = String::Concat(left, right); 599 right = String::NewExternal( 600 new TestAsciiResource(i::StrDup(one_byte_extern_1))); 601 source = String::Concat(source, right); 602 right = String::NewExternal( 603 new TestResource(AsciiToTwoByteString(two_byte_extern_1))); 604 source = String::Concat(source, right); 605 right = v8_str(one_byte_string_2); 606 source = String::Concat(source, right); 607 right = String::New(AsciiToTwoByteString(two_byte_string_2)); 608 source = String::Concat(source, right); 609 right = String::NewExternal( 610 new TestResource(AsciiToTwoByteString(two_byte_extern_2))); 611 source = String::Concat(source, right); 612 Local<Script> script = Script::Compile(source); 613 Local<Value> value = script->Run(); 614 CHECK(value->IsNumber()); 615 CHECK_EQ(68, value->Int32Value()); 616 } 617 v8::internal::CompilationCache::Clear(); 618 i::Heap::CollectAllGarbage(false); 619 i::Heap::CollectAllGarbage(false); 620 } 621 622 623 THREADED_TEST(GlobalProperties) { 624 v8::HandleScope scope; 625 LocalContext env; 626 v8::Handle<v8::Object> global = env->Global(); 627 global->Set(v8_str("pi"), v8_num(3.1415926)); 628 Local<Value> pi = global->Get(v8_str("pi")); 629 CHECK_EQ(3.1415926, pi->NumberValue()); 630 } 631 632 633 static v8::Handle<Value> handle_call(const v8::Arguments& args) { 634 ApiTestFuzzer::Fuzz(); 635 return v8_num(102); 636 } 637 638 639 static v8::Handle<Value> construct_call(const v8::Arguments& args) { 640 ApiTestFuzzer::Fuzz(); 641 args.This()->Set(v8_str("x"), v8_num(1)); 642 args.This()->Set(v8_str("y"), v8_num(2)); 643 return args.This(); 644 } 645 646 THREADED_TEST(FunctionTemplate) { 647 v8::HandleScope scope; 648 LocalContext env; 649 { 650 Local<v8::FunctionTemplate> fun_templ = 651 v8::FunctionTemplate::New(handle_call); 652 Local<Function> fun = fun_templ->GetFunction(); 653 env->Global()->Set(v8_str("obj"), fun); 654 Local<Script> script = v8_compile("obj()"); 655 CHECK_EQ(102, script->Run()->Int32Value()); 656 } 657 // Use SetCallHandler to initialize a function template, should work like the 658 // previous one. 659 { 660 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 661 fun_templ->SetCallHandler(handle_call); 662 Local<Function> fun = fun_templ->GetFunction(); 663 env->Global()->Set(v8_str("obj"), fun); 664 Local<Script> script = v8_compile("obj()"); 665 CHECK_EQ(102, script->Run()->Int32Value()); 666 } 667 // Test constructor calls. 668 { 669 Local<v8::FunctionTemplate> fun_templ = 670 v8::FunctionTemplate::New(construct_call); 671 fun_templ->SetClassName(v8_str("funky")); 672 Local<Function> fun = fun_templ->GetFunction(); 673 env->Global()->Set(v8_str("obj"), fun); 674 Local<Script> script = v8_compile("var s = new obj(); s.x"); 675 CHECK_EQ(1, script->Run()->Int32Value()); 676 677 Local<Value> result = v8_compile("(new obj()).toString()")->Run(); 678 CHECK_EQ(v8_str("[object funky]"), result); 679 } 680 } 681 682 683 THREADED_TEST(FindInstanceInPrototypeChain) { 684 v8::HandleScope scope; 685 LocalContext env; 686 687 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New(); 688 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New(); 689 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New(); 690 derived->Inherit(base); 691 692 Local<v8::Function> base_function = base->GetFunction(); 693 Local<v8::Function> derived_function = derived->GetFunction(); 694 Local<v8::Function> other_function = other->GetFunction(); 695 696 Local<v8::Object> base_instance = base_function->NewInstance(); 697 Local<v8::Object> derived_instance = derived_function->NewInstance(); 698 Local<v8::Object> derived_instance2 = derived_function->NewInstance(); 699 Local<v8::Object> other_instance = other_function->NewInstance(); 700 derived_instance2->Set(v8_str("__proto__"), derived_instance); 701 other_instance->Set(v8_str("__proto__"), derived_instance2); 702 703 // base_instance is only an instance of base. 704 CHECK_EQ(base_instance, 705 base_instance->FindInstanceInPrototypeChain(base)); 706 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty()); 707 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 708 709 // derived_instance is an instance of base and derived. 710 CHECK_EQ(derived_instance, 711 derived_instance->FindInstanceInPrototypeChain(base)); 712 CHECK_EQ(derived_instance, 713 derived_instance->FindInstanceInPrototypeChain(derived)); 714 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 715 716 // other_instance is an instance of other and its immediate 717 // prototype derived_instance2 is an instance of base and derived. 718 // Note, derived_instance is an instance of base and derived too, 719 // but it comes after derived_instance2 in the prototype chain of 720 // other_instance. 721 CHECK_EQ(derived_instance2, 722 other_instance->FindInstanceInPrototypeChain(base)); 723 CHECK_EQ(derived_instance2, 724 other_instance->FindInstanceInPrototypeChain(derived)); 725 CHECK_EQ(other_instance, 726 other_instance->FindInstanceInPrototypeChain(other)); 727 } 728 729 730 THREADED_TEST(TinyInteger) { 731 v8::HandleScope scope; 732 LocalContext env; 733 int32_t value = 239; 734 Local<v8::Integer> value_obj = v8::Integer::New(value); 735 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 736 } 737 738 739 THREADED_TEST(BigSmiInteger) { 740 v8::HandleScope scope; 741 LocalContext env; 742 int32_t value = i::Smi::kMaxValue; 743 // We cannot add one to a Smi::kMaxValue without wrapping. 744 if (i::kSmiValueSize < 32) { 745 CHECK(i::Smi::IsValid(value)); 746 CHECK(!i::Smi::IsValid(value + 1)); 747 Local<v8::Integer> value_obj = v8::Integer::New(value); 748 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 749 } 750 } 751 752 753 THREADED_TEST(BigInteger) { 754 v8::HandleScope scope; 755 LocalContext env; 756 // We cannot add one to a Smi::kMaxValue without wrapping. 757 if (i::kSmiValueSize < 32) { 758 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1. 759 // The code will not be run in that case, due to the "if" guard. 760 int32_t value = 761 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1); 762 CHECK(value > i::Smi::kMaxValue); 763 CHECK(!i::Smi::IsValid(value)); 764 Local<v8::Integer> value_obj = v8::Integer::New(value); 765 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 766 } 767 } 768 769 770 THREADED_TEST(TinyUnsignedInteger) { 771 v8::HandleScope scope; 772 LocalContext env; 773 uint32_t value = 239; 774 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 775 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 776 } 777 778 779 THREADED_TEST(BigUnsignedSmiInteger) { 780 v8::HandleScope scope; 781 LocalContext env; 782 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue); 783 CHECK(i::Smi::IsValid(value)); 784 CHECK(!i::Smi::IsValid(value + 1)); 785 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 786 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 787 } 788 789 790 THREADED_TEST(BigUnsignedInteger) { 791 v8::HandleScope scope; 792 LocalContext env; 793 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1; 794 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue)); 795 CHECK(!i::Smi::IsValid(value)); 796 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 797 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 798 } 799 800 801 THREADED_TEST(OutOfSignedRangeUnsignedInteger) { 802 v8::HandleScope scope; 803 LocalContext env; 804 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1; 805 uint32_t value = INT32_MAX_AS_UINT + 1; 806 CHECK(value > INT32_MAX_AS_UINT); // No overflow. 807 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(value); 808 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 809 } 810 811 812 THREADED_TEST(Number) { 813 v8::HandleScope scope; 814 LocalContext env; 815 double PI = 3.1415926; 816 Local<v8::Number> pi_obj = v8::Number::New(PI); 817 CHECK_EQ(PI, pi_obj->NumberValue()); 818 } 819 820 821 THREADED_TEST(ToNumber) { 822 v8::HandleScope scope; 823 LocalContext env; 824 Local<String> str = v8_str("3.1415926"); 825 CHECK_EQ(3.1415926, str->NumberValue()); 826 v8::Handle<v8::Boolean> t = v8::True(); 827 CHECK_EQ(1.0, t->NumberValue()); 828 v8::Handle<v8::Boolean> f = v8::False(); 829 CHECK_EQ(0.0, f->NumberValue()); 830 } 831 832 833 THREADED_TEST(Date) { 834 v8::HandleScope scope; 835 LocalContext env; 836 double PI = 3.1415926; 837 Local<Value> date_obj = v8::Date::New(PI); 838 CHECK_EQ(3.0, date_obj->NumberValue()); 839 } 840 841 842 THREADED_TEST(Boolean) { 843 v8::HandleScope scope; 844 LocalContext env; 845 v8::Handle<v8::Boolean> t = v8::True(); 846 CHECK(t->Value()); 847 v8::Handle<v8::Boolean> f = v8::False(); 848 CHECK(!f->Value()); 849 v8::Handle<v8::Primitive> u = v8::Undefined(); 850 CHECK(!u->BooleanValue()); 851 v8::Handle<v8::Primitive> n = v8::Null(); 852 CHECK(!n->BooleanValue()); 853 v8::Handle<String> str1 = v8_str(""); 854 CHECK(!str1->BooleanValue()); 855 v8::Handle<String> str2 = v8_str("x"); 856 CHECK(str2->BooleanValue()); 857 CHECK(!v8::Number::New(0)->BooleanValue()); 858 CHECK(v8::Number::New(-1)->BooleanValue()); 859 CHECK(v8::Number::New(1)->BooleanValue()); 860 CHECK(v8::Number::New(42)->BooleanValue()); 861 CHECK(!v8_compile("NaN")->Run()->BooleanValue()); 862 } 863 864 865 static v8::Handle<Value> DummyCallHandler(const v8::Arguments& args) { 866 ApiTestFuzzer::Fuzz(); 867 return v8_num(13.4); 868 } 869 870 871 static v8::Handle<Value> GetM(Local<String> name, const AccessorInfo&) { 872 ApiTestFuzzer::Fuzz(); 873 return v8_num(876); 874 } 875 876 877 THREADED_TEST(GlobalPrototype) { 878 v8::HandleScope scope; 879 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 880 func_templ->PrototypeTemplate()->Set( 881 "dummy", 882 v8::FunctionTemplate::New(DummyCallHandler)); 883 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate(); 884 templ->Set("x", v8_num(200)); 885 templ->SetAccessor(v8_str("m"), GetM); 886 LocalContext env(0, templ); 887 v8::Handle<v8::Object> obj = env->Global(); 888 v8::Handle<Script> script = v8_compile("dummy()"); 889 v8::Handle<Value> result = script->Run(); 890 CHECK_EQ(13.4, result->NumberValue()); 891 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value()); 892 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value()); 893 } 894 895 896 THREADED_TEST(ObjectTemplate) { 897 v8::HandleScope scope; 898 Local<ObjectTemplate> templ1 = ObjectTemplate::New(); 899 templ1->Set("x", v8_num(10)); 900 templ1->Set("y", v8_num(13)); 901 LocalContext env; 902 Local<v8::Object> instance1 = templ1->NewInstance(); 903 env->Global()->Set(v8_str("p"), instance1); 904 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue()); 905 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue()); 906 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(); 907 fun->PrototypeTemplate()->Set("nirk", v8_num(123)); 908 Local<ObjectTemplate> templ2 = fun->InstanceTemplate(); 909 templ2->Set("a", v8_num(12)); 910 templ2->Set("b", templ1); 911 Local<v8::Object> instance2 = templ2->NewInstance(); 912 env->Global()->Set(v8_str("q"), instance2); 913 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue()); 914 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue()); 915 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue()); 916 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue()); 917 } 918 919 920 static v8::Handle<Value> GetFlabby(const v8::Arguments& args) { 921 ApiTestFuzzer::Fuzz(); 922 return v8_num(17.2); 923 } 924 925 926 static v8::Handle<Value> GetKnurd(Local<String> property, const AccessorInfo&) { 927 ApiTestFuzzer::Fuzz(); 928 return v8_num(15.2); 929 } 930 931 932 THREADED_TEST(DescriptorInheritance) { 933 v8::HandleScope scope; 934 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New(); 935 super->PrototypeTemplate()->Set("flabby", 936 v8::FunctionTemplate::New(GetFlabby)); 937 super->PrototypeTemplate()->Set("PI", v8_num(3.14)); 938 939 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd); 940 941 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New(); 942 base1->Inherit(super); 943 base1->PrototypeTemplate()->Set("v1", v8_num(20.1)); 944 945 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New(); 946 base2->Inherit(super); 947 base2->PrototypeTemplate()->Set("v2", v8_num(10.1)); 948 949 LocalContext env; 950 951 env->Global()->Set(v8_str("s"), super->GetFunction()); 952 env->Global()->Set(v8_str("base1"), base1->GetFunction()); 953 env->Global()->Set(v8_str("base2"), base2->GetFunction()); 954 955 // Checks right __proto__ chain. 956 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue()); 957 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue()); 958 959 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue()); 960 961 // Instance accessor should not be visible on function object or its prototype 962 CHECK(CompileRun("s.knurd == undefined")->BooleanValue()); 963 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue()); 964 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue()); 965 966 env->Global()->Set(v8_str("obj"), 967 base1->GetFunction()->NewInstance()); 968 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue()); 969 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue()); 970 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue()); 971 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue()); 972 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue()); 973 974 env->Global()->Set(v8_str("obj2"), 975 base2->GetFunction()->NewInstance()); 976 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue()); 977 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue()); 978 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue()); 979 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue()); 980 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue()); 981 982 // base1 and base2 cannot cross reference to each's prototype 983 CHECK(v8_compile("obj.v2")->Run()->IsUndefined()); 984 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined()); 985 } 986 987 988 int echo_named_call_count; 989 990 991 static v8::Handle<Value> EchoNamedProperty(Local<String> name, 992 const AccessorInfo& info) { 993 ApiTestFuzzer::Fuzz(); 994 CHECK_EQ(v8_str("data"), info.Data()); 995 echo_named_call_count++; 996 return name; 997 } 998 999 1000 THREADED_TEST(NamedPropertyHandlerGetter) { 1001 echo_named_call_count = 0; 1002 v8::HandleScope scope; 1003 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1004 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty, 1005 0, 0, 0, 0, 1006 v8_str("data")); 1007 LocalContext env; 1008 env->Global()->Set(v8_str("obj"), 1009 templ->GetFunction()->NewInstance()); 1010 CHECK_EQ(echo_named_call_count, 0); 1011 v8_compile("obj.x")->Run(); 1012 CHECK_EQ(echo_named_call_count, 1); 1013 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;"; 1014 v8::Handle<Value> str = CompileRun(code); 1015 String::AsciiValue value(str); 1016 CHECK_EQ(*value, "oddlepoddle"); 1017 // Check default behavior 1018 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10); 1019 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue()); 1020 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue()); 1021 } 1022 1023 1024 int echo_indexed_call_count = 0; 1025 1026 1027 static v8::Handle<Value> EchoIndexedProperty(uint32_t index, 1028 const AccessorInfo& info) { 1029 ApiTestFuzzer::Fuzz(); 1030 CHECK_EQ(v8_num(637), info.Data()); 1031 echo_indexed_call_count++; 1032 return v8_num(index); 1033 } 1034 1035 1036 THREADED_TEST(IndexedPropertyHandlerGetter) { 1037 v8::HandleScope scope; 1038 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1039 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty, 1040 0, 0, 0, 0, 1041 v8_num(637)); 1042 LocalContext env; 1043 env->Global()->Set(v8_str("obj"), 1044 templ->GetFunction()->NewInstance()); 1045 Local<Script> script = v8_compile("obj[900]"); 1046 CHECK_EQ(script->Run()->Int32Value(), 900); 1047 } 1048 1049 1050 v8::Handle<v8::Object> bottom; 1051 1052 static v8::Handle<Value> CheckThisIndexedPropertyHandler( 1053 uint32_t index, 1054 const AccessorInfo& info) { 1055 ApiTestFuzzer::Fuzz(); 1056 CHECK(info.This()->Equals(bottom)); 1057 return v8::Handle<Value>(); 1058 } 1059 1060 static v8::Handle<Value> CheckThisNamedPropertyHandler( 1061 Local<String> name, 1062 const AccessorInfo& info) { 1063 ApiTestFuzzer::Fuzz(); 1064 CHECK(info.This()->Equals(bottom)); 1065 return v8::Handle<Value>(); 1066 } 1067 1068 1069 v8::Handle<Value> CheckThisIndexedPropertySetter(uint32_t index, 1070 Local<Value> value, 1071 const AccessorInfo& info) { 1072 ApiTestFuzzer::Fuzz(); 1073 CHECK(info.This()->Equals(bottom)); 1074 return v8::Handle<Value>(); 1075 } 1076 1077 1078 v8::Handle<Value> CheckThisNamedPropertySetter(Local<String> property, 1079 Local<Value> value, 1080 const AccessorInfo& info) { 1081 ApiTestFuzzer::Fuzz(); 1082 CHECK(info.This()->Equals(bottom)); 1083 return v8::Handle<Value>(); 1084 } 1085 1086 v8::Handle<v8::Boolean> CheckThisIndexedPropertyQuery( 1087 uint32_t index, 1088 const AccessorInfo& info) { 1089 ApiTestFuzzer::Fuzz(); 1090 CHECK(info.This()->Equals(bottom)); 1091 return v8::Handle<v8::Boolean>(); 1092 } 1093 1094 1095 v8::Handle<v8::Boolean> CheckThisNamedPropertyQuery(Local<String> property, 1096 const AccessorInfo& info) { 1097 ApiTestFuzzer::Fuzz(); 1098 CHECK(info.This()->Equals(bottom)); 1099 return v8::Handle<v8::Boolean>(); 1100 } 1101 1102 1103 v8::Handle<v8::Boolean> CheckThisIndexedPropertyDeleter( 1104 uint32_t index, 1105 const AccessorInfo& info) { 1106 ApiTestFuzzer::Fuzz(); 1107 CHECK(info.This()->Equals(bottom)); 1108 return v8::Handle<v8::Boolean>(); 1109 } 1110 1111 1112 v8::Handle<v8::Boolean> CheckThisNamedPropertyDeleter( 1113 Local<String> property, 1114 const AccessorInfo& info) { 1115 ApiTestFuzzer::Fuzz(); 1116 CHECK(info.This()->Equals(bottom)); 1117 return v8::Handle<v8::Boolean>(); 1118 } 1119 1120 1121 v8::Handle<v8::Array> CheckThisIndexedPropertyEnumerator( 1122 const AccessorInfo& info) { 1123 ApiTestFuzzer::Fuzz(); 1124 CHECK(info.This()->Equals(bottom)); 1125 return v8::Handle<v8::Array>(); 1126 } 1127 1128 1129 v8::Handle<v8::Array> CheckThisNamedPropertyEnumerator( 1130 const AccessorInfo& info) { 1131 ApiTestFuzzer::Fuzz(); 1132 CHECK(info.This()->Equals(bottom)); 1133 return v8::Handle<v8::Array>(); 1134 } 1135 1136 1137 THREADED_TEST(PropertyHandlerInPrototype) { 1138 v8::HandleScope scope; 1139 LocalContext env; 1140 1141 // Set up a prototype chain with three interceptors. 1142 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1143 templ->InstanceTemplate()->SetIndexedPropertyHandler( 1144 CheckThisIndexedPropertyHandler, 1145 CheckThisIndexedPropertySetter, 1146 CheckThisIndexedPropertyQuery, 1147 CheckThisIndexedPropertyDeleter, 1148 CheckThisIndexedPropertyEnumerator); 1149 1150 templ->InstanceTemplate()->SetNamedPropertyHandler( 1151 CheckThisNamedPropertyHandler, 1152 CheckThisNamedPropertySetter, 1153 CheckThisNamedPropertyQuery, 1154 CheckThisNamedPropertyDeleter, 1155 CheckThisNamedPropertyEnumerator); 1156 1157 bottom = templ->GetFunction()->NewInstance(); 1158 Local<v8::Object> top = templ->GetFunction()->NewInstance(); 1159 Local<v8::Object> middle = templ->GetFunction()->NewInstance(); 1160 1161 bottom->Set(v8_str("__proto__"), middle); 1162 middle->Set(v8_str("__proto__"), top); 1163 env->Global()->Set(v8_str("obj"), bottom); 1164 1165 // Indexed and named get. 1166 Script::Compile(v8_str("obj[0]"))->Run(); 1167 Script::Compile(v8_str("obj.x"))->Run(); 1168 1169 // Indexed and named set. 1170 Script::Compile(v8_str("obj[1] = 42"))->Run(); 1171 Script::Compile(v8_str("obj.y = 42"))->Run(); 1172 1173 // Indexed and named query. 1174 Script::Compile(v8_str("0 in obj"))->Run(); 1175 Script::Compile(v8_str("'x' in obj"))->Run(); 1176 1177 // Indexed and named deleter. 1178 Script::Compile(v8_str("delete obj[0]"))->Run(); 1179 Script::Compile(v8_str("delete obj.x"))->Run(); 1180 1181 // Enumerators. 1182 Script::Compile(v8_str("for (var p in obj) ;"))->Run(); 1183 } 1184 1185 1186 static v8::Handle<Value> PrePropertyHandlerGet(Local<String> key, 1187 const AccessorInfo& info) { 1188 ApiTestFuzzer::Fuzz(); 1189 if (v8_str("pre")->Equals(key)) { 1190 return v8_str("PrePropertyHandler: pre"); 1191 } 1192 return v8::Handle<String>(); 1193 } 1194 1195 1196 static v8::Handle<v8::Boolean> PrePropertyHandlerHas(Local<String> key, 1197 const AccessorInfo&) { 1198 if (v8_str("pre")->Equals(key)) { 1199 return v8::True(); 1200 } 1201 1202 return v8::Handle<v8::Boolean>(); // do not intercept the call 1203 } 1204 1205 1206 THREADED_TEST(PrePropertyHandler) { 1207 v8::HandleScope scope; 1208 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(); 1209 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet, 1210 0, 1211 PrePropertyHandlerHas); 1212 LocalContext env(NULL, desc->InstanceTemplate()); 1213 Script::Compile(v8_str( 1214 "var pre = 'Object: pre'; var on = 'Object: on';"))->Run(); 1215 v8::Handle<Value> result_pre = Script::Compile(v8_str("pre"))->Run(); 1216 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre); 1217 v8::Handle<Value> result_on = Script::Compile(v8_str("on"))->Run(); 1218 CHECK_EQ(v8_str("Object: on"), result_on); 1219 v8::Handle<Value> result_post = Script::Compile(v8_str("post"))->Run(); 1220 CHECK(result_post.IsEmpty()); 1221 } 1222 1223 1224 THREADED_TEST(UndefinedIsNotEnumerable) { 1225 v8::HandleScope scope; 1226 LocalContext env; 1227 v8::Handle<Value> result = Script::Compile(v8_str( 1228 "this.propertyIsEnumerable(undefined)"))->Run(); 1229 CHECK(result->IsFalse()); 1230 } 1231 1232 1233 v8::Handle<Script> call_recursively_script; 1234 static const int kTargetRecursionDepth = 200; // near maximum 1235 1236 1237 static v8::Handle<Value> CallScriptRecursivelyCall(const v8::Arguments& args) { 1238 ApiTestFuzzer::Fuzz(); 1239 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1240 if (depth == kTargetRecursionDepth) return v8::Undefined(); 1241 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1242 return call_recursively_script->Run(); 1243 } 1244 1245 1246 static v8::Handle<Value> CallFunctionRecursivelyCall( 1247 const v8::Arguments& args) { 1248 ApiTestFuzzer::Fuzz(); 1249 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 1250 if (depth == kTargetRecursionDepth) { 1251 printf("[depth = %d]\n", depth); 1252 return v8::Undefined(); 1253 } 1254 args.This()->Set(v8_str("depth"), v8::Integer::New(depth + 1)); 1255 v8::Handle<Value> function = 1256 args.This()->Get(v8_str("callFunctionRecursively")); 1257 return v8::Handle<Function>::Cast(function)->Call(args.This(), 0, NULL); 1258 } 1259 1260 1261 THREADED_TEST(DeepCrossLanguageRecursion) { 1262 v8::HandleScope scope; 1263 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 1264 global->Set(v8_str("callScriptRecursively"), 1265 v8::FunctionTemplate::New(CallScriptRecursivelyCall)); 1266 global->Set(v8_str("callFunctionRecursively"), 1267 v8::FunctionTemplate::New(CallFunctionRecursivelyCall)); 1268 LocalContext env(NULL, global); 1269 1270 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1271 call_recursively_script = v8_compile("callScriptRecursively()"); 1272 v8::Handle<Value> result = call_recursively_script->Run(); 1273 call_recursively_script = v8::Handle<Script>(); 1274 1275 env->Global()->Set(v8_str("depth"), v8::Integer::New(0)); 1276 Script::Compile(v8_str("callFunctionRecursively()"))->Run(); 1277 } 1278 1279 1280 static v8::Handle<Value> 1281 ThrowingPropertyHandlerGet(Local<String> key, const AccessorInfo&) { 1282 ApiTestFuzzer::Fuzz(); 1283 return v8::ThrowException(key); 1284 } 1285 1286 1287 static v8::Handle<Value> ThrowingPropertyHandlerSet(Local<String> key, 1288 Local<Value>, 1289 const AccessorInfo&) { 1290 v8::ThrowException(key); 1291 return v8::Undefined(); // not the same as v8::Handle<v8::Value>() 1292 } 1293 1294 1295 THREADED_TEST(CallbackExceptionRegression) { 1296 v8::HandleScope scope; 1297 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 1298 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet, 1299 ThrowingPropertyHandlerSet); 1300 LocalContext env; 1301 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 1302 v8::Handle<Value> otto = Script::Compile(v8_str( 1303 "try { with (obj) { otto; } } catch (e) { e; }"))->Run(); 1304 CHECK_EQ(v8_str("otto"), otto); 1305 v8::Handle<Value> netto = Script::Compile(v8_str( 1306 "try { with (obj) { netto = 4; } } catch (e) { e; }"))->Run(); 1307 CHECK_EQ(v8_str("netto"), netto); 1308 } 1309 1310 1311 THREADED_TEST(FunctionPrototype) { 1312 v8::HandleScope scope; 1313 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New(); 1314 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321)); 1315 LocalContext env; 1316 env->Global()->Set(v8_str("Foo"), Foo->GetFunction()); 1317 Local<Script> script = Script::Compile(v8_str("Foo.prototype.plak")); 1318 CHECK_EQ(script->Run()->Int32Value(), 321); 1319 } 1320 1321 1322 THREADED_TEST(InternalFields) { 1323 v8::HandleScope scope; 1324 LocalContext env; 1325 1326 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1327 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1328 instance_templ->SetInternalFieldCount(1); 1329 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1330 CHECK_EQ(1, obj->InternalFieldCount()); 1331 CHECK(obj->GetInternalField(0)->IsUndefined()); 1332 obj->SetInternalField(0, v8_num(17)); 1333 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value()); 1334 } 1335 1336 1337 THREADED_TEST(InternalFieldsNativePointers) { 1338 v8::HandleScope scope; 1339 LocalContext env; 1340 1341 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1342 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1343 instance_templ->SetInternalFieldCount(1); 1344 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1345 CHECK_EQ(1, obj->InternalFieldCount()); 1346 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1347 1348 char* data = new char[100]; 1349 1350 void* aligned = data; 1351 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1); 1352 void* unaligned = data + 1; 1353 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1); 1354 1355 // Check reading and writing aligned pointers. 1356 obj->SetPointerInInternalField(0, aligned); 1357 i::Heap::CollectAllGarbage(false); 1358 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1359 1360 // Check reading and writing unaligned pointers. 1361 obj->SetPointerInInternalField(0, unaligned); 1362 i::Heap::CollectAllGarbage(false); 1363 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1364 1365 delete[] data; 1366 } 1367 1368 1369 THREADED_TEST(InternalFieldsNativePointersAndExternal) { 1370 v8::HandleScope scope; 1371 LocalContext env; 1372 1373 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 1374 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 1375 instance_templ->SetInternalFieldCount(1); 1376 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 1377 CHECK_EQ(1, obj->InternalFieldCount()); 1378 CHECK(obj->GetPointerFromInternalField(0) == NULL); 1379 1380 char* data = new char[100]; 1381 1382 void* aligned = data; 1383 CHECK_EQ(0, reinterpret_cast<uintptr_t>(aligned) & 0x1); 1384 void* unaligned = data + 1; 1385 CHECK_EQ(1, reinterpret_cast<uintptr_t>(unaligned) & 0x1); 1386 1387 obj->SetPointerInInternalField(0, aligned); 1388 i::Heap::CollectAllGarbage(false); 1389 CHECK_EQ(aligned, v8::External::Unwrap(obj->GetInternalField(0))); 1390 1391 obj->SetPointerInInternalField(0, unaligned); 1392 i::Heap::CollectAllGarbage(false); 1393 CHECK_EQ(unaligned, v8::External::Unwrap(obj->GetInternalField(0))); 1394 1395 obj->SetInternalField(0, v8::External::Wrap(aligned)); 1396 i::Heap::CollectAllGarbage(false); 1397 CHECK_EQ(aligned, obj->GetPointerFromInternalField(0)); 1398 1399 obj->SetInternalField(0, v8::External::Wrap(unaligned)); 1400 i::Heap::CollectAllGarbage(false); 1401 CHECK_EQ(unaligned, obj->GetPointerFromInternalField(0)); 1402 1403 delete[] data; 1404 } 1405 1406 1407 THREADED_TEST(IdentityHash) { 1408 v8::HandleScope scope; 1409 LocalContext env; 1410 1411 // Ensure that the test starts with an fresh heap to test whether the hash 1412 // code is based on the address. 1413 i::Heap::CollectAllGarbage(false); 1414 Local<v8::Object> obj = v8::Object::New(); 1415 int hash = obj->GetIdentityHash(); 1416 int hash1 = obj->GetIdentityHash(); 1417 CHECK_EQ(hash, hash1); 1418 int hash2 = v8::Object::New()->GetIdentityHash(); 1419 // Since the identity hash is essentially a random number two consecutive 1420 // objects should not be assigned the same hash code. If the test below fails 1421 // the random number generator should be evaluated. 1422 CHECK_NE(hash, hash2); 1423 i::Heap::CollectAllGarbage(false); 1424 int hash3 = v8::Object::New()->GetIdentityHash(); 1425 // Make sure that the identity hash is not based on the initial address of 1426 // the object alone. If the test below fails the random number generator 1427 // should be evaluated. 1428 CHECK_NE(hash, hash3); 1429 int hash4 = obj->GetIdentityHash(); 1430 CHECK_EQ(hash, hash4); 1431 } 1432 1433 1434 THREADED_TEST(HiddenProperties) { 1435 v8::HandleScope scope; 1436 LocalContext env; 1437 1438 v8::Local<v8::Object> obj = v8::Object::New(); 1439 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 1440 v8::Local<v8::String> empty = v8_str(""); 1441 v8::Local<v8::String> prop_name = v8_str("prop_name"); 1442 1443 i::Heap::CollectAllGarbage(false); 1444 1445 // Make sure delete of a non-existent hidden value works 1446 CHECK(obj->DeleteHiddenValue(key)); 1447 1448 CHECK(obj->SetHiddenValue(key, v8::Integer::New(1503))); 1449 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value()); 1450 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2002))); 1451 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1452 1453 i::Heap::CollectAllGarbage(false); 1454 1455 // Make sure we do not find the hidden property. 1456 CHECK(!obj->Has(empty)); 1457 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1458 CHECK(obj->Get(empty)->IsUndefined()); 1459 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1460 CHECK(obj->Set(empty, v8::Integer::New(2003))); 1461 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1462 CHECK_EQ(2003, obj->Get(empty)->Int32Value()); 1463 1464 i::Heap::CollectAllGarbage(false); 1465 1466 // Add another property and delete it afterwards to force the object in 1467 // slow case. 1468 CHECK(obj->Set(prop_name, v8::Integer::New(2008))); 1469 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1470 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value()); 1471 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1472 CHECK(obj->Delete(prop_name)); 1473 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 1474 1475 i::Heap::CollectAllGarbage(false); 1476 1477 CHECK(obj->DeleteHiddenValue(key)); 1478 CHECK(obj->GetHiddenValue(key).IsEmpty()); 1479 } 1480 1481 1482 static bool interceptor_for_hidden_properties_called; 1483 static v8::Handle<Value> InterceptorForHiddenProperties( 1484 Local<String> name, const AccessorInfo& info) { 1485 interceptor_for_hidden_properties_called = true; 1486 return v8::Handle<Value>(); 1487 } 1488 1489 1490 THREADED_TEST(HiddenPropertiesWithInterceptors) { 1491 v8::HandleScope scope; 1492 LocalContext context; 1493 1494 interceptor_for_hidden_properties_called = false; 1495 1496 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 1497 1498 // Associate an interceptor with an object and start setting hidden values. 1499 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 1500 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 1501 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties); 1502 Local<v8::Function> function = fun_templ->GetFunction(); 1503 Local<v8::Object> obj = function->NewInstance(); 1504 CHECK(obj->SetHiddenValue(key, v8::Integer::New(2302))); 1505 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value()); 1506 CHECK(!interceptor_for_hidden_properties_called); 1507 } 1508 1509 1510 THREADED_TEST(External) { 1511 v8::HandleScope scope; 1512 int x = 3; 1513 Local<v8::External> ext = v8::External::New(&x); 1514 LocalContext env; 1515 env->Global()->Set(v8_str("ext"), ext); 1516 Local<Value> reext_obj = Script::Compile(v8_str("this.ext"))->Run(); 1517 v8::Handle<v8::External> reext = v8::Handle<v8::External>::Cast(reext_obj); 1518 int* ptr = static_cast<int*>(reext->Value()); 1519 CHECK_EQ(x, 3); 1520 *ptr = 10; 1521 CHECK_EQ(x, 10); 1522 1523 // Make sure unaligned pointers are wrapped properly. 1524 char* data = i::StrDup("0123456789"); 1525 Local<v8::Value> zero = v8::External::Wrap(&data[0]); 1526 Local<v8::Value> one = v8::External::Wrap(&data[1]); 1527 Local<v8::Value> two = v8::External::Wrap(&data[2]); 1528 Local<v8::Value> three = v8::External::Wrap(&data[3]); 1529 1530 char* char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(zero)); 1531 CHECK_EQ('0', *char_ptr); 1532 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(one)); 1533 CHECK_EQ('1', *char_ptr); 1534 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(two)); 1535 CHECK_EQ('2', *char_ptr); 1536 char_ptr = reinterpret_cast<char*>(v8::External::Unwrap(three)); 1537 CHECK_EQ('3', *char_ptr); 1538 i::DeleteArray(data); 1539 } 1540 1541 1542 THREADED_TEST(GlobalHandle) { 1543 v8::Persistent<String> global; 1544 { 1545 v8::HandleScope scope; 1546 Local<String> str = v8_str("str"); 1547 global = v8::Persistent<String>::New(str); 1548 } 1549 CHECK_EQ(global->Length(), 3); 1550 global.Dispose(); 1551 } 1552 1553 1554 THREADED_TEST(ScriptException) { 1555 v8::HandleScope scope; 1556 LocalContext env; 1557 Local<Script> script = Script::Compile(v8_str("throw 'panama!';")); 1558 v8::TryCatch try_catch; 1559 Local<Value> result = script->Run(); 1560 CHECK(result.IsEmpty()); 1561 CHECK(try_catch.HasCaught()); 1562 String::AsciiValue exception_value(try_catch.Exception()); 1563 CHECK_EQ(*exception_value, "panama!"); 1564 } 1565 1566 1567 bool message_received; 1568 1569 1570 static void check_message(v8::Handle<v8::Message> message, 1571 v8::Handle<Value> data) { 1572 CHECK_EQ(5.76, data->NumberValue()); 1573 CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue()); 1574 CHECK_EQ(7.56, message->GetScriptData()->NumberValue()); 1575 message_received = true; 1576 } 1577 1578 1579 THREADED_TEST(MessageHandlerData) { 1580 message_received = false; 1581 v8::HandleScope scope; 1582 CHECK(!message_received); 1583 v8::V8::AddMessageListener(check_message, v8_num(5.76)); 1584 LocalContext context; 1585 v8::ScriptOrigin origin = 1586 v8::ScriptOrigin(v8_str("6.75")); 1587 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"), 1588 &origin); 1589 script->SetData(v8_str("7.56")); 1590 script->Run(); 1591 CHECK(message_received); 1592 // clear out the message listener 1593 v8::V8::RemoveMessageListeners(check_message); 1594 } 1595 1596 1597 THREADED_TEST(GetSetProperty) { 1598 v8::HandleScope scope; 1599 LocalContext context; 1600 context->Global()->Set(v8_str("foo"), v8_num(14)); 1601 context->Global()->Set(v8_str("12"), v8_num(92)); 1602 context->Global()->Set(v8::Integer::New(16), v8_num(32)); 1603 context->Global()->Set(v8_num(13), v8_num(56)); 1604 Local<Value> foo = Script::Compile(v8_str("this.foo"))->Run(); 1605 CHECK_EQ(14, foo->Int32Value()); 1606 Local<Value> twelve = Script::Compile(v8_str("this[12]"))->Run(); 1607 CHECK_EQ(92, twelve->Int32Value()); 1608 Local<Value> sixteen = Script::Compile(v8_str("this[16]"))->Run(); 1609 CHECK_EQ(32, sixteen->Int32Value()); 1610 Local<Value> thirteen = Script::Compile(v8_str("this[13]"))->Run(); 1611 CHECK_EQ(56, thirteen->Int32Value()); 1612 CHECK_EQ(92, context->Global()->Get(v8::Integer::New(12))->Int32Value()); 1613 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value()); 1614 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value()); 1615 CHECK_EQ(32, context->Global()->Get(v8::Integer::New(16))->Int32Value()); 1616 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value()); 1617 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value()); 1618 CHECK_EQ(56, context->Global()->Get(v8::Integer::New(13))->Int32Value()); 1619 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value()); 1620 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value()); 1621 } 1622 1623 1624 THREADED_TEST(PropertyAttributes) { 1625 v8::HandleScope scope; 1626 LocalContext context; 1627 // read-only 1628 Local<String> prop = v8_str("read_only"); 1629 context->Global()->Set(prop, v8_num(7), v8::ReadOnly); 1630 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1631 Script::Compile(v8_str("read_only = 9"))->Run(); 1632 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1633 context->Global()->Set(prop, v8_num(10)); 1634 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 1635 // dont-delete 1636 prop = v8_str("dont_delete"); 1637 context->Global()->Set(prop, v8_num(13), v8::DontDelete); 1638 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 1639 Script::Compile(v8_str("delete dont_delete"))->Run(); 1640 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 1641 } 1642 1643 1644 THREADED_TEST(Array) { 1645 v8::HandleScope scope; 1646 LocalContext context; 1647 Local<v8::Array> array = v8::Array::New(); 1648 CHECK_EQ(0, array->Length()); 1649 CHECK(array->Get(v8::Integer::New(0))->IsUndefined()); 1650 CHECK(!array->Has(0)); 1651 CHECK(array->Get(v8::Integer::New(100))->IsUndefined()); 1652 CHECK(!array->Has(100)); 1653 array->Set(v8::Integer::New(2), v8_num(7)); 1654 CHECK_EQ(3, array->Length()); 1655 CHECK(!array->Has(0)); 1656 CHECK(!array->Has(1)); 1657 CHECK(array->Has(2)); 1658 CHECK_EQ(7, array->Get(v8::Integer::New(2))->Int32Value()); 1659 Local<Value> obj = Script::Compile(v8_str("[1, 2, 3]"))->Run(); 1660 Local<v8::Array> arr = Local<v8::Array>::Cast(obj); 1661 CHECK_EQ(3, arr->Length()); 1662 CHECK_EQ(1, arr->Get(v8::Integer::New(0))->Int32Value()); 1663 CHECK_EQ(2, arr->Get(v8::Integer::New(1))->Int32Value()); 1664 CHECK_EQ(3, arr->Get(v8::Integer::New(2))->Int32Value()); 1665 } 1666 1667 1668 v8::Handle<Value> HandleF(const v8::Arguments& args) { 1669 v8::HandleScope scope; 1670 ApiTestFuzzer::Fuzz(); 1671 Local<v8::Array> result = v8::Array::New(args.Length()); 1672 for (int i = 0; i < args.Length(); i++) 1673 result->Set(v8::Integer::New(i), args[i]); 1674 return scope.Close(result); 1675 } 1676 1677 1678 THREADED_TEST(Vector) { 1679 v8::HandleScope scope; 1680 Local<ObjectTemplate> global = ObjectTemplate::New(); 1681 global->Set(v8_str("f"), v8::FunctionTemplate::New(HandleF)); 1682 LocalContext context(0, global); 1683 1684 const char* fun = "f()"; 1685 Local<v8::Array> a0 = 1686 Local<v8::Array>::Cast(Script::Compile(String::New(fun))->Run()); 1687 CHECK_EQ(0, a0->Length()); 1688 1689 const char* fun2 = "f(11)"; 1690 Local<v8::Array> a1 = 1691 Local<v8::Array>::Cast(Script::Compile(String::New(fun2))->Run()); 1692 CHECK_EQ(1, a1->Length()); 1693 CHECK_EQ(11, a1->Get(v8::Integer::New(0))->Int32Value()); 1694 1695 const char* fun3 = "f(12, 13)"; 1696 Local<v8::Array> a2 = 1697 Local<v8::Array>::Cast(Script::Compile(String::New(fun3))->Run()); 1698 CHECK_EQ(2, a2->Length()); 1699 CHECK_EQ(12, a2->Get(v8::Integer::New(0))->Int32Value()); 1700 CHECK_EQ(13, a2->Get(v8::Integer::New(1))->Int32Value()); 1701 1702 const char* fun4 = "f(14, 15, 16)"; 1703 Local<v8::Array> a3 = 1704 Local<v8::Array>::Cast(Script::Compile(String::New(fun4))->Run()); 1705 CHECK_EQ(3, a3->Length()); 1706 CHECK_EQ(14, a3->Get(v8::Integer::New(0))->Int32Value()); 1707 CHECK_EQ(15, a3->Get(v8::Integer::New(1))->Int32Value()); 1708 CHECK_EQ(16, a3->Get(v8::Integer::New(2))->Int32Value()); 1709 1710 const char* fun5 = "f(17, 18, 19, 20)"; 1711 Local<v8::Array> a4 = 1712 Local<v8::Array>::Cast(Script::Compile(String::New(fun5))->Run()); 1713 CHECK_EQ(4, a4->Length()); 1714 CHECK_EQ(17, a4->Get(v8::Integer::New(0))->Int32Value()); 1715 CHECK_EQ(18, a4->Get(v8::Integer::New(1))->Int32Value()); 1716 CHECK_EQ(19, a4->Get(v8::Integer::New(2))->Int32Value()); 1717 CHECK_EQ(20, a4->Get(v8::Integer::New(3))->Int32Value()); 1718 } 1719 1720 1721 THREADED_TEST(FunctionCall) { 1722 v8::HandleScope scope; 1723 LocalContext context; 1724 CompileRun( 1725 "function Foo() {" 1726 " var result = [];" 1727 " for (var i = 0; i < arguments.length; i++) {" 1728 " result.push(arguments[i]);" 1729 " }" 1730 " return result;" 1731 "}"); 1732 Local<Function> Foo = 1733 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 1734 1735 v8::Handle<Value>* args0 = NULL; 1736 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0)); 1737 CHECK_EQ(0, a0->Length()); 1738 1739 v8::Handle<Value> args1[] = { v8_num(1.1) }; 1740 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1)); 1741 CHECK_EQ(1, a1->Length()); 1742 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 1743 1744 v8::Handle<Value> args2[] = { v8_num(2.2), 1745 v8_num(3.3) }; 1746 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2)); 1747 CHECK_EQ(2, a2->Length()); 1748 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 1749 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 1750 1751 v8::Handle<Value> args3[] = { v8_num(4.4), 1752 v8_num(5.5), 1753 v8_num(6.6) }; 1754 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3)); 1755 CHECK_EQ(3, a3->Length()); 1756 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 1757 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 1758 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 1759 1760 v8::Handle<Value> args4[] = { v8_num(7.7), 1761 v8_num(8.8), 1762 v8_num(9.9), 1763 v8_num(10.11) }; 1764 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4)); 1765 CHECK_EQ(4, a4->Length()); 1766 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 1767 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 1768 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 1769 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 1770 } 1771 1772 1773 static const char* js_code_causing_out_of_memory = 1774 "var a = new Array(); while(true) a.push(a);"; 1775 1776 1777 // These tests run for a long time and prevent us from running tests 1778 // that come after them so they cannot run in parallel. 1779 TEST(OutOfMemory) { 1780 // It's not possible to read a snapshot into a heap with different dimensions. 1781 if (v8::internal::Snapshot::IsEnabled()) return; 1782 // Set heap limits. 1783 static const int K = 1024; 1784 v8::ResourceConstraints constraints; 1785 constraints.set_max_young_space_size(256 * K); 1786 constraints.set_max_old_space_size(4 * K * K); 1787 v8::SetResourceConstraints(&constraints); 1788 1789 // Execute a script that causes out of memory. 1790 v8::HandleScope scope; 1791 LocalContext context; 1792 v8::V8::IgnoreOutOfMemoryException(); 1793 Local<Script> script = 1794 Script::Compile(String::New(js_code_causing_out_of_memory)); 1795 Local<Value> result = script->Run(); 1796 1797 // Check for out of memory state. 1798 CHECK(result.IsEmpty()); 1799 CHECK(context->HasOutOfMemoryException()); 1800 } 1801 1802 1803 v8::Handle<Value> ProvokeOutOfMemory(const v8::Arguments& args) { 1804 ApiTestFuzzer::Fuzz(); 1805 1806 v8::HandleScope scope; 1807 LocalContext context; 1808 Local<Script> script = 1809 Script::Compile(String::New(js_code_causing_out_of_memory)); 1810 Local<Value> result = script->Run(); 1811 1812 // Check for out of memory state. 1813 CHECK(result.IsEmpty()); 1814 CHECK(context->HasOutOfMemoryException()); 1815 1816 return result; 1817 } 1818 1819 1820 TEST(OutOfMemoryNested) { 1821 // It's not possible to read a snapshot into a heap with different dimensions. 1822 if (v8::internal::Snapshot::IsEnabled()) return; 1823 // Set heap limits. 1824 static const int K = 1024; 1825 v8::ResourceConstraints constraints; 1826 constraints.set_max_young_space_size(256 * K); 1827 constraints.set_max_old_space_size(4 * K * K); 1828 v8::SetResourceConstraints(&constraints); 1829 1830 v8::HandleScope scope; 1831 Local<ObjectTemplate> templ = ObjectTemplate::New(); 1832 templ->Set(v8_str("ProvokeOutOfMemory"), 1833 v8::FunctionTemplate::New(ProvokeOutOfMemory)); 1834 LocalContext context(0, templ); 1835 v8::V8::IgnoreOutOfMemoryException(); 1836 Local<Value> result = CompileRun( 1837 "var thrown = false;" 1838 "try {" 1839 " ProvokeOutOfMemory();" 1840 "} catch (e) {" 1841 " thrown = true;" 1842 "}"); 1843 // Check for out of memory state. 1844 CHECK(result.IsEmpty()); 1845 CHECK(context->HasOutOfMemoryException()); 1846 } 1847 1848 1849 TEST(HugeConsStringOutOfMemory) { 1850 // It's not possible to read a snapshot into a heap with different dimensions. 1851 if (v8::internal::Snapshot::IsEnabled()) return; 1852 v8::HandleScope scope; 1853 LocalContext context; 1854 // Set heap limits. 1855 static const int K = 1024; 1856 v8::ResourceConstraints constraints; 1857 constraints.set_max_young_space_size(256 * K); 1858 constraints.set_max_old_space_size(2 * K * K); 1859 v8::SetResourceConstraints(&constraints); 1860 1861 // Execute a script that causes out of memory. 1862 v8::V8::IgnoreOutOfMemoryException(); 1863 1864 // Build huge string. This should fail with out of memory exception. 1865 Local<Value> result = CompileRun( 1866 "var str = Array.prototype.join.call({length: 513}, \"A\").toUpperCase();" 1867 "for (var i = 0; i < 22; i++) { str = str + str; }"); 1868 1869 // Check for out of memory state. 1870 CHECK(result.IsEmpty()); 1871 CHECK(context->HasOutOfMemoryException()); 1872 } 1873 1874 1875 THREADED_TEST(ConstructCall) { 1876 v8::HandleScope scope; 1877 LocalContext context; 1878 CompileRun( 1879 "function Foo() {" 1880 " var result = [];" 1881 " for (var i = 0; i < arguments.length; i++) {" 1882 " result.push(arguments[i]);" 1883 " }" 1884 " return result;" 1885 "}"); 1886 Local<Function> Foo = 1887 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 1888 1889 v8::Handle<Value>* args0 = NULL; 1890 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0)); 1891 CHECK_EQ(0, a0->Length()); 1892 1893 v8::Handle<Value> args1[] = { v8_num(1.1) }; 1894 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1)); 1895 CHECK_EQ(1, a1->Length()); 1896 CHECK_EQ(1.1, a1->Get(v8::Integer::New(0))->NumberValue()); 1897 1898 v8::Handle<Value> args2[] = { v8_num(2.2), 1899 v8_num(3.3) }; 1900 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2)); 1901 CHECK_EQ(2, a2->Length()); 1902 CHECK_EQ(2.2, a2->Get(v8::Integer::New(0))->NumberValue()); 1903 CHECK_EQ(3.3, a2->Get(v8::Integer::New(1))->NumberValue()); 1904 1905 v8::Handle<Value> args3[] = { v8_num(4.4), 1906 v8_num(5.5), 1907 v8_num(6.6) }; 1908 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3)); 1909 CHECK_EQ(3, a3->Length()); 1910 CHECK_EQ(4.4, a3->Get(v8::Integer::New(0))->NumberValue()); 1911 CHECK_EQ(5.5, a3->Get(v8::Integer::New(1))->NumberValue()); 1912 CHECK_EQ(6.6, a3->Get(v8::Integer::New(2))->NumberValue()); 1913 1914 v8::Handle<Value> args4[] = { v8_num(7.7), 1915 v8_num(8.8), 1916 v8_num(9.9), 1917 v8_num(10.11) }; 1918 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4)); 1919 CHECK_EQ(4, a4->Length()); 1920 CHECK_EQ(7.7, a4->Get(v8::Integer::New(0))->NumberValue()); 1921 CHECK_EQ(8.8, a4->Get(v8::Integer::New(1))->NumberValue()); 1922 CHECK_EQ(9.9, a4->Get(v8::Integer::New(2))->NumberValue()); 1923 CHECK_EQ(10.11, a4->Get(v8::Integer::New(3))->NumberValue()); 1924 } 1925 1926 1927 static void CheckUncle(v8::TryCatch* try_catch) { 1928 CHECK(try_catch->HasCaught()); 1929 String::AsciiValue str_value(try_catch->Exception()); 1930 CHECK_EQ(*str_value, "uncle?"); 1931 try_catch->Reset(); 1932 } 1933 1934 1935 THREADED_TEST(ConversionException) { 1936 v8::HandleScope scope; 1937 LocalContext env; 1938 CompileRun( 1939 "function TestClass() { };" 1940 "TestClass.prototype.toString = function () { throw 'uncle?'; };" 1941 "var obj = new TestClass();"); 1942 Local<Value> obj = env->Global()->Get(v8_str("obj")); 1943 1944 v8::TryCatch try_catch; 1945 1946 Local<Value> to_string_result = obj->ToString(); 1947 CHECK(to_string_result.IsEmpty()); 1948 CheckUncle(&try_catch); 1949 1950 Local<Value> to_number_result = obj->ToNumber(); 1951 CHECK(to_number_result.IsEmpty()); 1952 CheckUncle(&try_catch); 1953 1954 Local<Value> to_integer_result = obj->ToInteger(); 1955 CHECK(to_integer_result.IsEmpty()); 1956 CheckUncle(&try_catch); 1957 1958 Local<Value> to_uint32_result = obj->ToUint32(); 1959 CHECK(to_uint32_result.IsEmpty()); 1960 CheckUncle(&try_catch); 1961 1962 Local<Value> to_int32_result = obj->ToInt32(); 1963 CHECK(to_int32_result.IsEmpty()); 1964 CheckUncle(&try_catch); 1965 1966 Local<Value> to_object_result = v8::Undefined()->ToObject(); 1967 CHECK(to_object_result.IsEmpty()); 1968 CHECK(try_catch.HasCaught()); 1969 try_catch.Reset(); 1970 1971 int32_t int32_value = obj->Int32Value(); 1972 CHECK_EQ(0, int32_value); 1973 CheckUncle(&try_catch); 1974 1975 uint32_t uint32_value = obj->Uint32Value(); 1976 CHECK_EQ(0, uint32_value); 1977 CheckUncle(&try_catch); 1978 1979 double number_value = obj->NumberValue(); 1980 CHECK_NE(0, IsNaN(number_value)); 1981 CheckUncle(&try_catch); 1982 1983 int64_t integer_value = obj->IntegerValue(); 1984 CHECK_EQ(0.0, static_cast<double>(integer_value)); 1985 CheckUncle(&try_catch); 1986 } 1987 1988 1989 v8::Handle<Value> ThrowFromC(const v8::Arguments& args) { 1990 ApiTestFuzzer::Fuzz(); 1991 return v8::ThrowException(v8_str("konto")); 1992 } 1993 1994 1995 v8::Handle<Value> CCatcher(const v8::Arguments& args) { 1996 if (args.Length() < 1) return v8::Boolean::New(false); 1997 v8::HandleScope scope; 1998 v8::TryCatch try_catch; 1999 Local<Value> result = v8::Script::Compile(args[0]->ToString())->Run(); 2000 CHECK(!try_catch.HasCaught() || result.IsEmpty()); 2001 return v8::Boolean::New(try_catch.HasCaught()); 2002 } 2003 2004 2005 THREADED_TEST(APICatch) { 2006 v8::HandleScope scope; 2007 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2008 templ->Set(v8_str("ThrowFromC"), 2009 v8::FunctionTemplate::New(ThrowFromC)); 2010 LocalContext context(0, templ); 2011 CompileRun( 2012 "var thrown = false;" 2013 "try {" 2014 " ThrowFromC();" 2015 "} catch (e) {" 2016 " thrown = true;" 2017 "}"); 2018 Local<Value> thrown = context->Global()->Get(v8_str("thrown")); 2019 CHECK(thrown->BooleanValue()); 2020 } 2021 2022 2023 THREADED_TEST(APIThrowTryCatch) { 2024 v8::HandleScope scope; 2025 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2026 templ->Set(v8_str("ThrowFromC"), 2027 v8::FunctionTemplate::New(ThrowFromC)); 2028 LocalContext context(0, templ); 2029 v8::TryCatch try_catch; 2030 CompileRun("ThrowFromC();"); 2031 CHECK(try_catch.HasCaught()); 2032 } 2033 2034 2035 // Test that a try-finally block doesn't shadow a try-catch block 2036 // when setting up an external handler. 2037 // 2038 // BUG(271): Some of the exception propagation does not work on the 2039 // ARM simulator because the simulator separates the C++ stack and the 2040 // JS stack. This test therefore fails on the simulator. The test is 2041 // not threaded to allow the threading tests to run on the simulator. 2042 TEST(TryCatchInTryFinally) { 2043 v8::HandleScope scope; 2044 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2045 templ->Set(v8_str("CCatcher"), 2046 v8::FunctionTemplate::New(CCatcher)); 2047 LocalContext context(0, templ); 2048 Local<Value> result = CompileRun("try {" 2049 " try {" 2050 " CCatcher('throw 7;');" 2051 " } finally {" 2052 " }" 2053 "} catch (e) {" 2054 "}"); 2055 CHECK(result->IsTrue()); 2056 } 2057 2058 2059 static void receive_message(v8::Handle<v8::Message> message, 2060 v8::Handle<v8::Value> data) { 2061 message->Get(); 2062 message_received = true; 2063 } 2064 2065 2066 TEST(APIThrowMessage) { 2067 message_received = false; 2068 v8::HandleScope scope; 2069 v8::V8::AddMessageListener(receive_message); 2070 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2071 templ->Set(v8_str("ThrowFromC"), 2072 v8::FunctionTemplate::New(ThrowFromC)); 2073 LocalContext context(0, templ); 2074 CompileRun("ThrowFromC();"); 2075 CHECK(message_received); 2076 v8::V8::RemoveMessageListeners(check_message); 2077 } 2078 2079 2080 TEST(APIThrowMessageAndVerboseTryCatch) { 2081 message_received = false; 2082 v8::HandleScope scope; 2083 v8::V8::AddMessageListener(receive_message); 2084 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2085 templ->Set(v8_str("ThrowFromC"), 2086 v8::FunctionTemplate::New(ThrowFromC)); 2087 LocalContext context(0, templ); 2088 v8::TryCatch try_catch; 2089 try_catch.SetVerbose(true); 2090 Local<Value> result = CompileRun("ThrowFromC();"); 2091 CHECK(try_catch.HasCaught()); 2092 CHECK(result.IsEmpty()); 2093 CHECK(message_received); 2094 v8::V8::RemoveMessageListeners(check_message); 2095 } 2096 2097 2098 THREADED_TEST(ExternalScriptException) { 2099 v8::HandleScope scope; 2100 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2101 templ->Set(v8_str("ThrowFromC"), 2102 v8::FunctionTemplate::New(ThrowFromC)); 2103 LocalContext context(0, templ); 2104 2105 v8::TryCatch try_catch; 2106 Local<Script> script 2107 = Script::Compile(v8_str("ThrowFromC(); throw 'panama';")); 2108 Local<Value> result = script->Run(); 2109 CHECK(result.IsEmpty()); 2110 CHECK(try_catch.HasCaught()); 2111 String::AsciiValue exception_value(try_catch.Exception()); 2112 CHECK_EQ("konto", *exception_value); 2113 } 2114 2115 2116 2117 v8::Handle<Value> CThrowCountDown(const v8::Arguments& args) { 2118 ApiTestFuzzer::Fuzz(); 2119 CHECK_EQ(4, args.Length()); 2120 int count = args[0]->Int32Value(); 2121 int cInterval = args[2]->Int32Value(); 2122 if (count == 0) { 2123 return v8::ThrowException(v8_str("FromC")); 2124 } else { 2125 Local<v8::Object> global = Context::GetCurrent()->Global(); 2126 Local<Value> fun = global->Get(v8_str("JSThrowCountDown")); 2127 v8::Handle<Value> argv[] = { v8_num(count - 1), 2128 args[1], 2129 args[2], 2130 args[3] }; 2131 if (count % cInterval == 0) { 2132 v8::TryCatch try_catch; 2133 Local<Value> result = 2134 v8::Handle<Function>::Cast(fun)->Call(global, 4, argv); 2135 int expected = args[3]->Int32Value(); 2136 if (try_catch.HasCaught()) { 2137 CHECK_EQ(expected, count); 2138 CHECK(result.IsEmpty()); 2139 CHECK(!i::Top::has_scheduled_exception()); 2140 } else { 2141 CHECK_NE(expected, count); 2142 } 2143 return result; 2144 } else { 2145 return v8::Handle<Function>::Cast(fun)->Call(global, 4, argv); 2146 } 2147 } 2148 } 2149 2150 2151 v8::Handle<Value> JSCheck(const v8::Arguments& args) { 2152 ApiTestFuzzer::Fuzz(); 2153 CHECK_EQ(3, args.Length()); 2154 bool equality = args[0]->BooleanValue(); 2155 int count = args[1]->Int32Value(); 2156 int expected = args[2]->Int32Value(); 2157 if (equality) { 2158 CHECK_EQ(count, expected); 2159 } else { 2160 CHECK_NE(count, expected); 2161 } 2162 return v8::Undefined(); 2163 } 2164 2165 2166 THREADED_TEST(EvalInTryFinally) { 2167 v8::HandleScope scope; 2168 LocalContext context; 2169 v8::TryCatch try_catch; 2170 CompileRun("(function() {" 2171 " try {" 2172 " eval('asldkf (*&^&*^');" 2173 " } finally {" 2174 " return;" 2175 " }" 2176 "})()"); 2177 CHECK(!try_catch.HasCaught()); 2178 } 2179 2180 2181 // This test works by making a stack of alternating JavaScript and C 2182 // activations. These activations set up exception handlers with regular 2183 // intervals, one interval for C activations and another for JavaScript 2184 // activations. When enough activations have been created an exception is 2185 // thrown and we check that the right activation catches the exception and that 2186 // no other activations do. The right activation is always the topmost one with 2187 // a handler, regardless of whether it is in JavaScript or C. 2188 // 2189 // The notation used to describe a test case looks like this: 2190 // 2191 // *JS[4] *C[3] @JS[2] C[1] JS[0] 2192 // 2193 // Each entry is an activation, either JS or C. The index is the count at that 2194 // level. Stars identify activations with exception handlers, the @ identifies 2195 // the exception handler that should catch the exception. 2196 // 2197 // BUG(271): Some of the exception propagation does not work on the 2198 // ARM simulator because the simulator separates the C++ stack and the 2199 // JS stack. This test therefore fails on the simulator. The test is 2200 // not threaded to allow the threading tests to run on the simulator. 2201 TEST(ExceptionOrder) { 2202 v8::HandleScope scope; 2203 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2204 templ->Set(v8_str("check"), v8::FunctionTemplate::New(JSCheck)); 2205 templ->Set(v8_str("CThrowCountDown"), 2206 v8::FunctionTemplate::New(CThrowCountDown)); 2207 LocalContext context(0, templ); 2208 CompileRun( 2209 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {" 2210 " if (count == 0) throw 'FromJS';" 2211 " if (count % jsInterval == 0) {" 2212 " try {" 2213 " var value = CThrowCountDown(count - 1," 2214 " jsInterval," 2215 " cInterval," 2216 " expected);" 2217 " check(false, count, expected);" 2218 " return value;" 2219 " } catch (e) {" 2220 " check(true, count, expected);" 2221 " }" 2222 " } else {" 2223 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);" 2224 " }" 2225 "}"); 2226 Local<Function> fun = 2227 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown"))); 2228 2229 const int argc = 4; 2230 // count jsInterval cInterval expected 2231 2232 // *JS[4] *C[3] @JS[2] C[1] JS[0] 2233 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) }; 2234 fun->Call(fun, argc, a0); 2235 2236 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0] 2237 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) }; 2238 fun->Call(fun, argc, a1); 2239 2240 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0] 2241 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) }; 2242 fun->Call(fun, argc, a2); 2243 2244 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0] 2245 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) }; 2246 fun->Call(fun, argc, a3); 2247 2248 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0] 2249 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) }; 2250 fun->Call(fun, argc, a4); 2251 2252 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0] 2253 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) }; 2254 fun->Call(fun, argc, a5); 2255 } 2256 2257 2258 v8::Handle<Value> ThrowValue(const v8::Arguments& args) { 2259 ApiTestFuzzer::Fuzz(); 2260 CHECK_EQ(1, args.Length()); 2261 return v8::ThrowException(args[0]); 2262 } 2263 2264 2265 THREADED_TEST(ThrowValues) { 2266 v8::HandleScope scope; 2267 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2268 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(ThrowValue)); 2269 LocalContext context(0, templ); 2270 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 2271 "function Run(obj) {" 2272 " try {" 2273 " Throw(obj);" 2274 " } catch (e) {" 2275 " return e;" 2276 " }" 2277 " return 'no exception';" 2278 "}" 2279 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];")); 2280 CHECK_EQ(5, result->Length()); 2281 CHECK(result->Get(v8::Integer::New(0))->IsString()); 2282 CHECK(result->Get(v8::Integer::New(1))->IsNumber()); 2283 CHECK_EQ(1, result->Get(v8::Integer::New(1))->Int32Value()); 2284 CHECK(result->Get(v8::Integer::New(2))->IsNumber()); 2285 CHECK_EQ(0, result->Get(v8::Integer::New(2))->Int32Value()); 2286 CHECK(result->Get(v8::Integer::New(3))->IsNull()); 2287 CHECK(result->Get(v8::Integer::New(4))->IsUndefined()); 2288 } 2289 2290 2291 THREADED_TEST(CatchZero) { 2292 v8::HandleScope scope; 2293 LocalContext context; 2294 v8::TryCatch try_catch; 2295 CHECK(!try_catch.HasCaught()); 2296 Script::Compile(v8_str("throw 10"))->Run(); 2297 CHECK(try_catch.HasCaught()); 2298 CHECK_EQ(10, try_catch.Exception()->Int32Value()); 2299 try_catch.Reset(); 2300 CHECK(!try_catch.HasCaught()); 2301 Script::Compile(v8_str("throw 0"))->Run(); 2302 CHECK(try_catch.HasCaught()); 2303 CHECK_EQ(0, try_catch.Exception()->Int32Value()); 2304 } 2305 2306 2307 THREADED_TEST(CatchExceptionFromWith) { 2308 v8::HandleScope scope; 2309 LocalContext context; 2310 v8::TryCatch try_catch; 2311 CHECK(!try_catch.HasCaught()); 2312 Script::Compile(v8_str("var o = {}; with (o) { throw 42; }"))->Run(); 2313 CHECK(try_catch.HasCaught()); 2314 } 2315 2316 2317 THREADED_TEST(Equality) { 2318 v8::HandleScope scope; 2319 LocalContext context; 2320 // Check that equality works at all before relying on CHECK_EQ 2321 CHECK(v8_str("a")->Equals(v8_str("a"))); 2322 CHECK(!v8_str("a")->Equals(v8_str("b"))); 2323 2324 CHECK_EQ(v8_str("a"), v8_str("a")); 2325 CHECK_NE(v8_str("a"), v8_str("b")); 2326 CHECK_EQ(v8_num(1), v8_num(1)); 2327 CHECK_EQ(v8_num(1.00), v8_num(1)); 2328 CHECK_NE(v8_num(1), v8_num(2)); 2329 2330 // Assume String is not symbol. 2331 CHECK(v8_str("a")->StrictEquals(v8_str("a"))); 2332 CHECK(!v8_str("a")->StrictEquals(v8_str("b"))); 2333 CHECK(!v8_str("5")->StrictEquals(v8_num(5))); 2334 CHECK(v8_num(1)->StrictEquals(v8_num(1))); 2335 CHECK(!v8_num(1)->StrictEquals(v8_num(2))); 2336 CHECK(v8_num(0)->StrictEquals(v8_num(-0))); 2337 Local<Value> not_a_number = v8_num(i::OS::nan_value()); 2338 CHECK(!not_a_number->StrictEquals(not_a_number)); 2339 CHECK(v8::False()->StrictEquals(v8::False())); 2340 CHECK(!v8::False()->StrictEquals(v8::Undefined())); 2341 2342 v8::Handle<v8::Object> obj = v8::Object::New(); 2343 v8::Persistent<v8::Object> alias = v8::Persistent<v8::Object>::New(obj); 2344 CHECK(alias->StrictEquals(obj)); 2345 alias.Dispose(); 2346 } 2347 2348 2349 THREADED_TEST(MultiRun) { 2350 v8::HandleScope scope; 2351 LocalContext context; 2352 Local<Script> script = Script::Compile(v8_str("x")); 2353 for (int i = 0; i < 10; i++) 2354 script->Run(); 2355 } 2356 2357 2358 static v8::Handle<Value> GetXValue(Local<String> name, 2359 const AccessorInfo& info) { 2360 ApiTestFuzzer::Fuzz(); 2361 CHECK_EQ(info.Data(), v8_str("donut")); 2362 CHECK_EQ(name, v8_str("x")); 2363 return name; 2364 } 2365 2366 2367 THREADED_TEST(SimplePropertyRead) { 2368 v8::HandleScope scope; 2369 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2370 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2371 LocalContext context; 2372 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2373 Local<Script> script = Script::Compile(v8_str("obj.x")); 2374 for (int i = 0; i < 10; i++) { 2375 Local<Value> result = script->Run(); 2376 CHECK_EQ(result, v8_str("x")); 2377 } 2378 } 2379 2380 THREADED_TEST(DefinePropertyOnAPIAccessor) { 2381 v8::HandleScope scope; 2382 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2383 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2384 LocalContext context; 2385 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2386 2387 // Uses getOwnPropertyDescriptor to check the configurable status 2388 Local<Script> script_desc 2389 = Script::Compile(v8_str("var prop =Object.getOwnPropertyDescriptor( " 2390 "obj, 'x');" 2391 "prop.configurable;")); 2392 Local<Value> result = script_desc->Run(); 2393 CHECK_EQ(result->BooleanValue(), true); 2394 2395 // Redefine get - but still configurable 2396 Local<Script> script_define 2397 = Script::Compile(v8_str("var desc = { get: function(){return 42; }," 2398 " configurable: true };" 2399 "Object.defineProperty(obj, 'x', desc);" 2400 "obj.x")); 2401 result = script_define->Run(); 2402 CHECK_EQ(result, v8_num(42)); 2403 2404 // Check that the accessor is still configurable 2405 result = script_desc->Run(); 2406 CHECK_EQ(result->BooleanValue(), true); 2407 2408 // Redefine to a non-configurable 2409 script_define 2410 = Script::Compile(v8_str("var desc = { get: function(){return 43; }," 2411 " configurable: false };" 2412 "Object.defineProperty(obj, 'x', desc);" 2413 "obj.x")); 2414 result = script_define->Run(); 2415 CHECK_EQ(result, v8_num(43)); 2416 result = script_desc->Run(); 2417 CHECK_EQ(result->BooleanValue(), false); 2418 2419 // Make sure that it is not possible to redefine again 2420 v8::TryCatch try_catch; 2421 result = script_define->Run(); 2422 CHECK(try_catch.HasCaught()); 2423 String::AsciiValue exception_value(try_catch.Exception()); 2424 CHECK_EQ(*exception_value, 2425 "TypeError: Cannot redefine property: defineProperty"); 2426 } 2427 2428 THREADED_TEST(DefinePropertyOnDefineGetterSetter) { 2429 v8::HandleScope scope; 2430 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2431 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 2432 LocalContext context; 2433 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2434 2435 Local<Script> script_desc = Script::Compile(v8_str("var prop =" 2436 "Object.getOwnPropertyDescriptor( " 2437 "obj, 'x');" 2438 "prop.configurable;")); 2439 Local<Value> result = script_desc->Run(); 2440 CHECK_EQ(result->BooleanValue(), true); 2441 2442 Local<Script> script_define = 2443 Script::Compile(v8_str("var desc = {get: function(){return 42; }," 2444 " configurable: true };" 2445 "Object.defineProperty(obj, 'x', desc);" 2446 "obj.x")); 2447 result = script_define->Run(); 2448 CHECK_EQ(result, v8_num(42)); 2449 2450 2451 result = script_desc->Run(); 2452 CHECK_EQ(result->BooleanValue(), true); 2453 2454 2455 script_define = 2456 Script::Compile(v8_str("var desc = {get: function(){return 43; }," 2457 " configurable: false };" 2458 "Object.defineProperty(obj, 'x', desc);" 2459 "obj.x")); 2460 result = script_define->Run(); 2461 CHECK_EQ(result, v8_num(43)); 2462 result = script_desc->Run(); 2463 2464 CHECK_EQ(result->BooleanValue(), false); 2465 2466 v8::TryCatch try_catch; 2467 result = script_define->Run(); 2468 CHECK(try_catch.HasCaught()); 2469 String::AsciiValue exception_value(try_catch.Exception()); 2470 CHECK_EQ(*exception_value, 2471 "TypeError: Cannot redefine property: defineProperty"); 2472 } 2473 2474 2475 2476 2477 2478 v8::Persistent<Value> xValue; 2479 2480 2481 static void SetXValue(Local<String> name, 2482 Local<Value> value, 2483 const AccessorInfo& info) { 2484 CHECK_EQ(value, v8_num(4)); 2485 CHECK_EQ(info.Data(), v8_str("donut")); 2486 CHECK_EQ(name, v8_str("x")); 2487 CHECK(xValue.IsEmpty()); 2488 xValue = v8::Persistent<Value>::New(value); 2489 } 2490 2491 2492 THREADED_TEST(SimplePropertyWrite) { 2493 v8::HandleScope scope; 2494 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2495 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, 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 = 4")); 2499 for (int i = 0; i < 10; i++) { 2500 CHECK(xValue.IsEmpty()); 2501 script->Run(); 2502 CHECK_EQ(v8_num(4), xValue); 2503 xValue.Dispose(); 2504 xValue = v8::Persistent<Value>(); 2505 } 2506 } 2507 2508 2509 static v8::Handle<Value> XPropertyGetter(Local<String> property, 2510 const AccessorInfo& info) { 2511 ApiTestFuzzer::Fuzz(); 2512 CHECK(info.Data()->IsUndefined()); 2513 return property; 2514 } 2515 2516 2517 THREADED_TEST(NamedInterceptorPropertyRead) { 2518 v8::HandleScope scope; 2519 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2520 templ->SetNamedPropertyHandler(XPropertyGetter); 2521 LocalContext context; 2522 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2523 Local<Script> script = Script::Compile(v8_str("obj.x")); 2524 for (int i = 0; i < 10; i++) { 2525 Local<Value> result = script->Run(); 2526 CHECK_EQ(result, v8_str("x")); 2527 } 2528 } 2529 2530 2531 static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property, 2532 const AccessorInfo& info) { 2533 // Set x on the prototype object and do not handle the get request. 2534 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype(); 2535 v8::Handle<v8::Object>::Cast(proto)->Set(v8_str("x"), v8::Integer::New(23)); 2536 return v8::Handle<Value>(); 2537 } 2538 2539 2540 // This is a regression test for http://crbug.com/20104. Map 2541 // transitions should not interfere with post interceptor lookup. 2542 THREADED_TEST(NamedInterceptorMapTransitionRead) { 2543 v8::HandleScope scope; 2544 Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(); 2545 Local<v8::ObjectTemplate> instance_template 2546 = function_template->InstanceTemplate(); 2547 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter); 2548 LocalContext context; 2549 context->Global()->Set(v8_str("F"), function_template->GetFunction()); 2550 // Create an instance of F and introduce a map transition for x. 2551 CompileRun("var o = new F(); o.x = 23;"); 2552 // Create an instance of F and invoke the getter. The result should be 23. 2553 Local<Value> result = CompileRun("o = new F(); o.x"); 2554 CHECK_EQ(result->Int32Value(), 23); 2555 } 2556 2557 2558 static v8::Handle<Value> IndexedPropertyGetter(uint32_t index, 2559 const AccessorInfo& info) { 2560 ApiTestFuzzer::Fuzz(); 2561 if (index == 37) { 2562 return v8::Handle<Value>(v8_num(625)); 2563 } 2564 return v8::Handle<Value>(); 2565 } 2566 2567 2568 static v8::Handle<Value> IndexedPropertySetter(uint32_t index, 2569 Local<Value> value, 2570 const AccessorInfo& info) { 2571 ApiTestFuzzer::Fuzz(); 2572 if (index == 39) { 2573 return value; 2574 } 2575 return v8::Handle<Value>(); 2576 } 2577 2578 2579 THREADED_TEST(IndexedInterceptorWithIndexedAccessor) { 2580 v8::HandleScope scope; 2581 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2582 templ->SetIndexedPropertyHandler(IndexedPropertyGetter, 2583 IndexedPropertySetter); 2584 LocalContext context; 2585 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2586 Local<Script> getter_script = Script::Compile(v8_str( 2587 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];")); 2588 Local<Script> setter_script = Script::Compile(v8_str( 2589 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});" 2590 "obj[17] = 23;" 2591 "obj.foo;")); 2592 Local<Script> interceptor_setter_script = Script::Compile(v8_str( 2593 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});" 2594 "obj[39] = 47;" 2595 "obj.foo;")); // This setter should not run, due to the interceptor. 2596 Local<Script> interceptor_getter_script = Script::Compile(v8_str( 2597 "obj[37];")); 2598 Local<Value> result = getter_script->Run(); 2599 CHECK_EQ(v8_num(5), result); 2600 result = setter_script->Run(); 2601 CHECK_EQ(v8_num(23), result); 2602 result = interceptor_setter_script->Run(); 2603 CHECK_EQ(v8_num(23), result); 2604 result = interceptor_getter_script->Run(); 2605 CHECK_EQ(v8_num(625), result); 2606 } 2607 2608 2609 static v8::Handle<Value> IdentityIndexedPropertyGetter( 2610 uint32_t index, 2611 const AccessorInfo& info) { 2612 return v8::Integer::New(index); 2613 } 2614 2615 2616 THREADED_TEST(IndexedInterceptorWithNoSetter) { 2617 v8::HandleScope scope; 2618 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2619 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 2620 2621 LocalContext context; 2622 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 2623 2624 const char* code = 2625 "try {" 2626 " obj[0] = 239;" 2627 " for (var i = 0; i < 100; i++) {" 2628 " var v = obj[0];" 2629 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;" 2630 " }" 2631 " 'PASSED'" 2632 "} catch(e) {" 2633 " e" 2634 "}"; 2635 ExpectString(code, "PASSED"); 2636 } 2637 2638 2639 THREADED_TEST(IndexedInterceptorWithAccessorCheck) { 2640 v8::HandleScope scope; 2641 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2642 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 2643 2644 LocalContext context; 2645 Local<v8::Object> obj = templ->NewInstance(); 2646 obj->TurnOnAccessCheck(); 2647 context->Global()->Set(v8_str("obj"), obj); 2648 2649 const char* code = 2650 "try {" 2651 " for (var i = 0; i < 100; i++) {" 2652 " var v = obj[0];" 2653 " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;" 2654 " }" 2655 " 'PASSED'" 2656 "} catch(e) {" 2657 " e" 2658 "}"; 2659 ExpectString(code, "PASSED"); 2660 } 2661 2662 2663 THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) { 2664 i::FLAG_allow_natives_syntax = true; 2665 v8::HandleScope scope; 2666 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2667 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 2668 2669 LocalContext context; 2670 Local<v8::Object> obj = templ->NewInstance(); 2671 context->Global()->Set(v8_str("obj"), obj); 2672 2673 const char* code = 2674 "try {" 2675 " for (var i = 0; i < 100; i++) {" 2676 " var expected = i;" 2677 " if (i == 5) {" 2678 " %EnableAccessChecks(obj);" 2679 " expected = undefined;" 2680 " }" 2681 " var v = obj[i];" 2682 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 2683 " if (i == 5) %DisableAccessChecks(obj);" 2684 " }" 2685 " 'PASSED'" 2686 "} catch(e) {" 2687 " e" 2688 "}"; 2689 ExpectString(code, "PASSED"); 2690 } 2691 2692 2693 THREADED_TEST(IndexedInterceptorWithDifferentIndices) { 2694 v8::HandleScope scope; 2695 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2696 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 2697 2698 LocalContext context; 2699 Local<v8::Object> obj = templ->NewInstance(); 2700 context->Global()->Set(v8_str("obj"), obj); 2701 2702 const char* code = 2703 "try {" 2704 " for (var i = 0; i < 100; i++) {" 2705 " var v = obj[i];" 2706 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 2707 " }" 2708 " 'PASSED'" 2709 "} catch(e) {" 2710 " e" 2711 "}"; 2712 ExpectString(code, "PASSED"); 2713 } 2714 2715 2716 THREADED_TEST(IndexedInterceptorWithNotSmiLookup) { 2717 v8::HandleScope scope; 2718 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2719 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 2720 2721 LocalContext context; 2722 Local<v8::Object> obj = templ->NewInstance(); 2723 context->Global()->Set(v8_str("obj"), obj); 2724 2725 const char* code = 2726 "try {" 2727 " for (var i = 0; i < 100; i++) {" 2728 " var expected = i;" 2729 " if (i == 50) {" 2730 " i = 'foobar';" 2731 " expected = undefined;" 2732 " }" 2733 " var v = obj[i];" 2734 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 2735 " }" 2736 " 'PASSED'" 2737 "} catch(e) {" 2738 " e" 2739 "}"; 2740 ExpectString(code, "PASSED"); 2741 } 2742 2743 2744 THREADED_TEST(IndexedInterceptorGoingMegamorphic) { 2745 v8::HandleScope scope; 2746 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2747 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 2748 2749 LocalContext context; 2750 Local<v8::Object> obj = templ->NewInstance(); 2751 context->Global()->Set(v8_str("obj"), obj); 2752 2753 const char* code = 2754 "var original = obj;" 2755 "try {" 2756 " for (var i = 0; i < 100; i++) {" 2757 " var expected = i;" 2758 " if (i == 50) {" 2759 " obj = {50: 'foobar'};" 2760 " expected = 'foobar';" 2761 " }" 2762 " var v = obj[i];" 2763 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 2764 " if (i == 50) obj = original;" 2765 " }" 2766 " 'PASSED'" 2767 "} catch(e) {" 2768 " e" 2769 "}"; 2770 ExpectString(code, "PASSED"); 2771 } 2772 2773 2774 THREADED_TEST(IndexedInterceptorReceiverTurningSmi) { 2775 v8::HandleScope scope; 2776 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2777 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 2778 2779 LocalContext context; 2780 Local<v8::Object> obj = templ->NewInstance(); 2781 context->Global()->Set(v8_str("obj"), obj); 2782 2783 const char* code = 2784 "var original = obj;" 2785 "try {" 2786 " for (var i = 0; i < 100; i++) {" 2787 " var expected = i;" 2788 " if (i == 5) {" 2789 " obj = 239;" 2790 " expected = undefined;" 2791 " }" 2792 " var v = obj[i];" 2793 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 2794 " if (i == 5) obj = original;" 2795 " }" 2796 " 'PASSED'" 2797 "} catch(e) {" 2798 " e" 2799 "}"; 2800 ExpectString(code, "PASSED"); 2801 } 2802 2803 2804 THREADED_TEST(IndexedInterceptorOnProto) { 2805 v8::HandleScope scope; 2806 Local<ObjectTemplate> templ = ObjectTemplate::New(); 2807 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 2808 2809 LocalContext context; 2810 Local<v8::Object> obj = templ->NewInstance(); 2811 context->Global()->Set(v8_str("obj"), obj); 2812 2813 const char* code = 2814 "var o = {__proto__: obj};" 2815 "try {" 2816 " for (var i = 0; i < 100; i++) {" 2817 " var v = o[i];" 2818 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 2819 " }" 2820 " 'PASSED'" 2821 "} catch(e) {" 2822 " e" 2823 "}"; 2824 ExpectString(code, "PASSED"); 2825 } 2826 2827 2828 THREADED_TEST(MultiContexts) { 2829 v8::HandleScope scope; 2830 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New(); 2831 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(DummyCallHandler)); 2832 2833 Local<String> password = v8_str("Password"); 2834 2835 // Create an environment 2836 LocalContext context0(0, templ); 2837 context0->SetSecurityToken(password); 2838 v8::Handle<v8::Object> global0 = context0->Global(); 2839 global0->Set(v8_str("custom"), v8_num(1234)); 2840 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 2841 2842 // Create an independent environment 2843 LocalContext context1(0, templ); 2844 context1->SetSecurityToken(password); 2845 v8::Handle<v8::Object> global1 = context1->Global(); 2846 global1->Set(v8_str("custom"), v8_num(1234)); 2847 CHECK_NE(global0, global1); 2848 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 2849 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value()); 2850 2851 // Now create a new context with the old global 2852 LocalContext context2(0, templ, global1); 2853 context2->SetSecurityToken(password); 2854 v8::Handle<v8::Object> global2 = context2->Global(); 2855 CHECK_EQ(global1, global2); 2856 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value()); 2857 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value()); 2858 } 2859 2860 2861 THREADED_TEST(FunctionPrototypeAcrossContexts) { 2862 // Make sure that functions created by cloning boilerplates cannot 2863 // communicate through their __proto__ field. 2864 2865 v8::HandleScope scope; 2866 2867 LocalContext env0; 2868 v8::Handle<v8::Object> global0 = 2869 env0->Global(); 2870 v8::Handle<v8::Object> object0 = 2871 v8::Handle<v8::Object>::Cast(global0->Get(v8_str("Object"))); 2872 v8::Handle<v8::Object> tostring0 = 2873 v8::Handle<v8::Object>::Cast(object0->Get(v8_str("toString"))); 2874 v8::Handle<v8::Object> proto0 = 2875 v8::Handle<v8::Object>::Cast(tostring0->Get(v8_str("__proto__"))); 2876 proto0->Set(v8_str("custom"), v8_num(1234)); 2877 2878 LocalContext env1; 2879 v8::Handle<v8::Object> global1 = 2880 env1->Global(); 2881 v8::Handle<v8::Object> object1 = 2882 v8::Handle<v8::Object>::Cast(global1->Get(v8_str("Object"))); 2883 v8::Handle<v8::Object> tostring1 = 2884 v8::Handle<v8::Object>::Cast(object1->Get(v8_str("toString"))); 2885 v8::Handle<v8::Object> proto1 = 2886 v8::Handle<v8::Object>::Cast(tostring1->Get(v8_str("__proto__"))); 2887 CHECK(!proto1->Has(v8_str("custom"))); 2888 } 2889 2890 2891 THREADED_TEST(Regress892105) { 2892 // Make sure that object and array literals created by cloning 2893 // boilerplates cannot communicate through their __proto__ 2894 // field. This is rather difficult to check, but we try to add stuff 2895 // to Object.prototype and Array.prototype and create a new 2896 // environment. This should succeed. 2897 2898 v8::HandleScope scope; 2899 2900 Local<String> source = v8_str("Object.prototype.obj = 1234;" 2901 "Array.prototype.arr = 4567;" 2902 "8901"); 2903 2904 LocalContext env0; 2905 Local<Script> script0 = Script::Compile(source); 2906 CHECK_EQ(8901.0, script0->Run()->NumberValue()); 2907 2908 LocalContext env1; 2909 Local<Script> script1 = Script::Compile(source); 2910 CHECK_EQ(8901.0, script1->Run()->NumberValue()); 2911 } 2912 2913 2914 THREADED_TEST(UndetectableObject) { 2915 v8::HandleScope scope; 2916 LocalContext env; 2917 2918 Local<v8::FunctionTemplate> desc = 2919 v8::FunctionTemplate::New(0, v8::Handle<Value>()); 2920 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 2921 2922 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 2923 env->Global()->Set(v8_str("undetectable"), obj); 2924 2925 ExpectString("undetectable.toString()", "[object Object]"); 2926 ExpectString("typeof undetectable", "undefined"); 2927 ExpectString("typeof(undetectable)", "undefined"); 2928 ExpectBoolean("typeof undetectable == 'undefined'", true); 2929 ExpectBoolean("typeof undetectable == 'object'", false); 2930 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 2931 ExpectBoolean("!undetectable", true); 2932 2933 ExpectObject("true&&undetectable", obj); 2934 ExpectBoolean("false&&undetectable", false); 2935 ExpectBoolean("true||undetectable", true); 2936 ExpectObject("false||undetectable", obj); 2937 2938 ExpectObject("undetectable&&true", obj); 2939 ExpectObject("undetectable&&false", obj); 2940 ExpectBoolean("undetectable||true", true); 2941 ExpectBoolean("undetectable||false", false); 2942 2943 ExpectBoolean("undetectable==null", true); 2944 ExpectBoolean("null==undetectable", true); 2945 ExpectBoolean("undetectable==undefined", true); 2946 ExpectBoolean("undefined==undetectable", true); 2947 ExpectBoolean("undetectable==undetectable", true); 2948 2949 2950 ExpectBoolean("undetectable===null", false); 2951 ExpectBoolean("null===undetectable", false); 2952 ExpectBoolean("undetectable===undefined", false); 2953 ExpectBoolean("undefined===undetectable", false); 2954 ExpectBoolean("undetectable===undetectable", true); 2955 } 2956 2957 2958 THREADED_TEST(UndetectableString) { 2959 v8::HandleScope scope; 2960 LocalContext env; 2961 2962 Local<String> obj = String::NewUndetectable("foo"); 2963 env->Global()->Set(v8_str("undetectable"), obj); 2964 2965 ExpectString("undetectable", "foo"); 2966 ExpectString("typeof undetectable", "undefined"); 2967 ExpectString("typeof(undetectable)", "undefined"); 2968 ExpectBoolean("typeof undetectable == 'undefined'", true); 2969 ExpectBoolean("typeof undetectable == 'string'", false); 2970 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 2971 ExpectBoolean("!undetectable", true); 2972 2973 ExpectObject("true&&undetectable", obj); 2974 ExpectBoolean("false&&undetectable", false); 2975 ExpectBoolean("true||undetectable", true); 2976 ExpectObject("false||undetectable", obj); 2977 2978 ExpectObject("undetectable&&true", obj); 2979 ExpectObject("undetectable&&false", obj); 2980 ExpectBoolean("undetectable||true", true); 2981 ExpectBoolean("undetectable||false", false); 2982 2983 ExpectBoolean("undetectable==null", true); 2984 ExpectBoolean("null==undetectable", true); 2985 ExpectBoolean("undetectable==undefined", true); 2986 ExpectBoolean("undefined==undetectable", true); 2987 ExpectBoolean("undetectable==undetectable", true); 2988 2989 2990 ExpectBoolean("undetectable===null", false); 2991 ExpectBoolean("null===undetectable", false); 2992 ExpectBoolean("undetectable===undefined", false); 2993 ExpectBoolean("undefined===undetectable", false); 2994 ExpectBoolean("undetectable===undetectable", true); 2995 } 2996 2997 2998 template <typename T> static void USE(T) { } 2999 3000 3001 // This test is not intended to be run, just type checked. 3002 static void PersistentHandles() { 3003 USE(PersistentHandles); 3004 Local<String> str = v8_str("foo"); 3005 v8::Persistent<String> p_str = v8::Persistent<String>::New(str); 3006 USE(p_str); 3007 Local<Script> scr = Script::Compile(v8_str("")); 3008 v8::Persistent<Script> p_scr = v8::Persistent<Script>::New(scr); 3009 USE(p_scr); 3010 Local<ObjectTemplate> templ = ObjectTemplate::New(); 3011 v8::Persistent<ObjectTemplate> p_templ = 3012 v8::Persistent<ObjectTemplate>::New(templ); 3013 USE(p_templ); 3014 } 3015 3016 3017 static v8::Handle<Value> HandleLogDelegator(const v8::Arguments& args) { 3018 ApiTestFuzzer::Fuzz(); 3019 return v8::Undefined(); 3020 } 3021 3022 3023 THREADED_TEST(GlobalObjectTemplate) { 3024 v8::HandleScope handle_scope; 3025 Local<ObjectTemplate> global_template = ObjectTemplate::New(); 3026 global_template->Set(v8_str("JSNI_Log"), 3027 v8::FunctionTemplate::New(HandleLogDelegator)); 3028 v8::Persistent<Context> context = Context::New(0, global_template); 3029 Context::Scope context_scope(context); 3030 Script::Compile(v8_str("JSNI_Log('LOG')"))->Run(); 3031 context.Dispose(); 3032 } 3033 3034 3035 static const char* kSimpleExtensionSource = 3036 "function Foo() {" 3037 " return 4;" 3038 "}"; 3039 3040 3041 THREADED_TEST(SimpleExtensions) { 3042 v8::HandleScope handle_scope; 3043 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource)); 3044 const char* extension_names[] = { "simpletest" }; 3045 v8::ExtensionConfiguration extensions(1, extension_names); 3046 v8::Handle<Context> context = Context::New(&extensions); 3047 Context::Scope lock(context); 3048 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 3049 CHECK_EQ(result, v8::Integer::New(4)); 3050 } 3051 3052 3053 static const char* kEvalExtensionSource1 = 3054 "function UseEval1() {" 3055 " var x = 42;" 3056 " return eval('x');" 3057 "}"; 3058 3059 3060 static const char* kEvalExtensionSource2 = 3061 "(function() {" 3062 " var x = 42;" 3063 " function e() {" 3064 " return eval('x');" 3065 " }" 3066 " this.UseEval2 = e;" 3067 "})()"; 3068 3069 3070 THREADED_TEST(UseEvalFromExtension) { 3071 v8::HandleScope handle_scope; 3072 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1)); 3073 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2)); 3074 const char* extension_names[] = { "evaltest1", "evaltest2" }; 3075 v8::ExtensionConfiguration extensions(2, extension_names); 3076 v8::Handle<Context> context = Context::New(&extensions); 3077 Context::Scope lock(context); 3078 v8::Handle<Value> result = Script::Compile(v8_str("UseEval1()"))->Run(); 3079 CHECK_EQ(result, v8::Integer::New(42)); 3080 result = Script::Compile(v8_str("UseEval2()"))->Run(); 3081 CHECK_EQ(result, v8::Integer::New(42)); 3082 } 3083 3084 3085 static const char* kWithExtensionSource1 = 3086 "function UseWith1() {" 3087 " var x = 42;" 3088 " with({x:87}) { return x; }" 3089 "}"; 3090 3091 3092 3093 static const char* kWithExtensionSource2 = 3094 "(function() {" 3095 " var x = 42;" 3096 " function e() {" 3097 " with ({x:87}) { return x; }" 3098 " }" 3099 " this.UseWith2 = e;" 3100 "})()"; 3101 3102 3103 THREADED_TEST(UseWithFromExtension) { 3104 v8::HandleScope handle_scope; 3105 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1)); 3106 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2)); 3107 const char* extension_names[] = { "withtest1", "withtest2" }; 3108 v8::ExtensionConfiguration extensions(2, extension_names); 3109 v8::Handle<Context> context = Context::New(&extensions); 3110 Context::Scope lock(context); 3111 v8::Handle<Value> result = Script::Compile(v8_str("UseWith1()"))->Run(); 3112 CHECK_EQ(result, v8::Integer::New(87)); 3113 result = Script::Compile(v8_str("UseWith2()"))->Run(); 3114 CHECK_EQ(result, v8::Integer::New(87)); 3115 } 3116 3117 3118 THREADED_TEST(AutoExtensions) { 3119 v8::HandleScope handle_scope; 3120 Extension* extension = new Extension("autotest", kSimpleExtensionSource); 3121 extension->set_auto_enable(true); 3122 v8::RegisterExtension(extension); 3123 v8::Handle<Context> context = Context::New(); 3124 Context::Scope lock(context); 3125 v8::Handle<Value> result = Script::Compile(v8_str("Foo()"))->Run(); 3126 CHECK_EQ(result, v8::Integer::New(4)); 3127 } 3128 3129 3130 static const char* kSyntaxErrorInExtensionSource = 3131 "["; 3132 3133 3134 // Test that a syntax error in an extension does not cause a fatal 3135 // error but results in an empty context. 3136 THREADED_TEST(SyntaxErrorExtensions) { 3137 v8::HandleScope handle_scope; 3138 v8::RegisterExtension(new Extension("syntaxerror", 3139 kSyntaxErrorInExtensionSource)); 3140 const char* extension_names[] = { "syntaxerror" }; 3141 v8::ExtensionConfiguration extensions(1, extension_names); 3142 v8::Handle<Context> context = Context::New(&extensions); 3143 CHECK(context.IsEmpty()); 3144 } 3145 3146 3147 static const char* kExceptionInExtensionSource = 3148 "throw 42"; 3149 3150 3151 // Test that an exception when installing an extension does not cause 3152 // a fatal error but results in an empty context. 3153 THREADED_TEST(ExceptionExtensions) { 3154 v8::HandleScope handle_scope; 3155 v8::RegisterExtension(new Extension("exception", 3156 kExceptionInExtensionSource)); 3157 const char* extension_names[] = { "exception" }; 3158 v8::ExtensionConfiguration extensions(1, extension_names); 3159 v8::Handle<Context> context = Context::New(&extensions); 3160 CHECK(context.IsEmpty()); 3161 } 3162 3163 3164 static void CheckDependencies(const char* name, const char* expected) { 3165 v8::HandleScope handle_scope; 3166 v8::ExtensionConfiguration config(1, &name); 3167 LocalContext context(&config); 3168 CHECK_EQ(String::New(expected), context->Global()->Get(v8_str("loaded"))); 3169 } 3170 3171 3172 /* 3173 * Configuration: 3174 * 3175 * /-- B <--\ 3176 * A <- -- D <-- E 3177 * \-- C <--/ 3178 */ 3179 THREADED_TEST(ExtensionDependency) { 3180 static const char* kEDeps[] = { "D" }; 3181 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps)); 3182 static const char* kDDeps[] = { "B", "C" }; 3183 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps)); 3184 static const char* kBCDeps[] = { "A" }; 3185 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps)); 3186 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps)); 3187 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';")); 3188 CheckDependencies("A", "undefinedA"); 3189 CheckDependencies("B", "undefinedAB"); 3190 CheckDependencies("C", "undefinedAC"); 3191 CheckDependencies("D", "undefinedABCD"); 3192 CheckDependencies("E", "undefinedABCDE"); 3193 v8::HandleScope handle_scope; 3194 static const char* exts[2] = { "C", "E" }; 3195 v8::ExtensionConfiguration config(2, exts); 3196 LocalContext context(&config); 3197 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded"))); 3198 } 3199 3200 3201 static const char* kExtensionTestScript = 3202 "native function A();" 3203 "native function B();" 3204 "native function C();" 3205 "function Foo(i) {" 3206 " if (i == 0) return A();" 3207 " if (i == 1) return B();" 3208 " if (i == 2) return C();" 3209 "}"; 3210 3211 3212 static v8::Handle<Value> CallFun(const v8::Arguments& args) { 3213 ApiTestFuzzer::Fuzz(); 3214 if (args.IsConstructCall()) { 3215 args.This()->Set(v8_str("data"), args.Data()); 3216 return v8::Null(); 3217 } 3218 return args.Data(); 3219 } 3220 3221 3222 class FunctionExtension : public Extension { 3223 public: 3224 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { } 3225 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( 3226 v8::Handle<String> name); 3227 }; 3228 3229 3230 static int lookup_count = 0; 3231 v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunction( 3232 v8::Handle<String> name) { 3233 lookup_count++; 3234 if (name->Equals(v8_str("A"))) { 3235 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(8)); 3236 } else if (name->Equals(v8_str("B"))) { 3237 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(7)); 3238 } else if (name->Equals(v8_str("C"))) { 3239 return v8::FunctionTemplate::New(CallFun, v8::Integer::New(6)); 3240 } else { 3241 return v8::Handle<v8::FunctionTemplate>(); 3242 } 3243 } 3244 3245 3246 THREADED_TEST(FunctionLookup) { 3247 v8::RegisterExtension(new FunctionExtension()); 3248 v8::HandleScope handle_scope; 3249 static const char* exts[1] = { "functiontest" }; 3250 v8::ExtensionConfiguration config(1, exts); 3251 LocalContext context(&config); 3252 CHECK_EQ(3, lookup_count); 3253 CHECK_EQ(v8::Integer::New(8), Script::Compile(v8_str("Foo(0)"))->Run()); 3254 CHECK_EQ(v8::Integer::New(7), Script::Compile(v8_str("Foo(1)"))->Run()); 3255 CHECK_EQ(v8::Integer::New(6), Script::Compile(v8_str("Foo(2)"))->Run()); 3256 } 3257 3258 3259 THREADED_TEST(NativeFunctionConstructCall) { 3260 v8::RegisterExtension(new FunctionExtension()); 3261 v8::HandleScope handle_scope; 3262 static const char* exts[1] = { "functiontest" }; 3263 v8::ExtensionConfiguration config(1, exts); 3264 LocalContext context(&config); 3265 for (int i = 0; i < 10; i++) { 3266 // Run a few times to ensure that allocation of objects doesn't 3267 // change behavior of a constructor function. 3268 CHECK_EQ(v8::Integer::New(8), 3269 Script::Compile(v8_str("(new A()).data"))->Run()); 3270 CHECK_EQ(v8::Integer::New(7), 3271 Script::Compile(v8_str("(new B()).data"))->Run()); 3272 CHECK_EQ(v8::Integer::New(6), 3273 Script::Compile(v8_str("(new C()).data"))->Run()); 3274 } 3275 } 3276 3277 3278 static const char* last_location; 3279 static const char* last_message; 3280 void StoringErrorCallback(const char* location, const char* message) { 3281 if (last_location == NULL) { 3282 last_location = location; 3283 last_message = message; 3284 } 3285 } 3286 3287 3288 // ErrorReporting creates a circular extensions configuration and 3289 // tests that the fatal error handler gets called. This renders V8 3290 // unusable and therefore this test cannot be run in parallel. 3291 TEST(ErrorReporting) { 3292 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 3293 static const char* aDeps[] = { "B" }; 3294 v8::RegisterExtension(new Extension("A", "", 1, aDeps)); 3295 static const char* bDeps[] = { "A" }; 3296 v8::RegisterExtension(new Extension("B", "", 1, bDeps)); 3297 last_location = NULL; 3298 v8::ExtensionConfiguration config(1, bDeps); 3299 v8::Handle<Context> context = Context::New(&config); 3300 CHECK(context.IsEmpty()); 3301 CHECK_NE(last_location, NULL); 3302 } 3303 3304 3305 static const char* js_code_causing_huge_string_flattening = 3306 "var str = 'X';" 3307 "for (var i = 0; i < 30; i++) {" 3308 " str = str + str;" 3309 "}" 3310 "str.match(/X/);"; 3311 3312 3313 void OOMCallback(const char* location, const char* message) { 3314 exit(0); 3315 } 3316 3317 3318 TEST(RegexpOutOfMemory) { 3319 // Execute a script that causes out of memory when flattening a string. 3320 v8::HandleScope scope; 3321 v8::V8::SetFatalErrorHandler(OOMCallback); 3322 LocalContext context; 3323 Local<Script> script = 3324 Script::Compile(String::New(js_code_causing_huge_string_flattening)); 3325 last_location = NULL; 3326 Local<Value> result = script->Run(); 3327 3328 CHECK(false); // Should not return. 3329 } 3330 3331 3332 static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message, 3333 v8::Handle<Value> data) { 3334 CHECK_EQ(v8::Undefined(), data); 3335 CHECK(message->GetScriptResourceName()->IsUndefined()); 3336 CHECK_EQ(v8::Undefined(), message->GetScriptResourceName()); 3337 message->GetLineNumber(); 3338 message->GetSourceLine(); 3339 } 3340 3341 3342 THREADED_TEST(ErrorWithMissingScriptInfo) { 3343 v8::HandleScope scope; 3344 LocalContext context; 3345 v8::V8::AddMessageListener(MissingScriptInfoMessageListener); 3346 Script::Compile(v8_str("throw Error()"))->Run(); 3347 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener); 3348 } 3349 3350 3351 int global_index = 0; 3352 3353 class Snorkel { 3354 public: 3355 Snorkel() { index_ = global_index++; } 3356 int index_; 3357 }; 3358 3359 class Whammy { 3360 public: 3361 Whammy() { 3362 cursor_ = 0; 3363 } 3364 ~Whammy() { 3365 script_.Dispose(); 3366 } 3367 v8::Handle<Script> getScript() { 3368 if (script_.IsEmpty()) 3369 script_ = v8::Persistent<Script>::New(v8_compile("({}).blammo")); 3370 return Local<Script>(*script_); 3371 } 3372 3373 public: 3374 static const int kObjectCount = 256; 3375 int cursor_; 3376 v8::Persistent<v8::Object> objects_[kObjectCount]; 3377 v8::Persistent<Script> script_; 3378 }; 3379 3380 static void HandleWeakReference(v8::Persistent<v8::Value> obj, void* data) { 3381 Snorkel* snorkel = reinterpret_cast<Snorkel*>(data); 3382 delete snorkel; 3383 obj.ClearWeak(); 3384 } 3385 3386 v8::Handle<Value> WhammyPropertyGetter(Local<String> name, 3387 const AccessorInfo& info) { 3388 Whammy* whammy = 3389 static_cast<Whammy*>(v8::Handle<v8::External>::Cast(info.Data())->Value()); 3390 3391 v8::Persistent<v8::Object> prev = whammy->objects_[whammy->cursor_]; 3392 3393 v8::Handle<v8::Object> obj = v8::Object::New(); 3394 v8::Persistent<v8::Object> global = v8::Persistent<v8::Object>::New(obj); 3395 if (!prev.IsEmpty()) { 3396 prev->Set(v8_str("next"), obj); 3397 prev.MakeWeak(new Snorkel(), &HandleWeakReference); 3398 whammy->objects_[whammy->cursor_].Clear(); 3399 } 3400 whammy->objects_[whammy->cursor_] = global; 3401 whammy->cursor_ = (whammy->cursor_ + 1) % Whammy::kObjectCount; 3402 return whammy->getScript()->Run(); 3403 } 3404 3405 THREADED_TEST(WeakReference) { 3406 v8::HandleScope handle_scope; 3407 v8::Handle<v8::ObjectTemplate> templ= v8::ObjectTemplate::New(); 3408 templ->SetNamedPropertyHandler(WhammyPropertyGetter, 3409 0, 0, 0, 0, 3410 v8::External::New(new Whammy())); 3411 const char* extension_list[] = { "v8/gc" }; 3412 v8::ExtensionConfiguration extensions(1, extension_list); 3413 v8::Persistent<Context> context = Context::New(&extensions); 3414 Context::Scope context_scope(context); 3415 3416 v8::Handle<v8::Object> interceptor = templ->NewInstance(); 3417 context->Global()->Set(v8_str("whammy"), interceptor); 3418 const char* code = 3419 "var last;" 3420 "for (var i = 0; i < 10000; i++) {" 3421 " var obj = whammy.length;" 3422 " if (last) last.next = obj;" 3423 " last = obj;" 3424 "}" 3425 "gc();" 3426 "4"; 3427 v8::Handle<Value> result = CompileRun(code); 3428 CHECK_EQ(4.0, result->NumberValue()); 3429 3430 context.Dispose(); 3431 } 3432 3433 3434 static bool in_scavenge = false; 3435 static int last = -1; 3436 3437 static void ForceScavenge(v8::Persistent<v8::Value> obj, void* data) { 3438 CHECK_EQ(-1, last); 3439 last = 0; 3440 obj.Dispose(); 3441 obj.Clear(); 3442 in_scavenge = true; 3443 i::Heap::PerformScavenge(); 3444 in_scavenge = false; 3445 *(reinterpret_cast<bool*>(data)) = true; 3446 } 3447 3448 static void CheckIsNotInvokedInScavenge(v8::Persistent<v8::Value> obj, 3449 void* data) { 3450 CHECK_EQ(0, last); 3451 last = 1; 3452 *(reinterpret_cast<bool*>(data)) = in_scavenge; 3453 obj.Dispose(); 3454 obj.Clear(); 3455 } 3456 3457 THREADED_TEST(NoWeakRefCallbacksInScavenge) { 3458 // Test verifies that scavenge cannot invoke WeakReferenceCallbacks. 3459 // Calling callbacks from scavenges is unsafe as objects held by those 3460 // handlers might have become strongly reachable, but scavenge doesn't 3461 // check that. 3462 v8::Persistent<Context> context = Context::New(); 3463 Context::Scope context_scope(context); 3464 3465 v8::Persistent<v8::Object> object_a; 3466 v8::Persistent<v8::Object> object_b; 3467 3468 { 3469 v8::HandleScope handle_scope; 3470 object_b = v8::Persistent<v8::Object>::New(v8::Object::New()); 3471 object_a = v8::Persistent<v8::Object>::New(v8::Object::New()); 3472 } 3473 3474 bool object_a_disposed = false; 3475 object_a.MakeWeak(&object_a_disposed, &ForceScavenge); 3476 bool released_in_scavenge = false; 3477 object_b.MakeWeak(&released_in_scavenge, &CheckIsNotInvokedInScavenge); 3478 3479 while (!object_a_disposed) { 3480 i::Heap::CollectAllGarbage(false); 3481 } 3482 CHECK(!released_in_scavenge); 3483 } 3484 3485 3486 v8::Handle<Function> args_fun; 3487 3488 3489 static v8::Handle<Value> ArgumentsTestCallback(const v8::Arguments& args) { 3490 ApiTestFuzzer::Fuzz(); 3491 CHECK_EQ(args_fun, args.Callee()); 3492 CHECK_EQ(3, args.Length()); 3493 CHECK_EQ(v8::Integer::New(1), args[0]); 3494 CHECK_EQ(v8::Integer::New(2), args[1]); 3495 CHECK_EQ(v8::Integer::New(3), args[2]); 3496 CHECK_EQ(v8::Undefined(), args[3]); 3497 v8::HandleScope scope; 3498 i::Heap::CollectAllGarbage(false); 3499 return v8::Undefined(); 3500 } 3501 3502 3503 THREADED_TEST(Arguments) { 3504 v8::HandleScope scope; 3505 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(); 3506 global->Set(v8_str("f"), v8::FunctionTemplate::New(ArgumentsTestCallback)); 3507 LocalContext context(NULL, global); 3508 args_fun = v8::Handle<Function>::Cast(context->Global()->Get(v8_str("f"))); 3509 v8_compile("f(1, 2, 3)")->Run(); 3510 } 3511 3512 3513 static v8::Handle<Value> NoBlockGetterX(Local<String> name, 3514 const AccessorInfo&) { 3515 return v8::Handle<Value>(); 3516 } 3517 3518 3519 static v8::Handle<Value> NoBlockGetterI(uint32_t index, 3520 const AccessorInfo&) { 3521 return v8::Handle<Value>(); 3522 } 3523 3524 3525 static v8::Handle<v8::Boolean> PDeleter(Local<String> name, 3526 const AccessorInfo&) { 3527 if (!name->Equals(v8_str("foo"))) { 3528 return v8::Handle<v8::Boolean>(); // not intercepted 3529 } 3530 3531 return v8::False(); // intercepted, and don't delete the property 3532 } 3533 3534 3535 static v8::Handle<v8::Boolean> IDeleter(uint32_t index, const AccessorInfo&) { 3536 if (index != 2) { 3537 return v8::Handle<v8::Boolean>(); // not intercepted 3538 } 3539 3540 return v8::False(); // intercepted, and don't delete the property 3541 } 3542 3543 3544 THREADED_TEST(Deleter) { 3545 v8::HandleScope scope; 3546 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 3547 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL); 3548 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL); 3549 LocalContext context; 3550 context->Global()->Set(v8_str("k"), obj->NewInstance()); 3551 CompileRun( 3552 "k.foo = 'foo';" 3553 "k.bar = 'bar';" 3554 "k[2] = 2;" 3555 "k[4] = 4;"); 3556 CHECK(v8_compile("delete k.foo")->Run()->IsFalse()); 3557 CHECK(v8_compile("delete k.bar")->Run()->IsTrue()); 3558 3559 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo")); 3560 CHECK(v8_compile("k.bar")->Run()->IsUndefined()); 3561 3562 CHECK(v8_compile("delete k[2]")->Run()->IsFalse()); 3563 CHECK(v8_compile("delete k[4]")->Run()->IsTrue()); 3564 3565 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2)); 3566 CHECK(v8_compile("k[4]")->Run()->IsUndefined()); 3567 } 3568 3569 3570 static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) { 3571 ApiTestFuzzer::Fuzz(); 3572 if (name->Equals(v8_str("foo")) || 3573 name->Equals(v8_str("bar")) || 3574 name->Equals(v8_str("baz"))) { 3575 return v8::Undefined(); 3576 } 3577 return v8::Handle<Value>(); 3578 } 3579 3580 3581 static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) { 3582 ApiTestFuzzer::Fuzz(); 3583 if (index == 0 || index == 1) return v8::Undefined(); 3584 return v8::Handle<Value>(); 3585 } 3586 3587 3588 static v8::Handle<v8::Array> NamedEnum(const AccessorInfo&) { 3589 ApiTestFuzzer::Fuzz(); 3590 v8::Handle<v8::Array> result = v8::Array::New(3); 3591 result->Set(v8::Integer::New(0), v8_str("foo")); 3592 result->Set(v8::Integer::New(1), v8_str("bar")); 3593 result->Set(v8::Integer::New(2), v8_str("baz")); 3594 return result; 3595 } 3596 3597 3598 static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) { 3599 ApiTestFuzzer::Fuzz(); 3600 v8::Handle<v8::Array> result = v8::Array::New(2); 3601 result->Set(v8::Integer::New(0), v8_str("0")); 3602 result->Set(v8::Integer::New(1), v8_str("1")); 3603 return result; 3604 } 3605 3606 3607 THREADED_TEST(Enumerators) { 3608 v8::HandleScope scope; 3609 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 3610 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum); 3611 obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum); 3612 LocalContext context; 3613 context->Global()->Set(v8_str("k"), obj->NewInstance()); 3614 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 3615 "k[10] = 0;" 3616 "k.a = 0;" 3617 "k[5] = 0;" 3618 "k.b = 0;" 3619 "k[4294967295] = 0;" 3620 "k.c = 0;" 3621 "k[4294967296] = 0;" 3622 "k.d = 0;" 3623 "k[140000] = 0;" 3624 "k.e = 0;" 3625 "k[30000000000] = 0;" 3626 "k.f = 0;" 3627 "var result = [];" 3628 "for (var prop in k) {" 3629 " result.push(prop);" 3630 "}" 3631 "result")); 3632 // Check that we get all the property names returned including the 3633 // ones from the enumerators in the right order: indexed properties 3634 // in numerical order, indexed interceptor properties, named 3635 // properties in insertion order, named interceptor properties. 3636 // This order is not mandated by the spec, so this test is just 3637 // documenting our behavior. 3638 CHECK_EQ(17, result->Length()); 3639 // Indexed properties in numerical order. 3640 CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0))); 3641 CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1))); 3642 CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2))); 3643 CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3))); 3644 // Indexed interceptor properties in the order they are returned 3645 // from the enumerator interceptor. 3646 CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4))); 3647 CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5))); 3648 // Named properties in insertion order. 3649 CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6))); 3650 CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7))); 3651 CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8))); 3652 CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9))); 3653 CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10))); 3654 CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11))); 3655 CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12))); 3656 CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13))); 3657 // Named interceptor properties. 3658 CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14))); 3659 CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15))); 3660 CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16))); 3661 } 3662 3663 3664 int p_getter_count; 3665 int p_getter_count2; 3666 3667 3668 static v8::Handle<Value> PGetter(Local<String> name, const AccessorInfo& info) { 3669 ApiTestFuzzer::Fuzz(); 3670 p_getter_count++; 3671 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 3672 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 3673 if (name->Equals(v8_str("p1"))) { 3674 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 3675 } else if (name->Equals(v8_str("p2"))) { 3676 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 3677 } else if (name->Equals(v8_str("p3"))) { 3678 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 3679 } else if (name->Equals(v8_str("p4"))) { 3680 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 3681 } 3682 return v8::Undefined(); 3683 } 3684 3685 3686 static void RunHolderTest(v8::Handle<v8::ObjectTemplate> obj) { 3687 ApiTestFuzzer::Fuzz(); 3688 LocalContext context; 3689 context->Global()->Set(v8_str("o1"), obj->NewInstance()); 3690 CompileRun( 3691 "o1.__proto__ = { };" 3692 "var o2 = { __proto__: o1 };" 3693 "var o3 = { __proto__: o2 };" 3694 "var o4 = { __proto__: o3 };" 3695 "for (var i = 0; i < 10; i++) o4.p4;" 3696 "for (var i = 0; i < 10; i++) o3.p3;" 3697 "for (var i = 0; i < 10; i++) o2.p2;" 3698 "for (var i = 0; i < 10; i++) o1.p1;"); 3699 } 3700 3701 3702 static v8::Handle<Value> PGetter2(Local<String> name, 3703 const AccessorInfo& info) { 3704 ApiTestFuzzer::Fuzz(); 3705 p_getter_count2++; 3706 v8::Handle<v8::Object> global = Context::GetCurrent()->Global(); 3707 CHECK_EQ(info.Holder(), global->Get(v8_str("o1"))); 3708 if (name->Equals(v8_str("p1"))) { 3709 CHECK_EQ(info.This(), global->Get(v8_str("o1"))); 3710 } else if (name->Equals(v8_str("p2"))) { 3711 CHECK_EQ(info.This(), global->Get(v8_str("o2"))); 3712 } else if (name->Equals(v8_str("p3"))) { 3713 CHECK_EQ(info.This(), global->Get(v8_str("o3"))); 3714 } else if (name->Equals(v8_str("p4"))) { 3715 CHECK_EQ(info.This(), global->Get(v8_str("o4"))); 3716 } 3717 return v8::Undefined(); 3718 } 3719 3720 3721 THREADED_TEST(GetterHolders) { 3722 v8::HandleScope scope; 3723 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 3724 obj->SetAccessor(v8_str("p1"), PGetter); 3725 obj->SetAccessor(v8_str("p2"), PGetter); 3726 obj->SetAccessor(v8_str("p3"), PGetter); 3727 obj->SetAccessor(v8_str("p4"), PGetter); 3728 p_getter_count = 0; 3729 RunHolderTest(obj); 3730 CHECK_EQ(40, p_getter_count); 3731 } 3732 3733 3734 THREADED_TEST(PreInterceptorHolders) { 3735 v8::HandleScope scope; 3736 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 3737 obj->SetNamedPropertyHandler(PGetter2); 3738 p_getter_count2 = 0; 3739 RunHolderTest(obj); 3740 CHECK_EQ(40, p_getter_count2); 3741 } 3742 3743 3744 THREADED_TEST(ObjectInstantiation) { 3745 v8::HandleScope scope; 3746 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 3747 templ->SetAccessor(v8_str("t"), PGetter2); 3748 LocalContext context; 3749 context->Global()->Set(v8_str("o"), templ->NewInstance()); 3750 for (int i = 0; i < 100; i++) { 3751 v8::HandleScope inner_scope; 3752 v8::Handle<v8::Object> obj = templ->NewInstance(); 3753 CHECK_NE(obj, context->Global()->Get(v8_str("o"))); 3754 context->Global()->Set(v8_str("o2"), obj); 3755 v8::Handle<Value> value = 3756 Script::Compile(v8_str("o.__proto__ === o2.__proto__"))->Run(); 3757 CHECK_EQ(v8::True(), value); 3758 context->Global()->Set(v8_str("o"), obj); 3759 } 3760 } 3761 3762 3763 THREADED_TEST(StringWrite) { 3764 v8::HandleScope scope; 3765 v8::Handle<String> str = v8_str("abcde"); 3766 3767 char buf[100]; 3768 int len; 3769 3770 memset(buf, 0x1, sizeof(buf)); 3771 len = str->WriteAscii(buf); 3772 CHECK_EQ(len, 5); 3773 CHECK_EQ(strncmp("abcde\0", buf, 6), 0); 3774 3775 memset(buf, 0x1, sizeof(buf)); 3776 len = str->WriteAscii(buf, 0, 4); 3777 CHECK_EQ(len, 4); 3778 CHECK_EQ(strncmp("abcd\1", buf, 5), 0); 3779 3780 memset(buf, 0x1, sizeof(buf)); 3781 len = str->WriteAscii(buf, 0, 5); 3782 CHECK_EQ(len, 5); 3783 CHECK_EQ(strncmp("abcde\1", buf, 6), 0); 3784 3785 memset(buf, 0x1, sizeof(buf)); 3786 len = str->WriteAscii(buf, 0, 6); 3787 CHECK_EQ(len, 5); 3788 CHECK_EQ(strncmp("abcde\0", buf, 6), 0); 3789 3790 memset(buf, 0x1, sizeof(buf)); 3791 len = str->WriteAscii(buf, 4, -1); 3792 CHECK_EQ(len, 1); 3793 CHECK_EQ(strncmp("e\0", buf, 2), 0); 3794 3795 memset(buf, 0x1, sizeof(buf)); 3796 len = str->WriteAscii(buf, 4, 6); 3797 CHECK_EQ(len, 1); 3798 CHECK_EQ(strncmp("e\0", buf, 2), 0); 3799 3800 memset(buf, 0x1, sizeof(buf)); 3801 len = str->WriteAscii(buf, 4, 1); 3802 CHECK_EQ(len, 1); 3803 CHECK_EQ(strncmp("e\1", buf, 2), 0); 3804 } 3805 3806 3807 THREADED_TEST(ToArrayIndex) { 3808 v8::HandleScope scope; 3809 LocalContext context; 3810 3811 v8::Handle<String> str = v8_str("42"); 3812 v8::Handle<v8::Uint32> index = str->ToArrayIndex(); 3813 CHECK(!index.IsEmpty()); 3814 CHECK_EQ(42.0, index->Uint32Value()); 3815 str = v8_str("42asdf"); 3816 index = str->ToArrayIndex(); 3817 CHECK(index.IsEmpty()); 3818 str = v8_str("-42"); 3819 index = str->ToArrayIndex(); 3820 CHECK(index.IsEmpty()); 3821 str = v8_str("4294967295"); 3822 index = str->ToArrayIndex(); 3823 CHECK(!index.IsEmpty()); 3824 CHECK_EQ(4294967295.0, index->Uint32Value()); 3825 v8::Handle<v8::Number> num = v8::Number::New(1); 3826 index = num->ToArrayIndex(); 3827 CHECK(!index.IsEmpty()); 3828 CHECK_EQ(1.0, index->Uint32Value()); 3829 num = v8::Number::New(-1); 3830 index = num->ToArrayIndex(); 3831 CHECK(index.IsEmpty()); 3832 v8::Handle<v8::Object> obj = v8::Object::New(); 3833 index = obj->ToArrayIndex(); 3834 CHECK(index.IsEmpty()); 3835 } 3836 3837 3838 THREADED_TEST(ErrorConstruction) { 3839 v8::HandleScope scope; 3840 LocalContext context; 3841 3842 v8::Handle<String> foo = v8_str("foo"); 3843 v8::Handle<String> message = v8_str("message"); 3844 v8::Handle<Value> range_error = v8::Exception::RangeError(foo); 3845 CHECK(range_error->IsObject()); 3846 v8::Handle<v8::Object> range_obj(v8::Handle<v8::Object>::Cast(range_error)); 3847 CHECK(v8::Handle<v8::Object>::Cast(range_error)->Get(message)->Equals(foo)); 3848 v8::Handle<Value> reference_error = v8::Exception::ReferenceError(foo); 3849 CHECK(reference_error->IsObject()); 3850 CHECK( 3851 v8::Handle<v8::Object>::Cast(reference_error)->Get(message)->Equals(foo)); 3852 v8::Handle<Value> syntax_error = v8::Exception::SyntaxError(foo); 3853 CHECK(syntax_error->IsObject()); 3854 CHECK(v8::Handle<v8::Object>::Cast(syntax_error)->Get(message)->Equals(foo)); 3855 v8::Handle<Value> type_error = v8::Exception::TypeError(foo); 3856 CHECK(type_error->IsObject()); 3857 CHECK(v8::Handle<v8::Object>::Cast(type_error)->Get(message)->Equals(foo)); 3858 v8::Handle<Value> error = v8::Exception::Error(foo); 3859 CHECK(error->IsObject()); 3860 CHECK(v8::Handle<v8::Object>::Cast(error)->Get(message)->Equals(foo)); 3861 } 3862 3863 3864 static v8::Handle<Value> YGetter(Local<String> name, const AccessorInfo& info) { 3865 ApiTestFuzzer::Fuzz(); 3866 return v8_num(10); 3867 } 3868 3869 3870 static void YSetter(Local<String> name, 3871 Local<Value> value, 3872 const AccessorInfo& info) { 3873 if (info.This()->Has(name)) { 3874 info.This()->Delete(name); 3875 } 3876 info.This()->Set(name, value); 3877 } 3878 3879 3880 THREADED_TEST(DeleteAccessor) { 3881 v8::HandleScope scope; 3882 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(); 3883 obj->SetAccessor(v8_str("y"), YGetter, YSetter); 3884 LocalContext context; 3885 v8::Handle<v8::Object> holder = obj->NewInstance(); 3886 context->Global()->Set(v8_str("holder"), holder); 3887 v8::Handle<Value> result = CompileRun( 3888 "holder.y = 11; holder.y = 12; holder.y"); 3889 CHECK_EQ(12, result->Uint32Value()); 3890 } 3891 3892 3893 THREADED_TEST(TypeSwitch) { 3894 v8::HandleScope scope; 3895 v8::Handle<v8::FunctionTemplate> templ1 = v8::FunctionTemplate::New(); 3896 v8::Handle<v8::FunctionTemplate> templ2 = v8::FunctionTemplate::New(); 3897 v8::Handle<v8::FunctionTemplate> templ3 = v8::FunctionTemplate::New(); 3898 v8::Handle<v8::FunctionTemplate> templs[3] = { templ1, templ2, templ3 }; 3899 v8::Handle<v8::TypeSwitch> type_switch = v8::TypeSwitch::New(3, templs); 3900 LocalContext context; 3901 v8::Handle<v8::Object> obj0 = v8::Object::New(); 3902 v8::Handle<v8::Object> obj1 = templ1->GetFunction()->NewInstance(); 3903 v8::Handle<v8::Object> obj2 = templ2->GetFunction()->NewInstance(); 3904 v8::Handle<v8::Object> obj3 = templ3->GetFunction()->NewInstance(); 3905 for (int i = 0; i < 10; i++) { 3906 CHECK_EQ(0, type_switch->match(obj0)); 3907 CHECK_EQ(1, type_switch->match(obj1)); 3908 CHECK_EQ(2, type_switch->match(obj2)); 3909 CHECK_EQ(3, type_switch->match(obj3)); 3910 CHECK_EQ(3, type_switch->match(obj3)); 3911 CHECK_EQ(2, type_switch->match(obj2)); 3912 CHECK_EQ(1, type_switch->match(obj1)); 3913 CHECK_EQ(0, type_switch->match(obj0)); 3914 } 3915 } 3916 3917 3918 // For use within the TestSecurityHandler() test. 3919 static bool g_security_callback_result = false; 3920 static bool NamedSecurityTestCallback(Local<v8::Object> global, 3921 Local<Value> name, 3922 v8::AccessType type, 3923 Local<Value> data) { 3924 // Always allow read access. 3925 if (type == v8::ACCESS_GET) 3926 return true; 3927 3928 // Sometimes allow other access. 3929 return g_security_callback_result; 3930 } 3931 3932 3933 static bool IndexedSecurityTestCallback(Local<v8::Object> global, 3934 uint32_t key, 3935 v8::AccessType type, 3936 Local<Value> data) { 3937 // Always allow read access. 3938 if (type == v8::ACCESS_GET) 3939 return true; 3940 3941 // Sometimes allow other access. 3942 return g_security_callback_result; 3943 } 3944 3945 3946 static int trouble_nesting = 0; 3947 static v8::Handle<Value> TroubleCallback(const v8::Arguments& args) { 3948 ApiTestFuzzer::Fuzz(); 3949 trouble_nesting++; 3950 3951 // Call a JS function that throws an uncaught exception. 3952 Local<v8::Object> arg_this = Context::GetCurrent()->Global(); 3953 Local<Value> trouble_callee = (trouble_nesting == 3) ? 3954 arg_this->Get(v8_str("trouble_callee")) : 3955 arg_this->Get(v8_str("trouble_caller")); 3956 CHECK(trouble_callee->IsFunction()); 3957 return Function::Cast(*trouble_callee)->Call(arg_this, 0, NULL); 3958 } 3959 3960 3961 static int report_count = 0; 3962 static void ApiUncaughtExceptionTestListener(v8::Handle<v8::Message>, 3963 v8::Handle<Value>) { 3964 report_count++; 3965 } 3966 3967 3968 // Counts uncaught exceptions, but other tests running in parallel 3969 // also have uncaught exceptions. 3970 TEST(ApiUncaughtException) { 3971 report_count = 0; 3972 v8::HandleScope scope; 3973 LocalContext env; 3974 v8::V8::AddMessageListener(ApiUncaughtExceptionTestListener); 3975 3976 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 3977 v8::Local<v8::Object> global = env->Global(); 3978 global->Set(v8_str("trouble"), fun->GetFunction()); 3979 3980 Script::Compile(v8_str("function trouble_callee() {" 3981 " var x = null;" 3982 " return x.foo;" 3983 "};" 3984 "function trouble_caller() {" 3985 " trouble();" 3986 "};"))->Run(); 3987 Local<Value> trouble = global->Get(v8_str("trouble")); 3988 CHECK(trouble->IsFunction()); 3989 Local<Value> trouble_callee = global->Get(v8_str("trouble_callee")); 3990 CHECK(trouble_callee->IsFunction()); 3991 Local<Value> trouble_caller = global->Get(v8_str("trouble_caller")); 3992 CHECK(trouble_caller->IsFunction()); 3993 Function::Cast(*trouble_caller)->Call(global, 0, NULL); 3994 CHECK_EQ(1, report_count); 3995 v8::V8::RemoveMessageListeners(ApiUncaughtExceptionTestListener); 3996 } 3997 3998 static const char* script_resource_name = "ExceptionInNativeScript.js"; 3999 static void ExceptionInNativeScriptTestListener(v8::Handle<v8::Message> message, 4000 v8::Handle<Value>) { 4001 v8::Handle<v8::Value> name_val = message->GetScriptResourceName(); 4002 CHECK(!name_val.IsEmpty() && name_val->IsString()); 4003 v8::String::AsciiValue name(message->GetScriptResourceName()); 4004 CHECK_EQ(script_resource_name, *name); 4005 CHECK_EQ(3, message->GetLineNumber()); 4006 v8::String::AsciiValue source_line(message->GetSourceLine()); 4007 CHECK_EQ(" new o.foo();", *source_line); 4008 } 4009 4010 TEST(ExceptionInNativeScript) { 4011 v8::HandleScope scope; 4012 LocalContext env; 4013 v8::V8::AddMessageListener(ExceptionInNativeScriptTestListener); 4014 4015 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(TroubleCallback); 4016 v8::Local<v8::Object> global = env->Global(); 4017 global->Set(v8_str("trouble"), fun->GetFunction()); 4018 4019 Script::Compile(v8_str("function trouble() {\n" 4020 " var o = {};\n" 4021 " new o.foo();\n" 4022 "};"), v8::String::New(script_resource_name))->Run(); 4023 Local<Value> trouble = global->Get(v8_str("trouble")); 4024 CHECK(trouble->IsFunction()); 4025 Function::Cast(*trouble)->Call(global, 0, NULL); 4026 v8::V8::RemoveMessageListeners(ExceptionInNativeScriptTestListener); 4027 } 4028 4029 4030 TEST(CompilationErrorUsingTryCatchHandler) { 4031 v8::HandleScope scope; 4032 LocalContext env; 4033 v8::TryCatch try_catch; 4034 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile.")); 4035 CHECK_NE(NULL, *try_catch.Exception()); 4036 CHECK(try_catch.HasCaught()); 4037 } 4038 4039 4040 TEST(TryCatchFinallyUsingTryCatchHandler) { 4041 v8::HandleScope scope; 4042 LocalContext env; 4043 v8::TryCatch try_catch; 4044 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run(); 4045 CHECK(!try_catch.HasCaught()); 4046 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run(); 4047 CHECK(try_catch.HasCaught()); 4048 try_catch.Reset(); 4049 Script::Compile(v8_str("(function() {" 4050 "try { throw ''; } finally { return; }" 4051 "})()"))->Run(); 4052 CHECK(!try_catch.HasCaught()); 4053 Script::Compile(v8_str("(function()" 4054 " { try { throw ''; } finally { throw 0; }" 4055 "})()"))->Run(); 4056 CHECK(try_catch.HasCaught()); 4057 } 4058 4059 4060 // SecurityHandler can't be run twice 4061 TEST(SecurityHandler) { 4062 v8::HandleScope scope0; 4063 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 4064 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback, 4065 IndexedSecurityTestCallback); 4066 // Create an environment 4067 v8::Persistent<Context> context0 = 4068 Context::New(NULL, global_template); 4069 context0->Enter(); 4070 4071 v8::Handle<v8::Object> global0 = context0->Global(); 4072 v8::Handle<Script> script0 = v8_compile("foo = 111"); 4073 script0->Run(); 4074 global0->Set(v8_str("0"), v8_num(999)); 4075 v8::Handle<Value> foo0 = global0->Get(v8_str("foo")); 4076 CHECK_EQ(111, foo0->Int32Value()); 4077 v8::Handle<Value> z0 = global0->Get(v8_str("0")); 4078 CHECK_EQ(999, z0->Int32Value()); 4079 4080 // Create another environment, should fail security checks. 4081 v8::HandleScope scope1; 4082 4083 v8::Persistent<Context> context1 = 4084 Context::New(NULL, global_template); 4085 context1->Enter(); 4086 4087 v8::Handle<v8::Object> global1 = context1->Global(); 4088 global1->Set(v8_str("othercontext"), global0); 4089 // This set will fail the security check. 4090 v8::Handle<Script> script1 = 4091 v8_compile("othercontext.foo = 222; othercontext[0] = 888;"); 4092 script1->Run(); 4093 // This read will pass the security check. 4094 v8::Handle<Value> foo1 = global0->Get(v8_str("foo")); 4095 CHECK_EQ(111, foo1->Int32Value()); 4096 // This read will pass the security check. 4097 v8::Handle<Value> z1 = global0->Get(v8_str("0")); 4098 CHECK_EQ(999, z1->Int32Value()); 4099 4100 // Create another environment, should pass security checks. 4101 { g_security_callback_result = true; // allow security handler to pass. 4102 v8::HandleScope scope2; 4103 LocalContext context2; 4104 v8::Handle<v8::Object> global2 = context2->Global(); 4105 global2->Set(v8_str("othercontext"), global0); 4106 v8::Handle<Script> script2 = 4107 v8_compile("othercontext.foo = 333; othercontext[0] = 888;"); 4108 script2->Run(); 4109 v8::Handle<Value> foo2 = global0->Get(v8_str("foo")); 4110 CHECK_EQ(333, foo2->Int32Value()); 4111 v8::Handle<Value> z2 = global0->Get(v8_str("0")); 4112 CHECK_EQ(888, z2->Int32Value()); 4113 } 4114 4115 context1->Exit(); 4116 context1.Dispose(); 4117 4118 context0->Exit(); 4119 context0.Dispose(); 4120 } 4121 4122 4123 THREADED_TEST(SecurityChecks) { 4124 v8::HandleScope handle_scope; 4125 LocalContext env1; 4126 v8::Persistent<Context> env2 = Context::New(); 4127 4128 Local<Value> foo = v8_str("foo"); 4129 Local<Value> bar = v8_str("bar"); 4130 4131 // Set to the same domain. 4132 env1->SetSecurityToken(foo); 4133 4134 // Create a function in env1. 4135 Script::Compile(v8_str("spy=function(){return spy;}"))->Run(); 4136 Local<Value> spy = env1->Global()->Get(v8_str("spy")); 4137 CHECK(spy->IsFunction()); 4138 4139 // Create another function accessing global objects. 4140 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run(); 4141 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2")); 4142 CHECK(spy2->IsFunction()); 4143 4144 // Switch to env2 in the same domain and invoke spy on env2. 4145 { 4146 env2->SetSecurityToken(foo); 4147 // Enter env2 4148 Context::Scope scope_env2(env2); 4149 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL); 4150 CHECK(result->IsFunction()); 4151 } 4152 4153 { 4154 env2->SetSecurityToken(bar); 4155 Context::Scope scope_env2(env2); 4156 4157 // Call cross_domain_call, it should throw an exception 4158 v8::TryCatch try_catch; 4159 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL); 4160 CHECK(try_catch.HasCaught()); 4161 } 4162 4163 env2.Dispose(); 4164 } 4165 4166 4167 // Regression test case for issue 1183439. 4168 THREADED_TEST(SecurityChecksForPrototypeChain) { 4169 v8::HandleScope scope; 4170 LocalContext current; 4171 v8::Persistent<Context> other = Context::New(); 4172 4173 // Change context to be able to get to the Object function in the 4174 // other context without hitting the security checks. 4175 v8::Local<Value> other_object; 4176 { Context::Scope scope(other); 4177 other_object = other->Global()->Get(v8_str("Object")); 4178 other->Global()->Set(v8_num(42), v8_num(87)); 4179 } 4180 4181 current->Global()->Set(v8_str("other"), other->Global()); 4182 CHECK(v8_compile("other")->Run()->Equals(other->Global())); 4183 4184 // Make sure the security check fails here and we get an undefined 4185 // result instead of getting the Object function. Repeat in a loop 4186 // to make sure to exercise the IC code. 4187 v8::Local<Script> access_other0 = v8_compile("other.Object"); 4188 v8::Local<Script> access_other1 = v8_compile("other[42]"); 4189 for (int i = 0; i < 5; i++) { 4190 CHECK(!access_other0->Run()->Equals(other_object)); 4191 CHECK(access_other0->Run()->IsUndefined()); 4192 CHECK(!access_other1->Run()->Equals(v8_num(87))); 4193 CHECK(access_other1->Run()->IsUndefined()); 4194 } 4195 4196 // Create an object that has 'other' in its prototype chain and make 4197 // sure we cannot access the Object function indirectly through 4198 // that. Repeat in a loop to make sure to exercise the IC code. 4199 v8_compile("function F() { };" 4200 "F.prototype = other;" 4201 "var f = new F();")->Run(); 4202 v8::Local<Script> access_f0 = v8_compile("f.Object"); 4203 v8::Local<Script> access_f1 = v8_compile("f[42]"); 4204 for (int j = 0; j < 5; j++) { 4205 CHECK(!access_f0->Run()->Equals(other_object)); 4206 CHECK(access_f0->Run()->IsUndefined()); 4207 CHECK(!access_f1->Run()->Equals(v8_num(87))); 4208 CHECK(access_f1->Run()->IsUndefined()); 4209 } 4210 4211 // Now it gets hairy: Set the prototype for the other global object 4212 // to be the current global object. The prototype chain for 'f' now 4213 // goes through 'other' but ends up in the current global object. 4214 { Context::Scope scope(other); 4215 other->Global()->Set(v8_str("__proto__"), current->Global()); 4216 } 4217 // Set a named and an index property on the current global 4218 // object. To force the lookup to go through the other global object, 4219 // the properties must not exist in the other global object. 4220 current->Global()->Set(v8_str("foo"), v8_num(100)); 4221 current->Global()->Set(v8_num(99), v8_num(101)); 4222 // Try to read the properties from f and make sure that the access 4223 // gets stopped by the security checks on the other global object. 4224 Local<Script> access_f2 = v8_compile("f.foo"); 4225 Local<Script> access_f3 = v8_compile("f[99]"); 4226 for (int k = 0; k < 5; k++) { 4227 CHECK(!access_f2->Run()->Equals(v8_num(100))); 4228 CHECK(access_f2->Run()->IsUndefined()); 4229 CHECK(!access_f3->Run()->Equals(v8_num(101))); 4230 CHECK(access_f3->Run()->IsUndefined()); 4231 } 4232 other.Dispose(); 4233 } 4234 4235 4236 THREADED_TEST(CrossDomainDelete) { 4237 v8::HandleScope handle_scope; 4238 LocalContext env1; 4239 v8::Persistent<Context> env2 = Context::New(); 4240 4241 Local<Value> foo = v8_str("foo"); 4242 Local<Value> bar = v8_str("bar"); 4243 4244 // Set to the same domain. 4245 env1->SetSecurityToken(foo); 4246 env2->SetSecurityToken(foo); 4247 4248 env1->Global()->Set(v8_str("prop"), v8_num(3)); 4249 env2->Global()->Set(v8_str("env1"), env1->Global()); 4250 4251 // Change env2 to a different domain and delete env1.prop. 4252 env2->SetSecurityToken(bar); 4253 { 4254 Context::Scope scope_env2(env2); 4255 Local<Value> result = 4256 Script::Compile(v8_str("delete env1.prop"))->Run(); 4257 CHECK(result->IsFalse()); 4258 } 4259 4260 // Check that env1.prop still exists. 4261 Local<Value> v = env1->Global()->Get(v8_str("prop")); 4262 CHECK(v->IsNumber()); 4263 CHECK_EQ(3, v->Int32Value()); 4264 4265 env2.Dispose(); 4266 } 4267 4268 4269 THREADED_TEST(CrossDomainIsPropertyEnumerable) { 4270 v8::HandleScope handle_scope; 4271 LocalContext env1; 4272 v8::Persistent<Context> env2 = Context::New(); 4273 4274 Local<Value> foo = v8_str("foo"); 4275 Local<Value> bar = v8_str("bar"); 4276 4277 // Set to the same domain. 4278 env1->SetSecurityToken(foo); 4279 env2->SetSecurityToken(foo); 4280 4281 env1->Global()->Set(v8_str("prop"), v8_num(3)); 4282 env2->Global()->Set(v8_str("env1"), env1->Global()); 4283 4284 // env1.prop is enumerable in env2. 4285 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')"); 4286 { 4287 Context::Scope scope_env2(env2); 4288 Local<Value> result = Script::Compile(test)->Run(); 4289 CHECK(result->IsTrue()); 4290 } 4291 4292 // Change env2 to a different domain and test again. 4293 env2->SetSecurityToken(bar); 4294 { 4295 Context::Scope scope_env2(env2); 4296 Local<Value> result = Script::Compile(test)->Run(); 4297 CHECK(result->IsFalse()); 4298 } 4299 4300 env2.Dispose(); 4301 } 4302 4303 4304 THREADED_TEST(CrossDomainForIn) { 4305 v8::HandleScope handle_scope; 4306 LocalContext env1; 4307 v8::Persistent<Context> env2 = Context::New(); 4308 4309 Local<Value> foo = v8_str("foo"); 4310 Local<Value> bar = v8_str("bar"); 4311 4312 // Set to the same domain. 4313 env1->SetSecurityToken(foo); 4314 env2->SetSecurityToken(foo); 4315 4316 env1->Global()->Set(v8_str("prop"), v8_num(3)); 4317 env2->Global()->Set(v8_str("env1"), env1->Global()); 4318 4319 // Change env2 to a different domain and set env1's global object 4320 // as the __proto__ of an object in env2 and enumerate properties 4321 // in for-in. It shouldn't enumerate properties on env1's global 4322 // object. 4323 env2->SetSecurityToken(bar); 4324 { 4325 Context::Scope scope_env2(env2); 4326 Local<Value> result = 4327 CompileRun("(function(){var obj = {'__proto__':env1};" 4328 "for (var p in obj)" 4329 " if (p == 'prop') return false;" 4330 "return true;})()"); 4331 CHECK(result->IsTrue()); 4332 } 4333 env2.Dispose(); 4334 } 4335 4336 4337 TEST(ContextDetachGlobal) { 4338 v8::HandleScope handle_scope; 4339 LocalContext env1; 4340 v8::Persistent<Context> env2 = Context::New(); 4341 4342 Local<v8::Object> global1 = env1->Global(); 4343 4344 Local<Value> foo = v8_str("foo"); 4345 4346 // Set to the same domain. 4347 env1->SetSecurityToken(foo); 4348 env2->SetSecurityToken(foo); 4349 4350 // Enter env2 4351 env2->Enter(); 4352 4353 // Create a function in env2 and add a reference to it in env1. 4354 Local<v8::Object> global2 = env2->Global(); 4355 global2->Set(v8_str("prop"), v8::Integer::New(1)); 4356 CompileRun("function getProp() {return prop;}"); 4357 4358 env1->Global()->Set(v8_str("getProp"), 4359 global2->Get(v8_str("getProp"))); 4360 4361 // Detach env2's global, and reuse the global object of env2 4362 env2->Exit(); 4363 env2->DetachGlobal(); 4364 // env2 has a new global object. 4365 CHECK(!env2->Global()->Equals(global2)); 4366 4367 v8::Persistent<Context> env3 = 4368 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 4369 env3->SetSecurityToken(v8_str("bar")); 4370 env3->Enter(); 4371 4372 Local<v8::Object> global3 = env3->Global(); 4373 CHECK_EQ(global2, global3); 4374 CHECK(global3->Get(v8_str("prop"))->IsUndefined()); 4375 CHECK(global3->Get(v8_str("getProp"))->IsUndefined()); 4376 global3->Set(v8_str("prop"), v8::Integer::New(-1)); 4377 global3->Set(v8_str("prop2"), v8::Integer::New(2)); 4378 env3->Exit(); 4379 4380 // Call getProp in env1, and it should return the value 1 4381 { 4382 Local<Value> get_prop = global1->Get(v8_str("getProp")); 4383 CHECK(get_prop->IsFunction()); 4384 v8::TryCatch try_catch; 4385 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL); 4386 CHECK(!try_catch.HasCaught()); 4387 CHECK_EQ(1, r->Int32Value()); 4388 } 4389 4390 // Check that env3 is not accessible from env1 4391 { 4392 Local<Value> r = global3->Get(v8_str("prop2")); 4393 CHECK(r->IsUndefined()); 4394 } 4395 4396 env2.Dispose(); 4397 env3.Dispose(); 4398 } 4399 4400 4401 TEST(DetachAndReattachGlobal) { 4402 v8::HandleScope scope; 4403 LocalContext env1; 4404 4405 // Create second environment. 4406 v8::Persistent<Context> env2 = Context::New(); 4407 4408 Local<Value> foo = v8_str("foo"); 4409 4410 // Set same security token for env1 and env2. 4411 env1->SetSecurityToken(foo); 4412 env2->SetSecurityToken(foo); 4413 4414 // Create a property on the global object in env2. 4415 { 4416 v8::Context::Scope scope(env2); 4417 env2->Global()->Set(v8_str("p"), v8::Integer::New(42)); 4418 } 4419 4420 // Create a reference to env2 global from env1 global. 4421 env1->Global()->Set(v8_str("other"), env2->Global()); 4422 4423 // Check that we have access to other.p in env2 from env1. 4424 Local<Value> result = CompileRun("other.p"); 4425 CHECK(result->IsInt32()); 4426 CHECK_EQ(42, result->Int32Value()); 4427 4428 // Hold on to global from env2 and detach global from env2. 4429 Local<v8::Object> global2 = env2->Global(); 4430 env2->DetachGlobal(); 4431 4432 // Check that the global has been detached. No other.p property can 4433 // be found. 4434 result = CompileRun("other.p"); 4435 CHECK(result->IsUndefined()); 4436 4437 // Reuse global2 for env3. 4438 v8::Persistent<Context> env3 = 4439 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 4440 CHECK_EQ(global2, env3->Global()); 4441 4442 // Start by using the same security token for env3 as for env1 and env2. 4443 env3->SetSecurityToken(foo); 4444 4445 // Create a property on the global object in env3. 4446 { 4447 v8::Context::Scope scope(env3); 4448 env3->Global()->Set(v8_str("p"), v8::Integer::New(24)); 4449 } 4450 4451 // Check that other.p is now the property in env3 and that we have access. 4452 result = CompileRun("other.p"); 4453 CHECK(result->IsInt32()); 4454 CHECK_EQ(24, result->Int32Value()); 4455 4456 // Change security token for env3 to something different from env1 and env2. 4457 env3->SetSecurityToken(v8_str("bar")); 4458 4459 // Check that we do not have access to other.p in env1. |other| is now 4460 // the global object for env3 which has a different security token, 4461 // so access should be blocked. 4462 result = CompileRun("other.p"); 4463 CHECK(result->IsUndefined()); 4464 4465 // Detach the global for env3 and reattach it to env2. 4466 env3->DetachGlobal(); 4467 env2->ReattachGlobal(global2); 4468 4469 // Check that we have access to other.p again in env1. |other| is now 4470 // the global object for env2 which has the same security token as env1. 4471 result = CompileRun("other.p"); 4472 CHECK(result->IsInt32()); 4473 CHECK_EQ(42, result->Int32Value()); 4474 4475 env2.Dispose(); 4476 env3.Dispose(); 4477 } 4478 4479 4480 static bool NamedAccessBlocker(Local<v8::Object> global, 4481 Local<Value> name, 4482 v8::AccessType type, 4483 Local<Value> data) { 4484 return Context::GetCurrent()->Global()->Equals(global); 4485 } 4486 4487 4488 static bool IndexedAccessBlocker(Local<v8::Object> global, 4489 uint32_t key, 4490 v8::AccessType type, 4491 Local<Value> data) { 4492 return Context::GetCurrent()->Global()->Equals(global); 4493 } 4494 4495 4496 static int g_echo_value = -1; 4497 static v8::Handle<Value> EchoGetter(Local<String> name, 4498 const AccessorInfo& info) { 4499 return v8_num(g_echo_value); 4500 } 4501 4502 4503 static void EchoSetter(Local<String> name, 4504 Local<Value> value, 4505 const AccessorInfo&) { 4506 if (value->IsNumber()) 4507 g_echo_value = value->Int32Value(); 4508 } 4509 4510 4511 static v8::Handle<Value> UnreachableGetter(Local<String> name, 4512 const AccessorInfo& info) { 4513 CHECK(false); // This function should not be called.. 4514 return v8::Undefined(); 4515 } 4516 4517 4518 static void UnreachableSetter(Local<String>, Local<Value>, 4519 const AccessorInfo&) { 4520 CHECK(false); // This function should nto be called. 4521 } 4522 4523 4524 THREADED_TEST(AccessControl) { 4525 v8::HandleScope handle_scope; 4526 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 4527 4528 global_template->SetAccessCheckCallbacks(NamedAccessBlocker, 4529 IndexedAccessBlocker); 4530 4531 // Add an accessor accessible by cross-domain JS code. 4532 global_template->SetAccessor( 4533 v8_str("accessible_prop"), 4534 EchoGetter, EchoSetter, 4535 v8::Handle<Value>(), 4536 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); 4537 4538 // Add an accessor that is not accessible by cross-domain JS code. 4539 global_template->SetAccessor(v8_str("blocked_prop"), 4540 UnreachableGetter, UnreachableSetter, 4541 v8::Handle<Value>(), 4542 v8::DEFAULT); 4543 4544 // Create an environment 4545 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 4546 context0->Enter(); 4547 4548 v8::Handle<v8::Object> global0 = context0->Global(); 4549 4550 v8::HandleScope scope1; 4551 4552 v8::Persistent<Context> context1 = Context::New(); 4553 context1->Enter(); 4554 4555 v8::Handle<v8::Object> global1 = context1->Global(); 4556 global1->Set(v8_str("other"), global0); 4557 4558 v8::Handle<Value> value; 4559 4560 // Access blocked property 4561 value = v8_compile("other.blocked_prop = 1")->Run(); 4562 value = v8_compile("other.blocked_prop")->Run(); 4563 CHECK(value->IsUndefined()); 4564 4565 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run(); 4566 CHECK(value->IsFalse()); 4567 4568 // Access accessible property 4569 value = v8_compile("other.accessible_prop = 3")->Run(); 4570 CHECK(value->IsNumber()); 4571 CHECK_EQ(3, value->Int32Value()); 4572 CHECK_EQ(3, g_echo_value); 4573 4574 value = v8_compile("other.accessible_prop")->Run(); 4575 CHECK(value->IsNumber()); 4576 CHECK_EQ(3, value->Int32Value()); 4577 4578 value = 4579 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run(); 4580 CHECK(value->IsTrue()); 4581 4582 // Enumeration doesn't enumerate accessors from inaccessible objects in 4583 // the prototype chain even if the accessors are in themselves accessible. 4584 Local<Value> result = 4585 CompileRun("(function(){var obj = {'__proto__':other};" 4586 "for (var p in obj)" 4587 " if (p == 'accessible_prop' || p == 'blocked_prop') {" 4588 " return false;" 4589 " }" 4590 "return true;})()"); 4591 CHECK(result->IsTrue()); 4592 4593 context1->Exit(); 4594 context0->Exit(); 4595 context1.Dispose(); 4596 context0.Dispose(); 4597 } 4598 4599 4600 static bool GetOwnPropertyNamesNamedBlocker(Local<v8::Object> global, 4601 Local<Value> name, 4602 v8::AccessType type, 4603 Local<Value> data) { 4604 return false; 4605 } 4606 4607 4608 static bool GetOwnPropertyNamesIndexedBlocker(Local<v8::Object> global, 4609 uint32_t key, 4610 v8::AccessType type, 4611 Local<Value> data) { 4612 return false; 4613 } 4614 4615 4616 THREADED_TEST(AccessControlGetOwnPropertyNames) { 4617 v8::HandleScope handle_scope; 4618 v8::Handle<v8::ObjectTemplate> obj_template = v8::ObjectTemplate::New(); 4619 4620 obj_template->Set(v8_str("x"), v8::Integer::New(42)); 4621 obj_template->SetAccessCheckCallbacks(GetOwnPropertyNamesNamedBlocker, 4622 GetOwnPropertyNamesIndexedBlocker); 4623 4624 // Create an environment 4625 v8::Persistent<Context> context0 = Context::New(NULL, obj_template); 4626 context0->Enter(); 4627 4628 v8::Handle<v8::Object> global0 = context0->Global(); 4629 4630 v8::HandleScope scope1; 4631 4632 v8::Persistent<Context> context1 = Context::New(); 4633 context1->Enter(); 4634 4635 v8::Handle<v8::Object> global1 = context1->Global(); 4636 global1->Set(v8_str("other"), global0); 4637 global1->Set(v8_str("object"), obj_template->NewInstance()); 4638 4639 v8::Handle<Value> value; 4640 4641 // Attempt to get the property names of the other global object and 4642 // of an object that requires access checks. Accessing the other 4643 // global object should be blocked by access checks on the global 4644 // proxy object. Accessing the object that requires access checks 4645 // is blocked by the access checks on the object itself. 4646 value = CompileRun("Object.getOwnPropertyNames(other).length == 0"); 4647 CHECK(value->IsTrue()); 4648 4649 value = CompileRun("Object.getOwnPropertyNames(object).length == 0"); 4650 CHECK(value->IsTrue()); 4651 4652 context1->Exit(); 4653 context0->Exit(); 4654 context1.Dispose(); 4655 context0.Dispose(); 4656 } 4657 4658 4659 static v8::Handle<Value> ConstTenGetter(Local<String> name, 4660 const AccessorInfo& info) { 4661 return v8_num(10); 4662 } 4663 4664 4665 THREADED_TEST(CrossDomainAccessors) { 4666 v8::HandleScope handle_scope; 4667 4668 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(); 4669 4670 v8::Handle<v8::ObjectTemplate> global_template = 4671 func_template->InstanceTemplate(); 4672 4673 v8::Handle<v8::ObjectTemplate> proto_template = 4674 func_template->PrototypeTemplate(); 4675 4676 // Add an accessor to proto that's accessible by cross-domain JS code. 4677 proto_template->SetAccessor(v8_str("accessible"), 4678 ConstTenGetter, 0, 4679 v8::Handle<Value>(), 4680 v8::ALL_CAN_READ); 4681 4682 // Add an accessor that is not accessible by cross-domain JS code. 4683 global_template->SetAccessor(v8_str("unreachable"), 4684 UnreachableGetter, 0, 4685 v8::Handle<Value>(), 4686 v8::DEFAULT); 4687 4688 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 4689 context0->Enter(); 4690 4691 Local<v8::Object> global = context0->Global(); 4692 // Add a normal property that shadows 'accessible' 4693 global->Set(v8_str("accessible"), v8_num(11)); 4694 4695 // Enter a new context. 4696 v8::HandleScope scope1; 4697 v8::Persistent<Context> context1 = Context::New(); 4698 context1->Enter(); 4699 4700 v8::Handle<v8::Object> global1 = context1->Global(); 4701 global1->Set(v8_str("other"), global); 4702 4703 // Should return 10, instead of 11 4704 v8::Handle<Value> value = v8_compile("other.accessible")->Run(); 4705 CHECK(value->IsNumber()); 4706 CHECK_EQ(10, value->Int32Value()); 4707 4708 value = v8_compile("other.unreachable")->Run(); 4709 CHECK(value->IsUndefined()); 4710 4711 context1->Exit(); 4712 context0->Exit(); 4713 context1.Dispose(); 4714 context0.Dispose(); 4715 } 4716 4717 4718 static int named_access_count = 0; 4719 static int indexed_access_count = 0; 4720 4721 static bool NamedAccessCounter(Local<v8::Object> global, 4722 Local<Value> name, 4723 v8::AccessType type, 4724 Local<Value> data) { 4725 named_access_count++; 4726 return true; 4727 } 4728 4729 4730 static bool IndexedAccessCounter(Local<v8::Object> global, 4731 uint32_t key, 4732 v8::AccessType type, 4733 Local<Value> data) { 4734 indexed_access_count++; 4735 return true; 4736 } 4737 4738 4739 // This one is too easily disturbed by other tests. 4740 TEST(AccessControlIC) { 4741 named_access_count = 0; 4742 indexed_access_count = 0; 4743 4744 v8::HandleScope handle_scope; 4745 4746 // Create an environment. 4747 v8::Persistent<Context> context0 = Context::New(); 4748 context0->Enter(); 4749 4750 // Create an object that requires access-check functions to be 4751 // called for cross-domain access. 4752 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 4753 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 4754 IndexedAccessCounter); 4755 Local<v8::Object> object = object_template->NewInstance(); 4756 4757 v8::HandleScope scope1; 4758 4759 // Create another environment. 4760 v8::Persistent<Context> context1 = Context::New(); 4761 context1->Enter(); 4762 4763 // Make easy access to the object from the other environment. 4764 v8::Handle<v8::Object> global1 = context1->Global(); 4765 global1->Set(v8_str("obj"), object); 4766 4767 v8::Handle<Value> value; 4768 4769 // Check that the named access-control function is called every time. 4770 CompileRun("function testProp(obj) {" 4771 " for (var i = 0; i < 10; i++) obj.prop = 1;" 4772 " for (var j = 0; j < 10; j++) obj.prop;" 4773 " return obj.prop" 4774 "}"); 4775 value = CompileRun("testProp(obj)"); 4776 CHECK(value->IsNumber()); 4777 CHECK_EQ(1, value->Int32Value()); 4778 CHECK_EQ(21, named_access_count); 4779 4780 // Check that the named access-control function is called every time. 4781 CompileRun("var p = 'prop';" 4782 "function testKeyed(obj) {" 4783 " for (var i = 0; i < 10; i++) obj[p] = 1;" 4784 " for (var j = 0; j < 10; j++) obj[p];" 4785 " return obj[p];" 4786 "}"); 4787 // Use obj which requires access checks. No inline caching is used 4788 // in that case. 4789 value = CompileRun("testKeyed(obj)"); 4790 CHECK(value->IsNumber()); 4791 CHECK_EQ(1, value->Int32Value()); 4792 CHECK_EQ(42, named_access_count); 4793 // Force the inline caches into generic state and try again. 4794 CompileRun("testKeyed({ a: 0 })"); 4795 CompileRun("testKeyed({ b: 0 })"); 4796 value = CompileRun("testKeyed(obj)"); 4797 CHECK(value->IsNumber()); 4798 CHECK_EQ(1, value->Int32Value()); 4799 CHECK_EQ(63, named_access_count); 4800 4801 // Check that the indexed access-control function is called every time. 4802 CompileRun("function testIndexed(obj) {" 4803 " for (var i = 0; i < 10; i++) obj[0] = 1;" 4804 " for (var j = 0; j < 10; j++) obj[0];" 4805 " return obj[0]" 4806 "}"); 4807 value = CompileRun("testIndexed(obj)"); 4808 CHECK(value->IsNumber()); 4809 CHECK_EQ(1, value->Int32Value()); 4810 CHECK_EQ(21, indexed_access_count); 4811 // Force the inline caches into generic state. 4812 CompileRun("testIndexed(new Array(1))"); 4813 // Test that the indexed access check is called. 4814 value = CompileRun("testIndexed(obj)"); 4815 CHECK(value->IsNumber()); 4816 CHECK_EQ(1, value->Int32Value()); 4817 CHECK_EQ(42, indexed_access_count); 4818 4819 // Check that the named access check is called when invoking 4820 // functions on an object that requires access checks. 4821 CompileRun("obj.f = function() {}"); 4822 CompileRun("function testCallNormal(obj) {" 4823 " for (var i = 0; i < 10; i++) obj.f();" 4824 "}"); 4825 CompileRun("testCallNormal(obj)"); 4826 CHECK_EQ(74, named_access_count); 4827 4828 // Force obj into slow case. 4829 value = CompileRun("delete obj.prop"); 4830 CHECK(value->BooleanValue()); 4831 // Force inline caches into dictionary probing mode. 4832 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);"); 4833 // Test that the named access check is called. 4834 value = CompileRun("testProp(obj);"); 4835 CHECK(value->IsNumber()); 4836 CHECK_EQ(1, value->Int32Value()); 4837 CHECK_EQ(96, named_access_count); 4838 4839 // Force the call inline cache into dictionary probing mode. 4840 CompileRun("o.f = function() {}; testCallNormal(o)"); 4841 // Test that the named access check is still called for each 4842 // invocation of the function. 4843 value = CompileRun("testCallNormal(obj)"); 4844 CHECK_EQ(106, named_access_count); 4845 4846 context1->Exit(); 4847 context0->Exit(); 4848 context1.Dispose(); 4849 context0.Dispose(); 4850 } 4851 4852 4853 static bool NamedAccessFlatten(Local<v8::Object> global, 4854 Local<Value> name, 4855 v8::AccessType type, 4856 Local<Value> data) { 4857 char buf[100]; 4858 int len; 4859 4860 CHECK(name->IsString()); 4861 4862 memset(buf, 0x1, sizeof(buf)); 4863 len = Local<String>::Cast(name)->WriteAscii(buf); 4864 CHECK_EQ(4, len); 4865 4866 uint16_t buf2[100]; 4867 4868 memset(buf, 0x1, sizeof(buf)); 4869 len = Local<String>::Cast(name)->Write(buf2); 4870 CHECK_EQ(4, len); 4871 4872 return true; 4873 } 4874 4875 4876 static bool IndexedAccessFlatten(Local<v8::Object> global, 4877 uint32_t key, 4878 v8::AccessType type, 4879 Local<Value> data) { 4880 return true; 4881 } 4882 4883 4884 // Regression test. In access checks, operations that may cause 4885 // garbage collection are not allowed. It used to be the case that 4886 // using the Write operation on a string could cause a garbage 4887 // collection due to flattening of the string. This is no longer the 4888 // case. 4889 THREADED_TEST(AccessControlFlatten) { 4890 named_access_count = 0; 4891 indexed_access_count = 0; 4892 4893 v8::HandleScope handle_scope; 4894 4895 // Create an environment. 4896 v8::Persistent<Context> context0 = Context::New(); 4897 context0->Enter(); 4898 4899 // Create an object that requires access-check functions to be 4900 // called for cross-domain access. 4901 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 4902 object_template->SetAccessCheckCallbacks(NamedAccessFlatten, 4903 IndexedAccessFlatten); 4904 Local<v8::Object> object = object_template->NewInstance(); 4905 4906 v8::HandleScope scope1; 4907 4908 // Create another environment. 4909 v8::Persistent<Context> context1 = Context::New(); 4910 context1->Enter(); 4911 4912 // Make easy access to the object from the other environment. 4913 v8::Handle<v8::Object> global1 = context1->Global(); 4914 global1->Set(v8_str("obj"), object); 4915 4916 v8::Handle<Value> value; 4917 4918 value = v8_compile("var p = 'as' + 'df';")->Run(); 4919 value = v8_compile("obj[p];")->Run(); 4920 4921 context1->Exit(); 4922 context0->Exit(); 4923 context1.Dispose(); 4924 context0.Dispose(); 4925 } 4926 4927 4928 static v8::Handle<Value> AccessControlNamedGetter( 4929 Local<String>, const AccessorInfo&) { 4930 return v8::Integer::New(42); 4931 } 4932 4933 4934 static v8::Handle<Value> AccessControlNamedSetter( 4935 Local<String>, Local<Value> value, const AccessorInfo&) { 4936 return value; 4937 } 4938 4939 4940 static v8::Handle<Value> AccessControlIndexedGetter( 4941 uint32_t index, 4942 const AccessorInfo& info) { 4943 return v8_num(42); 4944 } 4945 4946 4947 static v8::Handle<Value> AccessControlIndexedSetter( 4948 uint32_t, Local<Value> value, const AccessorInfo&) { 4949 return value; 4950 } 4951 4952 4953 THREADED_TEST(AccessControlInterceptorIC) { 4954 named_access_count = 0; 4955 indexed_access_count = 0; 4956 4957 v8::HandleScope handle_scope; 4958 4959 // Create an environment. 4960 v8::Persistent<Context> context0 = Context::New(); 4961 context0->Enter(); 4962 4963 // Create an object that requires access-check functions to be 4964 // called for cross-domain access. The object also has interceptors 4965 // interceptor. 4966 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 4967 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 4968 IndexedAccessCounter); 4969 object_template->SetNamedPropertyHandler(AccessControlNamedGetter, 4970 AccessControlNamedSetter); 4971 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter, 4972 AccessControlIndexedSetter); 4973 Local<v8::Object> object = object_template->NewInstance(); 4974 4975 v8::HandleScope scope1; 4976 4977 // Create another environment. 4978 v8::Persistent<Context> context1 = Context::New(); 4979 context1->Enter(); 4980 4981 // Make easy access to the object from the other environment. 4982 v8::Handle<v8::Object> global1 = context1->Global(); 4983 global1->Set(v8_str("obj"), object); 4984 4985 v8::Handle<Value> value; 4986 4987 // Check that the named access-control function is called every time 4988 // eventhough there is an interceptor on the object. 4989 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run(); 4990 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;" 4991 "obj.x")->Run(); 4992 CHECK(value->IsNumber()); 4993 CHECK_EQ(42, value->Int32Value()); 4994 CHECK_EQ(21, named_access_count); 4995 4996 value = v8_compile("var p = 'x';")->Run(); 4997 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run(); 4998 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];" 4999 "obj[p]")->Run(); 5000 CHECK(value->IsNumber()); 5001 CHECK_EQ(42, value->Int32Value()); 5002 CHECK_EQ(42, named_access_count); 5003 5004 // Check that the indexed access-control function is called every 5005 // time eventhough there is an interceptor on the object. 5006 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run(); 5007 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];" 5008 "obj[0]")->Run(); 5009 CHECK(value->IsNumber()); 5010 CHECK_EQ(42, value->Int32Value()); 5011 CHECK_EQ(21, indexed_access_count); 5012 5013 context1->Exit(); 5014 context0->Exit(); 5015 context1.Dispose(); 5016 context0.Dispose(); 5017 } 5018 5019 5020 THREADED_TEST(Version) { 5021 v8::V8::GetVersion(); 5022 } 5023 5024 5025 static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) { 5026 ApiTestFuzzer::Fuzz(); 5027 return v8_num(12); 5028 } 5029 5030 5031 THREADED_TEST(InstanceProperties) { 5032 v8::HandleScope handle_scope; 5033 LocalContext context; 5034 5035 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5036 Local<ObjectTemplate> instance = t->InstanceTemplate(); 5037 5038 instance->Set(v8_str("x"), v8_num(42)); 5039 instance->Set(v8_str("f"), 5040 v8::FunctionTemplate::New(InstanceFunctionCallback)); 5041 5042 Local<Value> o = t->GetFunction()->NewInstance(); 5043 5044 context->Global()->Set(v8_str("i"), o); 5045 Local<Value> value = Script::Compile(v8_str("i.x"))->Run(); 5046 CHECK_EQ(42, value->Int32Value()); 5047 5048 value = Script::Compile(v8_str("i.f()"))->Run(); 5049 CHECK_EQ(12, value->Int32Value()); 5050 } 5051 5052 5053 static v8::Handle<Value> 5054 GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) { 5055 ApiTestFuzzer::Fuzz(); 5056 return v8::Handle<Value>(); 5057 } 5058 5059 5060 THREADED_TEST(GlobalObjectInstanceProperties) { 5061 v8::HandleScope handle_scope; 5062 5063 Local<Value> global_object; 5064 5065 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5066 t->InstanceTemplate()->SetNamedPropertyHandler( 5067 GlobalObjectInstancePropertiesGet); 5068 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 5069 instance_template->Set(v8_str("x"), v8_num(42)); 5070 instance_template->Set(v8_str("f"), 5071 v8::FunctionTemplate::New(InstanceFunctionCallback)); 5072 5073 { 5074 LocalContext env(NULL, instance_template); 5075 // Hold on to the global object so it can be used again in another 5076 // environment initialization. 5077 global_object = env->Global(); 5078 5079 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 5080 CHECK_EQ(42, value->Int32Value()); 5081 value = Script::Compile(v8_str("f()"))->Run(); 5082 CHECK_EQ(12, value->Int32Value()); 5083 } 5084 5085 { 5086 // Create new environment reusing the global object. 5087 LocalContext env(NULL, instance_template, global_object); 5088 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 5089 CHECK_EQ(42, value->Int32Value()); 5090 value = Script::Compile(v8_str("f()"))->Run(); 5091 CHECK_EQ(12, value->Int32Value()); 5092 } 5093 } 5094 5095 5096 static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) { 5097 ApiTestFuzzer::Fuzz(); 5098 return v8_num(42); 5099 } 5100 5101 5102 static int shadow_y; 5103 static int shadow_y_setter_call_count; 5104 static int shadow_y_getter_call_count; 5105 5106 5107 static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) { 5108 shadow_y_setter_call_count++; 5109 shadow_y = 42; 5110 } 5111 5112 5113 static v8::Handle<Value> ShadowYGetter(Local<String> name, 5114 const AccessorInfo& info) { 5115 ApiTestFuzzer::Fuzz(); 5116 shadow_y_getter_call_count++; 5117 return v8_num(shadow_y); 5118 } 5119 5120 5121 static v8::Handle<Value> ShadowIndexedGet(uint32_t index, 5122 const AccessorInfo& info) { 5123 return v8::Handle<Value>(); 5124 } 5125 5126 5127 static v8::Handle<Value> ShadowNamedGet(Local<String> key, 5128 const AccessorInfo&) { 5129 return v8::Handle<Value>(); 5130 } 5131 5132 5133 THREADED_TEST(ShadowObject) { 5134 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0; 5135 v8::HandleScope handle_scope; 5136 5137 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New(); 5138 LocalContext context(NULL, global_template); 5139 5140 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5141 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet); 5142 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet); 5143 Local<ObjectTemplate> proto = t->PrototypeTemplate(); 5144 Local<ObjectTemplate> instance = t->InstanceTemplate(); 5145 5146 // Only allow calls of f on instances of t. 5147 Local<v8::Signature> signature = v8::Signature::New(t); 5148 proto->Set(v8_str("f"), 5149 v8::FunctionTemplate::New(ShadowFunctionCallback, 5150 Local<Value>(), 5151 signature)); 5152 proto->Set(v8_str("x"), v8_num(12)); 5153 5154 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter); 5155 5156 Local<Value> o = t->GetFunction()->NewInstance(); 5157 context->Global()->Set(v8_str("__proto__"), o); 5158 5159 Local<Value> value = 5160 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run(); 5161 CHECK(value->IsBoolean()); 5162 CHECK(!value->BooleanValue()); 5163 5164 value = Script::Compile(v8_str("x"))->Run(); 5165 CHECK_EQ(12, value->Int32Value()); 5166 5167 value = Script::Compile(v8_str("f()"))->Run(); 5168 CHECK_EQ(42, value->Int32Value()); 5169 5170 Script::Compile(v8_str("y = 42"))->Run(); 5171 CHECK_EQ(1, shadow_y_setter_call_count); 5172 value = Script::Compile(v8_str("y"))->Run(); 5173 CHECK_EQ(1, shadow_y_getter_call_count); 5174 CHECK_EQ(42, value->Int32Value()); 5175 } 5176 5177 5178 THREADED_TEST(HiddenPrototype) { 5179 v8::HandleScope handle_scope; 5180 LocalContext context; 5181 5182 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 5183 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 5184 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 5185 t1->SetHiddenPrototype(true); 5186 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 5187 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 5188 t2->SetHiddenPrototype(true); 5189 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 5190 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 5191 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 5192 5193 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 5194 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 5195 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 5196 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 5197 5198 // Setting the prototype on an object skips hidden prototypes. 5199 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5200 o0->Set(v8_str("__proto__"), o1); 5201 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5202 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5203 o0->Set(v8_str("__proto__"), o2); 5204 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5205 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5206 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5207 o0->Set(v8_str("__proto__"), o3); 5208 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5209 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5210 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5211 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 5212 5213 // Getting the prototype of o0 should get the first visible one 5214 // which is o3. Therefore, z should not be defined on the prototype 5215 // object. 5216 Local<Value> proto = o0->Get(v8_str("__proto__")); 5217 CHECK(proto->IsObject()); 5218 CHECK(Local<v8::Object>::Cast(proto)->Get(v8_str("z"))->IsUndefined()); 5219 } 5220 5221 5222 THREADED_TEST(SetPrototype) { 5223 v8::HandleScope handle_scope; 5224 LocalContext context; 5225 5226 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 5227 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 5228 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 5229 t1->SetHiddenPrototype(true); 5230 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 5231 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 5232 t2->SetHiddenPrototype(true); 5233 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 5234 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 5235 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 5236 5237 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 5238 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 5239 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 5240 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 5241 5242 // Setting the prototype on an object does not skip hidden prototypes. 5243 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5244 CHECK(o0->SetPrototype(o1)); 5245 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5246 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5247 CHECK(o1->SetPrototype(o2)); 5248 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5249 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5250 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5251 CHECK(o2->SetPrototype(o3)); 5252 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 5253 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 5254 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 5255 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 5256 5257 // Getting the prototype of o0 should get the first visible one 5258 // which is o3. Therefore, z should not be defined on the prototype 5259 // object. 5260 Local<Value> proto = o0->Get(v8_str("__proto__")); 5261 CHECK(proto->IsObject()); 5262 CHECK_EQ(v8::Handle<v8::Object>::Cast(proto), o3); 5263 5264 // However, Object::GetPrototype ignores hidden prototype. 5265 Local<Value> proto0 = o0->GetPrototype(); 5266 CHECK(proto0->IsObject()); 5267 CHECK_EQ(v8::Handle<v8::Object>::Cast(proto0), o1); 5268 5269 Local<Value> proto1 = o1->GetPrototype(); 5270 CHECK(proto1->IsObject()); 5271 CHECK_EQ(v8::Handle<v8::Object>::Cast(proto1), o2); 5272 5273 Local<Value> proto2 = o2->GetPrototype(); 5274 CHECK(proto2->IsObject()); 5275 CHECK_EQ(v8::Handle<v8::Object>::Cast(proto2), o3); 5276 } 5277 5278 5279 THREADED_TEST(SetPrototypeThrows) { 5280 v8::HandleScope handle_scope; 5281 LocalContext context; 5282 5283 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5284 5285 Local<v8::Object> o0 = t->GetFunction()->NewInstance(); 5286 Local<v8::Object> o1 = t->GetFunction()->NewInstance(); 5287 5288 CHECK(o0->SetPrototype(o1)); 5289 // If setting the prototype leads to the cycle, SetPrototype should 5290 // return false and keep VM in sane state. 5291 v8::TryCatch try_catch; 5292 CHECK(!o1->SetPrototype(o0)); 5293 CHECK(!try_catch.HasCaught()); 5294 ASSERT(!i::Top::has_pending_exception()); 5295 5296 CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value()); 5297 } 5298 5299 5300 THREADED_TEST(GetterSetterExceptions) { 5301 v8::HandleScope handle_scope; 5302 LocalContext context; 5303 CompileRun( 5304 "function Foo() { };" 5305 "function Throw() { throw 5; };" 5306 "var x = { };" 5307 "x.__defineSetter__('set', Throw);" 5308 "x.__defineGetter__('get', Throw);"); 5309 Local<v8::Object> x = 5310 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x"))); 5311 v8::TryCatch try_catch; 5312 x->Set(v8_str("set"), v8::Integer::New(8)); 5313 x->Get(v8_str("get")); 5314 x->Set(v8_str("set"), v8::Integer::New(8)); 5315 x->Get(v8_str("get")); 5316 x->Set(v8_str("set"), v8::Integer::New(8)); 5317 x->Get(v8_str("get")); 5318 x->Set(v8_str("set"), v8::Integer::New(8)); 5319 x->Get(v8_str("get")); 5320 } 5321 5322 5323 THREADED_TEST(Constructor) { 5324 v8::HandleScope handle_scope; 5325 LocalContext context; 5326 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 5327 templ->SetClassName(v8_str("Fun")); 5328 Local<Function> cons = templ->GetFunction(); 5329 context->Global()->Set(v8_str("Fun"), cons); 5330 Local<v8::Object> inst = cons->NewInstance(); 5331 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst); 5332 Local<Value> value = CompileRun("(new Fun()).constructor === Fun"); 5333 CHECK(value->BooleanValue()); 5334 } 5335 5336 THREADED_TEST(FunctionDescriptorException) { 5337 v8::HandleScope handle_scope; 5338 LocalContext context; 5339 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 5340 templ->SetClassName(v8_str("Fun")); 5341 Local<Function> cons = templ->GetFunction(); 5342 context->Global()->Set(v8_str("Fun"), cons); 5343 Local<Value> value = CompileRun( 5344 "function test() {" 5345 " try {" 5346 " (new Fun()).blah()" 5347 " } catch (e) {" 5348 " var str = String(e);" 5349 " if (str.indexOf('TypeError') == -1) return 1;" 5350 " if (str.indexOf('[object Fun]') != -1) return 2;" 5351 " if (str.indexOf('#<a Fun>') == -1) return 3;" 5352 " return 0;" 5353 " }" 5354 " return 4;" 5355 "}" 5356 "test();"); 5357 CHECK_EQ(0, value->Int32Value()); 5358 } 5359 5360 5361 THREADED_TEST(EvalAliasedDynamic) { 5362 v8::HandleScope scope; 5363 LocalContext current; 5364 5365 // Tests where aliased eval can only be resolved dynamically. 5366 Local<Script> script = 5367 Script::Compile(v8_str("function f(x) { " 5368 " var foo = 2;" 5369 " with (x) { return eval('foo'); }" 5370 "}" 5371 "foo = 0;" 5372 "result1 = f(new Object());" 5373 "result2 = f(this);" 5374 "var x = new Object();" 5375 "x.eval = function(x) { return 1; };" 5376 "result3 = f(x);")); 5377 script->Run(); 5378 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value()); 5379 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value()); 5380 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value()); 5381 5382 v8::TryCatch try_catch; 5383 script = 5384 Script::Compile(v8_str("function f(x) { " 5385 " var bar = 2;" 5386 " with (x) { return eval('bar'); }" 5387 "}" 5388 "f(this)")); 5389 script->Run(); 5390 CHECK(try_catch.HasCaught()); 5391 try_catch.Reset(); 5392 } 5393 5394 5395 THREADED_TEST(CrossEval) { 5396 v8::HandleScope scope; 5397 LocalContext other; 5398 LocalContext current; 5399 5400 Local<String> token = v8_str("<security token>"); 5401 other->SetSecurityToken(token); 5402 current->SetSecurityToken(token); 5403 5404 // Setup reference from current to other. 5405 current->Global()->Set(v8_str("other"), other->Global()); 5406 5407 // Check that new variables are introduced in other context. 5408 Local<Script> script = 5409 Script::Compile(v8_str("other.eval('var foo = 1234')")); 5410 script->Run(); 5411 Local<Value> foo = other->Global()->Get(v8_str("foo")); 5412 CHECK_EQ(1234, foo->Int32Value()); 5413 CHECK(!current->Global()->Has(v8_str("foo"))); 5414 5415 // Check that writing to non-existing properties introduces them in 5416 // the other context. 5417 script = 5418 Script::Compile(v8_str("other.eval('na = 1234')")); 5419 script->Run(); 5420 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value()); 5421 CHECK(!current->Global()->Has(v8_str("na"))); 5422 5423 // Check that global variables in current context are not visible in other 5424 // context. 5425 v8::TryCatch try_catch; 5426 script = 5427 Script::Compile(v8_str("var bar = 42; other.eval('bar');")); 5428 Local<Value> result = script->Run(); 5429 CHECK(try_catch.HasCaught()); 5430 try_catch.Reset(); 5431 5432 // Check that local variables in current context are not visible in other 5433 // context. 5434 script = 5435 Script::Compile(v8_str("(function() { " 5436 " var baz = 87;" 5437 " return other.eval('baz');" 5438 "})();")); 5439 result = script->Run(); 5440 CHECK(try_catch.HasCaught()); 5441 try_catch.Reset(); 5442 5443 // Check that global variables in the other environment are visible 5444 // when evaluting code. 5445 other->Global()->Set(v8_str("bis"), v8_num(1234)); 5446 script = Script::Compile(v8_str("other.eval('bis')")); 5447 CHECK_EQ(1234, script->Run()->Int32Value()); 5448 CHECK(!try_catch.HasCaught()); 5449 5450 // Check that the 'this' pointer points to the global object evaluating 5451 // code. 5452 other->Global()->Set(v8_str("t"), other->Global()); 5453 script = Script::Compile(v8_str("other.eval('this == t')")); 5454 result = script->Run(); 5455 CHECK(result->IsTrue()); 5456 CHECK(!try_catch.HasCaught()); 5457 5458 // Check that variables introduced in with-statement are not visible in 5459 // other context. 5460 script = 5461 Script::Compile(v8_str("with({x:2}){other.eval('x')}")); 5462 result = script->Run(); 5463 CHECK(try_catch.HasCaught()); 5464 try_catch.Reset(); 5465 5466 // Check that you cannot use 'eval.call' with another object than the 5467 // current global object. 5468 script = 5469 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')")); 5470 result = script->Run(); 5471 CHECK(try_catch.HasCaught()); 5472 } 5473 5474 5475 // Test that calling eval in a context which has been detached from 5476 // its global throws an exception. This behavior is consistent with 5477 // other JavaScript implementations. 5478 THREADED_TEST(EvalInDetachedGlobal) { 5479 v8::HandleScope scope; 5480 5481 v8::Persistent<Context> context0 = Context::New(); 5482 v8::Persistent<Context> context1 = Context::New(); 5483 5484 // Setup function in context0 that uses eval from context0. 5485 context0->Enter(); 5486 v8::Handle<v8::Value> fun = 5487 CompileRun("var x = 42;" 5488 "(function() {" 5489 " var e = eval;" 5490 " return function(s) { return e(s); }" 5491 "})()"); 5492 context0->Exit(); 5493 5494 // Put the function into context1 and call it before and after 5495 // detaching the global. Before detaching, the call succeeds and 5496 // after detaching and exception is thrown. 5497 context1->Enter(); 5498 context1->Global()->Set(v8_str("fun"), fun); 5499 v8::Handle<v8::Value> x_value = CompileRun("fun('x')"); 5500 CHECK_EQ(42, x_value->Int32Value()); 5501 context0->DetachGlobal(); 5502 v8::TryCatch catcher; 5503 x_value = CompileRun("fun('x')"); 5504 CHECK(x_value.IsEmpty()); 5505 CHECK(catcher.HasCaught()); 5506 context1->Exit(); 5507 5508 context1.Dispose(); 5509 context0.Dispose(); 5510 } 5511 5512 5513 THREADED_TEST(CrossLazyLoad) { 5514 v8::HandleScope scope; 5515 LocalContext other; 5516 LocalContext current; 5517 5518 Local<String> token = v8_str("<security token>"); 5519 other->SetSecurityToken(token); 5520 current->SetSecurityToken(token); 5521 5522 // Setup reference from current to other. 5523 current->Global()->Set(v8_str("other"), other->Global()); 5524 5525 // Trigger lazy loading in other context. 5526 Local<Script> script = 5527 Script::Compile(v8_str("other.eval('new Date(42)')")); 5528 Local<Value> value = script->Run(); 5529 CHECK_EQ(42.0, value->NumberValue()); 5530 } 5531 5532 5533 static v8::Handle<Value> call_as_function(const v8::Arguments& args) { 5534 ApiTestFuzzer::Fuzz(); 5535 if (args.IsConstructCall()) { 5536 if (args[0]->IsInt32()) { 5537 return v8_num(-args[0]->Int32Value()); 5538 } 5539 } 5540 5541 return args[0]; 5542 } 5543 5544 5545 // Test that a call handler can be set for objects which will allow 5546 // non-function objects created through the API to be called as 5547 // functions. 5548 THREADED_TEST(CallAsFunction) { 5549 v8::HandleScope scope; 5550 LocalContext context; 5551 5552 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 5553 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 5554 instance_template->SetCallAsFunctionHandler(call_as_function); 5555 Local<v8::Object> instance = t->GetFunction()->NewInstance(); 5556 context->Global()->Set(v8_str("obj"), instance); 5557 v8::TryCatch try_catch; 5558 Local<Value> value; 5559 CHECK(!try_catch.HasCaught()); 5560 5561 value = CompileRun("obj(42)"); 5562 CHECK(!try_catch.HasCaught()); 5563 CHECK_EQ(42, value->Int32Value()); 5564 5565 value = CompileRun("(function(o){return o(49)})(obj)"); 5566 CHECK(!try_catch.HasCaught()); 5567 CHECK_EQ(49, value->Int32Value()); 5568 5569 // test special case of call as function 5570 value = CompileRun("[obj]['0'](45)"); 5571 CHECK(!try_catch.HasCaught()); 5572 CHECK_EQ(45, value->Int32Value()); 5573 5574 value = CompileRun("obj.call = Function.prototype.call;" 5575 "obj.call(null, 87)"); 5576 CHECK(!try_catch.HasCaught()); 5577 CHECK_EQ(87, value->Int32Value()); 5578 5579 // Regression tests for bug #1116356: Calling call through call/apply 5580 // must work for non-function receivers. 5581 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])"; 5582 value = CompileRun(apply_99); 5583 CHECK(!try_catch.HasCaught()); 5584 CHECK_EQ(99, value->Int32Value()); 5585 5586 const char* call_17 = "Function.prototype.call.call(obj, this, 17)"; 5587 value = CompileRun(call_17); 5588 CHECK(!try_catch.HasCaught()); 5589 CHECK_EQ(17, value->Int32Value()); 5590 5591 // Check that the call-as-function handler can be called through 5592 // new. 5593 value = CompileRun("new obj(43)"); 5594 CHECK(!try_catch.HasCaught()); 5595 CHECK_EQ(-43, value->Int32Value()); 5596 } 5597 5598 5599 static int CountHandles() { 5600 return v8::HandleScope::NumberOfHandles(); 5601 } 5602 5603 5604 static int Recurse(int depth, int iterations) { 5605 v8::HandleScope scope; 5606 if (depth == 0) return CountHandles(); 5607 for (int i = 0; i < iterations; i++) { 5608 Local<v8::Number> n = v8::Integer::New(42); 5609 } 5610 return Recurse(depth - 1, iterations); 5611 } 5612 5613 5614 THREADED_TEST(HandleIteration) { 5615 static const int kIterations = 500; 5616 static const int kNesting = 200; 5617 CHECK_EQ(0, CountHandles()); 5618 { 5619 v8::HandleScope scope1; 5620 CHECK_EQ(0, CountHandles()); 5621 for (int i = 0; i < kIterations; i++) { 5622 Local<v8::Number> n = v8::Integer::New(42); 5623 CHECK_EQ(i + 1, CountHandles()); 5624 } 5625 5626 CHECK_EQ(kIterations, CountHandles()); 5627 { 5628 v8::HandleScope scope2; 5629 for (int j = 0; j < kIterations; j++) { 5630 Local<v8::Number> n = v8::Integer::New(42); 5631 CHECK_EQ(j + 1 + kIterations, CountHandles()); 5632 } 5633 } 5634 CHECK_EQ(kIterations, CountHandles()); 5635 } 5636 CHECK_EQ(0, CountHandles()); 5637 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations)); 5638 } 5639 5640 5641 static v8::Handle<Value> InterceptorHasOwnPropertyGetter( 5642 Local<String> name, 5643 const AccessorInfo& info) { 5644 ApiTestFuzzer::Fuzz(); 5645 return v8::Handle<Value>(); 5646 } 5647 5648 5649 THREADED_TEST(InterceptorHasOwnProperty) { 5650 v8::HandleScope scope; 5651 LocalContext context; 5652 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 5653 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 5654 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter); 5655 Local<Function> function = fun_templ->GetFunction(); 5656 context->Global()->Set(v8_str("constructor"), function); 5657 v8::Handle<Value> value = CompileRun( 5658 "var o = new constructor();" 5659 "o.hasOwnProperty('ostehaps');"); 5660 CHECK_EQ(false, value->BooleanValue()); 5661 value = CompileRun( 5662 "o.ostehaps = 42;" 5663 "o.hasOwnProperty('ostehaps');"); 5664 CHECK_EQ(true, value->BooleanValue()); 5665 value = CompileRun( 5666 "var p = new constructor();" 5667 "p.hasOwnProperty('ostehaps');"); 5668 CHECK_EQ(false, value->BooleanValue()); 5669 } 5670 5671 5672 static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC( 5673 Local<String> name, 5674 const AccessorInfo& info) { 5675 ApiTestFuzzer::Fuzz(); 5676 i::Heap::CollectAllGarbage(false); 5677 return v8::Handle<Value>(); 5678 } 5679 5680 5681 THREADED_TEST(InterceptorHasOwnPropertyCausingGC) { 5682 v8::HandleScope scope; 5683 LocalContext context; 5684 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 5685 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 5686 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC); 5687 Local<Function> function = fun_templ->GetFunction(); 5688 context->Global()->Set(v8_str("constructor"), function); 5689 // Let's first make some stuff so we can be sure to get a good GC. 5690 CompileRun( 5691 "function makestr(size) {" 5692 " switch (size) {" 5693 " case 1: return 'f';" 5694 " case 2: return 'fo';" 5695 " case 3: return 'foo';" 5696 " }" 5697 " return makestr(size >> 1) + makestr((size + 1) >> 1);" 5698 "}" 5699 "var x = makestr(12345);" 5700 "x = makestr(31415);" 5701 "x = makestr(23456);"); 5702 v8::Handle<Value> value = CompileRun( 5703 "var o = new constructor();" 5704 "o.__proto__ = new String(x);" 5705 "o.hasOwnProperty('ostehaps');"); 5706 CHECK_EQ(false, value->BooleanValue()); 5707 } 5708 5709 5710 typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property, 5711 const AccessorInfo& info); 5712 5713 5714 static void CheckInterceptorLoadIC(NamedPropertyGetter getter, 5715 const char* source, 5716 int expected) { 5717 v8::HandleScope scope; 5718 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5719 templ->SetNamedPropertyHandler(getter); 5720 LocalContext context; 5721 context->Global()->Set(v8_str("o"), templ->NewInstance()); 5722 v8::Handle<Value> value = CompileRun(source); 5723 CHECK_EQ(expected, value->Int32Value()); 5724 } 5725 5726 5727 static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name, 5728 const AccessorInfo& info) { 5729 ApiTestFuzzer::Fuzz(); 5730 CHECK(v8_str("x")->Equals(name)); 5731 return v8::Integer::New(42); 5732 } 5733 5734 5735 // This test should hit the load IC for the interceptor case. 5736 THREADED_TEST(InterceptorLoadIC) { 5737 CheckInterceptorLoadIC(InterceptorLoadICGetter, 5738 "var result = 0;" 5739 "for (var i = 0; i < 1000; i++) {" 5740 " result = o.x;" 5741 "}", 5742 42); 5743 } 5744 5745 5746 // Below go several tests which verify that JITing for various 5747 // configurations of interceptor and explicit fields works fine 5748 // (those cases are special cased to get better performance). 5749 5750 static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name, 5751 const AccessorInfo& info) { 5752 ApiTestFuzzer::Fuzz(); 5753 return v8_str("x")->Equals(name) 5754 ? v8::Integer::New(42) : v8::Handle<v8::Value>(); 5755 } 5756 5757 5758 THREADED_TEST(InterceptorLoadICWithFieldOnHolder) { 5759 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 5760 "var result = 0;" 5761 "o.y = 239;" 5762 "for (var i = 0; i < 1000; i++) {" 5763 " result = o.y;" 5764 "}", 5765 239); 5766 } 5767 5768 5769 THREADED_TEST(InterceptorLoadICWithSubstitutedProto) { 5770 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 5771 "var result = 0;" 5772 "o.__proto__ = { 'y': 239 };" 5773 "for (var i = 0; i < 1000; i++) {" 5774 " result = o.y + o.x;" 5775 "}", 5776 239 + 42); 5777 } 5778 5779 5780 THREADED_TEST(InterceptorLoadICWithPropertyOnProto) { 5781 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 5782 "var result = 0;" 5783 "o.__proto__.y = 239;" 5784 "for (var i = 0; i < 1000; i++) {" 5785 " result = o.y + o.x;" 5786 "}", 5787 239 + 42); 5788 } 5789 5790 5791 THREADED_TEST(InterceptorLoadICUndefined) { 5792 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 5793 "var result = 0;" 5794 "for (var i = 0; i < 1000; i++) {" 5795 " result = (o.y == undefined) ? 239 : 42;" 5796 "}", 5797 239); 5798 } 5799 5800 5801 THREADED_TEST(InterceptorLoadICWithOverride) { 5802 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 5803 "fst = new Object(); fst.__proto__ = o;" 5804 "snd = new Object(); snd.__proto__ = fst;" 5805 "var result1 = 0;" 5806 "for (var i = 0; i < 1000; i++) {" 5807 " result1 = snd.x;" 5808 "}" 5809 "fst.x = 239;" 5810 "var result = 0;" 5811 "for (var i = 0; i < 1000; i++) {" 5812 " result = snd.x;" 5813 "}" 5814 "result + result1", 5815 239 + 42); 5816 } 5817 5818 5819 // Test the case when we stored field into 5820 // a stub, but interceptor produced value on its own. 5821 THREADED_TEST(InterceptorLoadICFieldNotNeeded) { 5822 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 5823 "proto = new Object();" 5824 "o.__proto__ = proto;" 5825 "proto.x = 239;" 5826 "for (var i = 0; i < 1000; i++) {" 5827 " o.x;" 5828 // Now it should be ICed and keep a reference to x defined on proto 5829 "}" 5830 "var result = 0;" 5831 "for (var i = 0; i < 1000; i++) {" 5832 " result += o.x;" 5833 "}" 5834 "result;", 5835 42 * 1000); 5836 } 5837 5838 5839 // Test the case when we stored field into 5840 // a stub, but it got invalidated later on. 5841 THREADED_TEST(InterceptorLoadICInvalidatedField) { 5842 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 5843 "proto1 = new Object();" 5844 "proto2 = new Object();" 5845 "o.__proto__ = proto1;" 5846 "proto1.__proto__ = proto2;" 5847 "proto2.y = 239;" 5848 "for (var i = 0; i < 1000; i++) {" 5849 " o.y;" 5850 // Now it should be ICed and keep a reference to y defined on proto2 5851 "}" 5852 "proto1.y = 42;" 5853 "var result = 0;" 5854 "for (var i = 0; i < 1000; i++) {" 5855 " result += o.y;" 5856 "}" 5857 "result;", 5858 42 * 1000); 5859 } 5860 5861 5862 // Test the case when we stored field into 5863 // a stub, but it got invalidated later on due to override on 5864 // global object which is between interceptor and fields' holders. 5865 THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) { 5866 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 5867 "o.__proto__ = this;" // set a global to be a proto of o. 5868 "this.__proto__.y = 239;" 5869 "for (var i = 0; i < 10; i++) {" 5870 " if (o.y != 239) throw 'oops: ' + o.y;" 5871 // Now it should be ICed and keep a reference to y defined on field_holder. 5872 "}" 5873 "this.y = 42;" // Assign on a global. 5874 "var result = 0;" 5875 "for (var i = 0; i < 10; i++) {" 5876 " result += o.y;" 5877 "}" 5878 "result;", 5879 42 * 10); 5880 } 5881 5882 5883 static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) { 5884 ApiTestFuzzer::Fuzz(); 5885 return v8_num(239); 5886 } 5887 5888 5889 static void SetOnThis(Local<String> name, 5890 Local<Value> value, 5891 const AccessorInfo& info) { 5892 info.This()->ForceSet(name, value); 5893 } 5894 5895 5896 THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) { 5897 v8::HandleScope scope; 5898 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5899 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 5900 templ->SetAccessor(v8_str("y"), Return239); 5901 LocalContext context; 5902 context->Global()->Set(v8_str("o"), templ->NewInstance()); 5903 v8::Handle<Value> value = CompileRun( 5904 "var result = 0;" 5905 "for (var i = 0; i < 7; i++) {" 5906 " result = o.y;" 5907 "}"); 5908 CHECK_EQ(239, value->Int32Value()); 5909 } 5910 5911 5912 THREADED_TEST(InterceptorLoadICWithCallbackOnProto) { 5913 v8::HandleScope scope; 5914 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 5915 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 5916 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 5917 templ_p->SetAccessor(v8_str("y"), Return239); 5918 5919 LocalContext context; 5920 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 5921 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 5922 5923 v8::Handle<Value> value = CompileRun( 5924 "o.__proto__ = p;" 5925 "var result = 0;" 5926 "for (var i = 0; i < 7; i++) {" 5927 " result = o.x + o.y;" 5928 "}"); 5929 CHECK_EQ(239 + 42, value->Int32Value()); 5930 } 5931 5932 5933 THREADED_TEST(InterceptorLoadICForCallbackWithOverride) { 5934 v8::HandleScope scope; 5935 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5936 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 5937 templ->SetAccessor(v8_str("y"), Return239); 5938 5939 LocalContext context; 5940 context->Global()->Set(v8_str("o"), templ->NewInstance()); 5941 5942 v8::Handle<Value> value = CompileRun( 5943 "fst = new Object(); fst.__proto__ = o;" 5944 "snd = new Object(); snd.__proto__ = fst;" 5945 "var result1 = 0;" 5946 "for (var i = 0; i < 7; i++) {" 5947 " result1 = snd.x;" 5948 "}" 5949 "fst.x = 239;" 5950 "var result = 0;" 5951 "for (var i = 0; i < 7; i++) {" 5952 " result = snd.x;" 5953 "}" 5954 "result + result1"); 5955 CHECK_EQ(239 + 42, value->Int32Value()); 5956 } 5957 5958 5959 // Test the case when we stored callback into 5960 // a stub, but interceptor produced value on its own. 5961 THREADED_TEST(InterceptorLoadICCallbackNotNeeded) { 5962 v8::HandleScope scope; 5963 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 5964 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 5965 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 5966 templ_p->SetAccessor(v8_str("y"), Return239); 5967 5968 LocalContext context; 5969 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 5970 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 5971 5972 v8::Handle<Value> value = CompileRun( 5973 "o.__proto__ = p;" 5974 "for (var i = 0; i < 7; i++) {" 5975 " o.x;" 5976 // Now it should be ICed and keep a reference to x defined on p 5977 "}" 5978 "var result = 0;" 5979 "for (var i = 0; i < 7; i++) {" 5980 " result += o.x;" 5981 "}" 5982 "result"); 5983 CHECK_EQ(42 * 7, value->Int32Value()); 5984 } 5985 5986 5987 // Test the case when we stored callback into 5988 // a stub, but it got invalidated later on. 5989 THREADED_TEST(InterceptorLoadICInvalidatedCallback) { 5990 v8::HandleScope scope; 5991 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 5992 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 5993 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 5994 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 5995 5996 LocalContext context; 5997 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 5998 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 5999 6000 v8::Handle<Value> value = CompileRun( 6001 "inbetween = new Object();" 6002 "o.__proto__ = inbetween;" 6003 "inbetween.__proto__ = p;" 6004 "for (var i = 0; i < 10; i++) {" 6005 " o.y;" 6006 // Now it should be ICed and keep a reference to y defined on p 6007 "}" 6008 "inbetween.y = 42;" 6009 "var result = 0;" 6010 "for (var i = 0; i < 10; i++) {" 6011 " result += o.y;" 6012 "}" 6013 "result"); 6014 CHECK_EQ(42 * 10, value->Int32Value()); 6015 } 6016 6017 6018 // Test the case when we stored callback into 6019 // a stub, but it got invalidated later on due to override on 6020 // global object which is between interceptor and callbacks' holders. 6021 THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) { 6022 v8::HandleScope scope; 6023 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6024 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6025 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 6026 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 6027 6028 LocalContext context; 6029 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6030 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 6031 6032 v8::Handle<Value> value = CompileRun( 6033 "o.__proto__ = this;" 6034 "this.__proto__ = p;" 6035 "for (var i = 0; i < 10; i++) {" 6036 " if (o.y != 239) throw 'oops: ' + o.y;" 6037 // Now it should be ICed and keep a reference to y defined on p 6038 "}" 6039 "this.y = 42;" 6040 "var result = 0;" 6041 "for (var i = 0; i < 10; i++) {" 6042 " result += o.y;" 6043 "}" 6044 "result"); 6045 CHECK_EQ(42 * 10, value->Int32Value()); 6046 } 6047 6048 6049 static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name, 6050 const AccessorInfo& info) { 6051 ApiTestFuzzer::Fuzz(); 6052 CHECK(v8_str("x")->Equals(name)); 6053 return v8::Integer::New(0); 6054 } 6055 6056 6057 THREADED_TEST(InterceptorReturningZero) { 6058 CheckInterceptorLoadIC(InterceptorLoadICGetter0, 6059 "o.x == undefined ? 1 : 0", 6060 0); 6061 } 6062 6063 6064 static v8::Handle<Value> InterceptorStoreICSetter( 6065 Local<String> key, Local<Value> value, const AccessorInfo&) { 6066 CHECK(v8_str("x")->Equals(key)); 6067 CHECK_EQ(42, value->Int32Value()); 6068 return value; 6069 } 6070 6071 6072 // This test should hit the store IC for the interceptor case. 6073 THREADED_TEST(InterceptorStoreIC) { 6074 v8::HandleScope scope; 6075 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6076 templ->SetNamedPropertyHandler(InterceptorLoadICGetter, 6077 InterceptorStoreICSetter); 6078 LocalContext context; 6079 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6080 v8::Handle<Value> value = CompileRun( 6081 "for (var i = 0; i < 1000; i++) {" 6082 " o.x = 42;" 6083 "}"); 6084 } 6085 6086 6087 THREADED_TEST(InterceptorStoreICWithNoSetter) { 6088 v8::HandleScope scope; 6089 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6090 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 6091 LocalContext context; 6092 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6093 v8::Handle<Value> value = CompileRun( 6094 "for (var i = 0; i < 1000; i++) {" 6095 " o.y = 239;" 6096 "}" 6097 "42 + o.y"); 6098 CHECK_EQ(239 + 42, value->Int32Value()); 6099 } 6100 6101 6102 6103 6104 v8::Handle<Value> call_ic_function; 6105 v8::Handle<Value> call_ic_function2; 6106 v8::Handle<Value> call_ic_function3; 6107 6108 static v8::Handle<Value> InterceptorCallICGetter(Local<String> name, 6109 const AccessorInfo& info) { 6110 ApiTestFuzzer::Fuzz(); 6111 CHECK(v8_str("x")->Equals(name)); 6112 return call_ic_function; 6113 } 6114 6115 6116 // This test should hit the call IC for the interceptor case. 6117 THREADED_TEST(InterceptorCallIC) { 6118 v8::HandleScope scope; 6119 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6120 templ->SetNamedPropertyHandler(InterceptorCallICGetter); 6121 LocalContext context; 6122 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6123 call_ic_function = 6124 v8_compile("function f(x) { return x + 1; }; f")->Run(); 6125 v8::Handle<Value> value = CompileRun( 6126 "var result = 0;" 6127 "for (var i = 0; i < 1000; i++) {" 6128 " result = o.x(41);" 6129 "}"); 6130 CHECK_EQ(42, value->Int32Value()); 6131 } 6132 6133 6134 // This test checks that if interceptor doesn't provide 6135 // a value, we can fetch regular value. 6136 THREADED_TEST(InterceptorCallICSeesOthers) { 6137 v8::HandleScope scope; 6138 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6139 templ->SetNamedPropertyHandler(NoBlockGetterX); 6140 LocalContext context; 6141 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6142 v8::Handle<Value> value = CompileRun( 6143 "o.x = function f(x) { return x + 1; };" 6144 "var result = 0;" 6145 "for (var i = 0; i < 7; i++) {" 6146 " result = o.x(41);" 6147 "}"); 6148 CHECK_EQ(42, value->Int32Value()); 6149 } 6150 6151 6152 static v8::Handle<Value> call_ic_function4; 6153 static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name, 6154 const AccessorInfo& info) { 6155 ApiTestFuzzer::Fuzz(); 6156 CHECK(v8_str("x")->Equals(name)); 6157 return call_ic_function4; 6158 } 6159 6160 6161 // This test checks that if interceptor provides a function, 6162 // even if we cached shadowed variant, interceptor's function 6163 // is invoked 6164 THREADED_TEST(InterceptorCallICCacheableNotNeeded) { 6165 v8::HandleScope scope; 6166 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6167 templ->SetNamedPropertyHandler(InterceptorCallICGetter4); 6168 LocalContext context; 6169 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6170 call_ic_function4 = 6171 v8_compile("function f(x) { return x - 1; }; f")->Run(); 6172 v8::Handle<Value> value = CompileRun( 6173 "o.__proto__.x = function(x) { return x + 1; };" 6174 "var result = 0;" 6175 "for (var i = 0; i < 1000; i++) {" 6176 " result = o.x(42);" 6177 "}"); 6178 CHECK_EQ(41, value->Int32Value()); 6179 } 6180 6181 6182 // Test the case when we stored cacheable lookup into 6183 // a stub, but it got invalidated later on 6184 THREADED_TEST(InterceptorCallICInvalidatedCacheable) { 6185 v8::HandleScope scope; 6186 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6187 templ->SetNamedPropertyHandler(NoBlockGetterX); 6188 LocalContext context; 6189 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6190 v8::Handle<Value> value = CompileRun( 6191 "proto1 = new Object();" 6192 "proto2 = new Object();" 6193 "o.__proto__ = proto1;" 6194 "proto1.__proto__ = proto2;" 6195 "proto2.y = function(x) { return x + 1; };" 6196 // Invoke it many times to compile a stub 6197 "for (var i = 0; i < 7; i++) {" 6198 " o.y(42);" 6199 "}" 6200 "proto1.y = function(x) { return x - 1; };" 6201 "var result = 0;" 6202 "for (var i = 0; i < 7; i++) {" 6203 " result += o.y(42);" 6204 "}"); 6205 CHECK_EQ(41 * 7, value->Int32Value()); 6206 } 6207 6208 6209 static v8::Handle<Value> call_ic_function5; 6210 static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name, 6211 const AccessorInfo& info) { 6212 ApiTestFuzzer::Fuzz(); 6213 if (v8_str("x")->Equals(name)) 6214 return call_ic_function5; 6215 else 6216 return Local<Value>(); 6217 } 6218 6219 6220 // This test checks that if interceptor doesn't provide a function, 6221 // cached constant function is used 6222 THREADED_TEST(InterceptorCallICConstantFunctionUsed) { 6223 v8::HandleScope scope; 6224 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6225 templ->SetNamedPropertyHandler(NoBlockGetterX); 6226 LocalContext context; 6227 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6228 v8::Handle<Value> value = CompileRun( 6229 "function inc(x) { return x + 1; };" 6230 "inc(1);" 6231 "o.x = inc;" 6232 "var result = 0;" 6233 "for (var i = 0; i < 1000; i++) {" 6234 " result = o.x(42);" 6235 "}"); 6236 CHECK_EQ(43, value->Int32Value()); 6237 } 6238 6239 6240 // This test checks that if interceptor provides a function, 6241 // even if we cached constant function, interceptor's function 6242 // is invoked 6243 THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) { 6244 v8::HandleScope scope; 6245 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6246 templ->SetNamedPropertyHandler(InterceptorCallICGetter5); 6247 LocalContext context; 6248 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6249 call_ic_function5 = 6250 v8_compile("function f(x) { return x - 1; }; f")->Run(); 6251 v8::Handle<Value> value = CompileRun( 6252 "function inc(x) { return x + 1; };" 6253 "inc(1);" 6254 "o.x = inc;" 6255 "var result = 0;" 6256 "for (var i = 0; i < 1000; i++) {" 6257 " result = o.x(42);" 6258 "}"); 6259 CHECK_EQ(41, value->Int32Value()); 6260 } 6261 6262 6263 // Test the case when we stored constant function into 6264 // a stub, but it got invalidated later on 6265 THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) { 6266 v8::HandleScope scope; 6267 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6268 templ->SetNamedPropertyHandler(NoBlockGetterX); 6269 LocalContext context; 6270 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6271 v8::Handle<Value> value = CompileRun( 6272 "function inc(x) { return x + 1; };" 6273 "inc(1);" 6274 "proto1 = new Object();" 6275 "proto2 = new Object();" 6276 "o.__proto__ = proto1;" 6277 "proto1.__proto__ = proto2;" 6278 "proto2.y = inc;" 6279 // Invoke it many times to compile a stub 6280 "for (var i = 0; i < 7; i++) {" 6281 " o.y(42);" 6282 "}" 6283 "proto1.y = function(x) { return x - 1; };" 6284 "var result = 0;" 6285 "for (var i = 0; i < 7; i++) {" 6286 " result += o.y(42);" 6287 "}"); 6288 CHECK_EQ(41 * 7, value->Int32Value()); 6289 } 6290 6291 6292 // Test the case when we stored constant function into 6293 // a stub, but it got invalidated later on due to override on 6294 // global object which is between interceptor and constant function' holders. 6295 THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) { 6296 v8::HandleScope scope; 6297 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6298 templ->SetNamedPropertyHandler(NoBlockGetterX); 6299 LocalContext context; 6300 context->Global()->Set(v8_str("o"), templ->NewInstance()); 6301 v8::Handle<Value> value = CompileRun( 6302 "function inc(x) { return x + 1; };" 6303 "inc(1);" 6304 "o.__proto__ = this;" 6305 "this.__proto__.y = inc;" 6306 // Invoke it many times to compile a stub 6307 "for (var i = 0; i < 7; i++) {" 6308 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);" 6309 "}" 6310 "this.y = function(x) { return x - 1; };" 6311 "var result = 0;" 6312 "for (var i = 0; i < 7; i++) {" 6313 " result += o.y(42);" 6314 "}"); 6315 CHECK_EQ(41 * 7, value->Int32Value()); 6316 } 6317 6318 6319 // Test the case when actual function to call sits on global object. 6320 THREADED_TEST(InterceptorCallICCachedFromGlobal) { 6321 v8::HandleScope scope; 6322 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 6323 templ_o->SetNamedPropertyHandler(NoBlockGetterX); 6324 6325 LocalContext context; 6326 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 6327 6328 v8::Handle<Value> value = CompileRun( 6329 "try {" 6330 " o.__proto__ = this;" 6331 " for (var i = 0; i < 10; i++) {" 6332 " var v = o.parseFloat('239');" 6333 " if (v != 239) throw v;" 6334 // Now it should be ICed and keep a reference to parseFloat. 6335 " }" 6336 " var result = 0;" 6337 " for (var i = 0; i < 10; i++) {" 6338 " result += o.parseFloat('239');" 6339 " }" 6340 " result" 6341 "} catch(e) {" 6342 " e" 6343 "};"); 6344 CHECK_EQ(239 * 10, value->Int32Value()); 6345 } 6346 6347 static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name, 6348 const AccessorInfo& info) { 6349 ApiTestFuzzer::Fuzz(); 6350 int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data())); 6351 ++(*call_count); 6352 if ((*call_count) % 20 == 0) { 6353 v8::internal::Heap::CollectAllGarbage(true); 6354 } 6355 return v8::Handle<Value>(); 6356 } 6357 6358 static v8::Handle<Value> FastApiCallback_TrivialSignature( 6359 const v8::Arguments& args) { 6360 ApiTestFuzzer::Fuzz(); 6361 CHECK_EQ(args.This(), args.Holder()); 6362 CHECK(args.Data()->Equals(v8_str("method_data"))); 6363 return v8::Integer::New(args[0]->Int32Value() + 1); 6364 } 6365 6366 static v8::Handle<Value> FastApiCallback_SimpleSignature( 6367 const v8::Arguments& args) { 6368 ApiTestFuzzer::Fuzz(); 6369 CHECK_EQ(args.This()->GetPrototype(), args.Holder()); 6370 CHECK(args.Data()->Equals(v8_str("method_data"))); 6371 // Note, we're using HasRealNamedProperty instead of Has to avoid 6372 // invoking the interceptor again. 6373 CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo"))); 6374 return v8::Integer::New(args[0]->Int32Value() + 1); 6375 } 6376 6377 // Helper to maximize the odds of object moving. 6378 static void GenerateSomeGarbage() { 6379 CompileRun( 6380 "var garbage;" 6381 "for (var i = 0; i < 1000; i++) {" 6382 " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];" 6383 "}" 6384 "garbage = undefined;"); 6385 } 6386 6387 THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) { 6388 int interceptor_call_count = 0; 6389 v8::HandleScope scope; 6390 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6391 v8::Handle<v8::FunctionTemplate> method_templ = 6392 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 6393 v8_str("method_data"), 6394 v8::Handle<v8::Signature>()); 6395 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6396 proto_templ->Set(v8_str("method"), method_templ); 6397 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6398 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6399 NULL, NULL, NULL, NULL, 6400 v8::External::Wrap(&interceptor_call_count)); 6401 LocalContext context; 6402 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6403 GenerateSomeGarbage(); 6404 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6405 v8::Handle<Value> value = CompileRun( 6406 "var result = 0;" 6407 "for (var i = 0; i < 100; i++) {" 6408 " result = o.method(41);" 6409 "}"); 6410 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 6411 CHECK_EQ(100, interceptor_call_count); 6412 } 6413 6414 THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) { 6415 int interceptor_call_count = 0; 6416 v8::HandleScope scope; 6417 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6418 v8::Handle<v8::FunctionTemplate> method_templ = 6419 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6420 v8_str("method_data"), 6421 v8::Signature::New(fun_templ)); 6422 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6423 proto_templ->Set(v8_str("method"), method_templ); 6424 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6425 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6426 NULL, NULL, NULL, NULL, 6427 v8::External::Wrap(&interceptor_call_count)); 6428 LocalContext context; 6429 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6430 GenerateSomeGarbage(); 6431 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6432 v8::Handle<Value> value = CompileRun( 6433 "o.foo = 17;" 6434 "var receiver = {};" 6435 "receiver.__proto__ = o;" 6436 "var result = 0;" 6437 "for (var i = 0; i < 100; i++) {" 6438 " result = receiver.method(41);" 6439 "}"); 6440 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 6441 CHECK_EQ(100, interceptor_call_count); 6442 } 6443 6444 THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) { 6445 int interceptor_call_count = 0; 6446 v8::HandleScope scope; 6447 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6448 v8::Handle<v8::FunctionTemplate> method_templ = 6449 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6450 v8_str("method_data"), 6451 v8::Signature::New(fun_templ)); 6452 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6453 proto_templ->Set(v8_str("method"), method_templ); 6454 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6455 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6456 NULL, NULL, NULL, NULL, 6457 v8::External::Wrap(&interceptor_call_count)); 6458 LocalContext context; 6459 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6460 GenerateSomeGarbage(); 6461 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6462 v8::Handle<Value> value = CompileRun( 6463 "o.foo = 17;" 6464 "var receiver = {};" 6465 "receiver.__proto__ = o;" 6466 "var result = 0;" 6467 "var saved_result = 0;" 6468 "for (var i = 0; i < 100; i++) {" 6469 " result = receiver.method(41);" 6470 " if (i == 50) {" 6471 " saved_result = result;" 6472 " receiver = {method: function(x) { return x - 1 }};" 6473 " }" 6474 "}"); 6475 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 6476 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 6477 CHECK_GE(interceptor_call_count, 50); 6478 } 6479 6480 THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) { 6481 int interceptor_call_count = 0; 6482 v8::HandleScope scope; 6483 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6484 v8::Handle<v8::FunctionTemplate> method_templ = 6485 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6486 v8_str("method_data"), 6487 v8::Signature::New(fun_templ)); 6488 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6489 proto_templ->Set(v8_str("method"), method_templ); 6490 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6491 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6492 NULL, NULL, NULL, NULL, 6493 v8::External::Wrap(&interceptor_call_count)); 6494 LocalContext context; 6495 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6496 GenerateSomeGarbage(); 6497 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6498 v8::Handle<Value> value = CompileRun( 6499 "o.foo = 17;" 6500 "var receiver = {};" 6501 "receiver.__proto__ = o;" 6502 "var result = 0;" 6503 "var saved_result = 0;" 6504 "for (var i = 0; i < 100; i++) {" 6505 " result = receiver.method(41);" 6506 " if (i == 50) {" 6507 " saved_result = result;" 6508 " o.method = function(x) { return x - 1 };" 6509 " }" 6510 "}"); 6511 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 6512 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 6513 CHECK_GE(interceptor_call_count, 50); 6514 } 6515 6516 THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) { 6517 int interceptor_call_count = 0; 6518 v8::HandleScope scope; 6519 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6520 v8::Handle<v8::FunctionTemplate> method_templ = 6521 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6522 v8_str("method_data"), 6523 v8::Signature::New(fun_templ)); 6524 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6525 proto_templ->Set(v8_str("method"), method_templ); 6526 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6527 templ->SetNamedPropertyHandler(InterceptorCallICFastApi, 6528 NULL, NULL, NULL, NULL, 6529 v8::External::Wrap(&interceptor_call_count)); 6530 LocalContext context; 6531 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6532 GenerateSomeGarbage(); 6533 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6534 v8::TryCatch try_catch; 6535 v8::Handle<Value> value = CompileRun( 6536 "o.foo = 17;" 6537 "var receiver = {};" 6538 "receiver.__proto__ = o;" 6539 "var result = 0;" 6540 "var saved_result = 0;" 6541 "for (var i = 0; i < 100; i++) {" 6542 " result = receiver.method(41);" 6543 " if (i == 50) {" 6544 " saved_result = result;" 6545 " receiver = {method: receiver.method};" 6546 " }" 6547 "}"); 6548 CHECK(try_catch.HasCaught()); 6549 CHECK_EQ(v8_str("TypeError: Illegal invocation"), 6550 try_catch.Exception()->ToString()); 6551 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 6552 CHECK_GE(interceptor_call_count, 50); 6553 } 6554 6555 THREADED_TEST(CallICFastApi_TrivialSignature) { 6556 v8::HandleScope scope; 6557 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6558 v8::Handle<v8::FunctionTemplate> method_templ = 6559 v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, 6560 v8_str("method_data"), 6561 v8::Handle<v8::Signature>()); 6562 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6563 proto_templ->Set(v8_str("method"), method_templ); 6564 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6565 LocalContext context; 6566 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6567 GenerateSomeGarbage(); 6568 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6569 v8::Handle<Value> value = CompileRun( 6570 "var result = 0;" 6571 "for (var i = 0; i < 100; i++) {" 6572 " result = o.method(41);" 6573 "}"); 6574 6575 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 6576 } 6577 6578 THREADED_TEST(CallICFastApi_SimpleSignature) { 6579 v8::HandleScope scope; 6580 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6581 v8::Handle<v8::FunctionTemplate> method_templ = 6582 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6583 v8_str("method_data"), 6584 v8::Signature::New(fun_templ)); 6585 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6586 proto_templ->Set(v8_str("method"), method_templ); 6587 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6588 LocalContext context; 6589 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6590 GenerateSomeGarbage(); 6591 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6592 v8::Handle<Value> value = CompileRun( 6593 "o.foo = 17;" 6594 "var receiver = {};" 6595 "receiver.__proto__ = o;" 6596 "var result = 0;" 6597 "for (var i = 0; i < 100; i++) {" 6598 " result = receiver.method(41);" 6599 "}"); 6600 6601 CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); 6602 } 6603 6604 THREADED_TEST(CallICFastApi_SimpleSignature_Miss) { 6605 v8::HandleScope scope; 6606 v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 6607 v8::Handle<v8::FunctionTemplate> method_templ = 6608 v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, 6609 v8_str("method_data"), 6610 v8::Signature::New(fun_templ)); 6611 v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); 6612 proto_templ->Set(v8_str("method"), method_templ); 6613 v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); 6614 LocalContext context; 6615 v8::Handle<v8::Function> fun = fun_templ->GetFunction(); 6616 GenerateSomeGarbage(); 6617 context->Global()->Set(v8_str("o"), fun->NewInstance()); 6618 v8::Handle<Value> value = CompileRun( 6619 "o.foo = 17;" 6620 "var receiver = {};" 6621 "receiver.__proto__ = o;" 6622 "var result = 0;" 6623 "var saved_result = 0;" 6624 "for (var i = 0; i < 100; i++) {" 6625 " result = receiver.method(41);" 6626 " if (i == 50) {" 6627 " saved_result = result;" 6628 " receiver = {method: function(x) { return x - 1 }};" 6629 " }" 6630 "}"); 6631 CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); 6632 CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); 6633 } 6634 6635 6636 static int interceptor_call_count = 0; 6637 6638 static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name, 6639 const AccessorInfo& info) { 6640 ApiTestFuzzer::Fuzz(); 6641 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) { 6642 return call_ic_function2; 6643 } 6644 return v8::Handle<Value>(); 6645 } 6646 6647 6648 // This test should hit load and call ICs for the interceptor case. 6649 // Once in a while, the interceptor will reply that a property was not 6650 // found in which case we should get a reference error. 6651 THREADED_TEST(InterceptorICReferenceErrors) { 6652 v8::HandleScope scope; 6653 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6654 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter); 6655 LocalContext context(0, templ, v8::Handle<Value>()); 6656 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run(); 6657 v8::Handle<Value> value = CompileRun( 6658 "function f() {" 6659 " for (var i = 0; i < 1000; i++) {" 6660 " try { x; } catch(e) { return true; }" 6661 " }" 6662 " return false;" 6663 "};" 6664 "f();"); 6665 CHECK_EQ(true, value->BooleanValue()); 6666 interceptor_call_count = 0; 6667 value = CompileRun( 6668 "function g() {" 6669 " for (var i = 0; i < 1000; i++) {" 6670 " try { x(42); } catch(e) { return true; }" 6671 " }" 6672 " return false;" 6673 "};" 6674 "g();"); 6675 CHECK_EQ(true, value->BooleanValue()); 6676 } 6677 6678 6679 static int interceptor_ic_exception_get_count = 0; 6680 6681 static v8::Handle<Value> InterceptorICExceptionGetter( 6682 Local<String> name, 6683 const AccessorInfo& info) { 6684 ApiTestFuzzer::Fuzz(); 6685 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) { 6686 return call_ic_function3; 6687 } 6688 if (interceptor_ic_exception_get_count == 20) { 6689 return v8::ThrowException(v8_num(42)); 6690 } 6691 // Do not handle get for properties other than x. 6692 return v8::Handle<Value>(); 6693 } 6694 6695 // Test interceptor load/call IC where the interceptor throws an 6696 // exception once in a while. 6697 THREADED_TEST(InterceptorICGetterExceptions) { 6698 interceptor_ic_exception_get_count = 0; 6699 v8::HandleScope scope; 6700 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6701 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter); 6702 LocalContext context(0, templ, v8::Handle<Value>()); 6703 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run(); 6704 v8::Handle<Value> value = CompileRun( 6705 "function f() {" 6706 " for (var i = 0; i < 100; i++) {" 6707 " try { x; } catch(e) { return true; }" 6708 " }" 6709 " return false;" 6710 "};" 6711 "f();"); 6712 CHECK_EQ(true, value->BooleanValue()); 6713 interceptor_ic_exception_get_count = 0; 6714 value = CompileRun( 6715 "function f() {" 6716 " for (var i = 0; i < 100; i++) {" 6717 " try { x(42); } catch(e) { return true; }" 6718 " }" 6719 " return false;" 6720 "};" 6721 "f();"); 6722 CHECK_EQ(true, value->BooleanValue()); 6723 } 6724 6725 6726 static int interceptor_ic_exception_set_count = 0; 6727 6728 static v8::Handle<Value> InterceptorICExceptionSetter( 6729 Local<String> key, Local<Value> value, const AccessorInfo&) { 6730 ApiTestFuzzer::Fuzz(); 6731 if (++interceptor_ic_exception_set_count > 20) { 6732 return v8::ThrowException(v8_num(42)); 6733 } 6734 // Do not actually handle setting. 6735 return v8::Handle<Value>(); 6736 } 6737 6738 // Test interceptor store IC where the interceptor throws an exception 6739 // once in a while. 6740 THREADED_TEST(InterceptorICSetterExceptions) { 6741 interceptor_ic_exception_set_count = 0; 6742 v8::HandleScope scope; 6743 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6744 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter); 6745 LocalContext context(0, templ, v8::Handle<Value>()); 6746 v8::Handle<Value> value = CompileRun( 6747 "function f() {" 6748 " for (var i = 0; i < 100; i++) {" 6749 " try { x = 42; } catch(e) { return true; }" 6750 " }" 6751 " return false;" 6752 "};" 6753 "f();"); 6754 CHECK_EQ(true, value->BooleanValue()); 6755 } 6756 6757 6758 // Test that we ignore null interceptors. 6759 THREADED_TEST(NullNamedInterceptor) { 6760 v8::HandleScope scope; 6761 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6762 templ->SetNamedPropertyHandler(0); 6763 LocalContext context; 6764 templ->Set("x", v8_num(42)); 6765 v8::Handle<v8::Object> obj = templ->NewInstance(); 6766 context->Global()->Set(v8_str("obj"), obj); 6767 v8::Handle<Value> value = CompileRun("obj.x"); 6768 CHECK(value->IsInt32()); 6769 CHECK_EQ(42, value->Int32Value()); 6770 } 6771 6772 6773 // Test that we ignore null interceptors. 6774 THREADED_TEST(NullIndexedInterceptor) { 6775 v8::HandleScope scope; 6776 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 6777 templ->SetIndexedPropertyHandler(0); 6778 LocalContext context; 6779 templ->Set("42", v8_num(42)); 6780 v8::Handle<v8::Object> obj = templ->NewInstance(); 6781 context->Global()->Set(v8_str("obj"), obj); 6782 v8::Handle<Value> value = CompileRun("obj[42]"); 6783 CHECK(value->IsInt32()); 6784 CHECK_EQ(42, value->Int32Value()); 6785 } 6786 6787 6788 static v8::Handle<Value> ParentGetter(Local<String> name, 6789 const AccessorInfo& info) { 6790 ApiTestFuzzer::Fuzz(); 6791 return v8_num(1); 6792 } 6793 6794 6795 static v8::Handle<Value> ChildGetter(Local<String> name, 6796 const AccessorInfo& info) { 6797 ApiTestFuzzer::Fuzz(); 6798 return v8_num(42); 6799 } 6800 6801 6802 THREADED_TEST(Overriding) { 6803 v8::HandleScope scope; 6804 LocalContext context; 6805 6806 // Parent template. 6807 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New(); 6808 Local<ObjectTemplate> parent_instance_templ = 6809 parent_templ->InstanceTemplate(); 6810 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter); 6811 6812 // Template that inherits from the parent template. 6813 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New(); 6814 Local<ObjectTemplate> child_instance_templ = 6815 child_templ->InstanceTemplate(); 6816 child_templ->Inherit(parent_templ); 6817 // Override 'f'. The child version of 'f' should get called for child 6818 // instances. 6819 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter); 6820 // Add 'g' twice. The 'g' added last should get called for instances. 6821 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter); 6822 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter); 6823 6824 // Add 'h' as an accessor to the proto template with ReadOnly attributes 6825 // so 'h' can be shadowed on the instance object. 6826 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate(); 6827 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0, 6828 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 6829 6830 // Add 'i' as an accessor to the instance template with ReadOnly attributes 6831 // but the attribute does not have effect because it is duplicated with 6832 // NULL setter. 6833 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0, 6834 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 6835 6836 6837 6838 // Instantiate the child template. 6839 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance(); 6840 6841 // Check that the child function overrides the parent one. 6842 context->Global()->Set(v8_str("o"), instance); 6843 Local<Value> value = v8_compile("o.f")->Run(); 6844 // Check that the 'g' that was added last is hit. 6845 CHECK_EQ(42, value->Int32Value()); 6846 value = v8_compile("o.g")->Run(); 6847 CHECK_EQ(42, value->Int32Value()); 6848 6849 // Check 'h' can be shadowed. 6850 value = v8_compile("o.h = 3; o.h")->Run(); 6851 CHECK_EQ(3, value->Int32Value()); 6852 6853 // Check 'i' is cannot be shadowed or changed. 6854 value = v8_compile("o.i = 3; o.i")->Run(); 6855 CHECK_EQ(42, value->Int32Value()); 6856 } 6857 6858 6859 static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) { 6860 ApiTestFuzzer::Fuzz(); 6861 if (args.IsConstructCall()) { 6862 return v8::Boolean::New(true); 6863 } 6864 return v8::Boolean::New(false); 6865 } 6866 6867 6868 THREADED_TEST(IsConstructCall) { 6869 v8::HandleScope scope; 6870 6871 // Function template with call handler. 6872 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 6873 templ->SetCallHandler(IsConstructHandler); 6874 6875 LocalContext context; 6876 6877 context->Global()->Set(v8_str("f"), templ->GetFunction()); 6878 Local<Value> value = v8_compile("f()")->Run(); 6879 CHECK(!value->BooleanValue()); 6880 value = v8_compile("new f()")->Run(); 6881 CHECK(value->BooleanValue()); 6882 } 6883 6884 6885 THREADED_TEST(ObjectProtoToString) { 6886 v8::HandleScope scope; 6887 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 6888 templ->SetClassName(v8_str("MyClass")); 6889 6890 LocalContext context; 6891 6892 Local<String> customized_tostring = v8_str("customized toString"); 6893 6894 // Replace Object.prototype.toString 6895 v8_compile("Object.prototype.toString = function() {" 6896 " return 'customized toString';" 6897 "}")->Run(); 6898 6899 // Normal ToString call should call replaced Object.prototype.toString 6900 Local<v8::Object> instance = templ->GetFunction()->NewInstance(); 6901 Local<String> value = instance->ToString(); 6902 CHECK(value->IsString() && value->Equals(customized_tostring)); 6903 6904 // ObjectProtoToString should not call replace toString function. 6905 value = instance->ObjectProtoToString(); 6906 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]"))); 6907 6908 // Check global 6909 value = context->Global()->ObjectProtoToString(); 6910 CHECK(value->IsString() && value->Equals(v8_str("[object global]"))); 6911 6912 // Check ordinary object 6913 Local<Value> object = v8_compile("new Object()")->Run(); 6914 value = Local<v8::Object>::Cast(object)->ObjectProtoToString(); 6915 CHECK(value->IsString() && value->Equals(v8_str("[object Object]"))); 6916 } 6917 6918 6919 bool ApiTestFuzzer::fuzzing_ = false; 6920 v8::internal::Semaphore* ApiTestFuzzer::all_tests_done_= 6921 v8::internal::OS::CreateSemaphore(0); 6922 int ApiTestFuzzer::active_tests_; 6923 int ApiTestFuzzer::tests_being_run_; 6924 int ApiTestFuzzer::current_; 6925 6926 6927 // We are in a callback and want to switch to another thread (if we 6928 // are currently running the thread fuzzing test). 6929 void ApiTestFuzzer::Fuzz() { 6930 if (!fuzzing_) return; 6931 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_; 6932 test->ContextSwitch(); 6933 } 6934 6935 6936 // Let the next thread go. Since it is also waiting on the V8 lock it may 6937 // not start immediately. 6938 bool ApiTestFuzzer::NextThread() { 6939 int test_position = GetNextTestNumber(); 6940 const char* test_name = RegisterThreadedTest::nth(current_)->name(); 6941 if (test_position == current_) { 6942 if (kLogThreading) 6943 printf("Stay with %s\n", test_name); 6944 return false; 6945 } 6946 if (kLogThreading) { 6947 printf("Switch from %s to %s\n", 6948 test_name, 6949 RegisterThreadedTest::nth(test_position)->name()); 6950 } 6951 current_ = test_position; 6952 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal(); 6953 return true; 6954 } 6955 6956 6957 void ApiTestFuzzer::Run() { 6958 // When it is our turn... 6959 gate_->Wait(); 6960 { 6961 // ... get the V8 lock and start running the test. 6962 v8::Locker locker; 6963 CallTest(); 6964 } 6965 // This test finished. 6966 active_ = false; 6967 active_tests_--; 6968 // If it was the last then signal that fact. 6969 if (active_tests_ == 0) { 6970 all_tests_done_->Signal(); 6971 } else { 6972 // Otherwise select a new test and start that. 6973 NextThread(); 6974 } 6975 } 6976 6977 6978 static unsigned linear_congruential_generator; 6979 6980 6981 void ApiTestFuzzer::Setup(PartOfTest part) { 6982 linear_congruential_generator = i::FLAG_testing_prng_seed; 6983 fuzzing_ = true; 6984 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1); 6985 int end = (part == FIRST_PART) 6986 ? (RegisterThreadedTest::count() >> 1) 6987 : RegisterThreadedTest::count(); 6988 active_tests_ = tests_being_run_ = end - start; 6989 for (int i = 0; i < tests_being_run_; i++) { 6990 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start); 6991 } 6992 for (int i = 0; i < active_tests_; i++) { 6993 RegisterThreadedTest::nth(i)->fuzzer_->Start(); 6994 } 6995 } 6996 6997 6998 static void CallTestNumber(int test_number) { 6999 (RegisterThreadedTest::nth(test_number)->callback())(); 7000 } 7001 7002 7003 void ApiTestFuzzer::RunAllTests() { 7004 // Set off the first test. 7005 current_ = -1; 7006 NextThread(); 7007 // Wait till they are all done. 7008 all_tests_done_->Wait(); 7009 } 7010 7011 7012 int ApiTestFuzzer::GetNextTestNumber() { 7013 int next_test; 7014 do { 7015 next_test = (linear_congruential_generator >> 16) % tests_being_run_; 7016 linear_congruential_generator *= 1664525u; 7017 linear_congruential_generator += 1013904223u; 7018 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_); 7019 return next_test; 7020 } 7021 7022 7023 void ApiTestFuzzer::ContextSwitch() { 7024 // If the new thread is the same as the current thread there is nothing to do. 7025 if (NextThread()) { 7026 // Now it can start. 7027 v8::Unlocker unlocker; 7028 // Wait till someone starts us again. 7029 gate_->Wait(); 7030 // And we're off. 7031 } 7032 } 7033 7034 7035 void ApiTestFuzzer::TearDown() { 7036 fuzzing_ = false; 7037 for (int i = 0; i < RegisterThreadedTest::count(); i++) { 7038 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_; 7039 if (fuzzer != NULL) fuzzer->Join(); 7040 } 7041 } 7042 7043 7044 // Lets not be needlessly self-referential. 7045 TEST(Threading) { 7046 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART); 7047 ApiTestFuzzer::RunAllTests(); 7048 ApiTestFuzzer::TearDown(); 7049 } 7050 7051 TEST(Threading2) { 7052 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART); 7053 ApiTestFuzzer::RunAllTests(); 7054 ApiTestFuzzer::TearDown(); 7055 } 7056 7057 7058 void ApiTestFuzzer::CallTest() { 7059 if (kLogThreading) 7060 printf("Start test %d\n", test_number_); 7061 CallTestNumber(test_number_); 7062 if (kLogThreading) 7063 printf("End test %d\n", test_number_); 7064 } 7065 7066 7067 static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) { 7068 CHECK(v8::Locker::IsLocked()); 7069 ApiTestFuzzer::Fuzz(); 7070 v8::Unlocker unlocker; 7071 const char* code = "throw 7;"; 7072 { 7073 v8::Locker nested_locker; 7074 v8::HandleScope scope; 7075 v8::Handle<Value> exception; 7076 { v8::TryCatch try_catch; 7077 v8::Handle<Value> value = CompileRun(code); 7078 CHECK(value.IsEmpty()); 7079 CHECK(try_catch.HasCaught()); 7080 // Make sure to wrap the exception in a new handle because 7081 // the handle returned from the TryCatch is destroyed 7082 // when the TryCatch is destroyed. 7083 exception = Local<Value>::New(try_catch.Exception()); 7084 } 7085 return v8::ThrowException(exception); 7086 } 7087 } 7088 7089 7090 static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) { 7091 CHECK(v8::Locker::IsLocked()); 7092 ApiTestFuzzer::Fuzz(); 7093 v8::Unlocker unlocker; 7094 const char* code = "throw 7;"; 7095 { 7096 v8::Locker nested_locker; 7097 v8::HandleScope scope; 7098 v8::Handle<Value> value = CompileRun(code); 7099 CHECK(value.IsEmpty()); 7100 return v8_str("foo"); 7101 } 7102 } 7103 7104 7105 // These are locking tests that don't need to be run again 7106 // as part of the locking aggregation tests. 7107 TEST(NestedLockers) { 7108 v8::Locker locker; 7109 CHECK(v8::Locker::IsLocked()); 7110 v8::HandleScope scope; 7111 LocalContext env; 7112 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS); 7113 Local<Function> fun = fun_templ->GetFunction(); 7114 env->Global()->Set(v8_str("throw_in_js"), fun); 7115 Local<Script> script = v8_compile("(function () {" 7116 " try {" 7117 " throw_in_js();" 7118 " return 42;" 7119 " } catch (e) {" 7120 " return e * 13;" 7121 " }" 7122 "})();"); 7123 CHECK_EQ(91, script->Run()->Int32Value()); 7124 } 7125 7126 7127 // These are locking tests that don't need to be run again 7128 // as part of the locking aggregation tests. 7129 TEST(NestedLockersNoTryCatch) { 7130 v8::Locker locker; 7131 v8::HandleScope scope; 7132 LocalContext env; 7133 Local<v8::FunctionTemplate> fun_templ = 7134 v8::FunctionTemplate::New(ThrowInJSNoCatch); 7135 Local<Function> fun = fun_templ->GetFunction(); 7136 env->Global()->Set(v8_str("throw_in_js"), fun); 7137 Local<Script> script = v8_compile("(function () {" 7138 " try {" 7139 " throw_in_js();" 7140 " return 42;" 7141 " } catch (e) {" 7142 " return e * 13;" 7143 " }" 7144 "})();"); 7145 CHECK_EQ(91, script->Run()->Int32Value()); 7146 } 7147 7148 7149 THREADED_TEST(RecursiveLocking) { 7150 v8::Locker locker; 7151 { 7152 v8::Locker locker2; 7153 CHECK(v8::Locker::IsLocked()); 7154 } 7155 } 7156 7157 7158 static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) { 7159 ApiTestFuzzer::Fuzz(); 7160 v8::Unlocker unlocker; 7161 return v8::Undefined(); 7162 } 7163 7164 7165 THREADED_TEST(LockUnlockLock) { 7166 { 7167 v8::Locker locker; 7168 v8::HandleScope scope; 7169 LocalContext env; 7170 Local<v8::FunctionTemplate> fun_templ = 7171 v8::FunctionTemplate::New(UnlockForAMoment); 7172 Local<Function> fun = fun_templ->GetFunction(); 7173 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 7174 Local<Script> script = v8_compile("(function () {" 7175 " unlock_for_a_moment();" 7176 " return 42;" 7177 "})();"); 7178 CHECK_EQ(42, script->Run()->Int32Value()); 7179 } 7180 { 7181 v8::Locker locker; 7182 v8::HandleScope scope; 7183 LocalContext env; 7184 Local<v8::FunctionTemplate> fun_templ = 7185 v8::FunctionTemplate::New(UnlockForAMoment); 7186 Local<Function> fun = fun_templ->GetFunction(); 7187 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 7188 Local<Script> script = v8_compile("(function () {" 7189 " unlock_for_a_moment();" 7190 " return 42;" 7191 "})();"); 7192 CHECK_EQ(42, script->Run()->Int32Value()); 7193 } 7194 } 7195 7196 7197 static int GetGlobalObjectsCount() { 7198 int count = 0; 7199 v8::internal::HeapIterator it; 7200 for (i::HeapObject* object = it.next(); object != NULL; object = it.next()) 7201 if (object->IsJSGlobalObject()) count++; 7202 return count; 7203 } 7204 7205 7206 static int GetSurvivingGlobalObjectsCount() { 7207 // We need to collect all garbage twice to be sure that everything 7208 // has been collected. This is because inline caches are cleared in 7209 // the first garbage collection but some of the maps have already 7210 // been marked at that point. Therefore some of the maps are not 7211 // collected until the second garbage collection. 7212 v8::internal::Heap::CollectAllGarbage(false); 7213 v8::internal::Heap::CollectAllGarbage(false); 7214 int count = GetGlobalObjectsCount(); 7215 #ifdef DEBUG 7216 if (count > 0) v8::internal::Heap::TracePathToGlobal(); 7217 #endif 7218 return count; 7219 } 7220 7221 7222 TEST(DontLeakGlobalObjects) { 7223 // Regression test for issues 1139850 and 1174891. 7224 7225 v8::V8::Initialize(); 7226 7227 int count = GetSurvivingGlobalObjectsCount(); 7228 7229 for (int i = 0; i < 5; i++) { 7230 { v8::HandleScope scope; 7231 LocalContext context; 7232 } 7233 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 7234 7235 { v8::HandleScope scope; 7236 LocalContext context; 7237 v8_compile("Date")->Run(); 7238 } 7239 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 7240 7241 { v8::HandleScope scope; 7242 LocalContext context; 7243 v8_compile("/aaa/")->Run(); 7244 } 7245 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 7246 7247 { v8::HandleScope scope; 7248 const char* extension_list[] = { "v8/gc" }; 7249 v8::ExtensionConfiguration extensions(1, extension_list); 7250 LocalContext context(&extensions); 7251 v8_compile("gc();")->Run(); 7252 } 7253 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 7254 } 7255 } 7256 7257 7258 v8::Persistent<v8::Object> some_object; 7259 v8::Persistent<v8::Object> bad_handle; 7260 7261 void NewPersistentHandleCallback(v8::Persistent<v8::Value>, void*) { 7262 v8::HandleScope scope; 7263 bad_handle = v8::Persistent<v8::Object>::New(some_object); 7264 } 7265 7266 7267 THREADED_TEST(NewPersistentHandleFromWeakCallback) { 7268 LocalContext context; 7269 7270 v8::Persistent<v8::Object> handle1, handle2; 7271 { 7272 v8::HandleScope scope; 7273 some_object = v8::Persistent<v8::Object>::New(v8::Object::New()); 7274 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7275 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7276 } 7277 // Note: order is implementation dependent alas: currently 7278 // global handle nodes are processed by PostGarbageCollectionProcessing 7279 // in reverse allocation order, so if second allocated handle is deleted, 7280 // weak callback of the first handle would be able to 'reallocate' it. 7281 handle1.MakeWeak(NULL, NewPersistentHandleCallback); 7282 handle2.Dispose(); 7283 i::Heap::CollectAllGarbage(false); 7284 } 7285 7286 7287 v8::Persistent<v8::Object> to_be_disposed; 7288 7289 void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) { 7290 to_be_disposed.Dispose(); 7291 i::Heap::CollectAllGarbage(false); 7292 } 7293 7294 7295 THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) { 7296 LocalContext context; 7297 7298 v8::Persistent<v8::Object> handle1, handle2; 7299 { 7300 v8::HandleScope scope; 7301 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7302 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7303 } 7304 handle1.MakeWeak(NULL, DisposeAndForceGcCallback); 7305 to_be_disposed = handle2; 7306 i::Heap::CollectAllGarbage(false); 7307 } 7308 7309 void DisposingCallback(v8::Persistent<v8::Value> handle, void*) { 7310 handle.Dispose(); 7311 } 7312 7313 void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) { 7314 v8::HandleScope scope; 7315 v8::Persistent<v8::Object>::New(v8::Object::New()); 7316 } 7317 7318 7319 THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) { 7320 LocalContext context; 7321 7322 v8::Persistent<v8::Object> handle1, handle2, handle3; 7323 { 7324 v8::HandleScope scope; 7325 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7326 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7327 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 7328 } 7329 handle2.MakeWeak(NULL, DisposingCallback); 7330 handle3.MakeWeak(NULL, HandleCreatingCallback); 7331 i::Heap::CollectAllGarbage(false); 7332 } 7333 7334 7335 THREADED_TEST(CheckForCrossContextObjectLiterals) { 7336 v8::V8::Initialize(); 7337 7338 const int nof = 2; 7339 const char* sources[nof] = { 7340 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }", 7341 "Object()" 7342 }; 7343 7344 for (int i = 0; i < nof; i++) { 7345 const char* source = sources[i]; 7346 { v8::HandleScope scope; 7347 LocalContext context; 7348 CompileRun(source); 7349 } 7350 { v8::HandleScope scope; 7351 LocalContext context; 7352 CompileRun(source); 7353 } 7354 } 7355 } 7356 7357 7358 static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) { 7359 v8::HandleScope inner; 7360 env->Enter(); 7361 v8::Handle<Value> three = v8_num(3); 7362 v8::Handle<Value> value = inner.Close(three); 7363 env->Exit(); 7364 return value; 7365 } 7366 7367 7368 THREADED_TEST(NestedHandleScopeAndContexts) { 7369 v8::HandleScope outer; 7370 v8::Persistent<Context> env = Context::New(); 7371 env->Enter(); 7372 v8::Handle<Value> value = NestedScope(env); 7373 v8::Handle<String> str = value->ToString(); 7374 env->Exit(); 7375 env.Dispose(); 7376 } 7377 7378 7379 THREADED_TEST(ExternalAllocatedMemory) { 7380 v8::HandleScope outer; 7381 v8::Persistent<Context> env = Context::New(); 7382 const int kSize = 1024*1024; 7383 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize); 7384 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0); 7385 } 7386 7387 7388 THREADED_TEST(DisposeEnteredContext) { 7389 v8::HandleScope scope; 7390 LocalContext outer; 7391 { v8::Persistent<v8::Context> inner = v8::Context::New(); 7392 inner->Enter(); 7393 inner.Dispose(); 7394 inner.Clear(); 7395 inner->Exit(); 7396 } 7397 } 7398 7399 7400 // Regression test for issue 54, object templates with internal fields 7401 // but no accessors or interceptors did not get their internal field 7402 // count set on instances. 7403 THREADED_TEST(Regress54) { 7404 v8::HandleScope outer; 7405 LocalContext context; 7406 static v8::Persistent<v8::ObjectTemplate> templ; 7407 if (templ.IsEmpty()) { 7408 v8::HandleScope inner; 7409 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New(); 7410 local->SetInternalFieldCount(1); 7411 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local)); 7412 } 7413 v8::Handle<v8::Object> result = templ->NewInstance(); 7414 CHECK_EQ(1, result->InternalFieldCount()); 7415 } 7416 7417 7418 // If part of the threaded tests, this test makes ThreadingTest fail 7419 // on mac. 7420 TEST(CatchStackOverflow) { 7421 v8::HandleScope scope; 7422 LocalContext context; 7423 v8::TryCatch try_catch; 7424 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New( 7425 "function f() {" 7426 " return f();" 7427 "}" 7428 "" 7429 "f();")); 7430 v8::Handle<v8::Value> result = script->Run(); 7431 CHECK(result.IsEmpty()); 7432 } 7433 7434 7435 static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script, 7436 const char* resource_name, 7437 int line_offset) { 7438 v8::HandleScope scope; 7439 v8::TryCatch try_catch; 7440 v8::Handle<v8::Value> result = script->Run(); 7441 CHECK(result.IsEmpty()); 7442 CHECK(try_catch.HasCaught()); 7443 v8::Handle<v8::Message> message = try_catch.Message(); 7444 CHECK(!message.IsEmpty()); 7445 CHECK_EQ(10 + line_offset, message->GetLineNumber()); 7446 CHECK_EQ(91, message->GetStartPosition()); 7447 CHECK_EQ(92, message->GetEndPosition()); 7448 CHECK_EQ(2, message->GetStartColumn()); 7449 CHECK_EQ(3, message->GetEndColumn()); 7450 v8::String::AsciiValue line(message->GetSourceLine()); 7451 CHECK_EQ(" throw 'nirk';", *line); 7452 v8::String::AsciiValue name(message->GetScriptResourceName()); 7453 CHECK_EQ(resource_name, *name); 7454 } 7455 7456 7457 THREADED_TEST(TryCatchSourceInfo) { 7458 v8::HandleScope scope; 7459 LocalContext context; 7460 v8::Handle<v8::String> source = v8::String::New( 7461 "function Foo() {\n" 7462 " return Bar();\n" 7463 "}\n" 7464 "\n" 7465 "function Bar() {\n" 7466 " return Baz();\n" 7467 "}\n" 7468 "\n" 7469 "function Baz() {\n" 7470 " throw 'nirk';\n" 7471 "}\n" 7472 "\n" 7473 "Foo();\n"); 7474 7475 const char* resource_name; 7476 v8::Handle<v8::Script> script; 7477 resource_name = "test.js"; 7478 script = v8::Script::Compile(source, v8::String::New(resource_name)); 7479 CheckTryCatchSourceInfo(script, resource_name, 0); 7480 7481 resource_name = "test1.js"; 7482 v8::ScriptOrigin origin1(v8::String::New(resource_name)); 7483 script = v8::Script::Compile(source, &origin1); 7484 CheckTryCatchSourceInfo(script, resource_name, 0); 7485 7486 resource_name = "test2.js"; 7487 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7)); 7488 script = v8::Script::Compile(source, &origin2); 7489 CheckTryCatchSourceInfo(script, resource_name, 7); 7490 } 7491 7492 7493 THREADED_TEST(CompilationCache) { 7494 v8::HandleScope scope; 7495 LocalContext context; 7496 v8::Handle<v8::String> source0 = v8::String::New("1234"); 7497 v8::Handle<v8::String> source1 = v8::String::New("1234"); 7498 v8::Handle<v8::Script> script0 = 7499 v8::Script::Compile(source0, v8::String::New("test.js")); 7500 v8::Handle<v8::Script> script1 = 7501 v8::Script::Compile(source1, v8::String::New("test.js")); 7502 v8::Handle<v8::Script> script2 = 7503 v8::Script::Compile(source0); // different origin 7504 CHECK_EQ(1234, script0->Run()->Int32Value()); 7505 CHECK_EQ(1234, script1->Run()->Int32Value()); 7506 CHECK_EQ(1234, script2->Run()->Int32Value()); 7507 } 7508 7509 7510 static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) { 7511 ApiTestFuzzer::Fuzz(); 7512 return v8_num(42); 7513 } 7514 7515 7516 THREADED_TEST(CallbackFunctionName) { 7517 v8::HandleScope scope; 7518 LocalContext context; 7519 Local<ObjectTemplate> t = ObjectTemplate::New(); 7520 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback)); 7521 context->Global()->Set(v8_str("obj"), t->NewInstance()); 7522 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name"); 7523 CHECK(value->IsString()); 7524 v8::String::AsciiValue name(value); 7525 CHECK_EQ("asdf", *name); 7526 } 7527 7528 7529 THREADED_TEST(DateAccess) { 7530 v8::HandleScope scope; 7531 LocalContext context; 7532 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0); 7533 CHECK(date->IsDate()); 7534 CHECK_EQ(1224744689038.0, v8::Handle<v8::Date>::Cast(date)->NumberValue()); 7535 } 7536 7537 7538 void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) { 7539 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(val); 7540 v8::Handle<v8::Array> props = obj->GetPropertyNames(); 7541 CHECK_EQ(elmc, props->Length()); 7542 for (int i = 0; i < elmc; i++) { 7543 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i))); 7544 CHECK_EQ(elmv[i], *elm); 7545 } 7546 } 7547 7548 7549 THREADED_TEST(PropertyEnumeration) { 7550 v8::HandleScope scope; 7551 LocalContext context; 7552 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New( 7553 "var result = [];" 7554 "result[0] = {};" 7555 "result[1] = {a: 1, b: 2};" 7556 "result[2] = [1, 2, 3];" 7557 "var proto = {x: 1, y: 2, z: 3};" 7558 "var x = { __proto__: proto, w: 0, z: 1 };" 7559 "result[3] = x;" 7560 "result;"))->Run(); 7561 v8::Handle<v8::Array> elms = v8::Handle<v8::Array>::Cast(obj); 7562 CHECK_EQ(4, elms->Length()); 7563 int elmc0 = 0; 7564 const char** elmv0 = NULL; 7565 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0); 7566 int elmc1 = 2; 7567 const char* elmv1[] = {"a", "b"}; 7568 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1); 7569 int elmc2 = 3; 7570 const char* elmv2[] = {"0", "1", "2"}; 7571 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2); 7572 int elmc3 = 4; 7573 const char* elmv3[] = {"w", "z", "x", "y"}; 7574 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3); 7575 } 7576 7577 7578 static bool NamedSetAccessBlocker(Local<v8::Object> obj, 7579 Local<Value> name, 7580 v8::AccessType type, 7581 Local<Value> data) { 7582 return type != v8::ACCESS_SET; 7583 } 7584 7585 7586 static bool IndexedSetAccessBlocker(Local<v8::Object> obj, 7587 uint32_t key, 7588 v8::AccessType type, 7589 Local<Value> data) { 7590 return type != v8::ACCESS_SET; 7591 } 7592 7593 7594 THREADED_TEST(DisableAccessChecksWhileConfiguring) { 7595 v8::HandleScope scope; 7596 LocalContext context; 7597 Local<ObjectTemplate> templ = ObjectTemplate::New(); 7598 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker, 7599 IndexedSetAccessBlocker); 7600 templ->Set(v8_str("x"), v8::True()); 7601 Local<v8::Object> instance = templ->NewInstance(); 7602 context->Global()->Set(v8_str("obj"), instance); 7603 Local<Value> value = CompileRun("obj.x"); 7604 CHECK(value->BooleanValue()); 7605 } 7606 7607 7608 static bool NamedGetAccessBlocker(Local<v8::Object> obj, 7609 Local<Value> name, 7610 v8::AccessType type, 7611 Local<Value> data) { 7612 return false; 7613 } 7614 7615 7616 static bool IndexedGetAccessBlocker(Local<v8::Object> obj, 7617 uint32_t key, 7618 v8::AccessType type, 7619 Local<Value> data) { 7620 return false; 7621 } 7622 7623 7624 7625 THREADED_TEST(AccessChecksReenabledCorrectly) { 7626 v8::HandleScope scope; 7627 LocalContext context; 7628 Local<ObjectTemplate> templ = ObjectTemplate::New(); 7629 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker, 7630 IndexedGetAccessBlocker); 7631 templ->Set(v8_str("a"), v8_str("a")); 7632 // Add more than 8 (see kMaxFastProperties) properties 7633 // so that the constructor will force copying map. 7634 // Cannot sprintf, gcc complains unsafety. 7635 char buf[4]; 7636 for (char i = '0'; i <= '9' ; i++) { 7637 buf[0] = i; 7638 for (char j = '0'; j <= '9'; j++) { 7639 buf[1] = j; 7640 for (char k = '0'; k <= '9'; k++) { 7641 buf[2] = k; 7642 buf[3] = 0; 7643 templ->Set(v8_str(buf), v8::Number::New(k)); 7644 } 7645 } 7646 } 7647 7648 Local<v8::Object> instance_1 = templ->NewInstance(); 7649 context->Global()->Set(v8_str("obj_1"), instance_1); 7650 7651 Local<Value> value_1 = CompileRun("obj_1.a"); 7652 CHECK(value_1->IsUndefined()); 7653 7654 Local<v8::Object> instance_2 = templ->NewInstance(); 7655 context->Global()->Set(v8_str("obj_2"), instance_2); 7656 7657 Local<Value> value_2 = CompileRun("obj_2.a"); 7658 CHECK(value_2->IsUndefined()); 7659 } 7660 7661 7662 // This tests that access check information remains on the global 7663 // object template when creating contexts. 7664 THREADED_TEST(AccessControlRepeatedContextCreation) { 7665 v8::HandleScope handle_scope; 7666 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 7667 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker, 7668 IndexedSetAccessBlocker); 7669 i::Handle<i::ObjectTemplateInfo> internal_template = 7670 v8::Utils::OpenHandle(*global_template); 7671 CHECK(!internal_template->constructor()->IsUndefined()); 7672 i::Handle<i::FunctionTemplateInfo> constructor( 7673 i::FunctionTemplateInfo::cast(internal_template->constructor())); 7674 CHECK(!constructor->access_check_info()->IsUndefined()); 7675 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 7676 CHECK(!constructor->access_check_info()->IsUndefined()); 7677 } 7678 7679 7680 THREADED_TEST(TurnOnAccessCheck) { 7681 v8::HandleScope handle_scope; 7682 7683 // Create an environment with access check to the global object disabled by 7684 // default. 7685 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 7686 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker, 7687 IndexedGetAccessBlocker, 7688 v8::Handle<v8::Value>(), 7689 false); 7690 v8::Persistent<Context> context = Context::New(NULL, global_template); 7691 Context::Scope context_scope(context); 7692 7693 // Set up a property and a number of functions. 7694 context->Global()->Set(v8_str("a"), v8_num(1)); 7695 CompileRun("function f1() {return a;}" 7696 "function f2() {return a;}" 7697 "function g1() {return h();}" 7698 "function g2() {return h();}" 7699 "function h() {return 1;}"); 7700 Local<Function> f1 = 7701 Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); 7702 Local<Function> f2 = 7703 Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); 7704 Local<Function> g1 = 7705 Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); 7706 Local<Function> g2 = 7707 Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); 7708 Local<Function> h = 7709 Local<Function>::Cast(context->Global()->Get(v8_str("h"))); 7710 7711 // Get the global object. 7712 v8::Handle<v8::Object> global = context->Global(); 7713 7714 // Call f1 one time and f2 a number of times. This will ensure that f1 still 7715 // uses the runtime system to retreive property a whereas f2 uses global load 7716 // inline cache. 7717 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1))); 7718 for (int i = 0; i < 4; i++) { 7719 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1))); 7720 } 7721 7722 // Same for g1 and g2. 7723 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1))); 7724 for (int i = 0; i < 4; i++) { 7725 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1))); 7726 } 7727 7728 // Detach the global and turn on access check. 7729 context->DetachGlobal(); 7730 context->Global()->TurnOnAccessCheck(); 7731 7732 // Failing access check to property get results in undefined. 7733 CHECK(f1->Call(global, 0, NULL)->IsUndefined()); 7734 CHECK(f2->Call(global, 0, NULL)->IsUndefined()); 7735 7736 // Failing access check to function call results in exception. 7737 CHECK(g1->Call(global, 0, NULL).IsEmpty()); 7738 CHECK(g2->Call(global, 0, NULL).IsEmpty()); 7739 7740 // No failing access check when just returning a constant. 7741 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1))); 7742 } 7743 7744 7745 // This test verifies that pre-compilation (aka preparsing) can be called 7746 // without initializing the whole VM. Thus we cannot run this test in a 7747 // multi-threaded setup. 7748 TEST(PreCompile) { 7749 // TODO(155): This test would break without the initialization of V8. This is 7750 // a workaround for now to make this test not fail. 7751 v8::V8::Initialize(); 7752 const char *script = "function foo(a) { return a+1; }"; 7753 v8::ScriptData *sd = 7754 v8::ScriptData::PreCompile(script, i::StrLength(script)); 7755 CHECK_NE(sd->Length(), 0); 7756 CHECK_NE(sd->Data(), NULL); 7757 CHECK(!sd->HasError()); 7758 delete sd; 7759 } 7760 7761 7762 TEST(PreCompileWithError) { 7763 v8::V8::Initialize(); 7764 const char *script = "function foo(a) { return 1 * * 2; }"; 7765 v8::ScriptData *sd = 7766 v8::ScriptData::PreCompile(script, i::StrLength(script)); 7767 CHECK(sd->HasError()); 7768 delete sd; 7769 } 7770 7771 7772 TEST(Regress31661) { 7773 v8::V8::Initialize(); 7774 const char *script = " The Definintive Guide"; 7775 v8::ScriptData *sd = 7776 v8::ScriptData::PreCompile(script, i::StrLength(script)); 7777 CHECK(sd->HasError()); 7778 delete sd; 7779 } 7780 7781 7782 // This tests that we do not allow dictionary load/call inline caches 7783 // to use functions that have not yet been compiled. The potential 7784 // problem of loading a function that has not yet been compiled can 7785 // arise because we share code between contexts via the compilation 7786 // cache. 7787 THREADED_TEST(DictionaryICLoadedFunction) { 7788 v8::HandleScope scope; 7789 // Test LoadIC. 7790 for (int i = 0; i < 2; i++) { 7791 LocalContext context; 7792 context->Global()->Set(v8_str("tmp"), v8::True()); 7793 context->Global()->Delete(v8_str("tmp")); 7794 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');"); 7795 } 7796 // Test CallIC. 7797 for (int i = 0; i < 2; i++) { 7798 LocalContext context; 7799 context->Global()->Set(v8_str("tmp"), v8::True()); 7800 context->Global()->Delete(v8_str("tmp")); 7801 CompileRun("for (var j = 0; j < 10; j++) RegExp('')"); 7802 } 7803 } 7804 7805 7806 // Test that cross-context new calls use the context of the callee to 7807 // create the new JavaScript object. 7808 THREADED_TEST(CrossContextNew) { 7809 v8::HandleScope scope; 7810 v8::Persistent<Context> context0 = Context::New(); 7811 v8::Persistent<Context> context1 = Context::New(); 7812 7813 // Allow cross-domain access. 7814 Local<String> token = v8_str("<security token>"); 7815 context0->SetSecurityToken(token); 7816 context1->SetSecurityToken(token); 7817 7818 // Set an 'x' property on the Object prototype and define a 7819 // constructor function in context0. 7820 context0->Enter(); 7821 CompileRun("Object.prototype.x = 42; function C() {};"); 7822 context0->Exit(); 7823 7824 // Call the constructor function from context0 and check that the 7825 // result has the 'x' property. 7826 context1->Enter(); 7827 context1->Global()->Set(v8_str("other"), context0->Global()); 7828 Local<Value> value = CompileRun("var instance = new other.C(); instance.x"); 7829 CHECK(value->IsInt32()); 7830 CHECK_EQ(42, value->Int32Value()); 7831 context1->Exit(); 7832 7833 // Dispose the contexts to allow them to be garbage collected. 7834 context0.Dispose(); 7835 context1.Dispose(); 7836 } 7837 7838 7839 class RegExpInterruptTest { 7840 public: 7841 RegExpInterruptTest() : block_(NULL) {} 7842 ~RegExpInterruptTest() { delete block_; } 7843 void RunTest() { 7844 block_ = i::OS::CreateSemaphore(0); 7845 gc_count_ = 0; 7846 gc_during_regexp_ = 0; 7847 regexp_success_ = false; 7848 gc_success_ = false; 7849 GCThread gc_thread(this); 7850 gc_thread.Start(); 7851 v8::Locker::StartPreemption(1); 7852 7853 LongRunningRegExp(); 7854 { 7855 v8::Unlocker unlock; 7856 gc_thread.Join(); 7857 } 7858 v8::Locker::StopPreemption(); 7859 CHECK(regexp_success_); 7860 CHECK(gc_success_); 7861 } 7862 private: 7863 // Number of garbage collections required. 7864 static const int kRequiredGCs = 5; 7865 7866 class GCThread : public i::Thread { 7867 public: 7868 explicit GCThread(RegExpInterruptTest* test) 7869 : test_(test) {} 7870 virtual void Run() { 7871 test_->CollectGarbage(); 7872 } 7873 private: 7874 RegExpInterruptTest* test_; 7875 }; 7876 7877 void CollectGarbage() { 7878 block_->Wait(); 7879 while (gc_during_regexp_ < kRequiredGCs) { 7880 { 7881 v8::Locker lock; 7882 // TODO(lrn): Perhaps create some garbage before collecting. 7883 i::Heap::CollectAllGarbage(false); 7884 gc_count_++; 7885 } 7886 i::OS::Sleep(1); 7887 } 7888 gc_success_ = true; 7889 } 7890 7891 void LongRunningRegExp() { 7892 block_->Signal(); // Enable garbage collection thread on next preemption. 7893 int rounds = 0; 7894 while (gc_during_regexp_ < kRequiredGCs) { 7895 int gc_before = gc_count_; 7896 { 7897 // Match 15-30 "a"'s against 14 and a "b". 7898 const char* c_source = 7899 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 7900 ".exec('aaaaaaaaaaaaaaab') === null"; 7901 Local<String> source = String::New(c_source); 7902 Local<Script> script = Script::Compile(source); 7903 Local<Value> result = script->Run(); 7904 if (!result->BooleanValue()) { 7905 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit. 7906 return; 7907 } 7908 } 7909 { 7910 // Match 15-30 "a"'s against 15 and a "b". 7911 const char* c_source = 7912 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 7913 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'"; 7914 Local<String> source = String::New(c_source); 7915 Local<Script> script = Script::Compile(source); 7916 Local<Value> result = script->Run(); 7917 if (!result->BooleanValue()) { 7918 gc_during_regexp_ = kRequiredGCs; 7919 return; 7920 } 7921 } 7922 int gc_after = gc_count_; 7923 gc_during_regexp_ += gc_after - gc_before; 7924 rounds++; 7925 i::OS::Sleep(1); 7926 } 7927 regexp_success_ = true; 7928 } 7929 7930 i::Semaphore* block_; 7931 int gc_count_; 7932 int gc_during_regexp_; 7933 bool regexp_success_; 7934 bool gc_success_; 7935 }; 7936 7937 7938 // Test that a regular expression execution can be interrupted and 7939 // survive a garbage collection. 7940 TEST(RegExpInterruption) { 7941 v8::Locker lock; 7942 v8::V8::Initialize(); 7943 v8::HandleScope scope; 7944 Local<Context> local_env; 7945 { 7946 LocalContext env; 7947 local_env = env.local(); 7948 } 7949 7950 // Local context should still be live. 7951 CHECK(!local_env.IsEmpty()); 7952 local_env->Enter(); 7953 7954 // Should complete without problems. 7955 RegExpInterruptTest().RunTest(); 7956 7957 local_env->Exit(); 7958 } 7959 7960 7961 class ApplyInterruptTest { 7962 public: 7963 ApplyInterruptTest() : block_(NULL) {} 7964 ~ApplyInterruptTest() { delete block_; } 7965 void RunTest() { 7966 block_ = i::OS::CreateSemaphore(0); 7967 gc_count_ = 0; 7968 gc_during_apply_ = 0; 7969 apply_success_ = false; 7970 gc_success_ = false; 7971 GCThread gc_thread(this); 7972 gc_thread.Start(); 7973 v8::Locker::StartPreemption(1); 7974 7975 LongRunningApply(); 7976 { 7977 v8::Unlocker unlock; 7978 gc_thread.Join(); 7979 } 7980 v8::Locker::StopPreemption(); 7981 CHECK(apply_success_); 7982 CHECK(gc_success_); 7983 } 7984 private: 7985 // Number of garbage collections required. 7986 static const int kRequiredGCs = 2; 7987 7988 class GCThread : public i::Thread { 7989 public: 7990 explicit GCThread(ApplyInterruptTest* test) 7991 : test_(test) {} 7992 virtual void Run() { 7993 test_->CollectGarbage(); 7994 } 7995 private: 7996 ApplyInterruptTest* test_; 7997 }; 7998 7999 void CollectGarbage() { 8000 block_->Wait(); 8001 while (gc_during_apply_ < kRequiredGCs) { 8002 { 8003 v8::Locker lock; 8004 i::Heap::CollectAllGarbage(false); 8005 gc_count_++; 8006 } 8007 i::OS::Sleep(1); 8008 } 8009 gc_success_ = true; 8010 } 8011 8012 void LongRunningApply() { 8013 block_->Signal(); 8014 int rounds = 0; 8015 while (gc_during_apply_ < kRequiredGCs) { 8016 int gc_before = gc_count_; 8017 { 8018 const char* c_source = 8019 "function do_very_little(bar) {" 8020 " this.foo = bar;" 8021 "}" 8022 "for (var i = 0; i < 100000; i++) {" 8023 " do_very_little.apply(this, ['bar']);" 8024 "}"; 8025 Local<String> source = String::New(c_source); 8026 Local<Script> script = Script::Compile(source); 8027 Local<Value> result = script->Run(); 8028 // Check that no exception was thrown. 8029 CHECK(!result.IsEmpty()); 8030 } 8031 int gc_after = gc_count_; 8032 gc_during_apply_ += gc_after - gc_before; 8033 rounds++; 8034 } 8035 apply_success_ = true; 8036 } 8037 8038 i::Semaphore* block_; 8039 int gc_count_; 8040 int gc_during_apply_; 8041 bool apply_success_; 8042 bool gc_success_; 8043 }; 8044 8045 8046 // Test that nothing bad happens if we get a preemption just when we were 8047 // about to do an apply(). 8048 TEST(ApplyInterruption) { 8049 v8::Locker lock; 8050 v8::V8::Initialize(); 8051 v8::HandleScope scope; 8052 Local<Context> local_env; 8053 { 8054 LocalContext env; 8055 local_env = env.local(); 8056 } 8057 8058 // Local context should still be live. 8059 CHECK(!local_env.IsEmpty()); 8060 local_env->Enter(); 8061 8062 // Should complete without problems. 8063 ApplyInterruptTest().RunTest(); 8064 8065 local_env->Exit(); 8066 } 8067 8068 8069 // Verify that we can clone an object 8070 TEST(ObjectClone) { 8071 v8::HandleScope scope; 8072 LocalContext env; 8073 8074 const char* sample = 8075 "var rv = {};" \ 8076 "rv.alpha = 'hello';" \ 8077 "rv.beta = 123;" \ 8078 "rv;"; 8079 8080 // Create an object, verify basics. 8081 Local<Value> val = CompileRun(sample); 8082 CHECK(val->IsObject()); 8083 Local<v8::Object> obj = Local<v8::Object>::Cast(val); 8084 obj->Set(v8_str("gamma"), v8_str("cloneme")); 8085 8086 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha"))); 8087 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 8088 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma"))); 8089 8090 // Clone it. 8091 Local<v8::Object> clone = obj->Clone(); 8092 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha"))); 8093 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta"))); 8094 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma"))); 8095 8096 // Set a property on the clone, verify each object. 8097 clone->Set(v8_str("beta"), v8::Integer::New(456)); 8098 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 8099 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta"))); 8100 } 8101 8102 8103 class AsciiVectorResource : public v8::String::ExternalAsciiStringResource { 8104 public: 8105 explicit AsciiVectorResource(i::Vector<const char> vector) 8106 : data_(vector) {} 8107 virtual ~AsciiVectorResource() {} 8108 virtual size_t length() const { return data_.length(); } 8109 virtual const char* data() const { return data_.start(); } 8110 private: 8111 i::Vector<const char> data_; 8112 }; 8113 8114 8115 class UC16VectorResource : public v8::String::ExternalStringResource { 8116 public: 8117 explicit UC16VectorResource(i::Vector<const i::uc16> vector) 8118 : data_(vector) {} 8119 virtual ~UC16VectorResource() {} 8120 virtual size_t length() const { return data_.length(); } 8121 virtual const i::uc16* data() const { return data_.start(); } 8122 private: 8123 i::Vector<const i::uc16> data_; 8124 }; 8125 8126 8127 static void MorphAString(i::String* string, 8128 AsciiVectorResource* ascii_resource, 8129 UC16VectorResource* uc16_resource) { 8130 CHECK(i::StringShape(string).IsExternal()); 8131 if (string->IsAsciiRepresentation()) { 8132 // Check old map is not symbol or long. 8133 CHECK(string->map() == i::Heap::external_ascii_string_map()); 8134 // Morph external string to be TwoByte string. 8135 string->set_map(i::Heap::external_string_map()); 8136 i::ExternalTwoByteString* morphed = 8137 i::ExternalTwoByteString::cast(string); 8138 morphed->set_resource(uc16_resource); 8139 } else { 8140 // Check old map is not symbol or long. 8141 CHECK(string->map() == i::Heap::external_string_map()); 8142 // Morph external string to be ASCII string. 8143 string->set_map(i::Heap::external_ascii_string_map()); 8144 i::ExternalAsciiString* morphed = 8145 i::ExternalAsciiString::cast(string); 8146 morphed->set_resource(ascii_resource); 8147 } 8148 } 8149 8150 8151 // Test that we can still flatten a string if the components it is built up 8152 // from have been turned into 16 bit strings in the mean time. 8153 THREADED_TEST(MorphCompositeStringTest) { 8154 const char* c_string = "Now is the time for all good men" 8155 " to come to the aid of the party"; 8156 uint16_t* two_byte_string = AsciiToTwoByteString(c_string); 8157 { 8158 v8::HandleScope scope; 8159 LocalContext env; 8160 AsciiVectorResource ascii_resource( 8161 i::Vector<const char>(c_string, i::StrLength(c_string))); 8162 UC16VectorResource uc16_resource( 8163 i::Vector<const uint16_t>(two_byte_string, 8164 i::StrLength(c_string))); 8165 8166 Local<String> lhs(v8::Utils::ToLocal( 8167 i::Factory::NewExternalStringFromAscii(&ascii_resource))); 8168 Local<String> rhs(v8::Utils::ToLocal( 8169 i::Factory::NewExternalStringFromAscii(&ascii_resource))); 8170 8171 env->Global()->Set(v8_str("lhs"), lhs); 8172 env->Global()->Set(v8_str("rhs"), rhs); 8173 8174 CompileRun( 8175 "var cons = lhs + rhs;" 8176 "var slice = lhs.substring(1, lhs.length - 1);" 8177 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);"); 8178 8179 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource); 8180 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource); 8181 8182 // Now do some stuff to make sure the strings are flattened, etc. 8183 CompileRun( 8184 "/[^a-z]/.test(cons);" 8185 "/[^a-z]/.test(slice);" 8186 "/[^a-z]/.test(slice_on_cons);"); 8187 const char* expected_cons = 8188 "Now is the time for all good men to come to the aid of the party" 8189 "Now is the time for all good men to come to the aid of the party"; 8190 const char* expected_slice = 8191 "ow is the time for all good men to come to the aid of the part"; 8192 const char* expected_slice_on_cons = 8193 "ow is the time for all good men to come to the aid of the party" 8194 "Now is the time for all good men to come to the aid of the part"; 8195 CHECK_EQ(String::New(expected_cons), 8196 env->Global()->Get(v8_str("cons"))); 8197 CHECK_EQ(String::New(expected_slice), 8198 env->Global()->Get(v8_str("slice"))); 8199 CHECK_EQ(String::New(expected_slice_on_cons), 8200 env->Global()->Get(v8_str("slice_on_cons"))); 8201 } 8202 } 8203 8204 8205 TEST(CompileExternalTwoByteSource) { 8206 v8::HandleScope scope; 8207 LocalContext context; 8208 8209 // This is a very short list of sources, which currently is to check for a 8210 // regression caused by r2703. 8211 const char* ascii_sources[] = { 8212 "0.5", 8213 "-0.5", // This mainly testes PushBack in the Scanner. 8214 "--0.5", // This mainly testes PushBack in the Scanner. 8215 NULL 8216 }; 8217 8218 // Compile the sources as external two byte strings. 8219 for (int i = 0; ascii_sources[i] != NULL; i++) { 8220 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]); 8221 UC16VectorResource uc16_resource( 8222 i::Vector<const uint16_t>(two_byte_string, 8223 i::StrLength(ascii_sources[i]))); 8224 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource); 8225 v8::Script::Compile(source); 8226 } 8227 } 8228 8229 8230 class RegExpStringModificationTest { 8231 public: 8232 RegExpStringModificationTest() 8233 : block_(i::OS::CreateSemaphore(0)), 8234 morphs_(0), 8235 morphs_during_regexp_(0), 8236 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)), 8237 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {} 8238 ~RegExpStringModificationTest() { delete block_; } 8239 void RunTest() { 8240 regexp_success_ = false; 8241 morph_success_ = false; 8242 8243 // Initialize the contents of two_byte_content_ to be a uc16 representation 8244 // of "aaaaaaaaaaaaaab". 8245 for (int i = 0; i < 14; i++) { 8246 two_byte_content_[i] = 'a'; 8247 } 8248 two_byte_content_[14] = 'b'; 8249 8250 // Create the input string for the regexp - the one we are going to change 8251 // properties of. 8252 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_); 8253 8254 // Inject the input as a global variable. 8255 i::Handle<i::String> input_name = 8256 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5)); 8257 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE); 8258 8259 8260 MorphThread morph_thread(this); 8261 morph_thread.Start(); 8262 v8::Locker::StartPreemption(1); 8263 LongRunningRegExp(); 8264 { 8265 v8::Unlocker unlock; 8266 morph_thread.Join(); 8267 } 8268 v8::Locker::StopPreemption(); 8269 CHECK(regexp_success_); 8270 CHECK(morph_success_); 8271 } 8272 private: 8273 8274 // Number of string modifications required. 8275 static const int kRequiredModifications = 5; 8276 static const int kMaxModifications = 100; 8277 8278 class MorphThread : public i::Thread { 8279 public: 8280 explicit MorphThread(RegExpStringModificationTest* test) 8281 : test_(test) {} 8282 virtual void Run() { 8283 test_->MorphString(); 8284 } 8285 private: 8286 RegExpStringModificationTest* test_; 8287 }; 8288 8289 void MorphString() { 8290 block_->Wait(); 8291 while (morphs_during_regexp_ < kRequiredModifications && 8292 morphs_ < kMaxModifications) { 8293 { 8294 v8::Locker lock; 8295 // Swap string between ascii and two-byte representation. 8296 i::String* string = *input_; 8297 MorphAString(string, &ascii_resource_, &uc16_resource_); 8298 morphs_++; 8299 } 8300 i::OS::Sleep(1); 8301 } 8302 morph_success_ = true; 8303 } 8304 8305 void LongRunningRegExp() { 8306 block_->Signal(); // Enable morphing thread on next preemption. 8307 while (morphs_during_regexp_ < kRequiredModifications && 8308 morphs_ < kMaxModifications) { 8309 int morphs_before = morphs_; 8310 { 8311 // Match 15-30 "a"'s against 14 and a "b". 8312 const char* c_source = 8313 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 8314 ".exec(input) === null"; 8315 Local<String> source = String::New(c_source); 8316 Local<Script> script = Script::Compile(source); 8317 Local<Value> result = script->Run(); 8318 CHECK(result->IsTrue()); 8319 } 8320 int morphs_after = morphs_; 8321 morphs_during_regexp_ += morphs_after - morphs_before; 8322 } 8323 regexp_success_ = true; 8324 } 8325 8326 i::uc16 two_byte_content_[15]; 8327 i::Semaphore* block_; 8328 int morphs_; 8329 int morphs_during_regexp_; 8330 bool regexp_success_; 8331 bool morph_success_; 8332 i::Handle<i::String> input_; 8333 AsciiVectorResource ascii_resource_; 8334 UC16VectorResource uc16_resource_; 8335 }; 8336 8337 8338 // Test that a regular expression execution can be interrupted and 8339 // the string changed without failing. 8340 TEST(RegExpStringModification) { 8341 v8::Locker lock; 8342 v8::V8::Initialize(); 8343 v8::HandleScope scope; 8344 Local<Context> local_env; 8345 { 8346 LocalContext env; 8347 local_env = env.local(); 8348 } 8349 8350 // Local context should still be live. 8351 CHECK(!local_env.IsEmpty()); 8352 local_env->Enter(); 8353 8354 // Should complete without problems. 8355 RegExpStringModificationTest().RunTest(); 8356 8357 local_env->Exit(); 8358 } 8359 8360 8361 // Test that we can set a property on the global object even if there 8362 // is a read-only property in the prototype chain. 8363 TEST(ReadOnlyPropertyInGlobalProto) { 8364 v8::HandleScope scope; 8365 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 8366 LocalContext context(0, templ); 8367 v8::Handle<v8::Object> global = context->Global(); 8368 v8::Handle<v8::Object> global_proto = 8369 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__"))); 8370 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly); 8371 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly); 8372 // Check without 'eval' or 'with'. 8373 v8::Handle<v8::Value> res = 8374 CompileRun("function f() { x = 42; return x; }; f()"); 8375 // Check with 'eval'. 8376 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()"); 8377 CHECK_EQ(v8::Integer::New(42), res); 8378 // Check with 'with'. 8379 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()"); 8380 CHECK_EQ(v8::Integer::New(42), res); 8381 } 8382 8383 static int force_set_set_count = 0; 8384 static int force_set_get_count = 0; 8385 bool pass_on_get = false; 8386 8387 static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name, 8388 const v8::AccessorInfo& info) { 8389 force_set_get_count++; 8390 if (pass_on_get) { 8391 return v8::Handle<v8::Value>(); 8392 } else { 8393 return v8::Int32::New(3); 8394 } 8395 } 8396 8397 static void ForceSetSetter(v8::Local<v8::String> name, 8398 v8::Local<v8::Value> value, 8399 const v8::AccessorInfo& info) { 8400 force_set_set_count++; 8401 } 8402 8403 static v8::Handle<v8::Value> ForceSetInterceptSetter( 8404 v8::Local<v8::String> name, 8405 v8::Local<v8::Value> value, 8406 const v8::AccessorInfo& info) { 8407 force_set_set_count++; 8408 return v8::Undefined(); 8409 } 8410 8411 TEST(ForceSet) { 8412 force_set_get_count = 0; 8413 force_set_set_count = 0; 8414 pass_on_get = false; 8415 8416 v8::HandleScope scope; 8417 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 8418 v8::Handle<v8::String> access_property = v8::String::New("a"); 8419 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter); 8420 LocalContext context(NULL, templ); 8421 v8::Handle<v8::Object> global = context->Global(); 8422 8423 // Ordinary properties 8424 v8::Handle<v8::String> simple_property = v8::String::New("p"); 8425 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly); 8426 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 8427 // This should fail because the property is read-only 8428 global->Set(simple_property, v8::Int32::New(5)); 8429 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 8430 // This should succeed even though the property is read-only 8431 global->ForceSet(simple_property, v8::Int32::New(6)); 8432 CHECK_EQ(6, global->Get(simple_property)->Int32Value()); 8433 8434 // Accessors 8435 CHECK_EQ(0, force_set_set_count); 8436 CHECK_EQ(0, force_set_get_count); 8437 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 8438 // CHECK_EQ the property shouldn't override it, just call the setter 8439 // which in this case does nothing. 8440 global->Set(access_property, v8::Int32::New(7)); 8441 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 8442 CHECK_EQ(1, force_set_set_count); 8443 CHECK_EQ(2, force_set_get_count); 8444 // Forcing the property to be set should override the accessor without 8445 // calling it 8446 global->ForceSet(access_property, v8::Int32::New(8)); 8447 CHECK_EQ(8, global->Get(access_property)->Int32Value()); 8448 CHECK_EQ(1, force_set_set_count); 8449 CHECK_EQ(2, force_set_get_count); 8450 } 8451 8452 TEST(ForceSetWithInterceptor) { 8453 force_set_get_count = 0; 8454 force_set_set_count = 0; 8455 pass_on_get = false; 8456 8457 v8::HandleScope scope; 8458 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 8459 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter); 8460 LocalContext context(NULL, templ); 8461 v8::Handle<v8::Object> global = context->Global(); 8462 8463 v8::Handle<v8::String> some_property = v8::String::New("a"); 8464 CHECK_EQ(0, force_set_set_count); 8465 CHECK_EQ(0, force_set_get_count); 8466 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 8467 // Setting the property shouldn't override it, just call the setter 8468 // which in this case does nothing. 8469 global->Set(some_property, v8::Int32::New(7)); 8470 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 8471 CHECK_EQ(1, force_set_set_count); 8472 CHECK_EQ(2, force_set_get_count); 8473 // Getting the property when the interceptor returns an empty handle 8474 // should yield undefined, since the property isn't present on the 8475 // object itself yet. 8476 pass_on_get = true; 8477 CHECK(global->Get(some_property)->IsUndefined()); 8478 CHECK_EQ(1, force_set_set_count); 8479 CHECK_EQ(3, force_set_get_count); 8480 // Forcing the property to be set should cause the value to be 8481 // set locally without calling the interceptor. 8482 global->ForceSet(some_property, v8::Int32::New(8)); 8483 CHECK_EQ(8, global->Get(some_property)->Int32Value()); 8484 CHECK_EQ(1, force_set_set_count); 8485 CHECK_EQ(4, force_set_get_count); 8486 // Reenabling the interceptor should cause it to take precedence over 8487 // the property 8488 pass_on_get = false; 8489 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 8490 CHECK_EQ(1, force_set_set_count); 8491 CHECK_EQ(5, force_set_get_count); 8492 // The interceptor should also work for other properties 8493 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value()); 8494 CHECK_EQ(1, force_set_set_count); 8495 CHECK_EQ(6, force_set_get_count); 8496 } 8497 8498 8499 THREADED_TEST(ForceDelete) { 8500 v8::HandleScope scope; 8501 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 8502 LocalContext context(NULL, templ); 8503 v8::Handle<v8::Object> global = context->Global(); 8504 8505 // Ordinary properties 8506 v8::Handle<v8::String> simple_property = v8::String::New("p"); 8507 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete); 8508 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 8509 // This should fail because the property is dont-delete. 8510 CHECK(!global->Delete(simple_property)); 8511 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 8512 // This should succeed even though the property is dont-delete. 8513 CHECK(global->ForceDelete(simple_property)); 8514 CHECK(global->Get(simple_property)->IsUndefined()); 8515 } 8516 8517 8518 static int force_delete_interceptor_count = 0; 8519 static bool pass_on_delete = false; 8520 8521 8522 static v8::Handle<v8::Boolean> ForceDeleteDeleter( 8523 v8::Local<v8::String> name, 8524 const v8::AccessorInfo& info) { 8525 force_delete_interceptor_count++; 8526 if (pass_on_delete) { 8527 return v8::Handle<v8::Boolean>(); 8528 } else { 8529 return v8::True(); 8530 } 8531 } 8532 8533 8534 THREADED_TEST(ForceDeleteWithInterceptor) { 8535 force_delete_interceptor_count = 0; 8536 pass_on_delete = false; 8537 8538 v8::HandleScope scope; 8539 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 8540 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter); 8541 LocalContext context(NULL, templ); 8542 v8::Handle<v8::Object> global = context->Global(); 8543 8544 v8::Handle<v8::String> some_property = v8::String::New("a"); 8545 global->Set(some_property, v8::Integer::New(42), v8::DontDelete); 8546 8547 // Deleting a property should get intercepted and nothing should 8548 // happen. 8549 CHECK_EQ(0, force_delete_interceptor_count); 8550 CHECK(global->Delete(some_property)); 8551 CHECK_EQ(1, force_delete_interceptor_count); 8552 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 8553 // Deleting the property when the interceptor returns an empty 8554 // handle should not delete the property since it is DontDelete. 8555 pass_on_delete = true; 8556 CHECK(!global->Delete(some_property)); 8557 CHECK_EQ(2, force_delete_interceptor_count); 8558 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 8559 // Forcing the property to be deleted should delete the value 8560 // without calling the interceptor. 8561 CHECK(global->ForceDelete(some_property)); 8562 CHECK(global->Get(some_property)->IsUndefined()); 8563 CHECK_EQ(2, force_delete_interceptor_count); 8564 } 8565 8566 8567 // Make sure that forcing a delete invalidates any IC stubs, so we 8568 // don't read the hole value. 8569 THREADED_TEST(ForceDeleteIC) { 8570 v8::HandleScope scope; 8571 LocalContext context; 8572 // Create a DontDelete variable on the global object. 8573 CompileRun("this.__proto__ = { foo: 'horse' };" 8574 "var foo = 'fish';" 8575 "function f() { return foo.length; }"); 8576 // Initialize the IC for foo in f. 8577 CompileRun("for (var i = 0; i < 4; i++) f();"); 8578 // Make sure the value of foo is correct before the deletion. 8579 CHECK_EQ(4, CompileRun("f()")->Int32Value()); 8580 // Force the deletion of foo. 8581 CHECK(context->Global()->ForceDelete(v8_str("foo"))); 8582 // Make sure the value for foo is read from the prototype, and that 8583 // we don't get in trouble with reading the deleted cell value 8584 // sentinel. 8585 CHECK_EQ(5, CompileRun("f()")->Int32Value()); 8586 } 8587 8588 8589 v8::Persistent<Context> calling_context0; 8590 v8::Persistent<Context> calling_context1; 8591 v8::Persistent<Context> calling_context2; 8592 8593 8594 // Check that the call to the callback is initiated in 8595 // calling_context2, the directly calling context is calling_context1 8596 // and the callback itself is in calling_context0. 8597 static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) { 8598 ApiTestFuzzer::Fuzz(); 8599 CHECK(Context::GetCurrent() == calling_context0); 8600 CHECK(Context::GetCalling() == calling_context1); 8601 CHECK(Context::GetEntered() == calling_context2); 8602 return v8::Integer::New(42); 8603 } 8604 8605 8606 THREADED_TEST(GetCallingContext) { 8607 v8::HandleScope scope; 8608 8609 calling_context0 = Context::New(); 8610 calling_context1 = Context::New(); 8611 calling_context2 = Context::New(); 8612 8613 // Allow cross-domain access. 8614 Local<String> token = v8_str("<security token>"); 8615 calling_context0->SetSecurityToken(token); 8616 calling_context1->SetSecurityToken(token); 8617 calling_context2->SetSecurityToken(token); 8618 8619 // Create an object with a C++ callback in context0. 8620 calling_context0->Enter(); 8621 Local<v8::FunctionTemplate> callback_templ = 8622 v8::FunctionTemplate::New(GetCallingContextCallback); 8623 calling_context0->Global()->Set(v8_str("callback"), 8624 callback_templ->GetFunction()); 8625 calling_context0->Exit(); 8626 8627 // Expose context0 in context1 and setup a function that calls the 8628 // callback function. 8629 calling_context1->Enter(); 8630 calling_context1->Global()->Set(v8_str("context0"), 8631 calling_context0->Global()); 8632 CompileRun("function f() { context0.callback() }"); 8633 calling_context1->Exit(); 8634 8635 // Expose context1 in context2 and call the callback function in 8636 // context0 indirectly through f in context1. 8637 calling_context2->Enter(); 8638 calling_context2->Global()->Set(v8_str("context1"), 8639 calling_context1->Global()); 8640 CompileRun("context1.f()"); 8641 calling_context2->Exit(); 8642 8643 // Dispose the contexts to allow them to be garbage collected. 8644 calling_context0.Dispose(); 8645 calling_context1.Dispose(); 8646 calling_context2.Dispose(); 8647 calling_context0.Clear(); 8648 calling_context1.Clear(); 8649 calling_context2.Clear(); 8650 } 8651 8652 8653 // Check that a variable declaration with no explicit initialization 8654 // value does not shadow an existing property in the prototype chain. 8655 // 8656 // This is consistent with Firefox and Safari. 8657 // 8658 // See http://crbug.com/12548. 8659 THREADED_TEST(InitGlobalVarInProtoChain) { 8660 v8::HandleScope scope; 8661 LocalContext context; 8662 // Introduce a variable in the prototype chain. 8663 CompileRun("__proto__.x = 42"); 8664 v8::Handle<v8::Value> result = CompileRun("var x; x"); 8665 CHECK(!result->IsUndefined()); 8666 CHECK_EQ(42, result->Int32Value()); 8667 } 8668 8669 8670 // Regression test for issue 398. 8671 // If a function is added to an object, creating a constant function 8672 // field, and the result is cloned, replacing the constant function on the 8673 // original should not affect the clone. 8674 // See http://code.google.com/p/v8/issues/detail?id=398 8675 THREADED_TEST(ReplaceConstantFunction) { 8676 v8::HandleScope scope; 8677 LocalContext context; 8678 v8::Handle<v8::Object> obj = v8::Object::New(); 8679 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 8680 v8::Handle<v8::String> foo_string = v8::String::New("foo"); 8681 obj->Set(foo_string, func_templ->GetFunction()); 8682 v8::Handle<v8::Object> obj_clone = obj->Clone(); 8683 obj_clone->Set(foo_string, v8::String::New("Hello")); 8684 CHECK(!obj->Get(foo_string)->IsUndefined()); 8685 } 8686 8687 8688 // Regression test for http://crbug.com/16276. 8689 THREADED_TEST(Regress16276) { 8690 v8::HandleScope scope; 8691 LocalContext context; 8692 // Force the IC in f to be a dictionary load IC. 8693 CompileRun("function f(obj) { return obj.x; }\n" 8694 "var obj = { x: { foo: 42 }, y: 87 };\n" 8695 "var x = obj.x;\n" 8696 "delete obj.y;\n" 8697 "for (var i = 0; i < 5; i++) f(obj);"); 8698 // Detach the global object to make 'this' refer directly to the 8699 // global object (not the proxy), and make sure that the dictionary 8700 // load IC doesn't mess up loading directly from the global object. 8701 context->DetachGlobal(); 8702 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value()); 8703 } 8704 8705 8706 THREADED_TEST(PixelArray) { 8707 v8::HandleScope scope; 8708 LocalContext context; 8709 const int kElementCount = 260; 8710 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); 8711 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount, 8712 pixel_data); 8713 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 8714 for (int i = 0; i < kElementCount; i++) { 8715 pixels->set(i, i % 256); 8716 } 8717 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 8718 for (int i = 0; i < kElementCount; i++) { 8719 CHECK_EQ(i % 256, pixels->get(i)); 8720 CHECK_EQ(i % 256, pixel_data[i]); 8721 } 8722 8723 v8::Handle<v8::Object> obj = v8::Object::New(); 8724 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 8725 // Set the elements to be the pixels. 8726 // jsobj->set_elements(*pixels); 8727 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount); 8728 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value()); 8729 obj->Set(v8_str("field"), v8::Int32::New(1503)); 8730 context->Global()->Set(v8_str("pixels"), obj); 8731 v8::Handle<v8::Value> result = CompileRun("pixels.field"); 8732 CHECK_EQ(1503, result->Int32Value()); 8733 result = CompileRun("pixels[1]"); 8734 CHECK_EQ(1, result->Int32Value()); 8735 8736 result = CompileRun("var sum = 0;" 8737 "for (var i = 0; i < 8; i++) {" 8738 " sum += pixels[i] = pixels[i] = -i;" 8739 "}" 8740 "sum;"); 8741 CHECK_EQ(-28, result->Int32Value()); 8742 8743 result = CompileRun("var sum = 0;" 8744 "for (var i = 0; i < 8; i++) {" 8745 " sum += pixels[i] = pixels[i] = 0;" 8746 "}" 8747 "sum;"); 8748 CHECK_EQ(0, result->Int32Value()); 8749 8750 result = CompileRun("var sum = 0;" 8751 "for (var i = 0; i < 8; i++) {" 8752 " sum += pixels[i] = pixels[i] = 255;" 8753 "}" 8754 "sum;"); 8755 CHECK_EQ(8 * 255, result->Int32Value()); 8756 8757 result = CompileRun("var sum = 0;" 8758 "for (var i = 0; i < 8; i++) {" 8759 " sum += pixels[i] = pixels[i] = 256 + i;" 8760 "}" 8761 "sum;"); 8762 CHECK_EQ(2076, result->Int32Value()); 8763 8764 result = CompileRun("var sum = 0;" 8765 "for (var i = 0; i < 8; i++) {" 8766 " sum += pixels[i] = pixels[i] = i;" 8767 "}" 8768 "sum;"); 8769 CHECK_EQ(28, result->Int32Value()); 8770 8771 result = CompileRun("var sum = 0;" 8772 "for (var i = 0; i < 8; i++) {" 8773 " sum += pixels[i];" 8774 "}" 8775 "sum;"); 8776 CHECK_EQ(28, result->Int32Value()); 8777 8778 i::Handle<i::Smi> value(i::Smi::FromInt(2)); 8779 i::SetElement(jsobj, 1, value); 8780 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value()); 8781 *value.location() = i::Smi::FromInt(256); 8782 i::SetElement(jsobj, 1, value); 8783 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value()); 8784 *value.location() = i::Smi::FromInt(-1); 8785 i::SetElement(jsobj, 1, value); 8786 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value()); 8787 8788 result = CompileRun("for (var i = 0; i < 8; i++) {" 8789 " pixels[i] = (i * 65) - 109;" 8790 "}" 8791 "pixels[1] + pixels[6];"); 8792 CHECK_EQ(255, result->Int32Value()); 8793 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value()); 8794 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value()); 8795 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value()); 8796 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value()); 8797 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value()); 8798 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value()); 8799 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value()); 8800 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value()); 8801 result = CompileRun("var sum = 0;" 8802 "for (var i = 0; i < 8; i++) {" 8803 " sum += pixels[i];" 8804 "}" 8805 "sum;"); 8806 CHECK_EQ(984, result->Int32Value()); 8807 8808 result = CompileRun("for (var i = 0; i < 8; i++) {" 8809 " pixels[i] = (i * 1.1);" 8810 "}" 8811 "pixels[1] + pixels[6];"); 8812 CHECK_EQ(8, result->Int32Value()); 8813 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value()); 8814 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value()); 8815 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value()); 8816 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value()); 8817 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value()); 8818 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value()); 8819 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value()); 8820 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value()); 8821 8822 result = CompileRun("for (var i = 0; i < 8; i++) {" 8823 " pixels[7] = undefined;" 8824 "}" 8825 "pixels[7];"); 8826 CHECK_EQ(0, result->Int32Value()); 8827 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value()); 8828 8829 result = CompileRun("for (var i = 0; i < 8; i++) {" 8830 " pixels[6] = '2.3';" 8831 "}" 8832 "pixels[6];"); 8833 CHECK_EQ(2, result->Int32Value()); 8834 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value()); 8835 8836 result = CompileRun("for (var i = 0; i < 8; i++) {" 8837 " pixels[5] = NaN;" 8838 "}" 8839 "pixels[5];"); 8840 CHECK_EQ(0, result->Int32Value()); 8841 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 8842 8843 result = CompileRun("for (var i = 0; i < 8; i++) {" 8844 " pixels[8] = Infinity;" 8845 "}" 8846 "pixels[8];"); 8847 CHECK_EQ(255, result->Int32Value()); 8848 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value()); 8849 8850 result = CompileRun("for (var i = 0; i < 8; i++) {" 8851 " pixels[9] = -Infinity;" 8852 "}" 8853 "pixels[9];"); 8854 CHECK_EQ(0, result->Int32Value()); 8855 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value()); 8856 8857 result = CompileRun("pixels[3] = 33;" 8858 "delete pixels[3];" 8859 "pixels[3];"); 8860 CHECK_EQ(33, result->Int32Value()); 8861 8862 result = CompileRun("pixels[0] = 10; pixels[1] = 11;" 8863 "pixels[2] = 12; pixels[3] = 13;" 8864 "pixels.__defineGetter__('2'," 8865 "function() { return 120; });" 8866 "pixels[2];"); 8867 CHECK_EQ(12, result->Int32Value()); 8868 8869 result = CompileRun("var js_array = new Array(40);" 8870 "js_array[0] = 77;" 8871 "js_array;"); 8872 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 8873 8874 result = CompileRun("pixels[1] = 23;" 8875 "pixels.__proto__ = [];" 8876 "js_array.__proto__ = pixels;" 8877 "js_array.concat(pixels);"); 8878 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 8879 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 8880 8881 result = CompileRun("pixels[1] = 23;"); 8882 CHECK_EQ(23, result->Int32Value()); 8883 8884 // Test for index greater than 255. Regression test for: 8885 // http://code.google.com/p/chromium/issues/detail?id=26337. 8886 result = CompileRun("pixels[256] = 255;"); 8887 CHECK_EQ(255, result->Int32Value()); 8888 result = CompileRun("var i = 0;" 8889 "for (var j = 0; j < 8; j++) { i = pixels[256]; }" 8890 "i"); 8891 CHECK_EQ(255, result->Int32Value()); 8892 8893 free(pixel_data); 8894 } 8895 8896 8897 template <class ExternalArrayClass, class ElementType> 8898 static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, 8899 int64_t low, 8900 int64_t high) { 8901 v8::HandleScope scope; 8902 LocalContext context; 8903 const int kElementCount = 40; 8904 int element_size = 0; 8905 switch (array_type) { 8906 case v8::kExternalByteArray: 8907 case v8::kExternalUnsignedByteArray: 8908 element_size = 1; 8909 break; 8910 case v8::kExternalShortArray: 8911 case v8::kExternalUnsignedShortArray: 8912 element_size = 2; 8913 break; 8914 case v8::kExternalIntArray: 8915 case v8::kExternalUnsignedIntArray: 8916 case v8::kExternalFloatArray: 8917 element_size = 4; 8918 break; 8919 default: 8920 UNREACHABLE(); 8921 break; 8922 } 8923 ElementType* array_data = 8924 static_cast<ElementType*>(malloc(kElementCount * element_size)); 8925 i::Handle<ExternalArrayClass> array = 8926 i::Handle<ExternalArrayClass>::cast( 8927 i::Factory::NewExternalArray(kElementCount, array_type, array_data)); 8928 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 8929 for (int i = 0; i < kElementCount; i++) { 8930 array->set(i, static_cast<ElementType>(i)); 8931 } 8932 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 8933 for (int i = 0; i < kElementCount; i++) { 8934 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i))); 8935 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i])); 8936 } 8937 8938 v8::Handle<v8::Object> obj = v8::Object::New(); 8939 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 8940 // Set the elements to be the external array. 8941 obj->SetIndexedPropertiesToExternalArrayData(array_data, 8942 array_type, 8943 kElementCount); 8944 CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number())); 8945 obj->Set(v8_str("field"), v8::Int32::New(1503)); 8946 context->Global()->Set(v8_str("ext_array"), obj); 8947 v8::Handle<v8::Value> result = CompileRun("ext_array.field"); 8948 CHECK_EQ(1503, result->Int32Value()); 8949 result = CompileRun("ext_array[1]"); 8950 CHECK_EQ(1, result->Int32Value()); 8951 8952 // Check pass through of assigned smis 8953 result = CompileRun("var sum = 0;" 8954 "for (var i = 0; i < 8; i++) {" 8955 " sum += ext_array[i] = ext_array[i] = -i;" 8956 "}" 8957 "sum;"); 8958 CHECK_EQ(-28, result->Int32Value()); 8959 8960 // Check assigned smis 8961 result = CompileRun("for (var i = 0; i < 8; i++) {" 8962 " ext_array[i] = i;" 8963 "}" 8964 "var sum = 0;" 8965 "for (var i = 0; i < 8; i++) {" 8966 " sum += ext_array[i];" 8967 "}" 8968 "sum;"); 8969 CHECK_EQ(28, result->Int32Value()); 8970 8971 // Check assigned smis in reverse order 8972 result = CompileRun("for (var i = 8; --i >= 0; ) {" 8973 " ext_array[i] = i;" 8974 "}" 8975 "var sum = 0;" 8976 "for (var i = 0; i < 8; i++) {" 8977 " sum += ext_array[i];" 8978 "}" 8979 "sum;"); 8980 CHECK_EQ(28, result->Int32Value()); 8981 8982 // Check pass through of assigned HeapNumbers 8983 result = CompileRun("var sum = 0;" 8984 "for (var i = 0; i < 16; i+=2) {" 8985 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);" 8986 "}" 8987 "sum;"); 8988 CHECK_EQ(-28, result->Int32Value()); 8989 8990 // Check assigned HeapNumbers 8991 result = CompileRun("for (var i = 0; i < 16; i+=2) {" 8992 " ext_array[i] = (i * 0.5);" 8993 "}" 8994 "var sum = 0;" 8995 "for (var i = 0; i < 16; i+=2) {" 8996 " sum += ext_array[i];" 8997 "}" 8998 "sum;"); 8999 CHECK_EQ(28, result->Int32Value()); 9000 9001 // Check assigned HeapNumbers in reverse order 9002 result = CompileRun("for (var i = 14; i >= 0; i-=2) {" 9003 " ext_array[i] = (i * 0.5);" 9004 "}" 9005 "var sum = 0;" 9006 "for (var i = 0; i < 16; i+=2) {" 9007 " sum += ext_array[i];" 9008 "}" 9009 "sum;"); 9010 CHECK_EQ(28, result->Int32Value()); 9011 9012 i::ScopedVector<char> test_buf(1024); 9013 9014 // Check legal boundary conditions. 9015 // The repeated loads and stores ensure the ICs are exercised. 9016 const char* boundary_program = 9017 "var res = 0;" 9018 "for (var i = 0; i < 16; i++) {" 9019 " ext_array[i] = %lld;" 9020 " if (i > 8) {" 9021 " res = ext_array[i];" 9022 " }" 9023 "}" 9024 "res;"; 9025 i::OS::SNPrintF(test_buf, 9026 boundary_program, 9027 low); 9028 result = CompileRun(test_buf.start()); 9029 CHECK_EQ(low, result->IntegerValue()); 9030 9031 i::OS::SNPrintF(test_buf, 9032 boundary_program, 9033 high); 9034 result = CompileRun(test_buf.start()); 9035 CHECK_EQ(high, result->IntegerValue()); 9036 9037 // Check misprediction of type in IC. 9038 result = CompileRun("var tmp_array = ext_array;" 9039 "var sum = 0;" 9040 "for (var i = 0; i < 8; i++) {" 9041 " tmp_array[i] = i;" 9042 " sum += tmp_array[i];" 9043 " if (i == 4) {" 9044 " tmp_array = {};" 9045 " }" 9046 "}" 9047 "sum;"); 9048 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 9049 CHECK_EQ(28, result->Int32Value()); 9050 9051 // Make sure out-of-range loads do not throw. 9052 i::OS::SNPrintF(test_buf, 9053 "var caught_exception = false;" 9054 "try {" 9055 " ext_array[%d];" 9056 "} catch (e) {" 9057 " caught_exception = true;" 9058 "}" 9059 "caught_exception;", 9060 kElementCount); 9061 result = CompileRun(test_buf.start()); 9062 CHECK_EQ(false, result->BooleanValue()); 9063 9064 // Make sure out-of-range stores do not throw. 9065 i::OS::SNPrintF(test_buf, 9066 "var caught_exception = false;" 9067 "try {" 9068 " ext_array[%d] = 1;" 9069 "} catch (e) {" 9070 " caught_exception = true;" 9071 "}" 9072 "caught_exception;", 9073 kElementCount); 9074 result = CompileRun(test_buf.start()); 9075 CHECK_EQ(false, result->BooleanValue()); 9076 9077 // Check other boundary conditions, values and operations. 9078 result = CompileRun("for (var i = 0; i < 8; i++) {" 9079 " ext_array[7] = undefined;" 9080 "}" 9081 "ext_array[7];"); 9082 CHECK_EQ(0, result->Int32Value()); 9083 CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number())); 9084 9085 result = CompileRun("for (var i = 0; i < 8; i++) {" 9086 " ext_array[6] = '2.3';" 9087 "}" 9088 "ext_array[6];"); 9089 CHECK_EQ(2, result->Int32Value()); 9090 CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number())); 9091 9092 if (array_type != v8::kExternalFloatArray) { 9093 // Though the specification doesn't state it, be explicit about 9094 // converting NaNs and +/-Infinity to zero. 9095 result = CompileRun("for (var i = 0; i < 8; i++) {" 9096 " ext_array[i] = 5;" 9097 "}" 9098 "for (var i = 0; i < 8; i++) {" 9099 " ext_array[i] = NaN;" 9100 "}" 9101 "ext_array[5];"); 9102 CHECK_EQ(0, result->Int32Value()); 9103 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 9104 9105 result = CompileRun("for (var i = 0; i < 8; i++) {" 9106 " ext_array[i] = 5;" 9107 "}" 9108 "for (var i = 0; i < 8; i++) {" 9109 " ext_array[i] = Infinity;" 9110 "}" 9111 "ext_array[5];"); 9112 CHECK_EQ(0, result->Int32Value()); 9113 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 9114 9115 result = CompileRun("for (var i = 0; i < 8; i++) {" 9116 " ext_array[i] = 5;" 9117 "}" 9118 "for (var i = 0; i < 8; i++) {" 9119 " ext_array[i] = -Infinity;" 9120 "}" 9121 "ext_array[5];"); 9122 CHECK_EQ(0, result->Int32Value()); 9123 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 9124 } 9125 9126 result = CompileRun("ext_array[3] = 33;" 9127 "delete ext_array[3];" 9128 "ext_array[3];"); 9129 CHECK_EQ(33, result->Int32Value()); 9130 9131 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;" 9132 "ext_array[2] = 12; ext_array[3] = 13;" 9133 "ext_array.__defineGetter__('2'," 9134 "function() { return 120; });" 9135 "ext_array[2];"); 9136 CHECK_EQ(12, result->Int32Value()); 9137 9138 result = CompileRun("var js_array = new Array(40);" 9139 "js_array[0] = 77;" 9140 "js_array;"); 9141 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 9142 9143 result = CompileRun("ext_array[1] = 23;" 9144 "ext_array.__proto__ = [];" 9145 "js_array.__proto__ = ext_array;" 9146 "js_array.concat(ext_array);"); 9147 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 9148 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 9149 9150 result = CompileRun("ext_array[1] = 23;"); 9151 CHECK_EQ(23, result->Int32Value()); 9152 9153 // Test more complex manipulations which cause eax to contain values 9154 // that won't be completely overwritten by loads from the arrays. 9155 // This catches bugs in the instructions used for the KeyedLoadIC 9156 // for byte and word types. 9157 { 9158 const int kXSize = 300; 9159 const int kYSize = 300; 9160 const int kLargeElementCount = kXSize * kYSize * 4; 9161 ElementType* large_array_data = 9162 static_cast<ElementType*>(malloc(kLargeElementCount * element_size)); 9163 i::Handle<ExternalArrayClass> large_array = 9164 i::Handle<ExternalArrayClass>::cast( 9165 i::Factory::NewExternalArray(kLargeElementCount, 9166 array_type, 9167 array_data)); 9168 v8::Handle<v8::Object> large_obj = v8::Object::New(); 9169 // Set the elements to be the external array. 9170 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data, 9171 array_type, 9172 kLargeElementCount); 9173 context->Global()->Set(v8_str("large_array"), large_obj); 9174 // Initialize contents of a few rows. 9175 for (int x = 0; x < 300; x++) { 9176 int row = 0; 9177 int offset = row * 300 * 4; 9178 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 9179 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 9180 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 9181 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 9182 row = 150; 9183 offset = row * 300 * 4; 9184 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 9185 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 9186 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 9187 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 9188 row = 298; 9189 offset = row * 300 * 4; 9190 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 9191 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 9192 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 9193 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 9194 } 9195 // The goal of the code below is to make "offset" large enough 9196 // that the computation of the index (which goes into eax) has 9197 // high bits set which will not be overwritten by a byte or short 9198 // load. 9199 result = CompileRun("var failed = false;" 9200 "var offset = 0;" 9201 "for (var i = 0; i < 300; i++) {" 9202 " if (large_array[4 * i] != 127 ||" 9203 " large_array[4 * i + 1] != 0 ||" 9204 " large_array[4 * i + 2] != 0 ||" 9205 " large_array[4 * i + 3] != 127) {" 9206 " failed = true;" 9207 " }" 9208 "}" 9209 "offset = 150 * 300 * 4;" 9210 "for (var i = 0; i < 300; i++) {" 9211 " if (large_array[offset + 4 * i] != 127 ||" 9212 " large_array[offset + 4 * i + 1] != 0 ||" 9213 " large_array[offset + 4 * i + 2] != 0 ||" 9214 " large_array[offset + 4 * i + 3] != 127) {" 9215 " failed = true;" 9216 " }" 9217 "}" 9218 "offset = 298 * 300 * 4;" 9219 "for (var i = 0; i < 300; i++) {" 9220 " if (large_array[offset + 4 * i] != 127 ||" 9221 " large_array[offset + 4 * i + 1] != 0 ||" 9222 " large_array[offset + 4 * i + 2] != 0 ||" 9223 " large_array[offset + 4 * i + 3] != 127) {" 9224 " failed = true;" 9225 " }" 9226 "}" 9227 "!failed;"); 9228 CHECK_EQ(true, result->BooleanValue()); 9229 free(large_array_data); 9230 } 9231 9232 free(array_data); 9233 } 9234 9235 9236 THREADED_TEST(ExternalByteArray) { 9237 ExternalArrayTestHelper<v8::internal::ExternalByteArray, int8_t>( 9238 v8::kExternalByteArray, 9239 -128, 9240 127); 9241 } 9242 9243 9244 THREADED_TEST(ExternalUnsignedByteArray) { 9245 ExternalArrayTestHelper<v8::internal::ExternalUnsignedByteArray, uint8_t>( 9246 v8::kExternalUnsignedByteArray, 9247 0, 9248 255); 9249 } 9250 9251 9252 THREADED_TEST(ExternalShortArray) { 9253 ExternalArrayTestHelper<v8::internal::ExternalShortArray, int16_t>( 9254 v8::kExternalShortArray, 9255 -32768, 9256 32767); 9257 } 9258 9259 9260 THREADED_TEST(ExternalUnsignedShortArray) { 9261 ExternalArrayTestHelper<v8::internal::ExternalUnsignedShortArray, uint16_t>( 9262 v8::kExternalUnsignedShortArray, 9263 0, 9264 65535); 9265 } 9266 9267 9268 THREADED_TEST(ExternalIntArray) { 9269 ExternalArrayTestHelper<v8::internal::ExternalIntArray, int32_t>( 9270 v8::kExternalIntArray, 9271 INT_MIN, // -2147483648 9272 INT_MAX); // 2147483647 9273 } 9274 9275 9276 THREADED_TEST(ExternalUnsignedIntArray) { 9277 ExternalArrayTestHelper<v8::internal::ExternalUnsignedIntArray, uint32_t>( 9278 v8::kExternalUnsignedIntArray, 9279 0, 9280 UINT_MAX); // 4294967295 9281 } 9282 9283 9284 THREADED_TEST(ExternalFloatArray) { 9285 ExternalArrayTestHelper<v8::internal::ExternalFloatArray, float>( 9286 v8::kExternalFloatArray, 9287 -500, 9288 500); 9289 } 9290 9291 9292 THREADED_TEST(ExternalArrays) { 9293 TestExternalByteArray(); 9294 TestExternalUnsignedByteArray(); 9295 TestExternalShortArray(); 9296 TestExternalUnsignedShortArray(); 9297 TestExternalIntArray(); 9298 TestExternalUnsignedIntArray(); 9299 TestExternalFloatArray(); 9300 } 9301 9302 9303 THREADED_TEST(ScriptContextDependence) { 9304 v8::HandleScope scope; 9305 LocalContext c1; 9306 const char *source = "foo"; 9307 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source)); 9308 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source)); 9309 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100)); 9310 CHECK_EQ(dep->Run()->Int32Value(), 100); 9311 CHECK_EQ(indep->Run()->Int32Value(), 100); 9312 LocalContext c2; 9313 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101)); 9314 CHECK_EQ(dep->Run()->Int32Value(), 100); 9315 CHECK_EQ(indep->Run()->Int32Value(), 101); 9316 } 9317 9318 9319 THREADED_TEST(StackTrace) { 9320 v8::HandleScope scope; 9321 LocalContext context; 9322 v8::TryCatch try_catch; 9323 const char *source = "function foo() { FAIL.FAIL; }; foo();"; 9324 v8::Handle<v8::String> src = v8::String::New(source); 9325 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test"); 9326 v8::Script::New(src, origin)->Run(); 9327 CHECK(try_catch.HasCaught()); 9328 v8::String::Utf8Value stack(try_catch.StackTrace()); 9329 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL); 9330 } 9331 9332 9333 // Test that idle notification can be handled and eventually returns true. 9334 THREADED_TEST(IdleNotification) { 9335 bool rv = false; 9336 for (int i = 0; i < 100; i++) { 9337 rv = v8::V8::IdleNotification(); 9338 if (rv) 9339 break; 9340 } 9341 CHECK(rv == true); 9342 } 9343 9344 9345 static uint32_t* stack_limit; 9346 9347 static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) { 9348 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit()); 9349 return v8::Undefined(); 9350 } 9351 9352 9353 // Uses the address of a local variable to determine the stack top now. 9354 // Given a size, returns an address that is that far from the current 9355 // top of stack. 9356 static uint32_t* ComputeStackLimit(uint32_t size) { 9357 uint32_t* answer = &size - (size / sizeof(size)); 9358 // If the size is very large and the stack is very near the bottom of 9359 // memory then the calculation above may wrap around and give an address 9360 // that is above the (downwards-growing) stack. In that case we return 9361 // a very low address. 9362 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size)); 9363 return answer; 9364 } 9365 9366 9367 TEST(SetResourceConstraints) { 9368 static const int K = 1024; 9369 uint32_t* set_limit = ComputeStackLimit(128 * K); 9370 9371 // Set stack limit. 9372 v8::ResourceConstraints constraints; 9373 constraints.set_stack_limit(set_limit); 9374 CHECK(v8::SetResourceConstraints(&constraints)); 9375 9376 // Execute a script. 9377 v8::HandleScope scope; 9378 LocalContext env; 9379 Local<v8::FunctionTemplate> fun_templ = 9380 v8::FunctionTemplate::New(GetStackLimitCallback); 9381 Local<Function> fun = fun_templ->GetFunction(); 9382 env->Global()->Set(v8_str("get_stack_limit"), fun); 9383 CompileRun("get_stack_limit();"); 9384 9385 CHECK(stack_limit == set_limit); 9386 } 9387 9388 9389 TEST(SetResourceConstraintsInThread) { 9390 uint32_t* set_limit; 9391 { 9392 v8::Locker locker; 9393 static const int K = 1024; 9394 set_limit = ComputeStackLimit(128 * K); 9395 9396 // Set stack limit. 9397 v8::ResourceConstraints constraints; 9398 constraints.set_stack_limit(set_limit); 9399 CHECK(v8::SetResourceConstraints(&constraints)); 9400 9401 // Execute a script. 9402 v8::HandleScope scope; 9403 LocalContext env; 9404 Local<v8::FunctionTemplate> fun_templ = 9405 v8::FunctionTemplate::New(GetStackLimitCallback); 9406 Local<Function> fun = fun_templ->GetFunction(); 9407 env->Global()->Set(v8_str("get_stack_limit"), fun); 9408 CompileRun("get_stack_limit();"); 9409 9410 CHECK(stack_limit == set_limit); 9411 } 9412 { 9413 v8::Locker locker; 9414 CHECK(stack_limit == set_limit); 9415 } 9416 } 9417 9418 9419 THREADED_TEST(GetHeapStatistics) { 9420 v8::HandleScope scope; 9421 LocalContext c1; 9422 v8::HeapStatistics heap_statistics; 9423 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0); 9424 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0); 9425 v8::V8::GetHeapStatistics(&heap_statistics); 9426 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0); 9427 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0); 9428 } 9429 9430 9431 static double DoubleFromBits(uint64_t value) { 9432 double target; 9433 #ifdef BIG_ENDIAN_FLOATING_POINT 9434 const int kIntSize = 4; 9435 // Somebody swapped the lower and higher half of doubles. 9436 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); 9437 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); 9438 #else 9439 memcpy(&target, &value, sizeof(target)); 9440 #endif 9441 return target; 9442 } 9443 9444 9445 static uint64_t DoubleToBits(double value) { 9446 uint64_t target; 9447 #ifdef BIG_ENDIAN_FLOATING_POINT 9448 const int kIntSize = 4; 9449 // Somebody swapped the lower and higher half of doubles. 9450 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); 9451 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); 9452 #else 9453 memcpy(&target, &value, sizeof(target)); 9454 #endif 9455 return target; 9456 } 9457 9458 9459 static double DoubleToDateTime(double input) { 9460 double date_limit = 864e13; 9461 if (IsNaN(input) || input < -date_limit || input > date_limit) { 9462 return i::OS::nan_value(); 9463 } 9464 return (input < 0) ? -(floor(-input)) : floor(input); 9465 } 9466 9467 // We don't have a consistent way to write 64-bit constants syntactically, so we 9468 // split them into two 32-bit constants and combine them programmatically. 9469 static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) { 9470 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits); 9471 } 9472 9473 9474 THREADED_TEST(QuietSignalingNaNs) { 9475 v8::HandleScope scope; 9476 LocalContext context; 9477 v8::TryCatch try_catch; 9478 9479 // Special double values. 9480 double snan = DoubleFromBits(0x7ff00000, 0x00000001); 9481 double qnan = DoubleFromBits(0x7ff80000, 0x00000000); 9482 double infinity = DoubleFromBits(0x7ff00000, 0x00000000); 9483 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu); 9484 double min_normal = DoubleFromBits(0x00100000, 0x00000000); 9485 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu); 9486 double min_denormal = DoubleFromBits(0x00000000, 0x00000001); 9487 9488 // Date values are capped at +/-100000000 days (times 864e5 ms per day) 9489 // on either side of the epoch. 9490 double date_limit = 864e13; 9491 9492 double test_values[] = { 9493 snan, 9494 qnan, 9495 infinity, 9496 max_normal, 9497 date_limit + 1, 9498 date_limit, 9499 min_normal, 9500 max_denormal, 9501 min_denormal, 9502 0, 9503 -0, 9504 -min_denormal, 9505 -max_denormal, 9506 -min_normal, 9507 -date_limit, 9508 -date_limit - 1, 9509 -max_normal, 9510 -infinity, 9511 -qnan, 9512 -snan 9513 }; 9514 int num_test_values = 20; 9515 9516 for (int i = 0; i < num_test_values; i++) { 9517 double test_value = test_values[i]; 9518 9519 // Check that Number::New preserves non-NaNs and quiets SNaNs. 9520 v8::Handle<v8::Value> number = v8::Number::New(test_value); 9521 double stored_number = number->NumberValue(); 9522 if (!IsNaN(test_value)) { 9523 CHECK_EQ(test_value, stored_number); 9524 } else { 9525 uint64_t stored_bits = DoubleToBits(stored_number); 9526 // Check if quiet nan (bits 51..62 all set). 9527 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 9528 } 9529 9530 // Check that Date::New preserves non-NaNs in the date range and 9531 // quiets SNaNs. 9532 v8::Handle<v8::Value> date = v8::Date::New(test_value); 9533 double expected_stored_date = DoubleToDateTime(test_value); 9534 double stored_date = date->NumberValue(); 9535 if (!IsNaN(expected_stored_date)) { 9536 CHECK_EQ(expected_stored_date, stored_date); 9537 } else { 9538 uint64_t stored_bits = DoubleToBits(stored_date); 9539 // Check if quiet nan (bits 51..62 all set). 9540 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 9541 } 9542 } 9543 } 9544 9545 9546 static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) { 9547 v8::HandleScope scope; 9548 v8::TryCatch tc; 9549 v8::Handle<v8::String> str = args[0]->ToString(); 9550 if (tc.HasCaught()) 9551 return tc.ReThrow(); 9552 return v8::Undefined(); 9553 } 9554 9555 9556 // Test that an exception can be propagated down through a spaghetti 9557 // stack using ReThrow. 9558 THREADED_TEST(SpaghettiStackReThrow) { 9559 v8::HandleScope scope; 9560 LocalContext context; 9561 context->Global()->Set( 9562 v8::String::New("s"), 9563 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction()); 9564 v8::TryCatch try_catch; 9565 CompileRun( 9566 "var i = 0;" 9567 "var o = {" 9568 " toString: function () {" 9569 " if (i == 10) {" 9570 " throw 'Hey!';" 9571 " } else {" 9572 " i++;" 9573 " return s(o);" 9574 " }" 9575 " }" 9576 "};" 9577 "s(o);"); 9578 CHECK(try_catch.HasCaught()); 9579 v8::String::Utf8Value value(try_catch.Exception()); 9580 CHECK_EQ(0, strcmp(*value, "Hey!")); 9581 } 9582 9583 9584 TEST(Regress528) { 9585 v8::V8::Initialize(); 9586 9587 v8::HandleScope scope; 9588 v8::Persistent<Context> context; 9589 v8::Persistent<Context> other_context; 9590 int gc_count; 9591 9592 // Create a context used to keep the code from aging in the compilation 9593 // cache. 9594 other_context = Context::New(); 9595 9596 // Context-dependent context data creates reference from the compilation 9597 // cache to the global object. 9598 const char* source_simple = "1"; 9599 context = Context::New(); 9600 { 9601 v8::HandleScope scope; 9602 9603 context->Enter(); 9604 Local<v8::String> obj = v8::String::New(""); 9605 context->SetData(obj); 9606 CompileRun(source_simple); 9607 context->Exit(); 9608 } 9609 context.Dispose(); 9610 for (gc_count = 1; gc_count < 10; gc_count++) { 9611 other_context->Enter(); 9612 CompileRun(source_simple); 9613 other_context->Exit(); 9614 v8::internal::Heap::CollectAllGarbage(false); 9615 if (GetGlobalObjectsCount() == 1) break; 9616 } 9617 CHECK_GE(2, gc_count); 9618 CHECK_EQ(1, GetGlobalObjectsCount()); 9619 9620 // Eval in a function creates reference from the compilation cache to the 9621 // global object. 9622 const char* source_eval = "function f(){eval('1')}; f()"; 9623 context = Context::New(); 9624 { 9625 v8::HandleScope scope; 9626 9627 context->Enter(); 9628 CompileRun(source_eval); 9629 context->Exit(); 9630 } 9631 context.Dispose(); 9632 for (gc_count = 1; gc_count < 10; gc_count++) { 9633 other_context->Enter(); 9634 CompileRun(source_eval); 9635 other_context->Exit(); 9636 v8::internal::Heap::CollectAllGarbage(false); 9637 if (GetGlobalObjectsCount() == 1) break; 9638 } 9639 CHECK_GE(2, gc_count); 9640 CHECK_EQ(1, GetGlobalObjectsCount()); 9641 9642 // Looking up the line number for an exception creates reference from the 9643 // compilation cache to the global object. 9644 const char* source_exception = "function f(){throw 1;} f()"; 9645 context = Context::New(); 9646 { 9647 v8::HandleScope scope; 9648 9649 context->Enter(); 9650 v8::TryCatch try_catch; 9651 CompileRun(source_exception); 9652 CHECK(try_catch.HasCaught()); 9653 v8::Handle<v8::Message> message = try_catch.Message(); 9654 CHECK(!message.IsEmpty()); 9655 CHECK_EQ(1, message->GetLineNumber()); 9656 context->Exit(); 9657 } 9658 context.Dispose(); 9659 for (gc_count = 1; gc_count < 10; gc_count++) { 9660 other_context->Enter(); 9661 CompileRun(source_exception); 9662 other_context->Exit(); 9663 v8::internal::Heap::CollectAllGarbage(false); 9664 if (GetGlobalObjectsCount() == 1) break; 9665 } 9666 CHECK_GE(2, gc_count); 9667 CHECK_EQ(1, GetGlobalObjectsCount()); 9668 9669 other_context.Dispose(); 9670 } 9671 9672 9673 THREADED_TEST(ScriptOrigin) { 9674 v8::HandleScope scope; 9675 LocalContext env; 9676 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 9677 v8::Handle<v8::String> script = v8::String::New( 9678 "function f() {}\n\nfunction g() {}"); 9679 v8::Script::Compile(script, &origin)->Run(); 9680 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 9681 env->Global()->Get(v8::String::New("f"))); 9682 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 9683 env->Global()->Get(v8::String::New("g"))); 9684 9685 v8::ScriptOrigin script_origin_f = f->GetScriptOrigin(); 9686 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName())); 9687 CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value()); 9688 9689 v8::ScriptOrigin script_origin_g = g->GetScriptOrigin(); 9690 CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName())); 9691 CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value()); 9692 } 9693 9694 9695 THREADED_TEST(ScriptLineNumber) { 9696 v8::HandleScope scope; 9697 LocalContext env; 9698 v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); 9699 v8::Handle<v8::String> script = v8::String::New( 9700 "function f() {}\n\nfunction g() {}"); 9701 v8::Script::Compile(script, &origin)->Run(); 9702 v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( 9703 env->Global()->Get(v8::String::New("f"))); 9704 v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( 9705 env->Global()->Get(v8::String::New("g"))); 9706 CHECK_EQ(0, f->GetScriptLineNumber()); 9707 CHECK_EQ(2, g->GetScriptLineNumber()); 9708 } 9709 9710 9711 static v8::Handle<Value> GetterWhichReturns42(Local<String> name, 9712 const AccessorInfo& info) { 9713 return v8_num(42); 9714 } 9715 9716 9717 static void SetterWhichSetsYOnThisTo23(Local<String> name, 9718 Local<Value> value, 9719 const AccessorInfo& info) { 9720 info.This()->Set(v8_str("y"), v8_num(23)); 9721 } 9722 9723 9724 THREADED_TEST(SetterOnConstructorPrototype) { 9725 v8::HandleScope scope; 9726 Local<ObjectTemplate> templ = ObjectTemplate::New(); 9727 templ->SetAccessor(v8_str("x"), 9728 GetterWhichReturns42, 9729 SetterWhichSetsYOnThisTo23); 9730 LocalContext context; 9731 context->Global()->Set(v8_str("P"), templ->NewInstance()); 9732 CompileRun("function C1() {" 9733 " this.x = 23;" 9734 "};" 9735 "C1.prototype = P;" 9736 "function C2() {" 9737 " this.x = 23" 9738 "};" 9739 "C2.prototype = { };" 9740 "C2.prototype.__proto__ = P;"); 9741 9742 v8::Local<v8::Script> script; 9743 script = v8::Script::Compile(v8_str("new C1();")); 9744 for (int i = 0; i < 10; i++) { 9745 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 9746 CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); 9747 CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); 9748 } 9749 9750 script = v8::Script::Compile(v8_str("new C2();")); 9751 for (int i = 0; i < 10; i++) { 9752 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 9753 CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value()); 9754 CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value()); 9755 } 9756 } 9757 9758 9759 static v8::Handle<Value> NamedPropertyGetterWhichReturns42( 9760 Local<String> name, const AccessorInfo& info) { 9761 return v8_num(42); 9762 } 9763 9764 9765 static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23( 9766 Local<String> name, Local<Value> value, const AccessorInfo& info) { 9767 if (name->Equals(v8_str("x"))) { 9768 info.This()->Set(v8_str("y"), v8_num(23)); 9769 } 9770 return v8::Handle<Value>(); 9771 } 9772 9773 9774 THREADED_TEST(InterceptorOnConstructorPrototype) { 9775 v8::HandleScope scope; 9776 Local<ObjectTemplate> templ = ObjectTemplate::New(); 9777 templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42, 9778 NamedPropertySetterWhichSetsYOnThisTo23); 9779 LocalContext context; 9780 context->Global()->Set(v8_str("P"), templ->NewInstance()); 9781 CompileRun("function C1() {" 9782 " this.x = 23;" 9783 "};" 9784 "C1.prototype = P;" 9785 "function C2() {" 9786 " this.x = 23" 9787 "};" 9788 "C2.prototype = { };" 9789 "C2.prototype.__proto__ = P;"); 9790 9791 v8::Local<v8::Script> script; 9792 script = v8::Script::Compile(v8_str("new C1();")); 9793 for (int i = 0; i < 10; i++) { 9794 v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); 9795 CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); 9796 CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); 9797 } 9798 9799 script = v8::Script::Compile(v8_str("new C2();")); 9800 for (int i = 0; i < 10; i++) { 9801 v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); 9802 CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value()); 9803 CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value()); 9804 } 9805 } 9806