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
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.
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.
32 #include "src/heap/heap.h"
33 #include "test/cctest/cctest.h"
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
49 class DeclarationContext {
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_);
63 void Check(const char* source,
64 int get, int set, int has,
65 Expectations expectations,
66 v8::Handle<Value> value = Local<Value>());
68 int get_count() const { return get_count_; }
69 int set_count() const { return set_count_; }
70 int query_count() const { return query_count_; }
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);
77 void InitializeIfNeeded();
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) {}
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();
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,
95 const v8::PropertyCallbackInfo<v8::Value>& info);
96 static void HandleQuery(Local<String> key,
97 const v8::PropertyCallbackInfo<v8::Integer>& info);
99 v8::Isolate* isolate() const { return CcTest::isolate(); }
102 bool is_initialized_;
103 Persistent<Context> context_;
109 static DeclarationContext* GetInstance(Local<Value> data);
113 DeclarationContext::DeclarationContext()
114 : is_initialized_(false), get_count_(0), set_count_(0), query_count_(0) {
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,
130 Local<Context> context = Context::New(isolate,
132 function->InstanceTemplate(),
134 context_.Reset(isolate, context);
136 is_initialized_ = true;
137 PostInitializeContext(context);
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
148 CcTest::heap()->CollectGarbage(v8::internal::NEW_SPACE);
149 HandleScope scope(CcTest::isolate());
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());
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);
169 CHECK(expectations == EXPECT_EXCEPTION);
170 CHECK(catcher.HasCaught());
171 if (!value.IsEmpty()) {
172 CHECK_EQ(value, catcher.Exception());
175 // Clean slate for the next test.
176 CcTest::heap()->CollectAllAvailableGarbage();
180 void DeclarationContext::HandleGet(
182 const v8::PropertyCallbackInfo<v8::Value>& info) {
183 DeclarationContext* context = GetInstance(info.Data());
184 context->get_count_++;
185 info.GetReturnValue().Set(context->Get(key));
189 void DeclarationContext::HandleSet(
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));
199 void DeclarationContext::HandleQuery(
201 const v8::PropertyCallbackInfo<v8::Integer>& info) {
202 DeclarationContext* context = GetInstance(info.Data());
203 context->query_count_++;
204 info.GetReturnValue().Set(context->Query(key));
208 DeclarationContext* DeclarationContext::GetInstance(Local<Value> data) {
209 void* value = Local<External>::Cast(data)->Value();
210 return static_cast<DeclarationContext*>(value);
214 v8::Handle<Value> DeclarationContext::Get(Local<String> key) {
215 return v8::Handle<Value>();
219 v8::Handle<Value> DeclarationContext::Set(Local<String> key,
220 Local<Value> value) {
221 return v8::Handle<Value>();
225 v8::Handle<Integer> DeclarationContext::Query(Local<String> key) {
226 return v8::Handle<Integer>();
230 // Test global declaration of a property the interceptor doesn't know
231 // about and doesn't handle.
233 HandleScope scope(CcTest::isolate());
234 v8::V8::Initialize();
236 { DeclarationContext context;
237 context.Check("var x; x",
239 0, 0, EXPECT_RESULT, Undefined(CcTest::isolate()));
242 { DeclarationContext context;
243 context.Check("var x = 0; x",
246 0, EXPECT_RESULT, Number::New(CcTest::isolate(), 0));
249 { DeclarationContext context;
250 context.Check("function x() { }; x",
257 { DeclarationContext context;
258 context.Check("const x; x",
260 0, 0, EXPECT_RESULT, Undefined(CcTest::isolate()));
263 { DeclarationContext context;
264 context.Check("const x = 0; x",
268 EXPECT_RESULT, Number::New(CcTest::isolate(), 0));
273 class AbsentPropertyContext: public DeclarationContext {
275 virtual v8::Handle<Integer> Query(Local<String> key) {
276 return v8::Handle<Integer>();
282 v8::Isolate* isolate = CcTest::isolate();
283 v8::V8::Initialize();
284 HandleScope scope(isolate);
286 { AbsentPropertyContext context;
287 context.Check("var x; x",
289 0, 0, EXPECT_RESULT, Undefined(isolate));
292 { AbsentPropertyContext context;
293 context.Check("var x = 0; x",
296 0, EXPECT_RESULT, Number::New(isolate, 0));
299 { AbsentPropertyContext context;
300 context.Check("function x() { }; x",
307 { AbsentPropertyContext context;
308 context.Check("const x; x",
310 0, 0, EXPECT_RESULT, Undefined(isolate));
313 { AbsentPropertyContext context;
314 context.Check("const x = 0; x",
316 0, 0, EXPECT_RESULT, Number::New(isolate, 0));
319 { AbsentPropertyContext context;
320 context.Check("if (false) { var x = 0 }; x",
322 0, 0, EXPECT_RESULT, Undefined(isolate));
328 class AppearingPropertyContext: public DeclarationContext {
332 INITIALIZE_IF_ASSIGN,
336 AppearingPropertyContext() : state_(DECLARE) { }
339 virtual v8::Handle<Integer> Query(Local<String> key) {
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.
350 return Integer::New(isolate(), v8::None);
352 CHECK(state_ == UNKNOWN);
355 // Do the lookup in the object.
356 return v8::Handle<Integer>();
365 v8::V8::Initialize();
366 HandleScope scope(CcTest::isolate());
368 { AppearingPropertyContext context;
369 context.Check("var x; x",
371 0, 0, EXPECT_RESULT, Undefined(CcTest::isolate()));
374 { AppearingPropertyContext context;
375 context.Check("var x = 0; x",
378 0, EXPECT_RESULT, Number::New(CcTest::isolate(), 0));
381 { AppearingPropertyContext context;
382 context.Check("function x() { }; x",
389 { AppearingPropertyContext context;
390 context.Check("const x; x",
392 0, 0, EXPECT_RESULT, Undefined(CcTest::isolate()));
395 { AppearingPropertyContext context;
396 context.Check("const x = 0; x",
398 0, 0, EXPECT_RESULT, Number::New(CcTest::isolate(), 0));
404 class ExistsInPrototypeContext: public DeclarationContext {
406 ExistsInPrototypeContext() { InitializeIfNeeded(); }
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);
413 // Use the prototype as the holder for the interceptors.
414 virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
415 return function->PrototypeTemplate();
420 TEST(ExistsInPrototype) {
421 HandleScope scope(CcTest::isolate());
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));
430 { ExistsInPrototypeContext context;
431 context.Check("var x; x",
435 EXPECT_RESULT, Undefined(CcTest::isolate()));
438 { ExistsInPrototypeContext context;
439 context.Check("var x = 0; x",
443 EXPECT_RESULT, Number::New(CcTest::isolate(), 0));
446 { ExistsInPrototypeContext context;
447 context.Check("const x; x",
451 EXPECT_RESULT, Undefined(CcTest::isolate()));
454 { ExistsInPrototypeContext context;
455 context.Check("const x = 0; x",
459 EXPECT_RESULT, Number::New(CcTest::isolate(), 0));
465 class AbsentInPrototypeContext: public DeclarationContext {
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>();
472 // Use the prototype as the holder for the interceptors.
473 virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
474 return function->PrototypeTemplate();
479 TEST(AbsentInPrototype) {
480 v8::V8::Initialize();
481 HandleScope scope(CcTest::isolate());
483 { AbsentInPrototypeContext context;
484 context.Check("if (false) { var x = 0; }; x",
488 EXPECT_RESULT, Undefined(CcTest::isolate()));
494 class ExistsInHiddenPrototypeContext: public DeclarationContext {
496 ExistsInHiddenPrototypeContext() {
497 hidden_proto_ = FunctionTemplate::New(CcTest::isolate());
498 hidden_proto_->SetHiddenPrototype(true);
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);
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);
516 // Use the hidden prototype as the holder for the interceptors.
517 virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
518 return hidden_proto_->InstanceTemplate();
522 Local<FunctionTemplate> hidden_proto_;
526 TEST(ExistsInHiddenPrototype) {
527 HandleScope scope(CcTest::isolate());
529 { ExistsInHiddenPrototypeContext context;
530 context.Check("var x; x", 0, 0, 0, EXPECT_RESULT,
531 Undefined(CcTest::isolate()));
534 { ExistsInHiddenPrototypeContext context;
535 context.Check("var x = 0; x", 0, 0, 0, EXPECT_RESULT,
536 Number::New(CcTest::isolate(), 0));
539 { ExistsInHiddenPrototypeContext context;
540 context.Check("function x() { }; x",
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()));
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));
562 class SimpleContext {
565 : handle_scope_(CcTest::isolate()),
566 context_(Context::New(CcTest::isolate())) {
574 void Check(const char* source,
575 Expectations expectations,
576 v8::Handle<Value> value = Local<Value>()) {
577 HandleScope scope(context_->GetIsolate());
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());
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);
594 CHECK(expectations == EXPECT_EXCEPTION);
595 CHECK(catcher.HasCaught());
596 if (!value.IsEmpty()) {
597 CHECK_EQ(value, catcher.Exception());
603 HandleScope handle_scope_;
604 Local<Context> context_;
608 TEST(CrossScriptReferences) {
609 v8::Isolate* isolate = CcTest::isolate();
610 HandleScope scope(isolate);
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));
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",
647 TEST(CrossScriptReferencesHarmony) {
648 i::FLAG_use_strict = true;
649 i::FLAG_harmony_scoping = true;
650 i::FLAG_harmony_modules = true;
652 v8::Isolate* isolate = CcTest::isolate();
653 HandleScope scope(isolate);
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",
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.
672 context.Check(decs[i+2], EXPECT_RESULT, Number::New(isolate, 1));
678 TEST(CrossScriptConflicts) {
679 i::FLAG_use_strict = true;
680 i::FLAG_harmony_scoping = true;
681 i::FLAG_harmony_modules = true;
683 HandleScope scope(CcTest::isolate());
685 const char* firsts[] = {
687 "function x() { return 1 }; x()",
690 "module x { export let a = 1 }; x.a",
693 const char* seconds[] = {
695 "function x() { return 2 }; x()",
698 "module x { export let a = 2 }; x.a",
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
710 context.Check(seconds[j],
711 i < 2 ? EXPECT_RESULT : EXPECT_ERROR,
712 Number::New(CcTest::isolate(), 2));
718 TEST(CrossScriptDynamicLookup) {
719 i::FLAG_harmony_scoping = true;
721 HandleScope handle_scope(CcTest::isolate());
724 SimpleContext context;
725 Local<String> undefined_string = String::NewFromUtf8(
726 CcTest::isolate(), "undefined", String::kInternalizedString);
727 Local<String> number_string = String::NewFromUtf8(
728 CcTest::isolate(), "number", String::kInternalizedString);
731 "function f(o) { with(o) { return x; } }"
732 "function g(o) { with(o) { x = 15; } }"
733 "function h(o) { with(o) { return typeof x; } }",
734 EXPECT_RESULT, Undefined(CcTest::isolate()));
735 context.Check("h({})", EXPECT_RESULT, undefined_string);
740 EXPECT_RESULT, Number::New(CcTest::isolate(), 1));
745 EXPECT_RESULT, Number::New(CcTest::isolate(), 15));
746 context.Check("f({})", EXPECT_RESULT, Number::New(CcTest::isolate(), 15));
747 context.Check("h({})", EXPECT_RESULT, number_string);