test-api.cc revision 888f6729be6a6f6fbe246cb5a9f122e2dbe455b7
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 = 300; // 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 3605 3606TEST(CompilationErrorUsingTryCatchHandler) { 3607 v8::HandleScope scope; 3608 LocalContext env; 3609 v8::TryCatch try_catch; 3610 Script::Compile(v8_str("This doesn't &*&@#$&*^ compile.")); 3611 CHECK_NE(NULL, *try_catch.Exception()); 3612 CHECK(try_catch.HasCaught()); 3613} 3614 3615 3616TEST(TryCatchFinallyUsingTryCatchHandler) { 3617 v8::HandleScope scope; 3618 LocalContext env; 3619 v8::TryCatch try_catch; 3620 Script::Compile(v8_str("try { throw ''; } catch (e) {}"))->Run(); 3621 CHECK(!try_catch.HasCaught()); 3622 Script::Compile(v8_str("try { throw ''; } finally {}"))->Run(); 3623 CHECK(try_catch.HasCaught()); 3624 try_catch.Reset(); 3625 Script::Compile(v8_str("(function() {" 3626 "try { throw ''; } finally { return; }" 3627 "})()"))->Run(); 3628 CHECK(!try_catch.HasCaught()); 3629 Script::Compile(v8_str("(function()" 3630 " { try { throw ''; } finally { throw 0; }" 3631 "})()"))->Run(); 3632 CHECK(try_catch.HasCaught()); 3633} 3634 3635 3636// SecurityHandler can't be run twice 3637TEST(SecurityHandler) { 3638 v8::HandleScope scope0; 3639 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 3640 global_template->SetAccessCheckCallbacks(NamedSecurityTestCallback, 3641 IndexedSecurityTestCallback); 3642 // Create an environment 3643 v8::Persistent<Context> context0 = 3644 Context::New(NULL, global_template); 3645 context0->Enter(); 3646 3647 v8::Handle<v8::Object> global0 = context0->Global(); 3648 v8::Handle<Script> script0 = v8_compile("foo = 111"); 3649 script0->Run(); 3650 global0->Set(v8_str("0"), v8_num(999)); 3651 v8::Handle<Value> foo0 = global0->Get(v8_str("foo")); 3652 CHECK_EQ(111, foo0->Int32Value()); 3653 v8::Handle<Value> z0 = global0->Get(v8_str("0")); 3654 CHECK_EQ(999, z0->Int32Value()); 3655 3656 // Create another environment, should fail security checks. 3657 v8::HandleScope scope1; 3658 3659 v8::Persistent<Context> context1 = 3660 Context::New(NULL, global_template); 3661 context1->Enter(); 3662 3663 v8::Handle<v8::Object> global1 = context1->Global(); 3664 global1->Set(v8_str("othercontext"), global0); 3665 // This set will fail the security check. 3666 v8::Handle<Script> script1 = 3667 v8_compile("othercontext.foo = 222; othercontext[0] = 888;"); 3668 script1->Run(); 3669 // This read will pass the security check. 3670 v8::Handle<Value> foo1 = global0->Get(v8_str("foo")); 3671 CHECK_EQ(111, foo1->Int32Value()); 3672 // This read will pass the security check. 3673 v8::Handle<Value> z1 = global0->Get(v8_str("0")); 3674 CHECK_EQ(999, z1->Int32Value()); 3675 3676 // Create another environment, should pass security checks. 3677 { g_security_callback_result = true; // allow security handler to pass. 3678 v8::HandleScope scope2; 3679 LocalContext context2; 3680 v8::Handle<v8::Object> global2 = context2->Global(); 3681 global2->Set(v8_str("othercontext"), global0); 3682 v8::Handle<Script> script2 = 3683 v8_compile("othercontext.foo = 333; othercontext[0] = 888;"); 3684 script2->Run(); 3685 v8::Handle<Value> foo2 = global0->Get(v8_str("foo")); 3686 CHECK_EQ(333, foo2->Int32Value()); 3687 v8::Handle<Value> z2 = global0->Get(v8_str("0")); 3688 CHECK_EQ(888, z2->Int32Value()); 3689 } 3690 3691 context1->Exit(); 3692 context1.Dispose(); 3693 3694 context0->Exit(); 3695 context0.Dispose(); 3696} 3697 3698 3699THREADED_TEST(SecurityChecks) { 3700 v8::HandleScope handle_scope; 3701 LocalContext env1; 3702 v8::Persistent<Context> env2 = Context::New(); 3703 3704 Local<Value> foo = v8_str("foo"); 3705 Local<Value> bar = v8_str("bar"); 3706 3707 // Set to the same domain. 3708 env1->SetSecurityToken(foo); 3709 3710 // Create a function in env1. 3711 Script::Compile(v8_str("spy=function(){return spy;}"))->Run(); 3712 Local<Value> spy = env1->Global()->Get(v8_str("spy")); 3713 CHECK(spy->IsFunction()); 3714 3715 // Create another function accessing global objects. 3716 Script::Compile(v8_str("spy2=function(){return new this.Array();}"))->Run(); 3717 Local<Value> spy2 = env1->Global()->Get(v8_str("spy2")); 3718 CHECK(spy2->IsFunction()); 3719 3720 // Switch to env2 in the same domain and invoke spy on env2. 3721 { 3722 env2->SetSecurityToken(foo); 3723 // Enter env2 3724 Context::Scope scope_env2(env2); 3725 Local<Value> result = Function::Cast(*spy)->Call(env2->Global(), 0, NULL); 3726 CHECK(result->IsFunction()); 3727 } 3728 3729 { 3730 env2->SetSecurityToken(bar); 3731 Context::Scope scope_env2(env2); 3732 3733 // Call cross_domain_call, it should throw an exception 3734 v8::TryCatch try_catch; 3735 Function::Cast(*spy2)->Call(env2->Global(), 0, NULL); 3736 CHECK(try_catch.HasCaught()); 3737 } 3738 3739 env2.Dispose(); 3740} 3741 3742 3743// Regression test case for issue 1183439. 3744THREADED_TEST(SecurityChecksForPrototypeChain) { 3745 v8::HandleScope scope; 3746 LocalContext current; 3747 v8::Persistent<Context> other = Context::New(); 3748 3749 // Change context to be able to get to the Object function in the 3750 // other context without hitting the security checks. 3751 v8::Local<Value> other_object; 3752 { Context::Scope scope(other); 3753 other_object = other->Global()->Get(v8_str("Object")); 3754 other->Global()->Set(v8_num(42), v8_num(87)); 3755 } 3756 3757 current->Global()->Set(v8_str("other"), other->Global()); 3758 CHECK(v8_compile("other")->Run()->Equals(other->Global())); 3759 3760 // Make sure the security check fails here and we get an undefined 3761 // result instead of getting the Object function. Repeat in a loop 3762 // to make sure to exercise the IC code. 3763 v8::Local<Script> access_other0 = v8_compile("other.Object"); 3764 v8::Local<Script> access_other1 = v8_compile("other[42]"); 3765 for (int i = 0; i < 5; i++) { 3766 CHECK(!access_other0->Run()->Equals(other_object)); 3767 CHECK(access_other0->Run()->IsUndefined()); 3768 CHECK(!access_other1->Run()->Equals(v8_num(87))); 3769 CHECK(access_other1->Run()->IsUndefined()); 3770 } 3771 3772 // Create an object that has 'other' in its prototype chain and make 3773 // sure we cannot access the Object function indirectly through 3774 // that. Repeat in a loop to make sure to exercise the IC code. 3775 v8_compile("function F() { };" 3776 "F.prototype = other;" 3777 "var f = new F();")->Run(); 3778 v8::Local<Script> access_f0 = v8_compile("f.Object"); 3779 v8::Local<Script> access_f1 = v8_compile("f[42]"); 3780 for (int j = 0; j < 5; j++) { 3781 CHECK(!access_f0->Run()->Equals(other_object)); 3782 CHECK(access_f0->Run()->IsUndefined()); 3783 CHECK(!access_f1->Run()->Equals(v8_num(87))); 3784 CHECK(access_f1->Run()->IsUndefined()); 3785 } 3786 3787 // Now it gets hairy: Set the prototype for the other global object 3788 // to be the current global object. The prototype chain for 'f' now 3789 // goes through 'other' but ends up in the current global object. 3790 { Context::Scope scope(other); 3791 other->Global()->Set(v8_str("__proto__"), current->Global()); 3792 } 3793 // Set a named and an index property on the current global 3794 // object. To force the lookup to go through the other global object, 3795 // the properties must not exist in the other global object. 3796 current->Global()->Set(v8_str("foo"), v8_num(100)); 3797 current->Global()->Set(v8_num(99), v8_num(101)); 3798 // Try to read the properties from f and make sure that the access 3799 // gets stopped by the security checks on the other global object. 3800 Local<Script> access_f2 = v8_compile("f.foo"); 3801 Local<Script> access_f3 = v8_compile("f[99]"); 3802 for (int k = 0; k < 5; k++) { 3803 CHECK(!access_f2->Run()->Equals(v8_num(100))); 3804 CHECK(access_f2->Run()->IsUndefined()); 3805 CHECK(!access_f3->Run()->Equals(v8_num(101))); 3806 CHECK(access_f3->Run()->IsUndefined()); 3807 } 3808 other.Dispose(); 3809} 3810 3811 3812THREADED_TEST(CrossDomainDelete) { 3813 v8::HandleScope handle_scope; 3814 LocalContext env1; 3815 v8::Persistent<Context> env2 = Context::New(); 3816 3817 Local<Value> foo = v8_str("foo"); 3818 Local<Value> bar = v8_str("bar"); 3819 3820 // Set to the same domain. 3821 env1->SetSecurityToken(foo); 3822 env2->SetSecurityToken(foo); 3823 3824 env1->Global()->Set(v8_str("prop"), v8_num(3)); 3825 env2->Global()->Set(v8_str("env1"), env1->Global()); 3826 3827 // Change env2 to a different domain and delete env1.prop. 3828 env2->SetSecurityToken(bar); 3829 { 3830 Context::Scope scope_env2(env2); 3831 Local<Value> result = 3832 Script::Compile(v8_str("delete env1.prop"))->Run(); 3833 CHECK(result->IsFalse()); 3834 } 3835 3836 // Check that env1.prop still exists. 3837 Local<Value> v = env1->Global()->Get(v8_str("prop")); 3838 CHECK(v->IsNumber()); 3839 CHECK_EQ(3, v->Int32Value()); 3840 3841 env2.Dispose(); 3842} 3843 3844 3845THREADED_TEST(CrossDomainIsPropertyEnumerable) { 3846 v8::HandleScope handle_scope; 3847 LocalContext env1; 3848 v8::Persistent<Context> env2 = Context::New(); 3849 3850 Local<Value> foo = v8_str("foo"); 3851 Local<Value> bar = v8_str("bar"); 3852 3853 // Set to the same domain. 3854 env1->SetSecurityToken(foo); 3855 env2->SetSecurityToken(foo); 3856 3857 env1->Global()->Set(v8_str("prop"), v8_num(3)); 3858 env2->Global()->Set(v8_str("env1"), env1->Global()); 3859 3860 // env1.prop is enumerable in env2. 3861 Local<String> test = v8_str("propertyIsEnumerable.call(env1, 'prop')"); 3862 { 3863 Context::Scope scope_env2(env2); 3864 Local<Value> result = Script::Compile(test)->Run(); 3865 CHECK(result->IsTrue()); 3866 } 3867 3868 // Change env2 to a different domain and test again. 3869 env2->SetSecurityToken(bar); 3870 { 3871 Context::Scope scope_env2(env2); 3872 Local<Value> result = Script::Compile(test)->Run(); 3873 CHECK(result->IsFalse()); 3874 } 3875 3876 env2.Dispose(); 3877} 3878 3879 3880THREADED_TEST(CrossDomainForIn) { 3881 v8::HandleScope handle_scope; 3882 LocalContext env1; 3883 v8::Persistent<Context> env2 = Context::New(); 3884 3885 Local<Value> foo = v8_str("foo"); 3886 Local<Value> bar = v8_str("bar"); 3887 3888 // Set to the same domain. 3889 env1->SetSecurityToken(foo); 3890 env2->SetSecurityToken(foo); 3891 3892 env1->Global()->Set(v8_str("prop"), v8_num(3)); 3893 env2->Global()->Set(v8_str("env1"), env1->Global()); 3894 3895 // Change env2 to a different domain and set env1's global object 3896 // as the __proto__ of an object in env2 and enumerate properties 3897 // in for-in. It shouldn't enumerate properties on env1's global 3898 // object. 3899 env2->SetSecurityToken(bar); 3900 { 3901 Context::Scope scope_env2(env2); 3902 Local<Value> result = 3903 CompileRun("(function(){var obj = {'__proto__':env1};" 3904 "for (var p in obj)" 3905 " if (p == 'prop') return false;" 3906 "return true;})()"); 3907 CHECK(result->IsTrue()); 3908 } 3909 env2.Dispose(); 3910} 3911 3912 3913TEST(ContextDetachGlobal) { 3914 v8::HandleScope handle_scope; 3915 LocalContext env1; 3916 v8::Persistent<Context> env2 = Context::New(); 3917 3918 Local<v8::Object> global1 = env1->Global(); 3919 3920 Local<Value> foo = v8_str("foo"); 3921 3922 // Set to the same domain. 3923 env1->SetSecurityToken(foo); 3924 env2->SetSecurityToken(foo); 3925 3926 // Enter env2 3927 env2->Enter(); 3928 3929 // Create a function in env1 3930 Local<v8::Object> global2 = env2->Global(); 3931 global2->Set(v8_str("prop"), v8::Integer::New(1)); 3932 CompileRun("function getProp() {return prop;}"); 3933 3934 env1->Global()->Set(v8_str("getProp"), 3935 global2->Get(v8_str("getProp"))); 3936 3937 // Detach env1's global, and reuse the global object of env1 3938 env2->Exit(); 3939 env2->DetachGlobal(); 3940 // env2 has a new global object. 3941 CHECK(!env2->Global()->Equals(global2)); 3942 3943 v8::Persistent<Context> env3 = 3944 Context::New(0, v8::Handle<v8::ObjectTemplate>(), global2); 3945 env3->SetSecurityToken(v8_str("bar")); 3946 env3->Enter(); 3947 3948 Local<v8::Object> global3 = env3->Global(); 3949 CHECK_EQ(global2, global3); 3950 CHECK(global3->Get(v8_str("prop"))->IsUndefined()); 3951 CHECK(global3->Get(v8_str("getProp"))->IsUndefined()); 3952 global3->Set(v8_str("prop"), v8::Integer::New(-1)); 3953 global3->Set(v8_str("prop2"), v8::Integer::New(2)); 3954 env3->Exit(); 3955 3956 // Call getProp in env1, and it should return the value 1 3957 { 3958 Local<Value> get_prop = global1->Get(v8_str("getProp")); 3959 CHECK(get_prop->IsFunction()); 3960 v8::TryCatch try_catch; 3961 Local<Value> r = Function::Cast(*get_prop)->Call(global1, 0, NULL); 3962 CHECK(!try_catch.HasCaught()); 3963 CHECK_EQ(1, r->Int32Value()); 3964 } 3965 3966 // Check that env3 is not accessible from env1 3967 { 3968 Local<Value> r = global3->Get(v8_str("prop2")); 3969 CHECK(r->IsUndefined()); 3970 } 3971 3972 env2.Dispose(); 3973 env3.Dispose(); 3974} 3975 3976 3977static bool NamedAccessBlocker(Local<v8::Object> global, 3978 Local<Value> name, 3979 v8::AccessType type, 3980 Local<Value> data) { 3981 return Context::GetCurrent()->Global()->Equals(global); 3982} 3983 3984 3985static bool IndexedAccessBlocker(Local<v8::Object> global, 3986 uint32_t key, 3987 v8::AccessType type, 3988 Local<Value> data) { 3989 return Context::GetCurrent()->Global()->Equals(global); 3990} 3991 3992 3993static int g_echo_value = -1; 3994static v8::Handle<Value> EchoGetter(Local<String> name, 3995 const AccessorInfo& info) { 3996 return v8_num(g_echo_value); 3997} 3998 3999 4000static void EchoSetter(Local<String> name, 4001 Local<Value> value, 4002 const AccessorInfo&) { 4003 if (value->IsNumber()) 4004 g_echo_value = value->Int32Value(); 4005} 4006 4007 4008static v8::Handle<Value> UnreachableGetter(Local<String> name, 4009 const AccessorInfo& info) { 4010 CHECK(false); // This function should not be called.. 4011 return v8::Undefined(); 4012} 4013 4014 4015static void UnreachableSetter(Local<String>, Local<Value>, 4016 const AccessorInfo&) { 4017 CHECK(false); // This function should nto be called. 4018} 4019 4020 4021THREADED_TEST(AccessControl) { 4022 v8::HandleScope handle_scope; 4023 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 4024 4025 global_template->SetAccessCheckCallbacks(NamedAccessBlocker, 4026 IndexedAccessBlocker); 4027 4028 // Add an accessor accessible by cross-domain JS code. 4029 global_template->SetAccessor( 4030 v8_str("accessible_prop"), 4031 EchoGetter, EchoSetter, 4032 v8::Handle<Value>(), 4033 v8::AccessControl(v8::ALL_CAN_READ | v8::ALL_CAN_WRITE)); 4034 4035 // Add an accessor that is not accessible by cross-domain JS code. 4036 global_template->SetAccessor(v8_str("blocked_prop"), 4037 UnreachableGetter, UnreachableSetter, 4038 v8::Handle<Value>(), 4039 v8::DEFAULT); 4040 4041 // Create an environment 4042 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 4043 context0->Enter(); 4044 4045 v8::Handle<v8::Object> global0 = context0->Global(); 4046 4047 v8::HandleScope scope1; 4048 4049 v8::Persistent<Context> context1 = Context::New(); 4050 context1->Enter(); 4051 4052 v8::Handle<v8::Object> global1 = context1->Global(); 4053 global1->Set(v8_str("other"), global0); 4054 4055 v8::Handle<Value> value; 4056 4057 // Access blocked property 4058 value = v8_compile("other.blocked_prop = 1")->Run(); 4059 value = v8_compile("other.blocked_prop")->Run(); 4060 CHECK(value->IsUndefined()); 4061 4062 value = v8_compile("propertyIsEnumerable.call(other, 'blocked_prop')")->Run(); 4063 CHECK(value->IsFalse()); 4064 4065 // Access accessible property 4066 value = v8_compile("other.accessible_prop = 3")->Run(); 4067 CHECK(value->IsNumber()); 4068 CHECK_EQ(3, value->Int32Value()); 4069 4070 value = v8_compile("other.accessible_prop")->Run(); 4071 CHECK(value->IsNumber()); 4072 CHECK_EQ(3, value->Int32Value()); 4073 4074 value = 4075 v8_compile("propertyIsEnumerable.call(other, 'accessible_prop')")->Run(); 4076 CHECK(value->IsTrue()); 4077 4078 // Enumeration doesn't enumerate accessors from inaccessible objects in 4079 // the prototype chain even if the accessors are in themselves accessible. 4080 Local<Value> result = 4081 CompileRun("(function(){var obj = {'__proto__':other};" 4082 "for (var p in obj)" 4083 " if (p == 'accessible_prop' || p == 'blocked_prop') {" 4084 " return false;" 4085 " }" 4086 "return true;})()"); 4087 CHECK(result->IsTrue()); 4088 4089 context1->Exit(); 4090 context0->Exit(); 4091 context1.Dispose(); 4092 context0.Dispose(); 4093} 4094 4095 4096static v8::Handle<Value> ConstTenGetter(Local<String> name, 4097 const AccessorInfo& info) { 4098 return v8_num(10); 4099} 4100 4101 4102THREADED_TEST(CrossDomainAccessors) { 4103 v8::HandleScope handle_scope; 4104 4105 v8::Handle<v8::FunctionTemplate> func_template = v8::FunctionTemplate::New(); 4106 4107 v8::Handle<v8::ObjectTemplate> global_template = 4108 func_template->InstanceTemplate(); 4109 4110 v8::Handle<v8::ObjectTemplate> proto_template = 4111 func_template->PrototypeTemplate(); 4112 4113 // Add an accessor to proto that's accessible by cross-domain JS code. 4114 proto_template->SetAccessor(v8_str("accessible"), 4115 ConstTenGetter, 0, 4116 v8::Handle<Value>(), 4117 v8::ALL_CAN_READ); 4118 4119 // Add an accessor that is not accessible by cross-domain JS code. 4120 global_template->SetAccessor(v8_str("unreachable"), 4121 UnreachableGetter, 0, 4122 v8::Handle<Value>(), 4123 v8::DEFAULT); 4124 4125 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 4126 context0->Enter(); 4127 4128 Local<v8::Object> global = context0->Global(); 4129 // Add a normal property that shadows 'accessible' 4130 global->Set(v8_str("accessible"), v8_num(11)); 4131 4132 // Enter a new context. 4133 v8::HandleScope scope1; 4134 v8::Persistent<Context> context1 = Context::New(); 4135 context1->Enter(); 4136 4137 v8::Handle<v8::Object> global1 = context1->Global(); 4138 global1->Set(v8_str("other"), global); 4139 4140 // Should return 10, instead of 11 4141 v8::Handle<Value> value = v8_compile("other.accessible")->Run(); 4142 CHECK(value->IsNumber()); 4143 CHECK_EQ(10, value->Int32Value()); 4144 4145 value = v8_compile("other.unreachable")->Run(); 4146 CHECK(value->IsUndefined()); 4147 4148 context1->Exit(); 4149 context0->Exit(); 4150 context1.Dispose(); 4151 context0.Dispose(); 4152} 4153 4154 4155static int named_access_count = 0; 4156static int indexed_access_count = 0; 4157 4158static bool NamedAccessCounter(Local<v8::Object> global, 4159 Local<Value> name, 4160 v8::AccessType type, 4161 Local<Value> data) { 4162 named_access_count++; 4163 return true; 4164} 4165 4166 4167static bool IndexedAccessCounter(Local<v8::Object> global, 4168 uint32_t key, 4169 v8::AccessType type, 4170 Local<Value> data) { 4171 indexed_access_count++; 4172 return true; 4173} 4174 4175 4176// This one is too easily disturbed by other tests. 4177TEST(AccessControlIC) { 4178 named_access_count = 0; 4179 indexed_access_count = 0; 4180 4181 v8::HandleScope handle_scope; 4182 4183 // Create an environment. 4184 v8::Persistent<Context> context0 = Context::New(); 4185 context0->Enter(); 4186 4187 // Create an object that requires access-check functions to be 4188 // called for cross-domain access. 4189 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 4190 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 4191 IndexedAccessCounter); 4192 Local<v8::Object> object = object_template->NewInstance(); 4193 4194 v8::HandleScope scope1; 4195 4196 // Create another environment. 4197 v8::Persistent<Context> context1 = Context::New(); 4198 context1->Enter(); 4199 4200 // Make easy access to the object from the other environment. 4201 v8::Handle<v8::Object> global1 = context1->Global(); 4202 global1->Set(v8_str("obj"), object); 4203 4204 v8::Handle<Value> value; 4205 4206 // Check that the named access-control function is called every time. 4207 CompileRun("function testProp(obj) {" 4208 " for (var i = 0; i < 10; i++) obj.prop = 1;" 4209 " for (var j = 0; j < 10; j++) obj.prop;" 4210 " return obj.prop" 4211 "}"); 4212 value = CompileRun("testProp(obj)"); 4213 CHECK(value->IsNumber()); 4214 CHECK_EQ(1, value->Int32Value()); 4215 CHECK_EQ(21, named_access_count); 4216 4217 // Check that the named access-control function is called every time. 4218 CompileRun("var p = 'prop';" 4219 "function testKeyed(obj) {" 4220 " for (var i = 0; i < 10; i++) obj[p] = 1;" 4221 " for (var j = 0; j < 10; j++) obj[p];" 4222 " return obj[p];" 4223 "}"); 4224 // Use obj which requires access checks. No inline caching is used 4225 // in that case. 4226 value = CompileRun("testKeyed(obj)"); 4227 CHECK(value->IsNumber()); 4228 CHECK_EQ(1, value->Int32Value()); 4229 CHECK_EQ(42, named_access_count); 4230 // Force the inline caches into generic state and try again. 4231 CompileRun("testKeyed({ a: 0 })"); 4232 CompileRun("testKeyed({ b: 0 })"); 4233 value = CompileRun("testKeyed(obj)"); 4234 CHECK(value->IsNumber()); 4235 CHECK_EQ(1, value->Int32Value()); 4236 CHECK_EQ(63, named_access_count); 4237 4238 // Check that the indexed access-control function is called every time. 4239 CompileRun("function testIndexed(obj) {" 4240 " for (var i = 0; i < 10; i++) obj[0] = 1;" 4241 " for (var j = 0; j < 10; j++) obj[0];" 4242 " return obj[0]" 4243 "}"); 4244 value = CompileRun("testIndexed(obj)"); 4245 CHECK(value->IsNumber()); 4246 CHECK_EQ(1, value->Int32Value()); 4247 CHECK_EQ(21, indexed_access_count); 4248 // Force the inline caches into generic state. 4249 CompileRun("testIndexed(new Array(1))"); 4250 // Test that the indexed access check is called. 4251 value = CompileRun("testIndexed(obj)"); 4252 CHECK(value->IsNumber()); 4253 CHECK_EQ(1, value->Int32Value()); 4254 CHECK_EQ(42, indexed_access_count); 4255 4256 // Check that the named access check is called when invoking 4257 // functions on an object that requires access checks. 4258 CompileRun("obj.f = function() {}"); 4259 CompileRun("function testCallNormal(obj) {" 4260 " for (var i = 0; i < 10; i++) obj.f();" 4261 "}"); 4262 CompileRun("testCallNormal(obj)"); 4263 CHECK_EQ(74, named_access_count); 4264 4265 // Force obj into slow case. 4266 value = CompileRun("delete obj.prop"); 4267 CHECK(value->BooleanValue()); 4268 // Force inline caches into dictionary probing mode. 4269 CompileRun("var o = { x: 0 }; delete o.x; testProp(o);"); 4270 // Test that the named access check is called. 4271 value = CompileRun("testProp(obj);"); 4272 CHECK(value->IsNumber()); 4273 CHECK_EQ(1, value->Int32Value()); 4274 CHECK_EQ(96, named_access_count); 4275 4276 // Force the call inline cache into dictionary probing mode. 4277 CompileRun("o.f = function() {}; testCallNormal(o)"); 4278 // Test that the named access check is still called for each 4279 // invocation of the function. 4280 value = CompileRun("testCallNormal(obj)"); 4281 CHECK_EQ(106, named_access_count); 4282 4283 context1->Exit(); 4284 context0->Exit(); 4285 context1.Dispose(); 4286 context0.Dispose(); 4287} 4288 4289 4290static bool NamedAccessFlatten(Local<v8::Object> global, 4291 Local<Value> name, 4292 v8::AccessType type, 4293 Local<Value> data) { 4294 char buf[100]; 4295 int len; 4296 4297 CHECK(name->IsString()); 4298 4299 memset(buf, 0x1, sizeof(buf)); 4300 len = Local<String>::Cast(name)->WriteAscii(buf); 4301 CHECK_EQ(4, len); 4302 4303 uint16_t buf2[100]; 4304 4305 memset(buf, 0x1, sizeof(buf)); 4306 len = Local<String>::Cast(name)->Write(buf2); 4307 CHECK_EQ(4, len); 4308 4309 return true; 4310} 4311 4312 4313static bool IndexedAccessFlatten(Local<v8::Object> global, 4314 uint32_t key, 4315 v8::AccessType type, 4316 Local<Value> data) { 4317 return true; 4318} 4319 4320 4321// Regression test. In access checks, operations that may cause 4322// garbage collection are not allowed. It used to be the case that 4323// using the Write operation on a string could cause a garbage 4324// collection due to flattening of the string. This is no longer the 4325// case. 4326THREADED_TEST(AccessControlFlatten) { 4327 named_access_count = 0; 4328 indexed_access_count = 0; 4329 4330 v8::HandleScope handle_scope; 4331 4332 // Create an environment. 4333 v8::Persistent<Context> context0 = Context::New(); 4334 context0->Enter(); 4335 4336 // Create an object that requires access-check functions to be 4337 // called for cross-domain access. 4338 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 4339 object_template->SetAccessCheckCallbacks(NamedAccessFlatten, 4340 IndexedAccessFlatten); 4341 Local<v8::Object> object = object_template->NewInstance(); 4342 4343 v8::HandleScope scope1; 4344 4345 // Create another environment. 4346 v8::Persistent<Context> context1 = Context::New(); 4347 context1->Enter(); 4348 4349 // Make easy access to the object from the other environment. 4350 v8::Handle<v8::Object> global1 = context1->Global(); 4351 global1->Set(v8_str("obj"), object); 4352 4353 v8::Handle<Value> value; 4354 4355 value = v8_compile("var p = 'as' + 'df';")->Run(); 4356 value = v8_compile("obj[p];")->Run(); 4357 4358 context1->Exit(); 4359 context0->Exit(); 4360 context1.Dispose(); 4361 context0.Dispose(); 4362} 4363 4364 4365static v8::Handle<Value> AccessControlNamedGetter( 4366 Local<String>, const AccessorInfo&) { 4367 return v8::Integer::New(42); 4368} 4369 4370 4371static v8::Handle<Value> AccessControlNamedSetter( 4372 Local<String>, Local<Value> value, const AccessorInfo&) { 4373 return value; 4374} 4375 4376 4377static v8::Handle<Value> AccessControlIndexedGetter( 4378 uint32_t index, 4379 const AccessorInfo& info) { 4380 return v8_num(42); 4381} 4382 4383 4384static v8::Handle<Value> AccessControlIndexedSetter( 4385 uint32_t, Local<Value> value, const AccessorInfo&) { 4386 return value; 4387} 4388 4389 4390THREADED_TEST(AccessControlInterceptorIC) { 4391 named_access_count = 0; 4392 indexed_access_count = 0; 4393 4394 v8::HandleScope handle_scope; 4395 4396 // Create an environment. 4397 v8::Persistent<Context> context0 = Context::New(); 4398 context0->Enter(); 4399 4400 // Create an object that requires access-check functions to be 4401 // called for cross-domain access. The object also has interceptors 4402 // interceptor. 4403 v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New(); 4404 object_template->SetAccessCheckCallbacks(NamedAccessCounter, 4405 IndexedAccessCounter); 4406 object_template->SetNamedPropertyHandler(AccessControlNamedGetter, 4407 AccessControlNamedSetter); 4408 object_template->SetIndexedPropertyHandler(AccessControlIndexedGetter, 4409 AccessControlIndexedSetter); 4410 Local<v8::Object> object = object_template->NewInstance(); 4411 4412 v8::HandleScope scope1; 4413 4414 // Create another environment. 4415 v8::Persistent<Context> context1 = Context::New(); 4416 context1->Enter(); 4417 4418 // Make easy access to the object from the other environment. 4419 v8::Handle<v8::Object> global1 = context1->Global(); 4420 global1->Set(v8_str("obj"), object); 4421 4422 v8::Handle<Value> value; 4423 4424 // Check that the named access-control function is called every time 4425 // eventhough there is an interceptor on the object. 4426 value = v8_compile("for (var i = 0; i < 10; i++) obj.x = 1;")->Run(); 4427 value = v8_compile("for (var i = 0; i < 10; i++) obj.x;" 4428 "obj.x")->Run(); 4429 CHECK(value->IsNumber()); 4430 CHECK_EQ(42, value->Int32Value()); 4431 CHECK_EQ(21, named_access_count); 4432 4433 value = v8_compile("var p = 'x';")->Run(); 4434 value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run(); 4435 value = v8_compile("for (var i = 0; i < 10; i++) obj[p];" 4436 "obj[p]")->Run(); 4437 CHECK(value->IsNumber()); 4438 CHECK_EQ(42, value->Int32Value()); 4439 CHECK_EQ(42, named_access_count); 4440 4441 // Check that the indexed access-control function is called every 4442 // time eventhough there is an interceptor on the object. 4443 value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run(); 4444 value = v8_compile("for (var i = 0; i < 10; i++) obj[0];" 4445 "obj[0]")->Run(); 4446 CHECK(value->IsNumber()); 4447 CHECK_EQ(42, value->Int32Value()); 4448 CHECK_EQ(21, indexed_access_count); 4449 4450 context1->Exit(); 4451 context0->Exit(); 4452 context1.Dispose(); 4453 context0.Dispose(); 4454} 4455 4456 4457THREADED_TEST(Version) { 4458 v8::V8::GetVersion(); 4459} 4460 4461 4462static v8::Handle<Value> InstanceFunctionCallback(const v8::Arguments& args) { 4463 ApiTestFuzzer::Fuzz(); 4464 return v8_num(12); 4465} 4466 4467 4468THREADED_TEST(InstanceProperties) { 4469 v8::HandleScope handle_scope; 4470 LocalContext context; 4471 4472 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 4473 Local<ObjectTemplate> instance = t->InstanceTemplate(); 4474 4475 instance->Set(v8_str("x"), v8_num(42)); 4476 instance->Set(v8_str("f"), 4477 v8::FunctionTemplate::New(InstanceFunctionCallback)); 4478 4479 Local<Value> o = t->GetFunction()->NewInstance(); 4480 4481 context->Global()->Set(v8_str("i"), o); 4482 Local<Value> value = Script::Compile(v8_str("i.x"))->Run(); 4483 CHECK_EQ(42, value->Int32Value()); 4484 4485 value = Script::Compile(v8_str("i.f()"))->Run(); 4486 CHECK_EQ(12, value->Int32Value()); 4487} 4488 4489 4490static v8::Handle<Value> 4491GlobalObjectInstancePropertiesGet(Local<String> key, const AccessorInfo&) { 4492 ApiTestFuzzer::Fuzz(); 4493 return v8::Handle<Value>(); 4494} 4495 4496 4497THREADED_TEST(GlobalObjectInstanceProperties) { 4498 v8::HandleScope handle_scope; 4499 4500 Local<Value> global_object; 4501 4502 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 4503 t->InstanceTemplate()->SetNamedPropertyHandler( 4504 GlobalObjectInstancePropertiesGet); 4505 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 4506 instance_template->Set(v8_str("x"), v8_num(42)); 4507 instance_template->Set(v8_str("f"), 4508 v8::FunctionTemplate::New(InstanceFunctionCallback)); 4509 4510 { 4511 LocalContext env(NULL, instance_template); 4512 // Hold on to the global object so it can be used again in another 4513 // environment initialization. 4514 global_object = env->Global(); 4515 4516 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 4517 CHECK_EQ(42, value->Int32Value()); 4518 value = Script::Compile(v8_str("f()"))->Run(); 4519 CHECK_EQ(12, value->Int32Value()); 4520 } 4521 4522 { 4523 // Create new environment reusing the global object. 4524 LocalContext env(NULL, instance_template, global_object); 4525 Local<Value> value = Script::Compile(v8_str("x"))->Run(); 4526 CHECK_EQ(42, value->Int32Value()); 4527 value = Script::Compile(v8_str("f()"))->Run(); 4528 CHECK_EQ(12, value->Int32Value()); 4529 } 4530} 4531 4532 4533static v8::Handle<Value> ShadowFunctionCallback(const v8::Arguments& args) { 4534 ApiTestFuzzer::Fuzz(); 4535 return v8_num(42); 4536} 4537 4538 4539static int shadow_y; 4540static int shadow_y_setter_call_count; 4541static int shadow_y_getter_call_count; 4542 4543 4544static void ShadowYSetter(Local<String>, Local<Value>, const AccessorInfo&) { 4545 shadow_y_setter_call_count++; 4546 shadow_y = 42; 4547} 4548 4549 4550static v8::Handle<Value> ShadowYGetter(Local<String> name, 4551 const AccessorInfo& info) { 4552 ApiTestFuzzer::Fuzz(); 4553 shadow_y_getter_call_count++; 4554 return v8_num(shadow_y); 4555} 4556 4557 4558static v8::Handle<Value> ShadowIndexedGet(uint32_t index, 4559 const AccessorInfo& info) { 4560 return v8::Handle<Value>(); 4561} 4562 4563 4564static v8::Handle<Value> ShadowNamedGet(Local<String> key, 4565 const AccessorInfo&) { 4566 return v8::Handle<Value>(); 4567} 4568 4569 4570THREADED_TEST(ShadowObject) { 4571 shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0; 4572 v8::HandleScope handle_scope; 4573 4574 Local<ObjectTemplate> global_template = v8::ObjectTemplate::New(); 4575 LocalContext context(NULL, global_template); 4576 4577 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 4578 t->InstanceTemplate()->SetNamedPropertyHandler(ShadowNamedGet); 4579 t->InstanceTemplate()->SetIndexedPropertyHandler(ShadowIndexedGet); 4580 Local<ObjectTemplate> proto = t->PrototypeTemplate(); 4581 Local<ObjectTemplate> instance = t->InstanceTemplate(); 4582 4583 // Only allow calls of f on instances of t. 4584 Local<v8::Signature> signature = v8::Signature::New(t); 4585 proto->Set(v8_str("f"), 4586 v8::FunctionTemplate::New(ShadowFunctionCallback, 4587 Local<Value>(), 4588 signature)); 4589 proto->Set(v8_str("x"), v8_num(12)); 4590 4591 instance->SetAccessor(v8_str("y"), ShadowYGetter, ShadowYSetter); 4592 4593 Local<Value> o = t->GetFunction()->NewInstance(); 4594 context->Global()->Set(v8_str("__proto__"), o); 4595 4596 Local<Value> value = 4597 Script::Compile(v8_str("propertyIsEnumerable(0)"))->Run(); 4598 CHECK(value->IsBoolean()); 4599 CHECK(!value->BooleanValue()); 4600 4601 value = Script::Compile(v8_str("x"))->Run(); 4602 CHECK_EQ(12, value->Int32Value()); 4603 4604 value = Script::Compile(v8_str("f()"))->Run(); 4605 CHECK_EQ(42, value->Int32Value()); 4606 4607 Script::Compile(v8_str("y = 42"))->Run(); 4608 CHECK_EQ(1, shadow_y_setter_call_count); 4609 value = Script::Compile(v8_str("y"))->Run(); 4610 CHECK_EQ(1, shadow_y_getter_call_count); 4611 CHECK_EQ(42, value->Int32Value()); 4612} 4613 4614 4615THREADED_TEST(HiddenPrototype) { 4616 v8::HandleScope handle_scope; 4617 LocalContext context; 4618 4619 Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); 4620 t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); 4621 Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); 4622 t1->SetHiddenPrototype(true); 4623 t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); 4624 Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); 4625 t2->SetHiddenPrototype(true); 4626 t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); 4627 Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); 4628 t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); 4629 4630 Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); 4631 Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); 4632 Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); 4633 Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); 4634 4635 // Setting the prototype on an object skips hidden prototypes. 4636 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 4637 o0->Set(v8_str("__proto__"), o1); 4638 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 4639 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 4640 o0->Set(v8_str("__proto__"), o2); 4641 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 4642 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 4643 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 4644 o0->Set(v8_str("__proto__"), o3); 4645 CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); 4646 CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); 4647 CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); 4648 CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); 4649 4650 // Getting the prototype of o0 should get the first visible one 4651 // which is o3. Therefore, z should not be defined on the prototype 4652 // object. 4653 Local<Value> proto = o0->Get(v8_str("__proto__")); 4654 CHECK(proto->IsObject()); 4655 CHECK(Local<v8::Object>::Cast(proto)->Get(v8_str("z"))->IsUndefined()); 4656} 4657 4658 4659THREADED_TEST(GetterSetterExceptions) { 4660 v8::HandleScope handle_scope; 4661 LocalContext context; 4662 CompileRun( 4663 "function Foo() { };" 4664 "function Throw() { throw 5; };" 4665 "var x = { };" 4666 "x.__defineSetter__('set', Throw);" 4667 "x.__defineGetter__('get', Throw);"); 4668 Local<v8::Object> x = 4669 Local<v8::Object>::Cast(context->Global()->Get(v8_str("x"))); 4670 v8::TryCatch try_catch; 4671 x->Set(v8_str("set"), v8::Integer::New(8)); 4672 x->Get(v8_str("get")); 4673 x->Set(v8_str("set"), v8::Integer::New(8)); 4674 x->Get(v8_str("get")); 4675 x->Set(v8_str("set"), v8::Integer::New(8)); 4676 x->Get(v8_str("get")); 4677 x->Set(v8_str("set"), v8::Integer::New(8)); 4678 x->Get(v8_str("get")); 4679} 4680 4681 4682THREADED_TEST(Constructor) { 4683 v8::HandleScope handle_scope; 4684 LocalContext context; 4685 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 4686 templ->SetClassName(v8_str("Fun")); 4687 Local<Function> cons = templ->GetFunction(); 4688 context->Global()->Set(v8_str("Fun"), cons); 4689 Local<v8::Object> inst = cons->NewInstance(); 4690 i::Handle<i::JSObject> obj = v8::Utils::OpenHandle(*inst); 4691 Local<Value> value = CompileRun("(new Fun()).constructor === Fun"); 4692 CHECK(value->BooleanValue()); 4693} 4694 4695THREADED_TEST(FunctionDescriptorException) { 4696 v8::HandleScope handle_scope; 4697 LocalContext context; 4698 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 4699 templ->SetClassName(v8_str("Fun")); 4700 Local<Function> cons = templ->GetFunction(); 4701 context->Global()->Set(v8_str("Fun"), cons); 4702 Local<Value> value = CompileRun( 4703 "function test() {" 4704 " try {" 4705 " (new Fun()).blah()" 4706 " } catch (e) {" 4707 " var str = String(e);" 4708 " if (str.indexOf('TypeError') == -1) return 1;" 4709 " if (str.indexOf('[object Fun]') != -1) return 2;" 4710 " if (str.indexOf('#<a Fun>') == -1) return 3;" 4711 " return 0;" 4712 " }" 4713 " return 4;" 4714 "}" 4715 "test();"); 4716 CHECK_EQ(0, value->Int32Value()); 4717} 4718 4719 4720THREADED_TEST(EvalAliasedDynamic) { 4721 v8::HandleScope scope; 4722 LocalContext current; 4723 4724 // Tests where aliased eval can only be resolved dynamically. 4725 Local<Script> script = 4726 Script::Compile(v8_str("function f(x) { " 4727 " var foo = 2;" 4728 " with (x) { return eval('foo'); }" 4729 "}" 4730 "foo = 0;" 4731 "result1 = f(new Object());" 4732 "result2 = f(this);" 4733 "var x = new Object();" 4734 "x.eval = function(x) { return 1; };" 4735 "result3 = f(x);")); 4736 script->Run(); 4737 CHECK_EQ(2, current->Global()->Get(v8_str("result1"))->Int32Value()); 4738 CHECK_EQ(0, current->Global()->Get(v8_str("result2"))->Int32Value()); 4739 CHECK_EQ(1, current->Global()->Get(v8_str("result3"))->Int32Value()); 4740 4741 v8::TryCatch try_catch; 4742 script = 4743 Script::Compile(v8_str("function f(x) { " 4744 " var bar = 2;" 4745 " with (x) { return eval('bar'); }" 4746 "}" 4747 "f(this)")); 4748 script->Run(); 4749 CHECK(try_catch.HasCaught()); 4750 try_catch.Reset(); 4751} 4752 4753 4754THREADED_TEST(CrossEval) { 4755 v8::HandleScope scope; 4756 LocalContext other; 4757 LocalContext current; 4758 4759 Local<String> token = v8_str("<security token>"); 4760 other->SetSecurityToken(token); 4761 current->SetSecurityToken(token); 4762 4763 // Setup reference from current to other. 4764 current->Global()->Set(v8_str("other"), other->Global()); 4765 4766 // Check that new variables are introduced in other context. 4767 Local<Script> script = 4768 Script::Compile(v8_str("other.eval('var foo = 1234')")); 4769 script->Run(); 4770 Local<Value> foo = other->Global()->Get(v8_str("foo")); 4771 CHECK_EQ(1234, foo->Int32Value()); 4772 CHECK(!current->Global()->Has(v8_str("foo"))); 4773 4774 // Check that writing to non-existing properties introduces them in 4775 // the other context. 4776 script = 4777 Script::Compile(v8_str("other.eval('na = 1234')")); 4778 script->Run(); 4779 CHECK_EQ(1234, other->Global()->Get(v8_str("na"))->Int32Value()); 4780 CHECK(!current->Global()->Has(v8_str("na"))); 4781 4782 // Check that global variables in current context are not visible in other 4783 // context. 4784 v8::TryCatch try_catch; 4785 script = 4786 Script::Compile(v8_str("var bar = 42; other.eval('bar');")); 4787 Local<Value> result = script->Run(); 4788 CHECK(try_catch.HasCaught()); 4789 try_catch.Reset(); 4790 4791 // Check that local variables in current context are not visible in other 4792 // context. 4793 script = 4794 Script::Compile(v8_str("(function() { " 4795 " var baz = 87;" 4796 " return other.eval('baz');" 4797 "})();")); 4798 result = script->Run(); 4799 CHECK(try_catch.HasCaught()); 4800 try_catch.Reset(); 4801 4802 // Check that global variables in the other environment are visible 4803 // when evaluting code. 4804 other->Global()->Set(v8_str("bis"), v8_num(1234)); 4805 script = Script::Compile(v8_str("other.eval('bis')")); 4806 CHECK_EQ(1234, script->Run()->Int32Value()); 4807 CHECK(!try_catch.HasCaught()); 4808 4809 // Check that the 'this' pointer points to the global object evaluating 4810 // code. 4811 other->Global()->Set(v8_str("t"), other->Global()); 4812 script = Script::Compile(v8_str("other.eval('this == t')")); 4813 result = script->Run(); 4814 CHECK(result->IsTrue()); 4815 CHECK(!try_catch.HasCaught()); 4816 4817 // Check that variables introduced in with-statement are not visible in 4818 // other context. 4819 script = 4820 Script::Compile(v8_str("with({x:2}){other.eval('x')}")); 4821 result = script->Run(); 4822 CHECK(try_catch.HasCaught()); 4823 try_catch.Reset(); 4824 4825 // Check that you cannot use 'eval.call' with another object than the 4826 // current global object. 4827 script = 4828 Script::Compile(v8_str("other.y = 1; eval.call(other, 'y')")); 4829 result = script->Run(); 4830 CHECK(try_catch.HasCaught()); 4831} 4832 4833 4834// Test that calling eval in a context which has been detached from 4835// its global throws an exception. This behavior is consistent with 4836// other JavaScript implementations. 4837THREADED_TEST(EvalInDetachedGlobal) { 4838 v8::HandleScope scope; 4839 4840 v8::Persistent<Context> context0 = Context::New(); 4841 v8::Persistent<Context> context1 = Context::New(); 4842 4843 // Setup function in context0 that uses eval from context0. 4844 context0->Enter(); 4845 v8::Handle<v8::Value> fun = 4846 CompileRun("var x = 42;" 4847 "(function() {" 4848 " var e = eval;" 4849 " return function(s) { return e(s); }" 4850 "})()"); 4851 context0->Exit(); 4852 4853 // Put the function into context1 and call it before and after 4854 // detaching the global. Before detaching, the call succeeds and 4855 // after detaching and exception is thrown. 4856 context1->Enter(); 4857 context1->Global()->Set(v8_str("fun"), fun); 4858 v8::Handle<v8::Value> x_value = CompileRun("fun('x')"); 4859 CHECK_EQ(42, x_value->Int32Value()); 4860 context0->DetachGlobal(); 4861 v8::TryCatch catcher; 4862 x_value = CompileRun("fun('x')"); 4863 CHECK(x_value.IsEmpty()); 4864 CHECK(catcher.HasCaught()); 4865 context1->Exit(); 4866 4867 context1.Dispose(); 4868 context0.Dispose(); 4869} 4870 4871 4872THREADED_TEST(CrossLazyLoad) { 4873 v8::HandleScope scope; 4874 LocalContext other; 4875 LocalContext current; 4876 4877 Local<String> token = v8_str("<security token>"); 4878 other->SetSecurityToken(token); 4879 current->SetSecurityToken(token); 4880 4881 // Setup reference from current to other. 4882 current->Global()->Set(v8_str("other"), other->Global()); 4883 4884 // Trigger lazy loading in other context. 4885 Local<Script> script = 4886 Script::Compile(v8_str("other.eval('new Date(42)')")); 4887 Local<Value> value = script->Run(); 4888 CHECK_EQ(42.0, value->NumberValue()); 4889} 4890 4891 4892static v8::Handle<Value> call_as_function(const v8::Arguments& args) { 4893 ApiTestFuzzer::Fuzz(); 4894 if (args.IsConstructCall()) { 4895 if (args[0]->IsInt32()) { 4896 return v8_num(-args[0]->Int32Value()); 4897 } 4898 } 4899 4900 return args[0]; 4901} 4902 4903 4904// Test that a call handler can be set for objects which will allow 4905// non-function objects created through the API to be called as 4906// functions. 4907THREADED_TEST(CallAsFunction) { 4908 v8::HandleScope scope; 4909 LocalContext context; 4910 4911 Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); 4912 Local<ObjectTemplate> instance_template = t->InstanceTemplate(); 4913 instance_template->SetCallAsFunctionHandler(call_as_function); 4914 Local<v8::Object> instance = t->GetFunction()->NewInstance(); 4915 context->Global()->Set(v8_str("obj"), instance); 4916 v8::TryCatch try_catch; 4917 Local<Value> value; 4918 CHECK(!try_catch.HasCaught()); 4919 4920 value = CompileRun("obj(42)"); 4921 CHECK(!try_catch.HasCaught()); 4922 CHECK_EQ(42, value->Int32Value()); 4923 4924 value = CompileRun("(function(o){return o(49)})(obj)"); 4925 CHECK(!try_catch.HasCaught()); 4926 CHECK_EQ(49, value->Int32Value()); 4927 4928 // test special case of call as function 4929 value = CompileRun("[obj]['0'](45)"); 4930 CHECK(!try_catch.HasCaught()); 4931 CHECK_EQ(45, value->Int32Value()); 4932 4933 value = CompileRun("obj.call = Function.prototype.call;" 4934 "obj.call(null, 87)"); 4935 CHECK(!try_catch.HasCaught()); 4936 CHECK_EQ(87, value->Int32Value()); 4937 4938 // Regression tests for bug #1116356: Calling call through call/apply 4939 // must work for non-function receivers. 4940 const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])"; 4941 value = CompileRun(apply_99); 4942 CHECK(!try_catch.HasCaught()); 4943 CHECK_EQ(99, value->Int32Value()); 4944 4945 const char* call_17 = "Function.prototype.call.call(obj, this, 17)"; 4946 value = CompileRun(call_17); 4947 CHECK(!try_catch.HasCaught()); 4948 CHECK_EQ(17, value->Int32Value()); 4949 4950 // Check that the call-as-function handler can be called through 4951 // new. 4952 value = CompileRun("new obj(43)"); 4953 CHECK(!try_catch.HasCaught()); 4954 CHECK_EQ(-43, value->Int32Value()); 4955} 4956 4957 4958static int CountHandles() { 4959 return v8::HandleScope::NumberOfHandles(); 4960} 4961 4962 4963static int Recurse(int depth, int iterations) { 4964 v8::HandleScope scope; 4965 if (depth == 0) return CountHandles(); 4966 for (int i = 0; i < iterations; i++) { 4967 Local<v8::Number> n = v8::Integer::New(42); 4968 } 4969 return Recurse(depth - 1, iterations); 4970} 4971 4972 4973THREADED_TEST(HandleIteration) { 4974 static const int kIterations = 500; 4975 static const int kNesting = 200; 4976 CHECK_EQ(0, CountHandles()); 4977 { 4978 v8::HandleScope scope1; 4979 CHECK_EQ(0, CountHandles()); 4980 for (int i = 0; i < kIterations; i++) { 4981 Local<v8::Number> n = v8::Integer::New(42); 4982 CHECK_EQ(i + 1, CountHandles()); 4983 } 4984 4985 CHECK_EQ(kIterations, CountHandles()); 4986 { 4987 v8::HandleScope scope2; 4988 for (int j = 0; j < kIterations; j++) { 4989 Local<v8::Number> n = v8::Integer::New(42); 4990 CHECK_EQ(j + 1 + kIterations, CountHandles()); 4991 } 4992 } 4993 CHECK_EQ(kIterations, CountHandles()); 4994 } 4995 CHECK_EQ(0, CountHandles()); 4996 CHECK_EQ(kNesting * kIterations, Recurse(kNesting, kIterations)); 4997} 4998 4999 5000static v8::Handle<Value> InterceptorHasOwnPropertyGetter( 5001 Local<String> name, 5002 const AccessorInfo& info) { 5003 ApiTestFuzzer::Fuzz(); 5004 return v8::Handle<Value>(); 5005} 5006 5007 5008THREADED_TEST(InterceptorHasOwnProperty) { 5009 v8::HandleScope scope; 5010 LocalContext context; 5011 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 5012 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 5013 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetter); 5014 Local<Function> function = fun_templ->GetFunction(); 5015 context->Global()->Set(v8_str("constructor"), function); 5016 v8::Handle<Value> value = CompileRun( 5017 "var o = new constructor();" 5018 "o.hasOwnProperty('ostehaps');"); 5019 CHECK_EQ(false, value->BooleanValue()); 5020 value = CompileRun( 5021 "o.ostehaps = 42;" 5022 "o.hasOwnProperty('ostehaps');"); 5023 CHECK_EQ(true, value->BooleanValue()); 5024 value = CompileRun( 5025 "var p = new constructor();" 5026 "p.hasOwnProperty('ostehaps');"); 5027 CHECK_EQ(false, value->BooleanValue()); 5028} 5029 5030 5031static v8::Handle<Value> InterceptorHasOwnPropertyGetterGC( 5032 Local<String> name, 5033 const AccessorInfo& info) { 5034 ApiTestFuzzer::Fuzz(); 5035 i::Heap::CollectAllGarbage(false); 5036 return v8::Handle<Value>(); 5037} 5038 5039 5040THREADED_TEST(InterceptorHasOwnPropertyCausingGC) { 5041 v8::HandleScope scope; 5042 LocalContext context; 5043 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); 5044 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 5045 instance_templ->SetNamedPropertyHandler(InterceptorHasOwnPropertyGetterGC); 5046 Local<Function> function = fun_templ->GetFunction(); 5047 context->Global()->Set(v8_str("constructor"), function); 5048 // Let's first make some stuff so we can be sure to get a good GC. 5049 CompileRun( 5050 "function makestr(size) {" 5051 " switch (size) {" 5052 " case 1: return 'f';" 5053 " case 2: return 'fo';" 5054 " case 3: return 'foo';" 5055 " }" 5056 " return makestr(size >> 1) + makestr((size + 1) >> 1);" 5057 "}" 5058 "var x = makestr(12345);" 5059 "x = makestr(31415);" 5060 "x = makestr(23456);"); 5061 v8::Handle<Value> value = CompileRun( 5062 "var o = new constructor();" 5063 "o.__proto__ = new String(x);" 5064 "o.hasOwnProperty('ostehaps');"); 5065 CHECK_EQ(false, value->BooleanValue()); 5066} 5067 5068 5069typedef v8::Handle<Value> (*NamedPropertyGetter)(Local<String> property, 5070 const AccessorInfo& info); 5071 5072 5073static void CheckInterceptorLoadIC(NamedPropertyGetter getter, 5074 const char* source, 5075 int expected) { 5076 v8::HandleScope scope; 5077 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5078 templ->SetNamedPropertyHandler(getter); 5079 LocalContext context; 5080 context->Global()->Set(v8_str("o"), templ->NewInstance()); 5081 v8::Handle<Value> value = CompileRun(source); 5082 CHECK_EQ(expected, value->Int32Value()); 5083} 5084 5085 5086static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name, 5087 const AccessorInfo& info) { 5088 ApiTestFuzzer::Fuzz(); 5089 CHECK(v8_str("x")->Equals(name)); 5090 return v8::Integer::New(42); 5091} 5092 5093 5094// This test should hit the load IC for the interceptor case. 5095THREADED_TEST(InterceptorLoadIC) { 5096 CheckInterceptorLoadIC(InterceptorLoadICGetter, 5097 "var result = 0;" 5098 "for (var i = 0; i < 1000; i++) {" 5099 " result = o.x;" 5100 "}", 5101 42); 5102} 5103 5104 5105// Below go several tests which verify that JITing for various 5106// configurations of interceptor and explicit fields works fine 5107// (those cases are special cased to get better performance). 5108 5109static v8::Handle<Value> InterceptorLoadXICGetter(Local<String> name, 5110 const AccessorInfo& info) { 5111 ApiTestFuzzer::Fuzz(); 5112 return v8_str("x")->Equals(name) 5113 ? v8::Integer::New(42) : v8::Handle<v8::Value>(); 5114} 5115 5116 5117THREADED_TEST(InterceptorLoadICWithFieldOnHolder) { 5118 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 5119 "var result = 0;" 5120 "o.y = 239;" 5121 "for (var i = 0; i < 1000; i++) {" 5122 " result = o.y;" 5123 "}", 5124 239); 5125} 5126 5127 5128THREADED_TEST(InterceptorLoadICWithSubstitutedProto) { 5129 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 5130 "var result = 0;" 5131 "o.__proto__ = { 'y': 239 };" 5132 "for (var i = 0; i < 1000; i++) {" 5133 " result = o.y + o.x;" 5134 "}", 5135 239 + 42); 5136} 5137 5138 5139THREADED_TEST(InterceptorLoadICWithPropertyOnProto) { 5140 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 5141 "var result = 0;" 5142 "o.__proto__.y = 239;" 5143 "for (var i = 0; i < 1000; i++) {" 5144 " result = o.y + o.x;" 5145 "}", 5146 239 + 42); 5147} 5148 5149 5150THREADED_TEST(InterceptorLoadICUndefined) { 5151 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 5152 "var result = 0;" 5153 "for (var i = 0; i < 1000; i++) {" 5154 " result = (o.y == undefined) ? 239 : 42;" 5155 "}", 5156 239); 5157} 5158 5159 5160THREADED_TEST(InterceptorLoadICWithOverride) { 5161 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 5162 "fst = new Object(); fst.__proto__ = o;" 5163 "snd = new Object(); snd.__proto__ = fst;" 5164 "var result1 = 0;" 5165 "for (var i = 0; i < 1000; i++) {" 5166 " result1 = snd.x;" 5167 "}" 5168 "fst.x = 239;" 5169 "var result = 0;" 5170 "for (var i = 0; i < 1000; i++) {" 5171 " result = snd.x;" 5172 "}" 5173 "result + result1", 5174 239 + 42); 5175} 5176 5177 5178// Test the case when we stored field into 5179// a stub, but interceptor produced value on its own. 5180THREADED_TEST(InterceptorLoadICFieldNotNeeded) { 5181 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 5182 "proto = new Object();" 5183 "o.__proto__ = proto;" 5184 "proto.x = 239;" 5185 "for (var i = 0; i < 1000; i++) {" 5186 " o.x;" 5187 // Now it should be ICed and keep a reference to x defined on proto 5188 "}" 5189 "var result = 0;" 5190 "for (var i = 0; i < 1000; i++) {" 5191 " result += o.x;" 5192 "}" 5193 "result;", 5194 42 * 1000); 5195} 5196 5197 5198// Test the case when we stored field into 5199// a stub, but it got invalidated later on. 5200THREADED_TEST(InterceptorLoadICInvalidatedField) { 5201 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 5202 "proto1 = new Object();" 5203 "proto2 = new Object();" 5204 "o.__proto__ = proto1;" 5205 "proto1.__proto__ = proto2;" 5206 "proto2.y = 239;" 5207 "for (var i = 0; i < 1000; i++) {" 5208 " o.y;" 5209 // Now it should be ICed and keep a reference to y defined on proto2 5210 "}" 5211 "proto1.y = 42;" 5212 "var result = 0;" 5213 "for (var i = 0; i < 1000; i++) {" 5214 " result += o.y;" 5215 "}" 5216 "result;", 5217 42 * 1000); 5218} 5219 5220 5221// Test the case when we stored field into 5222// a stub, but it got invalidated later on due to override on 5223// global object which is between interceptor and fields' holders. 5224THREADED_TEST(InterceptorLoadICInvalidatedFieldViaGlobal) { 5225 CheckInterceptorLoadIC(InterceptorLoadXICGetter, 5226 "o.__proto__ = this;" // set a global to be a proto of o. 5227 "this.__proto__.y = 239;" 5228 "for (var i = 0; i < 10; i++) {" 5229 " if (o.y != 239) throw 'oops: ' + o.y;" 5230 // Now it should be ICed and keep a reference to y defined on field_holder. 5231 "}" 5232 "this.y = 42;" // Assign on a global. 5233 "var result = 0;" 5234 "for (var i = 0; i < 10; i++) {" 5235 " result += o.y;" 5236 "}" 5237 "result;", 5238 42 * 10); 5239} 5240 5241 5242static v8::Handle<Value> Return239(Local<String> name, const AccessorInfo&) { 5243 ApiTestFuzzer::Fuzz(); 5244 return v8_num(239); 5245} 5246 5247 5248static void SetOnThis(Local<String> name, 5249 Local<Value> value, 5250 const AccessorInfo& info) { 5251 info.This()->ForceSet(name, value); 5252} 5253 5254 5255THREADED_TEST(InterceptorLoadICWithCallbackOnHolder) { 5256 v8::HandleScope scope; 5257 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5258 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 5259 templ->SetAccessor(v8_str("y"), Return239); 5260 LocalContext context; 5261 context->Global()->Set(v8_str("o"), templ->NewInstance()); 5262 v8::Handle<Value> value = CompileRun( 5263 "var result = 0;" 5264 "for (var i = 0; i < 7; i++) {" 5265 " result = o.y;" 5266 "}"); 5267 CHECK_EQ(239, value->Int32Value()); 5268} 5269 5270 5271THREADED_TEST(InterceptorLoadICWithCallbackOnProto) { 5272 v8::HandleScope scope; 5273 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 5274 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 5275 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 5276 templ_p->SetAccessor(v8_str("y"), Return239); 5277 5278 LocalContext context; 5279 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 5280 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 5281 5282 v8::Handle<Value> value = CompileRun( 5283 "o.__proto__ = p;" 5284 "var result = 0;" 5285 "for (var i = 0; i < 7; i++) {" 5286 " result = o.x + o.y;" 5287 "}"); 5288 CHECK_EQ(239 + 42, value->Int32Value()); 5289} 5290 5291 5292THREADED_TEST(InterceptorLoadICForCallbackWithOverride) { 5293 v8::HandleScope scope; 5294 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5295 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 5296 templ->SetAccessor(v8_str("y"), Return239); 5297 5298 LocalContext context; 5299 context->Global()->Set(v8_str("o"), templ->NewInstance()); 5300 5301 v8::Handle<Value> value = CompileRun( 5302 "fst = new Object(); fst.__proto__ = o;" 5303 "snd = new Object(); snd.__proto__ = fst;" 5304 "var result1 = 0;" 5305 "for (var i = 0; i < 7; i++) {" 5306 " result1 = snd.x;" 5307 "}" 5308 "fst.x = 239;" 5309 "var result = 0;" 5310 "for (var i = 0; i < 7; i++) {" 5311 " result = snd.x;" 5312 "}" 5313 "result + result1"); 5314 CHECK_EQ(239 + 42, value->Int32Value()); 5315} 5316 5317 5318// Test the case when we stored callback into 5319// a stub, but interceptor produced value on its own. 5320THREADED_TEST(InterceptorLoadICCallbackNotNeeded) { 5321 v8::HandleScope scope; 5322 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 5323 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 5324 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 5325 templ_p->SetAccessor(v8_str("y"), Return239); 5326 5327 LocalContext context; 5328 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 5329 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 5330 5331 v8::Handle<Value> value = CompileRun( 5332 "o.__proto__ = p;" 5333 "for (var i = 0; i < 7; i++) {" 5334 " o.x;" 5335 // Now it should be ICed and keep a reference to x defined on p 5336 "}" 5337 "var result = 0;" 5338 "for (var i = 0; i < 7; i++) {" 5339 " result += o.x;" 5340 "}" 5341 "result"); 5342 CHECK_EQ(42 * 7, value->Int32Value()); 5343} 5344 5345 5346// Test the case when we stored callback into 5347// a stub, but it got invalidated later on. 5348THREADED_TEST(InterceptorLoadICInvalidatedCallback) { 5349 v8::HandleScope scope; 5350 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 5351 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 5352 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 5353 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 5354 5355 LocalContext context; 5356 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 5357 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 5358 5359 v8::Handle<Value> value = CompileRun( 5360 "inbetween = new Object();" 5361 "o.__proto__ = inbetween;" 5362 "inbetween.__proto__ = p;" 5363 "for (var i = 0; i < 10; i++) {" 5364 " o.y;" 5365 // Now it should be ICed and keep a reference to y defined on p 5366 "}" 5367 "inbetween.y = 42;" 5368 "var result = 0;" 5369 "for (var i = 0; i < 10; i++) {" 5370 " result += o.y;" 5371 "}" 5372 "result"); 5373 CHECK_EQ(42 * 10, value->Int32Value()); 5374} 5375 5376 5377// Test the case when we stored callback into 5378// a stub, but it got invalidated later on due to override on 5379// global object which is between interceptor and callbacks' holders. 5380THREADED_TEST(InterceptorLoadICInvalidatedCallbackViaGlobal) { 5381 v8::HandleScope scope; 5382 v8::Handle<v8::ObjectTemplate> templ_o = ObjectTemplate::New(); 5383 templ_o->SetNamedPropertyHandler(InterceptorLoadXICGetter); 5384 v8::Handle<v8::ObjectTemplate> templ_p = ObjectTemplate::New(); 5385 templ_p->SetAccessor(v8_str("y"), Return239, SetOnThis); 5386 5387 LocalContext context; 5388 context->Global()->Set(v8_str("o"), templ_o->NewInstance()); 5389 context->Global()->Set(v8_str("p"), templ_p->NewInstance()); 5390 5391 v8::Handle<Value> value = CompileRun( 5392 "o.__proto__ = this;" 5393 "this.__proto__ = p;" 5394 "for (var i = 0; i < 10; i++) {" 5395 " if (o.y != 239) throw 'oops: ' + o.y;" 5396 // Now it should be ICed and keep a reference to y defined on p 5397 "}" 5398 "this.y = 42;" 5399 "var result = 0;" 5400 "for (var i = 0; i < 10; i++) {" 5401 " result += o.y;" 5402 "}" 5403 "result"); 5404 CHECK_EQ(42 * 10, value->Int32Value()); 5405} 5406 5407 5408static v8::Handle<Value> InterceptorLoadICGetter0(Local<String> name, 5409 const AccessorInfo& info) { 5410 ApiTestFuzzer::Fuzz(); 5411 CHECK(v8_str("x")->Equals(name)); 5412 return v8::Integer::New(0); 5413} 5414 5415 5416THREADED_TEST(InterceptorReturningZero) { 5417 CheckInterceptorLoadIC(InterceptorLoadICGetter0, 5418 "o.x == undefined ? 1 : 0", 5419 0); 5420} 5421 5422 5423static v8::Handle<Value> InterceptorStoreICSetter( 5424 Local<String> key, Local<Value> value, const AccessorInfo&) { 5425 CHECK(v8_str("x")->Equals(key)); 5426 CHECK_EQ(42, value->Int32Value()); 5427 return value; 5428} 5429 5430 5431// This test should hit the store IC for the interceptor case. 5432THREADED_TEST(InterceptorStoreIC) { 5433 v8::HandleScope scope; 5434 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5435 templ->SetNamedPropertyHandler(InterceptorLoadICGetter, 5436 InterceptorStoreICSetter); 5437 LocalContext context; 5438 context->Global()->Set(v8_str("o"), templ->NewInstance()); 5439 v8::Handle<Value> value = CompileRun( 5440 "for (var i = 0; i < 1000; i++) {" 5441 " o.x = 42;" 5442 "}"); 5443} 5444 5445 5446THREADED_TEST(InterceptorStoreICWithNoSetter) { 5447 v8::HandleScope scope; 5448 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5449 templ->SetNamedPropertyHandler(InterceptorLoadXICGetter); 5450 LocalContext context; 5451 context->Global()->Set(v8_str("o"), templ->NewInstance()); 5452 v8::Handle<Value> value = CompileRun( 5453 "for (var i = 0; i < 1000; i++) {" 5454 " o.y = 239;" 5455 "}" 5456 "42 + o.y"); 5457 CHECK_EQ(239 + 42, value->Int32Value()); 5458} 5459 5460 5461 5462 5463v8::Handle<Value> call_ic_function; 5464v8::Handle<Value> call_ic_function2; 5465v8::Handle<Value> call_ic_function3; 5466 5467static v8::Handle<Value> InterceptorCallICGetter(Local<String> name, 5468 const AccessorInfo& info) { 5469 ApiTestFuzzer::Fuzz(); 5470 CHECK(v8_str("x")->Equals(name)); 5471 return call_ic_function; 5472} 5473 5474 5475// This test should hit the call IC for the interceptor case. 5476THREADED_TEST(InterceptorCallIC) { 5477 v8::HandleScope scope; 5478 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5479 templ->SetNamedPropertyHandler(InterceptorCallICGetter); 5480 LocalContext context; 5481 context->Global()->Set(v8_str("o"), templ->NewInstance()); 5482 call_ic_function = 5483 v8_compile("function f(x) { return x + 1; }; f")->Run(); 5484 v8::Handle<Value> value = CompileRun( 5485 "var result = 0;" 5486 "for (var i = 0; i < 1000; i++) {" 5487 " result = o.x(41);" 5488 "}"); 5489 CHECK_EQ(42, value->Int32Value()); 5490} 5491 5492 5493// This test checks that if interceptor doesn't provide 5494// a value, we can fetch regular value. 5495THREADED_TEST(InterceptorCallICSeesOthers) { 5496 v8::HandleScope scope; 5497 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5498 templ->SetNamedPropertyHandler(NoBlockGetterX); 5499 LocalContext context; 5500 context->Global()->Set(v8_str("o"), templ->NewInstance()); 5501 v8::Handle<Value> value = CompileRun( 5502 "o.x = function f(x) { return x + 1; };" 5503 "var result = 0;" 5504 "for (var i = 0; i < 7; i++) {" 5505 " result = o.x(41);" 5506 "}"); 5507 CHECK_EQ(42, value->Int32Value()); 5508} 5509 5510 5511static v8::Handle<Value> call_ic_function4; 5512static v8::Handle<Value> InterceptorCallICGetter4(Local<String> name, 5513 const AccessorInfo& info) { 5514 ApiTestFuzzer::Fuzz(); 5515 CHECK(v8_str("x")->Equals(name)); 5516 return call_ic_function4; 5517} 5518 5519 5520// This test checks that if interceptor provides a function, 5521// even if we cached shadowed variant, interceptor's function 5522// is invoked 5523THREADED_TEST(InterceptorCallICCacheableNotNeeded) { 5524 v8::HandleScope scope; 5525 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5526 templ->SetNamedPropertyHandler(InterceptorCallICGetter4); 5527 LocalContext context; 5528 context->Global()->Set(v8_str("o"), templ->NewInstance()); 5529 call_ic_function4 = 5530 v8_compile("function f(x) { return x - 1; }; f")->Run(); 5531 v8::Handle<Value> value = CompileRun( 5532 "o.__proto__.x = function(x) { return x + 1; };" 5533 "var result = 0;" 5534 "for (var i = 0; i < 1000; i++) {" 5535 " result = o.x(42);" 5536 "}"); 5537 CHECK_EQ(41, value->Int32Value()); 5538} 5539 5540 5541// Test the case when we stored cacheable lookup into 5542// a stub, but it got invalidated later on 5543THREADED_TEST(InterceptorCallICInvalidatedCacheable) { 5544 v8::HandleScope scope; 5545 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5546 templ->SetNamedPropertyHandler(NoBlockGetterX); 5547 LocalContext context; 5548 context->Global()->Set(v8_str("o"), templ->NewInstance()); 5549 v8::Handle<Value> value = CompileRun( 5550 "proto1 = new Object();" 5551 "proto2 = new Object();" 5552 "o.__proto__ = proto1;" 5553 "proto1.__proto__ = proto2;" 5554 "proto2.y = function(x) { return x + 1; };" 5555 // Invoke it many times to compile a stub 5556 "for (var i = 0; i < 7; i++) {" 5557 " o.y(42);" 5558 "}" 5559 "proto1.y = function(x) { return x - 1; };" 5560 "var result = 0;" 5561 "for (var i = 0; i < 7; i++) {" 5562 " result += o.y(42);" 5563 "}"); 5564 CHECK_EQ(41 * 7, value->Int32Value()); 5565} 5566 5567 5568static v8::Handle<Value> call_ic_function5; 5569static v8::Handle<Value> InterceptorCallICGetter5(Local<String> name, 5570 const AccessorInfo& info) { 5571 ApiTestFuzzer::Fuzz(); 5572 if (v8_str("x")->Equals(name)) 5573 return call_ic_function5; 5574 else 5575 return Local<Value>(); 5576} 5577 5578 5579// This test checks that if interceptor doesn't provide a function, 5580// cached constant function is used 5581THREADED_TEST(InterceptorCallICConstantFunctionUsed) { 5582 v8::HandleScope scope; 5583 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5584 templ->SetNamedPropertyHandler(NoBlockGetterX); 5585 LocalContext context; 5586 context->Global()->Set(v8_str("o"), templ->NewInstance()); 5587 v8::Handle<Value> value = CompileRun( 5588 "function inc(x) { return x + 1; };" 5589 "inc(1);" 5590 "o.x = inc;" 5591 "var result = 0;" 5592 "for (var i = 0; i < 1000; i++) {" 5593 " result = o.x(42);" 5594 "}"); 5595 CHECK_EQ(43, value->Int32Value()); 5596} 5597 5598 5599// This test checks that if interceptor provides a function, 5600// even if we cached constant function, interceptor's function 5601// is invoked 5602THREADED_TEST(InterceptorCallICConstantFunctionNotNeeded) { 5603 v8::HandleScope scope; 5604 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5605 templ->SetNamedPropertyHandler(InterceptorCallICGetter5); 5606 LocalContext context; 5607 context->Global()->Set(v8_str("o"), templ->NewInstance()); 5608 call_ic_function5 = 5609 v8_compile("function f(x) { return x - 1; }; f")->Run(); 5610 v8::Handle<Value> value = CompileRun( 5611 "function inc(x) { return x + 1; };" 5612 "inc(1);" 5613 "o.x = inc;" 5614 "var result = 0;" 5615 "for (var i = 0; i < 1000; i++) {" 5616 " result = o.x(42);" 5617 "}"); 5618 CHECK_EQ(41, value->Int32Value()); 5619} 5620 5621 5622// Test the case when we stored constant function into 5623// a stub, but it got invalidated later on 5624THREADED_TEST(InterceptorCallICInvalidatedConstantFunction) { 5625 v8::HandleScope scope; 5626 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5627 templ->SetNamedPropertyHandler(NoBlockGetterX); 5628 LocalContext context; 5629 context->Global()->Set(v8_str("o"), templ->NewInstance()); 5630 v8::Handle<Value> value = CompileRun( 5631 "function inc(x) { return x + 1; };" 5632 "inc(1);" 5633 "proto1 = new Object();" 5634 "proto2 = new Object();" 5635 "o.__proto__ = proto1;" 5636 "proto1.__proto__ = proto2;" 5637 "proto2.y = inc;" 5638 // Invoke it many times to compile a stub 5639 "for (var i = 0; i < 7; i++) {" 5640 " o.y(42);" 5641 "}" 5642 "proto1.y = function(x) { return x - 1; };" 5643 "var result = 0;" 5644 "for (var i = 0; i < 7; i++) {" 5645 " result += o.y(42);" 5646 "}"); 5647 CHECK_EQ(41 * 7, value->Int32Value()); 5648} 5649 5650 5651// Test the case when we stored constant function into 5652// a stub, but it got invalidated later on due to override on 5653// global object which is between interceptor and constant function' holders. 5654THREADED_TEST(InterceptorCallICInvalidatedConstantFunctionViaGlobal) { 5655 v8::HandleScope scope; 5656 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5657 templ->SetNamedPropertyHandler(NoBlockGetterX); 5658 LocalContext context; 5659 context->Global()->Set(v8_str("o"), templ->NewInstance()); 5660 v8::Handle<Value> value = CompileRun( 5661 "function inc(x) { return x + 1; };" 5662 "inc(1);" 5663 "o.__proto__ = this;" 5664 "this.__proto__.y = inc;" 5665 // Invoke it many times to compile a stub 5666 "for (var i = 0; i < 7; i++) {" 5667 " if (o.y(42) != 43) throw 'oops: ' + o.y(42);" 5668 "}" 5669 "this.y = function(x) { return x - 1; };" 5670 "var result = 0;" 5671 "for (var i = 0; i < 7; i++) {" 5672 " result += o.y(42);" 5673 "}"); 5674 CHECK_EQ(41 * 7, value->Int32Value()); 5675} 5676 5677 5678static int interceptor_call_count = 0; 5679 5680static v8::Handle<Value> InterceptorICRefErrorGetter(Local<String> name, 5681 const AccessorInfo& info) { 5682 ApiTestFuzzer::Fuzz(); 5683 if (v8_str("x")->Equals(name) && interceptor_call_count++ < 20) { 5684 return call_ic_function2; 5685 } 5686 return v8::Handle<Value>(); 5687} 5688 5689 5690// This test should hit load and call ICs for the interceptor case. 5691// Once in a while, the interceptor will reply that a property was not 5692// found in which case we should get a reference error. 5693THREADED_TEST(InterceptorICReferenceErrors) { 5694 v8::HandleScope scope; 5695 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5696 templ->SetNamedPropertyHandler(InterceptorICRefErrorGetter); 5697 LocalContext context(0, templ, v8::Handle<Value>()); 5698 call_ic_function2 = v8_compile("function h(x) { return x; }; h")->Run(); 5699 v8::Handle<Value> value = CompileRun( 5700 "function f() {" 5701 " for (var i = 0; i < 1000; i++) {" 5702 " try { x; } catch(e) { return true; }" 5703 " }" 5704 " return false;" 5705 "};" 5706 "f();"); 5707 CHECK_EQ(true, value->BooleanValue()); 5708 interceptor_call_count = 0; 5709 value = CompileRun( 5710 "function g() {" 5711 " for (var i = 0; i < 1000; i++) {" 5712 " try { x(42); } catch(e) { return true; }" 5713 " }" 5714 " return false;" 5715 "};" 5716 "g();"); 5717 CHECK_EQ(true, value->BooleanValue()); 5718} 5719 5720 5721static int interceptor_ic_exception_get_count = 0; 5722 5723static v8::Handle<Value> InterceptorICExceptionGetter( 5724 Local<String> name, 5725 const AccessorInfo& info) { 5726 ApiTestFuzzer::Fuzz(); 5727 if (v8_str("x")->Equals(name) && ++interceptor_ic_exception_get_count < 20) { 5728 return call_ic_function3; 5729 } 5730 if (interceptor_ic_exception_get_count == 20) { 5731 return v8::ThrowException(v8_num(42)); 5732 } 5733 // Do not handle get for properties other than x. 5734 return v8::Handle<Value>(); 5735} 5736 5737// Test interceptor load/call IC where the interceptor throws an 5738// exception once in a while. 5739THREADED_TEST(InterceptorICGetterExceptions) { 5740 interceptor_ic_exception_get_count = 0; 5741 v8::HandleScope scope; 5742 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5743 templ->SetNamedPropertyHandler(InterceptorICExceptionGetter); 5744 LocalContext context(0, templ, v8::Handle<Value>()); 5745 call_ic_function3 = v8_compile("function h(x) { return x; }; h")->Run(); 5746 v8::Handle<Value> value = CompileRun( 5747 "function f() {" 5748 " for (var i = 0; i < 100; i++) {" 5749 " try { x; } catch(e) { return true; }" 5750 " }" 5751 " return false;" 5752 "};" 5753 "f();"); 5754 CHECK_EQ(true, value->BooleanValue()); 5755 interceptor_ic_exception_get_count = 0; 5756 value = CompileRun( 5757 "function f() {" 5758 " for (var i = 0; i < 100; i++) {" 5759 " try { x(42); } catch(e) { return true; }" 5760 " }" 5761 " return false;" 5762 "};" 5763 "f();"); 5764 CHECK_EQ(true, value->BooleanValue()); 5765} 5766 5767 5768static int interceptor_ic_exception_set_count = 0; 5769 5770static v8::Handle<Value> InterceptorICExceptionSetter( 5771 Local<String> key, Local<Value> value, const AccessorInfo&) { 5772 ApiTestFuzzer::Fuzz(); 5773 if (++interceptor_ic_exception_set_count > 20) { 5774 return v8::ThrowException(v8_num(42)); 5775 } 5776 // Do not actually handle setting. 5777 return v8::Handle<Value>(); 5778} 5779 5780// Test interceptor store IC where the interceptor throws an exception 5781// once in a while. 5782THREADED_TEST(InterceptorICSetterExceptions) { 5783 interceptor_ic_exception_set_count = 0; 5784 v8::HandleScope scope; 5785 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5786 templ->SetNamedPropertyHandler(0, InterceptorICExceptionSetter); 5787 LocalContext context(0, templ, v8::Handle<Value>()); 5788 v8::Handle<Value> value = CompileRun( 5789 "function f() {" 5790 " for (var i = 0; i < 100; i++) {" 5791 " try { x = 42; } catch(e) { return true; }" 5792 " }" 5793 " return false;" 5794 "};" 5795 "f();"); 5796 CHECK_EQ(true, value->BooleanValue()); 5797} 5798 5799 5800// Test that we ignore null interceptors. 5801THREADED_TEST(NullNamedInterceptor) { 5802 v8::HandleScope scope; 5803 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5804 templ->SetNamedPropertyHandler(0); 5805 LocalContext context; 5806 templ->Set("x", v8_num(42)); 5807 v8::Handle<v8::Object> obj = templ->NewInstance(); 5808 context->Global()->Set(v8_str("obj"), obj); 5809 v8::Handle<Value> value = CompileRun("obj.x"); 5810 CHECK(value->IsInt32()); 5811 CHECK_EQ(42, value->Int32Value()); 5812} 5813 5814 5815// Test that we ignore null interceptors. 5816THREADED_TEST(NullIndexedInterceptor) { 5817 v8::HandleScope scope; 5818 v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); 5819 templ->SetIndexedPropertyHandler(0); 5820 LocalContext context; 5821 templ->Set("42", v8_num(42)); 5822 v8::Handle<v8::Object> obj = templ->NewInstance(); 5823 context->Global()->Set(v8_str("obj"), obj); 5824 v8::Handle<Value> value = CompileRun("obj[42]"); 5825 CHECK(value->IsInt32()); 5826 CHECK_EQ(42, value->Int32Value()); 5827} 5828 5829 5830static v8::Handle<Value> ParentGetter(Local<String> name, 5831 const AccessorInfo& info) { 5832 ApiTestFuzzer::Fuzz(); 5833 return v8_num(1); 5834} 5835 5836 5837static v8::Handle<Value> ChildGetter(Local<String> name, 5838 const AccessorInfo& info) { 5839 ApiTestFuzzer::Fuzz(); 5840 return v8_num(42); 5841} 5842 5843 5844THREADED_TEST(Overriding) { 5845 v8::HandleScope scope; 5846 LocalContext context; 5847 5848 // Parent template. 5849 Local<v8::FunctionTemplate> parent_templ = v8::FunctionTemplate::New(); 5850 Local<ObjectTemplate> parent_instance_templ = 5851 parent_templ->InstanceTemplate(); 5852 parent_instance_templ->SetAccessor(v8_str("f"), ParentGetter); 5853 5854 // Template that inherits from the parent template. 5855 Local<v8::FunctionTemplate> child_templ = v8::FunctionTemplate::New(); 5856 Local<ObjectTemplate> child_instance_templ = 5857 child_templ->InstanceTemplate(); 5858 child_templ->Inherit(parent_templ); 5859 // Override 'f'. The child version of 'f' should get called for child 5860 // instances. 5861 child_instance_templ->SetAccessor(v8_str("f"), ChildGetter); 5862 // Add 'g' twice. The 'g' added last should get called for instances. 5863 child_instance_templ->SetAccessor(v8_str("g"), ParentGetter); 5864 child_instance_templ->SetAccessor(v8_str("g"), ChildGetter); 5865 5866 // Add 'h' as an accessor to the proto template with ReadOnly attributes 5867 // so 'h' can be shadowed on the instance object. 5868 Local<ObjectTemplate> child_proto_templ = child_templ->PrototypeTemplate(); 5869 child_proto_templ->SetAccessor(v8_str("h"), ParentGetter, 0, 5870 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 5871 5872 // Add 'i' as an accessor to the instance template with ReadOnly attributes 5873 // but the attribute does not have effect because it is duplicated with 5874 // NULL setter. 5875 child_instance_templ->SetAccessor(v8_str("i"), ChildGetter, 0, 5876 v8::Handle<Value>(), v8::DEFAULT, v8::ReadOnly); 5877 5878 5879 5880 // Instantiate the child template. 5881 Local<v8::Object> instance = child_templ->GetFunction()->NewInstance(); 5882 5883 // Check that the child function overrides the parent one. 5884 context->Global()->Set(v8_str("o"), instance); 5885 Local<Value> value = v8_compile("o.f")->Run(); 5886 // Check that the 'g' that was added last is hit. 5887 CHECK_EQ(42, value->Int32Value()); 5888 value = v8_compile("o.g")->Run(); 5889 CHECK_EQ(42, value->Int32Value()); 5890 5891 // Check 'h' can be shadowed. 5892 value = v8_compile("o.h = 3; o.h")->Run(); 5893 CHECK_EQ(3, value->Int32Value()); 5894 5895 // Check 'i' is cannot be shadowed or changed. 5896 value = v8_compile("o.i = 3; o.i")->Run(); 5897 CHECK_EQ(42, value->Int32Value()); 5898} 5899 5900 5901static v8::Handle<Value> IsConstructHandler(const v8::Arguments& args) { 5902 ApiTestFuzzer::Fuzz(); 5903 if (args.IsConstructCall()) { 5904 return v8::Boolean::New(true); 5905 } 5906 return v8::Boolean::New(false); 5907} 5908 5909 5910THREADED_TEST(IsConstructCall) { 5911 v8::HandleScope scope; 5912 5913 // Function template with call handler. 5914 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 5915 templ->SetCallHandler(IsConstructHandler); 5916 5917 LocalContext context; 5918 5919 context->Global()->Set(v8_str("f"), templ->GetFunction()); 5920 Local<Value> value = v8_compile("f()")->Run(); 5921 CHECK(!value->BooleanValue()); 5922 value = v8_compile("new f()")->Run(); 5923 CHECK(value->BooleanValue()); 5924} 5925 5926 5927THREADED_TEST(ObjectProtoToString) { 5928 v8::HandleScope scope; 5929 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 5930 templ->SetClassName(v8_str("MyClass")); 5931 5932 LocalContext context; 5933 5934 Local<String> customized_tostring = v8_str("customized toString"); 5935 5936 // Replace Object.prototype.toString 5937 v8_compile("Object.prototype.toString = function() {" 5938 " return 'customized toString';" 5939 "}")->Run(); 5940 5941 // Normal ToString call should call replaced Object.prototype.toString 5942 Local<v8::Object> instance = templ->GetFunction()->NewInstance(); 5943 Local<String> value = instance->ToString(); 5944 CHECK(value->IsString() && value->Equals(customized_tostring)); 5945 5946 // ObjectProtoToString should not call replace toString function. 5947 value = instance->ObjectProtoToString(); 5948 CHECK(value->IsString() && value->Equals(v8_str("[object MyClass]"))); 5949 5950 // Check global 5951 value = context->Global()->ObjectProtoToString(); 5952 CHECK(value->IsString() && value->Equals(v8_str("[object global]"))); 5953 5954 // Check ordinary object 5955 Local<Value> object = v8_compile("new Object()")->Run(); 5956 value = Local<v8::Object>::Cast(object)->ObjectProtoToString(); 5957 CHECK(value->IsString() && value->Equals(v8_str("[object Object]"))); 5958} 5959 5960 5961bool ApiTestFuzzer::fuzzing_ = false; 5962v8::internal::Semaphore* ApiTestFuzzer::all_tests_done_= 5963 v8::internal::OS::CreateSemaphore(0); 5964int ApiTestFuzzer::active_tests_; 5965int ApiTestFuzzer::tests_being_run_; 5966int ApiTestFuzzer::current_; 5967 5968 5969// We are in a callback and want to switch to another thread (if we 5970// are currently running the thread fuzzing test). 5971void ApiTestFuzzer::Fuzz() { 5972 if (!fuzzing_) return; 5973 ApiTestFuzzer* test = RegisterThreadedTest::nth(current_)->fuzzer_; 5974 test->ContextSwitch(); 5975} 5976 5977 5978// Let the next thread go. Since it is also waiting on the V8 lock it may 5979// not start immediately. 5980bool ApiTestFuzzer::NextThread() { 5981 int test_position = GetNextTestNumber(); 5982 const char* test_name = RegisterThreadedTest::nth(current_)->name(); 5983 if (test_position == current_) { 5984 if (kLogThreading) 5985 printf("Stay with %s\n", test_name); 5986 return false; 5987 } 5988 if (kLogThreading) { 5989 printf("Switch from %s to %s\n", 5990 test_name, 5991 RegisterThreadedTest::nth(test_position)->name()); 5992 } 5993 current_ = test_position; 5994 RegisterThreadedTest::nth(current_)->fuzzer_->gate_->Signal(); 5995 return true; 5996} 5997 5998 5999void ApiTestFuzzer::Run() { 6000 // When it is our turn... 6001 gate_->Wait(); 6002 { 6003 // ... get the V8 lock and start running the test. 6004 v8::Locker locker; 6005 CallTest(); 6006 } 6007 // This test finished. 6008 active_ = false; 6009 active_tests_--; 6010 // If it was the last then signal that fact. 6011 if (active_tests_ == 0) { 6012 all_tests_done_->Signal(); 6013 } else { 6014 // Otherwise select a new test and start that. 6015 NextThread(); 6016 } 6017} 6018 6019 6020static unsigned linear_congruential_generator; 6021 6022 6023void ApiTestFuzzer::Setup(PartOfTest part) { 6024 linear_congruential_generator = i::FLAG_testing_prng_seed; 6025 fuzzing_ = true; 6026 int start = (part == FIRST_PART) ? 0 : (RegisterThreadedTest::count() >> 1); 6027 int end = (part == FIRST_PART) 6028 ? (RegisterThreadedTest::count() >> 1) 6029 : RegisterThreadedTest::count(); 6030 active_tests_ = tests_being_run_ = end - start; 6031 for (int i = 0; i < tests_being_run_; i++) { 6032 RegisterThreadedTest::nth(i)->fuzzer_ = new ApiTestFuzzer(i + start); 6033 } 6034 for (int i = 0; i < active_tests_; i++) { 6035 RegisterThreadedTest::nth(i)->fuzzer_->Start(); 6036 } 6037} 6038 6039 6040static void CallTestNumber(int test_number) { 6041 (RegisterThreadedTest::nth(test_number)->callback())(); 6042} 6043 6044 6045void ApiTestFuzzer::RunAllTests() { 6046 // Set off the first test. 6047 current_ = -1; 6048 NextThread(); 6049 // Wait till they are all done. 6050 all_tests_done_->Wait(); 6051} 6052 6053 6054int ApiTestFuzzer::GetNextTestNumber() { 6055 int next_test; 6056 do { 6057 next_test = (linear_congruential_generator >> 16) % tests_being_run_; 6058 linear_congruential_generator *= 1664525u; 6059 linear_congruential_generator += 1013904223u; 6060 } while (!RegisterThreadedTest::nth(next_test)->fuzzer_->active_); 6061 return next_test; 6062} 6063 6064 6065void ApiTestFuzzer::ContextSwitch() { 6066 // If the new thread is the same as the current thread there is nothing to do. 6067 if (NextThread()) { 6068 // Now it can start. 6069 v8::Unlocker unlocker; 6070 // Wait till someone starts us again. 6071 gate_->Wait(); 6072 // And we're off. 6073 } 6074} 6075 6076 6077void ApiTestFuzzer::TearDown() { 6078 fuzzing_ = false; 6079 for (int i = 0; i < RegisterThreadedTest::count(); i++) { 6080 ApiTestFuzzer *fuzzer = RegisterThreadedTest::nth(i)->fuzzer_; 6081 if (fuzzer != NULL) fuzzer->Join(); 6082 } 6083} 6084 6085 6086// Lets not be needlessly self-referential. 6087TEST(Threading) { 6088 ApiTestFuzzer::Setup(ApiTestFuzzer::FIRST_PART); 6089 ApiTestFuzzer::RunAllTests(); 6090 ApiTestFuzzer::TearDown(); 6091} 6092 6093TEST(Threading2) { 6094 ApiTestFuzzer::Setup(ApiTestFuzzer::SECOND_PART); 6095 ApiTestFuzzer::RunAllTests(); 6096 ApiTestFuzzer::TearDown(); 6097} 6098 6099 6100void ApiTestFuzzer::CallTest() { 6101 if (kLogThreading) 6102 printf("Start test %d\n", test_number_); 6103 CallTestNumber(test_number_); 6104 if (kLogThreading) 6105 printf("End test %d\n", test_number_); 6106} 6107 6108 6109static v8::Handle<Value> ThrowInJS(const v8::Arguments& args) { 6110 CHECK(v8::Locker::IsLocked()); 6111 ApiTestFuzzer::Fuzz(); 6112 v8::Unlocker unlocker; 6113 const char* code = "throw 7;"; 6114 { 6115 v8::Locker nested_locker; 6116 v8::HandleScope scope; 6117 v8::Handle<Value> exception; 6118 { v8::TryCatch try_catch; 6119 v8::Handle<Value> value = CompileRun(code); 6120 CHECK(value.IsEmpty()); 6121 CHECK(try_catch.HasCaught()); 6122 // Make sure to wrap the exception in a new handle because 6123 // the handle returned from the TryCatch is destroyed 6124 // when the TryCatch is destroyed. 6125 exception = Local<Value>::New(try_catch.Exception()); 6126 } 6127 return v8::ThrowException(exception); 6128 } 6129} 6130 6131 6132static v8::Handle<Value> ThrowInJSNoCatch(const v8::Arguments& args) { 6133 CHECK(v8::Locker::IsLocked()); 6134 ApiTestFuzzer::Fuzz(); 6135 v8::Unlocker unlocker; 6136 const char* code = "throw 7;"; 6137 { 6138 v8::Locker nested_locker; 6139 v8::HandleScope scope; 6140 v8::Handle<Value> value = CompileRun(code); 6141 CHECK(value.IsEmpty()); 6142 return v8_str("foo"); 6143 } 6144} 6145 6146 6147// These are locking tests that don't need to be run again 6148// as part of the locking aggregation tests. 6149TEST(NestedLockers) { 6150 v8::Locker locker; 6151 CHECK(v8::Locker::IsLocked()); 6152 v8::HandleScope scope; 6153 LocalContext env; 6154 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(ThrowInJS); 6155 Local<Function> fun = fun_templ->GetFunction(); 6156 env->Global()->Set(v8_str("throw_in_js"), fun); 6157 Local<Script> script = v8_compile("(function () {" 6158 " try {" 6159 " throw_in_js();" 6160 " return 42;" 6161 " } catch (e) {" 6162 " return e * 13;" 6163 " }" 6164 "})();"); 6165 CHECK_EQ(91, script->Run()->Int32Value()); 6166} 6167 6168 6169// These are locking tests that don't need to be run again 6170// as part of the locking aggregation tests. 6171TEST(NestedLockersNoTryCatch) { 6172 v8::Locker locker; 6173 v8::HandleScope scope; 6174 LocalContext env; 6175 Local<v8::FunctionTemplate> fun_templ = 6176 v8::FunctionTemplate::New(ThrowInJSNoCatch); 6177 Local<Function> fun = fun_templ->GetFunction(); 6178 env->Global()->Set(v8_str("throw_in_js"), fun); 6179 Local<Script> script = v8_compile("(function () {" 6180 " try {" 6181 " throw_in_js();" 6182 " return 42;" 6183 " } catch (e) {" 6184 " return e * 13;" 6185 " }" 6186 "})();"); 6187 CHECK_EQ(91, script->Run()->Int32Value()); 6188} 6189 6190 6191THREADED_TEST(RecursiveLocking) { 6192 v8::Locker locker; 6193 { 6194 v8::Locker locker2; 6195 CHECK(v8::Locker::IsLocked()); 6196 } 6197} 6198 6199 6200static v8::Handle<Value> UnlockForAMoment(const v8::Arguments& args) { 6201 ApiTestFuzzer::Fuzz(); 6202 v8::Unlocker unlocker; 6203 return v8::Undefined(); 6204} 6205 6206 6207THREADED_TEST(LockUnlockLock) { 6208 { 6209 v8::Locker locker; 6210 v8::HandleScope scope; 6211 LocalContext env; 6212 Local<v8::FunctionTemplate> fun_templ = 6213 v8::FunctionTemplate::New(UnlockForAMoment); 6214 Local<Function> fun = fun_templ->GetFunction(); 6215 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 6216 Local<Script> script = v8_compile("(function () {" 6217 " unlock_for_a_moment();" 6218 " return 42;" 6219 "})();"); 6220 CHECK_EQ(42, script->Run()->Int32Value()); 6221 } 6222 { 6223 v8::Locker locker; 6224 v8::HandleScope scope; 6225 LocalContext env; 6226 Local<v8::FunctionTemplate> fun_templ = 6227 v8::FunctionTemplate::New(UnlockForAMoment); 6228 Local<Function> fun = fun_templ->GetFunction(); 6229 env->Global()->Set(v8_str("unlock_for_a_moment"), fun); 6230 Local<Script> script = v8_compile("(function () {" 6231 " unlock_for_a_moment();" 6232 " return 42;" 6233 "})();"); 6234 CHECK_EQ(42, script->Run()->Int32Value()); 6235 } 6236} 6237 6238 6239static int GetGlobalObjectsCount() { 6240 int count = 0; 6241 v8::internal::HeapIterator it; 6242 for (i::HeapObject* object = it.next(); object != NULL; object = it.next()) 6243 if (object->IsJSGlobalObject()) count++; 6244 return count; 6245} 6246 6247 6248static int GetSurvivingGlobalObjectsCount() { 6249 // We need to collect all garbage twice to be sure that everything 6250 // has been collected. This is because inline caches are cleared in 6251 // the first garbage collection but some of the maps have already 6252 // been marked at that point. Therefore some of the maps are not 6253 // collected until the second garbage collection. 6254 v8::internal::Heap::CollectAllGarbage(false); 6255 v8::internal::Heap::CollectAllGarbage(false); 6256 int count = GetGlobalObjectsCount(); 6257#ifdef DEBUG 6258 if (count > 0) v8::internal::Heap::TracePathToGlobal(); 6259#endif 6260 return count; 6261} 6262 6263 6264TEST(DontLeakGlobalObjects) { 6265 // Regression test for issues 1139850 and 1174891. 6266 6267 v8::V8::Initialize(); 6268 6269 int count = GetSurvivingGlobalObjectsCount(); 6270 6271 for (int i = 0; i < 5; i++) { 6272 { v8::HandleScope scope; 6273 LocalContext context; 6274 } 6275 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 6276 6277 { v8::HandleScope scope; 6278 LocalContext context; 6279 v8_compile("Date")->Run(); 6280 } 6281 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 6282 6283 { v8::HandleScope scope; 6284 LocalContext context; 6285 v8_compile("/aaa/")->Run(); 6286 } 6287 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 6288 6289 { v8::HandleScope scope; 6290 const char* extension_list[] = { "v8/gc" }; 6291 v8::ExtensionConfiguration extensions(1, extension_list); 6292 LocalContext context(&extensions); 6293 v8_compile("gc();")->Run(); 6294 } 6295 CHECK_EQ(count, GetSurvivingGlobalObjectsCount()); 6296 } 6297} 6298 6299 6300v8::Persistent<v8::Object> some_object; 6301v8::Persistent<v8::Object> bad_handle; 6302 6303void NewPersistentHandleCallback(v8::Persistent<v8::Value>, void*) { 6304 v8::HandleScope scope; 6305 bad_handle = v8::Persistent<v8::Object>::New(some_object); 6306} 6307 6308 6309THREADED_TEST(NewPersistentHandleFromWeakCallback) { 6310 LocalContext context; 6311 6312 v8::Persistent<v8::Object> handle1, handle2; 6313 { 6314 v8::HandleScope scope; 6315 some_object = v8::Persistent<v8::Object>::New(v8::Object::New()); 6316 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 6317 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 6318 } 6319 // Note: order is implementation dependent alas: currently 6320 // global handle nodes are processed by PostGarbageCollectionProcessing 6321 // in reverse allocation order, so if second allocated handle is deleted, 6322 // weak callback of the first handle would be able to 'reallocate' it. 6323 handle1.MakeWeak(NULL, NewPersistentHandleCallback); 6324 handle2.Dispose(); 6325 i::Heap::CollectAllGarbage(false); 6326} 6327 6328 6329v8::Persistent<v8::Object> to_be_disposed; 6330 6331void DisposeAndForceGcCallback(v8::Persistent<v8::Value> handle, void*) { 6332 to_be_disposed.Dispose(); 6333 i::Heap::CollectAllGarbage(false); 6334} 6335 6336 6337THREADED_TEST(DoNotUseDeletedNodesInSecondLevelGc) { 6338 LocalContext context; 6339 6340 v8::Persistent<v8::Object> handle1, handle2; 6341 { 6342 v8::HandleScope scope; 6343 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 6344 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 6345 } 6346 handle1.MakeWeak(NULL, DisposeAndForceGcCallback); 6347 to_be_disposed = handle2; 6348 i::Heap::CollectAllGarbage(false); 6349} 6350 6351void DisposingCallback(v8::Persistent<v8::Value> handle, void*) { 6352 handle.Dispose(); 6353} 6354 6355void HandleCreatingCallback(v8::Persistent<v8::Value> handle, void*) { 6356 v8::HandleScope scope; 6357 v8::Persistent<v8::Object>::New(v8::Object::New()); 6358} 6359 6360 6361THREADED_TEST(NoGlobalHandlesOrphaningDueToWeakCallback) { 6362 LocalContext context; 6363 6364 v8::Persistent<v8::Object> handle1, handle2, handle3; 6365 { 6366 v8::HandleScope scope; 6367 handle3 = v8::Persistent<v8::Object>::New(v8::Object::New()); 6368 handle2 = v8::Persistent<v8::Object>::New(v8::Object::New()); 6369 handle1 = v8::Persistent<v8::Object>::New(v8::Object::New()); 6370 } 6371 handle2.MakeWeak(NULL, DisposingCallback); 6372 handle3.MakeWeak(NULL, HandleCreatingCallback); 6373 i::Heap::CollectAllGarbage(false); 6374} 6375 6376 6377THREADED_TEST(CheckForCrossContextObjectLiterals) { 6378 v8::V8::Initialize(); 6379 6380 const int nof = 2; 6381 const char* sources[nof] = { 6382 "try { [ 2, 3, 4 ].forEach(5); } catch(e) { e.toString(); }", 6383 "Object()" 6384 }; 6385 6386 for (int i = 0; i < nof; i++) { 6387 const char* source = sources[i]; 6388 { v8::HandleScope scope; 6389 LocalContext context; 6390 CompileRun(source); 6391 } 6392 { v8::HandleScope scope; 6393 LocalContext context; 6394 CompileRun(source); 6395 } 6396 } 6397} 6398 6399 6400static v8::Handle<Value> NestedScope(v8::Persistent<Context> env) { 6401 v8::HandleScope inner; 6402 env->Enter(); 6403 v8::Handle<Value> three = v8_num(3); 6404 v8::Handle<Value> value = inner.Close(three); 6405 env->Exit(); 6406 return value; 6407} 6408 6409 6410THREADED_TEST(NestedHandleScopeAndContexts) { 6411 v8::HandleScope outer; 6412 v8::Persistent<Context> env = Context::New(); 6413 env->Enter(); 6414 v8::Handle<Value> value = NestedScope(env); 6415 v8::Handle<String> str = value->ToString(); 6416 env->Exit(); 6417 env.Dispose(); 6418} 6419 6420 6421THREADED_TEST(ExternalAllocatedMemory) { 6422 v8::HandleScope outer; 6423 v8::Persistent<Context> env = Context::New(); 6424 const int kSize = 1024*1024; 6425 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(kSize), kSize); 6426 CHECK_EQ(v8::V8::AdjustAmountOfExternalAllocatedMemory(-kSize), 0); 6427} 6428 6429 6430THREADED_TEST(DisposeEnteredContext) { 6431 v8::HandleScope scope; 6432 LocalContext outer; 6433 { v8::Persistent<v8::Context> inner = v8::Context::New(); 6434 inner->Enter(); 6435 inner.Dispose(); 6436 inner.Clear(); 6437 inner->Exit(); 6438 } 6439} 6440 6441 6442// Regression test for issue 54, object templates with internal fields 6443// but no accessors or interceptors did not get their internal field 6444// count set on instances. 6445THREADED_TEST(Regress54) { 6446 v8::HandleScope outer; 6447 LocalContext context; 6448 static v8::Persistent<v8::ObjectTemplate> templ; 6449 if (templ.IsEmpty()) { 6450 v8::HandleScope inner; 6451 v8::Handle<v8::ObjectTemplate> local = v8::ObjectTemplate::New(); 6452 local->SetInternalFieldCount(1); 6453 templ = v8::Persistent<v8::ObjectTemplate>::New(inner.Close(local)); 6454 } 6455 v8::Handle<v8::Object> result = templ->NewInstance(); 6456 CHECK_EQ(1, result->InternalFieldCount()); 6457} 6458 6459 6460// If part of the threaded tests, this test makes ThreadingTest fail 6461// on mac. 6462TEST(CatchStackOverflow) { 6463 v8::HandleScope scope; 6464 LocalContext context; 6465 v8::TryCatch try_catch; 6466 v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New( 6467 "function f() {" 6468 " return f();" 6469 "}" 6470 "" 6471 "f();")); 6472 v8::Handle<v8::Value> result = script->Run(); 6473 CHECK(result.IsEmpty()); 6474} 6475 6476 6477static void CheckTryCatchSourceInfo(v8::Handle<v8::Script> script, 6478 const char* resource_name, 6479 int line_offset) { 6480 v8::HandleScope scope; 6481 v8::TryCatch try_catch; 6482 v8::Handle<v8::Value> result = script->Run(); 6483 CHECK(result.IsEmpty()); 6484 CHECK(try_catch.HasCaught()); 6485 v8::Handle<v8::Message> message = try_catch.Message(); 6486 CHECK(!message.IsEmpty()); 6487 CHECK_EQ(10 + line_offset, message->GetLineNumber()); 6488 CHECK_EQ(91, message->GetStartPosition()); 6489 CHECK_EQ(92, message->GetEndPosition()); 6490 CHECK_EQ(2, message->GetStartColumn()); 6491 CHECK_EQ(3, message->GetEndColumn()); 6492 v8::String::AsciiValue line(message->GetSourceLine()); 6493 CHECK_EQ(" throw 'nirk';", *line); 6494 v8::String::AsciiValue name(message->GetScriptResourceName()); 6495 CHECK_EQ(resource_name, *name); 6496} 6497 6498 6499THREADED_TEST(TryCatchSourceInfo) { 6500 v8::HandleScope scope; 6501 LocalContext context; 6502 v8::Handle<v8::String> source = v8::String::New( 6503 "function Foo() {\n" 6504 " return Bar();\n" 6505 "}\n" 6506 "\n" 6507 "function Bar() {\n" 6508 " return Baz();\n" 6509 "}\n" 6510 "\n" 6511 "function Baz() {\n" 6512 " throw 'nirk';\n" 6513 "}\n" 6514 "\n" 6515 "Foo();\n"); 6516 6517 const char* resource_name; 6518 v8::Handle<v8::Script> script; 6519 resource_name = "test.js"; 6520 script = v8::Script::Compile(source, v8::String::New(resource_name)); 6521 CheckTryCatchSourceInfo(script, resource_name, 0); 6522 6523 resource_name = "test1.js"; 6524 v8::ScriptOrigin origin1(v8::String::New(resource_name)); 6525 script = v8::Script::Compile(source, &origin1); 6526 CheckTryCatchSourceInfo(script, resource_name, 0); 6527 6528 resource_name = "test2.js"; 6529 v8::ScriptOrigin origin2(v8::String::New(resource_name), v8::Integer::New(7)); 6530 script = v8::Script::Compile(source, &origin2); 6531 CheckTryCatchSourceInfo(script, resource_name, 7); 6532} 6533 6534 6535THREADED_TEST(CompilationCache) { 6536 v8::HandleScope scope; 6537 LocalContext context; 6538 v8::Handle<v8::String> source0 = v8::String::New("1234"); 6539 v8::Handle<v8::String> source1 = v8::String::New("1234"); 6540 v8::Handle<v8::Script> script0 = 6541 v8::Script::Compile(source0, v8::String::New("test.js")); 6542 v8::Handle<v8::Script> script1 = 6543 v8::Script::Compile(source1, v8::String::New("test.js")); 6544 v8::Handle<v8::Script> script2 = 6545 v8::Script::Compile(source0); // different origin 6546 CHECK_EQ(1234, script0->Run()->Int32Value()); 6547 CHECK_EQ(1234, script1->Run()->Int32Value()); 6548 CHECK_EQ(1234, script2->Run()->Int32Value()); 6549} 6550 6551 6552static v8::Handle<Value> FunctionNameCallback(const v8::Arguments& args) { 6553 ApiTestFuzzer::Fuzz(); 6554 return v8_num(42); 6555} 6556 6557 6558THREADED_TEST(CallbackFunctionName) { 6559 v8::HandleScope scope; 6560 LocalContext context; 6561 Local<ObjectTemplate> t = ObjectTemplate::New(); 6562 t->Set(v8_str("asdf"), v8::FunctionTemplate::New(FunctionNameCallback)); 6563 context->Global()->Set(v8_str("obj"), t->NewInstance()); 6564 v8::Handle<v8::Value> value = CompileRun("obj.asdf.name"); 6565 CHECK(value->IsString()); 6566 v8::String::AsciiValue name(value); 6567 CHECK_EQ("asdf", *name); 6568} 6569 6570 6571THREADED_TEST(DateAccess) { 6572 v8::HandleScope scope; 6573 LocalContext context; 6574 v8::Handle<v8::Value> date = v8::Date::New(1224744689038.0); 6575 CHECK(date->IsDate()); 6576 CHECK_EQ(1224744689038.0, v8::Handle<v8::Date>::Cast(date)->NumberValue()); 6577} 6578 6579 6580void CheckProperties(v8::Handle<v8::Value> val, int elmc, const char* elmv[]) { 6581 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(val); 6582 v8::Handle<v8::Array> props = obj->GetPropertyNames(); 6583 CHECK_EQ(elmc, props->Length()); 6584 for (int i = 0; i < elmc; i++) { 6585 v8::String::Utf8Value elm(props->Get(v8::Integer::New(i))); 6586 CHECK_EQ(elmv[i], *elm); 6587 } 6588} 6589 6590 6591THREADED_TEST(PropertyEnumeration) { 6592 v8::HandleScope scope; 6593 LocalContext context; 6594 v8::Handle<v8::Value> obj = v8::Script::Compile(v8::String::New( 6595 "var result = [];" 6596 "result[0] = {};" 6597 "result[1] = {a: 1, b: 2};" 6598 "result[2] = [1, 2, 3];" 6599 "var proto = {x: 1, y: 2, z: 3};" 6600 "var x = { __proto__: proto, w: 0, z: 1 };" 6601 "result[3] = x;" 6602 "result;"))->Run(); 6603 v8::Handle<v8::Array> elms = v8::Handle<v8::Array>::Cast(obj); 6604 CHECK_EQ(4, elms->Length()); 6605 int elmc0 = 0; 6606 const char** elmv0 = NULL; 6607 CheckProperties(elms->Get(v8::Integer::New(0)), elmc0, elmv0); 6608 int elmc1 = 2; 6609 const char* elmv1[] = {"a", "b"}; 6610 CheckProperties(elms->Get(v8::Integer::New(1)), elmc1, elmv1); 6611 int elmc2 = 3; 6612 const char* elmv2[] = {"0", "1", "2"}; 6613 CheckProperties(elms->Get(v8::Integer::New(2)), elmc2, elmv2); 6614 int elmc3 = 4; 6615 const char* elmv3[] = {"w", "z", "x", "y"}; 6616 CheckProperties(elms->Get(v8::Integer::New(3)), elmc3, elmv3); 6617} 6618 6619 6620static bool NamedSetAccessBlocker(Local<v8::Object> obj, 6621 Local<Value> name, 6622 v8::AccessType type, 6623 Local<Value> data) { 6624 return type != v8::ACCESS_SET; 6625} 6626 6627 6628static bool IndexedSetAccessBlocker(Local<v8::Object> obj, 6629 uint32_t key, 6630 v8::AccessType type, 6631 Local<Value> data) { 6632 return type != v8::ACCESS_SET; 6633} 6634 6635 6636THREADED_TEST(DisableAccessChecksWhileConfiguring) { 6637 v8::HandleScope scope; 6638 LocalContext context; 6639 Local<ObjectTemplate> templ = ObjectTemplate::New(); 6640 templ->SetAccessCheckCallbacks(NamedSetAccessBlocker, 6641 IndexedSetAccessBlocker); 6642 templ->Set(v8_str("x"), v8::True()); 6643 Local<v8::Object> instance = templ->NewInstance(); 6644 context->Global()->Set(v8_str("obj"), instance); 6645 Local<Value> value = CompileRun("obj.x"); 6646 CHECK(value->BooleanValue()); 6647} 6648 6649 6650static bool NamedGetAccessBlocker(Local<v8::Object> obj, 6651 Local<Value> name, 6652 v8::AccessType type, 6653 Local<Value> data) { 6654 return false; 6655} 6656 6657 6658static bool IndexedGetAccessBlocker(Local<v8::Object> obj, 6659 uint32_t key, 6660 v8::AccessType type, 6661 Local<Value> data) { 6662 return false; 6663} 6664 6665 6666 6667THREADED_TEST(AccessChecksReenabledCorrectly) { 6668 v8::HandleScope scope; 6669 LocalContext context; 6670 Local<ObjectTemplate> templ = ObjectTemplate::New(); 6671 templ->SetAccessCheckCallbacks(NamedGetAccessBlocker, 6672 IndexedGetAccessBlocker); 6673 templ->Set(v8_str("a"), v8_str("a")); 6674 // Add more than 8 (see kMaxFastProperties) properties 6675 // so that the constructor will force copying map. 6676 // Cannot sprintf, gcc complains unsafety. 6677 char buf[4]; 6678 for (char i = '0'; i <= '9' ; i++) { 6679 buf[0] = i; 6680 for (char j = '0'; j <= '9'; j++) { 6681 buf[1] = j; 6682 for (char k = '0'; k <= '9'; k++) { 6683 buf[2] = k; 6684 buf[3] = 0; 6685 templ->Set(v8_str(buf), v8::Number::New(k)); 6686 } 6687 } 6688 } 6689 6690 Local<v8::Object> instance_1 = templ->NewInstance(); 6691 context->Global()->Set(v8_str("obj_1"), instance_1); 6692 6693 Local<Value> value_1 = CompileRun("obj_1.a"); 6694 CHECK(value_1->IsUndefined()); 6695 6696 Local<v8::Object> instance_2 = templ->NewInstance(); 6697 context->Global()->Set(v8_str("obj_2"), instance_2); 6698 6699 Local<Value> value_2 = CompileRun("obj_2.a"); 6700 CHECK(value_2->IsUndefined()); 6701} 6702 6703 6704// This tests that access check information remains on the global 6705// object template when creating contexts. 6706THREADED_TEST(AccessControlRepeatedContextCreation) { 6707 v8::HandleScope handle_scope; 6708 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 6709 global_template->SetAccessCheckCallbacks(NamedSetAccessBlocker, 6710 IndexedSetAccessBlocker); 6711 i::Handle<i::ObjectTemplateInfo> internal_template = 6712 v8::Utils::OpenHandle(*global_template); 6713 CHECK(!internal_template->constructor()->IsUndefined()); 6714 i::Handle<i::FunctionTemplateInfo> constructor( 6715 i::FunctionTemplateInfo::cast(internal_template->constructor())); 6716 CHECK(!constructor->access_check_info()->IsUndefined()); 6717 v8::Persistent<Context> context0 = Context::New(NULL, global_template); 6718 CHECK(!constructor->access_check_info()->IsUndefined()); 6719} 6720 6721 6722THREADED_TEST(TurnOnAccessCheck) { 6723 v8::HandleScope handle_scope; 6724 6725 // Create an environment with access check to the global object disabled by 6726 // default. 6727 v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(); 6728 global_template->SetAccessCheckCallbacks(NamedGetAccessBlocker, 6729 IndexedGetAccessBlocker, 6730 v8::Handle<v8::Value>(), 6731 false); 6732 v8::Persistent<Context> context = Context::New(NULL, global_template); 6733 Context::Scope context_scope(context); 6734 6735 // Set up a property and a number of functions. 6736 context->Global()->Set(v8_str("a"), v8_num(1)); 6737 CompileRun("function f1() {return a;}" 6738 "function f2() {return a;}" 6739 "function g1() {return h();}" 6740 "function g2() {return h();}" 6741 "function h() {return 1;}"); 6742 Local<Function> f1 = 6743 Local<Function>::Cast(context->Global()->Get(v8_str("f1"))); 6744 Local<Function> f2 = 6745 Local<Function>::Cast(context->Global()->Get(v8_str("f2"))); 6746 Local<Function> g1 = 6747 Local<Function>::Cast(context->Global()->Get(v8_str("g1"))); 6748 Local<Function> g2 = 6749 Local<Function>::Cast(context->Global()->Get(v8_str("g2"))); 6750 Local<Function> h = 6751 Local<Function>::Cast(context->Global()->Get(v8_str("h"))); 6752 6753 // Get the global object. 6754 v8::Handle<v8::Object> global = context->Global(); 6755 6756 // Call f1 one time and f2 a number of times. This will ensure that f1 still 6757 // uses the runtime system to retreive property a whereas f2 uses global load 6758 // inline cache. 6759 CHECK(f1->Call(global, 0, NULL)->Equals(v8_num(1))); 6760 for (int i = 0; i < 4; i++) { 6761 CHECK(f2->Call(global, 0, NULL)->Equals(v8_num(1))); 6762 } 6763 6764 // Same for g1 and g2. 6765 CHECK(g1->Call(global, 0, NULL)->Equals(v8_num(1))); 6766 for (int i = 0; i < 4; i++) { 6767 CHECK(g2->Call(global, 0, NULL)->Equals(v8_num(1))); 6768 } 6769 6770 // Detach the global and turn on access check. 6771 context->DetachGlobal(); 6772 context->Global()->TurnOnAccessCheck(); 6773 6774 // Failing access check to property get results in undefined. 6775 CHECK(f1->Call(global, 0, NULL)->IsUndefined()); 6776 CHECK(f2->Call(global, 0, NULL)->IsUndefined()); 6777 6778 // Failing access check to function call results in exception. 6779 CHECK(g1->Call(global, 0, NULL).IsEmpty()); 6780 CHECK(g2->Call(global, 0, NULL).IsEmpty()); 6781 6782 // No failing access check when just returning a constant. 6783 CHECK(h->Call(global, 0, NULL)->Equals(v8_num(1))); 6784} 6785 6786 6787// This test verifies that pre-compilation (aka preparsing) can be called 6788// without initializing the whole VM. Thus we cannot run this test in a 6789// multi-threaded setup. 6790TEST(PreCompile) { 6791 // TODO(155): This test would break without the initialization of V8. This is 6792 // a workaround for now to make this test not fail. 6793 v8::V8::Initialize(); 6794 const char *script = "function foo(a) { return a+1; }"; 6795 v8::ScriptData *sd = 6796 v8::ScriptData::PreCompile(script, i::StrLength(script)); 6797 CHECK_NE(sd->Length(), 0); 6798 CHECK_NE(sd->Data(), NULL); 6799 CHECK(!sd->HasError()); 6800 delete sd; 6801} 6802 6803 6804TEST(PreCompileWithError) { 6805 v8::V8::Initialize(); 6806 const char *script = "function foo(a) { return 1 * * 2; }"; 6807 v8::ScriptData *sd = 6808 v8::ScriptData::PreCompile(script, i::StrLength(script)); 6809 CHECK(sd->HasError()); 6810 delete sd; 6811} 6812 6813 6814TEST(Regress31661) { 6815 v8::V8::Initialize(); 6816 const char *script = " The Definintive Guide"; 6817 v8::ScriptData *sd = 6818 v8::ScriptData::PreCompile(script, i::StrLength(script)); 6819 CHECK(sd->HasError()); 6820 delete sd; 6821} 6822 6823 6824// This tests that we do not allow dictionary load/call inline caches 6825// to use functions that have not yet been compiled. The potential 6826// problem of loading a function that has not yet been compiled can 6827// arise because we share code between contexts via the compilation 6828// cache. 6829THREADED_TEST(DictionaryICLoadedFunction) { 6830 v8::HandleScope scope; 6831 // Test LoadIC. 6832 for (int i = 0; i < 2; i++) { 6833 LocalContext context; 6834 context->Global()->Set(v8_str("tmp"), v8::True()); 6835 context->Global()->Delete(v8_str("tmp")); 6836 CompileRun("for (var j = 0; j < 10; j++) new RegExp('');"); 6837 } 6838 // Test CallIC. 6839 for (int i = 0; i < 2; i++) { 6840 LocalContext context; 6841 context->Global()->Set(v8_str("tmp"), v8::True()); 6842 context->Global()->Delete(v8_str("tmp")); 6843 CompileRun("for (var j = 0; j < 10; j++) RegExp('')"); 6844 } 6845} 6846 6847 6848// Test that cross-context new calls use the context of the callee to 6849// create the new JavaScript object. 6850THREADED_TEST(CrossContextNew) { 6851 v8::HandleScope scope; 6852 v8::Persistent<Context> context0 = Context::New(); 6853 v8::Persistent<Context> context1 = Context::New(); 6854 6855 // Allow cross-domain access. 6856 Local<String> token = v8_str("<security token>"); 6857 context0->SetSecurityToken(token); 6858 context1->SetSecurityToken(token); 6859 6860 // Set an 'x' property on the Object prototype and define a 6861 // constructor function in context0. 6862 context0->Enter(); 6863 CompileRun("Object.prototype.x = 42; function C() {};"); 6864 context0->Exit(); 6865 6866 // Call the constructor function from context0 and check that the 6867 // result has the 'x' property. 6868 context1->Enter(); 6869 context1->Global()->Set(v8_str("other"), context0->Global()); 6870 Local<Value> value = CompileRun("var instance = new other.C(); instance.x"); 6871 CHECK(value->IsInt32()); 6872 CHECK_EQ(42, value->Int32Value()); 6873 context1->Exit(); 6874 6875 // Dispose the contexts to allow them to be garbage collected. 6876 context0.Dispose(); 6877 context1.Dispose(); 6878} 6879 6880 6881class RegExpInterruptTest { 6882 public: 6883 RegExpInterruptTest() : block_(NULL) {} 6884 ~RegExpInterruptTest() { delete block_; } 6885 void RunTest() { 6886 block_ = i::OS::CreateSemaphore(0); 6887 gc_count_ = 0; 6888 gc_during_regexp_ = 0; 6889 regexp_success_ = false; 6890 gc_success_ = false; 6891 GCThread gc_thread(this); 6892 gc_thread.Start(); 6893 v8::Locker::StartPreemption(1); 6894 6895 LongRunningRegExp(); 6896 { 6897 v8::Unlocker unlock; 6898 gc_thread.Join(); 6899 } 6900 v8::Locker::StopPreemption(); 6901 CHECK(regexp_success_); 6902 CHECK(gc_success_); 6903 } 6904 private: 6905 // Number of garbage collections required. 6906 static const int kRequiredGCs = 5; 6907 6908 class GCThread : public i::Thread { 6909 public: 6910 explicit GCThread(RegExpInterruptTest* test) 6911 : test_(test) {} 6912 virtual void Run() { 6913 test_->CollectGarbage(); 6914 } 6915 private: 6916 RegExpInterruptTest* test_; 6917 }; 6918 6919 void CollectGarbage() { 6920 block_->Wait(); 6921 while (gc_during_regexp_ < kRequiredGCs) { 6922 { 6923 v8::Locker lock; 6924 // TODO(lrn): Perhaps create some garbage before collecting. 6925 i::Heap::CollectAllGarbage(false); 6926 gc_count_++; 6927 } 6928 i::OS::Sleep(1); 6929 } 6930 gc_success_ = true; 6931 } 6932 6933 void LongRunningRegExp() { 6934 block_->Signal(); // Enable garbage collection thread on next preemption. 6935 int rounds = 0; 6936 while (gc_during_regexp_ < kRequiredGCs) { 6937 int gc_before = gc_count_; 6938 { 6939 // Match 15-30 "a"'s against 14 and a "b". 6940 const char* c_source = 6941 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 6942 ".exec('aaaaaaaaaaaaaaab') === null"; 6943 Local<String> source = String::New(c_source); 6944 Local<Script> script = Script::Compile(source); 6945 Local<Value> result = script->Run(); 6946 if (!result->BooleanValue()) { 6947 gc_during_regexp_ = kRequiredGCs; // Allow gc thread to exit. 6948 return; 6949 } 6950 } 6951 { 6952 // Match 15-30 "a"'s against 15 and a "b". 6953 const char* c_source = 6954 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 6955 ".exec('aaaaaaaaaaaaaaaab')[0] === 'aaaaaaaaaaaaaaaa'"; 6956 Local<String> source = String::New(c_source); 6957 Local<Script> script = Script::Compile(source); 6958 Local<Value> result = script->Run(); 6959 if (!result->BooleanValue()) { 6960 gc_during_regexp_ = kRequiredGCs; 6961 return; 6962 } 6963 } 6964 int gc_after = gc_count_; 6965 gc_during_regexp_ += gc_after - gc_before; 6966 rounds++; 6967 i::OS::Sleep(1); 6968 } 6969 regexp_success_ = true; 6970 } 6971 6972 i::Semaphore* block_; 6973 int gc_count_; 6974 int gc_during_regexp_; 6975 bool regexp_success_; 6976 bool gc_success_; 6977}; 6978 6979 6980// Test that a regular expression execution can be interrupted and 6981// survive a garbage collection. 6982TEST(RegExpInterruption) { 6983 v8::Locker lock; 6984 v8::V8::Initialize(); 6985 v8::HandleScope scope; 6986 Local<Context> local_env; 6987 { 6988 LocalContext env; 6989 local_env = env.local(); 6990 } 6991 6992 // Local context should still be live. 6993 CHECK(!local_env.IsEmpty()); 6994 local_env->Enter(); 6995 6996 // Should complete without problems. 6997 RegExpInterruptTest().RunTest(); 6998 6999 local_env->Exit(); 7000} 7001 7002 7003class ApplyInterruptTest { 7004 public: 7005 ApplyInterruptTest() : block_(NULL) {} 7006 ~ApplyInterruptTest() { delete block_; } 7007 void RunTest() { 7008 block_ = i::OS::CreateSemaphore(0); 7009 gc_count_ = 0; 7010 gc_during_apply_ = 0; 7011 apply_success_ = false; 7012 gc_success_ = false; 7013 GCThread gc_thread(this); 7014 gc_thread.Start(); 7015 v8::Locker::StartPreemption(1); 7016 7017 LongRunningApply(); 7018 { 7019 v8::Unlocker unlock; 7020 gc_thread.Join(); 7021 } 7022 v8::Locker::StopPreemption(); 7023 CHECK(apply_success_); 7024 CHECK(gc_success_); 7025 } 7026 private: 7027 // Number of garbage collections required. 7028 static const int kRequiredGCs = 2; 7029 7030 class GCThread : public i::Thread { 7031 public: 7032 explicit GCThread(ApplyInterruptTest* test) 7033 : test_(test) {} 7034 virtual void Run() { 7035 test_->CollectGarbage(); 7036 } 7037 private: 7038 ApplyInterruptTest* test_; 7039 }; 7040 7041 void CollectGarbage() { 7042 block_->Wait(); 7043 while (gc_during_apply_ < kRequiredGCs) { 7044 { 7045 v8::Locker lock; 7046 i::Heap::CollectAllGarbage(false); 7047 gc_count_++; 7048 } 7049 i::OS::Sleep(1); 7050 } 7051 gc_success_ = true; 7052 } 7053 7054 void LongRunningApply() { 7055 block_->Signal(); 7056 int rounds = 0; 7057 while (gc_during_apply_ < kRequiredGCs) { 7058 int gc_before = gc_count_; 7059 { 7060 const char* c_source = 7061 "function do_very_little(bar) {" 7062 " this.foo = bar;" 7063 "}" 7064 "for (var i = 0; i < 100000; i++) {" 7065 " do_very_little.apply(this, ['bar']);" 7066 "}"; 7067 Local<String> source = String::New(c_source); 7068 Local<Script> script = Script::Compile(source); 7069 Local<Value> result = script->Run(); 7070 // Check that no exception was thrown. 7071 CHECK(!result.IsEmpty()); 7072 } 7073 int gc_after = gc_count_; 7074 gc_during_apply_ += gc_after - gc_before; 7075 rounds++; 7076 } 7077 apply_success_ = true; 7078 } 7079 7080 i::Semaphore* block_; 7081 int gc_count_; 7082 int gc_during_apply_; 7083 bool apply_success_; 7084 bool gc_success_; 7085}; 7086 7087 7088// Test that nothing bad happens if we get a preemption just when we were 7089// about to do an apply(). 7090TEST(ApplyInterruption) { 7091 v8::Locker lock; 7092 v8::V8::Initialize(); 7093 v8::HandleScope scope; 7094 Local<Context> local_env; 7095 { 7096 LocalContext env; 7097 local_env = env.local(); 7098 } 7099 7100 // Local context should still be live. 7101 CHECK(!local_env.IsEmpty()); 7102 local_env->Enter(); 7103 7104 // Should complete without problems. 7105 ApplyInterruptTest().RunTest(); 7106 7107 local_env->Exit(); 7108} 7109 7110 7111// Verify that we can clone an object 7112TEST(ObjectClone) { 7113 v8::HandleScope scope; 7114 LocalContext env; 7115 7116 const char* sample = 7117 "var rv = {};" \ 7118 "rv.alpha = 'hello';" \ 7119 "rv.beta = 123;" \ 7120 "rv;"; 7121 7122 // Create an object, verify basics. 7123 Local<Value> val = CompileRun(sample); 7124 CHECK(val->IsObject()); 7125 Local<v8::Object> obj = Local<v8::Object>::Cast(val); 7126 obj->Set(v8_str("gamma"), v8_str("cloneme")); 7127 7128 CHECK_EQ(v8_str("hello"), obj->Get(v8_str("alpha"))); 7129 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 7130 CHECK_EQ(v8_str("cloneme"), obj->Get(v8_str("gamma"))); 7131 7132 // Clone it. 7133 Local<v8::Object> clone = obj->Clone(); 7134 CHECK_EQ(v8_str("hello"), clone->Get(v8_str("alpha"))); 7135 CHECK_EQ(v8::Integer::New(123), clone->Get(v8_str("beta"))); 7136 CHECK_EQ(v8_str("cloneme"), clone->Get(v8_str("gamma"))); 7137 7138 // Set a property on the clone, verify each object. 7139 clone->Set(v8_str("beta"), v8::Integer::New(456)); 7140 CHECK_EQ(v8::Integer::New(123), obj->Get(v8_str("beta"))); 7141 CHECK_EQ(v8::Integer::New(456), clone->Get(v8_str("beta"))); 7142} 7143 7144 7145class AsciiVectorResource : public v8::String::ExternalAsciiStringResource { 7146 public: 7147 explicit AsciiVectorResource(i::Vector<const char> vector) 7148 : data_(vector) {} 7149 virtual ~AsciiVectorResource() {} 7150 virtual size_t length() const { return data_.length(); } 7151 virtual const char* data() const { return data_.start(); } 7152 private: 7153 i::Vector<const char> data_; 7154}; 7155 7156 7157class UC16VectorResource : public v8::String::ExternalStringResource { 7158 public: 7159 explicit UC16VectorResource(i::Vector<const i::uc16> vector) 7160 : data_(vector) {} 7161 virtual ~UC16VectorResource() {} 7162 virtual size_t length() const { return data_.length(); } 7163 virtual const i::uc16* data() const { return data_.start(); } 7164 private: 7165 i::Vector<const i::uc16> data_; 7166}; 7167 7168 7169static void MorphAString(i::String* string, 7170 AsciiVectorResource* ascii_resource, 7171 UC16VectorResource* uc16_resource) { 7172 CHECK(i::StringShape(string).IsExternal()); 7173 if (string->IsAsciiRepresentation()) { 7174 // Check old map is not symbol or long. 7175 CHECK(string->map() == i::Heap::external_ascii_string_map()); 7176 // Morph external string to be TwoByte string. 7177 string->set_map(i::Heap::external_string_map()); 7178 i::ExternalTwoByteString* morphed = 7179 i::ExternalTwoByteString::cast(string); 7180 morphed->set_resource(uc16_resource); 7181 } else { 7182 // Check old map is not symbol or long. 7183 CHECK(string->map() == i::Heap::external_string_map()); 7184 // Morph external string to be ASCII string. 7185 string->set_map(i::Heap::external_ascii_string_map()); 7186 i::ExternalAsciiString* morphed = 7187 i::ExternalAsciiString::cast(string); 7188 morphed->set_resource(ascii_resource); 7189 } 7190} 7191 7192 7193// Test that we can still flatten a string if the components it is built up 7194// from have been turned into 16 bit strings in the mean time. 7195THREADED_TEST(MorphCompositeStringTest) { 7196 const char* c_string = "Now is the time for all good men" 7197 " to come to the aid of the party"; 7198 uint16_t* two_byte_string = AsciiToTwoByteString(c_string); 7199 { 7200 v8::HandleScope scope; 7201 LocalContext env; 7202 AsciiVectorResource ascii_resource( 7203 i::Vector<const char>(c_string, i::StrLength(c_string))); 7204 UC16VectorResource uc16_resource( 7205 i::Vector<const uint16_t>(two_byte_string, 7206 i::StrLength(c_string))); 7207 7208 Local<String> lhs(v8::Utils::ToLocal( 7209 i::Factory::NewExternalStringFromAscii(&ascii_resource))); 7210 Local<String> rhs(v8::Utils::ToLocal( 7211 i::Factory::NewExternalStringFromAscii(&ascii_resource))); 7212 7213 env->Global()->Set(v8_str("lhs"), lhs); 7214 env->Global()->Set(v8_str("rhs"), rhs); 7215 7216 CompileRun( 7217 "var cons = lhs + rhs;" 7218 "var slice = lhs.substring(1, lhs.length - 1);" 7219 "var slice_on_cons = (lhs + rhs).substring(1, lhs.length *2 - 1);"); 7220 7221 MorphAString(*v8::Utils::OpenHandle(*lhs), &ascii_resource, &uc16_resource); 7222 MorphAString(*v8::Utils::OpenHandle(*rhs), &ascii_resource, &uc16_resource); 7223 7224 // Now do some stuff to make sure the strings are flattened, etc. 7225 CompileRun( 7226 "/[^a-z]/.test(cons);" 7227 "/[^a-z]/.test(slice);" 7228 "/[^a-z]/.test(slice_on_cons);"); 7229 const char* expected_cons = 7230 "Now is the time for all good men to come to the aid of the party" 7231 "Now is the time for all good men to come to the aid of the party"; 7232 const char* expected_slice = 7233 "ow is the time for all good men to come to the aid of the part"; 7234 const char* expected_slice_on_cons = 7235 "ow is the time for all good men to come to the aid of the party" 7236 "Now is the time for all good men to come to the aid of the part"; 7237 CHECK_EQ(String::New(expected_cons), 7238 env->Global()->Get(v8_str("cons"))); 7239 CHECK_EQ(String::New(expected_slice), 7240 env->Global()->Get(v8_str("slice"))); 7241 CHECK_EQ(String::New(expected_slice_on_cons), 7242 env->Global()->Get(v8_str("slice_on_cons"))); 7243 } 7244} 7245 7246 7247TEST(CompileExternalTwoByteSource) { 7248 v8::HandleScope scope; 7249 LocalContext context; 7250 7251 // This is a very short list of sources, which currently is to check for a 7252 // regression caused by r2703. 7253 const char* ascii_sources[] = { 7254 "0.5", 7255 "-0.5", // This mainly testes PushBack in the Scanner. 7256 "--0.5", // This mainly testes PushBack in the Scanner. 7257 NULL 7258 }; 7259 7260 // Compile the sources as external two byte strings. 7261 for (int i = 0; ascii_sources[i] != NULL; i++) { 7262 uint16_t* two_byte_string = AsciiToTwoByteString(ascii_sources[i]); 7263 UC16VectorResource uc16_resource( 7264 i::Vector<const uint16_t>(two_byte_string, 7265 i::StrLength(ascii_sources[i]))); 7266 v8::Local<v8::String> source = v8::String::NewExternal(&uc16_resource); 7267 v8::Script::Compile(source); 7268 } 7269} 7270 7271 7272class RegExpStringModificationTest { 7273 public: 7274 RegExpStringModificationTest() 7275 : block_(i::OS::CreateSemaphore(0)), 7276 morphs_(0), 7277 morphs_during_regexp_(0), 7278 ascii_resource_(i::Vector<const char>("aaaaaaaaaaaaaab", 15)), 7279 uc16_resource_(i::Vector<const uint16_t>(two_byte_content_, 15)) {} 7280 ~RegExpStringModificationTest() { delete block_; } 7281 void RunTest() { 7282 regexp_success_ = false; 7283 morph_success_ = false; 7284 7285 // Initialize the contents of two_byte_content_ to be a uc16 representation 7286 // of "aaaaaaaaaaaaaab". 7287 for (int i = 0; i < 14; i++) { 7288 two_byte_content_[i] = 'a'; 7289 } 7290 two_byte_content_[14] = 'b'; 7291 7292 // Create the input string for the regexp - the one we are going to change 7293 // properties of. 7294 input_ = i::Factory::NewExternalStringFromAscii(&ascii_resource_); 7295 7296 // Inject the input as a global variable. 7297 i::Handle<i::String> input_name = 7298 i::Factory::NewStringFromAscii(i::Vector<const char>("input", 5)); 7299 i::Top::global_context()->global()->SetProperty(*input_name, *input_, NONE); 7300 7301 7302 MorphThread morph_thread(this); 7303 morph_thread.Start(); 7304 v8::Locker::StartPreemption(1); 7305 LongRunningRegExp(); 7306 { 7307 v8::Unlocker unlock; 7308 morph_thread.Join(); 7309 } 7310 v8::Locker::StopPreemption(); 7311 CHECK(regexp_success_); 7312 CHECK(morph_success_); 7313 } 7314 private: 7315 7316 // Number of string modifications required. 7317 static const int kRequiredModifications = 5; 7318 static const int kMaxModifications = 100; 7319 7320 class MorphThread : public i::Thread { 7321 public: 7322 explicit MorphThread(RegExpStringModificationTest* test) 7323 : test_(test) {} 7324 virtual void Run() { 7325 test_->MorphString(); 7326 } 7327 private: 7328 RegExpStringModificationTest* test_; 7329 }; 7330 7331 void MorphString() { 7332 block_->Wait(); 7333 while (morphs_during_regexp_ < kRequiredModifications && 7334 morphs_ < kMaxModifications) { 7335 { 7336 v8::Locker lock; 7337 // Swap string between ascii and two-byte representation. 7338 i::String* string = *input_; 7339 MorphAString(string, &ascii_resource_, &uc16_resource_); 7340 morphs_++; 7341 } 7342 i::OS::Sleep(1); 7343 } 7344 morph_success_ = true; 7345 } 7346 7347 void LongRunningRegExp() { 7348 block_->Signal(); // Enable morphing thread on next preemption. 7349 while (morphs_during_regexp_ < kRequiredModifications && 7350 morphs_ < kMaxModifications) { 7351 int morphs_before = morphs_; 7352 { 7353 // Match 15-30 "a"'s against 14 and a "b". 7354 const char* c_source = 7355 "/a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa/" 7356 ".exec(input) === null"; 7357 Local<String> source = String::New(c_source); 7358 Local<Script> script = Script::Compile(source); 7359 Local<Value> result = script->Run(); 7360 CHECK(result->IsTrue()); 7361 } 7362 int morphs_after = morphs_; 7363 morphs_during_regexp_ += morphs_after - morphs_before; 7364 } 7365 regexp_success_ = true; 7366 } 7367 7368 i::uc16 two_byte_content_[15]; 7369 i::Semaphore* block_; 7370 int morphs_; 7371 int morphs_during_regexp_; 7372 bool regexp_success_; 7373 bool morph_success_; 7374 i::Handle<i::String> input_; 7375 AsciiVectorResource ascii_resource_; 7376 UC16VectorResource uc16_resource_; 7377}; 7378 7379 7380// Test that a regular expression execution can be interrupted and 7381// the string changed without failing. 7382TEST(RegExpStringModification) { 7383 v8::Locker lock; 7384 v8::V8::Initialize(); 7385 v8::HandleScope scope; 7386 Local<Context> local_env; 7387 { 7388 LocalContext env; 7389 local_env = env.local(); 7390 } 7391 7392 // Local context should still be live. 7393 CHECK(!local_env.IsEmpty()); 7394 local_env->Enter(); 7395 7396 // Should complete without problems. 7397 RegExpStringModificationTest().RunTest(); 7398 7399 local_env->Exit(); 7400} 7401 7402 7403// Test that we can set a property on the global object even if there 7404// is a read-only property in the prototype chain. 7405TEST(ReadOnlyPropertyInGlobalProto) { 7406 v8::HandleScope scope; 7407 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 7408 LocalContext context(0, templ); 7409 v8::Handle<v8::Object> global = context->Global(); 7410 v8::Handle<v8::Object> global_proto = 7411 v8::Handle<v8::Object>::Cast(global->Get(v8_str("__proto__"))); 7412 global_proto->Set(v8_str("x"), v8::Integer::New(0), v8::ReadOnly); 7413 global_proto->Set(v8_str("y"), v8::Integer::New(0), v8::ReadOnly); 7414 // Check without 'eval' or 'with'. 7415 v8::Handle<v8::Value> res = 7416 CompileRun("function f() { x = 42; return x; }; f()"); 7417 // Check with 'eval'. 7418 res = CompileRun("function f() { eval('1'); y = 42; return y; }; f()"); 7419 CHECK_EQ(v8::Integer::New(42), res); 7420 // Check with 'with'. 7421 res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()"); 7422 CHECK_EQ(v8::Integer::New(42), res); 7423} 7424 7425static int force_set_set_count = 0; 7426static int force_set_get_count = 0; 7427bool pass_on_get = false; 7428 7429static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name, 7430 const v8::AccessorInfo& info) { 7431 force_set_get_count++; 7432 if (pass_on_get) { 7433 return v8::Handle<v8::Value>(); 7434 } else { 7435 return v8::Int32::New(3); 7436 } 7437} 7438 7439static void ForceSetSetter(v8::Local<v8::String> name, 7440 v8::Local<v8::Value> value, 7441 const v8::AccessorInfo& info) { 7442 force_set_set_count++; 7443} 7444 7445static v8::Handle<v8::Value> ForceSetInterceptSetter( 7446 v8::Local<v8::String> name, 7447 v8::Local<v8::Value> value, 7448 const v8::AccessorInfo& info) { 7449 force_set_set_count++; 7450 return v8::Undefined(); 7451} 7452 7453TEST(ForceSet) { 7454 force_set_get_count = 0; 7455 force_set_set_count = 0; 7456 pass_on_get = false; 7457 7458 v8::HandleScope scope; 7459 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 7460 v8::Handle<v8::String> access_property = v8::String::New("a"); 7461 templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter); 7462 LocalContext context(NULL, templ); 7463 v8::Handle<v8::Object> global = context->Global(); 7464 7465 // Ordinary properties 7466 v8::Handle<v8::String> simple_property = v8::String::New("p"); 7467 global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly); 7468 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 7469 // This should fail because the property is read-only 7470 global->Set(simple_property, v8::Int32::New(5)); 7471 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 7472 // This should succeed even though the property is read-only 7473 global->ForceSet(simple_property, v8::Int32::New(6)); 7474 CHECK_EQ(6, global->Get(simple_property)->Int32Value()); 7475 7476 // Accessors 7477 CHECK_EQ(0, force_set_set_count); 7478 CHECK_EQ(0, force_set_get_count); 7479 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 7480 // CHECK_EQ the property shouldn't override it, just call the setter 7481 // which in this case does nothing. 7482 global->Set(access_property, v8::Int32::New(7)); 7483 CHECK_EQ(3, global->Get(access_property)->Int32Value()); 7484 CHECK_EQ(1, force_set_set_count); 7485 CHECK_EQ(2, force_set_get_count); 7486 // Forcing the property to be set should override the accessor without 7487 // calling it 7488 global->ForceSet(access_property, v8::Int32::New(8)); 7489 CHECK_EQ(8, global->Get(access_property)->Int32Value()); 7490 CHECK_EQ(1, force_set_set_count); 7491 CHECK_EQ(2, force_set_get_count); 7492} 7493 7494TEST(ForceSetWithInterceptor) { 7495 force_set_get_count = 0; 7496 force_set_set_count = 0; 7497 pass_on_get = false; 7498 7499 v8::HandleScope scope; 7500 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 7501 templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter); 7502 LocalContext context(NULL, templ); 7503 v8::Handle<v8::Object> global = context->Global(); 7504 7505 v8::Handle<v8::String> some_property = v8::String::New("a"); 7506 CHECK_EQ(0, force_set_set_count); 7507 CHECK_EQ(0, force_set_get_count); 7508 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 7509 // Setting the property shouldn't override it, just call the setter 7510 // which in this case does nothing. 7511 global->Set(some_property, v8::Int32::New(7)); 7512 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 7513 CHECK_EQ(1, force_set_set_count); 7514 CHECK_EQ(2, force_set_get_count); 7515 // Getting the property when the interceptor returns an empty handle 7516 // should yield undefined, since the property isn't present on the 7517 // object itself yet. 7518 pass_on_get = true; 7519 CHECK(global->Get(some_property)->IsUndefined()); 7520 CHECK_EQ(1, force_set_set_count); 7521 CHECK_EQ(3, force_set_get_count); 7522 // Forcing the property to be set should cause the value to be 7523 // set locally without calling the interceptor. 7524 global->ForceSet(some_property, v8::Int32::New(8)); 7525 CHECK_EQ(8, global->Get(some_property)->Int32Value()); 7526 CHECK_EQ(1, force_set_set_count); 7527 CHECK_EQ(4, force_set_get_count); 7528 // Reenabling the interceptor should cause it to take precedence over 7529 // the property 7530 pass_on_get = false; 7531 CHECK_EQ(3, global->Get(some_property)->Int32Value()); 7532 CHECK_EQ(1, force_set_set_count); 7533 CHECK_EQ(5, force_set_get_count); 7534 // The interceptor should also work for other properties 7535 CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value()); 7536 CHECK_EQ(1, force_set_set_count); 7537 CHECK_EQ(6, force_set_get_count); 7538} 7539 7540 7541THREADED_TEST(ForceDelete) { 7542 v8::HandleScope scope; 7543 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 7544 LocalContext context(NULL, templ); 7545 v8::Handle<v8::Object> global = context->Global(); 7546 7547 // Ordinary properties 7548 v8::Handle<v8::String> simple_property = v8::String::New("p"); 7549 global->Set(simple_property, v8::Int32::New(4), v8::DontDelete); 7550 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 7551 // This should fail because the property is dont-delete. 7552 CHECK(!global->Delete(simple_property)); 7553 CHECK_EQ(4, global->Get(simple_property)->Int32Value()); 7554 // This should succeed even though the property is dont-delete. 7555 CHECK(global->ForceDelete(simple_property)); 7556 CHECK(global->Get(simple_property)->IsUndefined()); 7557} 7558 7559 7560static int force_delete_interceptor_count = 0; 7561static bool pass_on_delete = false; 7562 7563 7564static v8::Handle<v8::Boolean> ForceDeleteDeleter( 7565 v8::Local<v8::String> name, 7566 const v8::AccessorInfo& info) { 7567 force_delete_interceptor_count++; 7568 if (pass_on_delete) { 7569 return v8::Handle<v8::Boolean>(); 7570 } else { 7571 return v8::True(); 7572 } 7573} 7574 7575 7576THREADED_TEST(ForceDeleteWithInterceptor) { 7577 force_delete_interceptor_count = 0; 7578 pass_on_delete = false; 7579 7580 v8::HandleScope scope; 7581 v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New(); 7582 templ->SetNamedPropertyHandler(0, 0, 0, ForceDeleteDeleter); 7583 LocalContext context(NULL, templ); 7584 v8::Handle<v8::Object> global = context->Global(); 7585 7586 v8::Handle<v8::String> some_property = v8::String::New("a"); 7587 global->Set(some_property, v8::Integer::New(42), v8::DontDelete); 7588 7589 // Deleting a property should get intercepted and nothing should 7590 // happen. 7591 CHECK_EQ(0, force_delete_interceptor_count); 7592 CHECK(global->Delete(some_property)); 7593 CHECK_EQ(1, force_delete_interceptor_count); 7594 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 7595 // Deleting the property when the interceptor returns an empty 7596 // handle should not delete the property since it is DontDelete. 7597 pass_on_delete = true; 7598 CHECK(!global->Delete(some_property)); 7599 CHECK_EQ(2, force_delete_interceptor_count); 7600 CHECK_EQ(42, global->Get(some_property)->Int32Value()); 7601 // Forcing the property to be deleted should delete the value 7602 // without calling the interceptor. 7603 CHECK(global->ForceDelete(some_property)); 7604 CHECK(global->Get(some_property)->IsUndefined()); 7605 CHECK_EQ(2, force_delete_interceptor_count); 7606} 7607 7608 7609// Make sure that forcing a delete invalidates any IC stubs, so we 7610// don't read the hole value. 7611THREADED_TEST(ForceDeleteIC) { 7612 v8::HandleScope scope; 7613 LocalContext context; 7614 // Create a DontDelete variable on the global object. 7615 CompileRun("this.__proto__ = { foo: 'horse' };" 7616 "var foo = 'fish';" 7617 "function f() { return foo.length; }"); 7618 // Initialize the IC for foo in f. 7619 CompileRun("for (var i = 0; i < 4; i++) f();"); 7620 // Make sure the value of foo is correct before the deletion. 7621 CHECK_EQ(4, CompileRun("f()")->Int32Value()); 7622 // Force the deletion of foo. 7623 CHECK(context->Global()->ForceDelete(v8_str("foo"))); 7624 // Make sure the value for foo is read from the prototype, and that 7625 // we don't get in trouble with reading the deleted cell value 7626 // sentinel. 7627 CHECK_EQ(5, CompileRun("f()")->Int32Value()); 7628} 7629 7630 7631v8::Persistent<Context> calling_context0; 7632v8::Persistent<Context> calling_context1; 7633v8::Persistent<Context> calling_context2; 7634 7635 7636// Check that the call to the callback is initiated in 7637// calling_context2, the directly calling context is calling_context1 7638// and the callback itself is in calling_context0. 7639static v8::Handle<Value> GetCallingContextCallback(const v8::Arguments& args) { 7640 ApiTestFuzzer::Fuzz(); 7641 CHECK(Context::GetCurrent() == calling_context0); 7642 CHECK(Context::GetCalling() == calling_context1); 7643 CHECK(Context::GetEntered() == calling_context2); 7644 return v8::Integer::New(42); 7645} 7646 7647 7648THREADED_TEST(GetCallingContext) { 7649 v8::HandleScope scope; 7650 7651 calling_context0 = Context::New(); 7652 calling_context1 = Context::New(); 7653 calling_context2 = Context::New(); 7654 7655 // Allow cross-domain access. 7656 Local<String> token = v8_str("<security token>"); 7657 calling_context0->SetSecurityToken(token); 7658 calling_context1->SetSecurityToken(token); 7659 calling_context2->SetSecurityToken(token); 7660 7661 // Create an object with a C++ callback in context0. 7662 calling_context0->Enter(); 7663 Local<v8::FunctionTemplate> callback_templ = 7664 v8::FunctionTemplate::New(GetCallingContextCallback); 7665 calling_context0->Global()->Set(v8_str("callback"), 7666 callback_templ->GetFunction()); 7667 calling_context0->Exit(); 7668 7669 // Expose context0 in context1 and setup a function that calls the 7670 // callback function. 7671 calling_context1->Enter(); 7672 calling_context1->Global()->Set(v8_str("context0"), 7673 calling_context0->Global()); 7674 CompileRun("function f() { context0.callback() }"); 7675 calling_context1->Exit(); 7676 7677 // Expose context1 in context2 and call the callback function in 7678 // context0 indirectly through f in context1. 7679 calling_context2->Enter(); 7680 calling_context2->Global()->Set(v8_str("context1"), 7681 calling_context1->Global()); 7682 CompileRun("context1.f()"); 7683 calling_context2->Exit(); 7684 7685 // Dispose the contexts to allow them to be garbage collected. 7686 calling_context0.Dispose(); 7687 calling_context1.Dispose(); 7688 calling_context2.Dispose(); 7689 calling_context0.Clear(); 7690 calling_context1.Clear(); 7691 calling_context2.Clear(); 7692} 7693 7694 7695// Check that a variable declaration with no explicit initialization 7696// value does not shadow an existing property in the prototype chain. 7697// 7698// This is consistent with Firefox and Safari. 7699// 7700// See http://crbug.com/12548. 7701THREADED_TEST(InitGlobalVarInProtoChain) { 7702 v8::HandleScope scope; 7703 LocalContext context; 7704 // Introduce a variable in the prototype chain. 7705 CompileRun("__proto__.x = 42"); 7706 v8::Handle<v8::Value> result = CompileRun("var x; x"); 7707 CHECK(!result->IsUndefined()); 7708 CHECK_EQ(42, result->Int32Value()); 7709} 7710 7711 7712// Regression test for issue 398. 7713// If a function is added to an object, creating a constant function 7714// field, and the result is cloned, replacing the constant function on the 7715// original should not affect the clone. 7716// See http://code.google.com/p/v8/issues/detail?id=398 7717THREADED_TEST(ReplaceConstantFunction) { 7718 v8::HandleScope scope; 7719 LocalContext context; 7720 v8::Handle<v8::Object> obj = v8::Object::New(); 7721 v8::Handle<v8::FunctionTemplate> func_templ = v8::FunctionTemplate::New(); 7722 v8::Handle<v8::String> foo_string = v8::String::New("foo"); 7723 obj->Set(foo_string, func_templ->GetFunction()); 7724 v8::Handle<v8::Object> obj_clone = obj->Clone(); 7725 obj_clone->Set(foo_string, v8::String::New("Hello")); 7726 CHECK(!obj->Get(foo_string)->IsUndefined()); 7727} 7728 7729 7730// Regression test for http://crbug.com/16276. 7731THREADED_TEST(Regress16276) { 7732 v8::HandleScope scope; 7733 LocalContext context; 7734 // Force the IC in f to be a dictionary load IC. 7735 CompileRun("function f(obj) { return obj.x; }\n" 7736 "var obj = { x: { foo: 42 }, y: 87 };\n" 7737 "var x = obj.x;\n" 7738 "delete obj.y;\n" 7739 "for (var i = 0; i < 5; i++) f(obj);"); 7740 // Detach the global object to make 'this' refer directly to the 7741 // global object (not the proxy), and make sure that the dictionary 7742 // load IC doesn't mess up loading directly from the global object. 7743 context->DetachGlobal(); 7744 CHECK_EQ(42, CompileRun("f(this).foo")->Int32Value()); 7745} 7746 7747 7748THREADED_TEST(PixelArray) { 7749 v8::HandleScope scope; 7750 LocalContext context; 7751 const int kElementCount = 260; 7752 uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); 7753 i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount, 7754 pixel_data); 7755 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 7756 for (int i = 0; i < kElementCount; i++) { 7757 pixels->set(i, i % 256); 7758 } 7759 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 7760 for (int i = 0; i < kElementCount; i++) { 7761 CHECK_EQ(i % 256, pixels->get(i)); 7762 CHECK_EQ(i % 256, pixel_data[i]); 7763 } 7764 7765 v8::Handle<v8::Object> obj = v8::Object::New(); 7766 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 7767 // Set the elements to be the pixels. 7768 // jsobj->set_elements(*pixels); 7769 obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount); 7770 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value()); 7771 obj->Set(v8_str("field"), v8::Int32::New(1503)); 7772 context->Global()->Set(v8_str("pixels"), obj); 7773 v8::Handle<v8::Value> result = CompileRun("pixels.field"); 7774 CHECK_EQ(1503, result->Int32Value()); 7775 result = CompileRun("pixels[1]"); 7776 CHECK_EQ(1, result->Int32Value()); 7777 7778 result = CompileRun("var sum = 0;" 7779 "for (var i = 0; i < 8; i++) {" 7780 " sum += pixels[i] = pixels[i] = -i;" 7781 "}" 7782 "sum;"); 7783 CHECK_EQ(-28, result->Int32Value()); 7784 7785 result = CompileRun("var sum = 0;" 7786 "for (var i = 0; i < 8; i++) {" 7787 " sum += pixels[i] = pixels[i] = 0;" 7788 "}" 7789 "sum;"); 7790 CHECK_EQ(0, result->Int32Value()); 7791 7792 result = CompileRun("var sum = 0;" 7793 "for (var i = 0; i < 8; i++) {" 7794 " sum += pixels[i] = pixels[i] = 255;" 7795 "}" 7796 "sum;"); 7797 CHECK_EQ(8 * 255, result->Int32Value()); 7798 7799 result = CompileRun("var sum = 0;" 7800 "for (var i = 0; i < 8; i++) {" 7801 " sum += pixels[i] = pixels[i] = 256 + i;" 7802 "}" 7803 "sum;"); 7804 CHECK_EQ(2076, result->Int32Value()); 7805 7806 result = CompileRun("var sum = 0;" 7807 "for (var i = 0; i < 8; i++) {" 7808 " sum += pixels[i] = pixels[i] = i;" 7809 "}" 7810 "sum;"); 7811 CHECK_EQ(28, result->Int32Value()); 7812 7813 result = CompileRun("var sum = 0;" 7814 "for (var i = 0; i < 8; i++) {" 7815 " sum += pixels[i];" 7816 "}" 7817 "sum;"); 7818 CHECK_EQ(28, result->Int32Value()); 7819 7820 i::Handle<i::Smi> value(i::Smi::FromInt(2)); 7821 i::SetElement(jsobj, 1, value); 7822 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(1))->value()); 7823 *value.location() = i::Smi::FromInt(256); 7824 i::SetElement(jsobj, 1, value); 7825 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(1))->value()); 7826 *value.location() = i::Smi::FromInt(-1); 7827 i::SetElement(jsobj, 1, value); 7828 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value()); 7829 7830 result = CompileRun("for (var i = 0; i < 8; i++) {" 7831 " pixels[i] = (i * 65) - 109;" 7832 "}" 7833 "pixels[1] + pixels[6];"); 7834 CHECK_EQ(255, result->Int32Value()); 7835 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value()); 7836 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(1))->value()); 7837 CHECK_EQ(21, i::Smi::cast(jsobj->GetElement(2))->value()); 7838 CHECK_EQ(86, i::Smi::cast(jsobj->GetElement(3))->value()); 7839 CHECK_EQ(151, i::Smi::cast(jsobj->GetElement(4))->value()); 7840 CHECK_EQ(216, i::Smi::cast(jsobj->GetElement(5))->value()); 7841 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(6))->value()); 7842 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(7))->value()); 7843 result = CompileRun("var sum = 0;" 7844 "for (var i = 0; i < 8; i++) {" 7845 " sum += pixels[i];" 7846 "}" 7847 "sum;"); 7848 CHECK_EQ(984, result->Int32Value()); 7849 7850 result = CompileRun("for (var i = 0; i < 8; i++) {" 7851 " pixels[i] = (i * 1.1);" 7852 "}" 7853 "pixels[1] + pixels[6];"); 7854 CHECK_EQ(8, result->Int32Value()); 7855 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(0))->value()); 7856 CHECK_EQ(1, i::Smi::cast(jsobj->GetElement(1))->value()); 7857 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(2))->value()); 7858 CHECK_EQ(3, i::Smi::cast(jsobj->GetElement(3))->value()); 7859 CHECK_EQ(4, i::Smi::cast(jsobj->GetElement(4))->value()); 7860 CHECK_EQ(6, i::Smi::cast(jsobj->GetElement(5))->value()); 7861 CHECK_EQ(7, i::Smi::cast(jsobj->GetElement(6))->value()); 7862 CHECK_EQ(8, i::Smi::cast(jsobj->GetElement(7))->value()); 7863 7864 result = CompileRun("for (var i = 0; i < 8; i++) {" 7865 " pixels[7] = undefined;" 7866 "}" 7867 "pixels[7];"); 7868 CHECK_EQ(0, result->Int32Value()); 7869 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(7))->value()); 7870 7871 result = CompileRun("for (var i = 0; i < 8; i++) {" 7872 " pixels[6] = '2.3';" 7873 "}" 7874 "pixels[6];"); 7875 CHECK_EQ(2, result->Int32Value()); 7876 CHECK_EQ(2, i::Smi::cast(jsobj->GetElement(6))->value()); 7877 7878 result = CompileRun("for (var i = 0; i < 8; i++) {" 7879 " pixels[5] = NaN;" 7880 "}" 7881 "pixels[5];"); 7882 CHECK_EQ(0, result->Int32Value()); 7883 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 7884 7885 result = CompileRun("for (var i = 0; i < 8; i++) {" 7886 " pixels[8] = Infinity;" 7887 "}" 7888 "pixels[8];"); 7889 CHECK_EQ(255, result->Int32Value()); 7890 CHECK_EQ(255, i::Smi::cast(jsobj->GetElement(8))->value()); 7891 7892 result = CompileRun("for (var i = 0; i < 8; i++) {" 7893 " pixels[9] = -Infinity;" 7894 "}" 7895 "pixels[9];"); 7896 CHECK_EQ(0, result->Int32Value()); 7897 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(9))->value()); 7898 7899 result = CompileRun("pixels[3] = 33;" 7900 "delete pixels[3];" 7901 "pixels[3];"); 7902 CHECK_EQ(33, result->Int32Value()); 7903 7904 result = CompileRun("pixels[0] = 10; pixels[1] = 11;" 7905 "pixels[2] = 12; pixels[3] = 13;" 7906 "pixels.__defineGetter__('2'," 7907 "function() { return 120; });" 7908 "pixels[2];"); 7909 CHECK_EQ(12, result->Int32Value()); 7910 7911 result = CompileRun("var js_array = new Array(40);" 7912 "js_array[0] = 77;" 7913 "js_array;"); 7914 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 7915 7916 result = CompileRun("pixels[1] = 23;" 7917 "pixels.__proto__ = [];" 7918 "js_array.__proto__ = pixels;" 7919 "js_array.concat(pixels);"); 7920 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 7921 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 7922 7923 result = CompileRun("pixels[1] = 23;"); 7924 CHECK_EQ(23, result->Int32Value()); 7925 7926 // Test for index greater than 255. Regression test for: 7927 // http://code.google.com/p/chromium/issues/detail?id=26337. 7928 result = CompileRun("pixels[256] = 255;"); 7929 CHECK_EQ(255, result->Int32Value()); 7930 result = CompileRun("var i = 0;" 7931 "for (var j = 0; j < 8; j++) { i = pixels[256]; }" 7932 "i"); 7933 CHECK_EQ(255, result->Int32Value()); 7934 7935 free(pixel_data); 7936} 7937 7938 7939template <class ExternalArrayClass, class ElementType> 7940static void ExternalArrayTestHelper(v8::ExternalArrayType array_type, 7941 int64_t low, 7942 int64_t high) { 7943 v8::HandleScope scope; 7944 LocalContext context; 7945 const int kElementCount = 40; 7946 int element_size = 0; 7947 switch (array_type) { 7948 case v8::kExternalByteArray: 7949 case v8::kExternalUnsignedByteArray: 7950 element_size = 1; 7951 break; 7952 case v8::kExternalShortArray: 7953 case v8::kExternalUnsignedShortArray: 7954 element_size = 2; 7955 break; 7956 case v8::kExternalIntArray: 7957 case v8::kExternalUnsignedIntArray: 7958 case v8::kExternalFloatArray: 7959 element_size = 4; 7960 break; 7961 default: 7962 UNREACHABLE(); 7963 break; 7964 } 7965 ElementType* array_data = 7966 static_cast<ElementType*>(malloc(kElementCount * element_size)); 7967 i::Handle<ExternalArrayClass> array = 7968 i::Handle<ExternalArrayClass>::cast( 7969 i::Factory::NewExternalArray(kElementCount, array_type, array_data)); 7970 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 7971 for (int i = 0; i < kElementCount; i++) { 7972 array->set(i, static_cast<ElementType>(i)); 7973 } 7974 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 7975 for (int i = 0; i < kElementCount; i++) { 7976 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array->get(i))); 7977 CHECK_EQ(static_cast<int64_t>(i), static_cast<int64_t>(array_data[i])); 7978 } 7979 7980 v8::Handle<v8::Object> obj = v8::Object::New(); 7981 i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); 7982 // Set the elements to be the external array. 7983 obj->SetIndexedPropertiesToExternalArrayData(array_data, 7984 array_type, 7985 kElementCount); 7986 CHECK_EQ(1, static_cast<int>(jsobj->GetElement(1)->Number())); 7987 obj->Set(v8_str("field"), v8::Int32::New(1503)); 7988 context->Global()->Set(v8_str("ext_array"), obj); 7989 v8::Handle<v8::Value> result = CompileRun("ext_array.field"); 7990 CHECK_EQ(1503, result->Int32Value()); 7991 result = CompileRun("ext_array[1]"); 7992 CHECK_EQ(1, result->Int32Value()); 7993 7994 // Check pass through of assigned smis 7995 result = CompileRun("var sum = 0;" 7996 "for (var i = 0; i < 8; i++) {" 7997 " sum += ext_array[i] = ext_array[i] = -i;" 7998 "}" 7999 "sum;"); 8000 CHECK_EQ(-28, result->Int32Value()); 8001 8002 // Check assigned smis 8003 result = CompileRun("for (var i = 0; i < 8; i++) {" 8004 " ext_array[i] = i;" 8005 "}" 8006 "var sum = 0;" 8007 "for (var i = 0; i < 8; i++) {" 8008 " sum += ext_array[i];" 8009 "}" 8010 "sum;"); 8011 CHECK_EQ(28, result->Int32Value()); 8012 8013 // Check assigned smis in reverse order 8014 result = CompileRun("for (var i = 8; --i >= 0; ) {" 8015 " ext_array[i] = i;" 8016 "}" 8017 "var sum = 0;" 8018 "for (var i = 0; i < 8; i++) {" 8019 " sum += ext_array[i];" 8020 "}" 8021 "sum;"); 8022 CHECK_EQ(28, result->Int32Value()); 8023 8024 // Check pass through of assigned HeapNumbers 8025 result = CompileRun("var sum = 0;" 8026 "for (var i = 0; i < 16; i+=2) {" 8027 " sum += ext_array[i] = ext_array[i] = (-i * 0.5);" 8028 "}" 8029 "sum;"); 8030 CHECK_EQ(-28, result->Int32Value()); 8031 8032 // Check assigned HeapNumbers 8033 result = CompileRun("for (var i = 0; i < 16; i+=2) {" 8034 " ext_array[i] = (i * 0.5);" 8035 "}" 8036 "var sum = 0;" 8037 "for (var i = 0; i < 16; i+=2) {" 8038 " sum += ext_array[i];" 8039 "}" 8040 "sum;"); 8041 CHECK_EQ(28, result->Int32Value()); 8042 8043 // Check assigned HeapNumbers in reverse order 8044 result = CompileRun("for (var i = 14; i >= 0; i-=2) {" 8045 " ext_array[i] = (i * 0.5);" 8046 "}" 8047 "var sum = 0;" 8048 "for (var i = 0; i < 16; i+=2) {" 8049 " sum += ext_array[i];" 8050 "}" 8051 "sum;"); 8052 CHECK_EQ(28, result->Int32Value()); 8053 8054 i::ScopedVector<char> test_buf(1024); 8055 8056 // Check legal boundary conditions. 8057 // The repeated loads and stores ensure the ICs are exercised. 8058 const char* boundary_program = 8059 "var res = 0;" 8060 "for (var i = 0; i < 16; i++) {" 8061 " ext_array[i] = %lld;" 8062 " if (i > 8) {" 8063 " res = ext_array[i];" 8064 " }" 8065 "}" 8066 "res;"; 8067 i::OS::SNPrintF(test_buf, 8068 boundary_program, 8069 low); 8070 result = CompileRun(test_buf.start()); 8071 CHECK_EQ(low, result->IntegerValue()); 8072 8073 i::OS::SNPrintF(test_buf, 8074 boundary_program, 8075 high); 8076 result = CompileRun(test_buf.start()); 8077 CHECK_EQ(high, result->IntegerValue()); 8078 8079 // Check misprediction of type in IC. 8080 result = CompileRun("var tmp_array = ext_array;" 8081 "var sum = 0;" 8082 "for (var i = 0; i < 8; i++) {" 8083 " tmp_array[i] = i;" 8084 " sum += tmp_array[i];" 8085 " if (i == 4) {" 8086 " tmp_array = {};" 8087 " }" 8088 "}" 8089 "sum;"); 8090 i::Heap::CollectAllGarbage(false); // Force GC to trigger verification. 8091 CHECK_EQ(28, result->Int32Value()); 8092 8093 // Make sure out-of-range loads do not throw. 8094 i::OS::SNPrintF(test_buf, 8095 "var caught_exception = false;" 8096 "try {" 8097 " ext_array[%d];" 8098 "} catch (e) {" 8099 " caught_exception = true;" 8100 "}" 8101 "caught_exception;", 8102 kElementCount); 8103 result = CompileRun(test_buf.start()); 8104 CHECK_EQ(false, result->BooleanValue()); 8105 8106 // Make sure out-of-range stores do not throw. 8107 i::OS::SNPrintF(test_buf, 8108 "var caught_exception = false;" 8109 "try {" 8110 " ext_array[%d] = 1;" 8111 "} catch (e) {" 8112 " caught_exception = true;" 8113 "}" 8114 "caught_exception;", 8115 kElementCount); 8116 result = CompileRun(test_buf.start()); 8117 CHECK_EQ(false, result->BooleanValue()); 8118 8119 // Check other boundary conditions, values and operations. 8120 result = CompileRun("for (var i = 0; i < 8; i++) {" 8121 " ext_array[7] = undefined;" 8122 "}" 8123 "ext_array[7];"); 8124 CHECK_EQ(0, result->Int32Value()); 8125 CHECK_EQ(0, static_cast<int>(jsobj->GetElement(7)->Number())); 8126 8127 result = CompileRun("for (var i = 0; i < 8; i++) {" 8128 " ext_array[6] = '2.3';" 8129 "}" 8130 "ext_array[6];"); 8131 CHECK_EQ(2, result->Int32Value()); 8132 CHECK_EQ(2, static_cast<int>(jsobj->GetElement(6)->Number())); 8133 8134 if (array_type != v8::kExternalFloatArray) { 8135 // Though the specification doesn't state it, be explicit about 8136 // converting NaNs and +/-Infinity to zero. 8137 result = CompileRun("for (var i = 0; i < 8; i++) {" 8138 " ext_array[i] = 5;" 8139 "}" 8140 "for (var i = 0; i < 8; i++) {" 8141 " ext_array[i] = NaN;" 8142 "}" 8143 "ext_array[5];"); 8144 CHECK_EQ(0, result->Int32Value()); 8145 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 8146 8147 result = CompileRun("for (var i = 0; i < 8; i++) {" 8148 " ext_array[i] = 5;" 8149 "}" 8150 "for (var i = 0; i < 8; i++) {" 8151 " ext_array[i] = Infinity;" 8152 "}" 8153 "ext_array[5];"); 8154 CHECK_EQ(0, result->Int32Value()); 8155 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 8156 8157 result = CompileRun("for (var i = 0; i < 8; i++) {" 8158 " ext_array[i] = 5;" 8159 "}" 8160 "for (var i = 0; i < 8; i++) {" 8161 " ext_array[i] = -Infinity;" 8162 "}" 8163 "ext_array[5];"); 8164 CHECK_EQ(0, result->Int32Value()); 8165 CHECK_EQ(0, i::Smi::cast(jsobj->GetElement(5))->value()); 8166 } 8167 8168 result = CompileRun("ext_array[3] = 33;" 8169 "delete ext_array[3];" 8170 "ext_array[3];"); 8171 CHECK_EQ(33, result->Int32Value()); 8172 8173 result = CompileRun("ext_array[0] = 10; ext_array[1] = 11;" 8174 "ext_array[2] = 12; ext_array[3] = 13;" 8175 "ext_array.__defineGetter__('2'," 8176 "function() { return 120; });" 8177 "ext_array[2];"); 8178 CHECK_EQ(12, result->Int32Value()); 8179 8180 result = CompileRun("var js_array = new Array(40);" 8181 "js_array[0] = 77;" 8182 "js_array;"); 8183 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 8184 8185 result = CompileRun("ext_array[1] = 23;" 8186 "ext_array.__proto__ = [];" 8187 "js_array.__proto__ = ext_array;" 8188 "js_array.concat(ext_array);"); 8189 CHECK_EQ(77, v8::Object::Cast(*result)->Get(v8_str("0"))->Int32Value()); 8190 CHECK_EQ(23, v8::Object::Cast(*result)->Get(v8_str("1"))->Int32Value()); 8191 8192 result = CompileRun("ext_array[1] = 23;"); 8193 CHECK_EQ(23, result->Int32Value()); 8194 8195 // Test more complex manipulations which cause eax to contain values 8196 // that won't be completely overwritten by loads from the arrays. 8197 // This catches bugs in the instructions used for the KeyedLoadIC 8198 // for byte and word types. 8199 { 8200 const int kXSize = 300; 8201 const int kYSize = 300; 8202 const int kLargeElementCount = kXSize * kYSize * 4; 8203 ElementType* large_array_data = 8204 static_cast<ElementType*>(malloc(kLargeElementCount * element_size)); 8205 i::Handle<ExternalArrayClass> large_array = 8206 i::Handle<ExternalArrayClass>::cast( 8207 i::Factory::NewExternalArray(kLargeElementCount, 8208 array_type, 8209 array_data)); 8210 v8::Handle<v8::Object> large_obj = v8::Object::New(); 8211 // Set the elements to be the external array. 8212 large_obj->SetIndexedPropertiesToExternalArrayData(large_array_data, 8213 array_type, 8214 kLargeElementCount); 8215 context->Global()->Set(v8_str("large_array"), large_obj); 8216 // Initialize contents of a few rows. 8217 for (int x = 0; x < 300; x++) { 8218 int row = 0; 8219 int offset = row * 300 * 4; 8220 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 8221 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 8222 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 8223 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 8224 row = 150; 8225 offset = row * 300 * 4; 8226 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 8227 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 8228 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 8229 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 8230 row = 298; 8231 offset = row * 300 * 4; 8232 large_array_data[offset + 4 * x + 0] = (ElementType) 127; 8233 large_array_data[offset + 4 * x + 1] = (ElementType) 0; 8234 large_array_data[offset + 4 * x + 2] = (ElementType) 0; 8235 large_array_data[offset + 4 * x + 3] = (ElementType) 127; 8236 } 8237 // The goal of the code below is to make "offset" large enough 8238 // that the computation of the index (which goes into eax) has 8239 // high bits set which will not be overwritten by a byte or short 8240 // load. 8241 result = CompileRun("var failed = false;" 8242 "var offset = 0;" 8243 "for (var i = 0; i < 300; i++) {" 8244 " if (large_array[4 * i] != 127 ||" 8245 " large_array[4 * i + 1] != 0 ||" 8246 " large_array[4 * i + 2] != 0 ||" 8247 " large_array[4 * i + 3] != 127) {" 8248 " failed = true;" 8249 " }" 8250 "}" 8251 "offset = 150 * 300 * 4;" 8252 "for (var i = 0; i < 300; i++) {" 8253 " if (large_array[offset + 4 * i] != 127 ||" 8254 " large_array[offset + 4 * i + 1] != 0 ||" 8255 " large_array[offset + 4 * i + 2] != 0 ||" 8256 " large_array[offset + 4 * i + 3] != 127) {" 8257 " failed = true;" 8258 " }" 8259 "}" 8260 "offset = 298 * 300 * 4;" 8261 "for (var i = 0; i < 300; i++) {" 8262 " if (large_array[offset + 4 * i] != 127 ||" 8263 " large_array[offset + 4 * i + 1] != 0 ||" 8264 " large_array[offset + 4 * i + 2] != 0 ||" 8265 " large_array[offset + 4 * i + 3] != 127) {" 8266 " failed = true;" 8267 " }" 8268 "}" 8269 "!failed;"); 8270 CHECK_EQ(true, result->BooleanValue()); 8271 free(large_array_data); 8272 } 8273 8274 free(array_data); 8275} 8276 8277 8278THREADED_TEST(ExternalByteArray) { 8279 ExternalArrayTestHelper<v8::internal::ExternalByteArray, int8_t>( 8280 v8::kExternalByteArray, 8281 -128, 8282 127); 8283} 8284 8285 8286THREADED_TEST(ExternalUnsignedByteArray) { 8287 ExternalArrayTestHelper<v8::internal::ExternalUnsignedByteArray, uint8_t>( 8288 v8::kExternalUnsignedByteArray, 8289 0, 8290 255); 8291} 8292 8293 8294THREADED_TEST(ExternalShortArray) { 8295 ExternalArrayTestHelper<v8::internal::ExternalShortArray, int16_t>( 8296 v8::kExternalShortArray, 8297 -32768, 8298 32767); 8299} 8300 8301 8302THREADED_TEST(ExternalUnsignedShortArray) { 8303 ExternalArrayTestHelper<v8::internal::ExternalUnsignedShortArray, uint16_t>( 8304 v8::kExternalUnsignedShortArray, 8305 0, 8306 65535); 8307} 8308 8309 8310THREADED_TEST(ExternalIntArray) { 8311 ExternalArrayTestHelper<v8::internal::ExternalIntArray, int32_t>( 8312 v8::kExternalIntArray, 8313 INT_MIN, // -2147483648 8314 INT_MAX); // 2147483647 8315} 8316 8317 8318THREADED_TEST(ExternalUnsignedIntArray) { 8319 ExternalArrayTestHelper<v8::internal::ExternalUnsignedIntArray, uint32_t>( 8320 v8::kExternalUnsignedIntArray, 8321 0, 8322 UINT_MAX); // 4294967295 8323} 8324 8325 8326THREADED_TEST(ExternalFloatArray) { 8327 ExternalArrayTestHelper<v8::internal::ExternalFloatArray, float>( 8328 v8::kExternalFloatArray, 8329 -500, 8330 500); 8331} 8332 8333 8334THREADED_TEST(ExternalArrays) { 8335 TestExternalByteArray(); 8336 TestExternalUnsignedByteArray(); 8337 TestExternalShortArray(); 8338 TestExternalUnsignedShortArray(); 8339 TestExternalIntArray(); 8340 TestExternalUnsignedIntArray(); 8341 TestExternalFloatArray(); 8342} 8343 8344 8345THREADED_TEST(ScriptContextDependence) { 8346 v8::HandleScope scope; 8347 LocalContext c1; 8348 const char *source = "foo"; 8349 v8::Handle<v8::Script> dep = v8::Script::Compile(v8::String::New(source)); 8350 v8::Handle<v8::Script> indep = v8::Script::New(v8::String::New(source)); 8351 c1->Global()->Set(v8::String::New("foo"), v8::Integer::New(100)); 8352 CHECK_EQ(dep->Run()->Int32Value(), 100); 8353 CHECK_EQ(indep->Run()->Int32Value(), 100); 8354 LocalContext c2; 8355 c2->Global()->Set(v8::String::New("foo"), v8::Integer::New(101)); 8356 CHECK_EQ(dep->Run()->Int32Value(), 100); 8357 CHECK_EQ(indep->Run()->Int32Value(), 101); 8358} 8359 8360 8361THREADED_TEST(StackTrace) { 8362 v8::HandleScope scope; 8363 LocalContext context; 8364 v8::TryCatch try_catch; 8365 const char *source = "function foo() { FAIL.FAIL; }; foo();"; 8366 v8::Handle<v8::String> src = v8::String::New(source); 8367 v8::Handle<v8::String> origin = v8::String::New("stack-trace-test"); 8368 v8::Script::New(src, origin)->Run(); 8369 CHECK(try_catch.HasCaught()); 8370 v8::String::Utf8Value stack(try_catch.StackTrace()); 8371 CHECK(strstr(*stack, "at foo (stack-trace-test") != NULL); 8372} 8373 8374 8375// Test that idle notification can be handled and eventually returns true. 8376THREADED_TEST(IdleNotification) { 8377 bool rv = false; 8378 for (int i = 0; i < 100; i++) { 8379 rv = v8::V8::IdleNotification(); 8380 if (rv) 8381 break; 8382 } 8383 CHECK(rv == true); 8384} 8385 8386 8387static uint32_t* stack_limit; 8388 8389static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) { 8390 stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit()); 8391 return v8::Undefined(); 8392} 8393 8394 8395// Uses the address of a local variable to determine the stack top now. 8396// Given a size, returns an address that is that far from the current 8397// top of stack. 8398static uint32_t* ComputeStackLimit(uint32_t size) { 8399 uint32_t* answer = &size - (size / sizeof(size)); 8400 // If the size is very large and the stack is very near the bottom of 8401 // memory then the calculation above may wrap around and give an address 8402 // that is above the (downwards-growing) stack. In that case we return 8403 // a very low address. 8404 if (answer > &size) return reinterpret_cast<uint32_t*>(sizeof(size)); 8405 return answer; 8406} 8407 8408 8409TEST(SetResourceConstraints) { 8410 static const int K = 1024; 8411 uint32_t* set_limit = ComputeStackLimit(128 * K); 8412 8413 // Set stack limit. 8414 v8::ResourceConstraints constraints; 8415 constraints.set_stack_limit(set_limit); 8416 CHECK(v8::SetResourceConstraints(&constraints)); 8417 8418 // Execute a script. 8419 v8::HandleScope scope; 8420 LocalContext env; 8421 Local<v8::FunctionTemplate> fun_templ = 8422 v8::FunctionTemplate::New(GetStackLimitCallback); 8423 Local<Function> fun = fun_templ->GetFunction(); 8424 env->Global()->Set(v8_str("get_stack_limit"), fun); 8425 CompileRun("get_stack_limit();"); 8426 8427 CHECK(stack_limit == set_limit); 8428} 8429 8430 8431TEST(SetResourceConstraintsInThread) { 8432 uint32_t* set_limit; 8433 { 8434 v8::Locker locker; 8435 static const int K = 1024; 8436 set_limit = ComputeStackLimit(128 * K); 8437 8438 // Set stack limit. 8439 v8::ResourceConstraints constraints; 8440 constraints.set_stack_limit(set_limit); 8441 CHECK(v8::SetResourceConstraints(&constraints)); 8442 8443 // Execute a script. 8444 v8::HandleScope scope; 8445 LocalContext env; 8446 Local<v8::FunctionTemplate> fun_templ = 8447 v8::FunctionTemplate::New(GetStackLimitCallback); 8448 Local<Function> fun = fun_templ->GetFunction(); 8449 env->Global()->Set(v8_str("get_stack_limit"), fun); 8450 CompileRun("get_stack_limit();"); 8451 8452 CHECK(stack_limit == set_limit); 8453 } 8454 { 8455 v8::Locker locker; 8456 CHECK(stack_limit == set_limit); 8457 } 8458} 8459 8460 8461THREADED_TEST(GetHeapStatistics) { 8462 v8::HandleScope scope; 8463 LocalContext c1; 8464 v8::HeapStatistics heap_statistics; 8465 CHECK_EQ(static_cast<int>(heap_statistics.total_heap_size()), 0); 8466 CHECK_EQ(static_cast<int>(heap_statistics.used_heap_size()), 0); 8467 v8::V8::GetHeapStatistics(&heap_statistics); 8468 CHECK_NE(static_cast<int>(heap_statistics.total_heap_size()), 0); 8469 CHECK_NE(static_cast<int>(heap_statistics.used_heap_size()), 0); 8470} 8471 8472 8473static double DoubleFromBits(uint64_t value) { 8474 double target; 8475#ifdef BIG_ENDIAN_FLOATING_POINT 8476 const int kIntSize = 4; 8477 // Somebody swapped the lower and higher half of doubles. 8478 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); 8479 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); 8480#else 8481 memcpy(&target, &value, sizeof(target)); 8482#endif 8483 return target; 8484} 8485 8486 8487static uint64_t DoubleToBits(double value) { 8488 uint64_t target; 8489#ifdef BIG_ENDIAN_FLOATING_POINT 8490 const int kIntSize = 4; 8491 // Somebody swapped the lower and higher half of doubles. 8492 memcpy(&target, reinterpret_cast<char*>(&value) + kIntSize, kIntSize); 8493 memcpy(reinterpret_cast<char*>(&target) + kIntSize, &value, kIntSize); 8494#else 8495 memcpy(&target, &value, sizeof(target)); 8496#endif 8497 return target; 8498} 8499 8500 8501static double DoubleToDateTime(double input) { 8502 double date_limit = 864e13; 8503 if (IsNaN(input) || input < -date_limit || input > date_limit) { 8504 return i::OS::nan_value(); 8505 } 8506 return (input < 0) ? -(floor(-input)) : floor(input); 8507} 8508 8509// We don't have a consistent way to write 64-bit constants syntactically, so we 8510// split them into two 32-bit constants and combine them programmatically. 8511static double DoubleFromBits(uint32_t high_bits, uint32_t low_bits) { 8512 return DoubleFromBits((static_cast<uint64_t>(high_bits) << 32) | low_bits); 8513} 8514 8515 8516THREADED_TEST(QuietSignalingNaNs) { 8517 v8::HandleScope scope; 8518 LocalContext context; 8519 v8::TryCatch try_catch; 8520 8521 // Special double values. 8522 double snan = DoubleFromBits(0x7ff00000, 0x00000001); 8523 double qnan = DoubleFromBits(0x7ff80000, 0x00000000); 8524 double infinity = DoubleFromBits(0x7ff00000, 0x00000000); 8525 double max_normal = DoubleFromBits(0x7fefffff, 0xffffffffu); 8526 double min_normal = DoubleFromBits(0x00100000, 0x00000000); 8527 double max_denormal = DoubleFromBits(0x000fffff, 0xffffffffu); 8528 double min_denormal = DoubleFromBits(0x00000000, 0x00000001); 8529 8530 // Date values are capped at +/-100000000 days (times 864e5 ms per day) 8531 // on either side of the epoch. 8532 double date_limit = 864e13; 8533 8534 double test_values[] = { 8535 snan, 8536 qnan, 8537 infinity, 8538 max_normal, 8539 date_limit + 1, 8540 date_limit, 8541 min_normal, 8542 max_denormal, 8543 min_denormal, 8544 0, 8545 -0, 8546 -min_denormal, 8547 -max_denormal, 8548 -min_normal, 8549 -date_limit, 8550 -date_limit - 1, 8551 -max_normal, 8552 -infinity, 8553 -qnan, 8554 -snan 8555 }; 8556 int num_test_values = 20; 8557 8558 for (int i = 0; i < num_test_values; i++) { 8559 double test_value = test_values[i]; 8560 8561 // Check that Number::New preserves non-NaNs and quiets SNaNs. 8562 v8::Handle<v8::Value> number = v8::Number::New(test_value); 8563 double stored_number = number->NumberValue(); 8564 if (!IsNaN(test_value)) { 8565 CHECK_EQ(test_value, stored_number); 8566 } else { 8567 uint64_t stored_bits = DoubleToBits(stored_number); 8568 // Check if quiet nan (bits 51..62 all set). 8569 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 8570 } 8571 8572 // Check that Date::New preserves non-NaNs in the date range and 8573 // quiets SNaNs. 8574 v8::Handle<v8::Value> date = v8::Date::New(test_value); 8575 double expected_stored_date = DoubleToDateTime(test_value); 8576 double stored_date = date->NumberValue(); 8577 if (!IsNaN(expected_stored_date)) { 8578 CHECK_EQ(expected_stored_date, stored_date); 8579 } else { 8580 uint64_t stored_bits = DoubleToBits(stored_date); 8581 // Check if quiet nan (bits 51..62 all set). 8582 CHECK_EQ(0xfff, static_cast<int>((stored_bits >> 51) & 0xfff)); 8583 } 8584 } 8585} 8586 8587 8588static v8::Handle<Value> SpaghettiIncident(const v8::Arguments& args) { 8589 v8::HandleScope scope; 8590 v8::TryCatch tc; 8591 v8::Handle<v8::String> str = args[0]->ToString(); 8592 if (tc.HasCaught()) 8593 return tc.ReThrow(); 8594 return v8::Undefined(); 8595} 8596 8597 8598// Test that an exception can be propagated down through a spaghetti 8599// stack using ReThrow. 8600THREADED_TEST(SpaghettiStackReThrow) { 8601 v8::HandleScope scope; 8602 LocalContext context; 8603 context->Global()->Set( 8604 v8::String::New("s"), 8605 v8::FunctionTemplate::New(SpaghettiIncident)->GetFunction()); 8606 v8::TryCatch try_catch; 8607 CompileRun( 8608 "var i = 0;" 8609 "var o = {" 8610 " toString: function () {" 8611 " if (i == 10) {" 8612 " throw 'Hey!';" 8613 " } else {" 8614 " i++;" 8615 " return s(o);" 8616 " }" 8617 " }" 8618 "};" 8619 "s(o);"); 8620 CHECK(try_catch.HasCaught()); 8621 v8::String::Utf8Value value(try_catch.Exception()); 8622 CHECK_EQ(0, strcmp(*value, "Hey!")); 8623} 8624 8625 8626TEST(Regress528) { 8627 v8::V8::Initialize(); 8628 8629 v8::HandleScope scope; 8630 v8::Persistent<Context> context; 8631 v8::Persistent<Context> other_context; 8632 int gc_count; 8633 8634 // Create a context used to keep the code from aging in the compilation 8635 // cache. 8636 other_context = Context::New(); 8637 8638 // Context-dependent context data creates reference from the compilation 8639 // cache to the global object. 8640 const char* source_simple = "1"; 8641 context = Context::New(); 8642 { 8643 v8::HandleScope scope; 8644 8645 context->Enter(); 8646 Local<v8::String> obj = v8::String::New(""); 8647 context->SetData(obj); 8648 CompileRun(source_simple); 8649 context->Exit(); 8650 } 8651 context.Dispose(); 8652 for (gc_count = 1; gc_count < 10; gc_count++) { 8653 other_context->Enter(); 8654 CompileRun(source_simple); 8655 other_context->Exit(); 8656 v8::internal::Heap::CollectAllGarbage(false); 8657 if (GetGlobalObjectsCount() == 1) break; 8658 } 8659 CHECK_GE(2, gc_count); 8660 CHECK_EQ(1, GetGlobalObjectsCount()); 8661 8662 // Eval in a function creates reference from the compilation cache to the 8663 // global object. 8664 const char* source_eval = "function f(){eval('1')}; f()"; 8665 context = Context::New(); 8666 { 8667 v8::HandleScope scope; 8668 8669 context->Enter(); 8670 CompileRun(source_eval); 8671 context->Exit(); 8672 } 8673 context.Dispose(); 8674 for (gc_count = 1; gc_count < 10; gc_count++) { 8675 other_context->Enter(); 8676 CompileRun(source_eval); 8677 other_context->Exit(); 8678 v8::internal::Heap::CollectAllGarbage(false); 8679 if (GetGlobalObjectsCount() == 1) break; 8680 } 8681 CHECK_GE(2, gc_count); 8682 CHECK_EQ(1, GetGlobalObjectsCount()); 8683 8684 // Looking up the line number for an exception creates reference from the 8685 // compilation cache to the global object. 8686 const char* source_exception = "function f(){throw 1;} f()"; 8687 context = Context::New(); 8688 { 8689 v8::HandleScope scope; 8690 8691 context->Enter(); 8692 v8::TryCatch try_catch; 8693 CompileRun(source_exception); 8694 CHECK(try_catch.HasCaught()); 8695 v8::Handle<v8::Message> message = try_catch.Message(); 8696 CHECK(!message.IsEmpty()); 8697 CHECK_EQ(1, message->GetLineNumber()); 8698 context->Exit(); 8699 } 8700 context.Dispose(); 8701 for (gc_count = 1; gc_count < 10; gc_count++) { 8702 other_context->Enter(); 8703 CompileRun(source_exception); 8704 other_context->Exit(); 8705 v8::internal::Heap::CollectAllGarbage(false); 8706 if (GetGlobalObjectsCount() == 1) break; 8707 } 8708 CHECK_GE(2, gc_count); 8709 CHECK_EQ(1, GetGlobalObjectsCount()); 8710 8711 other_context.Dispose(); 8712} 8713