1 // Copyright 2012 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 <climits> 29 #include <csignal> 30 #include <map> 31 #include <string> 32 33 #include "src/v8.h" 34 35 #if V8_OS_POSIX 36 #include <unistd.h> // NOLINT 37 #endif 38 39 #include "include/v8-util.h" 40 #include "src/api.h" 41 #include "src/arguments.h" 42 #include "src/base/platform/platform.h" 43 #include "src/compilation-cache.h" 44 #include "src/cpu-profiler.h" 45 #include "src/execution.h" 46 #include "src/isolate.h" 47 #include "src/objects.h" 48 #include "src/parser.h" 49 #include "src/snapshot.h" 50 #include "src/unicode-inl.h" 51 #include "src/utils.h" 52 #include "src/vm-state.h" 53 #include "test/cctest/cctest.h" 54 55 static const bool kLogThreading = false; 56 57 using ::v8::Boolean; 58 using ::v8::BooleanObject; 59 using ::v8::Context; 60 using ::v8::Extension; 61 using ::v8::Function; 62 using ::v8::FunctionTemplate; 63 using ::v8::Handle; 64 using ::v8::HandleScope; 65 using ::v8::Local; 66 using ::v8::Name; 67 using ::v8::Message; 68 using ::v8::MessageCallback; 69 using ::v8::Object; 70 using ::v8::ObjectTemplate; 71 using ::v8::Persistent; 72 using ::v8::Script; 73 using ::v8::StackTrace; 74 using ::v8::String; 75 using ::v8::Symbol; 76 using ::v8::TryCatch; 77 using ::v8::Undefined; 78 using ::v8::UniqueId; 79 using ::v8::V8; 80 using ::v8::Value; 81 82 83 #define THREADED_PROFILED_TEST(Name) \ 84 static void Test##Name(); \ 85 TEST(Name##WithProfiler) { \ 86 RunWithProfiler(&Test##Name); \ 87 } \ 88 THREADED_TEST(Name) 89 90 91 void RunWithProfiler(void (*test)()) { 92 LocalContext env; 93 v8::HandleScope scope(env->GetIsolate()); 94 v8::Local<v8::String> profile_name = 95 v8::String::NewFromUtf8(env->GetIsolate(), "my_profile1"); 96 v8::CpuProfiler* cpu_profiler = env->GetIsolate()->GetCpuProfiler(); 97 98 cpu_profiler->StartProfiling(profile_name); 99 (*test)(); 100 reinterpret_cast<i::CpuProfiler*>(cpu_profiler)->DeleteAllProfiles(); 101 } 102 103 104 static int signature_callback_count; 105 static Local<Value> signature_expected_receiver; 106 static void IncrementingSignatureCallback( 107 const v8::FunctionCallbackInfo<v8::Value>& args) { 108 ApiTestFuzzer::Fuzz(); 109 signature_callback_count++; 110 CHECK_EQ(signature_expected_receiver, args.Holder()); 111 CHECK_EQ(signature_expected_receiver, args.This()); 112 v8::Handle<v8::Array> result = 113 v8::Array::New(args.GetIsolate(), args.Length()); 114 for (int i = 0; i < args.Length(); i++) 115 result->Set(v8::Integer::New(args.GetIsolate(), i), args[i]); 116 args.GetReturnValue().Set(result); 117 } 118 119 120 static void SignatureCallback( 121 const v8::FunctionCallbackInfo<v8::Value>& args) { 122 ApiTestFuzzer::Fuzz(); 123 v8::Handle<v8::Array> result = 124 v8::Array::New(args.GetIsolate(), args.Length()); 125 for (int i = 0; i < args.Length(); i++) { 126 result->Set(v8::Integer::New(args.GetIsolate(), i), args[i]); 127 } 128 args.GetReturnValue().Set(result); 129 } 130 131 132 // Tests that call v8::V8::Dispose() cannot be threaded. 133 UNINITIALIZED_TEST(InitializeAndDisposeOnce) { 134 CHECK(v8::V8::Initialize()); 135 CHECK(v8::V8::Dispose()); 136 } 137 138 139 // Tests that call v8::V8::Dispose() cannot be threaded. 140 UNINITIALIZED_TEST(InitializeAndDisposeMultiple) { 141 for (int i = 0; i < 3; ++i) CHECK(v8::V8::Dispose()); 142 for (int i = 0; i < 3; ++i) CHECK(v8::V8::Initialize()); 143 for (int i = 0; i < 3; ++i) CHECK(v8::V8::Dispose()); 144 for (int i = 0; i < 3; ++i) CHECK(v8::V8::Initialize()); 145 for (int i = 0; i < 3; ++i) CHECK(v8::V8::Dispose()); 146 } 147 148 149 THREADED_TEST(Handles) { 150 v8::HandleScope scope(CcTest::isolate()); 151 Local<Context> local_env; 152 { 153 LocalContext env; 154 local_env = env.local(); 155 } 156 157 // Local context should still be live. 158 CHECK(!local_env.IsEmpty()); 159 local_env->Enter(); 160 161 v8::Handle<v8::Primitive> undef = v8::Undefined(CcTest::isolate()); 162 CHECK(!undef.IsEmpty()); 163 CHECK(undef->IsUndefined()); 164 165 const char* source = "1 + 2 + 3"; 166 Local<Script> script = v8_compile(source); 167 CHECK_EQ(6, script->Run()->Int32Value()); 168 169 local_env->Exit(); 170 } 171 172 173 THREADED_TEST(IsolateOfContext) { 174 v8::HandleScope scope(CcTest::isolate()); 175 v8::Handle<Context> env = Context::New(CcTest::isolate()); 176 177 CHECK(!env->GetIsolate()->InContext()); 178 CHECK(env->GetIsolate() == CcTest::isolate()); 179 env->Enter(); 180 CHECK(env->GetIsolate()->InContext()); 181 CHECK(env->GetIsolate() == CcTest::isolate()); 182 env->Exit(); 183 CHECK(!env->GetIsolate()->InContext()); 184 CHECK(env->GetIsolate() == CcTest::isolate()); 185 } 186 187 188 static void TestSignature(const char* loop_js, Local<Value> receiver) { 189 i::ScopedVector<char> source(200); 190 i::SNPrintF(source, 191 "for (var i = 0; i < 10; i++) {" 192 " %s" 193 "}", 194 loop_js); 195 signature_callback_count = 0; 196 signature_expected_receiver = receiver; 197 bool expected_to_throw = receiver.IsEmpty(); 198 v8::TryCatch try_catch; 199 CompileRun(source.start()); 200 CHECK_EQ(expected_to_throw, try_catch.HasCaught()); 201 if (!expected_to_throw) { 202 CHECK_EQ(10, signature_callback_count); 203 } else { 204 CHECK_EQ(v8_str("TypeError: Illegal invocation"), 205 try_catch.Exception()->ToString()); 206 } 207 } 208 209 210 THREADED_TEST(ReceiverSignature) { 211 LocalContext env; 212 v8::Isolate* isolate = env->GetIsolate(); 213 v8::HandleScope scope(isolate); 214 // Setup templates. 215 v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate); 216 v8::Handle<v8::Signature> sig = v8::Signature::New(isolate, fun); 217 v8::Handle<v8::FunctionTemplate> callback_sig = 218 v8::FunctionTemplate::New( 219 isolate, IncrementingSignatureCallback, Local<Value>(), sig); 220 v8::Handle<v8::FunctionTemplate> callback = 221 v8::FunctionTemplate::New(isolate, IncrementingSignatureCallback); 222 v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New(isolate); 223 sub_fun->Inherit(fun); 224 v8::Handle<v8::FunctionTemplate> unrel_fun = 225 v8::FunctionTemplate::New(isolate); 226 // Install properties. 227 v8::Handle<v8::ObjectTemplate> fun_proto = fun->PrototypeTemplate(); 228 fun_proto->Set(v8_str("prop_sig"), callback_sig); 229 fun_proto->Set(v8_str("prop"), callback); 230 fun_proto->SetAccessorProperty( 231 v8_str("accessor_sig"), callback_sig, callback_sig); 232 fun_proto->SetAccessorProperty(v8_str("accessor"), callback, callback); 233 // Instantiate templates. 234 Local<Value> fun_instance = fun->InstanceTemplate()->NewInstance(); 235 Local<Value> sub_fun_instance = sub_fun->InstanceTemplate()->NewInstance(); 236 // Setup global variables. 237 env->Global()->Set(v8_str("Fun"), fun->GetFunction()); 238 env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction()); 239 env->Global()->Set(v8_str("fun_instance"), fun_instance); 240 env->Global()->Set(v8_str("sub_fun_instance"), sub_fun_instance); 241 CompileRun( 242 "var accessor_sig_key = 'accessor_sig';" 243 "var accessor_key = 'accessor';" 244 "var prop_sig_key = 'prop_sig';" 245 "var prop_key = 'prop';" 246 "" 247 "function copy_props(obj) {" 248 " var keys = [accessor_sig_key, accessor_key, prop_sig_key, prop_key];" 249 " var source = Fun.prototype;" 250 " for (var i in keys) {" 251 " var key = keys[i];" 252 " var desc = Object.getOwnPropertyDescriptor(source, key);" 253 " Object.defineProperty(obj, key, desc);" 254 " }" 255 "}" 256 "" 257 "var obj = {};" 258 "copy_props(obj);" 259 "var unrel = new UnrelFun();" 260 "copy_props(unrel);"); 261 // Test with and without ICs 262 const char* test_objects[] = { 263 "fun_instance", "sub_fun_instance", "obj", "unrel" }; 264 unsigned bad_signature_start_offset = 2; 265 for (unsigned i = 0; i < arraysize(test_objects); i++) { 266 i::ScopedVector<char> source(200); 267 i::SNPrintF( 268 source, "var test_object = %s; test_object", test_objects[i]); 269 Local<Value> test_object = CompileRun(source.start()); 270 TestSignature("test_object.prop();", test_object); 271 TestSignature("test_object.accessor;", test_object); 272 TestSignature("test_object[accessor_key];", test_object); 273 TestSignature("test_object.accessor = 1;", test_object); 274 TestSignature("test_object[accessor_key] = 1;", test_object); 275 if (i >= bad_signature_start_offset) test_object = Local<Value>(); 276 TestSignature("test_object.prop_sig();", test_object); 277 TestSignature("test_object.accessor_sig;", test_object); 278 TestSignature("test_object[accessor_sig_key];", test_object); 279 TestSignature("test_object.accessor_sig = 1;", test_object); 280 TestSignature("test_object[accessor_sig_key] = 1;", test_object); 281 } 282 } 283 284 285 THREADED_TEST(ArgumentSignature) { 286 LocalContext env; 287 v8::Isolate* isolate = env->GetIsolate(); 288 v8::HandleScope scope(isolate); 289 v8::Handle<v8::FunctionTemplate> cons = v8::FunctionTemplate::New(isolate); 290 cons->SetClassName(v8_str("Cons")); 291 v8::Handle<v8::Signature> sig = v8::Signature::New( 292 isolate, v8::Handle<v8::FunctionTemplate>(), 1, &cons); 293 v8::Handle<v8::FunctionTemplate> fun = 294 v8::FunctionTemplate::New(isolate, 295 SignatureCallback, 296 v8::Handle<Value>(), 297 sig); 298 env->Global()->Set(v8_str("Cons"), cons->GetFunction()); 299 env->Global()->Set(v8_str("Fun1"), fun->GetFunction()); 300 301 v8::Handle<Value> value1 = CompileRun("Fun1(4) == '';"); 302 CHECK(value1->IsTrue()); 303 304 v8::Handle<Value> value2 = CompileRun("Fun1(new Cons()) == '[object Cons]';"); 305 CHECK(value2->IsTrue()); 306 307 v8::Handle<Value> value3 = CompileRun("Fun1() == '';"); 308 CHECK(value3->IsTrue()); 309 310 v8::Handle<v8::FunctionTemplate> cons1 = v8::FunctionTemplate::New(isolate); 311 cons1->SetClassName(v8_str("Cons1")); 312 v8::Handle<v8::FunctionTemplate> cons2 = v8::FunctionTemplate::New(isolate); 313 cons2->SetClassName(v8_str("Cons2")); 314 v8::Handle<v8::FunctionTemplate> cons3 = v8::FunctionTemplate::New(isolate); 315 cons3->SetClassName(v8_str("Cons3")); 316 317 v8::Handle<v8::FunctionTemplate> args[3] = { cons1, cons2, cons3 }; 318 v8::Handle<v8::Signature> wsig = v8::Signature::New( 319 isolate, v8::Handle<v8::FunctionTemplate>(), 3, args); 320 v8::Handle<v8::FunctionTemplate> fun2 = 321 v8::FunctionTemplate::New(isolate, 322 SignatureCallback, 323 v8::Handle<Value>(), 324 wsig); 325 326 env->Global()->Set(v8_str("Cons1"), cons1->GetFunction()); 327 env->Global()->Set(v8_str("Cons2"), cons2->GetFunction()); 328 env->Global()->Set(v8_str("Cons3"), cons3->GetFunction()); 329 env->Global()->Set(v8_str("Fun2"), fun2->GetFunction()); 330 v8::Handle<Value> value4 = CompileRun( 331 "Fun2(new Cons1(), new Cons2(), new Cons3()) ==" 332 "'[object Cons1],[object Cons2],[object Cons3]'"); 333 CHECK(value4->IsTrue()); 334 335 v8::Handle<Value> value5 = CompileRun( 336 "Fun2(new Cons1(), new Cons2(), 5) == '[object Cons1],[object Cons2],'"); 337 CHECK(value5->IsTrue()); 338 339 v8::Handle<Value> value6 = CompileRun( 340 "Fun2(new Cons3(), new Cons2(), new Cons1()) == ',[object Cons2],'"); 341 CHECK(value6->IsTrue()); 342 343 v8::Handle<Value> value7 = CompileRun( 344 "Fun2(new Cons1(), new Cons2(), new Cons3(), 'd') == " 345 "'[object Cons1],[object Cons2],[object Cons3],d';"); 346 CHECK(value7->IsTrue()); 347 348 v8::Handle<Value> value8 = CompileRun( 349 "Fun2(new Cons1(), new Cons2()) == '[object Cons1],[object Cons2]'"); 350 CHECK(value8->IsTrue()); 351 } 352 353 354 THREADED_TEST(HulIgennem) { 355 LocalContext env; 356 v8::Isolate* isolate = env->GetIsolate(); 357 v8::HandleScope scope(isolate); 358 v8::Handle<v8::Primitive> undef = v8::Undefined(isolate); 359 Local<String> undef_str = undef->ToString(); 360 char* value = i::NewArray<char>(undef_str->Utf8Length() + 1); 361 undef_str->WriteUtf8(value); 362 CHECK_EQ(0, strcmp(value, "undefined")); 363 i::DeleteArray(value); 364 } 365 366 367 THREADED_TEST(Access) { 368 LocalContext env; 369 v8::Isolate* isolate = env->GetIsolate(); 370 v8::HandleScope scope(isolate); 371 Local<v8::Object> obj = v8::Object::New(isolate); 372 Local<Value> foo_before = obj->Get(v8_str("foo")); 373 CHECK(foo_before->IsUndefined()); 374 Local<String> bar_str = v8_str("bar"); 375 obj->Set(v8_str("foo"), bar_str); 376 Local<Value> foo_after = obj->Get(v8_str("foo")); 377 CHECK(!foo_after->IsUndefined()); 378 CHECK(foo_after->IsString()); 379 CHECK_EQ(bar_str, foo_after); 380 } 381 382 383 THREADED_TEST(AccessElement) { 384 LocalContext env; 385 v8::HandleScope scope(env->GetIsolate()); 386 Local<v8::Object> obj = v8::Object::New(env->GetIsolate()); 387 Local<Value> before = obj->Get(1); 388 CHECK(before->IsUndefined()); 389 Local<String> bar_str = v8_str("bar"); 390 obj->Set(1, bar_str); 391 Local<Value> after = obj->Get(1); 392 CHECK(!after->IsUndefined()); 393 CHECK(after->IsString()); 394 CHECK_EQ(bar_str, after); 395 396 Local<v8::Array> value = CompileRun("[\"a\", \"b\"]").As<v8::Array>(); 397 CHECK_EQ(v8_str("a"), value->Get(0)); 398 CHECK_EQ(v8_str("b"), value->Get(1)); 399 } 400 401 402 THREADED_TEST(Script) { 403 LocalContext env; 404 v8::HandleScope scope(env->GetIsolate()); 405 const char* source = "1 + 2 + 3"; 406 Local<Script> script = v8_compile(source); 407 CHECK_EQ(6, script->Run()->Int32Value()); 408 } 409 410 411 class TestResource: public String::ExternalStringResource { 412 public: 413 explicit TestResource(uint16_t* data, int* counter = NULL, 414 bool owning_data = true) 415 : data_(data), length_(0), counter_(counter), owning_data_(owning_data) { 416 while (data[length_]) ++length_; 417 } 418 419 ~TestResource() { 420 if (owning_data_) i::DeleteArray(data_); 421 if (counter_ != NULL) ++*counter_; 422 } 423 424 const uint16_t* data() const { 425 return data_; 426 } 427 428 size_t length() const { 429 return length_; 430 } 431 432 private: 433 uint16_t* data_; 434 size_t length_; 435 int* counter_; 436 bool owning_data_; 437 }; 438 439 440 class TestOneByteResource : public String::ExternalOneByteStringResource { 441 public: 442 explicit TestOneByteResource(const char* data, int* counter = NULL, 443 size_t offset = 0) 444 : orig_data_(data), 445 data_(data + offset), 446 length_(strlen(data) - offset), 447 counter_(counter) {} 448 449 ~TestOneByteResource() { 450 i::DeleteArray(orig_data_); 451 if (counter_ != NULL) ++*counter_; 452 } 453 454 const char* data() const { 455 return data_; 456 } 457 458 size_t length() const { 459 return length_; 460 } 461 462 private: 463 const char* orig_data_; 464 const char* data_; 465 size_t length_; 466 int* counter_; 467 }; 468 469 470 THREADED_TEST(ScriptUsingStringResource) { 471 int dispose_count = 0; 472 const char* c_source = "1 + 2 * 3"; 473 uint16_t* two_byte_source = AsciiToTwoByteString(c_source); 474 { 475 LocalContext env; 476 v8::HandleScope scope(env->GetIsolate()); 477 TestResource* resource = new TestResource(two_byte_source, &dispose_count); 478 Local<String> source = String::NewExternal(env->GetIsolate(), resource); 479 Local<Script> script = v8_compile(source); 480 Local<Value> value = script->Run(); 481 CHECK(value->IsNumber()); 482 CHECK_EQ(7, value->Int32Value()); 483 CHECK(source->IsExternal()); 484 CHECK_EQ(resource, 485 static_cast<TestResource*>(source->GetExternalStringResource())); 486 String::Encoding encoding = String::UNKNOWN_ENCODING; 487 CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource), 488 source->GetExternalStringResourceBase(&encoding)); 489 CHECK_EQ(String::TWO_BYTE_ENCODING, encoding); 490 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 491 CHECK_EQ(0, dispose_count); 492 } 493 CcTest::i_isolate()->compilation_cache()->Clear(); 494 CcTest::heap()->CollectAllAvailableGarbage(); 495 CHECK_EQ(1, dispose_count); 496 } 497 498 499 THREADED_TEST(ScriptUsingOneByteStringResource) { 500 int dispose_count = 0; 501 const char* c_source = "1 + 2 * 3"; 502 { 503 LocalContext env; 504 v8::HandleScope scope(env->GetIsolate()); 505 TestOneByteResource* resource = 506 new TestOneByteResource(i::StrDup(c_source), &dispose_count); 507 Local<String> source = String::NewExternal(env->GetIsolate(), resource); 508 CHECK(source->IsExternalOneByte()); 509 CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource), 510 source->GetExternalOneByteStringResource()); 511 String::Encoding encoding = String::UNKNOWN_ENCODING; 512 CHECK_EQ(static_cast<const String::ExternalStringResourceBase*>(resource), 513 source->GetExternalStringResourceBase(&encoding)); 514 CHECK_EQ(String::ONE_BYTE_ENCODING, encoding); 515 Local<Script> script = v8_compile(source); 516 Local<Value> value = script->Run(); 517 CHECK(value->IsNumber()); 518 CHECK_EQ(7, value->Int32Value()); 519 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 520 CHECK_EQ(0, dispose_count); 521 } 522 CcTest::i_isolate()->compilation_cache()->Clear(); 523 CcTest::heap()->CollectAllAvailableGarbage(); 524 CHECK_EQ(1, dispose_count); 525 } 526 527 528 THREADED_TEST(ScriptMakingExternalString) { 529 int dispose_count = 0; 530 uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3"); 531 { 532 LocalContext env; 533 v8::HandleScope scope(env->GetIsolate()); 534 Local<String> source = 535 String::NewFromTwoByte(env->GetIsolate(), two_byte_source); 536 // Trigger GCs so that the newly allocated string moves to old gen. 537 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now 538 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now 539 CHECK_EQ(source->IsExternal(), false); 540 CHECK_EQ(source->IsExternalOneByte(), false); 541 String::Encoding encoding = String::UNKNOWN_ENCODING; 542 CHECK_EQ(NULL, source->GetExternalStringResourceBase(&encoding)); 543 CHECK_EQ(String::ONE_BYTE_ENCODING, encoding); 544 bool success = source->MakeExternal(new TestResource(two_byte_source, 545 &dispose_count)); 546 CHECK(success); 547 Local<Script> script = v8_compile(source); 548 Local<Value> value = script->Run(); 549 CHECK(value->IsNumber()); 550 CHECK_EQ(7, value->Int32Value()); 551 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 552 CHECK_EQ(0, dispose_count); 553 } 554 CcTest::i_isolate()->compilation_cache()->Clear(); 555 CcTest::heap()->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 556 CHECK_EQ(1, dispose_count); 557 } 558 559 560 THREADED_TEST(ScriptMakingExternalOneByteString) { 561 int dispose_count = 0; 562 const char* c_source = "1 + 2 * 3"; 563 { 564 LocalContext env; 565 v8::HandleScope scope(env->GetIsolate()); 566 Local<String> source = v8_str(c_source); 567 // Trigger GCs so that the newly allocated string moves to old gen. 568 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now 569 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now 570 bool success = source->MakeExternal( 571 new TestOneByteResource(i::StrDup(c_source), &dispose_count)); 572 CHECK(success); 573 Local<Script> script = v8_compile(source); 574 Local<Value> value = script->Run(); 575 CHECK(value->IsNumber()); 576 CHECK_EQ(7, value->Int32Value()); 577 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 578 CHECK_EQ(0, dispose_count); 579 } 580 CcTest::i_isolate()->compilation_cache()->Clear(); 581 CcTest::heap()->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 582 CHECK_EQ(1, dispose_count); 583 } 584 585 586 TEST(MakingExternalStringConditions) { 587 LocalContext env; 588 v8::HandleScope scope(env->GetIsolate()); 589 590 // Free some space in the new space so that we can check freshness. 591 CcTest::heap()->CollectGarbage(i::NEW_SPACE); 592 CcTest::heap()->CollectGarbage(i::NEW_SPACE); 593 594 uint16_t* two_byte_string = AsciiToTwoByteString("s1"); 595 Local<String> small_string = 596 String::NewFromTwoByte(env->GetIsolate(), two_byte_string); 597 i::DeleteArray(two_byte_string); 598 599 // We should refuse to externalize newly created small string. 600 CHECK(!small_string->CanMakeExternal()); 601 // Trigger GCs so that the newly allocated string moves to old gen. 602 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now 603 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now 604 // Old space strings should be accepted. 605 CHECK(small_string->CanMakeExternal()); 606 607 two_byte_string = AsciiToTwoByteString("small string 2"); 608 small_string = String::NewFromTwoByte(env->GetIsolate(), two_byte_string); 609 i::DeleteArray(two_byte_string); 610 611 // We should refuse externalizing newly created small string. 612 CHECK(!small_string->CanMakeExternal()); 613 for (int i = 0; i < 100; i++) { 614 String::Value value(small_string); 615 } 616 // Frequently used strings should be accepted. 617 CHECK(small_string->CanMakeExternal()); 618 619 const int buf_size = 10 * 1024; 620 char* buf = i::NewArray<char>(buf_size); 621 memset(buf, 'a', buf_size); 622 buf[buf_size - 1] = '\0'; 623 624 two_byte_string = AsciiToTwoByteString(buf); 625 Local<String> large_string = 626 String::NewFromTwoByte(env->GetIsolate(), two_byte_string); 627 i::DeleteArray(buf); 628 i::DeleteArray(two_byte_string); 629 // Large strings should be immediately accepted. 630 CHECK(large_string->CanMakeExternal()); 631 } 632 633 634 TEST(MakingExternalOneByteStringConditions) { 635 LocalContext env; 636 v8::HandleScope scope(env->GetIsolate()); 637 638 // Free some space in the new space so that we can check freshness. 639 CcTest::heap()->CollectGarbage(i::NEW_SPACE); 640 CcTest::heap()->CollectGarbage(i::NEW_SPACE); 641 642 Local<String> small_string = String::NewFromUtf8(env->GetIsolate(), "s1"); 643 // We should refuse to externalize newly created small string. 644 CHECK(!small_string->CanMakeExternal()); 645 // Trigger GCs so that the newly allocated string moves to old gen. 646 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now 647 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now 648 // Old space strings should be accepted. 649 CHECK(small_string->CanMakeExternal()); 650 651 small_string = String::NewFromUtf8(env->GetIsolate(), "small string 2"); 652 // We should refuse externalizing newly created small string. 653 CHECK(!small_string->CanMakeExternal()); 654 for (int i = 0; i < 100; i++) { 655 String::Value value(small_string); 656 } 657 // Frequently used strings should be accepted. 658 CHECK(small_string->CanMakeExternal()); 659 660 const int buf_size = 10 * 1024; 661 char* buf = i::NewArray<char>(buf_size); 662 memset(buf, 'a', buf_size); 663 buf[buf_size - 1] = '\0'; 664 Local<String> large_string = String::NewFromUtf8(env->GetIsolate(), buf); 665 i::DeleteArray(buf); 666 // Large strings should be immediately accepted. 667 CHECK(large_string->CanMakeExternal()); 668 } 669 670 671 TEST(MakingExternalUnalignedOneByteString) { 672 LocalContext env; 673 v8::HandleScope scope(env->GetIsolate()); 674 675 CompileRun("function cons(a, b) { return a + b; }" 676 "function slice(a) { return a.substring(1); }"); 677 // Create a cons string that will land in old pointer space. 678 Local<String> cons = Local<String>::Cast(CompileRun( 679 "cons('abcdefghijklm', 'nopqrstuvwxyz');")); 680 // Create a sliced string that will land in old pointer space. 681 Local<String> slice = Local<String>::Cast(CompileRun( 682 "slice('abcdefghijklmnopqrstuvwxyz');")); 683 684 // Trigger GCs so that the newly allocated string moves to old gen. 685 SimulateFullSpace(CcTest::heap()->old_pointer_space()); 686 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now 687 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now 688 689 // Turn into external string with unaligned resource data. 690 const char* c_cons = "_abcdefghijklmnopqrstuvwxyz"; 691 bool success = 692 cons->MakeExternal(new TestOneByteResource(i::StrDup(c_cons), NULL, 1)); 693 CHECK(success); 694 const char* c_slice = "_bcdefghijklmnopqrstuvwxyz"; 695 success = 696 slice->MakeExternal(new TestOneByteResource(i::StrDup(c_slice), NULL, 1)); 697 CHECK(success); 698 699 // Trigger GCs and force evacuation. 700 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 701 CcTest::heap()->CollectAllGarbage(i::Heap::kReduceMemoryFootprintMask); 702 } 703 704 705 THREADED_TEST(UsingExternalString) { 706 i::Factory* factory = CcTest::i_isolate()->factory(); 707 { 708 v8::HandleScope scope(CcTest::isolate()); 709 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 710 Local<String> string = String::NewExternal( 711 CcTest::isolate(), new TestResource(two_byte_string)); 712 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 713 // Trigger GCs so that the newly allocated string moves to old gen. 714 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now 715 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now 716 i::Handle<i::String> isymbol = 717 factory->InternalizeString(istring); 718 CHECK(isymbol->IsInternalizedString()); 719 } 720 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 721 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 722 } 723 724 725 THREADED_TEST(UsingExternalOneByteString) { 726 i::Factory* factory = CcTest::i_isolate()->factory(); 727 { 728 v8::HandleScope scope(CcTest::isolate()); 729 const char* one_byte_string = "test string"; 730 Local<String> string = String::NewExternal( 731 CcTest::isolate(), new TestOneByteResource(i::StrDup(one_byte_string))); 732 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 733 // Trigger GCs so that the newly allocated string moves to old gen. 734 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in survivor space now 735 CcTest::heap()->CollectGarbage(i::NEW_SPACE); // in old gen now 736 i::Handle<i::String> isymbol = 737 factory->InternalizeString(istring); 738 CHECK(isymbol->IsInternalizedString()); 739 } 740 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 741 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 742 } 743 744 745 THREADED_TEST(ScavengeExternalString) { 746 i::FLAG_stress_compaction = false; 747 i::FLAG_gc_global = false; 748 int dispose_count = 0; 749 bool in_new_space = false; 750 { 751 v8::HandleScope scope(CcTest::isolate()); 752 uint16_t* two_byte_string = AsciiToTwoByteString("test string"); 753 Local<String> string = String::NewExternal( 754 CcTest::isolate(), new TestResource(two_byte_string, &dispose_count)); 755 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 756 CcTest::heap()->CollectGarbage(i::NEW_SPACE); 757 in_new_space = CcTest::heap()->InNewSpace(*istring); 758 CHECK(in_new_space || CcTest::heap()->old_data_space()->Contains(*istring)); 759 CHECK_EQ(0, dispose_count); 760 } 761 CcTest::heap()->CollectGarbage( 762 in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); 763 CHECK_EQ(1, dispose_count); 764 } 765 766 767 THREADED_TEST(ScavengeExternalOneByteString) { 768 i::FLAG_stress_compaction = false; 769 i::FLAG_gc_global = false; 770 int dispose_count = 0; 771 bool in_new_space = false; 772 { 773 v8::HandleScope scope(CcTest::isolate()); 774 const char* one_byte_string = "test string"; 775 Local<String> string = String::NewExternal( 776 CcTest::isolate(), 777 new TestOneByteResource(i::StrDup(one_byte_string), &dispose_count)); 778 i::Handle<i::String> istring = v8::Utils::OpenHandle(*string); 779 CcTest::heap()->CollectGarbage(i::NEW_SPACE); 780 in_new_space = CcTest::heap()->InNewSpace(*istring); 781 CHECK(in_new_space || CcTest::heap()->old_data_space()->Contains(*istring)); 782 CHECK_EQ(0, dispose_count); 783 } 784 CcTest::heap()->CollectGarbage( 785 in_new_space ? i::NEW_SPACE : i::OLD_DATA_SPACE); 786 CHECK_EQ(1, dispose_count); 787 } 788 789 790 class TestOneByteResourceWithDisposeControl : public TestOneByteResource { 791 public: 792 // Only used by non-threaded tests, so it can use static fields. 793 static int dispose_calls; 794 static int dispose_count; 795 796 TestOneByteResourceWithDisposeControl(const char* data, bool dispose) 797 : TestOneByteResource(data, &dispose_count), dispose_(dispose) {} 798 799 void Dispose() { 800 ++dispose_calls; 801 if (dispose_) delete this; 802 } 803 private: 804 bool dispose_; 805 }; 806 807 808 int TestOneByteResourceWithDisposeControl::dispose_count = 0; 809 int TestOneByteResourceWithDisposeControl::dispose_calls = 0; 810 811 812 TEST(ExternalStringWithDisposeHandling) { 813 const char* c_source = "1 + 2 * 3"; 814 815 // Use a stack allocated external string resource allocated object. 816 TestOneByteResourceWithDisposeControl::dispose_count = 0; 817 TestOneByteResourceWithDisposeControl::dispose_calls = 0; 818 TestOneByteResourceWithDisposeControl res_stack(i::StrDup(c_source), false); 819 { 820 LocalContext env; 821 v8::HandleScope scope(env->GetIsolate()); 822 Local<String> source = String::NewExternal(env->GetIsolate(), &res_stack); 823 Local<Script> script = v8_compile(source); 824 Local<Value> value = script->Run(); 825 CHECK(value->IsNumber()); 826 CHECK_EQ(7, value->Int32Value()); 827 CcTest::heap()->CollectAllAvailableGarbage(); 828 CHECK_EQ(0, TestOneByteResourceWithDisposeControl::dispose_count); 829 } 830 CcTest::i_isolate()->compilation_cache()->Clear(); 831 CcTest::heap()->CollectAllAvailableGarbage(); 832 CHECK_EQ(1, TestOneByteResourceWithDisposeControl::dispose_calls); 833 CHECK_EQ(0, TestOneByteResourceWithDisposeControl::dispose_count); 834 835 // Use a heap allocated external string resource allocated object. 836 TestOneByteResourceWithDisposeControl::dispose_count = 0; 837 TestOneByteResourceWithDisposeControl::dispose_calls = 0; 838 TestOneByteResource* res_heap = 839 new TestOneByteResourceWithDisposeControl(i::StrDup(c_source), true); 840 { 841 LocalContext env; 842 v8::HandleScope scope(env->GetIsolate()); 843 Local<String> source = String::NewExternal(env->GetIsolate(), res_heap); 844 Local<Script> script = v8_compile(source); 845 Local<Value> value = script->Run(); 846 CHECK(value->IsNumber()); 847 CHECK_EQ(7, value->Int32Value()); 848 CcTest::heap()->CollectAllAvailableGarbage(); 849 CHECK_EQ(0, TestOneByteResourceWithDisposeControl::dispose_count); 850 } 851 CcTest::i_isolate()->compilation_cache()->Clear(); 852 CcTest::heap()->CollectAllAvailableGarbage(); 853 CHECK_EQ(1, TestOneByteResourceWithDisposeControl::dispose_calls); 854 CHECK_EQ(1, TestOneByteResourceWithDisposeControl::dispose_count); 855 } 856 857 858 THREADED_TEST(StringConcat) { 859 { 860 LocalContext env; 861 v8::HandleScope scope(env->GetIsolate()); 862 const char* one_byte_string_1 = "function a_times_t"; 863 const char* two_byte_string_1 = "wo_plus_b(a, b) {return "; 864 const char* one_byte_extern_1 = "a * 2 + b;} a_times_two_plus_b(4, 8) + "; 865 const char* two_byte_extern_1 = "a_times_two_plus_b(4, 8) + "; 866 const char* one_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 867 const char* two_byte_string_2 = "a_times_two_plus_b(4, 8) + "; 868 const char* two_byte_extern_2 = "a_times_two_plus_b(1, 2);"; 869 Local<String> left = v8_str(one_byte_string_1); 870 871 uint16_t* two_byte_source = AsciiToTwoByteString(two_byte_string_1); 872 Local<String> right = 873 String::NewFromTwoByte(env->GetIsolate(), two_byte_source); 874 i::DeleteArray(two_byte_source); 875 876 Local<String> source = String::Concat(left, right); 877 right = String::NewExternal( 878 env->GetIsolate(), 879 new TestOneByteResource(i::StrDup(one_byte_extern_1))); 880 source = String::Concat(source, right); 881 right = String::NewExternal( 882 env->GetIsolate(), 883 new TestResource(AsciiToTwoByteString(two_byte_extern_1))); 884 source = String::Concat(source, right); 885 right = v8_str(one_byte_string_2); 886 source = String::Concat(source, right); 887 888 two_byte_source = AsciiToTwoByteString(two_byte_string_2); 889 right = String::NewFromTwoByte(env->GetIsolate(), two_byte_source); 890 i::DeleteArray(two_byte_source); 891 892 source = String::Concat(source, right); 893 right = String::NewExternal( 894 env->GetIsolate(), 895 new TestResource(AsciiToTwoByteString(two_byte_extern_2))); 896 source = String::Concat(source, right); 897 Local<Script> script = v8_compile(source); 898 Local<Value> value = script->Run(); 899 CHECK(value->IsNumber()); 900 CHECK_EQ(68, value->Int32Value()); 901 } 902 CcTest::i_isolate()->compilation_cache()->Clear(); 903 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 904 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 905 } 906 907 908 THREADED_TEST(GlobalProperties) { 909 LocalContext env; 910 v8::HandleScope scope(env->GetIsolate()); 911 v8::Handle<v8::Object> global = env->Global(); 912 global->Set(v8_str("pi"), v8_num(3.1415926)); 913 Local<Value> pi = global->Get(v8_str("pi")); 914 CHECK_EQ(3.1415926, pi->NumberValue()); 915 } 916 917 918 template<typename T> 919 static void CheckReturnValue(const T& t, i::Address callback) { 920 v8::ReturnValue<v8::Value> rv = t.GetReturnValue(); 921 i::Object** o = *reinterpret_cast<i::Object***>(&rv); 922 CHECK_EQ(CcTest::isolate(), t.GetIsolate()); 923 CHECK_EQ(t.GetIsolate(), rv.GetIsolate()); 924 CHECK((*o)->IsTheHole() || (*o)->IsUndefined()); 925 // Verify reset 926 bool is_runtime = (*o)->IsTheHole(); 927 rv.Set(true); 928 CHECK(!(*o)->IsTheHole() && !(*o)->IsUndefined()); 929 rv.Set(v8::Handle<v8::Object>()); 930 CHECK((*o)->IsTheHole() || (*o)->IsUndefined()); 931 CHECK_EQ(is_runtime, (*o)->IsTheHole()); 932 933 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(t.GetIsolate()); 934 // If CPU profiler is active check that when API callback is invoked 935 // VMState is set to EXTERNAL. 936 if (isolate->cpu_profiler()->is_profiling()) { 937 CHECK_EQ(i::EXTERNAL, isolate->current_vm_state()); 938 CHECK(isolate->external_callback_scope()); 939 CHECK_EQ(callback, isolate->external_callback_scope()->callback()); 940 } 941 } 942 943 944 static void handle_callback_impl(const v8::FunctionCallbackInfo<Value>& info, 945 i::Address callback) { 946 ApiTestFuzzer::Fuzz(); 947 CheckReturnValue(info, callback); 948 info.GetReturnValue().Set(v8_str("bad value")); 949 info.GetReturnValue().Set(v8_num(102)); 950 } 951 952 953 static void handle_callback(const v8::FunctionCallbackInfo<Value>& info) { 954 return handle_callback_impl(info, FUNCTION_ADDR(handle_callback)); 955 } 956 957 958 static void handle_callback_2(const v8::FunctionCallbackInfo<Value>& info) { 959 return handle_callback_impl(info, FUNCTION_ADDR(handle_callback_2)); 960 } 961 962 static void construct_callback( 963 const v8::FunctionCallbackInfo<Value>& info) { 964 ApiTestFuzzer::Fuzz(); 965 CheckReturnValue(info, FUNCTION_ADDR(construct_callback)); 966 info.This()->Set(v8_str("x"), v8_num(1)); 967 info.This()->Set(v8_str("y"), v8_num(2)); 968 info.GetReturnValue().Set(v8_str("bad value")); 969 info.GetReturnValue().Set(info.This()); 970 } 971 972 973 static void Return239Callback( 974 Local<String> name, const v8::PropertyCallbackInfo<Value>& info) { 975 ApiTestFuzzer::Fuzz(); 976 CheckReturnValue(info, FUNCTION_ADDR(Return239Callback)); 977 info.GetReturnValue().Set(v8_str("bad value")); 978 info.GetReturnValue().Set(v8_num(239)); 979 } 980 981 982 template<typename Handler> 983 static void TestFunctionTemplateInitializer(Handler handler, 984 Handler handler_2) { 985 // Test constructor calls. 986 { 987 LocalContext env; 988 v8::Isolate* isolate = env->GetIsolate(); 989 v8::HandleScope scope(isolate); 990 991 Local<v8::FunctionTemplate> fun_templ = 992 v8::FunctionTemplate::New(isolate, handler); 993 Local<Function> fun = fun_templ->GetFunction(); 994 env->Global()->Set(v8_str("obj"), fun); 995 Local<Script> script = v8_compile("obj()"); 996 for (int i = 0; i < 30; i++) { 997 CHECK_EQ(102, script->Run()->Int32Value()); 998 } 999 } 1000 // Use SetCallHandler to initialize a function template, should work like 1001 // the previous one. 1002 { 1003 LocalContext env; 1004 v8::Isolate* isolate = env->GetIsolate(); 1005 v8::HandleScope scope(isolate); 1006 1007 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate); 1008 fun_templ->SetCallHandler(handler_2); 1009 Local<Function> fun = fun_templ->GetFunction(); 1010 env->Global()->Set(v8_str("obj"), fun); 1011 Local<Script> script = v8_compile("obj()"); 1012 for (int i = 0; i < 30; i++) { 1013 CHECK_EQ(102, script->Run()->Int32Value()); 1014 } 1015 } 1016 } 1017 1018 1019 template<typename Constructor, typename Accessor> 1020 static void TestFunctionTemplateAccessor(Constructor constructor, 1021 Accessor accessor) { 1022 LocalContext env; 1023 v8::HandleScope scope(env->GetIsolate()); 1024 1025 Local<v8::FunctionTemplate> fun_templ = 1026 v8::FunctionTemplate::New(env->GetIsolate(), constructor); 1027 fun_templ->SetClassName(v8_str("funky")); 1028 fun_templ->InstanceTemplate()->SetAccessor(v8_str("m"), accessor); 1029 Local<Function> fun = fun_templ->GetFunction(); 1030 env->Global()->Set(v8_str("obj"), fun); 1031 Local<Value> result = v8_compile("(new obj()).toString()")->Run(); 1032 CHECK_EQ(v8_str("[object funky]"), result); 1033 CompileRun("var obj_instance = new obj();"); 1034 Local<Script> script; 1035 script = v8_compile("obj_instance.x"); 1036 for (int i = 0; i < 30; i++) { 1037 CHECK_EQ(1, script->Run()->Int32Value()); 1038 } 1039 script = v8_compile("obj_instance.m"); 1040 for (int i = 0; i < 30; i++) { 1041 CHECK_EQ(239, script->Run()->Int32Value()); 1042 } 1043 } 1044 1045 1046 THREADED_PROFILED_TEST(FunctionTemplate) { 1047 TestFunctionTemplateInitializer(handle_callback, handle_callback_2); 1048 TestFunctionTemplateAccessor(construct_callback, Return239Callback); 1049 } 1050 1051 1052 static void SimpleCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { 1053 ApiTestFuzzer::Fuzz(); 1054 CheckReturnValue(info, FUNCTION_ADDR(SimpleCallback)); 1055 info.GetReturnValue().Set(v8_num(51423 + info.Length())); 1056 } 1057 1058 1059 template<typename Callback> 1060 static void TestSimpleCallback(Callback callback) { 1061 LocalContext env; 1062 v8::Isolate* isolate = env->GetIsolate(); 1063 v8::HandleScope scope(isolate); 1064 1065 v8::Handle<v8::ObjectTemplate> object_template = 1066 v8::ObjectTemplate::New(isolate); 1067 object_template->Set(isolate, "callback", 1068 v8::FunctionTemplate::New(isolate, callback)); 1069 v8::Local<v8::Object> object = object_template->NewInstance(); 1070 (*env)->Global()->Set(v8_str("callback_object"), object); 1071 v8::Handle<v8::Script> script; 1072 script = v8_compile("callback_object.callback(17)"); 1073 for (int i = 0; i < 30; i++) { 1074 CHECK_EQ(51424, script->Run()->Int32Value()); 1075 } 1076 script = v8_compile("callback_object.callback(17, 24)"); 1077 for (int i = 0; i < 30; i++) { 1078 CHECK_EQ(51425, script->Run()->Int32Value()); 1079 } 1080 } 1081 1082 1083 THREADED_PROFILED_TEST(SimpleCallback) { 1084 TestSimpleCallback(SimpleCallback); 1085 } 1086 1087 1088 template<typename T> 1089 void FastReturnValueCallback(const v8::FunctionCallbackInfo<v8::Value>& info); 1090 1091 // constant return values 1092 static int32_t fast_return_value_int32 = 471; 1093 static uint32_t fast_return_value_uint32 = 571; 1094 static const double kFastReturnValueDouble = 2.7; 1095 // variable return values 1096 static bool fast_return_value_bool = false; 1097 enum ReturnValueOddball { 1098 kNullReturnValue, 1099 kUndefinedReturnValue, 1100 kEmptyStringReturnValue 1101 }; 1102 static ReturnValueOddball fast_return_value_void; 1103 static bool fast_return_value_object_is_empty = false; 1104 1105 // Helper function to avoid compiler error: insufficient contextual information 1106 // to determine type when applying FUNCTION_ADDR to a template function. 1107 static i::Address address_of(v8::FunctionCallback callback) { 1108 return FUNCTION_ADDR(callback); 1109 } 1110 1111 template<> 1112 void FastReturnValueCallback<int32_t>( 1113 const v8::FunctionCallbackInfo<v8::Value>& info) { 1114 CheckReturnValue(info, address_of(FastReturnValueCallback<int32_t>)); 1115 info.GetReturnValue().Set(fast_return_value_int32); 1116 } 1117 1118 template<> 1119 void FastReturnValueCallback<uint32_t>( 1120 const v8::FunctionCallbackInfo<v8::Value>& info) { 1121 CheckReturnValue(info, address_of(FastReturnValueCallback<uint32_t>)); 1122 info.GetReturnValue().Set(fast_return_value_uint32); 1123 } 1124 1125 template<> 1126 void FastReturnValueCallback<double>( 1127 const v8::FunctionCallbackInfo<v8::Value>& info) { 1128 CheckReturnValue(info, address_of(FastReturnValueCallback<double>)); 1129 info.GetReturnValue().Set(kFastReturnValueDouble); 1130 } 1131 1132 template<> 1133 void FastReturnValueCallback<bool>( 1134 const v8::FunctionCallbackInfo<v8::Value>& info) { 1135 CheckReturnValue(info, address_of(FastReturnValueCallback<bool>)); 1136 info.GetReturnValue().Set(fast_return_value_bool); 1137 } 1138 1139 template<> 1140 void FastReturnValueCallback<void>( 1141 const v8::FunctionCallbackInfo<v8::Value>& info) { 1142 CheckReturnValue(info, address_of(FastReturnValueCallback<void>)); 1143 switch (fast_return_value_void) { 1144 case kNullReturnValue: 1145 info.GetReturnValue().SetNull(); 1146 break; 1147 case kUndefinedReturnValue: 1148 info.GetReturnValue().SetUndefined(); 1149 break; 1150 case kEmptyStringReturnValue: 1151 info.GetReturnValue().SetEmptyString(); 1152 break; 1153 } 1154 } 1155 1156 template<> 1157 void FastReturnValueCallback<Object>( 1158 const v8::FunctionCallbackInfo<v8::Value>& info) { 1159 v8::Handle<v8::Object> object; 1160 if (!fast_return_value_object_is_empty) { 1161 object = Object::New(info.GetIsolate()); 1162 } 1163 info.GetReturnValue().Set(object); 1164 } 1165 1166 template<typename T> 1167 Handle<Value> TestFastReturnValues() { 1168 LocalContext env; 1169 v8::Isolate* isolate = env->GetIsolate(); 1170 v8::EscapableHandleScope scope(isolate); 1171 v8::Handle<v8::ObjectTemplate> object_template = 1172 v8::ObjectTemplate::New(isolate); 1173 v8::FunctionCallback callback = &FastReturnValueCallback<T>; 1174 object_template->Set(isolate, "callback", 1175 v8::FunctionTemplate::New(isolate, callback)); 1176 v8::Local<v8::Object> object = object_template->NewInstance(); 1177 (*env)->Global()->Set(v8_str("callback_object"), object); 1178 return scope.Escape(CompileRun("callback_object.callback()")); 1179 } 1180 1181 1182 THREADED_PROFILED_TEST(FastReturnValues) { 1183 LocalContext env; 1184 v8::HandleScope scope(CcTest::isolate()); 1185 v8::Handle<v8::Value> value; 1186 // check int32_t and uint32_t 1187 int32_t int_values[] = { 1188 0, 234, -723, 1189 i::Smi::kMinValue, i::Smi::kMaxValue 1190 }; 1191 for (size_t i = 0; i < arraysize(int_values); i++) { 1192 for (int modifier = -1; modifier <= 1; modifier++) { 1193 int int_value = int_values[i] + modifier; 1194 // check int32_t 1195 fast_return_value_int32 = int_value; 1196 value = TestFastReturnValues<int32_t>(); 1197 CHECK(value->IsInt32()); 1198 CHECK(fast_return_value_int32 == value->Int32Value()); 1199 // check uint32_t 1200 fast_return_value_uint32 = static_cast<uint32_t>(int_value); 1201 value = TestFastReturnValues<uint32_t>(); 1202 CHECK(value->IsUint32()); 1203 CHECK(fast_return_value_uint32 == value->Uint32Value()); 1204 } 1205 } 1206 // check double 1207 value = TestFastReturnValues<double>(); 1208 CHECK(value->IsNumber()); 1209 CHECK_EQ(kFastReturnValueDouble, value->ToNumber()->Value()); 1210 // check bool values 1211 for (int i = 0; i < 2; i++) { 1212 fast_return_value_bool = i == 0; 1213 value = TestFastReturnValues<bool>(); 1214 CHECK(value->IsBoolean()); 1215 CHECK_EQ(fast_return_value_bool, value->ToBoolean()->Value()); 1216 } 1217 // check oddballs 1218 ReturnValueOddball oddballs[] = { 1219 kNullReturnValue, 1220 kUndefinedReturnValue, 1221 kEmptyStringReturnValue 1222 }; 1223 for (size_t i = 0; i < arraysize(oddballs); i++) { 1224 fast_return_value_void = oddballs[i]; 1225 value = TestFastReturnValues<void>(); 1226 switch (fast_return_value_void) { 1227 case kNullReturnValue: 1228 CHECK(value->IsNull()); 1229 break; 1230 case kUndefinedReturnValue: 1231 CHECK(value->IsUndefined()); 1232 break; 1233 case kEmptyStringReturnValue: 1234 CHECK(value->IsString()); 1235 CHECK_EQ(0, v8::String::Cast(*value)->Length()); 1236 break; 1237 } 1238 } 1239 // check handles 1240 fast_return_value_object_is_empty = false; 1241 value = TestFastReturnValues<Object>(); 1242 CHECK(value->IsObject()); 1243 fast_return_value_object_is_empty = true; 1244 value = TestFastReturnValues<Object>(); 1245 CHECK(value->IsUndefined()); 1246 } 1247 1248 1249 THREADED_TEST(FunctionTemplateSetLength) { 1250 LocalContext env; 1251 v8::Isolate* isolate = env->GetIsolate(); 1252 v8::HandleScope scope(isolate); 1253 { 1254 Local<v8::FunctionTemplate> fun_templ = 1255 v8::FunctionTemplate::New(isolate, 1256 handle_callback, 1257 Handle<v8::Value>(), 1258 Handle<v8::Signature>(), 1259 23); 1260 Local<Function> fun = fun_templ->GetFunction(); 1261 env->Global()->Set(v8_str("obj"), fun); 1262 Local<Script> script = v8_compile("obj.length"); 1263 CHECK_EQ(23, script->Run()->Int32Value()); 1264 } 1265 { 1266 Local<v8::FunctionTemplate> fun_templ = 1267 v8::FunctionTemplate::New(isolate, handle_callback); 1268 fun_templ->SetLength(22); 1269 Local<Function> fun = fun_templ->GetFunction(); 1270 env->Global()->Set(v8_str("obj"), fun); 1271 Local<Script> script = v8_compile("obj.length"); 1272 CHECK_EQ(22, script->Run()->Int32Value()); 1273 } 1274 { 1275 // Without setting length it defaults to 0. 1276 Local<v8::FunctionTemplate> fun_templ = 1277 v8::FunctionTemplate::New(isolate, handle_callback); 1278 Local<Function> fun = fun_templ->GetFunction(); 1279 env->Global()->Set(v8_str("obj"), fun); 1280 Local<Script> script = v8_compile("obj.length"); 1281 CHECK_EQ(0, script->Run()->Int32Value()); 1282 } 1283 } 1284 1285 1286 static void* expected_ptr; 1287 static void callback(const v8::FunctionCallbackInfo<v8::Value>& args) { 1288 void* ptr = v8::External::Cast(*args.Data())->Value(); 1289 CHECK_EQ(expected_ptr, ptr); 1290 args.GetReturnValue().Set(true); 1291 } 1292 1293 1294 static void TestExternalPointerWrapping() { 1295 LocalContext env; 1296 v8::Isolate* isolate = env->GetIsolate(); 1297 v8::HandleScope scope(isolate); 1298 1299 v8::Handle<v8::Value> data = 1300 v8::External::New(isolate, expected_ptr); 1301 1302 v8::Handle<v8::Object> obj = v8::Object::New(isolate); 1303 obj->Set(v8_str("func"), 1304 v8::FunctionTemplate::New(isolate, callback, data)->GetFunction()); 1305 env->Global()->Set(v8_str("obj"), obj); 1306 1307 CHECK(CompileRun( 1308 "function foo() {\n" 1309 " for (var i = 0; i < 13; i++) obj.func();\n" 1310 "}\n" 1311 "foo(), true")->BooleanValue()); 1312 } 1313 1314 1315 THREADED_TEST(ExternalWrap) { 1316 // Check heap allocated object. 1317 int* ptr = new int; 1318 expected_ptr = ptr; 1319 TestExternalPointerWrapping(); 1320 delete ptr; 1321 1322 // Check stack allocated object. 1323 int foo; 1324 expected_ptr = &foo; 1325 TestExternalPointerWrapping(); 1326 1327 // Check not aligned addresses. 1328 const int n = 100; 1329 char* s = new char[n]; 1330 for (int i = 0; i < n; i++) { 1331 expected_ptr = s + i; 1332 TestExternalPointerWrapping(); 1333 } 1334 1335 delete[] s; 1336 1337 // Check several invalid addresses. 1338 expected_ptr = reinterpret_cast<void*>(1); 1339 TestExternalPointerWrapping(); 1340 1341 expected_ptr = reinterpret_cast<void*>(0xdeadbeef); 1342 TestExternalPointerWrapping(); 1343 1344 expected_ptr = reinterpret_cast<void*>(0xdeadbeef + 1); 1345 TestExternalPointerWrapping(); 1346 1347 #if defined(V8_HOST_ARCH_X64) 1348 // Check a value with a leading 1 bit in x64 Smi encoding. 1349 expected_ptr = reinterpret_cast<void*>(0x400000000); 1350 TestExternalPointerWrapping(); 1351 1352 expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef); 1353 TestExternalPointerWrapping(); 1354 1355 expected_ptr = reinterpret_cast<void*>(0xdeadbeefdeadbeef + 1); 1356 TestExternalPointerWrapping(); 1357 #endif 1358 } 1359 1360 1361 THREADED_TEST(FindInstanceInPrototypeChain) { 1362 LocalContext env; 1363 v8::Isolate* isolate = env->GetIsolate(); 1364 v8::HandleScope scope(isolate); 1365 1366 Local<v8::FunctionTemplate> base = v8::FunctionTemplate::New(isolate); 1367 Local<v8::FunctionTemplate> derived = v8::FunctionTemplate::New(isolate); 1368 Local<v8::FunctionTemplate> other = v8::FunctionTemplate::New(isolate); 1369 derived->Inherit(base); 1370 1371 Local<v8::Function> base_function = base->GetFunction(); 1372 Local<v8::Function> derived_function = derived->GetFunction(); 1373 Local<v8::Function> other_function = other->GetFunction(); 1374 1375 Local<v8::Object> base_instance = base_function->NewInstance(); 1376 Local<v8::Object> derived_instance = derived_function->NewInstance(); 1377 Local<v8::Object> derived_instance2 = derived_function->NewInstance(); 1378 Local<v8::Object> other_instance = other_function->NewInstance(); 1379 derived_instance2->Set(v8_str("__proto__"), derived_instance); 1380 other_instance->Set(v8_str("__proto__"), derived_instance2); 1381 1382 // base_instance is only an instance of base. 1383 CHECK_EQ(base_instance, 1384 base_instance->FindInstanceInPrototypeChain(base)); 1385 CHECK(base_instance->FindInstanceInPrototypeChain(derived).IsEmpty()); 1386 CHECK(base_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 1387 1388 // derived_instance is an instance of base and derived. 1389 CHECK_EQ(derived_instance, 1390 derived_instance->FindInstanceInPrototypeChain(base)); 1391 CHECK_EQ(derived_instance, 1392 derived_instance->FindInstanceInPrototypeChain(derived)); 1393 CHECK(derived_instance->FindInstanceInPrototypeChain(other).IsEmpty()); 1394 1395 // other_instance is an instance of other and its immediate 1396 // prototype derived_instance2 is an instance of base and derived. 1397 // Note, derived_instance is an instance of base and derived too, 1398 // but it comes after derived_instance2 in the prototype chain of 1399 // other_instance. 1400 CHECK_EQ(derived_instance2, 1401 other_instance->FindInstanceInPrototypeChain(base)); 1402 CHECK_EQ(derived_instance2, 1403 other_instance->FindInstanceInPrototypeChain(derived)); 1404 CHECK_EQ(other_instance, 1405 other_instance->FindInstanceInPrototypeChain(other)); 1406 } 1407 1408 1409 THREADED_TEST(TinyInteger) { 1410 LocalContext env; 1411 v8::Isolate* isolate = env->GetIsolate(); 1412 v8::HandleScope scope(isolate); 1413 1414 int32_t value = 239; 1415 Local<v8::Integer> value_obj = v8::Integer::New(isolate, value); 1416 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1417 1418 value_obj = v8::Integer::New(isolate, value); 1419 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1420 } 1421 1422 1423 THREADED_TEST(BigSmiInteger) { 1424 LocalContext env; 1425 v8::HandleScope scope(env->GetIsolate()); 1426 v8::Isolate* isolate = CcTest::isolate(); 1427 1428 int32_t value = i::Smi::kMaxValue; 1429 // We cannot add one to a Smi::kMaxValue without wrapping. 1430 if (i::SmiValuesAre31Bits()) { 1431 CHECK(i::Smi::IsValid(value)); 1432 CHECK(!i::Smi::IsValid(value + 1)); 1433 1434 Local<v8::Integer> value_obj = v8::Integer::New(isolate, value); 1435 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1436 1437 value_obj = v8::Integer::New(isolate, value); 1438 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1439 } 1440 } 1441 1442 1443 THREADED_TEST(BigInteger) { 1444 LocalContext env; 1445 v8::HandleScope scope(env->GetIsolate()); 1446 v8::Isolate* isolate = CcTest::isolate(); 1447 1448 // We cannot add one to a Smi::kMaxValue without wrapping. 1449 if (i::SmiValuesAre31Bits()) { 1450 // The casts allow this to compile, even if Smi::kMaxValue is 2^31-1. 1451 // The code will not be run in that case, due to the "if" guard. 1452 int32_t value = 1453 static_cast<int32_t>(static_cast<uint32_t>(i::Smi::kMaxValue) + 1); 1454 CHECK(value > i::Smi::kMaxValue); 1455 CHECK(!i::Smi::IsValid(value)); 1456 1457 Local<v8::Integer> value_obj = v8::Integer::New(isolate, value); 1458 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1459 1460 value_obj = v8::Integer::New(isolate, value); 1461 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1462 } 1463 } 1464 1465 1466 THREADED_TEST(TinyUnsignedInteger) { 1467 LocalContext env; 1468 v8::HandleScope scope(env->GetIsolate()); 1469 v8::Isolate* isolate = CcTest::isolate(); 1470 1471 uint32_t value = 239; 1472 1473 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(isolate, value); 1474 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1475 1476 value_obj = v8::Integer::NewFromUnsigned(isolate, value); 1477 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1478 } 1479 1480 1481 THREADED_TEST(BigUnsignedSmiInteger) { 1482 LocalContext env; 1483 v8::HandleScope scope(env->GetIsolate()); 1484 v8::Isolate* isolate = CcTest::isolate(); 1485 1486 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue); 1487 CHECK(i::Smi::IsValid(value)); 1488 CHECK(!i::Smi::IsValid(value + 1)); 1489 1490 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(isolate, value); 1491 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1492 1493 value_obj = v8::Integer::NewFromUnsigned(isolate, value); 1494 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1495 } 1496 1497 1498 THREADED_TEST(BigUnsignedInteger) { 1499 LocalContext env; 1500 v8::HandleScope scope(env->GetIsolate()); 1501 v8::Isolate* isolate = CcTest::isolate(); 1502 1503 uint32_t value = static_cast<uint32_t>(i::Smi::kMaxValue) + 1; 1504 CHECK(value > static_cast<uint32_t>(i::Smi::kMaxValue)); 1505 CHECK(!i::Smi::IsValid(value)); 1506 1507 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(isolate, value); 1508 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1509 1510 value_obj = v8::Integer::NewFromUnsigned(isolate, value); 1511 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1512 } 1513 1514 1515 THREADED_TEST(OutOfSignedRangeUnsignedInteger) { 1516 LocalContext env; 1517 v8::HandleScope scope(env->GetIsolate()); 1518 v8::Isolate* isolate = CcTest::isolate(); 1519 1520 uint32_t INT32_MAX_AS_UINT = (1U << 31) - 1; 1521 uint32_t value = INT32_MAX_AS_UINT + 1; 1522 CHECK(value > INT32_MAX_AS_UINT); // No overflow. 1523 1524 Local<v8::Integer> value_obj = v8::Integer::NewFromUnsigned(isolate, value); 1525 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1526 1527 value_obj = v8::Integer::NewFromUnsigned(isolate, value); 1528 CHECK_EQ(static_cast<int64_t>(value), value_obj->Value()); 1529 } 1530 1531 1532 THREADED_TEST(IsNativeError) { 1533 LocalContext env; 1534 v8::HandleScope scope(env->GetIsolate()); 1535 v8::Handle<Value> syntax_error = CompileRun( 1536 "var out = 0; try { eval(\"#\"); } catch(x) { out = x; } out; "); 1537 CHECK(syntax_error->IsNativeError()); 1538 v8::Handle<Value> not_error = CompileRun("{a:42}"); 1539 CHECK(!not_error->IsNativeError()); 1540 v8::Handle<Value> not_object = CompileRun("42"); 1541 CHECK(!not_object->IsNativeError()); 1542 } 1543 1544 1545 THREADED_TEST(ArgumentsObject) { 1546 LocalContext env; 1547 v8::HandleScope scope(env->GetIsolate()); 1548 v8::Handle<Value> arguments_object = 1549 CompileRun("var out = 0; (function(){ out = arguments; })(1,2,3); out;"); 1550 CHECK(arguments_object->IsArgumentsObject()); 1551 v8::Handle<Value> array = CompileRun("[1,2,3]"); 1552 CHECK(!array->IsArgumentsObject()); 1553 v8::Handle<Value> object = CompileRun("{a:42}"); 1554 CHECK(!object->IsArgumentsObject()); 1555 } 1556 1557 1558 THREADED_TEST(IsMapOrSet) { 1559 LocalContext env; 1560 v8::HandleScope scope(env->GetIsolate()); 1561 v8::Handle<Value> map = CompileRun("new Map()"); 1562 v8::Handle<Value> set = CompileRun("new Set()"); 1563 v8::Handle<Value> weak_map = CompileRun("new WeakMap()"); 1564 v8::Handle<Value> weak_set = CompileRun("new WeakSet()"); 1565 CHECK(map->IsMap()); 1566 CHECK(set->IsSet()); 1567 CHECK(weak_map->IsWeakMap()); 1568 CHECK(weak_set->IsWeakSet()); 1569 1570 CHECK(!map->IsSet()); 1571 CHECK(!map->IsWeakMap()); 1572 CHECK(!map->IsWeakSet()); 1573 1574 CHECK(!set->IsMap()); 1575 CHECK(!set->IsWeakMap()); 1576 CHECK(!set->IsWeakSet()); 1577 1578 CHECK(!weak_map->IsMap()); 1579 CHECK(!weak_map->IsSet()); 1580 CHECK(!weak_map->IsWeakSet()); 1581 1582 CHECK(!weak_set->IsMap()); 1583 CHECK(!weak_set->IsSet()); 1584 CHECK(!weak_set->IsWeakMap()); 1585 1586 v8::Handle<Value> object = CompileRun("{a:42}"); 1587 CHECK(!object->IsMap()); 1588 CHECK(!object->IsSet()); 1589 CHECK(!object->IsWeakMap()); 1590 CHECK(!object->IsWeakSet()); 1591 } 1592 1593 1594 THREADED_TEST(StringObject) { 1595 LocalContext env; 1596 v8::HandleScope scope(env->GetIsolate()); 1597 v8::Handle<Value> boxed_string = CompileRun("new String(\"test\")"); 1598 CHECK(boxed_string->IsStringObject()); 1599 v8::Handle<Value> unboxed_string = CompileRun("\"test\""); 1600 CHECK(!unboxed_string->IsStringObject()); 1601 v8::Handle<Value> boxed_not_string = CompileRun("new Number(42)"); 1602 CHECK(!boxed_not_string->IsStringObject()); 1603 v8::Handle<Value> not_object = CompileRun("0"); 1604 CHECK(!not_object->IsStringObject()); 1605 v8::Handle<v8::StringObject> as_boxed = boxed_string.As<v8::StringObject>(); 1606 CHECK(!as_boxed.IsEmpty()); 1607 Local<v8::String> the_string = as_boxed->ValueOf(); 1608 CHECK(!the_string.IsEmpty()); 1609 ExpectObject("\"test\"", the_string); 1610 v8::Handle<v8::Value> new_boxed_string = v8::StringObject::New(the_string); 1611 CHECK(new_boxed_string->IsStringObject()); 1612 as_boxed = new_boxed_string.As<v8::StringObject>(); 1613 the_string = as_boxed->ValueOf(); 1614 CHECK(!the_string.IsEmpty()); 1615 ExpectObject("\"test\"", the_string); 1616 } 1617 1618 1619 THREADED_TEST(NumberObject) { 1620 LocalContext env; 1621 v8::HandleScope scope(env->GetIsolate()); 1622 v8::Handle<Value> boxed_number = CompileRun("new Number(42)"); 1623 CHECK(boxed_number->IsNumberObject()); 1624 v8::Handle<Value> unboxed_number = CompileRun("42"); 1625 CHECK(!unboxed_number->IsNumberObject()); 1626 v8::Handle<Value> boxed_not_number = CompileRun("new Boolean(false)"); 1627 CHECK(!boxed_not_number->IsNumberObject()); 1628 v8::Handle<v8::NumberObject> as_boxed = boxed_number.As<v8::NumberObject>(); 1629 CHECK(!as_boxed.IsEmpty()); 1630 double the_number = as_boxed->ValueOf(); 1631 CHECK_EQ(42.0, the_number); 1632 v8::Handle<v8::Value> new_boxed_number = 1633 v8::NumberObject::New(env->GetIsolate(), 43); 1634 CHECK(new_boxed_number->IsNumberObject()); 1635 as_boxed = new_boxed_number.As<v8::NumberObject>(); 1636 the_number = as_boxed->ValueOf(); 1637 CHECK_EQ(43.0, the_number); 1638 } 1639 1640 1641 THREADED_TEST(BooleanObject) { 1642 LocalContext env; 1643 v8::HandleScope scope(env->GetIsolate()); 1644 v8::Handle<Value> boxed_boolean = CompileRun("new Boolean(true)"); 1645 CHECK(boxed_boolean->IsBooleanObject()); 1646 v8::Handle<Value> unboxed_boolean = CompileRun("true"); 1647 CHECK(!unboxed_boolean->IsBooleanObject()); 1648 v8::Handle<Value> boxed_not_boolean = CompileRun("new Number(42)"); 1649 CHECK(!boxed_not_boolean->IsBooleanObject()); 1650 v8::Handle<v8::BooleanObject> as_boxed = 1651 boxed_boolean.As<v8::BooleanObject>(); 1652 CHECK(!as_boxed.IsEmpty()); 1653 bool the_boolean = as_boxed->ValueOf(); 1654 CHECK_EQ(true, the_boolean); 1655 v8::Handle<v8::Value> boxed_true = v8::BooleanObject::New(true); 1656 v8::Handle<v8::Value> boxed_false = v8::BooleanObject::New(false); 1657 CHECK(boxed_true->IsBooleanObject()); 1658 CHECK(boxed_false->IsBooleanObject()); 1659 as_boxed = boxed_true.As<v8::BooleanObject>(); 1660 CHECK_EQ(true, as_boxed->ValueOf()); 1661 as_boxed = boxed_false.As<v8::BooleanObject>(); 1662 CHECK_EQ(false, as_boxed->ValueOf()); 1663 } 1664 1665 1666 THREADED_TEST(PrimitiveAndWrappedBooleans) { 1667 LocalContext env; 1668 v8::HandleScope scope(env->GetIsolate()); 1669 1670 Local<Value> primitive_false = Boolean::New(env->GetIsolate(), false); 1671 CHECK(primitive_false->IsBoolean()); 1672 CHECK(!primitive_false->IsBooleanObject()); 1673 CHECK(!primitive_false->BooleanValue()); 1674 CHECK(!primitive_false->IsTrue()); 1675 CHECK(primitive_false->IsFalse()); 1676 1677 Local<Value> false_value = BooleanObject::New(false); 1678 CHECK(!false_value->IsBoolean()); 1679 CHECK(false_value->IsBooleanObject()); 1680 CHECK(false_value->BooleanValue()); 1681 CHECK(!false_value->IsTrue()); 1682 CHECK(!false_value->IsFalse()); 1683 1684 Local<BooleanObject> false_boolean_object = false_value.As<BooleanObject>(); 1685 CHECK(!false_boolean_object->IsBoolean()); 1686 CHECK(false_boolean_object->IsBooleanObject()); 1687 // TODO(svenpanne) Uncomment when BooleanObject::BooleanValue() is deleted. 1688 // CHECK(false_boolean_object->BooleanValue()); 1689 CHECK(!false_boolean_object->ValueOf()); 1690 CHECK(!false_boolean_object->IsTrue()); 1691 CHECK(!false_boolean_object->IsFalse()); 1692 1693 Local<Value> primitive_true = Boolean::New(env->GetIsolate(), true); 1694 CHECK(primitive_true->IsBoolean()); 1695 CHECK(!primitive_true->IsBooleanObject()); 1696 CHECK(primitive_true->BooleanValue()); 1697 CHECK(primitive_true->IsTrue()); 1698 CHECK(!primitive_true->IsFalse()); 1699 1700 Local<Value> true_value = BooleanObject::New(true); 1701 CHECK(!true_value->IsBoolean()); 1702 CHECK(true_value->IsBooleanObject()); 1703 CHECK(true_value->BooleanValue()); 1704 CHECK(!true_value->IsTrue()); 1705 CHECK(!true_value->IsFalse()); 1706 1707 Local<BooleanObject> true_boolean_object = true_value.As<BooleanObject>(); 1708 CHECK(!true_boolean_object->IsBoolean()); 1709 CHECK(true_boolean_object->IsBooleanObject()); 1710 // TODO(svenpanne) Uncomment when BooleanObject::BooleanValue() is deleted. 1711 // CHECK(true_boolean_object->BooleanValue()); 1712 CHECK(true_boolean_object->ValueOf()); 1713 CHECK(!true_boolean_object->IsTrue()); 1714 CHECK(!true_boolean_object->IsFalse()); 1715 } 1716 1717 1718 THREADED_TEST(Number) { 1719 LocalContext env; 1720 v8::HandleScope scope(env->GetIsolate()); 1721 double PI = 3.1415926; 1722 Local<v8::Number> pi_obj = v8::Number::New(env->GetIsolate(), PI); 1723 CHECK_EQ(PI, pi_obj->NumberValue()); 1724 } 1725 1726 1727 THREADED_TEST(ToNumber) { 1728 LocalContext env; 1729 v8::Isolate* isolate = CcTest::isolate(); 1730 v8::HandleScope scope(isolate); 1731 Local<String> str = v8_str("3.1415926"); 1732 CHECK_EQ(3.1415926, str->NumberValue()); 1733 v8::Handle<v8::Boolean> t = v8::True(isolate); 1734 CHECK_EQ(1.0, t->NumberValue()); 1735 v8::Handle<v8::Boolean> f = v8::False(isolate); 1736 CHECK_EQ(0.0, f->NumberValue()); 1737 } 1738 1739 1740 THREADED_TEST(Date) { 1741 LocalContext env; 1742 v8::HandleScope scope(env->GetIsolate()); 1743 double PI = 3.1415926; 1744 Local<Value> date = v8::Date::New(env->GetIsolate(), PI); 1745 CHECK_EQ(3.0, date->NumberValue()); 1746 date.As<v8::Date>()->Set(v8_str("property"), 1747 v8::Integer::New(env->GetIsolate(), 42)); 1748 CHECK_EQ(42, date.As<v8::Date>()->Get(v8_str("property"))->Int32Value()); 1749 } 1750 1751 1752 THREADED_TEST(Boolean) { 1753 LocalContext env; 1754 v8::Isolate* isolate = env->GetIsolate(); 1755 v8::HandleScope scope(isolate); 1756 v8::Handle<v8::Boolean> t = v8::True(isolate); 1757 CHECK(t->Value()); 1758 v8::Handle<v8::Boolean> f = v8::False(isolate); 1759 CHECK(!f->Value()); 1760 v8::Handle<v8::Primitive> u = v8::Undefined(isolate); 1761 CHECK(!u->BooleanValue()); 1762 v8::Handle<v8::Primitive> n = v8::Null(isolate); 1763 CHECK(!n->BooleanValue()); 1764 v8::Handle<String> str1 = v8_str(""); 1765 CHECK(!str1->BooleanValue()); 1766 v8::Handle<String> str2 = v8_str("x"); 1767 CHECK(str2->BooleanValue()); 1768 CHECK(!v8::Number::New(isolate, 0)->BooleanValue()); 1769 CHECK(v8::Number::New(isolate, -1)->BooleanValue()); 1770 CHECK(v8::Number::New(isolate, 1)->BooleanValue()); 1771 CHECK(v8::Number::New(isolate, 42)->BooleanValue()); 1772 CHECK(!v8_compile("NaN")->Run()->BooleanValue()); 1773 } 1774 1775 1776 static void DummyCallHandler(const v8::FunctionCallbackInfo<v8::Value>& args) { 1777 ApiTestFuzzer::Fuzz(); 1778 args.GetReturnValue().Set(v8_num(13.4)); 1779 } 1780 1781 1782 static void GetM(Local<String> name, 1783 const v8::PropertyCallbackInfo<v8::Value>& info) { 1784 ApiTestFuzzer::Fuzz(); 1785 info.GetReturnValue().Set(v8_num(876)); 1786 } 1787 1788 1789 THREADED_TEST(GlobalPrototype) { 1790 v8::Isolate* isolate = CcTest::isolate(); 1791 v8::HandleScope scope(isolate); 1792 v8::Handle<v8::FunctionTemplate> func_templ = 1793 v8::FunctionTemplate::New(isolate); 1794 func_templ->PrototypeTemplate()->Set( 1795 isolate, "dummy", v8::FunctionTemplate::New(isolate, DummyCallHandler)); 1796 v8::Handle<ObjectTemplate> templ = func_templ->InstanceTemplate(); 1797 templ->Set(isolate, "x", v8_num(200)); 1798 templ->SetAccessor(v8_str("m"), GetM); 1799 LocalContext env(0, templ); 1800 v8::Handle<Script> script(v8_compile("dummy()")); 1801 v8::Handle<Value> result(script->Run()); 1802 CHECK_EQ(13.4, result->NumberValue()); 1803 CHECK_EQ(200, v8_compile("x")->Run()->Int32Value()); 1804 CHECK_EQ(876, v8_compile("m")->Run()->Int32Value()); 1805 } 1806 1807 1808 THREADED_TEST(ObjectTemplate) { 1809 v8::Isolate* isolate = CcTest::isolate(); 1810 v8::HandleScope scope(isolate); 1811 Local<ObjectTemplate> templ1 = ObjectTemplate::New(isolate); 1812 templ1->Set(isolate, "x", v8_num(10)); 1813 templ1->Set(isolate, "y", v8_num(13)); 1814 LocalContext env; 1815 Local<v8::Object> instance1 = templ1->NewInstance(); 1816 env->Global()->Set(v8_str("p"), instance1); 1817 CHECK(v8_compile("(p.x == 10)")->Run()->BooleanValue()); 1818 CHECK(v8_compile("(p.y == 13)")->Run()->BooleanValue()); 1819 Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate); 1820 fun->PrototypeTemplate()->Set(isolate, "nirk", v8_num(123)); 1821 Local<ObjectTemplate> templ2 = fun->InstanceTemplate(); 1822 templ2->Set(isolate, "a", v8_num(12)); 1823 templ2->Set(isolate, "b", templ1); 1824 Local<v8::Object> instance2 = templ2->NewInstance(); 1825 env->Global()->Set(v8_str("q"), instance2); 1826 CHECK(v8_compile("(q.nirk == 123)")->Run()->BooleanValue()); 1827 CHECK(v8_compile("(q.a == 12)")->Run()->BooleanValue()); 1828 CHECK(v8_compile("(q.b.x == 10)")->Run()->BooleanValue()); 1829 CHECK(v8_compile("(q.b.y == 13)")->Run()->BooleanValue()); 1830 } 1831 1832 1833 static void GetFlabby(const v8::FunctionCallbackInfo<v8::Value>& args) { 1834 ApiTestFuzzer::Fuzz(); 1835 args.GetReturnValue().Set(v8_num(17.2)); 1836 } 1837 1838 1839 static void GetKnurd(Local<String> property, 1840 const v8::PropertyCallbackInfo<v8::Value>& info) { 1841 ApiTestFuzzer::Fuzz(); 1842 info.GetReturnValue().Set(v8_num(15.2)); 1843 } 1844 1845 1846 THREADED_TEST(DescriptorInheritance) { 1847 v8::Isolate* isolate = CcTest::isolate(); 1848 v8::HandleScope scope(isolate); 1849 v8::Handle<v8::FunctionTemplate> super = v8::FunctionTemplate::New(isolate); 1850 super->PrototypeTemplate()->Set(isolate, "flabby", 1851 v8::FunctionTemplate::New(isolate, 1852 GetFlabby)); 1853 super->PrototypeTemplate()->Set(isolate, "PI", v8_num(3.14)); 1854 1855 super->InstanceTemplate()->SetAccessor(v8_str("knurd"), GetKnurd); 1856 1857 v8::Handle<v8::FunctionTemplate> base1 = v8::FunctionTemplate::New(isolate); 1858 base1->Inherit(super); 1859 base1->PrototypeTemplate()->Set(isolate, "v1", v8_num(20.1)); 1860 1861 v8::Handle<v8::FunctionTemplate> base2 = v8::FunctionTemplate::New(isolate); 1862 base2->Inherit(super); 1863 base2->PrototypeTemplate()->Set(isolate, "v2", v8_num(10.1)); 1864 1865 LocalContext env; 1866 1867 env->Global()->Set(v8_str("s"), super->GetFunction()); 1868 env->Global()->Set(v8_str("base1"), base1->GetFunction()); 1869 env->Global()->Set(v8_str("base2"), base2->GetFunction()); 1870 1871 // Checks right __proto__ chain. 1872 CHECK(CompileRun("base1.prototype.__proto__ == s.prototype")->BooleanValue()); 1873 CHECK(CompileRun("base2.prototype.__proto__ == s.prototype")->BooleanValue()); 1874 1875 CHECK(v8_compile("s.prototype.PI == 3.14")->Run()->BooleanValue()); 1876 1877 // Instance accessor should not be visible on function object or its prototype 1878 CHECK(CompileRun("s.knurd == undefined")->BooleanValue()); 1879 CHECK(CompileRun("s.prototype.knurd == undefined")->BooleanValue()); 1880 CHECK(CompileRun("base1.prototype.knurd == undefined")->BooleanValue()); 1881 1882 env->Global()->Set(v8_str("obj"), 1883 base1->GetFunction()->NewInstance()); 1884 CHECK_EQ(17.2, v8_compile("obj.flabby()")->Run()->NumberValue()); 1885 CHECK(v8_compile("'flabby' in obj")->Run()->BooleanValue()); 1886 CHECK_EQ(15.2, v8_compile("obj.knurd")->Run()->NumberValue()); 1887 CHECK(v8_compile("'knurd' in obj")->Run()->BooleanValue()); 1888 CHECK_EQ(20.1, v8_compile("obj.v1")->Run()->NumberValue()); 1889 1890 env->Global()->Set(v8_str("obj2"), 1891 base2->GetFunction()->NewInstance()); 1892 CHECK_EQ(17.2, v8_compile("obj2.flabby()")->Run()->NumberValue()); 1893 CHECK(v8_compile("'flabby' in obj2")->Run()->BooleanValue()); 1894 CHECK_EQ(15.2, v8_compile("obj2.knurd")->Run()->NumberValue()); 1895 CHECK(v8_compile("'knurd' in obj2")->Run()->BooleanValue()); 1896 CHECK_EQ(10.1, v8_compile("obj2.v2")->Run()->NumberValue()); 1897 1898 // base1 and base2 cannot cross reference to each's prototype 1899 CHECK(v8_compile("obj.v2")->Run()->IsUndefined()); 1900 CHECK(v8_compile("obj2.v1")->Run()->IsUndefined()); 1901 } 1902 1903 1904 int echo_named_call_count; 1905 1906 1907 static void EchoNamedProperty(Local<String> name, 1908 const v8::PropertyCallbackInfo<v8::Value>& info) { 1909 ApiTestFuzzer::Fuzz(); 1910 CHECK_EQ(v8_str("data"), info.Data()); 1911 echo_named_call_count++; 1912 info.GetReturnValue().Set(name); 1913 } 1914 1915 1916 // Helper functions for Interceptor/Accessor interaction tests 1917 1918 void SimpleAccessorGetter(Local<String> name, 1919 const v8::PropertyCallbackInfo<v8::Value>& info) { 1920 Handle<Object> self = Handle<Object>::Cast(info.This()); 1921 info.GetReturnValue().Set( 1922 self->Get(String::Concat(v8_str("accessor_"), name))); 1923 } 1924 1925 void SimpleAccessorSetter(Local<String> name, Local<Value> value, 1926 const v8::PropertyCallbackInfo<void>& info) { 1927 Handle<Object> self = Handle<Object>::Cast(info.This()); 1928 self->Set(String::Concat(v8_str("accessor_"), name), value); 1929 } 1930 1931 void SymbolAccessorGetter(Local<Name> name, 1932 const v8::PropertyCallbackInfo<v8::Value>& info) { 1933 CHECK(name->IsSymbol()); 1934 Local<Symbol> sym = Local<Symbol>::Cast(name); 1935 if (sym->Name()->IsUndefined()) 1936 return; 1937 SimpleAccessorGetter(Local<String>::Cast(sym->Name()), info); 1938 } 1939 1940 void SymbolAccessorSetter(Local<Name> name, Local<Value> value, 1941 const v8::PropertyCallbackInfo<void>& info) { 1942 CHECK(name->IsSymbol()); 1943 Local<Symbol> sym = Local<Symbol>::Cast(name); 1944 if (sym->Name()->IsUndefined()) 1945 return; 1946 SimpleAccessorSetter(Local<String>::Cast(sym->Name()), value, info); 1947 } 1948 1949 void EmptyInterceptorGetter(Local<String> name, 1950 const v8::PropertyCallbackInfo<v8::Value>& info) { 1951 } 1952 1953 void EmptyInterceptorSetter(Local<String> name, 1954 Local<Value> value, 1955 const v8::PropertyCallbackInfo<v8::Value>& info) { 1956 } 1957 1958 void InterceptorGetter(Local<String> name, 1959 const v8::PropertyCallbackInfo<v8::Value>& info) { 1960 // Intercept names that start with 'interceptor_'. 1961 String::Utf8Value utf8(name); 1962 char* name_str = *utf8; 1963 char prefix[] = "interceptor_"; 1964 int i; 1965 for (i = 0; name_str[i] && prefix[i]; ++i) { 1966 if (name_str[i] != prefix[i]) return; 1967 } 1968 Handle<Object> self = Handle<Object>::Cast(info.This()); 1969 info.GetReturnValue().Set(self->GetHiddenValue(v8_str(name_str + i))); 1970 } 1971 1972 void InterceptorSetter(Local<String> name, 1973 Local<Value> value, 1974 const v8::PropertyCallbackInfo<v8::Value>& info) { 1975 // Intercept accesses that set certain integer values, for which the name does 1976 // not start with 'accessor_'. 1977 String::Utf8Value utf8(name); 1978 char* name_str = *utf8; 1979 char prefix[] = "accessor_"; 1980 int i; 1981 for (i = 0; name_str[i] && prefix[i]; ++i) { 1982 if (name_str[i] != prefix[i]) break; 1983 } 1984 if (!prefix[i]) return; 1985 1986 if (value->IsInt32() && value->Int32Value() < 10000) { 1987 Handle<Object> self = Handle<Object>::Cast(info.This()); 1988 self->SetHiddenValue(name, value); 1989 info.GetReturnValue().Set(value); 1990 } 1991 } 1992 1993 void AddAccessor(Handle<FunctionTemplate> templ, 1994 Handle<String> name, 1995 v8::AccessorGetterCallback getter, 1996 v8::AccessorSetterCallback setter) { 1997 templ->PrototypeTemplate()->SetAccessor(name, getter, setter); 1998 } 1999 2000 void AddInterceptor(Handle<FunctionTemplate> templ, 2001 v8::NamedPropertyGetterCallback getter, 2002 v8::NamedPropertySetterCallback setter) { 2003 templ->InstanceTemplate()->SetNamedPropertyHandler(getter, setter); 2004 } 2005 2006 2007 void AddAccessor(Handle<FunctionTemplate> templ, 2008 Handle<Name> name, 2009 v8::AccessorNameGetterCallback getter, 2010 v8::AccessorNameSetterCallback setter) { 2011 templ->PrototypeTemplate()->SetAccessor(name, getter, setter); 2012 } 2013 2014 2015 THREADED_TEST(EmptyInterceptorDoesNotShadowAccessors) { 2016 v8::HandleScope scope(CcTest::isolate()); 2017 Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate()); 2018 Handle<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate()); 2019 child->Inherit(parent); 2020 AddAccessor(parent, v8_str("age"), 2021 SimpleAccessorGetter, SimpleAccessorSetter); 2022 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); 2023 LocalContext env; 2024 env->Global()->Set(v8_str("Child"), child->GetFunction()); 2025 CompileRun("var child = new Child;" 2026 "child.age = 10;"); 2027 ExpectBoolean("child.hasOwnProperty('age')", false); 2028 ExpectInt32("child.age", 10); 2029 ExpectInt32("child.accessor_age", 10); 2030 } 2031 2032 2033 THREADED_TEST(ExecutableAccessorIsPreservedOnAttributeChange) { 2034 v8::Isolate* isolate = CcTest::isolate(); 2035 v8::HandleScope scope(isolate); 2036 LocalContext env; 2037 v8::Local<v8::Value> res = CompileRun("var a = []; a;"); 2038 i::Handle<i::JSObject> a(v8::Utils::OpenHandle(v8::Object::Cast(*res))); 2039 CHECK(a->map()->instance_descriptors()->IsFixedArray()); 2040 CHECK_GT(i::FixedArray::cast(a->map()->instance_descriptors())->length(), 0); 2041 CompileRun("Object.defineProperty(a, 'length', { writable: false });"); 2042 CHECK_EQ(i::FixedArray::cast(a->map()->instance_descriptors())->length(), 0); 2043 // But we should still have an ExecutableAccessorInfo. 2044 i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); 2045 i::LookupResult lookup(i_isolate); 2046 i::Handle<i::String> name(v8::Utils::OpenHandle(*v8_str("length"))); 2047 i::LookupIterator it(a, name, i::LookupIterator::OWN_SKIP_INTERCEPTOR); 2048 CHECK_EQ(i::LookupIterator::ACCESSOR, it.state()); 2049 CHECK(it.GetAccessors()->IsExecutableAccessorInfo()); 2050 } 2051 2052 2053 THREADED_TEST(EmptyInterceptorBreakTransitions) { 2054 v8::HandleScope scope(CcTest::isolate()); 2055 Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); 2056 AddInterceptor(templ, EmptyInterceptorGetter, EmptyInterceptorSetter); 2057 LocalContext env; 2058 env->Global()->Set(v8_str("Constructor"), templ->GetFunction()); 2059 CompileRun("var o1 = new Constructor;" 2060 "o1.a = 1;" // Ensure a and x share the descriptor array. 2061 "Object.defineProperty(o1, 'x', {value: 10});"); 2062 CompileRun("var o2 = new Constructor;" 2063 "o2.a = 1;" 2064 "Object.defineProperty(o2, 'x', {value: 10});"); 2065 } 2066 2067 2068 THREADED_TEST(EmptyInterceptorDoesNotShadowJSAccessors) { 2069 v8::Isolate* isolate = CcTest::isolate(); 2070 v8::HandleScope scope(isolate); 2071 Handle<FunctionTemplate> parent = FunctionTemplate::New(isolate); 2072 Handle<FunctionTemplate> child = FunctionTemplate::New(isolate); 2073 child->Inherit(parent); 2074 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); 2075 LocalContext env; 2076 env->Global()->Set(v8_str("Child"), child->GetFunction()); 2077 CompileRun("var child = new Child;" 2078 "var parent = child.__proto__;" 2079 "Object.defineProperty(parent, 'age', " 2080 " {get: function(){ return this.accessor_age; }, " 2081 " set: function(v){ this.accessor_age = v; }, " 2082 " enumerable: true, configurable: true});" 2083 "child.age = 10;"); 2084 ExpectBoolean("child.hasOwnProperty('age')", false); 2085 ExpectInt32("child.age", 10); 2086 ExpectInt32("child.accessor_age", 10); 2087 } 2088 2089 2090 THREADED_TEST(EmptyInterceptorDoesNotAffectJSProperties) { 2091 v8::Isolate* isolate = CcTest::isolate(); 2092 v8::HandleScope scope(isolate); 2093 Handle<FunctionTemplate> parent = FunctionTemplate::New(isolate); 2094 Handle<FunctionTemplate> child = FunctionTemplate::New(isolate); 2095 child->Inherit(parent); 2096 AddInterceptor(child, EmptyInterceptorGetter, EmptyInterceptorSetter); 2097 LocalContext env; 2098 env->Global()->Set(v8_str("Child"), child->GetFunction()); 2099 CompileRun("var child = new Child;" 2100 "var parent = child.__proto__;" 2101 "parent.name = 'Alice';"); 2102 ExpectBoolean("child.hasOwnProperty('name')", false); 2103 ExpectString("child.name", "Alice"); 2104 CompileRun("child.name = 'Bob';"); 2105 ExpectString("child.name", "Bob"); 2106 ExpectBoolean("child.hasOwnProperty('name')", true); 2107 ExpectString("parent.name", "Alice"); 2108 } 2109 2110 2111 THREADED_TEST(SwitchFromInterceptorToAccessor) { 2112 v8::HandleScope scope(CcTest::isolate()); 2113 Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); 2114 AddAccessor(templ, v8_str("age"), 2115 SimpleAccessorGetter, SimpleAccessorSetter); 2116 AddInterceptor(templ, InterceptorGetter, InterceptorSetter); 2117 LocalContext env; 2118 env->Global()->Set(v8_str("Obj"), templ->GetFunction()); 2119 CompileRun("var obj = new Obj;" 2120 "function setAge(i){ obj.age = i; };" 2121 "for(var i = 0; i <= 10000; i++) setAge(i);"); 2122 // All i < 10000 go to the interceptor. 2123 ExpectInt32("obj.interceptor_age", 9999); 2124 // The last i goes to the accessor. 2125 ExpectInt32("obj.accessor_age", 10000); 2126 } 2127 2128 2129 THREADED_TEST(SwitchFromAccessorToInterceptor) { 2130 v8::HandleScope scope(CcTest::isolate()); 2131 Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); 2132 AddAccessor(templ, v8_str("age"), 2133 SimpleAccessorGetter, SimpleAccessorSetter); 2134 AddInterceptor(templ, InterceptorGetter, InterceptorSetter); 2135 LocalContext env; 2136 env->Global()->Set(v8_str("Obj"), templ->GetFunction()); 2137 CompileRun("var obj = new Obj;" 2138 "function setAge(i){ obj.age = i; };" 2139 "for(var i = 20000; i >= 9999; i--) setAge(i);"); 2140 // All i >= 10000 go to the accessor. 2141 ExpectInt32("obj.accessor_age", 10000); 2142 // The last i goes to the interceptor. 2143 ExpectInt32("obj.interceptor_age", 9999); 2144 } 2145 2146 2147 THREADED_TEST(SwitchFromInterceptorToAccessorWithInheritance) { 2148 v8::HandleScope scope(CcTest::isolate()); 2149 Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate()); 2150 Handle<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate()); 2151 child->Inherit(parent); 2152 AddAccessor(parent, v8_str("age"), 2153 SimpleAccessorGetter, SimpleAccessorSetter); 2154 AddInterceptor(child, InterceptorGetter, InterceptorSetter); 2155 LocalContext env; 2156 env->Global()->Set(v8_str("Child"), child->GetFunction()); 2157 CompileRun("var child = new Child;" 2158 "function setAge(i){ child.age = i; };" 2159 "for(var i = 0; i <= 10000; i++) setAge(i);"); 2160 // All i < 10000 go to the interceptor. 2161 ExpectInt32("child.interceptor_age", 9999); 2162 // The last i goes to the accessor. 2163 ExpectInt32("child.accessor_age", 10000); 2164 } 2165 2166 2167 THREADED_TEST(SwitchFromAccessorToInterceptorWithInheritance) { 2168 v8::HandleScope scope(CcTest::isolate()); 2169 Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate()); 2170 Handle<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate()); 2171 child->Inherit(parent); 2172 AddAccessor(parent, v8_str("age"), 2173 SimpleAccessorGetter, SimpleAccessorSetter); 2174 AddInterceptor(child, InterceptorGetter, InterceptorSetter); 2175 LocalContext env; 2176 env->Global()->Set(v8_str("Child"), child->GetFunction()); 2177 CompileRun("var child = new Child;" 2178 "function setAge(i){ child.age = i; };" 2179 "for(var i = 20000; i >= 9999; i--) setAge(i);"); 2180 // All i >= 10000 go to the accessor. 2181 ExpectInt32("child.accessor_age", 10000); 2182 // The last i goes to the interceptor. 2183 ExpectInt32("child.interceptor_age", 9999); 2184 } 2185 2186 2187 THREADED_TEST(SwitchFromInterceptorToJSAccessor) { 2188 v8::HandleScope scope(CcTest::isolate()); 2189 Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); 2190 AddInterceptor(templ, InterceptorGetter, InterceptorSetter); 2191 LocalContext env; 2192 env->Global()->Set(v8_str("Obj"), templ->GetFunction()); 2193 CompileRun("var obj = new Obj;" 2194 "function setter(i) { this.accessor_age = i; };" 2195 "function getter() { return this.accessor_age; };" 2196 "function setAge(i) { obj.age = i; };" 2197 "Object.defineProperty(obj, 'age', { get:getter, set:setter });" 2198 "for(var i = 0; i <= 10000; i++) setAge(i);"); 2199 // All i < 10000 go to the interceptor. 2200 ExpectInt32("obj.interceptor_age", 9999); 2201 // The last i goes to the JavaScript accessor. 2202 ExpectInt32("obj.accessor_age", 10000); 2203 // The installed JavaScript getter is still intact. 2204 // This last part is a regression test for issue 1651 and relies on the fact 2205 // that both interceptor and accessor are being installed on the same object. 2206 ExpectInt32("obj.age", 10000); 2207 ExpectBoolean("obj.hasOwnProperty('age')", true); 2208 ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value"); 2209 } 2210 2211 2212 THREADED_TEST(SwitchFromJSAccessorToInterceptor) { 2213 v8::HandleScope scope(CcTest::isolate()); 2214 Handle<FunctionTemplate> templ = FunctionTemplate::New(CcTest::isolate()); 2215 AddInterceptor(templ, InterceptorGetter, InterceptorSetter); 2216 LocalContext env; 2217 env->Global()->Set(v8_str("Obj"), templ->GetFunction()); 2218 CompileRun("var obj = new Obj;" 2219 "function setter(i) { this.accessor_age = i; };" 2220 "function getter() { return this.accessor_age; };" 2221 "function setAge(i) { obj.age = i; };" 2222 "Object.defineProperty(obj, 'age', { get:getter, set:setter });" 2223 "for(var i = 20000; i >= 9999; i--) setAge(i);"); 2224 // All i >= 10000 go to the accessor. 2225 ExpectInt32("obj.accessor_age", 10000); 2226 // The last i goes to the interceptor. 2227 ExpectInt32("obj.interceptor_age", 9999); 2228 // The installed JavaScript getter is still intact. 2229 // This last part is a regression test for issue 1651 and relies on the fact 2230 // that both interceptor and accessor are being installed on the same object. 2231 ExpectInt32("obj.age", 10000); 2232 ExpectBoolean("obj.hasOwnProperty('age')", true); 2233 ExpectUndefined("Object.getOwnPropertyDescriptor(obj, 'age').value"); 2234 } 2235 2236 2237 THREADED_TEST(SwitchFromInterceptorToProperty) { 2238 v8::HandleScope scope(CcTest::isolate()); 2239 Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate()); 2240 Handle<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate()); 2241 child->Inherit(parent); 2242 AddInterceptor(child, InterceptorGetter, InterceptorSetter); 2243 LocalContext env; 2244 env->Global()->Set(v8_str("Child"), child->GetFunction()); 2245 CompileRun("var child = new Child;" 2246 "function setAge(i){ child.age = i; };" 2247 "for(var i = 0; i <= 10000; i++) setAge(i);"); 2248 // All i < 10000 go to the interceptor. 2249 ExpectInt32("child.interceptor_age", 9999); 2250 // The last i goes to child's own property. 2251 ExpectInt32("child.age", 10000); 2252 } 2253 2254 2255 THREADED_TEST(SwitchFromPropertyToInterceptor) { 2256 v8::HandleScope scope(CcTest::isolate()); 2257 Handle<FunctionTemplate> parent = FunctionTemplate::New(CcTest::isolate()); 2258 Handle<FunctionTemplate> child = FunctionTemplate::New(CcTest::isolate()); 2259 child->Inherit(parent); 2260 AddInterceptor(child, InterceptorGetter, InterceptorSetter); 2261 LocalContext env; 2262 env->Global()->Set(v8_str("Child"), child->GetFunction()); 2263 CompileRun("var child = new Child;" 2264 "function setAge(i){ child.age = i; };" 2265 "for(var i = 20000; i >= 9999; i--) setAge(i);"); 2266 // All i >= 10000 go to child's own property. 2267 ExpectInt32("child.age", 10000); 2268 // The last i goes to the interceptor. 2269 ExpectInt32("child.interceptor_age", 9999); 2270 } 2271 2272 2273 THREADED_TEST(NamedPropertyHandlerGetter) { 2274 echo_named_call_count = 0; 2275 v8::HandleScope scope(CcTest::isolate()); 2276 v8::Handle<v8::FunctionTemplate> templ = 2277 v8::FunctionTemplate::New(CcTest::isolate()); 2278 templ->InstanceTemplate()->SetNamedPropertyHandler(EchoNamedProperty, 2279 0, 0, 0, 0, 2280 v8_str("data")); 2281 LocalContext env; 2282 env->Global()->Set(v8_str("obj"), 2283 templ->GetFunction()->NewInstance()); 2284 CHECK_EQ(echo_named_call_count, 0); 2285 v8_compile("obj.x")->Run(); 2286 CHECK_EQ(echo_named_call_count, 1); 2287 const char* code = "var str = 'oddle'; obj[str] + obj.poddle;"; 2288 v8::Handle<Value> str = CompileRun(code); 2289 String::Utf8Value value(str); 2290 CHECK_EQ(*value, "oddlepoddle"); 2291 // Check default behavior 2292 CHECK_EQ(v8_compile("obj.flob = 10;")->Run()->Int32Value(), 10); 2293 CHECK(v8_compile("'myProperty' in obj")->Run()->BooleanValue()); 2294 CHECK(v8_compile("delete obj.myProperty")->Run()->BooleanValue()); 2295 } 2296 2297 2298 int echo_indexed_call_count = 0; 2299 2300 2301 static void EchoIndexedProperty( 2302 uint32_t index, 2303 const v8::PropertyCallbackInfo<v8::Value>& info) { 2304 ApiTestFuzzer::Fuzz(); 2305 CHECK_EQ(v8_num(637), info.Data()); 2306 echo_indexed_call_count++; 2307 info.GetReturnValue().Set(v8_num(index)); 2308 } 2309 2310 2311 THREADED_TEST(IndexedPropertyHandlerGetter) { 2312 v8::Isolate* isolate = CcTest::isolate(); 2313 v8::HandleScope scope(isolate); 2314 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); 2315 templ->InstanceTemplate()->SetIndexedPropertyHandler(EchoIndexedProperty, 2316 0, 0, 0, 0, 2317 v8_num(637)); 2318 LocalContext env; 2319 env->Global()->Set(v8_str("obj"), 2320 templ->GetFunction()->NewInstance()); 2321 Local<Script> script = v8_compile("obj[900]"); 2322 CHECK_EQ(script->Run()->Int32Value(), 900); 2323 } 2324 2325 2326 v8::Handle<v8::Object> bottom; 2327 2328 static void CheckThisIndexedPropertyHandler( 2329 uint32_t index, 2330 const v8::PropertyCallbackInfo<v8::Value>& info) { 2331 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyHandler)); 2332 ApiTestFuzzer::Fuzz(); 2333 CHECK(info.This()->Equals(bottom)); 2334 } 2335 2336 static void CheckThisNamedPropertyHandler( 2337 Local<String> name, 2338 const v8::PropertyCallbackInfo<v8::Value>& info) { 2339 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyHandler)); 2340 ApiTestFuzzer::Fuzz(); 2341 CHECK(info.This()->Equals(bottom)); 2342 } 2343 2344 void CheckThisIndexedPropertySetter( 2345 uint32_t index, 2346 Local<Value> value, 2347 const v8::PropertyCallbackInfo<v8::Value>& info) { 2348 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertySetter)); 2349 ApiTestFuzzer::Fuzz(); 2350 CHECK(info.This()->Equals(bottom)); 2351 } 2352 2353 2354 void CheckThisNamedPropertySetter( 2355 Local<String> property, 2356 Local<Value> value, 2357 const v8::PropertyCallbackInfo<v8::Value>& info) { 2358 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertySetter)); 2359 ApiTestFuzzer::Fuzz(); 2360 CHECK(info.This()->Equals(bottom)); 2361 } 2362 2363 void CheckThisIndexedPropertyQuery( 2364 uint32_t index, 2365 const v8::PropertyCallbackInfo<v8::Integer>& info) { 2366 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyQuery)); 2367 ApiTestFuzzer::Fuzz(); 2368 CHECK(info.This()->Equals(bottom)); 2369 } 2370 2371 2372 void CheckThisNamedPropertyQuery( 2373 Local<String> property, 2374 const v8::PropertyCallbackInfo<v8::Integer>& info) { 2375 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyQuery)); 2376 ApiTestFuzzer::Fuzz(); 2377 CHECK(info.This()->Equals(bottom)); 2378 } 2379 2380 2381 void CheckThisIndexedPropertyDeleter( 2382 uint32_t index, 2383 const v8::PropertyCallbackInfo<v8::Boolean>& info) { 2384 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyDeleter)); 2385 ApiTestFuzzer::Fuzz(); 2386 CHECK(info.This()->Equals(bottom)); 2387 } 2388 2389 2390 void CheckThisNamedPropertyDeleter( 2391 Local<String> property, 2392 const v8::PropertyCallbackInfo<v8::Boolean>& info) { 2393 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyDeleter)); 2394 ApiTestFuzzer::Fuzz(); 2395 CHECK(info.This()->Equals(bottom)); 2396 } 2397 2398 2399 void CheckThisIndexedPropertyEnumerator( 2400 const v8::PropertyCallbackInfo<v8::Array>& info) { 2401 CheckReturnValue(info, FUNCTION_ADDR(CheckThisIndexedPropertyEnumerator)); 2402 ApiTestFuzzer::Fuzz(); 2403 CHECK(info.This()->Equals(bottom)); 2404 } 2405 2406 2407 void CheckThisNamedPropertyEnumerator( 2408 const v8::PropertyCallbackInfo<v8::Array>& info) { 2409 CheckReturnValue(info, FUNCTION_ADDR(CheckThisNamedPropertyEnumerator)); 2410 ApiTestFuzzer::Fuzz(); 2411 CHECK(info.This()->Equals(bottom)); 2412 } 2413 2414 2415 THREADED_PROFILED_TEST(PropertyHandlerInPrototype) { 2416 LocalContext env; 2417 v8::Isolate* isolate = env->GetIsolate(); 2418 v8::HandleScope scope(isolate); 2419 2420 // Set up a prototype chain with three interceptors. 2421 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); 2422 templ->InstanceTemplate()->SetIndexedPropertyHandler( 2423 CheckThisIndexedPropertyHandler, 2424 CheckThisIndexedPropertySetter, 2425 CheckThisIndexedPropertyQuery, 2426 CheckThisIndexedPropertyDeleter, 2427 CheckThisIndexedPropertyEnumerator); 2428 2429 templ->InstanceTemplate()->SetNamedPropertyHandler( 2430 CheckThisNamedPropertyHandler, 2431 CheckThisNamedPropertySetter, 2432 CheckThisNamedPropertyQuery, 2433 CheckThisNamedPropertyDeleter, 2434 CheckThisNamedPropertyEnumerator); 2435 2436 bottom = templ->GetFunction()->NewInstance(); 2437 Local<v8::Object> top = templ->GetFunction()->NewInstance(); 2438 Local<v8::Object> middle = templ->GetFunction()->NewInstance(); 2439 2440 bottom->SetPrototype(middle); 2441 middle->SetPrototype(top); 2442 env->Global()->Set(v8_str("obj"), bottom); 2443 2444 // Indexed and named get. 2445 CompileRun("obj[0]"); 2446 CompileRun("obj.x"); 2447 2448 // Indexed and named set. 2449 CompileRun("obj[1] = 42"); 2450 CompileRun("obj.y = 42"); 2451 2452 // Indexed and named query. 2453 CompileRun("0 in obj"); 2454 CompileRun("'x' in obj"); 2455 2456 // Indexed and named deleter. 2457 CompileRun("delete obj[0]"); 2458 CompileRun("delete obj.x"); 2459 2460 // Enumerators. 2461 CompileRun("for (var p in obj) ;"); 2462 } 2463 2464 2465 static void PrePropertyHandlerGet( 2466 Local<String> key, 2467 const v8::PropertyCallbackInfo<v8::Value>& info) { 2468 ApiTestFuzzer::Fuzz(); 2469 if (v8_str("pre")->Equals(key)) { 2470 info.GetReturnValue().Set(v8_str("PrePropertyHandler: pre")); 2471 } 2472 } 2473 2474 2475 static void PrePropertyHandlerQuery( 2476 Local<String> key, 2477 const v8::PropertyCallbackInfo<v8::Integer>& info) { 2478 if (v8_str("pre")->Equals(key)) { 2479 info.GetReturnValue().Set(static_cast<int32_t>(v8::None)); 2480 } 2481 } 2482 2483 2484 THREADED_TEST(PrePropertyHandler) { 2485 v8::Isolate* isolate = CcTest::isolate(); 2486 v8::HandleScope scope(isolate); 2487 v8::Handle<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate); 2488 desc->InstanceTemplate()->SetNamedPropertyHandler(PrePropertyHandlerGet, 2489 0, 2490 PrePropertyHandlerQuery); 2491 LocalContext env(NULL, desc->InstanceTemplate()); 2492 CompileRun("var pre = 'Object: pre'; var on = 'Object: on';"); 2493 v8::Handle<Value> result_pre = CompileRun("pre"); 2494 CHECK_EQ(v8_str("PrePropertyHandler: pre"), result_pre); 2495 v8::Handle<Value> result_on = CompileRun("on"); 2496 CHECK_EQ(v8_str("Object: on"), result_on); 2497 v8::Handle<Value> result_post = CompileRun("post"); 2498 CHECK(result_post.IsEmpty()); 2499 } 2500 2501 2502 THREADED_TEST(UndefinedIsNotEnumerable) { 2503 LocalContext env; 2504 v8::HandleScope scope(env->GetIsolate()); 2505 v8::Handle<Value> result = CompileRun("this.propertyIsEnumerable(undefined)"); 2506 CHECK(result->IsFalse()); 2507 } 2508 2509 2510 v8::Handle<Script> call_recursively_script; 2511 static const int kTargetRecursionDepth = 200; // near maximum 2512 2513 2514 static void CallScriptRecursivelyCall( 2515 const v8::FunctionCallbackInfo<v8::Value>& args) { 2516 ApiTestFuzzer::Fuzz(); 2517 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 2518 if (depth == kTargetRecursionDepth) return; 2519 args.This()->Set(v8_str("depth"), 2520 v8::Integer::New(args.GetIsolate(), depth + 1)); 2521 args.GetReturnValue().Set(call_recursively_script->Run()); 2522 } 2523 2524 2525 static void CallFunctionRecursivelyCall( 2526 const v8::FunctionCallbackInfo<v8::Value>& args) { 2527 ApiTestFuzzer::Fuzz(); 2528 int depth = args.This()->Get(v8_str("depth"))->Int32Value(); 2529 if (depth == kTargetRecursionDepth) { 2530 printf("[depth = %d]\n", depth); 2531 return; 2532 } 2533 args.This()->Set(v8_str("depth"), 2534 v8::Integer::New(args.GetIsolate(), depth + 1)); 2535 v8::Handle<Value> function = 2536 args.This()->Get(v8_str("callFunctionRecursively")); 2537 args.GetReturnValue().Set( 2538 function.As<Function>()->Call(args.This(), 0, NULL)); 2539 } 2540 2541 2542 THREADED_TEST(DeepCrossLanguageRecursion) { 2543 v8::Isolate* isolate = CcTest::isolate(); 2544 v8::HandleScope scope(isolate); 2545 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(isolate); 2546 global->Set(v8_str("callScriptRecursively"), 2547 v8::FunctionTemplate::New(isolate, CallScriptRecursivelyCall)); 2548 global->Set(v8_str("callFunctionRecursively"), 2549 v8::FunctionTemplate::New(isolate, CallFunctionRecursivelyCall)); 2550 LocalContext env(NULL, global); 2551 2552 env->Global()->Set(v8_str("depth"), v8::Integer::New(isolate, 0)); 2553 call_recursively_script = v8_compile("callScriptRecursively()"); 2554 call_recursively_script->Run(); 2555 call_recursively_script = v8::Handle<Script>(); 2556 2557 env->Global()->Set(v8_str("depth"), v8::Integer::New(isolate, 0)); 2558 CompileRun("callFunctionRecursively()"); 2559 } 2560 2561 2562 static void ThrowingPropertyHandlerGet( 2563 Local<String> key, 2564 const v8::PropertyCallbackInfo<v8::Value>& info) { 2565 ApiTestFuzzer::Fuzz(); 2566 info.GetReturnValue().Set(info.GetIsolate()->ThrowException(key)); 2567 } 2568 2569 2570 static void ThrowingPropertyHandlerSet( 2571 Local<String> key, 2572 Local<Value>, 2573 const v8::PropertyCallbackInfo<v8::Value>& info) { 2574 info.GetIsolate()->ThrowException(key); 2575 info.GetReturnValue().SetUndefined(); // not the same as empty handle 2576 } 2577 2578 2579 THREADED_TEST(CallbackExceptionRegression) { 2580 v8::Isolate* isolate = CcTest::isolate(); 2581 v8::HandleScope scope(isolate); 2582 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); 2583 obj->SetNamedPropertyHandler(ThrowingPropertyHandlerGet, 2584 ThrowingPropertyHandlerSet); 2585 LocalContext env; 2586 env->Global()->Set(v8_str("obj"), obj->NewInstance()); 2587 v8::Handle<Value> otto = CompileRun( 2588 "try { with (obj) { otto; } } catch (e) { e; }"); 2589 CHECK_EQ(v8_str("otto"), otto); 2590 v8::Handle<Value> netto = CompileRun( 2591 "try { with (obj) { netto = 4; } } catch (e) { e; }"); 2592 CHECK_EQ(v8_str("netto"), netto); 2593 } 2594 2595 2596 THREADED_TEST(FunctionPrototype) { 2597 v8::Isolate* isolate = CcTest::isolate(); 2598 v8::HandleScope scope(isolate); 2599 Local<v8::FunctionTemplate> Foo = v8::FunctionTemplate::New(isolate); 2600 Foo->PrototypeTemplate()->Set(v8_str("plak"), v8_num(321)); 2601 LocalContext env; 2602 env->Global()->Set(v8_str("Foo"), Foo->GetFunction()); 2603 Local<Script> script = v8_compile("Foo.prototype.plak"); 2604 CHECK_EQ(script->Run()->Int32Value(), 321); 2605 } 2606 2607 2608 THREADED_TEST(InternalFields) { 2609 LocalContext env; 2610 v8::Isolate* isolate = env->GetIsolate(); 2611 v8::HandleScope scope(isolate); 2612 2613 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); 2614 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 2615 instance_templ->SetInternalFieldCount(1); 2616 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 2617 CHECK_EQ(1, obj->InternalFieldCount()); 2618 CHECK(obj->GetInternalField(0)->IsUndefined()); 2619 obj->SetInternalField(0, v8_num(17)); 2620 CHECK_EQ(17, obj->GetInternalField(0)->Int32Value()); 2621 } 2622 2623 2624 THREADED_TEST(GlobalObjectInternalFields) { 2625 v8::Isolate* isolate = CcTest::isolate(); 2626 v8::HandleScope scope(isolate); 2627 Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(isolate); 2628 global_template->SetInternalFieldCount(1); 2629 LocalContext env(NULL, global_template); 2630 v8::Handle<v8::Object> global_proxy = env->Global(); 2631 v8::Handle<v8::Object> global = global_proxy->GetPrototype().As<v8::Object>(); 2632 CHECK_EQ(1, global->InternalFieldCount()); 2633 CHECK(global->GetInternalField(0)->IsUndefined()); 2634 global->SetInternalField(0, v8_num(17)); 2635 CHECK_EQ(17, global->GetInternalField(0)->Int32Value()); 2636 } 2637 2638 2639 THREADED_TEST(GlobalObjectHasRealIndexedProperty) { 2640 LocalContext env; 2641 v8::HandleScope scope(CcTest::isolate()); 2642 2643 v8::Local<v8::Object> global = env->Global(); 2644 global->Set(0, v8::String::NewFromUtf8(CcTest::isolate(), "value")); 2645 CHECK(global->HasRealIndexedProperty(0)); 2646 } 2647 2648 2649 static void CheckAlignedPointerInInternalField(Handle<v8::Object> obj, 2650 void* value) { 2651 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(value) & 0x1)); 2652 obj->SetAlignedPointerInInternalField(0, value); 2653 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 2654 CHECK_EQ(value, obj->GetAlignedPointerFromInternalField(0)); 2655 } 2656 2657 2658 THREADED_TEST(InternalFieldsAlignedPointers) { 2659 LocalContext env; 2660 v8::Isolate* isolate = env->GetIsolate(); 2661 v8::HandleScope scope(isolate); 2662 2663 Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); 2664 Local<v8::ObjectTemplate> instance_templ = templ->InstanceTemplate(); 2665 instance_templ->SetInternalFieldCount(1); 2666 Local<v8::Object> obj = templ->GetFunction()->NewInstance(); 2667 CHECK_EQ(1, obj->InternalFieldCount()); 2668 2669 CheckAlignedPointerInInternalField(obj, NULL); 2670 2671 int* heap_allocated = new int[100]; 2672 CheckAlignedPointerInInternalField(obj, heap_allocated); 2673 delete[] heap_allocated; 2674 2675 int stack_allocated[100]; 2676 CheckAlignedPointerInInternalField(obj, stack_allocated); 2677 2678 void* huge = reinterpret_cast<void*>(~static_cast<uintptr_t>(1)); 2679 CheckAlignedPointerInInternalField(obj, huge); 2680 2681 v8::UniquePersistent<v8::Object> persistent(isolate, obj); 2682 CHECK_EQ(1, Object::InternalFieldCount(persistent)); 2683 CHECK_EQ(huge, Object::GetAlignedPointerFromInternalField(persistent, 0)); 2684 } 2685 2686 2687 static void CheckAlignedPointerInEmbedderData(LocalContext* env, 2688 int index, 2689 void* value) { 2690 CHECK_EQ(0, static_cast<int>(reinterpret_cast<uintptr_t>(value) & 0x1)); 2691 (*env)->SetAlignedPointerInEmbedderData(index, value); 2692 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 2693 CHECK_EQ(value, (*env)->GetAlignedPointerFromEmbedderData(index)); 2694 } 2695 2696 2697 static void* AlignedTestPointer(int i) { 2698 return reinterpret_cast<void*>(i * 1234); 2699 } 2700 2701 2702 THREADED_TEST(EmbedderDataAlignedPointers) { 2703 LocalContext env; 2704 v8::HandleScope scope(env->GetIsolate()); 2705 2706 CheckAlignedPointerInEmbedderData(&env, 0, NULL); 2707 2708 int* heap_allocated = new int[100]; 2709 CheckAlignedPointerInEmbedderData(&env, 1, heap_allocated); 2710 delete[] heap_allocated; 2711 2712 int stack_allocated[100]; 2713 CheckAlignedPointerInEmbedderData(&env, 2, stack_allocated); 2714 2715 void* huge = reinterpret_cast<void*>(~static_cast<uintptr_t>(1)); 2716 CheckAlignedPointerInEmbedderData(&env, 3, huge); 2717 2718 // Test growing of the embedder data's backing store. 2719 for (int i = 0; i < 100; i++) { 2720 env->SetAlignedPointerInEmbedderData(i, AlignedTestPointer(i)); 2721 } 2722 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 2723 for (int i = 0; i < 100; i++) { 2724 CHECK_EQ(AlignedTestPointer(i), env->GetAlignedPointerFromEmbedderData(i)); 2725 } 2726 } 2727 2728 2729 static void CheckEmbedderData(LocalContext* env, 2730 int index, 2731 v8::Handle<Value> data) { 2732 (*env)->SetEmbedderData(index, data); 2733 CHECK((*env)->GetEmbedderData(index)->StrictEquals(data)); 2734 } 2735 2736 2737 THREADED_TEST(EmbedderData) { 2738 LocalContext env; 2739 v8::Isolate* isolate = env->GetIsolate(); 2740 v8::HandleScope scope(isolate); 2741 2742 CheckEmbedderData( 2743 &env, 3, 2744 v8::String::NewFromUtf8(isolate, "The quick brown fox jumps")); 2745 CheckEmbedderData(&env, 2, v8::String::NewFromUtf8(isolate, 2746 "over the lazy dog.")); 2747 CheckEmbedderData(&env, 1, v8::Number::New(isolate, 1.2345)); 2748 CheckEmbedderData(&env, 0, v8::Boolean::New(isolate, true)); 2749 } 2750 2751 2752 THREADED_TEST(IdentityHash) { 2753 LocalContext env; 2754 v8::Isolate* isolate = env->GetIsolate(); 2755 v8::HandleScope scope(isolate); 2756 2757 // Ensure that the test starts with an fresh heap to test whether the hash 2758 // code is based on the address. 2759 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 2760 Local<v8::Object> obj = v8::Object::New(isolate); 2761 int hash = obj->GetIdentityHash(); 2762 int hash1 = obj->GetIdentityHash(); 2763 CHECK_EQ(hash, hash1); 2764 int hash2 = v8::Object::New(isolate)->GetIdentityHash(); 2765 // Since the identity hash is essentially a random number two consecutive 2766 // objects should not be assigned the same hash code. If the test below fails 2767 // the random number generator should be evaluated. 2768 CHECK_NE(hash, hash2); 2769 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 2770 int hash3 = v8::Object::New(isolate)->GetIdentityHash(); 2771 // Make sure that the identity hash is not based on the initial address of 2772 // the object alone. If the test below fails the random number generator 2773 // should be evaluated. 2774 CHECK_NE(hash, hash3); 2775 int hash4 = obj->GetIdentityHash(); 2776 CHECK_EQ(hash, hash4); 2777 2778 // Check identity hashes behaviour in the presence of JS accessors. 2779 // Put a getter for 'v8::IdentityHash' on the Object's prototype: 2780 { 2781 CompileRun("Object.prototype['v8::IdentityHash'] = 42;\n"); 2782 Local<v8::Object> o1 = v8::Object::New(isolate); 2783 Local<v8::Object> o2 = v8::Object::New(isolate); 2784 CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash()); 2785 } 2786 { 2787 CompileRun( 2788 "function cnst() { return 42; };\n" 2789 "Object.prototype.__defineGetter__('v8::IdentityHash', cnst);\n"); 2790 Local<v8::Object> o1 = v8::Object::New(isolate); 2791 Local<v8::Object> o2 = v8::Object::New(isolate); 2792 CHECK_NE(o1->GetIdentityHash(), o2->GetIdentityHash()); 2793 } 2794 } 2795 2796 2797 THREADED_TEST(GlobalProxyIdentityHash) { 2798 LocalContext env; 2799 v8::Isolate* isolate = env->GetIsolate(); 2800 v8::HandleScope scope(isolate); 2801 Handle<Object> global_proxy = env->Global(); 2802 int hash1 = global_proxy->GetIdentityHash(); 2803 // Hash should be retained after being detached. 2804 env->DetachGlobal(); 2805 int hash2 = global_proxy->GetIdentityHash(); 2806 CHECK_EQ(hash1, hash2); 2807 { 2808 // Re-attach global proxy to a new context, hash should stay the same. 2809 LocalContext env2(NULL, Handle<ObjectTemplate>(), global_proxy); 2810 int hash3 = global_proxy->GetIdentityHash(); 2811 CHECK_EQ(hash1, hash3); 2812 } 2813 } 2814 2815 2816 THREADED_TEST(SymbolProperties) { 2817 LocalContext env; 2818 v8::Isolate* isolate = env->GetIsolate(); 2819 v8::HandleScope scope(isolate); 2820 2821 v8::Local<v8::Object> obj = v8::Object::New(isolate); 2822 v8::Local<v8::Symbol> sym1 = v8::Symbol::New(isolate); 2823 v8::Local<v8::Symbol> sym2 = 2824 v8::Symbol::New(isolate, v8_str("my-symbol")); 2825 v8::Local<v8::Symbol> sym3 = 2826 v8::Symbol::New(isolate, v8_str("sym3")); 2827 2828 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 2829 2830 // Check basic symbol functionality. 2831 CHECK(sym1->IsSymbol()); 2832 CHECK(sym2->IsSymbol()); 2833 CHECK(!obj->IsSymbol()); 2834 2835 CHECK(sym1->Equals(sym1)); 2836 CHECK(sym2->Equals(sym2)); 2837 CHECK(!sym1->Equals(sym2)); 2838 CHECK(!sym2->Equals(sym1)); 2839 CHECK(sym1->StrictEquals(sym1)); 2840 CHECK(sym2->StrictEquals(sym2)); 2841 CHECK(!sym1->StrictEquals(sym2)); 2842 CHECK(!sym2->StrictEquals(sym1)); 2843 2844 CHECK(sym2->Name()->Equals(v8_str("my-symbol"))); 2845 2846 v8::Local<v8::Value> sym_val = sym2; 2847 CHECK(sym_val->IsSymbol()); 2848 CHECK(sym_val->Equals(sym2)); 2849 CHECK(sym_val->StrictEquals(sym2)); 2850 CHECK(v8::Symbol::Cast(*sym_val)->Equals(sym2)); 2851 2852 v8::Local<v8::Value> sym_obj = v8::SymbolObject::New(isolate, sym2); 2853 CHECK(sym_obj->IsSymbolObject()); 2854 CHECK(!sym2->IsSymbolObject()); 2855 CHECK(!obj->IsSymbolObject()); 2856 CHECK(!sym_obj->Equals(sym2)); 2857 CHECK(!sym_obj->StrictEquals(sym2)); 2858 CHECK(v8::SymbolObject::Cast(*sym_obj)->Equals(sym_obj)); 2859 CHECK(v8::SymbolObject::Cast(*sym_obj)->ValueOf()->Equals(sym2)); 2860 2861 // Make sure delete of a non-existent symbol property works. 2862 CHECK(obj->Delete(sym1)); 2863 CHECK(!obj->Has(sym1)); 2864 2865 CHECK(obj->Set(sym1, v8::Integer::New(isolate, 1503))); 2866 CHECK(obj->Has(sym1)); 2867 CHECK_EQ(1503, obj->Get(sym1)->Int32Value()); 2868 CHECK(obj->Set(sym1, v8::Integer::New(isolate, 2002))); 2869 CHECK(obj->Has(sym1)); 2870 CHECK_EQ(2002, obj->Get(sym1)->Int32Value()); 2871 CHECK_EQ(v8::None, obj->GetPropertyAttributes(sym1)); 2872 2873 CHECK_EQ(0, obj->GetOwnPropertyNames()->Length()); 2874 int num_props = obj->GetPropertyNames()->Length(); 2875 CHECK(obj->Set(v8::String::NewFromUtf8(isolate, "bla"), 2876 v8::Integer::New(isolate, 20))); 2877 CHECK_EQ(1, obj->GetOwnPropertyNames()->Length()); 2878 CHECK_EQ(num_props + 1, obj->GetPropertyNames()->Length()); 2879 2880 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 2881 2882 CHECK(obj->SetAccessor(sym3, SymbolAccessorGetter, SymbolAccessorSetter)); 2883 CHECK(obj->Get(sym3)->IsUndefined()); 2884 CHECK(obj->Set(sym3, v8::Integer::New(isolate, 42))); 2885 CHECK(obj->Get(sym3)->Equals(v8::Integer::New(isolate, 42))); 2886 CHECK(obj->Get(v8::String::NewFromUtf8(isolate, "accessor_sym3"))->Equals( 2887 v8::Integer::New(isolate, 42))); 2888 2889 // Add another property and delete it afterwards to force the object in 2890 // slow case. 2891 CHECK(obj->Set(sym2, v8::Integer::New(isolate, 2008))); 2892 CHECK_EQ(2002, obj->Get(sym1)->Int32Value()); 2893 CHECK_EQ(2008, obj->Get(sym2)->Int32Value()); 2894 CHECK_EQ(2002, obj->Get(sym1)->Int32Value()); 2895 CHECK_EQ(2, obj->GetOwnPropertyNames()->Length()); 2896 2897 CHECK(obj->Has(sym1)); 2898 CHECK(obj->Has(sym2)); 2899 CHECK(obj->Has(sym3)); 2900 CHECK(obj->Has(v8::String::NewFromUtf8(isolate, "accessor_sym3"))); 2901 CHECK(obj->Delete(sym2)); 2902 CHECK(obj->Has(sym1)); 2903 CHECK(!obj->Has(sym2)); 2904 CHECK(obj->Has(sym3)); 2905 CHECK(obj->Has(v8::String::NewFromUtf8(isolate, "accessor_sym3"))); 2906 CHECK_EQ(2002, obj->Get(sym1)->Int32Value()); 2907 CHECK(obj->Get(sym3)->Equals(v8::Integer::New(isolate, 42))); 2908 CHECK(obj->Get(v8::String::NewFromUtf8(isolate, "accessor_sym3"))->Equals( 2909 v8::Integer::New(isolate, 42))); 2910 CHECK_EQ(2, obj->GetOwnPropertyNames()->Length()); 2911 2912 // Symbol properties are inherited. 2913 v8::Local<v8::Object> child = v8::Object::New(isolate); 2914 child->SetPrototype(obj); 2915 CHECK(child->Has(sym1)); 2916 CHECK_EQ(2002, child->Get(sym1)->Int32Value()); 2917 CHECK(obj->Get(sym3)->Equals(v8::Integer::New(isolate, 42))); 2918 CHECK(obj->Get(v8::String::NewFromUtf8(isolate, "accessor_sym3"))->Equals( 2919 v8::Integer::New(isolate, 42))); 2920 CHECK_EQ(0, child->GetOwnPropertyNames()->Length()); 2921 } 2922 2923 2924 THREADED_TEST(SymbolTemplateProperties) { 2925 LocalContext env; 2926 v8::Isolate* isolate = env->GetIsolate(); 2927 v8::HandleScope scope(isolate); 2928 v8::Local<v8::FunctionTemplate> foo = v8::FunctionTemplate::New(isolate); 2929 v8::Local<v8::Name> name = v8::Symbol::New(isolate); 2930 CHECK(!name.IsEmpty()); 2931 foo->PrototypeTemplate()->Set(name, v8::FunctionTemplate::New(isolate)); 2932 v8::Local<v8::Object> new_instance = foo->InstanceTemplate()->NewInstance(); 2933 CHECK(!new_instance.IsEmpty()); 2934 CHECK(new_instance->Has(name)); 2935 } 2936 2937 2938 THREADED_TEST(PrivateProperties) { 2939 LocalContext env; 2940 v8::Isolate* isolate = env->GetIsolate(); 2941 v8::HandleScope scope(isolate); 2942 2943 v8::Local<v8::Object> obj = v8::Object::New(isolate); 2944 v8::Local<v8::Private> priv1 = v8::Private::New(isolate); 2945 v8::Local<v8::Private> priv2 = 2946 v8::Private::New(isolate, v8_str("my-private")); 2947 2948 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 2949 2950 CHECK(priv2->Name()->Equals(v8::String::NewFromUtf8(isolate, "my-private"))); 2951 2952 // Make sure delete of a non-existent private symbol property works. 2953 CHECK(obj->DeletePrivate(priv1)); 2954 CHECK(!obj->HasPrivate(priv1)); 2955 2956 CHECK(obj->SetPrivate(priv1, v8::Integer::New(isolate, 1503))); 2957 CHECK(obj->HasPrivate(priv1)); 2958 CHECK_EQ(1503, obj->GetPrivate(priv1)->Int32Value()); 2959 CHECK(obj->SetPrivate(priv1, v8::Integer::New(isolate, 2002))); 2960 CHECK(obj->HasPrivate(priv1)); 2961 CHECK_EQ(2002, obj->GetPrivate(priv1)->Int32Value()); 2962 2963 CHECK_EQ(0, obj->GetOwnPropertyNames()->Length()); 2964 int num_props = obj->GetPropertyNames()->Length(); 2965 CHECK(obj->Set(v8::String::NewFromUtf8(isolate, "bla"), 2966 v8::Integer::New(isolate, 20))); 2967 CHECK_EQ(1, obj->GetOwnPropertyNames()->Length()); 2968 CHECK_EQ(num_props + 1, obj->GetPropertyNames()->Length()); 2969 2970 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 2971 2972 // Add another property and delete it afterwards to force the object in 2973 // slow case. 2974 CHECK(obj->SetPrivate(priv2, v8::Integer::New(isolate, 2008))); 2975 CHECK_EQ(2002, obj->GetPrivate(priv1)->Int32Value()); 2976 CHECK_EQ(2008, obj->GetPrivate(priv2)->Int32Value()); 2977 CHECK_EQ(2002, obj->GetPrivate(priv1)->Int32Value()); 2978 CHECK_EQ(1, obj->GetOwnPropertyNames()->Length()); 2979 2980 CHECK(obj->HasPrivate(priv1)); 2981 CHECK(obj->HasPrivate(priv2)); 2982 CHECK(obj->DeletePrivate(priv2)); 2983 CHECK(obj->HasPrivate(priv1)); 2984 CHECK(!obj->HasPrivate(priv2)); 2985 CHECK_EQ(2002, obj->GetPrivate(priv1)->Int32Value()); 2986 CHECK_EQ(1, obj->GetOwnPropertyNames()->Length()); 2987 2988 // Private properties are inherited (for the time being). 2989 v8::Local<v8::Object> child = v8::Object::New(isolate); 2990 child->SetPrototype(obj); 2991 CHECK(child->HasPrivate(priv1)); 2992 CHECK_EQ(2002, child->GetPrivate(priv1)->Int32Value()); 2993 CHECK_EQ(0, child->GetOwnPropertyNames()->Length()); 2994 } 2995 2996 2997 THREADED_TEST(GlobalSymbols) { 2998 LocalContext env; 2999 v8::Isolate* isolate = env->GetIsolate(); 3000 v8::HandleScope scope(isolate); 3001 3002 v8::Local<String> name = v8_str("my-symbol"); 3003 v8::Local<v8::Symbol> glob = v8::Symbol::For(isolate, name); 3004 v8::Local<v8::Symbol> glob2 = v8::Symbol::For(isolate, name); 3005 CHECK(glob2->SameValue(glob)); 3006 3007 v8::Local<v8::Symbol> glob_api = v8::Symbol::ForApi(isolate, name); 3008 v8::Local<v8::Symbol> glob_api2 = v8::Symbol::ForApi(isolate, name); 3009 CHECK(glob_api2->SameValue(glob_api)); 3010 CHECK(!glob_api->SameValue(glob)); 3011 3012 v8::Local<v8::Symbol> sym = v8::Symbol::New(isolate, name); 3013 CHECK(!sym->SameValue(glob)); 3014 3015 CompileRun("var sym2 = Symbol.for('my-symbol')"); 3016 v8::Local<Value> sym2 = env->Global()->Get(v8_str("sym2")); 3017 CHECK(sym2->SameValue(glob)); 3018 CHECK(!sym2->SameValue(glob_api)); 3019 } 3020 3021 3022 static void CheckWellKnownSymbol(v8::Local<v8::Symbol>(*getter)(v8::Isolate*), 3023 const char* name) { 3024 LocalContext env; 3025 v8::Isolate* isolate = env->GetIsolate(); 3026 v8::HandleScope scope(isolate); 3027 3028 v8::Local<v8::Symbol> symbol = getter(isolate); 3029 std::string script = std::string("var sym = ") + name; 3030 CompileRun(script.c_str()); 3031 v8::Local<Value> value = env->Global()->Get(v8_str("sym")); 3032 3033 CHECK(!value.IsEmpty()); 3034 CHECK(!symbol.IsEmpty()); 3035 CHECK(value->SameValue(symbol)); 3036 } 3037 3038 3039 THREADED_TEST(WellKnownSymbols) { 3040 CheckWellKnownSymbol(v8::Symbol::GetIterator, "Symbol.iterator"); 3041 CheckWellKnownSymbol(v8::Symbol::GetUnscopables, "Symbol.unscopables"); 3042 } 3043 3044 3045 THREADED_TEST(GlobalPrivates) { 3046 LocalContext env; 3047 v8::Isolate* isolate = env->GetIsolate(); 3048 v8::HandleScope scope(isolate); 3049 3050 v8::Local<String> name = v8_str("my-private"); 3051 v8::Local<v8::Private> glob = v8::Private::ForApi(isolate, name); 3052 v8::Local<v8::Object> obj = v8::Object::New(isolate); 3053 CHECK(obj->SetPrivate(glob, v8::Integer::New(isolate, 3))); 3054 3055 v8::Local<v8::Private> glob2 = v8::Private::ForApi(isolate, name); 3056 CHECK(obj->HasPrivate(glob2)); 3057 3058 v8::Local<v8::Private> priv = v8::Private::New(isolate, name); 3059 CHECK(!obj->HasPrivate(priv)); 3060 3061 CompileRun("var intern = %CreateGlobalPrivateSymbol('my-private')"); 3062 v8::Local<Value> intern = env->Global()->Get(v8_str("intern")); 3063 CHECK(!obj->Has(intern)); 3064 } 3065 3066 3067 class ScopedArrayBufferContents { 3068 public: 3069 explicit ScopedArrayBufferContents( 3070 const v8::ArrayBuffer::Contents& contents) 3071 : contents_(contents) {} 3072 ~ScopedArrayBufferContents() { free(contents_.Data()); } 3073 void* Data() const { return contents_.Data(); } 3074 size_t ByteLength() const { return contents_.ByteLength(); } 3075 private: 3076 const v8::ArrayBuffer::Contents contents_; 3077 }; 3078 3079 template <typename T> 3080 static void CheckInternalFieldsAreZero(v8::Handle<T> value) { 3081 CHECK_EQ(T::kInternalFieldCount, value->InternalFieldCount()); 3082 for (int i = 0; i < value->InternalFieldCount(); i++) { 3083 CHECK_EQ(0, value->GetInternalField(i)->Int32Value()); 3084 } 3085 } 3086 3087 3088 THREADED_TEST(ArrayBuffer_ApiInternalToExternal) { 3089 LocalContext env; 3090 v8::Isolate* isolate = env->GetIsolate(); 3091 v8::HandleScope handle_scope(isolate); 3092 3093 Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 1024); 3094 CheckInternalFieldsAreZero(ab); 3095 CHECK_EQ(1024, static_cast<int>(ab->ByteLength())); 3096 CHECK(!ab->IsExternal()); 3097 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 3098 3099 ScopedArrayBufferContents ab_contents(ab->Externalize()); 3100 CHECK(ab->IsExternal()); 3101 3102 CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength())); 3103 uint8_t* data = static_cast<uint8_t*>(ab_contents.Data()); 3104 DCHECK(data != NULL); 3105 env->Global()->Set(v8_str("ab"), ab); 3106 3107 v8::Handle<v8::Value> result = CompileRun("ab.byteLength"); 3108 CHECK_EQ(1024, result->Int32Value()); 3109 3110 result = CompileRun("var u8 = new Uint8Array(ab);" 3111 "u8[0] = 0xFF;" 3112 "u8[1] = 0xAA;" 3113 "u8.length"); 3114 CHECK_EQ(1024, result->Int32Value()); 3115 CHECK_EQ(0xFF, data[0]); 3116 CHECK_EQ(0xAA, data[1]); 3117 data[0] = 0xCC; 3118 data[1] = 0x11; 3119 result = CompileRun("u8[0] + u8[1]"); 3120 CHECK_EQ(0xDD, result->Int32Value()); 3121 } 3122 3123 3124 THREADED_TEST(ArrayBuffer_JSInternalToExternal) { 3125 LocalContext env; 3126 v8::Isolate* isolate = env->GetIsolate(); 3127 v8::HandleScope handle_scope(isolate); 3128 3129 3130 v8::Local<v8::Value> result = 3131 CompileRun("var ab1 = new ArrayBuffer(2);" 3132 "var u8_a = new Uint8Array(ab1);" 3133 "u8_a[0] = 0xAA;" 3134 "u8_a[1] = 0xFF; u8_a.buffer"); 3135 Local<v8::ArrayBuffer> ab1 = Local<v8::ArrayBuffer>::Cast(result); 3136 CheckInternalFieldsAreZero(ab1); 3137 CHECK_EQ(2, static_cast<int>(ab1->ByteLength())); 3138 CHECK(!ab1->IsExternal()); 3139 ScopedArrayBufferContents ab1_contents(ab1->Externalize()); 3140 CHECK(ab1->IsExternal()); 3141 3142 result = CompileRun("ab1.byteLength"); 3143 CHECK_EQ(2, result->Int32Value()); 3144 result = CompileRun("u8_a[0]"); 3145 CHECK_EQ(0xAA, result->Int32Value()); 3146 result = CompileRun("u8_a[1]"); 3147 CHECK_EQ(0xFF, result->Int32Value()); 3148 result = CompileRun("var u8_b = new Uint8Array(ab1);" 3149 "u8_b[0] = 0xBB;" 3150 "u8_a[0]"); 3151 CHECK_EQ(0xBB, result->Int32Value()); 3152 result = CompileRun("u8_b[1]"); 3153 CHECK_EQ(0xFF, result->Int32Value()); 3154 3155 CHECK_EQ(2, static_cast<int>(ab1_contents.ByteLength())); 3156 uint8_t* ab1_data = static_cast<uint8_t*>(ab1_contents.Data()); 3157 CHECK_EQ(0xBB, ab1_data[0]); 3158 CHECK_EQ(0xFF, ab1_data[1]); 3159 ab1_data[0] = 0xCC; 3160 ab1_data[1] = 0x11; 3161 result = CompileRun("u8_a[0] + u8_a[1]"); 3162 CHECK_EQ(0xDD, result->Int32Value()); 3163 } 3164 3165 3166 THREADED_TEST(ArrayBuffer_External) { 3167 LocalContext env; 3168 v8::Isolate* isolate = env->GetIsolate(); 3169 v8::HandleScope handle_scope(isolate); 3170 3171 i::ScopedVector<uint8_t> my_data(100); 3172 memset(my_data.start(), 0, 100); 3173 Local<v8::ArrayBuffer> ab3 = 3174 v8::ArrayBuffer::New(isolate, my_data.start(), 100); 3175 CheckInternalFieldsAreZero(ab3); 3176 CHECK_EQ(100, static_cast<int>(ab3->ByteLength())); 3177 CHECK(ab3->IsExternal()); 3178 3179 env->Global()->Set(v8_str("ab3"), ab3); 3180 3181 v8::Handle<v8::Value> result = CompileRun("ab3.byteLength"); 3182 CHECK_EQ(100, result->Int32Value()); 3183 3184 result = CompileRun("var u8_b = new Uint8Array(ab3);" 3185 "u8_b[0] = 0xBB;" 3186 "u8_b[1] = 0xCC;" 3187 "u8_b.length"); 3188 CHECK_EQ(100, result->Int32Value()); 3189 CHECK_EQ(0xBB, my_data[0]); 3190 CHECK_EQ(0xCC, my_data[1]); 3191 my_data[0] = 0xCC; 3192 my_data[1] = 0x11; 3193 result = CompileRun("u8_b[0] + u8_b[1]"); 3194 CHECK_EQ(0xDD, result->Int32Value()); 3195 } 3196 3197 3198 static void CheckDataViewIsNeutered(v8::Handle<v8::DataView> dv) { 3199 CHECK_EQ(0, static_cast<int>(dv->ByteLength())); 3200 CHECK_EQ(0, static_cast<int>(dv->ByteOffset())); 3201 } 3202 3203 3204 static void CheckIsNeutered(v8::Handle<v8::TypedArray> ta) { 3205 CHECK_EQ(0, static_cast<int>(ta->ByteLength())); 3206 CHECK_EQ(0, static_cast<int>(ta->Length())); 3207 CHECK_EQ(0, static_cast<int>(ta->ByteOffset())); 3208 } 3209 3210 3211 static void CheckIsTypedArrayVarNeutered(const char* name) { 3212 i::ScopedVector<char> source(1024); 3213 i::SNPrintF(source, 3214 "%s.byteLength == 0 && %s.byteOffset == 0 && %s.length == 0", 3215 name, name, name); 3216 CHECK(CompileRun(source.start())->IsTrue()); 3217 v8::Handle<v8::TypedArray> ta = 3218 v8::Handle<v8::TypedArray>::Cast(CompileRun(name)); 3219 CheckIsNeutered(ta); 3220 } 3221 3222 3223 template <typename TypedArray, int kElementSize> 3224 static Handle<TypedArray> CreateAndCheck(Handle<v8::ArrayBuffer> ab, 3225 int byteOffset, 3226 int length) { 3227 v8::Handle<TypedArray> ta = TypedArray::New(ab, byteOffset, length); 3228 CheckInternalFieldsAreZero<v8::ArrayBufferView>(ta); 3229 CHECK_EQ(byteOffset, static_cast<int>(ta->ByteOffset())); 3230 CHECK_EQ(length, static_cast<int>(ta->Length())); 3231 CHECK_EQ(length * kElementSize, static_cast<int>(ta->ByteLength())); 3232 return ta; 3233 } 3234 3235 3236 THREADED_TEST(ArrayBuffer_NeuteringApi) { 3237 LocalContext env; 3238 v8::Isolate* isolate = env->GetIsolate(); 3239 v8::HandleScope handle_scope(isolate); 3240 3241 v8::Handle<v8::ArrayBuffer> buffer = v8::ArrayBuffer::New(isolate, 1024); 3242 3243 v8::Handle<v8::Uint8Array> u8a = 3244 CreateAndCheck<v8::Uint8Array, 1>(buffer, 1, 1023); 3245 v8::Handle<v8::Uint8ClampedArray> u8c = 3246 CreateAndCheck<v8::Uint8ClampedArray, 1>(buffer, 1, 1023); 3247 v8::Handle<v8::Int8Array> i8a = 3248 CreateAndCheck<v8::Int8Array, 1>(buffer, 1, 1023); 3249 3250 v8::Handle<v8::Uint16Array> u16a = 3251 CreateAndCheck<v8::Uint16Array, 2>(buffer, 2, 511); 3252 v8::Handle<v8::Int16Array> i16a = 3253 CreateAndCheck<v8::Int16Array, 2>(buffer, 2, 511); 3254 3255 v8::Handle<v8::Uint32Array> u32a = 3256 CreateAndCheck<v8::Uint32Array, 4>(buffer, 4, 255); 3257 v8::Handle<v8::Int32Array> i32a = 3258 CreateAndCheck<v8::Int32Array, 4>(buffer, 4, 255); 3259 3260 v8::Handle<v8::Float32Array> f32a = 3261 CreateAndCheck<v8::Float32Array, 4>(buffer, 4, 255); 3262 v8::Handle<v8::Float64Array> f64a = 3263 CreateAndCheck<v8::Float64Array, 8>(buffer, 8, 127); 3264 3265 v8::Handle<v8::DataView> dv = v8::DataView::New(buffer, 1, 1023); 3266 CheckInternalFieldsAreZero<v8::ArrayBufferView>(dv); 3267 CHECK_EQ(1, static_cast<int>(dv->ByteOffset())); 3268 CHECK_EQ(1023, static_cast<int>(dv->ByteLength())); 3269 3270 ScopedArrayBufferContents contents(buffer->Externalize()); 3271 buffer->Neuter(); 3272 CHECK_EQ(0, static_cast<int>(buffer->ByteLength())); 3273 CheckIsNeutered(u8a); 3274 CheckIsNeutered(u8c); 3275 CheckIsNeutered(i8a); 3276 CheckIsNeutered(u16a); 3277 CheckIsNeutered(i16a); 3278 CheckIsNeutered(u32a); 3279 CheckIsNeutered(i32a); 3280 CheckIsNeutered(f32a); 3281 CheckIsNeutered(f64a); 3282 CheckDataViewIsNeutered(dv); 3283 } 3284 3285 3286 THREADED_TEST(ArrayBuffer_NeuteringScript) { 3287 LocalContext env; 3288 v8::Isolate* isolate = env->GetIsolate(); 3289 v8::HandleScope handle_scope(isolate); 3290 3291 CompileRun( 3292 "var ab = new ArrayBuffer(1024);" 3293 "var u8a = new Uint8Array(ab, 1, 1023);" 3294 "var u8c = new Uint8ClampedArray(ab, 1, 1023);" 3295 "var i8a = new Int8Array(ab, 1, 1023);" 3296 "var u16a = new Uint16Array(ab, 2, 511);" 3297 "var i16a = new Int16Array(ab, 2, 511);" 3298 "var u32a = new Uint32Array(ab, 4, 255);" 3299 "var i32a = new Int32Array(ab, 4, 255);" 3300 "var f32a = new Float32Array(ab, 4, 255);" 3301 "var f64a = new Float64Array(ab, 8, 127);" 3302 "var dv = new DataView(ab, 1, 1023);"); 3303 3304 v8::Handle<v8::ArrayBuffer> ab = 3305 Local<v8::ArrayBuffer>::Cast(CompileRun("ab")); 3306 3307 v8::Handle<v8::DataView> dv = 3308 v8::Handle<v8::DataView>::Cast(CompileRun("dv")); 3309 3310 ScopedArrayBufferContents contents(ab->Externalize()); 3311 ab->Neuter(); 3312 CHECK_EQ(0, static_cast<int>(ab->ByteLength())); 3313 CHECK_EQ(0, CompileRun("ab.byteLength")->Int32Value()); 3314 3315 CheckIsTypedArrayVarNeutered("u8a"); 3316 CheckIsTypedArrayVarNeutered("u8c"); 3317 CheckIsTypedArrayVarNeutered("i8a"); 3318 CheckIsTypedArrayVarNeutered("u16a"); 3319 CheckIsTypedArrayVarNeutered("i16a"); 3320 CheckIsTypedArrayVarNeutered("u32a"); 3321 CheckIsTypedArrayVarNeutered("i32a"); 3322 CheckIsTypedArrayVarNeutered("f32a"); 3323 CheckIsTypedArrayVarNeutered("f64a"); 3324 3325 CHECK(CompileRun("dv.byteLength == 0 && dv.byteOffset == 0")->IsTrue()); 3326 CheckDataViewIsNeutered(dv); 3327 } 3328 3329 3330 3331 THREADED_TEST(HiddenProperties) { 3332 LocalContext env; 3333 v8::Isolate* isolate = env->GetIsolate(); 3334 v8::HandleScope scope(isolate); 3335 3336 v8::Local<v8::Object> obj = v8::Object::New(env->GetIsolate()); 3337 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 3338 v8::Local<v8::String> empty = v8_str(""); 3339 v8::Local<v8::String> prop_name = v8_str("prop_name"); 3340 3341 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 3342 3343 // Make sure delete of a non-existent hidden value works 3344 CHECK(obj->DeleteHiddenValue(key)); 3345 3346 CHECK(obj->SetHiddenValue(key, v8::Integer::New(isolate, 1503))); 3347 CHECK_EQ(1503, obj->GetHiddenValue(key)->Int32Value()); 3348 CHECK(obj->SetHiddenValue(key, v8::Integer::New(isolate, 2002))); 3349 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 3350 3351 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 3352 3353 // Make sure we do not find the hidden property. 3354 CHECK(!obj->Has(empty)); 3355 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 3356 CHECK(obj->Get(empty)->IsUndefined()); 3357 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 3358 CHECK(obj->Set(empty, v8::Integer::New(isolate, 2003))); 3359 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 3360 CHECK_EQ(2003, obj->Get(empty)->Int32Value()); 3361 3362 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 3363 3364 // Add another property and delete it afterwards to force the object in 3365 // slow case. 3366 CHECK(obj->Set(prop_name, v8::Integer::New(isolate, 2008))); 3367 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 3368 CHECK_EQ(2008, obj->Get(prop_name)->Int32Value()); 3369 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 3370 CHECK(obj->Delete(prop_name)); 3371 CHECK_EQ(2002, obj->GetHiddenValue(key)->Int32Value()); 3372 3373 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 3374 3375 CHECK(obj->SetHiddenValue(key, Handle<Value>())); 3376 CHECK(obj->GetHiddenValue(key).IsEmpty()); 3377 3378 CHECK(obj->SetHiddenValue(key, v8::Integer::New(isolate, 2002))); 3379 CHECK(obj->DeleteHiddenValue(key)); 3380 CHECK(obj->GetHiddenValue(key).IsEmpty()); 3381 } 3382 3383 3384 THREADED_TEST(Regress97784) { 3385 // Regression test for crbug.com/97784 3386 // Messing with the Object.prototype should not have effect on 3387 // hidden properties. 3388 LocalContext env; 3389 v8::HandleScope scope(env->GetIsolate()); 3390 3391 v8::Local<v8::Object> obj = v8::Object::New(env->GetIsolate()); 3392 v8::Local<v8::String> key = v8_str("hidden"); 3393 3394 CompileRun( 3395 "set_called = false;" 3396 "Object.defineProperty(" 3397 " Object.prototype," 3398 " 'hidden'," 3399 " {get: function() { return 45; }," 3400 " set: function() { set_called = true; }})"); 3401 3402 CHECK(obj->GetHiddenValue(key).IsEmpty()); 3403 // Make sure that the getter and setter from Object.prototype is not invoked. 3404 // If it did we would have full access to the hidden properties in 3405 // the accessor. 3406 CHECK(obj->SetHiddenValue(key, v8::Integer::New(env->GetIsolate(), 42))); 3407 ExpectFalse("set_called"); 3408 CHECK_EQ(42, obj->GetHiddenValue(key)->Int32Value()); 3409 } 3410 3411 3412 static bool interceptor_for_hidden_properties_called; 3413 static void InterceptorForHiddenProperties( 3414 Local<String> name, const v8::PropertyCallbackInfo<v8::Value>& info) { 3415 interceptor_for_hidden_properties_called = true; 3416 } 3417 3418 3419 THREADED_TEST(HiddenPropertiesWithInterceptors) { 3420 LocalContext context; 3421 v8::Isolate* isolate = context->GetIsolate(); 3422 v8::HandleScope scope(isolate); 3423 3424 interceptor_for_hidden_properties_called = false; 3425 3426 v8::Local<v8::String> key = v8_str("api-test::hidden-key"); 3427 3428 // Associate an interceptor with an object and start setting hidden values. 3429 Local<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(isolate); 3430 Local<v8::ObjectTemplate> instance_templ = fun_templ->InstanceTemplate(); 3431 instance_templ->SetNamedPropertyHandler(InterceptorForHiddenProperties); 3432 Local<v8::Function> function = fun_templ->GetFunction(); 3433 Local<v8::Object> obj = function->NewInstance(); 3434 CHECK(obj->SetHiddenValue(key, v8::Integer::New(isolate, 2302))); 3435 CHECK_EQ(2302, obj->GetHiddenValue(key)->Int32Value()); 3436 CHECK(!interceptor_for_hidden_properties_called); 3437 } 3438 3439 3440 THREADED_TEST(External) { 3441 v8::HandleScope scope(CcTest::isolate()); 3442 int x = 3; 3443 Local<v8::External> ext = v8::External::New(CcTest::isolate(), &x); 3444 LocalContext env; 3445 env->Global()->Set(v8_str("ext"), ext); 3446 Local<Value> reext_obj = CompileRun("this.ext"); 3447 v8::Handle<v8::External> reext = reext_obj.As<v8::External>(); 3448 int* ptr = static_cast<int*>(reext->Value()); 3449 CHECK_EQ(x, 3); 3450 *ptr = 10; 3451 CHECK_EQ(x, 10); 3452 3453 // Make sure unaligned pointers are wrapped properly. 3454 char* data = i::StrDup("0123456789"); 3455 Local<v8::Value> zero = v8::External::New(CcTest::isolate(), &data[0]); 3456 Local<v8::Value> one = v8::External::New(CcTest::isolate(), &data[1]); 3457 Local<v8::Value> two = v8::External::New(CcTest::isolate(), &data[2]); 3458 Local<v8::Value> three = v8::External::New(CcTest::isolate(), &data[3]); 3459 3460 char* char_ptr = reinterpret_cast<char*>(v8::External::Cast(*zero)->Value()); 3461 CHECK_EQ('0', *char_ptr); 3462 char_ptr = reinterpret_cast<char*>(v8::External::Cast(*one)->Value()); 3463 CHECK_EQ('1', *char_ptr); 3464 char_ptr = reinterpret_cast<char*>(v8::External::Cast(*two)->Value()); 3465 CHECK_EQ('2', *char_ptr); 3466 char_ptr = reinterpret_cast<char*>(v8::External::Cast(*three)->Value()); 3467 CHECK_EQ('3', *char_ptr); 3468 i::DeleteArray(data); 3469 } 3470 3471 3472 THREADED_TEST(GlobalHandle) { 3473 v8::Isolate* isolate = CcTest::isolate(); 3474 v8::Persistent<String> global; 3475 { 3476 v8::HandleScope scope(isolate); 3477 global.Reset(isolate, v8_str("str")); 3478 } 3479 { 3480 v8::HandleScope scope(isolate); 3481 CHECK_EQ(v8::Local<String>::New(isolate, global)->Length(), 3); 3482 } 3483 global.Reset(); 3484 { 3485 v8::HandleScope scope(isolate); 3486 global.Reset(isolate, v8_str("str")); 3487 } 3488 { 3489 v8::HandleScope scope(isolate); 3490 CHECK_EQ(v8::Local<String>::New(isolate, global)->Length(), 3); 3491 } 3492 global.Reset(); 3493 } 3494 3495 3496 THREADED_TEST(ResettingGlobalHandle) { 3497 v8::Isolate* isolate = CcTest::isolate(); 3498 v8::Persistent<String> global; 3499 { 3500 v8::HandleScope scope(isolate); 3501 global.Reset(isolate, v8_str("str")); 3502 } 3503 v8::internal::GlobalHandles* global_handles = 3504 reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); 3505 int initial_handle_count = global_handles->global_handles_count(); 3506 { 3507 v8::HandleScope scope(isolate); 3508 CHECK_EQ(v8::Local<String>::New(isolate, global)->Length(), 3); 3509 } 3510 { 3511 v8::HandleScope scope(isolate); 3512 global.Reset(isolate, v8_str("longer")); 3513 } 3514 CHECK_EQ(global_handles->global_handles_count(), initial_handle_count); 3515 { 3516 v8::HandleScope scope(isolate); 3517 CHECK_EQ(v8::Local<String>::New(isolate, global)->Length(), 6); 3518 } 3519 global.Reset(); 3520 CHECK_EQ(global_handles->global_handles_count(), initial_handle_count - 1); 3521 } 3522 3523 3524 THREADED_TEST(ResettingGlobalHandleToEmpty) { 3525 v8::Isolate* isolate = CcTest::isolate(); 3526 v8::Persistent<String> global; 3527 { 3528 v8::HandleScope scope(isolate); 3529 global.Reset(isolate, v8_str("str")); 3530 } 3531 v8::internal::GlobalHandles* global_handles = 3532 reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); 3533 int initial_handle_count = global_handles->global_handles_count(); 3534 { 3535 v8::HandleScope scope(isolate); 3536 CHECK_EQ(v8::Local<String>::New(isolate, global)->Length(), 3); 3537 } 3538 { 3539 v8::HandleScope scope(isolate); 3540 Local<String> empty; 3541 global.Reset(isolate, empty); 3542 } 3543 CHECK(global.IsEmpty()); 3544 CHECK_EQ(global_handles->global_handles_count(), initial_handle_count - 1); 3545 } 3546 3547 3548 template<class T> 3549 static v8::UniquePersistent<T> PassUnique(v8::UniquePersistent<T> unique) { 3550 return unique.Pass(); 3551 } 3552 3553 3554 template<class T> 3555 static v8::UniquePersistent<T> ReturnUnique(v8::Isolate* isolate, 3556 const v8::Persistent<T> & global) { 3557 v8::UniquePersistent<String> unique(isolate, global); 3558 return unique.Pass(); 3559 } 3560 3561 3562 THREADED_TEST(UniquePersistent) { 3563 v8::Isolate* isolate = CcTest::isolate(); 3564 v8::Persistent<String> global; 3565 { 3566 v8::HandleScope scope(isolate); 3567 global.Reset(isolate, v8_str("str")); 3568 } 3569 v8::internal::GlobalHandles* global_handles = 3570 reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); 3571 int initial_handle_count = global_handles->global_handles_count(); 3572 { 3573 v8::UniquePersistent<String> unique(isolate, global); 3574 CHECK_EQ(initial_handle_count + 1, global_handles->global_handles_count()); 3575 // Test assignment via Pass 3576 { 3577 v8::UniquePersistent<String> copy = unique.Pass(); 3578 CHECK(unique.IsEmpty()); 3579 CHECK(copy == global); 3580 CHECK_EQ(initial_handle_count + 1, 3581 global_handles->global_handles_count()); 3582 unique = copy.Pass(); 3583 } 3584 // Test ctor via Pass 3585 { 3586 v8::UniquePersistent<String> copy(unique.Pass()); 3587 CHECK(unique.IsEmpty()); 3588 CHECK(copy == global); 3589 CHECK_EQ(initial_handle_count + 1, 3590 global_handles->global_handles_count()); 3591 unique = copy.Pass(); 3592 } 3593 // Test pass through function call 3594 { 3595 v8::UniquePersistent<String> copy = PassUnique(unique.Pass()); 3596 CHECK(unique.IsEmpty()); 3597 CHECK(copy == global); 3598 CHECK_EQ(initial_handle_count + 1, 3599 global_handles->global_handles_count()); 3600 unique = copy.Pass(); 3601 } 3602 CHECK_EQ(initial_handle_count + 1, global_handles->global_handles_count()); 3603 } 3604 // Test pass from function call 3605 { 3606 v8::UniquePersistent<String> unique = ReturnUnique(isolate, global); 3607 CHECK(unique == global); 3608 CHECK_EQ(initial_handle_count + 1, global_handles->global_handles_count()); 3609 } 3610 CHECK_EQ(initial_handle_count, global_handles->global_handles_count()); 3611 global.Reset(); 3612 } 3613 3614 3615 template<typename K, typename V> 3616 class WeakStdMapTraits : public v8::StdMapTraits<K, V> { 3617 public: 3618 typedef typename v8::PersistentValueMap<K, V, WeakStdMapTraits<K, V> > 3619 MapType; 3620 static const v8::PersistentContainerCallbackType kCallbackType = v8::kWeak; 3621 struct WeakCallbackDataType { 3622 MapType* map; 3623 K key; 3624 }; 3625 static WeakCallbackDataType* WeakCallbackParameter( 3626 MapType* map, const K& key, Local<V> value) { 3627 WeakCallbackDataType* data = new WeakCallbackDataType; 3628 data->map = map; 3629 data->key = key; 3630 return data; 3631 } 3632 static MapType* MapFromWeakCallbackData( 3633 const v8::WeakCallbackData<V, WeakCallbackDataType>& data) { 3634 return data.GetParameter()->map; 3635 } 3636 static K KeyFromWeakCallbackData( 3637 const v8::WeakCallbackData<V, WeakCallbackDataType>& data) { 3638 return data.GetParameter()->key; 3639 } 3640 static void DisposeCallbackData(WeakCallbackDataType* data) { 3641 delete data; 3642 } 3643 static void Dispose(v8::Isolate* isolate, v8::UniquePersistent<V> value, 3644 K key) { } 3645 }; 3646 3647 3648 template<typename Map> 3649 static void TestPersistentValueMap() { 3650 LocalContext env; 3651 v8::Isolate* isolate = env->GetIsolate(); 3652 Map map(isolate); 3653 v8::internal::GlobalHandles* global_handles = 3654 reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); 3655 int initial_handle_count = global_handles->global_handles_count(); 3656 CHECK_EQ(0, static_cast<int>(map.Size())); 3657 { 3658 HandleScope scope(isolate); 3659 Local<v8::Object> obj = map.Get(7); 3660 CHECK(obj.IsEmpty()); 3661 Local<v8::Object> expected = v8::Object::New(isolate); 3662 map.Set(7, expected); 3663 CHECK_EQ(1, static_cast<int>(map.Size())); 3664 obj = map.Get(7); 3665 CHECK_EQ(expected, obj); 3666 { 3667 typename Map::PersistentValueReference ref = map.GetReference(7); 3668 CHECK_EQ(expected, ref.NewLocal(isolate)); 3669 } 3670 v8::UniquePersistent<v8::Object> removed = map.Remove(7); 3671 CHECK_EQ(0, static_cast<int>(map.Size())); 3672 CHECK(expected == removed); 3673 removed = map.Remove(7); 3674 CHECK(removed.IsEmpty()); 3675 map.Set(8, expected); 3676 CHECK_EQ(1, static_cast<int>(map.Size())); 3677 map.Set(8, expected); 3678 CHECK_EQ(1, static_cast<int>(map.Size())); 3679 { 3680 typename Map::PersistentValueReference ref; 3681 Local<v8::Object> expected2 = v8::Object::New(isolate); 3682 removed = map.Set(8, 3683 v8::UniquePersistent<v8::Object>(isolate, expected2), &ref); 3684 CHECK_EQ(1, static_cast<int>(map.Size())); 3685 CHECK(expected == removed); 3686 CHECK_EQ(expected2, ref.NewLocal(isolate)); 3687 } 3688 } 3689 CHECK_EQ(initial_handle_count + 1, global_handles->global_handles_count()); 3690 if (map.IsWeak()) { 3691 reinterpret_cast<v8::internal::Isolate*>(isolate)->heap()-> 3692 CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 3693 } else { 3694 map.Clear(); 3695 } 3696 CHECK_EQ(0, static_cast<int>(map.Size())); 3697 CHECK_EQ(initial_handle_count, global_handles->global_handles_count()); 3698 } 3699 3700 3701 TEST(PersistentValueMap) { 3702 // Default case, w/o weak callbacks: 3703 TestPersistentValueMap<v8::StdPersistentValueMap<int, v8::Object> >(); 3704 3705 // Custom traits with weak callbacks: 3706 typedef v8::PersistentValueMap<int, v8::Object, 3707 WeakStdMapTraits<int, v8::Object> > WeakPersistentValueMap; 3708 TestPersistentValueMap<WeakPersistentValueMap>(); 3709 } 3710 3711 3712 TEST(PersistentValueVector) { 3713 LocalContext env; 3714 v8::Isolate* isolate = env->GetIsolate(); 3715 v8::internal::GlobalHandles* global_handles = 3716 reinterpret_cast<v8::internal::Isolate*>(isolate)->global_handles(); 3717 int handle_count = global_handles->global_handles_count(); 3718 HandleScope scope(isolate); 3719 3720 v8::PersistentValueVector<v8::Object> vector(isolate); 3721 3722 Local<v8::Object> obj1 = v8::Object::New(isolate); 3723 Local<v8::Object> obj2 = v8::Object::New(isolate); 3724 v8::UniquePersistent<v8::Object> obj3(isolate, v8::Object::New(isolate)); 3725 3726 CHECK(vector.IsEmpty()); 3727 CHECK_EQ(0, static_cast<int>(vector.Size())); 3728 3729 vector.ReserveCapacity(3); 3730 CHECK(vector.IsEmpty()); 3731 3732 vector.Append(obj1); 3733 vector.Append(obj2); 3734 vector.Append(obj1); 3735 vector.Append(obj3.Pass()); 3736 vector.Append(obj1); 3737 3738 CHECK(!vector.IsEmpty()); 3739 CHECK_EQ(5, static_cast<int>(vector.Size())); 3740 CHECK(obj3.IsEmpty()); 3741 CHECK_EQ(obj1, vector.Get(0)); 3742 CHECK_EQ(obj1, vector.Get(2)); 3743 CHECK_EQ(obj1, vector.Get(4)); 3744 CHECK_EQ(obj2, vector.Get(1)); 3745 3746 CHECK_EQ(5 + handle_count, global_handles->global_handles_count()); 3747 3748 vector.Clear(); 3749 CHECK(vector.IsEmpty()); 3750 CHECK_EQ(0, static_cast<int>(vector.Size())); 3751 CHECK_EQ(handle_count, global_handles->global_handles_count()); 3752 } 3753 3754 3755 THREADED_TEST(GlobalHandleUpcast) { 3756 v8::Isolate* isolate = CcTest::isolate(); 3757 v8::HandleScope scope(isolate); 3758 v8::Local<String> local = v8::Local<String>::New(isolate, v8_str("str")); 3759 v8::Persistent<String> global_string(isolate, local); 3760 v8::Persistent<Value>& global_value = 3761 v8::Persistent<Value>::Cast(global_string); 3762 CHECK(v8::Local<v8::Value>::New(isolate, global_value)->IsString()); 3763 CHECK(global_string == v8::Persistent<String>::Cast(global_value)); 3764 global_string.Reset(); 3765 } 3766 3767 3768 THREADED_TEST(HandleEquality) { 3769 v8::Isolate* isolate = CcTest::isolate(); 3770 v8::Persistent<String> global1; 3771 v8::Persistent<String> global2; 3772 { 3773 v8::HandleScope scope(isolate); 3774 global1.Reset(isolate, v8_str("str")); 3775 global2.Reset(isolate, v8_str("str2")); 3776 } 3777 CHECK_EQ(global1 == global1, true); 3778 CHECK_EQ(global1 != global1, false); 3779 { 3780 v8::HandleScope scope(isolate); 3781 Local<String> local1 = Local<String>::New(isolate, global1); 3782 Local<String> local2 = Local<String>::New(isolate, global2); 3783 3784 CHECK_EQ(global1 == local1, true); 3785 CHECK_EQ(global1 != local1, false); 3786 CHECK_EQ(local1 == global1, true); 3787 CHECK_EQ(local1 != global1, false); 3788 3789 CHECK_EQ(global1 == local2, false); 3790 CHECK_EQ(global1 != local2, true); 3791 CHECK_EQ(local2 == global1, false); 3792 CHECK_EQ(local2 != global1, true); 3793 3794 CHECK_EQ(local1 == local2, false); 3795 CHECK_EQ(local1 != local2, true); 3796 3797 Local<String> anotherLocal1 = Local<String>::New(isolate, global1); 3798 CHECK_EQ(local1 == anotherLocal1, true); 3799 CHECK_EQ(local1 != anotherLocal1, false); 3800 } 3801 global1.Reset(); 3802 global2.Reset(); 3803 } 3804 3805 3806 THREADED_TEST(LocalHandle) { 3807 v8::HandleScope scope(CcTest::isolate()); 3808 v8::Local<String> local = 3809 v8::Local<String>::New(CcTest::isolate(), v8_str("str")); 3810 CHECK_EQ(local->Length(), 3); 3811 } 3812 3813 3814 class WeakCallCounter { 3815 public: 3816 explicit WeakCallCounter(int id) : id_(id), number_of_weak_calls_(0) { } 3817 int id() { return id_; } 3818 void increment() { number_of_weak_calls_++; } 3819 int NumberOfWeakCalls() { return number_of_weak_calls_; } 3820 private: 3821 int id_; 3822 int number_of_weak_calls_; 3823 }; 3824 3825 3826 template<typename T> 3827 struct WeakCallCounterAndPersistent { 3828 explicit WeakCallCounterAndPersistent(WeakCallCounter* counter) 3829 : counter(counter) {} 3830 WeakCallCounter* counter; 3831 v8::Persistent<T> handle; 3832 }; 3833 3834 3835 template <typename T> 3836 static void WeakPointerCallback( 3837 const v8::WeakCallbackData<T, WeakCallCounterAndPersistent<T> >& data) { 3838 CHECK_EQ(1234, data.GetParameter()->counter->id()); 3839 data.GetParameter()->counter->increment(); 3840 data.GetParameter()->handle.Reset(); 3841 } 3842 3843 3844 template<typename T> 3845 static UniqueId MakeUniqueId(const Persistent<T>& p) { 3846 return UniqueId(reinterpret_cast<uintptr_t>(*v8::Utils::OpenPersistent(p))); 3847 } 3848 3849 3850 THREADED_TEST(ApiObjectGroups) { 3851 LocalContext env; 3852 v8::Isolate* iso = env->GetIsolate(); 3853 HandleScope scope(iso); 3854 3855 WeakCallCounter counter(1234); 3856 3857 WeakCallCounterAndPersistent<Value> g1s1(&counter); 3858 WeakCallCounterAndPersistent<Value> g1s2(&counter); 3859 WeakCallCounterAndPersistent<Value> g1c1(&counter); 3860 WeakCallCounterAndPersistent<Value> g2s1(&counter); 3861 WeakCallCounterAndPersistent<Value> g2s2(&counter); 3862 WeakCallCounterAndPersistent<Value> g2c1(&counter); 3863 3864 { 3865 HandleScope scope(iso); 3866 g1s1.handle.Reset(iso, Object::New(iso)); 3867 g1s2.handle.Reset(iso, Object::New(iso)); 3868 g1c1.handle.Reset(iso, Object::New(iso)); 3869 g1s1.handle.SetWeak(&g1s1, &WeakPointerCallback); 3870 g1s2.handle.SetWeak(&g1s2, &WeakPointerCallback); 3871 g1c1.handle.SetWeak(&g1c1, &WeakPointerCallback); 3872 3873 g2s1.handle.Reset(iso, Object::New(iso)); 3874 g2s2.handle.Reset(iso, Object::New(iso)); 3875 g2c1.handle.Reset(iso, Object::New(iso)); 3876 g2s1.handle.SetWeak(&g2s1, &WeakPointerCallback); 3877 g2s2.handle.SetWeak(&g2s2, &WeakPointerCallback); 3878 g2c1.handle.SetWeak(&g2c1, &WeakPointerCallback); 3879 } 3880 3881 WeakCallCounterAndPersistent<Value> root(&counter); 3882 root.handle.Reset(iso, g1s1.handle); // make a root. 3883 3884 // Connect group 1 and 2, make a cycle. 3885 { 3886 HandleScope scope(iso); 3887 CHECK(Local<Object>::New(iso, g1s2.handle.As<Object>())-> 3888 Set(0, Local<Value>::New(iso, g2s2.handle))); 3889 CHECK(Local<Object>::New(iso, g2s1.handle.As<Object>())-> 3890 Set(0, Local<Value>::New(iso, g1s1.handle))); 3891 } 3892 3893 { 3894 UniqueId id1 = MakeUniqueId(g1s1.handle); 3895 UniqueId id2 = MakeUniqueId(g2s2.handle); 3896 iso->SetObjectGroupId(g1s1.handle, id1); 3897 iso->SetObjectGroupId(g1s2.handle, id1); 3898 iso->SetReferenceFromGroup(id1, g1c1.handle); 3899 iso->SetObjectGroupId(g2s1.handle, id2); 3900 iso->SetObjectGroupId(g2s2.handle, id2); 3901 iso->SetReferenceFromGroup(id2, g2c1.handle); 3902 } 3903 // Do a single full GC, ensure incremental marking is stopped. 3904 v8::internal::Heap* heap = reinterpret_cast<v8::internal::Isolate*>( 3905 iso)->heap(); 3906 heap->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 3907 3908 // All object should be alive. 3909 CHECK_EQ(0, counter.NumberOfWeakCalls()); 3910 3911 // Weaken the root. 3912 root.handle.SetWeak(&root, &WeakPointerCallback); 3913 // But make children strong roots---all the objects (except for children) 3914 // should be collectable now. 3915 g1c1.handle.ClearWeak(); 3916 g2c1.handle.ClearWeak(); 3917 3918 // Groups are deleted, rebuild groups. 3919 { 3920 UniqueId id1 = MakeUniqueId(g1s1.handle); 3921 UniqueId id2 = MakeUniqueId(g2s2.handle); 3922 iso->SetObjectGroupId(g1s1.handle, id1); 3923 iso->SetObjectGroupId(g1s2.handle, id1); 3924 iso->SetReferenceFromGroup(id1, g1c1.handle); 3925 iso->SetObjectGroupId(g2s1.handle, id2); 3926 iso->SetObjectGroupId(g2s2.handle, id2); 3927 iso->SetReferenceFromGroup(id2, g2c1.handle); 3928 } 3929 3930 heap->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 3931 3932 // All objects should be gone. 5 global handles in total. 3933 CHECK_EQ(5, counter.NumberOfWeakCalls()); 3934 3935 // And now make children weak again and collect them. 3936 g1c1.handle.SetWeak(&g1c1, &WeakPointerCallback); 3937 g2c1.handle.SetWeak(&g2c1, &WeakPointerCallback); 3938 3939 heap->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 3940 CHECK_EQ(7, counter.NumberOfWeakCalls()); 3941 } 3942 3943 3944 THREADED_TEST(ApiObjectGroupsForSubtypes) { 3945 LocalContext env; 3946 v8::Isolate* iso = env->GetIsolate(); 3947 HandleScope scope(iso); 3948 3949 WeakCallCounter counter(1234); 3950 3951 WeakCallCounterAndPersistent<Object> g1s1(&counter); 3952 WeakCallCounterAndPersistent<String> g1s2(&counter); 3953 WeakCallCounterAndPersistent<String> g1c1(&counter); 3954 WeakCallCounterAndPersistent<Object> g2s1(&counter); 3955 WeakCallCounterAndPersistent<String> g2s2(&counter); 3956 WeakCallCounterAndPersistent<String> g2c1(&counter); 3957 3958 { 3959 HandleScope scope(iso); 3960 g1s1.handle.Reset(iso, Object::New(iso)); 3961 g1s2.handle.Reset(iso, String::NewFromUtf8(iso, "foo1")); 3962 g1c1.handle.Reset(iso, String::NewFromUtf8(iso, "foo2")); 3963 g1s1.handle.SetWeak(&g1s1, &WeakPointerCallback); 3964 g1s2.handle.SetWeak(&g1s2, &WeakPointerCallback); 3965 g1c1.handle.SetWeak(&g1c1, &WeakPointerCallback); 3966 3967 g2s1.handle.Reset(iso, Object::New(iso)); 3968 g2s2.handle.Reset(iso, String::NewFromUtf8(iso, "foo3")); 3969 g2c1.handle.Reset(iso, String::NewFromUtf8(iso, "foo4")); 3970 g2s1.handle.SetWeak(&g2s1, &WeakPointerCallback); 3971 g2s2.handle.SetWeak(&g2s2, &WeakPointerCallback); 3972 g2c1.handle.SetWeak(&g2c1, &WeakPointerCallback); 3973 } 3974 3975 WeakCallCounterAndPersistent<Value> root(&counter); 3976 root.handle.Reset(iso, g1s1.handle); // make a root. 3977 3978 // Connect group 1 and 2, make a cycle. 3979 { 3980 HandleScope scope(iso); 3981 CHECK(Local<Object>::New(iso, g1s1.handle) 3982 ->Set(0, Local<Object>::New(iso, g2s1.handle))); 3983 CHECK(Local<Object>::New(iso, g2s1.handle) 3984 ->Set(0, Local<Object>::New(iso, g1s1.handle))); 3985 } 3986 3987 { 3988 UniqueId id1 = MakeUniqueId(g1s1.handle); 3989 UniqueId id2 = MakeUniqueId(g2s2.handle); 3990 iso->SetObjectGroupId(g1s1.handle, id1); 3991 iso->SetObjectGroupId(g1s2.handle, id1); 3992 iso->SetReference(g1s1.handle, g1c1.handle); 3993 iso->SetObjectGroupId(g2s1.handle, id2); 3994 iso->SetObjectGroupId(g2s2.handle, id2); 3995 iso->SetReferenceFromGroup(id2, g2c1.handle); 3996 } 3997 // Do a single full GC, ensure incremental marking is stopped. 3998 v8::internal::Heap* heap = reinterpret_cast<v8::internal::Isolate*>( 3999 iso)->heap(); 4000 heap->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 4001 4002 // All object should be alive. 4003 CHECK_EQ(0, counter.NumberOfWeakCalls()); 4004 4005 // Weaken the root. 4006 root.handle.SetWeak(&root, &WeakPointerCallback); 4007 // But make children strong roots---all the objects (except for children) 4008 // should be collectable now. 4009 g1c1.handle.ClearWeak(); 4010 g2c1.handle.ClearWeak(); 4011 4012 // Groups are deleted, rebuild groups. 4013 { 4014 UniqueId id1 = MakeUniqueId(g1s1.handle); 4015 UniqueId id2 = MakeUniqueId(g2s2.handle); 4016 iso->SetObjectGroupId(g1s1.handle, id1); 4017 iso->SetObjectGroupId(g1s2.handle, id1); 4018 iso->SetReference(g1s1.handle, g1c1.handle); 4019 iso->SetObjectGroupId(g2s1.handle, id2); 4020 iso->SetObjectGroupId(g2s2.handle, id2); 4021 iso->SetReferenceFromGroup(id2, g2c1.handle); 4022 } 4023 4024 heap->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 4025 4026 // All objects should be gone. 5 global handles in total. 4027 CHECK_EQ(5, counter.NumberOfWeakCalls()); 4028 4029 // And now make children weak again and collect them. 4030 g1c1.handle.SetWeak(&g1c1, &WeakPointerCallback); 4031 g2c1.handle.SetWeak(&g2c1, &WeakPointerCallback); 4032 4033 heap->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 4034 CHECK_EQ(7, counter.NumberOfWeakCalls()); 4035 } 4036 4037 4038 THREADED_TEST(ApiObjectGroupsCycle) { 4039 LocalContext env; 4040 v8::Isolate* iso = env->GetIsolate(); 4041 HandleScope scope(iso); 4042 4043 WeakCallCounter counter(1234); 4044 4045 WeakCallCounterAndPersistent<Value> g1s1(&counter); 4046 WeakCallCounterAndPersistent<Value> g1s2(&counter); 4047 WeakCallCounterAndPersistent<Value> g2s1(&counter); 4048 WeakCallCounterAndPersistent<Value> g2s2(&counter); 4049 WeakCallCounterAndPersistent<Value> g3s1(&counter); 4050 WeakCallCounterAndPersistent<Value> g3s2(&counter); 4051 WeakCallCounterAndPersistent<Value> g4s1(&counter); 4052 WeakCallCounterAndPersistent<Value> g4s2(&counter); 4053 4054 { 4055 HandleScope scope(iso); 4056 g1s1.handle.Reset(iso, Object::New(iso)); 4057 g1s2.handle.Reset(iso, Object::New(iso)); 4058 g1s1.handle.SetWeak(&g1s1, &WeakPointerCallback); 4059 g1s2.handle.SetWeak(&g1s2, &WeakPointerCallback); 4060 CHECK(g1s1.handle.IsWeak()); 4061 CHECK(g1s2.handle.IsWeak()); 4062 4063 g2s1.handle.Reset(iso, Object::New(iso)); 4064 g2s2.handle.Reset(iso, Object::New(iso)); 4065 g2s1.handle.SetWeak(&g2s1, &WeakPointerCallback); 4066 g2s2.handle.SetWeak(&g2s2, &WeakPointerCallback); 4067 CHECK(g2s1.handle.IsWeak()); 4068 CHECK(g2s2.handle.IsWeak()); 4069 4070 g3s1.handle.Reset(iso, Object::New(iso)); 4071 g3s2.handle.Reset(iso, Object::New(iso)); 4072 g3s1.handle.SetWeak(&g3s1, &WeakPointerCallback); 4073 g3s2.handle.SetWeak(&g3s2, &WeakPointerCallback); 4074 CHECK(g3s1.handle.IsWeak()); 4075 CHECK(g3s2.handle.IsWeak()); 4076 4077 g4s1.handle.Reset(iso, Object::New(iso)); 4078 g4s2.handle.Reset(iso, Object::New(iso)); 4079 g4s1.handle.SetWeak(&g4s1, &WeakPointerCallback); 4080 g4s2.handle.SetWeak(&g4s2, &WeakPointerCallback); 4081 CHECK(g4s1.handle.IsWeak()); 4082 CHECK(g4s2.handle.IsWeak()); 4083 } 4084 4085 WeakCallCounterAndPersistent<Value> root(&counter); 4086 root.handle.Reset(iso, g1s1.handle); // make a root. 4087 4088 // Connect groups. We're building the following cycle: 4089 // G1: { g1s1, g2s1 }, g1s1 implicitly references g2s1, ditto for other 4090 // groups. 4091 { 4092 UniqueId id1 = MakeUniqueId(g1s1.handle); 4093 UniqueId id2 = MakeUniqueId(g2s1.handle); 4094 UniqueId id3 = MakeUniqueId(g3s1.handle); 4095 UniqueId id4 = MakeUniqueId(g4s1.handle); 4096 iso->SetObjectGroupId(g1s1.handle, id1); 4097 iso->SetObjectGroupId(g1s2.handle, id1); 4098 iso->SetReferenceFromGroup(id1, g2s1.handle); 4099 iso->SetObjectGroupId(g2s1.handle, id2); 4100 iso->SetObjectGroupId(g2s2.handle, id2); 4101 iso->SetReferenceFromGroup(id2, g3s1.handle); 4102 iso->SetObjectGroupId(g3s1.handle, id3); 4103 iso->SetObjectGroupId(g3s2.handle, id3); 4104 iso->SetReferenceFromGroup(id3, g4s1.handle); 4105 iso->SetObjectGroupId(g4s1.handle, id4); 4106 iso->SetObjectGroupId(g4s2.handle, id4); 4107 iso->SetReferenceFromGroup(id4, g1s1.handle); 4108 } 4109 // Do a single full GC 4110 v8::internal::Heap* heap = reinterpret_cast<v8::internal::Isolate*>( 4111 iso)->heap(); 4112 heap->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 4113 4114 // All object should be alive. 4115 CHECK_EQ(0, counter.NumberOfWeakCalls()); 4116 4117 // Weaken the root. 4118 root.handle.SetWeak(&root, &WeakPointerCallback); 4119 4120 // Groups are deleted, rebuild groups. 4121 { 4122 UniqueId id1 = MakeUniqueId(g1s1.handle); 4123 UniqueId id2 = MakeUniqueId(g2s1.handle); 4124 UniqueId id3 = MakeUniqueId(g3s1.handle); 4125 UniqueId id4 = MakeUniqueId(g4s1.handle); 4126 iso->SetObjectGroupId(g1s1.handle, id1); 4127 iso->SetObjectGroupId(g1s2.handle, id1); 4128 iso->SetReferenceFromGroup(id1, g2s1.handle); 4129 iso->SetObjectGroupId(g2s1.handle, id2); 4130 iso->SetObjectGroupId(g2s2.handle, id2); 4131 iso->SetReferenceFromGroup(id2, g3s1.handle); 4132 iso->SetObjectGroupId(g3s1.handle, id3); 4133 iso->SetObjectGroupId(g3s2.handle, id3); 4134 iso->SetReferenceFromGroup(id3, g4s1.handle); 4135 iso->SetObjectGroupId(g4s1.handle, id4); 4136 iso->SetObjectGroupId(g4s2.handle, id4); 4137 iso->SetReferenceFromGroup(id4, g1s1.handle); 4138 } 4139 4140 heap->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 4141 4142 // All objects should be gone. 9 global handles in total. 4143 CHECK_EQ(9, counter.NumberOfWeakCalls()); 4144 } 4145 4146 4147 // TODO(mstarzinger): This should be a THREADED_TEST but causes failures 4148 // on the buildbots, so was made non-threaded for the time being. 4149 TEST(ApiObjectGroupsCycleForScavenger) { 4150 i::FLAG_stress_compaction = false; 4151 i::FLAG_gc_global = false; 4152 LocalContext env; 4153 v8::Isolate* iso = env->GetIsolate(); 4154 HandleScope scope(iso); 4155 4156 WeakCallCounter counter(1234); 4157 4158 WeakCallCounterAndPersistent<Value> g1s1(&counter); 4159 WeakCallCounterAndPersistent<Value> g1s2(&counter); 4160 WeakCallCounterAndPersistent<Value> g2s1(&counter); 4161 WeakCallCounterAndPersistent<Value> g2s2(&counter); 4162 WeakCallCounterAndPersistent<Value> g3s1(&counter); 4163 WeakCallCounterAndPersistent<Value> g3s2(&counter); 4164 4165 { 4166 HandleScope scope(iso); 4167 g1s1.handle.Reset(iso, Object::New(iso)); 4168 g1s2.handle.Reset(iso, Object::New(iso)); 4169 g1s1.handle.SetWeak(&g1s1, &WeakPointerCallback); 4170 g1s2.handle.SetWeak(&g1s2, &WeakPointerCallback); 4171 4172 g2s1.handle.Reset(iso, Object::New(iso)); 4173 g2s2.handle.Reset(iso, Object::New(iso)); 4174 g2s1.handle.SetWeak(&g2s1, &WeakPointerCallback); 4175 g2s2.handle.SetWeak(&g2s2, &WeakPointerCallback); 4176 4177 g3s1.handle.Reset(iso, Object::New(iso)); 4178 g3s2.handle.Reset(iso, Object::New(iso)); 4179 g3s1.handle.SetWeak(&g3s1, &WeakPointerCallback); 4180 g3s2.handle.SetWeak(&g3s2, &WeakPointerCallback); 4181 } 4182 4183 // Make a root. 4184 WeakCallCounterAndPersistent<Value> root(&counter); 4185 root.handle.Reset(iso, g1s1.handle); 4186 root.handle.MarkPartiallyDependent(); 4187 4188 // Connect groups. We're building the following cycle: 4189 // G1: { g1s1, g2s1 }, g1s1 implicitly references g2s1, ditto for other 4190 // groups. 4191 { 4192 HandleScope handle_scope(iso); 4193 g1s1.handle.MarkPartiallyDependent(); 4194 g1s2.handle.MarkPartiallyDependent(); 4195 g2s1.handle.MarkPartiallyDependent(); 4196 g2s2.handle.MarkPartiallyDependent(); 4197 g3s1.handle.MarkPartiallyDependent(); 4198 g3s2.handle.MarkPartiallyDependent(); 4199 iso->SetObjectGroupId(g1s1.handle, UniqueId(1)); 4200 iso->SetObjectGroupId(g1s2.handle, UniqueId(1)); 4201 Local<Object>::New(iso, g1s1.handle.As<Object>())->Set( 4202 v8_str("x"), Local<Value>::New(iso, g2s1.handle)); 4203 iso->SetObjectGroupId(g2s1.handle, UniqueId(2)); 4204 iso->SetObjectGroupId(g2s2.handle, UniqueId(2)); 4205 Local<Object>::New(iso, g2s1.handle.As<Object>())->Set( 4206 v8_str("x"), Local<Value>::New(iso, g3s1.handle)); 4207 iso->SetObjectGroupId(g3s1.handle, UniqueId(3)); 4208 iso->SetObjectGroupId(g3s2.handle, UniqueId(3)); 4209 Local<Object>::New(iso, g3s1.handle.As<Object>())->Set( 4210 v8_str("x"), Local<Value>::New(iso, g1s1.handle)); 4211 } 4212 4213 v8::internal::Heap* heap = reinterpret_cast<v8::internal::Isolate*>( 4214 iso)->heap(); 4215 heap->CollectAllGarbage(i::Heap::kNoGCFlags); 4216 4217 // All objects should be alive. 4218 CHECK_EQ(0, counter.NumberOfWeakCalls()); 4219 4220 // Weaken the root. 4221 root.handle.SetWeak(&root, &WeakPointerCallback); 4222 root.handle.MarkPartiallyDependent(); 4223 4224 // Groups are deleted, rebuild groups. 4225 { 4226 HandleScope handle_scope(iso); 4227 g1s1.handle.MarkPartiallyDependent(); 4228 g1s2.handle.MarkPartiallyDependent(); 4229 g2s1.handle.MarkPartiallyDependent(); 4230 g2s2.handle.MarkPartiallyDependent(); 4231 g3s1.handle.MarkPartiallyDependent(); 4232 g3s2.handle.MarkPartiallyDependent(); 4233 iso->SetObjectGroupId(g1s1.handle, UniqueId(1)); 4234 iso->SetObjectGroupId(g1s2.handle, UniqueId(1)); 4235 Local<Object>::New(iso, g1s1.handle.As<Object>())->Set( 4236 v8_str("x"), Local<Value>::New(iso, g2s1.handle)); 4237 iso->SetObjectGroupId(g2s1.handle, UniqueId(2)); 4238 iso->SetObjectGroupId(g2s2.handle, UniqueId(2)); 4239 Local<Object>::New(iso, g2s1.handle.As<Object>())->Set( 4240 v8_str("x"), Local<Value>::New(iso, g3s1.handle)); 4241 iso->SetObjectGroupId(g3s1.handle, UniqueId(3)); 4242 iso->SetObjectGroupId(g3s2.handle, UniqueId(3)); 4243 Local<Object>::New(iso, g3s1.handle.As<Object>())->Set( 4244 v8_str("x"), Local<Value>::New(iso, g1s1.handle)); 4245 } 4246 4247 heap->CollectAllGarbage(i::Heap::kNoGCFlags); 4248 4249 // All objects should be gone. 7 global handles in total. 4250 CHECK_EQ(7, counter.NumberOfWeakCalls()); 4251 } 4252 4253 4254 THREADED_TEST(ScriptException) { 4255 LocalContext env; 4256 v8::HandleScope scope(env->GetIsolate()); 4257 Local<Script> script = v8_compile("throw 'panama!';"); 4258 v8::TryCatch try_catch; 4259 Local<Value> result = script->Run(); 4260 CHECK(result.IsEmpty()); 4261 CHECK(try_catch.HasCaught()); 4262 String::Utf8Value exception_value(try_catch.Exception()); 4263 CHECK_EQ(*exception_value, "panama!"); 4264 } 4265 4266 4267 TEST(TryCatchCustomException) { 4268 LocalContext env; 4269 v8::HandleScope scope(env->GetIsolate()); 4270 v8::TryCatch try_catch; 4271 CompileRun("function CustomError() { this.a = 'b'; }" 4272 "(function f() { throw new CustomError(); })();"); 4273 CHECK(try_catch.HasCaught()); 4274 CHECK(try_catch.Exception()->ToObject()-> 4275 Get(v8_str("a"))->Equals(v8_str("b"))); 4276 } 4277 4278 4279 bool message_received; 4280 4281 4282 static void check_message_0(v8::Handle<v8::Message> message, 4283 v8::Handle<Value> data) { 4284 CHECK_EQ(5.76, data->NumberValue()); 4285 CHECK_EQ(6.75, message->GetScriptOrigin().ResourceName()->NumberValue()); 4286 CHECK(!message->IsSharedCrossOrigin()); 4287 message_received = true; 4288 } 4289 4290 4291 THREADED_TEST(MessageHandler0) { 4292 message_received = false; 4293 v8::HandleScope scope(CcTest::isolate()); 4294 CHECK(!message_received); 4295 LocalContext context; 4296 v8::V8::AddMessageListener(check_message_0, v8_num(5.76)); 4297 v8::Handle<v8::Script> script = CompileWithOrigin("throw 'error'", "6.75"); 4298 script->Run(); 4299 CHECK(message_received); 4300 // clear out the message listener 4301 v8::V8::RemoveMessageListeners(check_message_0); 4302 } 4303 4304 4305 static void check_message_1(v8::Handle<v8::Message> message, 4306 v8::Handle<Value> data) { 4307 CHECK(data->IsNumber()); 4308 CHECK_EQ(1337, data->Int32Value()); 4309 CHECK(!message->IsSharedCrossOrigin()); 4310 message_received = true; 4311 } 4312 4313 4314 TEST(MessageHandler1) { 4315 message_received = false; 4316 v8::HandleScope scope(CcTest::isolate()); 4317 CHECK(!message_received); 4318 v8::V8::AddMessageListener(check_message_1); 4319 LocalContext context; 4320 CompileRun("throw 1337;"); 4321 CHECK(message_received); 4322 // clear out the message listener 4323 v8::V8::RemoveMessageListeners(check_message_1); 4324 } 4325 4326 4327 static void check_message_2(v8::Handle<v8::Message> message, 4328 v8::Handle<Value> data) { 4329 LocalContext context; 4330 CHECK(data->IsObject()); 4331 v8::Local<v8::Value> hidden_property = 4332 v8::Object::Cast(*data)->GetHiddenValue(v8_str("hidden key")); 4333 CHECK(v8_str("hidden value")->Equals(hidden_property)); 4334 CHECK(!message->IsSharedCrossOrigin()); 4335 message_received = true; 4336 } 4337 4338 4339 TEST(MessageHandler2) { 4340 message_received = false; 4341 v8::HandleScope scope(CcTest::isolate()); 4342 CHECK(!message_received); 4343 v8::V8::AddMessageListener(check_message_2); 4344 LocalContext context; 4345 v8::Local<v8::Value> error = v8::Exception::Error(v8_str("custom error")); 4346 v8::Object::Cast(*error)->SetHiddenValue(v8_str("hidden key"), 4347 v8_str("hidden value")); 4348 context->Global()->Set(v8_str("error"), error); 4349 CompileRun("throw error;"); 4350 CHECK(message_received); 4351 // clear out the message listener 4352 v8::V8::RemoveMessageListeners(check_message_2); 4353 } 4354 4355 4356 static void check_message_3(v8::Handle<v8::Message> message, 4357 v8::Handle<Value> data) { 4358 CHECK(message->IsSharedCrossOrigin()); 4359 CHECK_EQ(6.75, message->GetScriptOrigin().ResourceName()->NumberValue()); 4360 message_received = true; 4361 } 4362 4363 4364 TEST(MessageHandler3) { 4365 message_received = false; 4366 v8::Isolate* isolate = CcTest::isolate(); 4367 v8::HandleScope scope(isolate); 4368 CHECK(!message_received); 4369 v8::V8::AddMessageListener(check_message_3); 4370 LocalContext context; 4371 v8::ScriptOrigin origin = 4372 v8::ScriptOrigin(v8_str("6.75"), 4373 v8::Integer::New(isolate, 1), 4374 v8::Integer::New(isolate, 2), 4375 v8::True(isolate)); 4376 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"), 4377 &origin); 4378 script->Run(); 4379 CHECK(message_received); 4380 // clear out the message listener 4381 v8::V8::RemoveMessageListeners(check_message_3); 4382 } 4383 4384 4385 static void check_message_4(v8::Handle<v8::Message> message, 4386 v8::Handle<Value> data) { 4387 CHECK(!message->IsSharedCrossOrigin()); 4388 CHECK_EQ(6.75, message->GetScriptOrigin().ResourceName()->NumberValue()); 4389 message_received = true; 4390 } 4391 4392 4393 TEST(MessageHandler4) { 4394 message_received = false; 4395 v8::Isolate* isolate = CcTest::isolate(); 4396 v8::HandleScope scope(isolate); 4397 CHECK(!message_received); 4398 v8::V8::AddMessageListener(check_message_4); 4399 LocalContext context; 4400 v8::ScriptOrigin origin = 4401 v8::ScriptOrigin(v8_str("6.75"), 4402 v8::Integer::New(isolate, 1), 4403 v8::Integer::New(isolate, 2), 4404 v8::False(isolate)); 4405 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"), 4406 &origin); 4407 script->Run(); 4408 CHECK(message_received); 4409 // clear out the message listener 4410 v8::V8::RemoveMessageListeners(check_message_4); 4411 } 4412 4413 4414 static void check_message_5a(v8::Handle<v8::Message> message, 4415 v8::Handle<Value> data) { 4416 CHECK(message->IsSharedCrossOrigin()); 4417 CHECK_EQ(6.75, message->GetScriptOrigin().ResourceName()->NumberValue()); 4418 message_received = true; 4419 } 4420 4421 4422 static void check_message_5b(v8::Handle<v8::Message> message, 4423 v8::Handle<Value> data) { 4424 CHECK(!message->IsSharedCrossOrigin()); 4425 CHECK_EQ(6.75, message->GetScriptOrigin().ResourceName()->NumberValue()); 4426 message_received = true; 4427 } 4428 4429 4430 TEST(MessageHandler5) { 4431 message_received = false; 4432 v8::Isolate* isolate = CcTest::isolate(); 4433 v8::HandleScope scope(isolate); 4434 CHECK(!message_received); 4435 v8::V8::AddMessageListener(check_message_5a); 4436 LocalContext context; 4437 v8::ScriptOrigin origin = 4438 v8::ScriptOrigin(v8_str("6.75"), 4439 v8::Integer::New(isolate, 1), 4440 v8::Integer::New(isolate, 2), 4441 v8::True(isolate)); 4442 v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"), 4443 &origin); 4444 script->Run(); 4445 CHECK(message_received); 4446 // clear out the message listener 4447 v8::V8::RemoveMessageListeners(check_message_5a); 4448 4449 message_received = false; 4450 v8::V8::AddMessageListener(check_message_5b); 4451 origin = 4452 v8::ScriptOrigin(v8_str("6.75"), 4453 v8::Integer::New(isolate, 1), 4454 v8::Integer::New(isolate, 2), 4455 v8::False(isolate)); 4456 script = Script::Compile(v8_str("throw 'error'"), 4457 &origin); 4458 script->Run(); 4459 CHECK(message_received); 4460 // clear out the message listener 4461 v8::V8::RemoveMessageListeners(check_message_5b); 4462 } 4463 4464 4465 THREADED_TEST(GetSetProperty) { 4466 LocalContext context; 4467 v8::Isolate* isolate = context->GetIsolate(); 4468 v8::HandleScope scope(isolate); 4469 context->Global()->Set(v8_str("foo"), v8_num(14)); 4470 context->Global()->Set(v8_str("12"), v8_num(92)); 4471 context->Global()->Set(v8::Integer::New(isolate, 16), v8_num(32)); 4472 context->Global()->Set(v8_num(13), v8_num(56)); 4473 Local<Value> foo = CompileRun("this.foo"); 4474 CHECK_EQ(14, foo->Int32Value()); 4475 Local<Value> twelve = CompileRun("this[12]"); 4476 CHECK_EQ(92, twelve->Int32Value()); 4477 Local<Value> sixteen = CompileRun("this[16]"); 4478 CHECK_EQ(32, sixteen->Int32Value()); 4479 Local<Value> thirteen = CompileRun("this[13]"); 4480 CHECK_EQ(56, thirteen->Int32Value()); 4481 CHECK_EQ(92, 4482 context->Global()->Get(v8::Integer::New(isolate, 12))->Int32Value()); 4483 CHECK_EQ(92, context->Global()->Get(v8_str("12"))->Int32Value()); 4484 CHECK_EQ(92, context->Global()->Get(v8_num(12))->Int32Value()); 4485 CHECK_EQ(32, 4486 context->Global()->Get(v8::Integer::New(isolate, 16))->Int32Value()); 4487 CHECK_EQ(32, context->Global()->Get(v8_str("16"))->Int32Value()); 4488 CHECK_EQ(32, context->Global()->Get(v8_num(16))->Int32Value()); 4489 CHECK_EQ(56, 4490 context->Global()->Get(v8::Integer::New(isolate, 13))->Int32Value()); 4491 CHECK_EQ(56, context->Global()->Get(v8_str("13"))->Int32Value()); 4492 CHECK_EQ(56, context->Global()->Get(v8_num(13))->Int32Value()); 4493 } 4494 4495 4496 THREADED_TEST(PropertyAttributes) { 4497 LocalContext context; 4498 v8::HandleScope scope(context->GetIsolate()); 4499 // none 4500 Local<String> prop = v8_str("none"); 4501 context->Global()->Set(prop, v8_num(7)); 4502 CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(prop)); 4503 // read-only 4504 prop = v8_str("read_only"); 4505 context->Global()->ForceSet(prop, v8_num(7), v8::ReadOnly); 4506 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 4507 CHECK_EQ(v8::ReadOnly, context->Global()->GetPropertyAttributes(prop)); 4508 CompileRun("read_only = 9"); 4509 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 4510 context->Global()->Set(prop, v8_num(10)); 4511 CHECK_EQ(7, context->Global()->Get(prop)->Int32Value()); 4512 // dont-delete 4513 prop = v8_str("dont_delete"); 4514 context->Global()->ForceSet(prop, v8_num(13), v8::DontDelete); 4515 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 4516 CompileRun("delete dont_delete"); 4517 CHECK_EQ(13, context->Global()->Get(prop)->Int32Value()); 4518 CHECK_EQ(v8::DontDelete, context->Global()->GetPropertyAttributes(prop)); 4519 // dont-enum 4520 prop = v8_str("dont_enum"); 4521 context->Global()->ForceSet(prop, v8_num(28), v8::DontEnum); 4522 CHECK_EQ(v8::DontEnum, context->Global()->GetPropertyAttributes(prop)); 4523 // absent 4524 prop = v8_str("absent"); 4525 CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(prop)); 4526 Local<Value> fake_prop = v8_num(1); 4527 CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(fake_prop)); 4528 // exception 4529 TryCatch try_catch; 4530 Local<Value> exception = 4531 CompileRun("({ toString: function() { throw 'exception';} })"); 4532 CHECK_EQ(v8::None, context->Global()->GetPropertyAttributes(exception)); 4533 CHECK(try_catch.HasCaught()); 4534 String::Utf8Value exception_value(try_catch.Exception()); 4535 CHECK_EQ("exception", *exception_value); 4536 try_catch.Reset(); 4537 } 4538 4539 4540 THREADED_TEST(Array) { 4541 LocalContext context; 4542 v8::HandleScope scope(context->GetIsolate()); 4543 Local<v8::Array> array = v8::Array::New(context->GetIsolate()); 4544 CHECK_EQ(0, array->Length()); 4545 CHECK(array->Get(0)->IsUndefined()); 4546 CHECK(!array->Has(0)); 4547 CHECK(array->Get(100)->IsUndefined()); 4548 CHECK(!array->Has(100)); 4549 array->Set(2, v8_num(7)); 4550 CHECK_EQ(3, array->Length()); 4551 CHECK(!array->Has(0)); 4552 CHECK(!array->Has(1)); 4553 CHECK(array->Has(2)); 4554 CHECK_EQ(7, array->Get(2)->Int32Value()); 4555 Local<Value> obj = CompileRun("[1, 2, 3]"); 4556 Local<v8::Array> arr = obj.As<v8::Array>(); 4557 CHECK_EQ(3, arr->Length()); 4558 CHECK_EQ(1, arr->Get(0)->Int32Value()); 4559 CHECK_EQ(2, arr->Get(1)->Int32Value()); 4560 CHECK_EQ(3, arr->Get(2)->Int32Value()); 4561 array = v8::Array::New(context->GetIsolate(), 27); 4562 CHECK_EQ(27, array->Length()); 4563 array = v8::Array::New(context->GetIsolate(), -27); 4564 CHECK_EQ(0, array->Length()); 4565 } 4566 4567 4568 void HandleF(const v8::FunctionCallbackInfo<v8::Value>& args) { 4569 v8::EscapableHandleScope scope(args.GetIsolate()); 4570 ApiTestFuzzer::Fuzz(); 4571 Local<v8::Array> result = v8::Array::New(args.GetIsolate(), args.Length()); 4572 for (int i = 0; i < args.Length(); i++) 4573 result->Set(i, args[i]); 4574 args.GetReturnValue().Set(scope.Escape(result)); 4575 } 4576 4577 4578 THREADED_TEST(Vector) { 4579 v8::Isolate* isolate = CcTest::isolate(); 4580 v8::HandleScope scope(isolate); 4581 Local<ObjectTemplate> global = ObjectTemplate::New(isolate); 4582 global->Set(v8_str("f"), v8::FunctionTemplate::New(isolate, HandleF)); 4583 LocalContext context(0, global); 4584 4585 const char* fun = "f()"; 4586 Local<v8::Array> a0 = CompileRun(fun).As<v8::Array>(); 4587 CHECK_EQ(0, a0->Length()); 4588 4589 const char* fun2 = "f(11)"; 4590 Local<v8::Array> a1 = CompileRun(fun2).As<v8::Array>(); 4591 CHECK_EQ(1, a1->Length()); 4592 CHECK_EQ(11, a1->Get(0)->Int32Value()); 4593 4594 const char* fun3 = "f(12, 13)"; 4595 Local<v8::Array> a2 = CompileRun(fun3).As<v8::Array>(); 4596 CHECK_EQ(2, a2->Length()); 4597 CHECK_EQ(12, a2->Get(0)->Int32Value()); 4598 CHECK_EQ(13, a2->Get(1)->Int32Value()); 4599 4600 const char* fun4 = "f(14, 15, 16)"; 4601 Local<v8::Array> a3 = CompileRun(fun4).As<v8::Array>(); 4602 CHECK_EQ(3, a3->Length()); 4603 CHECK_EQ(14, a3->Get(0)->Int32Value()); 4604 CHECK_EQ(15, a3->Get(1)->Int32Value()); 4605 CHECK_EQ(16, a3->Get(2)->Int32Value()); 4606 4607 const char* fun5 = "f(17, 18, 19, 20)"; 4608 Local<v8::Array> a4 = CompileRun(fun5).As<v8::Array>(); 4609 CHECK_EQ(4, a4->Length()); 4610 CHECK_EQ(17, a4->Get(0)->Int32Value()); 4611 CHECK_EQ(18, a4->Get(1)->Int32Value()); 4612 CHECK_EQ(19, a4->Get(2)->Int32Value()); 4613 CHECK_EQ(20, a4->Get(3)->Int32Value()); 4614 } 4615 4616 4617 THREADED_TEST(FunctionCall) { 4618 LocalContext context; 4619 v8::Isolate* isolate = context->GetIsolate(); 4620 v8::HandleScope scope(isolate); 4621 CompileRun( 4622 "function Foo() {" 4623 " var result = [];" 4624 " for (var i = 0; i < arguments.length; i++) {" 4625 " result.push(arguments[i]);" 4626 " }" 4627 " return result;" 4628 "}" 4629 "function ReturnThisSloppy() {" 4630 " return this;" 4631 "}" 4632 "function ReturnThisStrict() {" 4633 " 'use strict';" 4634 " return this;" 4635 "}"); 4636 Local<Function> Foo = 4637 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 4638 Local<Function> ReturnThisSloppy = 4639 Local<Function>::Cast(context->Global()->Get(v8_str("ReturnThisSloppy"))); 4640 Local<Function> ReturnThisStrict = 4641 Local<Function>::Cast(context->Global()->Get(v8_str("ReturnThisStrict"))); 4642 4643 v8::Handle<Value>* args0 = NULL; 4644 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->Call(Foo, 0, args0)); 4645 CHECK_EQ(0, a0->Length()); 4646 4647 v8::Handle<Value> args1[] = { v8_num(1.1) }; 4648 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->Call(Foo, 1, args1)); 4649 CHECK_EQ(1, a1->Length()); 4650 CHECK_EQ(1.1, a1->Get(v8::Integer::New(isolate, 0))->NumberValue()); 4651 4652 v8::Handle<Value> args2[] = { v8_num(2.2), 4653 v8_num(3.3) }; 4654 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->Call(Foo, 2, args2)); 4655 CHECK_EQ(2, a2->Length()); 4656 CHECK_EQ(2.2, a2->Get(v8::Integer::New(isolate, 0))->NumberValue()); 4657 CHECK_EQ(3.3, a2->Get(v8::Integer::New(isolate, 1))->NumberValue()); 4658 4659 v8::Handle<Value> args3[] = { v8_num(4.4), 4660 v8_num(5.5), 4661 v8_num(6.6) }; 4662 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->Call(Foo, 3, args3)); 4663 CHECK_EQ(3, a3->Length()); 4664 CHECK_EQ(4.4, a3->Get(v8::Integer::New(isolate, 0))->NumberValue()); 4665 CHECK_EQ(5.5, a3->Get(v8::Integer::New(isolate, 1))->NumberValue()); 4666 CHECK_EQ(6.6, a3->Get(v8::Integer::New(isolate, 2))->NumberValue()); 4667 4668 v8::Handle<Value> args4[] = { v8_num(7.7), 4669 v8_num(8.8), 4670 v8_num(9.9), 4671 v8_num(10.11) }; 4672 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->Call(Foo, 4, args4)); 4673 CHECK_EQ(4, a4->Length()); 4674 CHECK_EQ(7.7, a4->Get(v8::Integer::New(isolate, 0))->NumberValue()); 4675 CHECK_EQ(8.8, a4->Get(v8::Integer::New(isolate, 1))->NumberValue()); 4676 CHECK_EQ(9.9, a4->Get(v8::Integer::New(isolate, 2))->NumberValue()); 4677 CHECK_EQ(10.11, a4->Get(v8::Integer::New(isolate, 3))->NumberValue()); 4678 4679 Local<v8::Value> r1 = ReturnThisSloppy->Call(v8::Undefined(isolate), 0, NULL); 4680 CHECK(r1->StrictEquals(context->Global())); 4681 Local<v8::Value> r2 = ReturnThisSloppy->Call(v8::Null(isolate), 0, NULL); 4682 CHECK(r2->StrictEquals(context->Global())); 4683 Local<v8::Value> r3 = ReturnThisSloppy->Call(v8_num(42), 0, NULL); 4684 CHECK(r3->IsNumberObject()); 4685 CHECK_EQ(42.0, r3.As<v8::NumberObject>()->ValueOf()); 4686 Local<v8::Value> r4 = ReturnThisSloppy->Call(v8_str("hello"), 0, NULL); 4687 CHECK(r4->IsStringObject()); 4688 CHECK(r4.As<v8::StringObject>()->ValueOf()->StrictEquals(v8_str("hello"))); 4689 Local<v8::Value> r5 = ReturnThisSloppy->Call(v8::True(isolate), 0, NULL); 4690 CHECK(r5->IsBooleanObject()); 4691 CHECK(r5.As<v8::BooleanObject>()->ValueOf()); 4692 4693 Local<v8::Value> r6 = ReturnThisStrict->Call(v8::Undefined(isolate), 0, NULL); 4694 CHECK(r6->IsUndefined()); 4695 Local<v8::Value> r7 = ReturnThisStrict->Call(v8::Null(isolate), 0, NULL); 4696 CHECK(r7->IsNull()); 4697 Local<v8::Value> r8 = ReturnThisStrict->Call(v8_num(42), 0, NULL); 4698 CHECK(r8->StrictEquals(v8_num(42))); 4699 Local<v8::Value> r9 = ReturnThisStrict->Call(v8_str("hello"), 0, NULL); 4700 CHECK(r9->StrictEquals(v8_str("hello"))); 4701 Local<v8::Value> r10 = ReturnThisStrict->Call(v8::True(isolate), 0, NULL); 4702 CHECK(r10->StrictEquals(v8::True(isolate))); 4703 } 4704 4705 4706 THREADED_TEST(ConstructCall) { 4707 LocalContext context; 4708 v8::Isolate* isolate = context->GetIsolate(); 4709 v8::HandleScope scope(isolate); 4710 CompileRun( 4711 "function Foo() {" 4712 " var result = [];" 4713 " for (var i = 0; i < arguments.length; i++) {" 4714 " result.push(arguments[i]);" 4715 " }" 4716 " return result;" 4717 "}"); 4718 Local<Function> Foo = 4719 Local<Function>::Cast(context->Global()->Get(v8_str("Foo"))); 4720 4721 v8::Handle<Value>* args0 = NULL; 4722 Local<v8::Array> a0 = Local<v8::Array>::Cast(Foo->NewInstance(0, args0)); 4723 CHECK_EQ(0, a0->Length()); 4724 4725 v8::Handle<Value> args1[] = { v8_num(1.1) }; 4726 Local<v8::Array> a1 = Local<v8::Array>::Cast(Foo->NewInstance(1, args1)); 4727 CHECK_EQ(1, a1->Length()); 4728 CHECK_EQ(1.1, a1->Get(v8::Integer::New(isolate, 0))->NumberValue()); 4729 4730 v8::Handle<Value> args2[] = { v8_num(2.2), 4731 v8_num(3.3) }; 4732 Local<v8::Array> a2 = Local<v8::Array>::Cast(Foo->NewInstance(2, args2)); 4733 CHECK_EQ(2, a2->Length()); 4734 CHECK_EQ(2.2, a2->Get(v8::Integer::New(isolate, 0))->NumberValue()); 4735 CHECK_EQ(3.3, a2->Get(v8::Integer::New(isolate, 1))->NumberValue()); 4736 4737 v8::Handle<Value> args3[] = { v8_num(4.4), 4738 v8_num(5.5), 4739 v8_num(6.6) }; 4740 Local<v8::Array> a3 = Local<v8::Array>::Cast(Foo->NewInstance(3, args3)); 4741 CHECK_EQ(3, a3->Length()); 4742 CHECK_EQ(4.4, a3->Get(v8::Integer::New(isolate, 0))->NumberValue()); 4743 CHECK_EQ(5.5, a3->Get(v8::Integer::New(isolate, 1))->NumberValue()); 4744 CHECK_EQ(6.6, a3->Get(v8::Integer::New(isolate, 2))->NumberValue()); 4745 4746 v8::Handle<Value> args4[] = { v8_num(7.7), 4747 v8_num(8.8), 4748 v8_num(9.9), 4749 v8_num(10.11) }; 4750 Local<v8::Array> a4 = Local<v8::Array>::Cast(Foo->NewInstance(4, args4)); 4751 CHECK_EQ(4, a4->Length()); 4752 CHECK_EQ(7.7, a4->Get(v8::Integer::New(isolate, 0))->NumberValue()); 4753 CHECK_EQ(8.8, a4->Get(v8::Integer::New(isolate, 1))->NumberValue()); 4754 CHECK_EQ(9.9, a4->Get(v8::Integer::New(isolate, 2))->NumberValue()); 4755 CHECK_EQ(10.11, a4->Get(v8::Integer::New(isolate, 3))->NumberValue()); 4756 } 4757 4758 4759 static void CheckUncle(v8::TryCatch* try_catch) { 4760 CHECK(try_catch->HasCaught()); 4761 String::Utf8Value str_value(try_catch->Exception()); 4762 CHECK_EQ(*str_value, "uncle?"); 4763 try_catch->Reset(); 4764 } 4765 4766 4767 THREADED_TEST(ConversionNumber) { 4768 LocalContext env; 4769 v8::HandleScope scope(env->GetIsolate()); 4770 // Very large number. 4771 CompileRun("var obj = Math.pow(2,32) * 1237;"); 4772 Local<Value> obj = env->Global()->Get(v8_str("obj")); 4773 CHECK_EQ(5312874545152.0, obj->ToNumber()->Value()); 4774 CHECK_EQ(0, obj->ToInt32()->Value()); 4775 CHECK(0u == obj->ToUint32()->Value()); // NOLINT - no CHECK_EQ for unsigned. 4776 // Large number. 4777 CompileRun("var obj = -1234567890123;"); 4778 obj = env->Global()->Get(v8_str("obj")); 4779 CHECK_EQ(-1234567890123.0, obj->ToNumber()->Value()); 4780 CHECK_EQ(-1912276171, obj->ToInt32()->Value()); 4781 CHECK(2382691125u == obj->ToUint32()->Value()); // NOLINT 4782 // Small positive integer. 4783 CompileRun("var obj = 42;"); 4784 obj = env->Global()->Get(v8_str("obj")); 4785 CHECK_EQ(42.0, obj->ToNumber()->Value()); 4786 CHECK_EQ(42, obj->ToInt32()->Value()); 4787 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 4788 // Negative integer. 4789 CompileRun("var obj = -37;"); 4790 obj = env->Global()->Get(v8_str("obj")); 4791 CHECK_EQ(-37.0, obj->ToNumber()->Value()); 4792 CHECK_EQ(-37, obj->ToInt32()->Value()); 4793 CHECK(4294967259u == obj->ToUint32()->Value()); // NOLINT 4794 // Positive non-int32 integer. 4795 CompileRun("var obj = 0x81234567;"); 4796 obj = env->Global()->Get(v8_str("obj")); 4797 CHECK_EQ(2166572391.0, obj->ToNumber()->Value()); 4798 CHECK_EQ(-2128394905, obj->ToInt32()->Value()); 4799 CHECK(2166572391u == obj->ToUint32()->Value()); // NOLINT 4800 // Fraction. 4801 CompileRun("var obj = 42.3;"); 4802 obj = env->Global()->Get(v8_str("obj")); 4803 CHECK_EQ(42.3, obj->ToNumber()->Value()); 4804 CHECK_EQ(42, obj->ToInt32()->Value()); 4805 CHECK(42u == obj->ToUint32()->Value()); // NOLINT 4806 // Large negative fraction. 4807 CompileRun("var obj = -5726623061.75;"); 4808 obj = env->Global()->Get(v8_str("obj")); 4809 CHECK_EQ(-5726623061.75, obj->ToNumber()->Value()); 4810 CHECK_EQ(-1431655765, obj->ToInt32()->Value()); 4811 CHECK(2863311531u == obj->ToUint32()->Value()); // NOLINT 4812 } 4813 4814 4815 THREADED_TEST(isNumberType) { 4816 LocalContext env; 4817 v8::HandleScope scope(env->GetIsolate()); 4818 // Very large number. 4819 CompileRun("var obj = Math.pow(2,32) * 1237;"); 4820 Local<Value> obj = env->Global()->Get(v8_str("obj")); 4821 CHECK(!obj->IsInt32()); 4822 CHECK(!obj->IsUint32()); 4823 // Large negative number. 4824 CompileRun("var obj = -1234567890123;"); 4825 obj = env->Global()->Get(v8_str("obj")); 4826 CHECK(!obj->IsInt32()); 4827 CHECK(!obj->IsUint32()); 4828 // Small positive integer. 4829 CompileRun("var obj = 42;"); 4830 obj = env->Global()->Get(v8_str("obj")); 4831 CHECK(obj->IsInt32()); 4832 CHECK(obj->IsUint32()); 4833 // Negative integer. 4834 CompileRun("var obj = -37;"); 4835 obj = env->Global()->Get(v8_str("obj")); 4836 CHECK(obj->IsInt32()); 4837 CHECK(!obj->IsUint32()); 4838 // Positive non-int32 integer. 4839 CompileRun("var obj = 0x81234567;"); 4840 obj = env->Global()->Get(v8_str("obj")); 4841 CHECK(!obj->IsInt32()); 4842 CHECK(obj->IsUint32()); 4843 // Fraction. 4844 CompileRun("var obj = 42.3;"); 4845 obj = env->Global()->Get(v8_str("obj")); 4846 CHECK(!obj->IsInt32()); 4847 CHECK(!obj->IsUint32()); 4848 // Large negative fraction. 4849 CompileRun("var obj = -5726623061.75;"); 4850 obj = env->Global()->Get(v8_str("obj")); 4851 CHECK(!obj->IsInt32()); 4852 CHECK(!obj->IsUint32()); 4853 // Positive zero 4854 CompileRun("var obj = 0.0;"); 4855 obj = env->Global()->Get(v8_str("obj")); 4856 CHECK(obj->IsInt32()); 4857 CHECK(obj->IsUint32()); 4858 // Positive zero 4859 CompileRun("var obj = -0.0;"); 4860 obj = env->Global()->Get(v8_str("obj")); 4861 CHECK(!obj->IsInt32()); 4862 CHECK(!obj->IsUint32()); 4863 } 4864 4865 4866 THREADED_TEST(ConversionException) { 4867 LocalContext env; 4868 v8::Isolate* isolate = env->GetIsolate(); 4869 v8::HandleScope scope(isolate); 4870 CompileRun( 4871 "function TestClass() { };" 4872 "TestClass.prototype.toString = function () { throw 'uncle?'; };" 4873 "var obj = new TestClass();"); 4874 Local<Value> obj = env->Global()->Get(v8_str("obj")); 4875 4876 v8::TryCatch try_catch; 4877 4878 Local<Value> to_string_result = obj->ToString(); 4879 CHECK(to_string_result.IsEmpty()); 4880 CheckUncle(&try_catch); 4881 4882 Local<Value> to_number_result = obj->ToNumber(); 4883 CHECK(to_number_result.IsEmpty()); 4884 CheckUncle(&try_catch); 4885 4886 Local<Value> to_integer_result = obj->ToInteger(); 4887 CHECK(to_integer_result.IsEmpty()); 4888 CheckUncle(&try_catch); 4889 4890 Local<Value> to_uint32_result = obj->ToUint32(); 4891 CHECK(to_uint32_result.IsEmpty()); 4892 CheckUncle(&try_catch); 4893 4894 Local<Value> to_int32_result = obj->ToInt32(); 4895 CHECK(to_int32_result.IsEmpty()); 4896 CheckUncle(&try_catch); 4897 4898 Local<Value> to_object_result = v8::Undefined(isolate)->ToObject(); 4899 CHECK(to_object_result.IsEmpty()); 4900 CHECK(try_catch.HasCaught()); 4901 try_catch.Reset(); 4902 4903 int32_t int32_value = obj->Int32Value(); 4904 CHECK_EQ(0, int32_value); 4905 CheckUncle(&try_catch); 4906 4907 uint32_t uint32_value = obj->Uint32Value(); 4908 CHECK_EQ(0, uint32_value); 4909 CheckUncle(&try_catch); 4910 4911 double number_value = obj->NumberValue(); 4912 CHECK_NE(0, std::isnan(number_value)); 4913 CheckUncle(&try_catch); 4914 4915 int64_t integer_value = obj->IntegerValue(); 4916 CHECK_EQ(0.0, static_cast<double>(integer_value)); 4917 CheckUncle(&try_catch); 4918 } 4919 4920 4921 void ThrowFromC(const v8::FunctionCallbackInfo<v8::Value>& args) { 4922 ApiTestFuzzer::Fuzz(); 4923 args.GetIsolate()->ThrowException(v8_str("konto")); 4924 } 4925 4926 4927 void CCatcher(const v8::FunctionCallbackInfo<v8::Value>& args) { 4928 if (args.Length() < 1) { 4929 args.GetReturnValue().Set(false); 4930 return; 4931 } 4932 v8::HandleScope scope(args.GetIsolate()); 4933 v8::TryCatch try_catch; 4934 Local<Value> result = CompileRun(args[0]->ToString()); 4935 CHECK(!try_catch.HasCaught() || result.IsEmpty()); 4936 args.GetReturnValue().Set(try_catch.HasCaught()); 4937 } 4938 4939 4940 THREADED_TEST(APICatch) { 4941 v8::Isolate* isolate = CcTest::isolate(); 4942 v8::HandleScope scope(isolate); 4943 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 4944 templ->Set(v8_str("ThrowFromC"), 4945 v8::FunctionTemplate::New(isolate, ThrowFromC)); 4946 LocalContext context(0, templ); 4947 CompileRun( 4948 "var thrown = false;" 4949 "try {" 4950 " ThrowFromC();" 4951 "} catch (e) {" 4952 " thrown = true;" 4953 "}"); 4954 Local<Value> thrown = context->Global()->Get(v8_str("thrown")); 4955 CHECK(thrown->BooleanValue()); 4956 } 4957 4958 4959 THREADED_TEST(APIThrowTryCatch) { 4960 v8::Isolate* isolate = CcTest::isolate(); 4961 v8::HandleScope scope(isolate); 4962 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 4963 templ->Set(v8_str("ThrowFromC"), 4964 v8::FunctionTemplate::New(isolate, ThrowFromC)); 4965 LocalContext context(0, templ); 4966 v8::TryCatch try_catch; 4967 CompileRun("ThrowFromC();"); 4968 CHECK(try_catch.HasCaught()); 4969 } 4970 4971 4972 // Test that a try-finally block doesn't shadow a try-catch block 4973 // when setting up an external handler. 4974 // 4975 // BUG(271): Some of the exception propagation does not work on the 4976 // ARM simulator because the simulator separates the C++ stack and the 4977 // JS stack. This test therefore fails on the simulator. The test is 4978 // not threaded to allow the threading tests to run on the simulator. 4979 TEST(TryCatchInTryFinally) { 4980 v8::Isolate* isolate = CcTest::isolate(); 4981 v8::HandleScope scope(isolate); 4982 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 4983 templ->Set(v8_str("CCatcher"), 4984 v8::FunctionTemplate::New(isolate, CCatcher)); 4985 LocalContext context(0, templ); 4986 Local<Value> result = CompileRun("try {" 4987 " try {" 4988 " CCatcher('throw 7;');" 4989 " } finally {" 4990 " }" 4991 "} catch (e) {" 4992 "}"); 4993 CHECK(result->IsTrue()); 4994 } 4995 4996 4997 static void check_reference_error_message( 4998 v8::Handle<v8::Message> message, 4999 v8::Handle<v8::Value> data) { 5000 const char* reference_error = "Uncaught ReferenceError: asdf is not defined"; 5001 CHECK(message->Get()->Equals(v8_str(reference_error))); 5002 } 5003 5004 5005 static void Fail(const v8::FunctionCallbackInfo<v8::Value>& args) { 5006 ApiTestFuzzer::Fuzz(); 5007 CHECK(false); 5008 } 5009 5010 5011 // Test that overwritten methods are not invoked on uncaught exception 5012 // formatting. However, they are invoked when performing normal error 5013 // string conversions. 5014 TEST(APIThrowMessageOverwrittenToString) { 5015 v8::Isolate* isolate = CcTest::isolate(); 5016 v8::HandleScope scope(isolate); 5017 v8::V8::AddMessageListener(check_reference_error_message); 5018 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 5019 templ->Set(v8_str("fail"), v8::FunctionTemplate::New(isolate, Fail)); 5020 LocalContext context(NULL, templ); 5021 CompileRun("asdf;"); 5022 CompileRun("var limit = {};" 5023 "limit.valueOf = fail;" 5024 "Error.stackTraceLimit = limit;"); 5025 CompileRun("asdf"); 5026 CompileRun("Array.prototype.pop = fail;"); 5027 CompileRun("Object.prototype.hasOwnProperty = fail;"); 5028 CompileRun("Object.prototype.toString = function f() { return 'Yikes'; }"); 5029 CompileRun("Number.prototype.toString = function f() { return 'Yikes'; }"); 5030 CompileRun("String.prototype.toString = function f() { return 'Yikes'; }"); 5031 CompileRun("ReferenceError.prototype.toString =" 5032 " function() { return 'Whoops' }"); 5033 CompileRun("asdf;"); 5034 CompileRun("ReferenceError.prototype.constructor.name = void 0;"); 5035 CompileRun("asdf;"); 5036 CompileRun("ReferenceError.prototype.constructor = void 0;"); 5037 CompileRun("asdf;"); 5038 CompileRun("ReferenceError.prototype.__proto__ = new Object();"); 5039 CompileRun("asdf;"); 5040 CompileRun("ReferenceError.prototype = new Object();"); 5041 CompileRun("asdf;"); 5042 v8::Handle<Value> string = CompileRun("try { asdf; } catch(e) { e + ''; }"); 5043 CHECK(string->Equals(v8_str("Whoops"))); 5044 CompileRun("ReferenceError.prototype.constructor = new Object();" 5045 "ReferenceError.prototype.constructor.name = 1;" 5046 "Number.prototype.toString = function() { return 'Whoops'; };" 5047 "ReferenceError.prototype.toString = Object.prototype.toString;"); 5048 CompileRun("asdf;"); 5049 v8::V8::RemoveMessageListeners(check_reference_error_message); 5050 } 5051 5052 5053 static void check_custom_error_tostring( 5054 v8::Handle<v8::Message> message, 5055 v8::Handle<v8::Value> data) { 5056 const char* uncaught_error = "Uncaught MyError toString"; 5057 CHECK(message->Get()->Equals(v8_str(uncaught_error))); 5058 } 5059 5060 5061 TEST(CustomErrorToString) { 5062 LocalContext context; 5063 v8::HandleScope scope(context->GetIsolate()); 5064 v8::V8::AddMessageListener(check_custom_error_tostring); 5065 CompileRun( 5066 "function MyError(name, message) { " 5067 " this.name = name; " 5068 " this.message = message; " 5069 "} " 5070 "MyError.prototype = Object.create(Error.prototype); " 5071 "MyError.prototype.toString = function() { " 5072 " return 'MyError toString'; " 5073 "}; " 5074 "throw new MyError('my name', 'my message'); "); 5075 v8::V8::RemoveMessageListeners(check_custom_error_tostring); 5076 } 5077 5078 5079 static void check_custom_error_message( 5080 v8::Handle<v8::Message> message, 5081 v8::Handle<v8::Value> data) { 5082 const char* uncaught_error = "Uncaught MyError: my message"; 5083 printf("%s\n", *v8::String::Utf8Value(message->Get())); 5084 CHECK(message->Get()->Equals(v8_str(uncaught_error))); 5085 } 5086 5087 5088 TEST(CustomErrorMessage) { 5089 LocalContext context; 5090 v8::HandleScope scope(context->GetIsolate()); 5091 v8::V8::AddMessageListener(check_custom_error_message); 5092 5093 // Handlebars. 5094 CompileRun( 5095 "function MyError(msg) { " 5096 " this.name = 'MyError'; " 5097 " this.message = msg; " 5098 "} " 5099 "MyError.prototype = new Error(); " 5100 "throw new MyError('my message'); "); 5101 5102 // Closure. 5103 CompileRun( 5104 "function MyError(msg) { " 5105 " this.name = 'MyError'; " 5106 " this.message = msg; " 5107 "} " 5108 "inherits = function(childCtor, parentCtor) { " 5109 " function tempCtor() {}; " 5110 " tempCtor.prototype = parentCtor.prototype; " 5111 " childCtor.superClass_ = parentCtor.prototype; " 5112 " childCtor.prototype = new tempCtor(); " 5113 " childCtor.prototype.constructor = childCtor; " 5114 "}; " 5115 "inherits(MyError, Error); " 5116 "throw new MyError('my message'); "); 5117 5118 // Object.create. 5119 CompileRun( 5120 "function MyError(msg) { " 5121 " this.name = 'MyError'; " 5122 " this.message = msg; " 5123 "} " 5124 "MyError.prototype = Object.create(Error.prototype); " 5125 "throw new MyError('my message'); "); 5126 5127 v8::V8::RemoveMessageListeners(check_custom_error_message); 5128 } 5129 5130 5131 static void receive_message(v8::Handle<v8::Message> message, 5132 v8::Handle<v8::Value> data) { 5133 message->Get(); 5134 message_received = true; 5135 } 5136 5137 5138 TEST(APIThrowMessage) { 5139 message_received = false; 5140 v8::Isolate* isolate = CcTest::isolate(); 5141 v8::HandleScope scope(isolate); 5142 v8::V8::AddMessageListener(receive_message); 5143 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 5144 templ->Set(v8_str("ThrowFromC"), 5145 v8::FunctionTemplate::New(isolate, ThrowFromC)); 5146 LocalContext context(0, templ); 5147 CompileRun("ThrowFromC();"); 5148 CHECK(message_received); 5149 v8::V8::RemoveMessageListeners(receive_message); 5150 } 5151 5152 5153 TEST(APIThrowMessageAndVerboseTryCatch) { 5154 message_received = false; 5155 v8::Isolate* isolate = CcTest::isolate(); 5156 v8::HandleScope scope(isolate); 5157 v8::V8::AddMessageListener(receive_message); 5158 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 5159 templ->Set(v8_str("ThrowFromC"), 5160 v8::FunctionTemplate::New(isolate, ThrowFromC)); 5161 LocalContext context(0, templ); 5162 v8::TryCatch try_catch; 5163 try_catch.SetVerbose(true); 5164 Local<Value> result = CompileRun("ThrowFromC();"); 5165 CHECK(try_catch.HasCaught()); 5166 CHECK(result.IsEmpty()); 5167 CHECK(message_received); 5168 v8::V8::RemoveMessageListeners(receive_message); 5169 } 5170 5171 5172 TEST(APIStackOverflowAndVerboseTryCatch) { 5173 message_received = false; 5174 LocalContext context; 5175 v8::HandleScope scope(context->GetIsolate()); 5176 v8::V8::AddMessageListener(receive_message); 5177 v8::TryCatch try_catch; 5178 try_catch.SetVerbose(true); 5179 Local<Value> result = CompileRun("function foo() { foo(); } foo();"); 5180 CHECK(try_catch.HasCaught()); 5181 CHECK(result.IsEmpty()); 5182 CHECK(message_received); 5183 v8::V8::RemoveMessageListeners(receive_message); 5184 } 5185 5186 5187 THREADED_TEST(ExternalScriptException) { 5188 v8::Isolate* isolate = CcTest::isolate(); 5189 v8::HandleScope scope(isolate); 5190 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 5191 templ->Set(v8_str("ThrowFromC"), 5192 v8::FunctionTemplate::New(isolate, ThrowFromC)); 5193 LocalContext context(0, templ); 5194 5195 v8::TryCatch try_catch; 5196 Local<Value> result = CompileRun("ThrowFromC(); throw 'panama';"); 5197 CHECK(result.IsEmpty()); 5198 CHECK(try_catch.HasCaught()); 5199 String::Utf8Value exception_value(try_catch.Exception()); 5200 CHECK_EQ("konto", *exception_value); 5201 } 5202 5203 5204 5205 void CThrowCountDown(const v8::FunctionCallbackInfo<v8::Value>& args) { 5206 ApiTestFuzzer::Fuzz(); 5207 CHECK_EQ(4, args.Length()); 5208 int count = args[0]->Int32Value(); 5209 int cInterval = args[2]->Int32Value(); 5210 if (count == 0) { 5211 args.GetIsolate()->ThrowException(v8_str("FromC")); 5212 return; 5213 } else { 5214 Local<v8::Object> global = 5215 args.GetIsolate()->GetCurrentContext()->Global(); 5216 Local<Value> fun = global->Get(v8_str("JSThrowCountDown")); 5217 v8::Handle<Value> argv[] = { v8_num(count - 1), 5218 args[1], 5219 args[2], 5220 args[3] }; 5221 if (count % cInterval == 0) { 5222 v8::TryCatch try_catch; 5223 Local<Value> result = fun.As<Function>()->Call(global, 4, argv); 5224 int expected = args[3]->Int32Value(); 5225 if (try_catch.HasCaught()) { 5226 CHECK_EQ(expected, count); 5227 CHECK(result.IsEmpty()); 5228 CHECK(!CcTest::i_isolate()->has_scheduled_exception()); 5229 } else { 5230 CHECK_NE(expected, count); 5231 } 5232 args.GetReturnValue().Set(result); 5233 return; 5234 } else { 5235 args.GetReturnValue().Set(fun.As<Function>()->Call(global, 4, argv)); 5236 return; 5237 } 5238 } 5239 } 5240 5241 5242 void JSCheck(const v8::FunctionCallbackInfo<v8::Value>& args) { 5243 ApiTestFuzzer::Fuzz(); 5244 CHECK_EQ(3, args.Length()); 5245 bool equality = args[0]->BooleanValue(); 5246 int count = args[1]->Int32Value(); 5247 int expected = args[2]->Int32Value(); 5248 if (equality) { 5249 CHECK_EQ(count, expected); 5250 } else { 5251 CHECK_NE(count, expected); 5252 } 5253 } 5254 5255 5256 THREADED_TEST(EvalInTryFinally) { 5257 LocalContext context; 5258 v8::HandleScope scope(context->GetIsolate()); 5259 v8::TryCatch try_catch; 5260 CompileRun("(function() {" 5261 " try {" 5262 " eval('asldkf (*&^&*^');" 5263 " } finally {" 5264 " return;" 5265 " }" 5266 "})()"); 5267 CHECK(!try_catch.HasCaught()); 5268 } 5269 5270 5271 // This test works by making a stack of alternating JavaScript and C 5272 // activations. These activations set up exception handlers with regular 5273 // intervals, one interval for C activations and another for JavaScript 5274 // activations. When enough activations have been created an exception is 5275 // thrown and we check that the right activation catches the exception and that 5276 // no other activations do. The right activation is always the topmost one with 5277 // a handler, regardless of whether it is in JavaScript or C. 5278 // 5279 // The notation used to describe a test case looks like this: 5280 // 5281 // *JS[4] *C[3] @JS[2] C[1] JS[0] 5282 // 5283 // Each entry is an activation, either JS or C. The index is the count at that 5284 // level. Stars identify activations with exception handlers, the @ identifies 5285 // the exception handler that should catch the exception. 5286 // 5287 // BUG(271): Some of the exception propagation does not work on the 5288 // ARM simulator because the simulator separates the C++ stack and the 5289 // JS stack. This test therefore fails on the simulator. The test is 5290 // not threaded to allow the threading tests to run on the simulator. 5291 TEST(ExceptionOrder) { 5292 v8::Isolate* isolate = CcTest::isolate(); 5293 v8::HandleScope scope(isolate); 5294 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 5295 templ->Set(v8_str("check"), v8::FunctionTemplate::New(isolate, JSCheck)); 5296 templ->Set(v8_str("CThrowCountDown"), 5297 v8::FunctionTemplate::New(isolate, CThrowCountDown)); 5298 LocalContext context(0, templ); 5299 CompileRun( 5300 "function JSThrowCountDown(count, jsInterval, cInterval, expected) {" 5301 " if (count == 0) throw 'FromJS';" 5302 " if (count % jsInterval == 0) {" 5303 " try {" 5304 " var value = CThrowCountDown(count - 1," 5305 " jsInterval," 5306 " cInterval," 5307 " expected);" 5308 " check(false, count, expected);" 5309 " return value;" 5310 " } catch (e) {" 5311 " check(true, count, expected);" 5312 " }" 5313 " } else {" 5314 " return CThrowCountDown(count - 1, jsInterval, cInterval, expected);" 5315 " }" 5316 "}"); 5317 Local<Function> fun = 5318 Local<Function>::Cast(context->Global()->Get(v8_str("JSThrowCountDown"))); 5319 5320 const int argc = 4; 5321 // count jsInterval cInterval expected 5322 5323 // *JS[4] *C[3] @JS[2] C[1] JS[0] 5324 v8::Handle<Value> a0[argc] = { v8_num(4), v8_num(2), v8_num(3), v8_num(2) }; 5325 fun->Call(fun, argc, a0); 5326 5327 // JS[5] *C[4] JS[3] @C[2] JS[1] C[0] 5328 v8::Handle<Value> a1[argc] = { v8_num(5), v8_num(6), v8_num(1), v8_num(2) }; 5329 fun->Call(fun, argc, a1); 5330 5331 // JS[6] @C[5] JS[4] C[3] JS[2] C[1] JS[0] 5332 v8::Handle<Value> a2[argc] = { v8_num(6), v8_num(7), v8_num(5), v8_num(5) }; 5333 fun->Call(fun, argc, a2); 5334 5335 // @JS[6] C[5] JS[4] C[3] JS[2] C[1] JS[0] 5336 v8::Handle<Value> a3[argc] = { v8_num(6), v8_num(6), v8_num(7), v8_num(6) }; 5337 fun->Call(fun, argc, a3); 5338 5339 // JS[6] *C[5] @JS[4] C[3] JS[2] C[1] JS[0] 5340 v8::Handle<Value> a4[argc] = { v8_num(6), v8_num(4), v8_num(5), v8_num(4) }; 5341 fun->Call(fun, argc, a4); 5342 5343 // JS[6] C[5] *JS[4] @C[3] JS[2] C[1] JS[0] 5344 v8::Handle<Value> a5[argc] = { v8_num(6), v8_num(4), v8_num(3), v8_num(3) }; 5345 fun->Call(fun, argc, a5); 5346 } 5347 5348 5349 void ThrowValue(const v8::FunctionCallbackInfo<v8::Value>& args) { 5350 ApiTestFuzzer::Fuzz(); 5351 CHECK_EQ(1, args.Length()); 5352 args.GetIsolate()->ThrowException(args[0]); 5353 } 5354 5355 5356 THREADED_TEST(ThrowValues) { 5357 v8::Isolate* isolate = CcTest::isolate(); 5358 v8::HandleScope scope(isolate); 5359 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 5360 templ->Set(v8_str("Throw"), v8::FunctionTemplate::New(isolate, ThrowValue)); 5361 LocalContext context(0, templ); 5362 v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun( 5363 "function Run(obj) {" 5364 " try {" 5365 " Throw(obj);" 5366 " } catch (e) {" 5367 " return e;" 5368 " }" 5369 " return 'no exception';" 5370 "}" 5371 "[Run('str'), Run(1), Run(0), Run(null), Run(void 0)];")); 5372 CHECK_EQ(5, result->Length()); 5373 CHECK(result->Get(v8::Integer::New(isolate, 0))->IsString()); 5374 CHECK(result->Get(v8::Integer::New(isolate, 1))->IsNumber()); 5375 CHECK_EQ(1, result->Get(v8::Integer::New(isolate, 1))->Int32Value()); 5376 CHECK(result->Get(v8::Integer::New(isolate, 2))->IsNumber()); 5377 CHECK_EQ(0, result->Get(v8::Integer::New(isolate, 2))->Int32Value()); 5378 CHECK(result->Get(v8::Integer::New(isolate, 3))->IsNull()); 5379 CHECK(result->Get(v8::Integer::New(isolate, 4))->IsUndefined()); 5380 } 5381 5382 5383 THREADED_TEST(CatchZero) { 5384 LocalContext context; 5385 v8::HandleScope scope(context->GetIsolate()); 5386 v8::TryCatch try_catch; 5387 CHECK(!try_catch.HasCaught()); 5388 CompileRun("throw 10"); 5389 CHECK(try_catch.HasCaught()); 5390 CHECK_EQ(10, try_catch.Exception()->Int32Value()); 5391 try_catch.Reset(); 5392 CHECK(!try_catch.HasCaught()); 5393 CompileRun("throw 0"); 5394 CHECK(try_catch.HasCaught()); 5395 CHECK_EQ(0, try_catch.Exception()->Int32Value()); 5396 } 5397 5398 5399 THREADED_TEST(CatchExceptionFromWith) { 5400 LocalContext context; 5401 v8::HandleScope scope(context->GetIsolate()); 5402 v8::TryCatch try_catch; 5403 CHECK(!try_catch.HasCaught()); 5404 CompileRun("var o = {}; with (o) { throw 42; }"); 5405 CHECK(try_catch.HasCaught()); 5406 } 5407 5408 5409 THREADED_TEST(TryCatchAndFinallyHidingException) { 5410 LocalContext context; 5411 v8::HandleScope scope(context->GetIsolate()); 5412 v8::TryCatch try_catch; 5413 CHECK(!try_catch.HasCaught()); 5414 CompileRun("function f(k) { try { this[k]; } finally { return 0; } };"); 5415 CompileRun("f({toString: function() { throw 42; }});"); 5416 CHECK(!try_catch.HasCaught()); 5417 } 5418 5419 5420 void WithTryCatch(const v8::FunctionCallbackInfo<v8::Value>& args) { 5421 v8::TryCatch try_catch; 5422 } 5423 5424 5425 THREADED_TEST(TryCatchAndFinally) { 5426 LocalContext context; 5427 v8::Isolate* isolate = context->GetIsolate(); 5428 v8::HandleScope scope(isolate); 5429 context->Global()->Set( 5430 v8_str("native_with_try_catch"), 5431 v8::FunctionTemplate::New(isolate, WithTryCatch)->GetFunction()); 5432 v8::TryCatch try_catch; 5433 CHECK(!try_catch.HasCaught()); 5434 CompileRun( 5435 "try {\n" 5436 " throw new Error('a');\n" 5437 "} finally {\n" 5438 " native_with_try_catch();\n" 5439 "}\n"); 5440 CHECK(try_catch.HasCaught()); 5441 } 5442 5443 5444 static void TryCatchNested1Helper(int depth) { 5445 if (depth > 0) { 5446 v8::TryCatch try_catch; 5447 try_catch.SetVerbose(true); 5448 TryCatchNested1Helper(depth - 1); 5449 CHECK(try_catch.HasCaught()); 5450 try_catch.ReThrow(); 5451 } else { 5452 CcTest::isolate()->ThrowException(v8_str("E1")); 5453 } 5454 } 5455 5456 5457 static void TryCatchNested2Helper(int depth) { 5458 if (depth > 0) { 5459 v8::TryCatch try_catch; 5460 try_catch.SetVerbose(true); 5461 TryCatchNested2Helper(depth - 1); 5462 CHECK(try_catch.HasCaught()); 5463 try_catch.ReThrow(); 5464 } else { 5465 CompileRun("throw 'E2';"); 5466 } 5467 } 5468 5469 5470 TEST(TryCatchNested) { 5471 v8::V8::Initialize(); 5472 LocalContext context; 5473 v8::HandleScope scope(context->GetIsolate()); 5474 5475 { 5476 // Test nested try-catch with a native throw in the end. 5477 v8::TryCatch try_catch; 5478 TryCatchNested1Helper(5); 5479 CHECK(try_catch.HasCaught()); 5480 CHECK_EQ(0, strcmp(*v8::String::Utf8Value(try_catch.Exception()), "E1")); 5481 } 5482 5483 { 5484 // Test nested try-catch with a JavaScript throw in the end. 5485 v8::TryCatch try_catch; 5486 TryCatchNested2Helper(5); 5487 CHECK(try_catch.HasCaught()); 5488 CHECK_EQ(0, strcmp(*v8::String::Utf8Value(try_catch.Exception()), "E2")); 5489 } 5490 } 5491 5492 5493 void TryCatchMixedNestingCheck(v8::TryCatch* try_catch) { 5494 CHECK(try_catch->HasCaught()); 5495 Handle<Message> message = try_catch->Message(); 5496 Handle<Value> resource = message->GetScriptOrigin().ResourceName(); 5497 CHECK_EQ(0, strcmp(*v8::String::Utf8Value(resource), "inner")); 5498 CHECK_EQ(0, strcmp(*v8::String::Utf8Value(message->Get()), 5499 "Uncaught Error: a")); 5500 CHECK_EQ(1, message->GetLineNumber()); 5501 CHECK_EQ(6, message->GetStartColumn()); 5502 } 5503 5504 5505 void TryCatchMixedNestingHelper( 5506 const v8::FunctionCallbackInfo<v8::Value>& args) { 5507 ApiTestFuzzer::Fuzz(); 5508 v8::TryCatch try_catch; 5509 CompileRunWithOrigin("throw new Error('a');\n", "inner", 0, 0); 5510 CHECK(try_catch.HasCaught()); 5511 TryCatchMixedNestingCheck(&try_catch); 5512 try_catch.ReThrow(); 5513 } 5514 5515 5516 // This test ensures that an outer TryCatch in the following situation: 5517 // C++/TryCatch -> JS -> C++/TryCatch -> JS w/ SyntaxError 5518 // does not clobber the Message object generated for the inner TryCatch. 5519 // This exercises the ability of TryCatch.ReThrow() to restore the 5520 // inner pending Message before throwing the exception again. 5521 TEST(TryCatchMixedNesting) { 5522 v8::Isolate* isolate = CcTest::isolate(); 5523 v8::HandleScope scope(isolate); 5524 v8::V8::Initialize(); 5525 v8::TryCatch try_catch; 5526 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 5527 templ->Set(v8_str("TryCatchMixedNestingHelper"), 5528 v8::FunctionTemplate::New(isolate, TryCatchMixedNestingHelper)); 5529 LocalContext context(0, templ); 5530 CompileRunWithOrigin("TryCatchMixedNestingHelper();\n", "outer", 1, 1); 5531 TryCatchMixedNestingCheck(&try_catch); 5532 } 5533 5534 5535 void TryCatchNativeHelper(const v8::FunctionCallbackInfo<v8::Value>& args) { 5536 ApiTestFuzzer::Fuzz(); 5537 v8::TryCatch try_catch; 5538 args.GetIsolate()->ThrowException(v8_str("boom")); 5539 CHECK(try_catch.HasCaught()); 5540 } 5541 5542 5543 TEST(TryCatchNative) { 5544 v8::Isolate* isolate = CcTest::isolate(); 5545 v8::HandleScope scope(isolate); 5546 v8::V8::Initialize(); 5547 v8::TryCatch try_catch; 5548 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 5549 templ->Set(v8_str("TryCatchNativeHelper"), 5550 v8::FunctionTemplate::New(isolate, TryCatchNativeHelper)); 5551 LocalContext context(0, templ); 5552 CompileRun("TryCatchNativeHelper();"); 5553 CHECK(!try_catch.HasCaught()); 5554 } 5555 5556 5557 void TryCatchNativeResetHelper( 5558 const v8::FunctionCallbackInfo<v8::Value>& args) { 5559 ApiTestFuzzer::Fuzz(); 5560 v8::TryCatch try_catch; 5561 args.GetIsolate()->ThrowException(v8_str("boom")); 5562 CHECK(try_catch.HasCaught()); 5563 try_catch.Reset(); 5564 CHECK(!try_catch.HasCaught()); 5565 } 5566 5567 5568 TEST(TryCatchNativeReset) { 5569 v8::Isolate* isolate = CcTest::isolate(); 5570 v8::HandleScope scope(isolate); 5571 v8::V8::Initialize(); 5572 v8::TryCatch try_catch; 5573 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 5574 templ->Set(v8_str("TryCatchNativeResetHelper"), 5575 v8::FunctionTemplate::New(isolate, TryCatchNativeResetHelper)); 5576 LocalContext context(0, templ); 5577 CompileRun("TryCatchNativeResetHelper();"); 5578 CHECK(!try_catch.HasCaught()); 5579 } 5580 5581 5582 THREADED_TEST(Equality) { 5583 LocalContext context; 5584 v8::Isolate* isolate = context->GetIsolate(); 5585 v8::HandleScope scope(context->GetIsolate()); 5586 // Check that equality works at all before relying on CHECK_EQ 5587 CHECK(v8_str("a")->Equals(v8_str("a"))); 5588 CHECK(!v8_str("a")->Equals(v8_str("b"))); 5589 5590 CHECK_EQ(v8_str("a"), v8_str("a")); 5591 CHECK_NE(v8_str("a"), v8_str("b")); 5592 CHECK_EQ(v8_num(1), v8_num(1)); 5593 CHECK_EQ(v8_num(1.00), v8_num(1)); 5594 CHECK_NE(v8_num(1), v8_num(2)); 5595 5596 // Assume String is not internalized. 5597 CHECK(v8_str("a")->StrictEquals(v8_str("a"))); 5598 CHECK(!v8_str("a")->StrictEquals(v8_str("b"))); 5599 CHECK(!v8_str("5")->StrictEquals(v8_num(5))); 5600 CHECK(v8_num(1)->StrictEquals(v8_num(1))); 5601 CHECK(!v8_num(1)->StrictEquals(v8_num(2))); 5602 CHECK(v8_num(0.0)->StrictEquals(v8_num(-0.0))); 5603 Local<Value> not_a_number = v8_num(v8::base::OS::nan_value()); 5604 CHECK(!not_a_number->StrictEquals(not_a_number)); 5605 CHECK(v8::False(isolate)->StrictEquals(v8::False(isolate))); 5606 CHECK(!v8::False(isolate)->StrictEquals(v8::Undefined(isolate))); 5607 5608 v8::Handle<v8::Object> obj = v8::Object::New(isolate); 5609 v8::Persistent<v8::Object> alias(isolate, obj); 5610 CHECK(v8::Local<v8::Object>::New(isolate, alias)->StrictEquals(obj)); 5611 alias.Reset(); 5612 5613 CHECK(v8_str("a")->SameValue(v8_str("a"))); 5614 CHECK(!v8_str("a")->SameValue(v8_str("b"))); 5615 CHECK(!v8_str("5")->SameValue(v8_num(5))); 5616 CHECK(v8_num(1)->SameValue(v8_num(1))); 5617 CHECK(!v8_num(1)->SameValue(v8_num(2))); 5618 CHECK(!v8_num(0.0)->SameValue(v8_num(-0.0))); 5619 CHECK(not_a_number->SameValue(not_a_number)); 5620 CHECK(v8::False(isolate)->SameValue(v8::False(isolate))); 5621 CHECK(!v8::False(isolate)->SameValue(v8::Undefined(isolate))); 5622 } 5623 5624 5625 THREADED_TEST(MultiRun) { 5626 LocalContext context; 5627 v8::HandleScope scope(context->GetIsolate()); 5628 Local<Script> script = v8_compile("x"); 5629 for (int i = 0; i < 10; i++) 5630 script->Run(); 5631 } 5632 5633 5634 static void GetXValue(Local<String> name, 5635 const v8::PropertyCallbackInfo<v8::Value>& info) { 5636 ApiTestFuzzer::Fuzz(); 5637 CHECK_EQ(info.Data(), v8_str("donut")); 5638 CHECK_EQ(name, v8_str("x")); 5639 info.GetReturnValue().Set(name); 5640 } 5641 5642 5643 THREADED_TEST(SimplePropertyRead) { 5644 LocalContext context; 5645 v8::Isolate* isolate = context->GetIsolate(); 5646 v8::HandleScope scope(isolate); 5647 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 5648 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 5649 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 5650 Local<Script> script = v8_compile("obj.x"); 5651 for (int i = 0; i < 10; i++) { 5652 Local<Value> result = script->Run(); 5653 CHECK_EQ(result, v8_str("x")); 5654 } 5655 } 5656 5657 5658 THREADED_TEST(DefinePropertyOnAPIAccessor) { 5659 LocalContext context; 5660 v8::Isolate* isolate = context->GetIsolate(); 5661 v8::HandleScope scope(isolate); 5662 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 5663 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 5664 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 5665 5666 // Uses getOwnPropertyDescriptor to check the configurable status 5667 Local<Script> script_desc = v8_compile( 5668 "var prop = Object.getOwnPropertyDescriptor( " 5669 "obj, 'x');" 5670 "prop.configurable;"); 5671 Local<Value> result = script_desc->Run(); 5672 CHECK_EQ(result->BooleanValue(), true); 5673 5674 // Redefine get - but still configurable 5675 Local<Script> script_define = v8_compile( 5676 "var desc = { get: function(){return 42; }," 5677 " configurable: true };" 5678 "Object.defineProperty(obj, 'x', desc);" 5679 "obj.x"); 5680 result = script_define->Run(); 5681 CHECK_EQ(result, v8_num(42)); 5682 5683 // Check that the accessor is still configurable 5684 result = script_desc->Run(); 5685 CHECK_EQ(result->BooleanValue(), true); 5686 5687 // Redefine to a non-configurable 5688 script_define = v8_compile( 5689 "var desc = { get: function(){return 43; }," 5690 " configurable: false };" 5691 "Object.defineProperty(obj, 'x', desc);" 5692 "obj.x"); 5693 result = script_define->Run(); 5694 CHECK_EQ(result, v8_num(43)); 5695 result = script_desc->Run(); 5696 CHECK_EQ(result->BooleanValue(), false); 5697 5698 // Make sure that it is not possible to redefine again 5699 v8::TryCatch try_catch; 5700 result = script_define->Run(); 5701 CHECK(try_catch.HasCaught()); 5702 String::Utf8Value exception_value(try_catch.Exception()); 5703 CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); 5704 } 5705 5706 5707 THREADED_TEST(DefinePropertyOnDefineGetterSetter) { 5708 v8::Isolate* isolate = CcTest::isolate(); 5709 v8::HandleScope scope(isolate); 5710 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 5711 templ->SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut")); 5712 LocalContext context; 5713 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 5714 5715 Local<Script> script_desc = v8_compile( 5716 "var prop =" 5717 "Object.getOwnPropertyDescriptor( " 5718 "obj, 'x');" 5719 "prop.configurable;"); 5720 Local<Value> result = script_desc->Run(); 5721 CHECK_EQ(result->BooleanValue(), true); 5722 5723 Local<Script> script_define = v8_compile( 5724 "var desc = {get: function(){return 42; }," 5725 " configurable: true };" 5726 "Object.defineProperty(obj, 'x', desc);" 5727 "obj.x"); 5728 result = script_define->Run(); 5729 CHECK_EQ(result, v8_num(42)); 5730 5731 5732 result = script_desc->Run(); 5733 CHECK_EQ(result->BooleanValue(), true); 5734 5735 5736 script_define = v8_compile( 5737 "var desc = {get: function(){return 43; }," 5738 " configurable: false };" 5739 "Object.defineProperty(obj, 'x', desc);" 5740 "obj.x"); 5741 result = script_define->Run(); 5742 CHECK_EQ(result, v8_num(43)); 5743 result = script_desc->Run(); 5744 5745 CHECK_EQ(result->BooleanValue(), false); 5746 5747 v8::TryCatch try_catch; 5748 result = script_define->Run(); 5749 CHECK(try_catch.HasCaught()); 5750 String::Utf8Value exception_value(try_catch.Exception()); 5751 CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); 5752 } 5753 5754 5755 static v8::Handle<v8::Object> GetGlobalProperty(LocalContext* context, 5756 char const* name) { 5757 return v8::Handle<v8::Object>::Cast((*context)->Global()->Get(v8_str(name))); 5758 } 5759 5760 5761 THREADED_TEST(DefineAPIAccessorOnObject) { 5762 v8::Isolate* isolate = CcTest::isolate(); 5763 v8::HandleScope scope(isolate); 5764 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 5765 LocalContext context; 5766 5767 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 5768 CompileRun("var obj2 = {};"); 5769 5770 CHECK(CompileRun("obj1.x")->IsUndefined()); 5771 CHECK(CompileRun("obj2.x")->IsUndefined()); 5772 5773 CHECK(GetGlobalProperty(&context, "obj1")-> 5774 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 5775 5776 ExpectString("obj1.x", "x"); 5777 CHECK(CompileRun("obj2.x")->IsUndefined()); 5778 5779 CHECK(GetGlobalProperty(&context, "obj2")-> 5780 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 5781 5782 ExpectString("obj1.x", "x"); 5783 ExpectString("obj2.x", "x"); 5784 5785 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 5786 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 5787 5788 CompileRun("Object.defineProperty(obj1, 'x'," 5789 "{ get: function() { return 'y'; }, configurable: true })"); 5790 5791 ExpectString("obj1.x", "y"); 5792 ExpectString("obj2.x", "x"); 5793 5794 CompileRun("Object.defineProperty(obj2, 'x'," 5795 "{ get: function() { return 'y'; }, configurable: true })"); 5796 5797 ExpectString("obj1.x", "y"); 5798 ExpectString("obj2.x", "y"); 5799 5800 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 5801 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 5802 5803 CHECK(GetGlobalProperty(&context, "obj1")-> 5804 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 5805 CHECK(GetGlobalProperty(&context, "obj2")-> 5806 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 5807 5808 ExpectString("obj1.x", "x"); 5809 ExpectString("obj2.x", "x"); 5810 5811 ExpectTrue("Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 5812 ExpectTrue("Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 5813 5814 // Define getters/setters, but now make them not configurable. 5815 CompileRun("Object.defineProperty(obj1, 'x'," 5816 "{ get: function() { return 'z'; }, configurable: false })"); 5817 CompileRun("Object.defineProperty(obj2, 'x'," 5818 "{ get: function() { return 'z'; }, configurable: false })"); 5819 5820 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 5821 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 5822 5823 ExpectString("obj1.x", "z"); 5824 ExpectString("obj2.x", "z"); 5825 5826 CHECK(!GetGlobalProperty(&context, "obj1")-> 5827 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 5828 CHECK(!GetGlobalProperty(&context, "obj2")-> 5829 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 5830 5831 ExpectString("obj1.x", "z"); 5832 ExpectString("obj2.x", "z"); 5833 } 5834 5835 5836 THREADED_TEST(DontDeleteAPIAccessorsCannotBeOverriden) { 5837 v8::Isolate* isolate = CcTest::isolate(); 5838 v8::HandleScope scope(isolate); 5839 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 5840 LocalContext context; 5841 5842 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 5843 CompileRun("var obj2 = {};"); 5844 5845 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 5846 v8_str("x"), 5847 GetXValue, NULL, 5848 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 5849 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 5850 v8_str("x"), 5851 GetXValue, NULL, 5852 v8_str("donut"), v8::DEFAULT, v8::DontDelete)); 5853 5854 ExpectString("obj1.x", "x"); 5855 ExpectString("obj2.x", "x"); 5856 5857 ExpectTrue("!Object.getOwnPropertyDescriptor(obj1, 'x').configurable"); 5858 ExpectTrue("!Object.getOwnPropertyDescriptor(obj2, 'x').configurable"); 5859 5860 CHECK(!GetGlobalProperty(&context, "obj1")-> 5861 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 5862 CHECK(!GetGlobalProperty(&context, "obj2")-> 5863 SetAccessor(v8_str("x"), GetXValue, NULL, v8_str("donut"))); 5864 5865 { 5866 v8::TryCatch try_catch; 5867 CompileRun("Object.defineProperty(obj1, 'x'," 5868 "{get: function() { return 'func'; }})"); 5869 CHECK(try_catch.HasCaught()); 5870 String::Utf8Value exception_value(try_catch.Exception()); 5871 CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); 5872 } 5873 { 5874 v8::TryCatch try_catch; 5875 CompileRun("Object.defineProperty(obj2, 'x'," 5876 "{get: function() { return 'func'; }})"); 5877 CHECK(try_catch.HasCaught()); 5878 String::Utf8Value exception_value(try_catch.Exception()); 5879 CHECK_EQ(*exception_value, "TypeError: Cannot redefine property: x"); 5880 } 5881 } 5882 5883 5884 static void Get239Value(Local<String> name, 5885 const v8::PropertyCallbackInfo<v8::Value>& info) { 5886 ApiTestFuzzer::Fuzz(); 5887 CHECK_EQ(info.Data(), v8_str("donut")); 5888 CHECK_EQ(name, v8_str("239")); 5889 info.GetReturnValue().Set(name); 5890 } 5891 5892 5893 THREADED_TEST(ElementAPIAccessor) { 5894 v8::Isolate* isolate = CcTest::isolate(); 5895 v8::HandleScope scope(isolate); 5896 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 5897 LocalContext context; 5898 5899 context->Global()->Set(v8_str("obj1"), templ->NewInstance()); 5900 CompileRun("var obj2 = {};"); 5901 5902 CHECK(GetGlobalProperty(&context, "obj1")->SetAccessor( 5903 v8_str("239"), 5904 Get239Value, NULL, 5905 v8_str("donut"))); 5906 CHECK(GetGlobalProperty(&context, "obj2")->SetAccessor( 5907 v8_str("239"), 5908 Get239Value, NULL, 5909 v8_str("donut"))); 5910 5911 ExpectString("obj1[239]", "239"); 5912 ExpectString("obj2[239]", "239"); 5913 ExpectString("obj1['239']", "239"); 5914 ExpectString("obj2['239']", "239"); 5915 } 5916 5917 5918 v8::Persistent<Value> xValue; 5919 5920 5921 static void SetXValue(Local<String> name, 5922 Local<Value> value, 5923 const v8::PropertyCallbackInfo<void>& info) { 5924 CHECK_EQ(value, v8_num(4)); 5925 CHECK_EQ(info.Data(), v8_str("donut")); 5926 CHECK_EQ(name, v8_str("x")); 5927 CHECK(xValue.IsEmpty()); 5928 xValue.Reset(info.GetIsolate(), value); 5929 } 5930 5931 5932 THREADED_TEST(SimplePropertyWrite) { 5933 v8::Isolate* isolate = CcTest::isolate(); 5934 v8::HandleScope scope(isolate); 5935 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 5936 templ->SetAccessor(v8_str("x"), GetXValue, SetXValue, v8_str("donut")); 5937 LocalContext context; 5938 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 5939 Local<Script> script = v8_compile("obj.x = 4"); 5940 for (int i = 0; i < 10; i++) { 5941 CHECK(xValue.IsEmpty()); 5942 script->Run(); 5943 CHECK_EQ(v8_num(4), Local<Value>::New(CcTest::isolate(), xValue)); 5944 xValue.Reset(); 5945 } 5946 } 5947 5948 5949 THREADED_TEST(SetterOnly) { 5950 v8::Isolate* isolate = CcTest::isolate(); 5951 v8::HandleScope scope(isolate); 5952 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 5953 templ->SetAccessor(v8_str("x"), NULL, SetXValue, v8_str("donut")); 5954 LocalContext context; 5955 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 5956 Local<Script> script = v8_compile("obj.x = 4; obj.x"); 5957 for (int i = 0; i < 10; i++) { 5958 CHECK(xValue.IsEmpty()); 5959 script->Run(); 5960 CHECK_EQ(v8_num(4), Local<Value>::New(CcTest::isolate(), xValue)); 5961 xValue.Reset(); 5962 } 5963 } 5964 5965 5966 THREADED_TEST(NoAccessors) { 5967 v8::Isolate* isolate = CcTest::isolate(); 5968 v8::HandleScope scope(isolate); 5969 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 5970 templ->SetAccessor(v8_str("x"), 5971 static_cast<v8::AccessorGetterCallback>(NULL), 5972 NULL, 5973 v8_str("donut")); 5974 LocalContext context; 5975 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 5976 Local<Script> script = v8_compile("obj.x = 4; obj.x"); 5977 for (int i = 0; i < 10; i++) { 5978 script->Run(); 5979 } 5980 } 5981 5982 5983 static void XPropertyGetter(Local<String> property, 5984 const v8::PropertyCallbackInfo<v8::Value>& info) { 5985 ApiTestFuzzer::Fuzz(); 5986 CHECK(info.Data()->IsUndefined()); 5987 info.GetReturnValue().Set(property); 5988 } 5989 5990 5991 THREADED_TEST(NamedInterceptorPropertyRead) { 5992 v8::Isolate* isolate = CcTest::isolate(); 5993 v8::HandleScope scope(isolate); 5994 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 5995 templ->SetNamedPropertyHandler(XPropertyGetter); 5996 LocalContext context; 5997 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 5998 Local<Script> script = v8_compile("obj.x"); 5999 for (int i = 0; i < 10; i++) { 6000 Local<Value> result = script->Run(); 6001 CHECK_EQ(result, v8_str("x")); 6002 } 6003 } 6004 6005 6006 THREADED_TEST(NamedInterceptorDictionaryIC) { 6007 v8::Isolate* isolate = CcTest::isolate(); 6008 v8::HandleScope scope(isolate); 6009 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 6010 templ->SetNamedPropertyHandler(XPropertyGetter); 6011 LocalContext context; 6012 // Create an object with a named interceptor. 6013 context->Global()->Set(v8_str("interceptor_obj"), templ->NewInstance()); 6014 Local<Script> script = v8_compile("interceptor_obj.x"); 6015 for (int i = 0; i < 10; i++) { 6016 Local<Value> result = script->Run(); 6017 CHECK_EQ(result, v8_str("x")); 6018 } 6019 // Create a slow case object and a function accessing a property in 6020 // that slow case object (with dictionary probing in generated 6021 // code). Then force object with a named interceptor into slow-case, 6022 // pass it to the function, and check that the interceptor is called 6023 // instead of accessing the local property. 6024 Local<Value> result = 6025 CompileRun("function get_x(o) { return o.x; };" 6026 "var obj = { x : 42, y : 0 };" 6027 "delete obj.y;" 6028 "for (var i = 0; i < 10; i++) get_x(obj);" 6029 "interceptor_obj.x = 42;" 6030 "interceptor_obj.y = 10;" 6031 "delete interceptor_obj.y;" 6032 "get_x(interceptor_obj)"); 6033 CHECK_EQ(result, v8_str("x")); 6034 } 6035 6036 6037 THREADED_TEST(NamedInterceptorDictionaryICMultipleContext) { 6038 v8::Isolate* isolate = CcTest::isolate(); 6039 v8::HandleScope scope(isolate); 6040 v8::Local<Context> context1 = Context::New(isolate); 6041 6042 context1->Enter(); 6043 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 6044 templ->SetNamedPropertyHandler(XPropertyGetter); 6045 // Create an object with a named interceptor. 6046 v8::Local<v8::Object> object = templ->NewInstance(); 6047 context1->Global()->Set(v8_str("interceptor_obj"), object); 6048 6049 // Force the object into the slow case. 6050 CompileRun("interceptor_obj.y = 0;" 6051 "delete interceptor_obj.y;"); 6052 context1->Exit(); 6053 6054 { 6055 // Introduce the object into a different context. 6056 // Repeat named loads to exercise ICs. 6057 LocalContext context2; 6058 context2->Global()->Set(v8_str("interceptor_obj"), object); 6059 Local<Value> result = 6060 CompileRun("function get_x(o) { return o.x; }" 6061 "interceptor_obj.x = 42;" 6062 "for (var i=0; i != 10; i++) {" 6063 " get_x(interceptor_obj);" 6064 "}" 6065 "get_x(interceptor_obj)"); 6066 // Check that the interceptor was actually invoked. 6067 CHECK_EQ(result, v8_str("x")); 6068 } 6069 6070 // Return to the original context and force some object to the slow case 6071 // to cause the NormalizedMapCache to verify. 6072 context1->Enter(); 6073 CompileRun("var obj = { x : 0 }; delete obj.x;"); 6074 context1->Exit(); 6075 } 6076 6077 6078 static void SetXOnPrototypeGetter( 6079 Local<String> property, 6080 const v8::PropertyCallbackInfo<v8::Value>& info) { 6081 // Set x on the prototype object and do not handle the get request. 6082 v8::Handle<v8::Value> proto = info.Holder()->GetPrototype(); 6083 proto.As<v8::Object>()->Set(v8_str("x"), 6084 v8::Integer::New(info.GetIsolate(), 23)); 6085 } 6086 6087 6088 // This is a regression test for http://crbug.com/20104. Map 6089 // transitions should not interfere with post interceptor lookup. 6090 THREADED_TEST(NamedInterceptorMapTransitionRead) { 6091 v8::Isolate* isolate = CcTest::isolate(); 6092 v8::HandleScope scope(isolate); 6093 Local<v8::FunctionTemplate> function_template = 6094 v8::FunctionTemplate::New(isolate); 6095 Local<v8::ObjectTemplate> instance_template 6096 = function_template->InstanceTemplate(); 6097 instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter); 6098 LocalContext context; 6099 context->Global()->Set(v8_str("F"), function_template->GetFunction()); 6100 // Create an instance of F and introduce a map transition for x. 6101 CompileRun("var o = new F(); o.x = 23;"); 6102 // Create an instance of F and invoke the getter. The result should be 23. 6103 Local<Value> result = CompileRun("o = new F(); o.x"); 6104 CHECK_EQ(result->Int32Value(), 23); 6105 } 6106 6107 6108 static void IndexedPropertyGetter( 6109 uint32_t index, 6110 const v8::PropertyCallbackInfo<v8::Value>& info) { 6111 ApiTestFuzzer::Fuzz(); 6112 if (index == 37) { 6113 info.GetReturnValue().Set(v8_num(625)); 6114 } 6115 } 6116 6117 6118 static void IndexedPropertySetter( 6119 uint32_t index, 6120 Local<Value> value, 6121 const v8::PropertyCallbackInfo<v8::Value>& info) { 6122 ApiTestFuzzer::Fuzz(); 6123 if (index == 39) { 6124 info.GetReturnValue().Set(value); 6125 } 6126 } 6127 6128 6129 THREADED_TEST(IndexedInterceptorWithIndexedAccessor) { 6130 v8::Isolate* isolate = CcTest::isolate(); 6131 v8::HandleScope scope(isolate); 6132 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 6133 templ->SetIndexedPropertyHandler(IndexedPropertyGetter, 6134 IndexedPropertySetter); 6135 LocalContext context; 6136 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 6137 Local<Script> getter_script = v8_compile( 6138 "obj.__defineGetter__(\"3\", function(){return 5;});obj[3];"); 6139 Local<Script> setter_script = v8_compile( 6140 "obj.__defineSetter__(\"17\", function(val){this.foo = val;});" 6141 "obj[17] = 23;" 6142 "obj.foo;"); 6143 Local<Script> interceptor_setter_script = v8_compile( 6144 "obj.__defineSetter__(\"39\", function(val){this.foo = \"hit\";});" 6145 "obj[39] = 47;" 6146 "obj.foo;"); // This setter should not run, due to the interceptor. 6147 Local<Script> interceptor_getter_script = v8_compile( 6148 "obj[37];"); 6149 Local<Value> result = getter_script->Run(); 6150 CHECK_EQ(v8_num(5), result); 6151 result = setter_script->Run(); 6152 CHECK_EQ(v8_num(23), result); 6153 result = interceptor_setter_script->Run(); 6154 CHECK_EQ(v8_num(23), result); 6155 result = interceptor_getter_script->Run(); 6156 CHECK_EQ(v8_num(625), result); 6157 } 6158 6159 6160 static void UnboxedDoubleIndexedPropertyGetter( 6161 uint32_t index, 6162 const v8::PropertyCallbackInfo<v8::Value>& info) { 6163 ApiTestFuzzer::Fuzz(); 6164 if (index < 25) { 6165 info.GetReturnValue().Set(v8_num(index)); 6166 } 6167 } 6168 6169 6170 static void UnboxedDoubleIndexedPropertySetter( 6171 uint32_t index, 6172 Local<Value> value, 6173 const v8::PropertyCallbackInfo<v8::Value>& info) { 6174 ApiTestFuzzer::Fuzz(); 6175 if (index < 25) { 6176 info.GetReturnValue().Set(v8_num(index)); 6177 } 6178 } 6179 6180 6181 void UnboxedDoubleIndexedPropertyEnumerator( 6182 const v8::PropertyCallbackInfo<v8::Array>& info) { 6183 // Force the list of returned keys to be stored in a FastDoubleArray. 6184 Local<Script> indexed_property_names_script = v8_compile( 6185 "keys = new Array(); keys[125000] = 1;" 6186 "for(i = 0; i < 80000; i++) { keys[i] = i; };" 6187 "keys.length = 25; keys;"); 6188 Local<Value> result = indexed_property_names_script->Run(); 6189 info.GetReturnValue().Set(Local<v8::Array>::Cast(result)); 6190 } 6191 6192 6193 // Make sure that the the interceptor code in the runtime properly handles 6194 // merging property name lists for double-array-backed arrays. 6195 THREADED_TEST(IndexedInterceptorUnboxedDoubleWithIndexedAccessor) { 6196 v8::Isolate* isolate = CcTest::isolate(); 6197 v8::HandleScope scope(isolate); 6198 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 6199 templ->SetIndexedPropertyHandler(UnboxedDoubleIndexedPropertyGetter, 6200 UnboxedDoubleIndexedPropertySetter, 6201 0, 6202 0, 6203 UnboxedDoubleIndexedPropertyEnumerator); 6204 LocalContext context; 6205 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 6206 // When obj is created, force it to be Stored in a FastDoubleArray. 6207 Local<Script> create_unboxed_double_script = v8_compile( 6208 "obj[125000] = 1; for(i = 0; i < 80000; i+=2) { obj[i] = i; } " 6209 "key_count = 0; " 6210 "for (x in obj) {key_count++;};" 6211 "obj;"); 6212 Local<Value> result = create_unboxed_double_script->Run(); 6213 CHECK(result->ToObject()->HasRealIndexedProperty(2000)); 6214 Local<Script> key_count_check = v8_compile("key_count;"); 6215 result = key_count_check->Run(); 6216 CHECK_EQ(v8_num(40013), result); 6217 } 6218 6219 6220 void SloppyArgsIndexedPropertyEnumerator( 6221 const v8::PropertyCallbackInfo<v8::Array>& info) { 6222 // Force the list of returned keys to be stored in a Arguments object. 6223 Local<Script> indexed_property_names_script = v8_compile( 6224 "function f(w,x) {" 6225 " return arguments;" 6226 "}" 6227 "keys = f(0, 1, 2, 3);" 6228 "keys;"); 6229 Local<Object> result = 6230 Local<Object>::Cast(indexed_property_names_script->Run()); 6231 // Have to populate the handle manually, as it's not Cast-able. 6232 i::Handle<i::JSObject> o = 6233 v8::Utils::OpenHandle<Object, i::JSObject>(result); 6234 i::Handle<i::JSArray> array(reinterpret_cast<i::JSArray*>(*o)); 6235 info.GetReturnValue().Set(v8::Utils::ToLocal(array)); 6236 } 6237 6238 6239 static void SloppyIndexedPropertyGetter( 6240 uint32_t index, 6241 const v8::PropertyCallbackInfo<v8::Value>& info) { 6242 ApiTestFuzzer::Fuzz(); 6243 if (index < 4) { 6244 info.GetReturnValue().Set(v8_num(index)); 6245 } 6246 } 6247 6248 6249 // Make sure that the the interceptor code in the runtime properly handles 6250 // merging property name lists for non-string arguments arrays. 6251 THREADED_TEST(IndexedInterceptorSloppyArgsWithIndexedAccessor) { 6252 v8::Isolate* isolate = CcTest::isolate(); 6253 v8::HandleScope scope(isolate); 6254 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 6255 templ->SetIndexedPropertyHandler(SloppyIndexedPropertyGetter, 6256 0, 6257 0, 6258 0, 6259 SloppyArgsIndexedPropertyEnumerator); 6260 LocalContext context; 6261 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 6262 Local<Script> create_args_script = v8_compile( 6263 "var key_count = 0;" 6264 "for (x in obj) {key_count++;} key_count;"); 6265 Local<Value> result = create_args_script->Run(); 6266 CHECK_EQ(v8_num(4), result); 6267 } 6268 6269 6270 static void IdentityIndexedPropertyGetter( 6271 uint32_t index, 6272 const v8::PropertyCallbackInfo<v8::Value>& info) { 6273 info.GetReturnValue().Set(index); 6274 } 6275 6276 6277 THREADED_TEST(IndexedInterceptorWithGetOwnPropertyDescriptor) { 6278 v8::Isolate* isolate = CcTest::isolate(); 6279 v8::HandleScope scope(isolate); 6280 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 6281 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 6282 6283 LocalContext context; 6284 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 6285 6286 // Check fast object case. 6287 const char* fast_case_code = 6288 "Object.getOwnPropertyDescriptor(obj, 0).value.toString()"; 6289 ExpectString(fast_case_code, "0"); 6290 6291 // Check slow case. 6292 const char* slow_case_code = 6293 "obj.x = 1; delete obj.x;" 6294 "Object.getOwnPropertyDescriptor(obj, 1).value.toString()"; 6295 ExpectString(slow_case_code, "1"); 6296 } 6297 6298 6299 THREADED_TEST(IndexedInterceptorWithNoSetter) { 6300 v8::Isolate* isolate = CcTest::isolate(); 6301 v8::HandleScope scope(isolate); 6302 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 6303 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 6304 6305 LocalContext context; 6306 context->Global()->Set(v8_str("obj"), templ->NewInstance()); 6307 6308 const char* code = 6309 "try {" 6310 " obj[0] = 239;" 6311 " for (var i = 0; i < 100; i++) {" 6312 " var v = obj[0];" 6313 " if (v != 0) throw 'Wrong value ' + v + ' at iteration ' + i;" 6314 " }" 6315 " 'PASSED'" 6316 "} catch(e) {" 6317 " e" 6318 "}"; 6319 ExpectString(code, "PASSED"); 6320 } 6321 6322 6323 THREADED_TEST(IndexedInterceptorWithAccessorCheck) { 6324 v8::Isolate* isolate = CcTest::isolate(); 6325 v8::HandleScope scope(isolate); 6326 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 6327 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 6328 6329 LocalContext context; 6330 Local<v8::Object> obj = templ->NewInstance(); 6331 obj->TurnOnAccessCheck(); 6332 context->Global()->Set(v8_str("obj"), obj); 6333 6334 const char* code = 6335 "var result = 'PASSED';" 6336 "for (var i = 0; i < 100; i++) {" 6337 " try {" 6338 " var v = obj[0];" 6339 " result = 'Wrong value ' + v + ' at iteration ' + i;" 6340 " break;" 6341 " } catch (e) {" 6342 " /* pass */" 6343 " }" 6344 "}" 6345 "result"; 6346 ExpectString(code, "PASSED"); 6347 } 6348 6349 6350 THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) { 6351 i::FLAG_allow_natives_syntax = true; 6352 v8::Isolate* isolate = CcTest::isolate(); 6353 v8::HandleScope scope(isolate); 6354 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 6355 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 6356 6357 LocalContext context; 6358 Local<v8::Object> obj = templ->NewInstance(); 6359 context->Global()->Set(v8_str("obj"), obj); 6360 6361 const char* code = 6362 "var result = 'PASSED';" 6363 "for (var i = 0; i < 100; i++) {" 6364 " var expected = i;" 6365 " if (i == 5) {" 6366 " %EnableAccessChecks(obj);" 6367 " }" 6368 " try {" 6369 " var v = obj[i];" 6370 " if (i == 5) {" 6371 " result = 'Should not have reached this!';" 6372 " break;" 6373 " } else if (v != expected) {" 6374 " result = 'Wrong value ' + v + ' at iteration ' + i;" 6375 " break;" 6376 " }" 6377 " } catch (e) {" 6378 " if (i != 5) {" 6379 " result = e;" 6380 " }" 6381 " }" 6382 " if (i == 5) %DisableAccessChecks(obj);" 6383 "}" 6384 "result"; 6385 ExpectString(code, "PASSED"); 6386 } 6387 6388 6389 THREADED_TEST(IndexedInterceptorWithDifferentIndices) { 6390 v8::Isolate* isolate = CcTest::isolate(); 6391 v8::HandleScope scope(isolate); 6392 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 6393 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 6394 6395 LocalContext context; 6396 Local<v8::Object> obj = templ->NewInstance(); 6397 context->Global()->Set(v8_str("obj"), obj); 6398 6399 const char* code = 6400 "try {" 6401 " for (var i = 0; i < 100; i++) {" 6402 " var v = obj[i];" 6403 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 6404 " }" 6405 " 'PASSED'" 6406 "} catch(e) {" 6407 " e" 6408 "}"; 6409 ExpectString(code, "PASSED"); 6410 } 6411 6412 6413 THREADED_TEST(IndexedInterceptorWithNegativeIndices) { 6414 v8::Isolate* isolate = CcTest::isolate(); 6415 v8::HandleScope scope(isolate); 6416 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 6417 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 6418 6419 LocalContext context; 6420 Local<v8::Object> obj = templ->NewInstance(); 6421 context->Global()->Set(v8_str("obj"), obj); 6422 6423 const char* code = 6424 "try {" 6425 " for (var i = 0; i < 100; i++) {" 6426 " var expected = i;" 6427 " var key = i;" 6428 " if (i == 25) {" 6429 " key = -1;" 6430 " expected = undefined;" 6431 " }" 6432 " if (i == 50) {" 6433 " /* probe minimal Smi number on 32-bit platforms */" 6434 " key = -(1 << 30);" 6435 " expected = undefined;" 6436 " }" 6437 " if (i == 75) {" 6438 " /* probe minimal Smi number on 64-bit platforms */" 6439 " key = 1 << 31;" 6440 " expected = undefined;" 6441 " }" 6442 " var v = obj[key];" 6443 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 6444 " }" 6445 " 'PASSED'" 6446 "} catch(e) {" 6447 " e" 6448 "}"; 6449 ExpectString(code, "PASSED"); 6450 } 6451 6452 6453 THREADED_TEST(IndexedInterceptorWithNotSmiLookup) { 6454 v8::Isolate* isolate = CcTest::isolate(); 6455 v8::HandleScope scope(isolate); 6456 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 6457 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 6458 6459 LocalContext context; 6460 Local<v8::Object> obj = templ->NewInstance(); 6461 context->Global()->Set(v8_str("obj"), obj); 6462 6463 const char* code = 6464 "try {" 6465 " for (var i = 0; i < 100; i++) {" 6466 " var expected = i;" 6467 " var key = i;" 6468 " if (i == 50) {" 6469 " key = 'foobar';" 6470 " expected = undefined;" 6471 " }" 6472 " var v = obj[key];" 6473 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 6474 " }" 6475 " 'PASSED'" 6476 "} catch(e) {" 6477 " e" 6478 "}"; 6479 ExpectString(code, "PASSED"); 6480 } 6481 6482 6483 THREADED_TEST(IndexedInterceptorGoingMegamorphic) { 6484 v8::Isolate* isolate = CcTest::isolate(); 6485 v8::HandleScope scope(isolate); 6486 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 6487 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 6488 6489 LocalContext context; 6490 Local<v8::Object> obj = templ->NewInstance(); 6491 context->Global()->Set(v8_str("obj"), obj); 6492 6493 const char* code = 6494 "var original = obj;" 6495 "try {" 6496 " for (var i = 0; i < 100; i++) {" 6497 " var expected = i;" 6498 " if (i == 50) {" 6499 " obj = {50: 'foobar'};" 6500 " expected = 'foobar';" 6501 " }" 6502 " var v = obj[i];" 6503 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 6504 " if (i == 50) obj = original;" 6505 " }" 6506 " 'PASSED'" 6507 "} catch(e) {" 6508 " e" 6509 "}"; 6510 ExpectString(code, "PASSED"); 6511 } 6512 6513 6514 THREADED_TEST(IndexedInterceptorReceiverTurningSmi) { 6515 v8::Isolate* isolate = CcTest::isolate(); 6516 v8::HandleScope scope(isolate); 6517 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 6518 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 6519 6520 LocalContext context; 6521 Local<v8::Object> obj = templ->NewInstance(); 6522 context->Global()->Set(v8_str("obj"), obj); 6523 6524 const char* code = 6525 "var original = obj;" 6526 "try {" 6527 " for (var i = 0; i < 100; i++) {" 6528 " var expected = i;" 6529 " if (i == 5) {" 6530 " obj = 239;" 6531 " expected = undefined;" 6532 " }" 6533 " var v = obj[i];" 6534 " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" 6535 " if (i == 5) obj = original;" 6536 " }" 6537 " 'PASSED'" 6538 "} catch(e) {" 6539 " e" 6540 "}"; 6541 ExpectString(code, "PASSED"); 6542 } 6543 6544 6545 THREADED_TEST(IndexedInterceptorOnProto) { 6546 v8::Isolate* isolate = CcTest::isolate(); 6547 v8::HandleScope scope(isolate); 6548 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 6549 templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); 6550 6551 LocalContext context; 6552 Local<v8::Object> obj = templ->NewInstance(); 6553 context->Global()->Set(v8_str("obj"), obj); 6554 6555 const char* code = 6556 "var o = {__proto__: obj};" 6557 "try {" 6558 " for (var i = 0; i < 100; i++) {" 6559 " var v = o[i];" 6560 " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" 6561 " }" 6562 " 'PASSED'" 6563 "} catch(e) {" 6564 " e" 6565 "}"; 6566 ExpectString(code, "PASSED"); 6567 } 6568 6569 6570 THREADED_TEST(MultiContexts) { 6571 v8::Isolate* isolate = CcTest::isolate(); 6572 v8::HandleScope scope(isolate); 6573 v8::Handle<ObjectTemplate> templ = ObjectTemplate::New(isolate); 6574 templ->Set(v8_str("dummy"), v8::FunctionTemplate::New(isolate, 6575 DummyCallHandler)); 6576 6577 Local<String> password = v8_str("Password"); 6578 6579 // Create an environment 6580 LocalContext context0(0, templ); 6581 context0->SetSecurityToken(password); 6582 v8::Handle<v8::Object> global0 = context0->Global(); 6583 global0->Set(v8_str("custom"), v8_num(1234)); 6584 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 6585 6586 // Create an independent environment 6587 LocalContext context1(0, templ); 6588 context1->SetSecurityToken(password); 6589 v8::Handle<v8::Object> global1 = context1->Global(); 6590 global1->Set(v8_str("custom"), v8_num(1234)); 6591 CHECK_NE(global0, global1); 6592 CHECK_EQ(1234, global0->Get(v8_str("custom"))->Int32Value()); 6593 CHECK_EQ(1234, global1->Get(v8_str("custom"))->Int32Value()); 6594 6595 // Now create a new context with the old global 6596 LocalContext context2(0, templ, global1); 6597 context2->SetSecurityToken(password); 6598 v8::Handle<v8::Object> global2 = context2->Global(); 6599 CHECK_EQ(global1, global2); 6600 CHECK_EQ(0, global1->Get(v8_str("custom"))->Int32Value()); 6601 CHECK_EQ(0, global2->Get(v8_str("custom"))->Int32Value()); 6602 } 6603 6604 6605 THREADED_TEST(FunctionPrototypeAcrossContexts) { 6606 // Make sure that functions created by cloning boilerplates cannot 6607 // communicate through their __proto__ field. 6608 6609 v8::HandleScope scope(CcTest::isolate()); 6610 6611 LocalContext env0; 6612 v8::Handle<v8::Object> global0 = 6613 env0->Global(); 6614 v8::Handle<v8::Object> object0 = 6615 global0->Get(v8_str("Object")).As<v8::Object>(); 6616 v8::Handle<v8::Object> tostring0 = 6617 object0->Get(v8_str("toString")).As<v8::Object>(); 6618 v8::Handle<v8::Object> proto0 = 6619 tostring0->Get(v8_str("__proto__")).As<v8::Object>(); 6620 proto0->Set(v8_str("custom"), v8_num(1234)); 6621 6622 LocalContext env1; 6623 v8::Handle<v8::Object> global1 = 6624 env1->Global(); 6625 v8::Handle<v8::Object> object1 = 6626 global1->Get(v8_str("Object")).As<v8::Object>(); 6627 v8::Handle<v8::Object> tostring1 = 6628 object1->Get(v8_str("toString")).As<v8::Object>(); 6629 v8::Handle<v8::Object> proto1 = 6630 tostring1->Get(v8_str("__proto__")).As<v8::Object>(); 6631 CHECK(!proto1->Has(v8_str("custom"))); 6632 } 6633 6634 6635 THREADED_TEST(Regress892105) { 6636 // Make sure that object and array literals created by cloning 6637 // boilerplates cannot communicate through their __proto__ 6638 // field. This is rather difficult to check, but we try to add stuff 6639 // to Object.prototype and Array.prototype and create a new 6640 // environment. This should succeed. 6641 6642 v8::HandleScope scope(CcTest::isolate()); 6643 6644 Local<String> source = v8_str("Object.prototype.obj = 1234;" 6645 "Array.prototype.arr = 4567;" 6646 "8901"); 6647 6648 LocalContext env0; 6649 Local<Script> script0 = v8_compile(source); 6650 CHECK_EQ(8901.0, script0->Run()->NumberValue()); 6651 6652 LocalContext env1; 6653 Local<Script> script1 = v8_compile(source); 6654 CHECK_EQ(8901.0, script1->Run()->NumberValue()); 6655 } 6656 6657 6658 THREADED_TEST(UndetectableObject) { 6659 LocalContext env; 6660 v8::HandleScope scope(env->GetIsolate()); 6661 6662 Local<v8::FunctionTemplate> desc = 6663 v8::FunctionTemplate::New(env->GetIsolate()); 6664 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 6665 6666 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 6667 env->Global()->Set(v8_str("undetectable"), obj); 6668 6669 ExpectString("undetectable.toString()", "[object Object]"); 6670 ExpectString("typeof undetectable", "undefined"); 6671 ExpectString("typeof(undetectable)", "undefined"); 6672 ExpectBoolean("typeof undetectable == 'undefined'", true); 6673 ExpectBoolean("typeof undetectable == 'object'", false); 6674 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 6675 ExpectBoolean("!undetectable", true); 6676 6677 ExpectObject("true&&undetectable", obj); 6678 ExpectBoolean("false&&undetectable", false); 6679 ExpectBoolean("true||undetectable", true); 6680 ExpectObject("false||undetectable", obj); 6681 6682 ExpectObject("undetectable&&true", obj); 6683 ExpectObject("undetectable&&false", obj); 6684 ExpectBoolean("undetectable||true", true); 6685 ExpectBoolean("undetectable||false", false); 6686 6687 ExpectBoolean("undetectable==null", true); 6688 ExpectBoolean("null==undetectable", true); 6689 ExpectBoolean("undetectable==undefined", true); 6690 ExpectBoolean("undefined==undetectable", true); 6691 ExpectBoolean("undetectable==undetectable", true); 6692 6693 6694 ExpectBoolean("undetectable===null", false); 6695 ExpectBoolean("null===undetectable", false); 6696 ExpectBoolean("undetectable===undefined", false); 6697 ExpectBoolean("undefined===undetectable", false); 6698 ExpectBoolean("undetectable===undetectable", true); 6699 } 6700 6701 6702 THREADED_TEST(VoidLiteral) { 6703 LocalContext env; 6704 v8::Isolate* isolate = env->GetIsolate(); 6705 v8::HandleScope scope(isolate); 6706 6707 Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate); 6708 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 6709 6710 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 6711 env->Global()->Set(v8_str("undetectable"), obj); 6712 6713 ExpectBoolean("undefined == void 0", true); 6714 ExpectBoolean("undetectable == void 0", true); 6715 ExpectBoolean("null == void 0", true); 6716 ExpectBoolean("undefined === void 0", true); 6717 ExpectBoolean("undetectable === void 0", false); 6718 ExpectBoolean("null === void 0", false); 6719 6720 ExpectBoolean("void 0 == undefined", true); 6721 ExpectBoolean("void 0 == undetectable", true); 6722 ExpectBoolean("void 0 == null", true); 6723 ExpectBoolean("void 0 === undefined", true); 6724 ExpectBoolean("void 0 === undetectable", false); 6725 ExpectBoolean("void 0 === null", false); 6726 6727 ExpectString("(function() {" 6728 " try {" 6729 " return x === void 0;" 6730 " } catch(e) {" 6731 " return e.toString();" 6732 " }" 6733 "})()", 6734 "ReferenceError: x is not defined"); 6735 ExpectString("(function() {" 6736 " try {" 6737 " return void 0 === x;" 6738 " } catch(e) {" 6739 " return e.toString();" 6740 " }" 6741 "})()", 6742 "ReferenceError: x is not defined"); 6743 } 6744 6745 6746 THREADED_TEST(ExtensibleOnUndetectable) { 6747 LocalContext env; 6748 v8::Isolate* isolate = env->GetIsolate(); 6749 v8::HandleScope scope(isolate); 6750 6751 Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate); 6752 desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable 6753 6754 Local<v8::Object> obj = desc->GetFunction()->NewInstance(); 6755 env->Global()->Set(v8_str("undetectable"), obj); 6756 6757 Local<String> source = v8_str("undetectable.x = 42;" 6758 "undetectable.x"); 6759 6760 Local<Script> script = v8_compile(source); 6761 6762 CHECK_EQ(v8::Integer::New(isolate, 42), script->Run()); 6763 6764 ExpectBoolean("Object.isExtensible(undetectable)", true); 6765 6766 source = v8_str("Object.preventExtensions(undetectable);"); 6767 script = v8_compile(source); 6768 script->Run(); 6769 ExpectBoolean("Object.isExtensible(undetectable)", false); 6770 6771 source = v8_str("undetectable.y = 2000;"); 6772 script = v8_compile(source); 6773 script->Run(); 6774 ExpectBoolean("undetectable.y == undefined", true); 6775 } 6776 6777 6778 6779 THREADED_TEST(UndetectableString) { 6780 LocalContext env; 6781 v8::HandleScope scope(env->GetIsolate()); 6782 6783 Local<String> obj = String::NewFromUtf8(env->GetIsolate(), "foo", 6784 String::kUndetectableString); 6785 env->Global()->Set(v8_str("undetectable"), obj); 6786 6787 ExpectString("undetectable", "foo"); 6788 ExpectString("typeof undetectable", "undefined"); 6789 ExpectString("typeof(undetectable)", "undefined"); 6790 ExpectBoolean("typeof undetectable == 'undefined'", true); 6791 ExpectBoolean("typeof undetectable == 'string'", false); 6792 ExpectBoolean("if (undetectable) { true; } else { false; }", false); 6793 ExpectBoolean("!undetectable", true); 6794 6795 ExpectObject("true&&undetectable", obj); 6796 ExpectBoolean("false&&undetectable", false); 6797 ExpectBoolean("true||undetectable", true); 6798 ExpectObject("false||undetectable", obj); 6799 6800 ExpectObject("undetectable&&true", obj); 6801 ExpectObject("undetectable&&false", obj); 6802 ExpectBoolean("undetectable||true", true); 6803 ExpectBoolean("undetectable||false", false); 6804 6805 ExpectBoolean("undetectable==null", true); 6806 ExpectBoolean("null==undetectable", true); 6807 ExpectBoolean("undetectable==undefined", true); 6808 ExpectBoolean("undefined==undetectable", true); 6809 ExpectBoolean("undetectable==undetectable", true); 6810 6811 6812 ExpectBoolean("undetectable===null", false); 6813 ExpectBoolean("null===undetectable", false); 6814 ExpectBoolean("undetectable===undefined", false); 6815 ExpectBoolean("undefined===undetectable", false); 6816 ExpectBoolean("undetectable===undetectable", true); 6817 } 6818 6819 6820 TEST(UndetectableOptimized) { 6821 i::FLAG_allow_natives_syntax = true; 6822 LocalContext env; 6823 v8::HandleScope scope(env->GetIsolate()); 6824 6825 Local<String> obj = String::NewFromUtf8(env->GetIsolate(), "foo", 6826 String::kUndetectableString); 6827 env->Global()->Set(v8_str("undetectable"), obj); 6828 env->Global()->Set(v8_str("detectable"), v8_str("bar")); 6829 6830 ExpectString( 6831 "function testBranch() {" 6832 " if (!%_IsUndetectableObject(undetectable)) throw 1;" 6833 " if (%_IsUndetectableObject(detectable)) throw 2;" 6834 "}\n" 6835 "function testBool() {" 6836 " var b1 = !%_IsUndetectableObject(undetectable);" 6837 " var b2 = %_IsUndetectableObject(detectable);" 6838 " if (b1) throw 3;" 6839 " if (b2) throw 4;" 6840 " return b1 == b2;" 6841 "}\n" 6842 "%OptimizeFunctionOnNextCall(testBranch);" 6843 "%OptimizeFunctionOnNextCall(testBool);" 6844 "for (var i = 0; i < 10; i++) {" 6845 " testBranch();" 6846 " testBool();" 6847 "}\n" 6848 "\"PASS\"", 6849 "PASS"); 6850 } 6851 6852 6853 // The point of this test is type checking. We run it only so compilers 6854 // don't complain about an unused function. 6855 TEST(PersistentHandles) { 6856 LocalContext env; 6857 v8::Isolate* isolate = CcTest::isolate(); 6858 v8::HandleScope scope(isolate); 6859 Local<String> str = v8_str("foo"); 6860 v8::Persistent<String> p_str(isolate, str); 6861 p_str.Reset(); 6862 Local<Script> scr = v8_compile(""); 6863 v8::Persistent<Script> p_scr(isolate, scr); 6864 p_scr.Reset(); 6865 Local<ObjectTemplate> templ = ObjectTemplate::New(isolate); 6866 v8::Persistent<ObjectTemplate> p_templ(isolate, templ); 6867 p_templ.Reset(); 6868 } 6869 6870 6871 static void HandleLogDelegator( 6872 const v8::FunctionCallbackInfo<v8::Value>& args) { 6873 ApiTestFuzzer::Fuzz(); 6874 } 6875 6876 6877 THREADED_TEST(GlobalObjectTemplate) { 6878 v8::Isolate* isolate = CcTest::isolate(); 6879 v8::HandleScope handle_scope(isolate); 6880 Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate); 6881 global_template->Set(v8_str("JSNI_Log"), 6882 v8::FunctionTemplate::New(isolate, HandleLogDelegator)); 6883 v8::Local<Context> context = Context::New(isolate, 0, global_template); 6884 Context::Scope context_scope(context); 6885 CompileRun("JSNI_Log('LOG')"); 6886 } 6887 6888 6889 static const char* kSimpleExtensionSource = 6890 "function Foo() {" 6891 " return 4;" 6892 "}"; 6893 6894 6895 TEST(SimpleExtensions) { 6896 v8::HandleScope handle_scope(CcTest::isolate()); 6897 v8::RegisterExtension(new Extension("simpletest", kSimpleExtensionSource)); 6898 const char* extension_names[] = { "simpletest" }; 6899 v8::ExtensionConfiguration extensions(1, extension_names); 6900 v8::Handle<Context> context = 6901 Context::New(CcTest::isolate(), &extensions); 6902 Context::Scope lock(context); 6903 v8::Handle<Value> result = CompileRun("Foo()"); 6904 CHECK_EQ(result, v8::Integer::New(CcTest::isolate(), 4)); 6905 } 6906 6907 6908 static const char* kStackTraceFromExtensionSource = 6909 "function foo() {" 6910 " throw new Error();" 6911 "}" 6912 "function bar() {" 6913 " foo();" 6914 "}"; 6915 6916 6917 TEST(StackTraceInExtension) { 6918 v8::HandleScope handle_scope(CcTest::isolate()); 6919 v8::RegisterExtension(new Extension("stacktracetest", 6920 kStackTraceFromExtensionSource)); 6921 const char* extension_names[] = { "stacktracetest" }; 6922 v8::ExtensionConfiguration extensions(1, extension_names); 6923 v8::Handle<Context> context = 6924 Context::New(CcTest::isolate(), &extensions); 6925 Context::Scope lock(context); 6926 CompileRun("function user() { bar(); }" 6927 "var error;" 6928 "try{ user(); } catch (e) { error = e; }"); 6929 CHECK_EQ(-1, CompileRun("error.stack.indexOf('foo')")->Int32Value()); 6930 CHECK_EQ(-1, CompileRun("error.stack.indexOf('bar')")->Int32Value()); 6931 CHECK_NE(-1, CompileRun("error.stack.indexOf('user')")->Int32Value()); 6932 } 6933 6934 6935 TEST(NullExtensions) { 6936 v8::HandleScope handle_scope(CcTest::isolate()); 6937 v8::RegisterExtension(new Extension("nulltest", NULL)); 6938 const char* extension_names[] = { "nulltest" }; 6939 v8::ExtensionConfiguration extensions(1, extension_names); 6940 v8::Handle<Context> context = 6941 Context::New(CcTest::isolate(), &extensions); 6942 Context::Scope lock(context); 6943 v8::Handle<Value> result = CompileRun("1+3"); 6944 CHECK_EQ(result, v8::Integer::New(CcTest::isolate(), 4)); 6945 } 6946 6947 6948 static const char* kEmbeddedExtensionSource = 6949 "function Ret54321(){return 54321;}~~@@$" 6950 "$%% THIS IS A SERIES OF NON-NULL-TERMINATED STRINGS."; 6951 static const int kEmbeddedExtensionSourceValidLen = 34; 6952 6953 6954 TEST(ExtensionMissingSourceLength) { 6955 v8::HandleScope handle_scope(CcTest::isolate()); 6956 v8::RegisterExtension(new Extension("srclentest_fail", 6957 kEmbeddedExtensionSource)); 6958 const char* extension_names[] = { "srclentest_fail" }; 6959 v8::ExtensionConfiguration extensions(1, extension_names); 6960 v8::Handle<Context> context = 6961 Context::New(CcTest::isolate(), &extensions); 6962 CHECK_EQ(0, *context); 6963 } 6964 6965 6966 TEST(ExtensionWithSourceLength) { 6967 for (int source_len = kEmbeddedExtensionSourceValidLen - 1; 6968 source_len <= kEmbeddedExtensionSourceValidLen + 1; ++source_len) { 6969 v8::HandleScope handle_scope(CcTest::isolate()); 6970 i::ScopedVector<char> extension_name(32); 6971 i::SNPrintF(extension_name, "ext #%d", source_len); 6972 v8::RegisterExtension(new Extension(extension_name.start(), 6973 kEmbeddedExtensionSource, 0, 0, 6974 source_len)); 6975 const char* extension_names[1] = { extension_name.start() }; 6976 v8::ExtensionConfiguration extensions(1, extension_names); 6977 v8::Handle<Context> context = 6978 Context::New(CcTest::isolate(), &extensions); 6979 if (source_len == kEmbeddedExtensionSourceValidLen) { 6980 Context::Scope lock(context); 6981 v8::Handle<Value> result = CompileRun("Ret54321()"); 6982 CHECK_EQ(v8::Integer::New(CcTest::isolate(), 54321), result); 6983 } else { 6984 // Anything but exactly the right length should fail to compile. 6985 CHECK_EQ(0, *context); 6986 } 6987 } 6988 } 6989 6990 6991 static const char* kEvalExtensionSource1 = 6992 "function UseEval1() {" 6993 " var x = 42;" 6994 " return eval('x');" 6995 "}"; 6996 6997 6998 static const char* kEvalExtensionSource2 = 6999 "(function() {" 7000 " var x = 42;" 7001 " function e() {" 7002 " return eval('x');" 7003 " }" 7004 " this.UseEval2 = e;" 7005 "})()"; 7006 7007 7008 TEST(UseEvalFromExtension) { 7009 v8::HandleScope handle_scope(CcTest::isolate()); 7010 v8::RegisterExtension(new Extension("evaltest1", kEvalExtensionSource1)); 7011 v8::RegisterExtension(new Extension("evaltest2", kEvalExtensionSource2)); 7012 const char* extension_names[] = { "evaltest1", "evaltest2" }; 7013 v8::ExtensionConfiguration extensions(2, extension_names); 7014 v8::Handle<Context> context = 7015 Context::New(CcTest::isolate(), &extensions); 7016 Context::Scope lock(context); 7017 v8::Handle<Value> result = CompileRun("UseEval1()"); 7018 CHECK_EQ(result, v8::Integer::New(CcTest::isolate(), 42)); 7019 result = CompileRun("UseEval2()"); 7020 CHECK_EQ(result, v8::Integer::New(CcTest::isolate(), 42)); 7021 } 7022 7023 7024 static const char* kWithExtensionSource1 = 7025 "function UseWith1() {" 7026 " var x = 42;" 7027 " with({x:87}) { return x; }" 7028 "}"; 7029 7030 7031 7032 static const char* kWithExtensionSource2 = 7033 "(function() {" 7034 " var x = 42;" 7035 " function e() {" 7036 " with ({x:87}) { return x; }" 7037 " }" 7038 " this.UseWith2 = e;" 7039 "})()"; 7040 7041 7042 TEST(UseWithFromExtension) { 7043 v8::HandleScope handle_scope(CcTest::isolate()); 7044 v8::RegisterExtension(new Extension("withtest1", kWithExtensionSource1)); 7045 v8::RegisterExtension(new Extension("withtest2", kWithExtensionSource2)); 7046 const char* extension_names[] = { "withtest1", "withtest2" }; 7047 v8::ExtensionConfiguration extensions(2, extension_names); 7048 v8::Handle<Context> context = 7049 Context::New(CcTest::isolate(), &extensions); 7050 Context::Scope lock(context); 7051 v8::Handle<Value> result = CompileRun("UseWith1()"); 7052 CHECK_EQ(result, v8::Integer::New(CcTest::isolate(), 87)); 7053 result = CompileRun("UseWith2()"); 7054 CHECK_EQ(result, v8::Integer::New(CcTest::isolate(), 87)); 7055 } 7056 7057 7058 TEST(AutoExtensions) { 7059 v8::HandleScope handle_scope(CcTest::isolate()); 7060 Extension* extension = new Extension("autotest", kSimpleExtensionSource); 7061 extension->set_auto_enable(true); 7062 v8::RegisterExtension(extension); 7063 v8::Handle<Context> context = 7064 Context::New(CcTest::isolate()); 7065 Context::Scope lock(context); 7066 v8::Handle<Value> result = CompileRun("Foo()"); 7067 CHECK_EQ(result, v8::Integer::New(CcTest::isolate(), 4)); 7068 } 7069 7070 7071 static const char* kSyntaxErrorInExtensionSource = 7072 "["; 7073 7074 7075 // Test that a syntax error in an extension does not cause a fatal 7076 // error but results in an empty context. 7077 TEST(SyntaxErrorExtensions) { 7078 v8::HandleScope handle_scope(CcTest::isolate()); 7079 v8::RegisterExtension(new Extension("syntaxerror", 7080 kSyntaxErrorInExtensionSource)); 7081 const char* extension_names[] = { "syntaxerror" }; 7082 v8::ExtensionConfiguration extensions(1, extension_names); 7083 v8::Handle<Context> context = 7084 Context::New(CcTest::isolate(), &extensions); 7085 CHECK(context.IsEmpty()); 7086 } 7087 7088 7089 static const char* kExceptionInExtensionSource = 7090 "throw 42"; 7091 7092 7093 // Test that an exception when installing an extension does not cause 7094 // a fatal error but results in an empty context. 7095 TEST(ExceptionExtensions) { 7096 v8::HandleScope handle_scope(CcTest::isolate()); 7097 v8::RegisterExtension(new Extension("exception", 7098 kExceptionInExtensionSource)); 7099 const char* extension_names[] = { "exception" }; 7100 v8::ExtensionConfiguration extensions(1, extension_names); 7101 v8::Handle<Context> context = 7102 Context::New(CcTest::isolate(), &extensions); 7103 CHECK(context.IsEmpty()); 7104 } 7105 7106 7107 static const char* kNativeCallInExtensionSource = 7108 "function call_runtime_last_index_of(x) {" 7109 " return %StringLastIndexOf(x, 'bob', 10);" 7110 "}"; 7111 7112 7113 static const char* kNativeCallTest = 7114 "call_runtime_last_index_of('bobbobboellebobboellebobbob');"; 7115 7116 // Test that a native runtime calls are supported in extensions. 7117 TEST(NativeCallInExtensions) { 7118 v8::HandleScope handle_scope(CcTest::isolate()); 7119 v8::RegisterExtension(new Extension("nativecall", 7120 kNativeCallInExtensionSource)); 7121 const char* extension_names[] = { "nativecall" }; 7122 v8::ExtensionConfiguration extensions(1, extension_names); 7123 v8::Handle<Context> context = 7124 Context::New(CcTest::isolate(), &extensions); 7125 Context::Scope lock(context); 7126 v8::Handle<Value> result = CompileRun(kNativeCallTest); 7127 CHECK_EQ(result, v8::Integer::New(CcTest::isolate(), 3)); 7128 } 7129 7130 7131 class NativeFunctionExtension : public Extension { 7132 public: 7133 NativeFunctionExtension(const char* name, 7134 const char* source, 7135 v8::FunctionCallback fun = &Echo) 7136 : Extension(name, source), 7137 function_(fun) { } 7138 7139 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunctionTemplate( 7140 v8::Isolate* isolate, 7141 v8::Handle<v8::String> name) { 7142 return v8::FunctionTemplate::New(isolate, function_); 7143 } 7144 7145 static void Echo(const v8::FunctionCallbackInfo<v8::Value>& args) { 7146 if (args.Length() >= 1) args.GetReturnValue().Set(args[0]); 7147 } 7148 private: 7149 v8::FunctionCallback function_; 7150 }; 7151 7152 7153 TEST(NativeFunctionDeclaration) { 7154 v8::HandleScope handle_scope(CcTest::isolate()); 7155 const char* name = "nativedecl"; 7156 v8::RegisterExtension(new NativeFunctionExtension(name, 7157 "native function foo();")); 7158 const char* extension_names[] = { name }; 7159 v8::ExtensionConfiguration extensions(1, extension_names); 7160 v8::Handle<Context> context = 7161 Context::New(CcTest::isolate(), &extensions); 7162 Context::Scope lock(context); 7163 v8::Handle<Value> result = CompileRun("foo(42);"); 7164 CHECK_EQ(result, v8::Integer::New(CcTest::isolate(), 42)); 7165 } 7166 7167 7168 TEST(NativeFunctionDeclarationError) { 7169 v8::HandleScope handle_scope(CcTest::isolate()); 7170 const char* name = "nativedeclerr"; 7171 // Syntax error in extension code. 7172 v8::RegisterExtension(new NativeFunctionExtension(name, 7173 "native\nfunction foo();")); 7174 const char* extension_names[] = { name }; 7175 v8::ExtensionConfiguration extensions(1, extension_names); 7176 v8::Handle<Context> context = 7177 Context::New(CcTest::isolate(), &extensions); 7178 CHECK(context.IsEmpty()); 7179 } 7180 7181 7182 TEST(NativeFunctionDeclarationErrorEscape) { 7183 v8::HandleScope handle_scope(CcTest::isolate()); 7184 const char* name = "nativedeclerresc"; 7185 // Syntax error in extension code - escape code in "native" means that 7186 // it's not treated as a keyword. 7187 v8::RegisterExtension(new NativeFunctionExtension( 7188 name, 7189 "nativ\\u0065 function foo();")); 7190 const char* extension_names[] = { name }; 7191 v8::ExtensionConfiguration extensions(1, extension_names); 7192 v8::Handle<Context> context = 7193 Context::New(CcTest::isolate(), &extensions); 7194 CHECK(context.IsEmpty()); 7195 } 7196 7197 7198 static void CheckDependencies(const char* name, const char* expected) { 7199 v8::HandleScope handle_scope(CcTest::isolate()); 7200 v8::ExtensionConfiguration config(1, &name); 7201 LocalContext context(&config); 7202 CHECK_EQ(String::NewFromUtf8(CcTest::isolate(), expected), 7203 context->Global()->Get(v8_str("loaded"))); 7204 } 7205 7206 7207 /* 7208 * Configuration: 7209 * 7210 * /-- B <--\ 7211 * A <- -- D <-- E 7212 * \-- C <--/ 7213 */ 7214 THREADED_TEST(ExtensionDependency) { 7215 static const char* kEDeps[] = { "D" }; 7216 v8::RegisterExtension(new Extension("E", "this.loaded += 'E';", 1, kEDeps)); 7217 static const char* kDDeps[] = { "B", "C" }; 7218 v8::RegisterExtension(new Extension("D", "this.loaded += 'D';", 2, kDDeps)); 7219 static const char* kBCDeps[] = { "A" }; 7220 v8::RegisterExtension(new Extension("B", "this.loaded += 'B';", 1, kBCDeps)); 7221 v8::RegisterExtension(new Extension("C", "this.loaded += 'C';", 1, kBCDeps)); 7222 v8::RegisterExtension(new Extension("A", "this.loaded += 'A';")); 7223 CheckDependencies("A", "undefinedA"); 7224 CheckDependencies("B", "undefinedAB"); 7225 CheckDependencies("C", "undefinedAC"); 7226 CheckDependencies("D", "undefinedABCD"); 7227 CheckDependencies("E", "undefinedABCDE"); 7228 v8::HandleScope handle_scope(CcTest::isolate()); 7229 static const char* exts[2] = { "C", "E" }; 7230 v8::ExtensionConfiguration config(2, exts); 7231 LocalContext context(&config); 7232 CHECK_EQ(v8_str("undefinedACBDE"), context->Global()->Get(v8_str("loaded"))); 7233 } 7234 7235 7236 static const char* kExtensionTestScript = 7237 "native function A();" 7238 "native function B();" 7239 "native function C();" 7240 "function Foo(i) {" 7241 " if (i == 0) return A();" 7242 " if (i == 1) return B();" 7243 " if (i == 2) return C();" 7244 "}"; 7245 7246 7247 static void CallFun(const v8::FunctionCallbackInfo<v8::Value>& args) { 7248 ApiTestFuzzer::Fuzz(); 7249 if (args.IsConstructCall()) { 7250 args.This()->Set(v8_str("data"), args.Data()); 7251 args.GetReturnValue().SetNull(); 7252 return; 7253 } 7254 args.GetReturnValue().Set(args.Data()); 7255 } 7256 7257 7258 class FunctionExtension : public Extension { 7259 public: 7260 FunctionExtension() : Extension("functiontest", kExtensionTestScript) { } 7261 virtual v8::Handle<v8::FunctionTemplate> GetNativeFunctionTemplate( 7262 v8::Isolate* isolate, 7263 v8::Handle<String> name); 7264 }; 7265 7266 7267 static int lookup_count = 0; 7268 v8::Handle<v8::FunctionTemplate> FunctionExtension::GetNativeFunctionTemplate( 7269 v8::Isolate* isolate, v8::Handle<String> name) { 7270 lookup_count++; 7271 if (name->Equals(v8_str("A"))) { 7272 return v8::FunctionTemplate::New( 7273 isolate, CallFun, v8::Integer::New(isolate, 8)); 7274 } else if (name->Equals(v8_str("B"))) { 7275 return v8::FunctionTemplate::New( 7276 isolate, CallFun, v8::Integer::New(isolate, 7)); 7277 } else if (name->Equals(v8_str("C"))) { 7278 return v8::FunctionTemplate::New( 7279 isolate, CallFun, v8::Integer::New(isolate, 6)); 7280 } else { 7281 return v8::Handle<v8::FunctionTemplate>(); 7282 } 7283 } 7284 7285 7286 THREADED_TEST(FunctionLookup) { 7287 v8::RegisterExtension(new FunctionExtension()); 7288 v8::HandleScope handle_scope(CcTest::isolate()); 7289 static const char* exts[1] = { "functiontest" }; 7290 v8::ExtensionConfiguration config(1, exts); 7291 LocalContext context(&config); 7292 CHECK_EQ(3, lookup_count); 7293 CHECK_EQ(v8::Integer::New(CcTest::isolate(), 8), 7294 CompileRun("Foo(0)")); 7295 CHECK_EQ(v8::Integer::New(CcTest::isolate(), 7), 7296 CompileRun("Foo(1)")); 7297 CHECK_EQ(v8::Integer::New(CcTest::isolate(), 6), 7298 CompileRun("Foo(2)")); 7299 } 7300 7301 7302 THREADED_TEST(NativeFunctionConstructCall) { 7303 v8::RegisterExtension(new FunctionExtension()); 7304 v8::HandleScope handle_scope(CcTest::isolate()); 7305 static const char* exts[1] = { "functiontest" }; 7306 v8::ExtensionConfiguration config(1, exts); 7307 LocalContext context(&config); 7308 for (int i = 0; i < 10; i++) { 7309 // Run a few times to ensure that allocation of objects doesn't 7310 // change behavior of a constructor function. 7311 CHECK_EQ(v8::Integer::New(CcTest::isolate(), 8), 7312 CompileRun("(new A()).data")); 7313 CHECK_EQ(v8::Integer::New(CcTest::isolate(), 7), 7314 CompileRun("(new B()).data")); 7315 CHECK_EQ(v8::Integer::New(CcTest::isolate(), 6), 7316 CompileRun("(new C()).data")); 7317 } 7318 } 7319 7320 7321 static const char* last_location; 7322 static const char* last_message; 7323 void StoringErrorCallback(const char* location, const char* message) { 7324 if (last_location == NULL) { 7325 last_location = location; 7326 last_message = message; 7327 } 7328 } 7329 7330 7331 // ErrorReporting creates a circular extensions configuration and 7332 // tests that the fatal error handler gets called. This renders V8 7333 // unusable and therefore this test cannot be run in parallel. 7334 TEST(ErrorReporting) { 7335 v8::V8::SetFatalErrorHandler(StoringErrorCallback); 7336 static const char* aDeps[] = { "B" }; 7337 v8::RegisterExtension(new Extension("A", "", 1, aDeps)); 7338 static const char* bDeps[] = { "A" }; 7339 v8::RegisterExtension(new Extension("B", "", 1, bDeps)); 7340 last_location = NULL; 7341 v8::ExtensionConfiguration config(1, bDeps); 7342 v8::Handle<Context> context = 7343 Context::New(CcTest::isolate(), &config); 7344 CHECK(context.IsEmpty()); 7345 CHECK_NE(last_location, NULL); 7346 } 7347 7348 7349 static void MissingScriptInfoMessageListener(v8::Handle<v8::Message> message, 7350 v8::Handle<Value> data) { 7351 CHECK(message->GetScriptOrigin().ResourceName()->IsUndefined()); 7352 CHECK_EQ(v8::Undefined(CcTest::isolate()), 7353 message->GetScriptOrigin().ResourceName()); 7354 message->GetLineNumber(); 7355 message->GetSourceLine(); 7356 } 7357 7358 7359 THREADED_TEST(ErrorWithMissingScriptInfo) { 7360 LocalContext context; 7361 v8::HandleScope scope(context->GetIsolate()); 7362 v8::V8::AddMessageListener(MissingScriptInfoMessageListener); 7363 CompileRun("throw Error()"); 7364 v8::V8::RemoveMessageListeners(MissingScriptInfoMessageListener); 7365 } 7366 7367 7368 struct FlagAndPersistent { 7369 bool flag; 7370 v8::Persistent<v8::Object> handle; 7371 }; 7372 7373 7374 static void DisposeAndSetFlag( 7375 const v8::WeakCallbackData<v8::Object, FlagAndPersistent>& data) { 7376 data.GetParameter()->handle.Reset(); 7377 data.GetParameter()->flag = true; 7378 } 7379 7380 7381 THREADED_TEST(IndependentWeakHandle) { 7382 v8::Isolate* iso = CcTest::isolate(); 7383 v8::HandleScope scope(iso); 7384 v8::Handle<Context> context = Context::New(iso); 7385 Context::Scope context_scope(context); 7386 7387 FlagAndPersistent object_a, object_b; 7388 7389 { 7390 v8::HandleScope handle_scope(iso); 7391 object_a.handle.Reset(iso, v8::Object::New(iso)); 7392 object_b.handle.Reset(iso, v8::Object::New(iso)); 7393 } 7394 7395 object_a.flag = false; 7396 object_b.flag = false; 7397 object_a.handle.SetWeak(&object_a, &DisposeAndSetFlag); 7398 object_b.handle.SetWeak(&object_b, &DisposeAndSetFlag); 7399 CHECK(!object_b.handle.IsIndependent()); 7400 object_a.handle.MarkIndependent(); 7401 object_b.handle.MarkIndependent(); 7402 CHECK(object_b.handle.IsIndependent()); 7403 CcTest::heap()->CollectGarbage(i::NEW_SPACE); 7404 CHECK(object_a.flag); 7405 CHECK(object_b.flag); 7406 } 7407 7408 7409 static void InvokeScavenge() { 7410 CcTest::heap()->CollectGarbage(i::NEW_SPACE); 7411 } 7412 7413 7414 static void InvokeMarkSweep() { 7415 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 7416 } 7417 7418 7419 static void ForceScavenge( 7420 const v8::WeakCallbackData<v8::Object, FlagAndPersistent>& data) { 7421 data.GetParameter()->handle.Reset(); 7422 data.GetParameter()->flag = true; 7423 InvokeScavenge(); 7424 } 7425 7426 7427 static void ForceMarkSweep( 7428 const v8::WeakCallbackData<v8::Object, FlagAndPersistent>& data) { 7429 data.GetParameter()->handle.Reset(); 7430 data.GetParameter()->flag = true; 7431 InvokeMarkSweep(); 7432 } 7433 7434 7435 THREADED_TEST(GCFromWeakCallbacks) { 7436 v8::Isolate* isolate = CcTest::isolate(); 7437 v8::HandleScope scope(isolate); 7438 v8::Handle<Context> context = Context::New(isolate); 7439 Context::Scope context_scope(context); 7440 7441 static const int kNumberOfGCTypes = 2; 7442 typedef v8::WeakCallbackData<v8::Object, FlagAndPersistent>::Callback 7443 Callback; 7444 Callback gc_forcing_callback[kNumberOfGCTypes] = 7445 {&ForceScavenge, &ForceMarkSweep}; 7446 7447 typedef void (*GCInvoker)(); 7448 GCInvoker invoke_gc[kNumberOfGCTypes] = {&InvokeScavenge, &InvokeMarkSweep}; 7449 7450 for (int outer_gc = 0; outer_gc < kNumberOfGCTypes; outer_gc++) { 7451 for (int inner_gc = 0; inner_gc < kNumberOfGCTypes; inner_gc++) { 7452 FlagAndPersistent object; 7453 { 7454 v8::HandleScope handle_scope(isolate); 7455 object.handle.Reset(isolate, v8::Object::New(isolate)); 7456 } 7457 object.flag = false; 7458 object.handle.SetWeak(&object, gc_forcing_callback[inner_gc]); 7459 object.handle.MarkIndependent(); 7460 invoke_gc[outer_gc](); 7461 CHECK(object.flag); 7462 } 7463 } 7464 } 7465 7466 7467 static void RevivingCallback( 7468 const v8::WeakCallbackData<v8::Object, FlagAndPersistent>& data) { 7469 data.GetParameter()->handle.ClearWeak(); 7470 data.GetParameter()->flag = true; 7471 } 7472 7473 7474 THREADED_TEST(IndependentHandleRevival) { 7475 v8::Isolate* isolate = CcTest::isolate(); 7476 v8::HandleScope scope(isolate); 7477 v8::Handle<Context> context = Context::New(isolate); 7478 Context::Scope context_scope(context); 7479 7480 FlagAndPersistent object; 7481 { 7482 v8::HandleScope handle_scope(isolate); 7483 v8::Local<v8::Object> o = v8::Object::New(isolate); 7484 object.handle.Reset(isolate, o); 7485 o->Set(v8_str("x"), v8::Integer::New(isolate, 1)); 7486 v8::Local<String> y_str = v8_str("y"); 7487 o->Set(y_str, y_str); 7488 } 7489 object.flag = false; 7490 object.handle.SetWeak(&object, &RevivingCallback); 7491 object.handle.MarkIndependent(); 7492 CcTest::heap()->CollectGarbage(i::NEW_SPACE); 7493 CHECK(object.flag); 7494 CcTest::heap()->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask); 7495 { 7496 v8::HandleScope handle_scope(isolate); 7497 v8::Local<v8::Object> o = 7498 v8::Local<v8::Object>::New(isolate, object.handle); 7499 v8::Local<String> y_str = v8_str("y"); 7500 CHECK_EQ(v8::Integer::New(isolate, 1), o->Get(v8_str("x"))); 7501 CHECK(o->Get(y_str)->Equals(y_str)); 7502 } 7503 } 7504 7505 7506 v8::Handle<Function> args_fun; 7507 7508 7509 static void ArgumentsTestCallback( 7510 const v8::FunctionCallbackInfo<v8::Value>& args) { 7511 ApiTestFuzzer::Fuzz(); 7512 v8::Isolate* isolate = args.GetIsolate(); 7513 CHECK_EQ(args_fun, args.Callee()); 7514 CHECK_EQ(3, args.Length()); 7515 CHECK_EQ(v8::Integer::New(isolate, 1), args[0]); 7516 CHECK_EQ(v8::Integer::New(isolate, 2), args[1]); 7517 CHECK_EQ(v8::Integer::New(isolate, 3), args[2]); 7518 CHECK_EQ(v8::Undefined(isolate), args[3]); 7519 v8::HandleScope scope(args.GetIsolate()); 7520 CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags); 7521 } 7522 7523 7524 THREADED_TEST(Arguments) { 7525 v8::Isolate* isolate = CcTest::isolate(); 7526 v8::HandleScope scope(isolate); 7527 v8::Handle<v8::ObjectTemplate> global = ObjectTemplate::New(isolate); 7528 global->Set(v8_str("f"), 7529 v8::FunctionTemplate::New(isolate, ArgumentsTestCallback)); 7530 LocalContext context(NULL, global); 7531 args_fun = context->Global()->Get(v8_str("f")).As<Function>(); 7532 v8_compile("f(1, 2, 3)")->Run(); 7533 } 7534 7535 7536 static void NoBlockGetterX(Local<String> name, 7537 const v8::PropertyCallbackInfo<v8::Value>&) { 7538 } 7539 7540 7541 static void NoBlockGetterI(uint32_t index, 7542 const v8::PropertyCallbackInfo<v8::Value>&) { 7543 } 7544 7545 7546 static void PDeleter(Local<String> name, 7547 const v8::PropertyCallbackInfo<v8::Boolean>& info) { 7548 if (!name->Equals(v8_str("foo"))) { 7549 return; // not intercepted 7550 } 7551 7552 info.GetReturnValue().Set(false); // intercepted, don't delete the property 7553 } 7554 7555 7556 static void IDeleter(uint32_t index, 7557 const v8::PropertyCallbackInfo<v8::Boolean>& info) { 7558 if (index != 2) { 7559 return; // not intercepted 7560 } 7561 7562 info.GetReturnValue().Set(false); // intercepted, don't delete the property 7563 } 7564 7565 7566 THREADED_TEST(Deleter) { 7567 v8::Isolate* isolate = CcTest::isolate(); 7568 v8::HandleScope scope(isolate); 7569 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); 7570 obj->SetNamedPropertyHandler(NoBlockGetterX, NULL, NULL, PDeleter, NULL); 7571 obj->SetIndexedPropertyHandler(NoBlockGetterI, NULL, NULL, IDeleter, NULL); 7572 LocalContext context; 7573 context->Global()->Set(v8_str("k"), obj->NewInstance()); 7574 CompileRun( 7575 "k.foo = 'foo';" 7576 "k.bar = 'bar';" 7577 "k[2] = 2;" 7578 "k[4] = 4;"); 7579 CHECK(v8_compile("delete k.foo")->Run()->IsFalse()); 7580 CHECK(v8_compile("delete k.bar")->Run()->IsTrue()); 7581 7582 CHECK_EQ(v8_compile("k.foo")->Run(), v8_str("foo")); 7583 CHECK(v8_compile("k.bar")->Run()->IsUndefined()); 7584 7585 CHECK(v8_compile("delete k[2]")->Run()->IsFalse()); 7586 CHECK(v8_compile("delete k[4]")->Run()->IsTrue()); 7587 7588 CHECK_EQ(v8_compile("k[2]")->Run(), v8_num(2)); 7589 CHECK(v8_compile("k[4]")->Run()->IsUndefined()); 7590 } 7591 7592 7593 static void GetK(Local<String> name, 7594 const v8::PropertyCallbackInfo<v8::Value>& info) { 7595 ApiTestFuzzer::Fuzz(); 7596 if (name->Equals(v8_str("foo")) || 7597 name->Equals(v8_str("bar")) || 7598 name->Equals(v8_str("baz"))) { 7599 info.GetReturnValue().SetUndefined(); 7600 } 7601 } 7602 7603 7604 static void IndexedGetK(uint32_t index, 7605 const v8::PropertyCallbackInfo<v8::Value>& info) { 7606 ApiTestFuzzer::Fuzz(); 7607 if (index == 0 || index == 1) info.GetReturnValue().SetUndefined(); 7608 } 7609 7610 7611 static void NamedEnum(const v8::PropertyCallbackInfo<v8::Array>& info) { 7612 ApiTestFuzzer::Fuzz(); 7613 v8::Handle<v8::Array> result = v8::Array::New(info.GetIsolate(), 3); 7614 result->Set(v8::Integer::New(info.GetIsolate(), 0), v8_str("foo")); 7615 result->Set(v8::Integer::New(info.GetIsolate(), 1), v8_str("bar")); 7616 result->Set(v8::Integer::New(info.GetIsolate(), 2), v8_str("baz")); 7617 info.GetReturnValue().Set(result); 7618 } 7619 7620 7621 static void IndexedEnum(const v8::PropertyCallbackInfo<v8::Array>& info) { 7622 ApiTestFuzzer::Fuzz(); 7623 v8::Handle<v8::Array> result = v8::Array::New(info.GetIsolate(), 2); 7624 result->Set(v8::Integer::New(info.GetIsolate(), 0), v8_str("0")); 7625 result->Set(v8::Integer::New(info.GetIsolate(), 1), v8_str("1")); 7626 info.GetReturnValue().Set(result); 7627 } 7628 7629 7630 THREADED_TEST(Enumerators) { 7631 v8::Isolate* isolate = CcTest::isolate(); 7632 v8::HandleScope scope(isolate); 7633 v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New(isolate); 7634 obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum); 7635 obj->SetIndex