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