1 // Copyright 2007-2008 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 <stdlib.h> 29 30 #include "src/v8.h" 31 32 #include "src/heap/heap.h" 33 #include "test/cctest/cctest.h" 34 35 using namespace v8; 36 37 38 enum Expectations { 39 EXPECT_RESULT, 40 EXPECT_EXCEPTION, 41 EXPECT_ERROR 42 }; 43 44 45 // A DeclarationContext holds a reference to a v8::Context and keeps 46 // track of various declaration related counters to make it easier to 47 // track if global declarations in the presence of interceptors behave 48 // the right way. 49 class DeclarationContext { 50 public: 51 DeclarationContext(); 52 53 virtual ~DeclarationContext() { 54 if (is_initialized_) { 55 Isolate* isolate = CcTest::isolate(); 56 HandleScope scope(isolate); 57 Local<Context> context = Local<Context>::New(isolate, context_); 58 context->Exit(); 59 context_.Reset(); 60 } 61 } 62 63 void Check(const char* source, 64 int get, int set, int has, 65 Expectations expectations, 66 v8::Handle<Value> value = Local<Value>()); 67 68 int get_count() const { return get_count_; } 69 int set_count() const { return set_count_; } 70 int query_count() const { return query_count_; } 71 72 protected: 73 virtual v8::Handle<Value> Get(Local<String> key); 74 virtual v8::Handle<Value> Set(Local<String> key, Local<Value> value); 75 virtual v8::Handle<Integer> Query(Local<String> key); 76 77 void InitializeIfNeeded(); 78 79 // Perform optional initialization steps on the context after it has 80 // been created. Defaults to none but may be overwritten. 81 virtual void PostInitializeContext(Handle<Context> context) {} 82 83 // Get the holder for the interceptor. Default to the instance template 84 // but may be overwritten. 85 virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) { 86 return function->InstanceTemplate(); 87 } 88 89 // The handlers are called as static functions that forward 90 // to the instance specific virtual methods. 91 static void HandleGet(Local<String> key, 92 const v8::PropertyCallbackInfo<v8::Value>& info); 93 static void HandleSet(Local<String> key, 94 Local<Value> value, 95 const v8::PropertyCallbackInfo<v8::Value>& info); 96 static void HandleQuery(Local<String> key, 97 const v8::PropertyCallbackInfo<v8::Integer>& info); 98 99 v8::Isolate* isolate() const { return CcTest::isolate(); } 100 101 private: 102 bool is_initialized_; 103 Persistent<Context> context_; 104 105 int get_count_; 106 int set_count_; 107 int query_count_; 108 109 static DeclarationContext* GetInstance(Local<Value> data); 110 }; 111 112 113 DeclarationContext::DeclarationContext() 114 : is_initialized_(false), get_count_(0), set_count_(0), query_count_(0) { 115 // Do nothing. 116 } 117 118 119 void DeclarationContext::InitializeIfNeeded() { 120 if (is_initialized_) return; 121 Isolate* isolate = CcTest::isolate(); 122 HandleScope scope(isolate); 123 Local<FunctionTemplate> function = FunctionTemplate::New(isolate); 124 Local<Value> data = External::New(CcTest::isolate(), this); 125 GetHolder(function)->SetNamedPropertyHandler(&HandleGet, 126 &HandleSet, 127 &HandleQuery, 128 0, 0, 129 data); 130 Local<Context> context = Context::New(isolate, 131 0, 132 function->InstanceTemplate(), 133 Local<Value>()); 134 context_.Reset(isolate, context); 135 context->Enter(); 136 is_initialized_ = true; 137 PostInitializeContext(context); 138 } 139 140 141 void DeclarationContext::Check(const char* source, 142 int get, int set, int query, 143 Expectations expectations, 144 v8::Handle<Value> value) { 145 InitializeIfNeeded(); 146 // A retry after a GC may pollute the counts, so perform gc now 147 // to avoid that. 148 CcTest::heap()->CollectGarbage(v8::internal::NEW_SPACE); 149 HandleScope scope(CcTest::isolate()); 150 TryCatch catcher; 151 catcher.SetVerbose(true); 152 Local<Script> script = 153 Script::Compile(String::NewFromUtf8(CcTest::isolate(), source)); 154 if (expectations == EXPECT_ERROR) { 155 CHECK(script.IsEmpty()); 156 return; 157 } 158 CHECK(!script.IsEmpty()); 159 Local<Value> result = script->Run(); 160 CHECK_EQ(get, get_count()); 161 CHECK_EQ(set, set_count()); 162 CHECK_EQ(query, query_count()); 163 if (expectations == EXPECT_RESULT) { 164 CHECK(!catcher.HasCaught()); 165 if (!value.IsEmpty()) { 166 CHECK_EQ(value, result); 167 } 168 } else { 169 CHECK(expectations == EXPECT_EXCEPTION); 170 CHECK(catcher.HasCaught()); 171 if (!value.IsEmpty()) { 172 CHECK_EQ(value, catcher.Exception()); 173 } 174 } 175 // Clean slate for the next test. 176 CcTest::heap()->CollectAllAvailableGarbage(); 177 } 178 179 180 void DeclarationContext::HandleGet( 181 Local<String> key, 182 const v8::PropertyCallbackInfo<v8::Value>& info) { 183 DeclarationContext* context = GetInstance(info.Data()); 184 context->get_count_++; 185 info.GetReturnValue().Set(context->Get(key)); 186 } 187 188 189 void DeclarationContext::HandleSet( 190 Local<String> key, 191 Local<Value> value, 192 const v8::PropertyCallbackInfo<v8::Value>& info) { 193 DeclarationContext* context = GetInstance(info.Data()); 194 context->set_count_++; 195 info.GetReturnValue().Set(context->Set(key, value)); 196 } 197 198 199 void DeclarationContext::HandleQuery( 200 Local<String> key, 201 const v8::PropertyCallbackInfo<v8::Integer>& info) { 202 DeclarationContext* context = GetInstance(info.Data()); 203 context->query_count_++; 204 info.GetReturnValue().Set(context->Query(key)); 205 } 206 207 208 DeclarationContext* DeclarationContext::GetInstance(Local<Value> data) { 209 void* value = Local<External>::Cast(data)->Value(); 210 return static_cast<DeclarationContext*>(value); 211 } 212 213 214 v8::Handle<Value> DeclarationContext::Get(Local<String> key) { 215 return v8::Handle<Value>(); 216 } 217 218 219 v8::Handle<Value> DeclarationContext::Set(Local<String> key, 220 Local<Value> value) { 221 return v8::Handle<Value>(); 222 } 223 224 225 v8::Handle<Integer> DeclarationContext::Query(Local<String> key) { 226 return v8::Handle<Integer>(); 227 } 228 229 230 // Test global declaration of a property the interceptor doesn't know 231 // about and doesn't handle. 232 TEST(Unknown) { 233 HandleScope scope(CcTest::isolate()); 234 v8::V8::Initialize(); 235 236 { DeclarationContext context; 237 context.Check("var x; x", 238 1, // access 239 0, 0, EXPECT_RESULT, Undefined(CcTest::isolate())); 240 } 241 242 { DeclarationContext context; 243 context.Check("var x = 0; x", 244 1, // access 245 1, // initialization 246 0, EXPECT_RESULT, Number::New(CcTest::isolate(), 0)); 247 } 248 249 { DeclarationContext context; 250 context.Check("function x() { }; x", 251 1, // access 252 0, 253 0, 254 EXPECT_RESULT); 255 } 256 257 { DeclarationContext context; 258 context.Check("const x; x", 259 1, // access 260 0, 0, EXPECT_RESULT, Undefined(CcTest::isolate())); 261 } 262 263 { DeclarationContext context; 264 context.Check("const x = 0; x", 265 1, // access 266 0, 267 0, 268 EXPECT_RESULT, Number::New(CcTest::isolate(), 0)); 269 } 270 } 271 272 273 class AbsentPropertyContext: public DeclarationContext { 274 protected: 275 virtual v8::Handle<Integer> Query(Local<String> key) { 276 return v8::Handle<Integer>(); 277 } 278 }; 279 280 281 TEST(Absent) { 282 v8::Isolate* isolate = CcTest::isolate(); 283 v8::V8::Initialize(); 284 HandleScope scope(isolate); 285 286 { AbsentPropertyContext context; 287 context.Check("var x; x", 288 1, // access 289 0, 0, EXPECT_RESULT, Undefined(isolate)); 290 } 291 292 { AbsentPropertyContext context; 293 context.Check("var x = 0; x", 294 1, // access 295 1, // initialization 296 0, EXPECT_RESULT, Number::New(isolate, 0)); 297 } 298 299 { AbsentPropertyContext context; 300 context.Check("function x() { }; x", 301 1, // access 302 0, 303 0, 304 EXPECT_RESULT); 305 } 306 307 { AbsentPropertyContext context; 308 context.Check("const x; x", 309 1, // access 310 0, 0, EXPECT_RESULT, Undefined(isolate)); 311 } 312 313 { AbsentPropertyContext context; 314 context.Check("const x = 0; x", 315 1, // access 316 0, 0, EXPECT_RESULT, Number::New(isolate, 0)); 317 } 318 319 { AbsentPropertyContext context; 320 context.Check("if (false) { var x = 0 }; x", 321 1, // access 322 0, 0, EXPECT_RESULT, Undefined(isolate)); 323 } 324 } 325 326 327 328 class AppearingPropertyContext: public DeclarationContext { 329 public: 330 enum State { 331 DECLARE, 332 INITIALIZE_IF_ASSIGN, 333 UNKNOWN 334 }; 335 336 AppearingPropertyContext() : state_(DECLARE) { } 337 338 protected: 339 virtual v8::Handle<Integer> Query(Local<String> key) { 340 switch (state_) { 341 case DECLARE: 342 // Force declaration by returning that the 343 // property is absent. 344 state_ = INITIALIZE_IF_ASSIGN; 345 return Handle<Integer>(); 346 case INITIALIZE_IF_ASSIGN: 347 // Return that the property is present so we only get the 348 // setter called when initializing with a value. 349 state_ = UNKNOWN; 350 return Integer::New(isolate(), v8::None); 351 default: 352 CHECK(state_ == UNKNOWN); 353 break; 354 } 355 // Do the lookup in the object. 356 return v8::Handle<Integer>(); 357 } 358 359 private: 360 State state_; 361 }; 362 363 364 TEST(Appearing) { 365 v8::V8::Initialize(); 366 HandleScope scope(CcTest::isolate()); 367 368 { AppearingPropertyContext context; 369 context.Check("var x; x", 370 1, // access 371 0, 0, EXPECT_RESULT, Undefined(CcTest::isolate())); 372 } 373 374 { AppearingPropertyContext context; 375 context.Check("var x = 0; x", 376 1, // access 377 1, // initialization 378 0, EXPECT_RESULT, Number::New(CcTest::isolate(), 0)); 379 } 380 381 { AppearingPropertyContext context; 382 context.Check("function x() { }; x", 383 1, // access 384 0, 385 0, 386 EXPECT_RESULT); 387 } 388 389 { AppearingPropertyContext context; 390 context.Check("const x; x", 391 1, // access 392 0, 0, EXPECT_RESULT, Undefined(CcTest::isolate())); 393 } 394 395 { AppearingPropertyContext context; 396 context.Check("const x = 0; x", 397 1, // access 398 0, 0, EXPECT_RESULT, Number::New(CcTest::isolate(), 0)); 399 } 400 } 401 402 403 404 class ExistsInPrototypeContext: public DeclarationContext { 405 public: 406 ExistsInPrototypeContext() { InitializeIfNeeded(); } 407 protected: 408 virtual v8::Handle<Integer> Query(Local<String> key) { 409 // Let it seem that the property exists in the prototype object. 410 return Integer::New(isolate(), v8::None); 411 } 412 413 // Use the prototype as the holder for the interceptors. 414 virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) { 415 return function->PrototypeTemplate(); 416 } 417 }; 418 419 420 TEST(ExistsInPrototype) { 421 HandleScope scope(CcTest::isolate()); 422 423 // Sanity check to make sure that the holder of the interceptor 424 // really is the prototype object. 425 { ExistsInPrototypeContext context; 426 context.Check("this.x = 87; this.x", 0, 0, 1, EXPECT_RESULT, 427 Number::New(CcTest::isolate(), 87)); 428 } 429 430 { ExistsInPrototypeContext context; 431 context.Check("var x; x", 432 0, 433 0, 434 0, 435 EXPECT_RESULT, Undefined(CcTest::isolate())); 436 } 437 438 { ExistsInPrototypeContext context; 439 context.Check("var x = 0; x", 440 0, 441 0, 442 0, 443 EXPECT_RESULT, Number::New(CcTest::isolate(), 0)); 444 } 445 446 { ExistsInPrototypeContext context; 447 context.Check("const x; x", 448 0, 449 0, 450 0, 451 EXPECT_RESULT, Undefined(CcTest::isolate())); 452 } 453 454 { ExistsInPrototypeContext context; 455 context.Check("const x = 0; x", 456 0, 457 0, 458 0, 459 EXPECT_RESULT, Number::New(CcTest::isolate(), 0)); 460 } 461 } 462 463 464 465 class AbsentInPrototypeContext: public DeclarationContext { 466 protected: 467 virtual v8::Handle<Integer> Query(Local<String> key) { 468 // Let it seem that the property is absent in the prototype object. 469 return Handle<Integer>(); 470 } 471 472 // Use the prototype as the holder for the interceptors. 473 virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) { 474 return function->PrototypeTemplate(); 475 } 476 }; 477 478 479 TEST(AbsentInPrototype) { 480 v8::V8::Initialize(); 481 HandleScope scope(CcTest::isolate()); 482 483 { AbsentInPrototypeContext context; 484 context.Check("if (false) { var x = 0; }; x", 485 0, 486 0, 487 0, 488 EXPECT_RESULT, Undefined(CcTest::isolate())); 489 } 490 } 491 492 493 494 class ExistsInHiddenPrototypeContext: public DeclarationContext { 495 public: 496 ExistsInHiddenPrototypeContext() { 497 hidden_proto_ = FunctionTemplate::New(CcTest::isolate()); 498 hidden_proto_->SetHiddenPrototype(true); 499 } 500 501 protected: 502 virtual v8::Handle<Integer> Query(Local<String> key) { 503 // Let it seem that the property exists in the hidden prototype object. 504 return Integer::New(isolate(), v8::None); 505 } 506 507 // Install the hidden prototype after the global object has been created. 508 virtual void PostInitializeContext(Handle<Context> context) { 509 Local<Object> global_object = context->Global(); 510 Local<Object> hidden_proto = hidden_proto_->GetFunction()->NewInstance(); 511 Local<Object> inner_global = 512 Local<Object>::Cast(global_object->GetPrototype()); 513 inner_global->SetPrototype(hidden_proto); 514 } 515 516 // Use the hidden prototype as the holder for the interceptors. 517 virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) { 518 return hidden_proto_->InstanceTemplate(); 519 } 520 521 private: 522 Local<FunctionTemplate> hidden_proto_; 523 }; 524 525 526 TEST(ExistsInHiddenPrototype) { 527 HandleScope scope(CcTest::isolate()); 528 529 { ExistsInHiddenPrototypeContext context; 530 context.Check("var x; x", 0, 0, 0, EXPECT_RESULT, 531 Undefined(CcTest::isolate())); 532 } 533 534 { ExistsInHiddenPrototypeContext context; 535 context.Check("var x = 0; x", 0, 0, 0, EXPECT_RESULT, 536 Number::New(CcTest::isolate(), 0)); 537 } 538 539 { ExistsInHiddenPrototypeContext context; 540 context.Check("function x() { }; x", 541 0, 542 0, 543 0, 544 EXPECT_RESULT); 545 } 546 547 // TODO(mstarzinger): The semantics of global const is vague. 548 { ExistsInHiddenPrototypeContext context; 549 context.Check("const x; x", 0, 0, 0, EXPECT_RESULT, 550 Undefined(CcTest::isolate())); 551 } 552 553 // TODO(mstarzinger): The semantics of global const is vague. 554 { ExistsInHiddenPrototypeContext context; 555 context.Check("const x = 0; x", 0, 0, 0, EXPECT_RESULT, 556 Number::New(CcTest::isolate(), 0)); 557 } 558 } 559 560 561 562 class SimpleContext { 563 public: 564 SimpleContext() 565 : handle_scope_(CcTest::isolate()), 566 context_(Context::New(CcTest::isolate())) { 567 context_->Enter(); 568 } 569 570 ~SimpleContext() { 571 context_->Exit(); 572 } 573 574 void Check(const char* source, 575 Expectations expectations, 576 v8::Handle<Value> value = Local<Value>()) { 577 HandleScope scope(context_->GetIsolate()); 578 TryCatch catcher; 579 catcher.SetVerbose(true); 580 Local<Script> script = 581 Script::Compile(String::NewFromUtf8(context_->GetIsolate(), source)); 582 if (expectations == EXPECT_ERROR) { 583 CHECK(script.IsEmpty()); 584 return; 585 } 586 CHECK(!script.IsEmpty()); 587 Local<Value> result = script->Run(); 588 if (expectations == EXPECT_RESULT) { 589 CHECK(!catcher.HasCaught()); 590 if (!value.IsEmpty()) { 591 CHECK_EQ(value, result); 592 } 593 } else { 594 CHECK(expectations == EXPECT_EXCEPTION); 595 CHECK(catcher.HasCaught()); 596 if (!value.IsEmpty()) { 597 CHECK_EQ(value, catcher.Exception()); 598 } 599 } 600 } 601 602 private: 603 HandleScope handle_scope_; 604 Local<Context> context_; 605 }; 606 607 608 TEST(CrossScriptReferences) { 609 v8::Isolate* isolate = CcTest::isolate(); 610 HandleScope scope(isolate); 611 612 { SimpleContext context; 613 context.Check("var x = 1; x", 614 EXPECT_RESULT, Number::New(isolate, 1)); 615 context.Check("var x = 2; x", 616 EXPECT_RESULT, Number::New(isolate, 2)); 617 context.Check("const x = 3; x", EXPECT_EXCEPTION); 618 context.Check("const x = 4; x", EXPECT_EXCEPTION); 619 context.Check("x = 5; x", 620 EXPECT_RESULT, Number::New(isolate, 5)); 621 context.Check("var x = 6; x", 622 EXPECT_RESULT, Number::New(isolate, 6)); 623 context.Check("this.x", 624 EXPECT_RESULT, Number::New(isolate, 6)); 625 context.Check("function x() { return 7 }; x()", 626 EXPECT_RESULT, Number::New(isolate, 7)); 627 } 628 629 { SimpleContext context; 630 context.Check("const x = 1; x", 631 EXPECT_RESULT, Number::New(isolate, 1)); 632 context.Check("var x = 2; x", // assignment ignored 633 EXPECT_RESULT, Number::New(isolate, 1)); 634 context.Check("const x = 3; x", EXPECT_EXCEPTION); 635 context.Check("x = 4; x", // assignment ignored 636 EXPECT_RESULT, Number::New(isolate, 1)); 637 context.Check("var x = 5; x", // assignment ignored 638 EXPECT_RESULT, Number::New(isolate, 1)); 639 context.Check("this.x", 640 EXPECT_RESULT, Number::New(isolate, 1)); 641 context.Check("function x() { return 7 }; x", 642 EXPECT_EXCEPTION); 643 } 644 } 645 646 647 TEST(CrossScriptReferencesHarmony) { 648 i::FLAG_use_strict = true; 649 i::FLAG_harmony_scoping = true; 650 i::FLAG_harmony_modules = true; 651 652 v8::Isolate* isolate = CcTest::isolate(); 653 HandleScope scope(isolate); 654 655 const char* decs[] = { 656 "var x = 1; x", "x", "this.x", 657 "function x() { return 1 }; x()", "x()", "this.x()", 658 "let x = 1; x", "x", "this.x", 659 "const x = 1; x", "x", "this.x", 660 "module x { export let a = 1 }; x.a", "x.a", "this.x.a", 661 NULL 662 }; 663 664 for (int i = 0; decs[i] != NULL; i += 3) { 665 SimpleContext context; 666 context.Check(decs[i], EXPECT_RESULT, Number::New(isolate, 1)); 667 context.Check(decs[i+1], EXPECT_RESULT, Number::New(isolate, 1)); 668 // TODO(rossberg): The current ES6 draft spec does not reflect lexical 669 // bindings on the global object. However, this will probably change, in 670 // which case we reactivate the following test. 671 if (i/3 < 2) { 672 context.Check(decs[i+2], EXPECT_RESULT, Number::New(isolate, 1)); 673 } 674 } 675 } 676 677 678 TEST(CrossScriptConflicts) { 679 i::FLAG_use_strict = true; 680 i::FLAG_harmony_scoping = true; 681 i::FLAG_harmony_modules = true; 682 683 HandleScope scope(CcTest::isolate()); 684 685 const char* firsts[] = { 686 "var x = 1; x", 687 "function x() { return 1 }; x()", 688 "let x = 1; x", 689 "const x = 1; x", 690 "module x { export let a = 1 }; x.a", 691 NULL 692 }; 693 const char* seconds[] = { 694 "var x = 2; x", 695 "function x() { return 2 }; x()", 696 "let x = 2; x", 697 "const x = 2; x", 698 "module x { export let a = 2 }; x.a", 699 NULL 700 }; 701 702 for (int i = 0; firsts[i] != NULL; ++i) { 703 for (int j = 0; seconds[j] != NULL; ++j) { 704 SimpleContext context; 705 context.Check(firsts[i], EXPECT_RESULT, 706 Number::New(CcTest::isolate(), 1)); 707 // TODO(rossberg): All tests should actually be errors in Harmony, 708 // but we currently do not detect the cases where the first declaration 709 // is not lexical. 710 context.Check(seconds[j], 711 i < 2 ? EXPECT_RESULT : EXPECT_ERROR, 712 Number::New(CcTest::isolate(), 2)); 713 } 714 } 715 } 716