| // Copyright 2007-2008 the V8 project authors. All rights reserved. |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following |
| // disclaimer in the documentation and/or other materials provided |
| // with the distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived |
| // from this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include <stdlib.h> |
| |
| #include "src/v8.h" |
| |
| #include "src/heap/heap-inl.h" |
| #include "src/heap/heap.h" |
| #include "test/cctest/cctest.h" |
| |
| namespace v8 { |
| |
| namespace { |
| |
| enum Expectations { |
| EXPECT_RESULT, |
| EXPECT_EXCEPTION, |
| EXPECT_ERROR |
| }; |
| |
| |
| // A DeclarationContext holds a reference to a v8::Context and keeps |
| // track of various declaration related counters to make it easier to |
| // track if global declarations in the presence of interceptors behave |
| // the right way. |
| class DeclarationContext { |
| public: |
| DeclarationContext(); |
| |
| virtual ~DeclarationContext() { |
| if (is_initialized_) { |
| Isolate* isolate = CcTest::isolate(); |
| HandleScope scope(isolate); |
| Local<Context> context = Local<Context>::New(isolate, context_); |
| context->Exit(); |
| context_.Reset(); |
| } |
| } |
| |
| void Check(const char* source, int get, int set, int has, |
| Expectations expectations, |
| v8::Local<Value> value = Local<Value>()); |
| |
| int get_count() const { return get_count_; } |
| int set_count() const { return set_count_; } |
| int query_count() const { return query_count_; } |
| |
| protected: |
| virtual v8::Local<Value> Get(Local<Name> key); |
| virtual v8::Local<Value> Set(Local<Name> key, Local<Value> value); |
| virtual v8::Local<Integer> Query(Local<Name> key); |
| |
| void InitializeIfNeeded(); |
| |
| // Perform optional initialization steps on the context after it has |
| // been created. Defaults to none but may be overwritten. |
| virtual void PostInitializeContext(Local<Context> context) {} |
| |
| // Get the holder for the interceptor. Default to the instance template |
| // but may be overwritten. |
| virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) { |
| return function->InstanceTemplate(); |
| } |
| |
| // The handlers are called as static functions that forward |
| // to the instance specific virtual methods. |
| static void HandleGet(Local<Name> key, |
| const v8::PropertyCallbackInfo<v8::Value>& info); |
| static void HandleSet(Local<Name> key, Local<Value> value, |
| const v8::PropertyCallbackInfo<v8::Value>& info); |
| static void HandleQuery(Local<Name> key, |
| const v8::PropertyCallbackInfo<v8::Integer>& info); |
| |
| v8::Isolate* isolate() const { return CcTest::isolate(); } |
| |
| private: |
| bool is_initialized_; |
| Persistent<Context> context_; |
| |
| int get_count_; |
| int set_count_; |
| int query_count_; |
| |
| static DeclarationContext* GetInstance(Local<Value> data); |
| }; |
| |
| |
| DeclarationContext::DeclarationContext() |
| : is_initialized_(false), get_count_(0), set_count_(0), query_count_(0) { |
| // Do nothing. |
| } |
| |
| |
| void DeclarationContext::InitializeIfNeeded() { |
| if (is_initialized_) return; |
| Isolate* isolate = CcTest::isolate(); |
| HandleScope scope(isolate); |
| Local<FunctionTemplate> function = FunctionTemplate::New(isolate); |
| Local<Value> data = External::New(CcTest::isolate(), this); |
| GetHolder(function)->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| &HandleGet, &HandleSet, &HandleQuery, 0, 0, data)); |
| Local<Context> context = Context::New(isolate, |
| 0, |
| function->InstanceTemplate(), |
| Local<Value>()); |
| context_.Reset(isolate, context); |
| context->Enter(); |
| is_initialized_ = true; |
| // Reset counts. Bootstrapping might have called into the interceptor. |
| get_count_ = 0; |
| set_count_ = 0; |
| query_count_ = 0; |
| PostInitializeContext(context); |
| } |
| |
| |
| void DeclarationContext::Check(const char* source, int get, int set, int query, |
| Expectations expectations, |
| v8::Local<Value> value) { |
| InitializeIfNeeded(); |
| // A retry after a GC may pollute the counts, so perform gc now |
| // to avoid that. |
| CcTest::CollectGarbage(v8::internal::NEW_SPACE); |
| HandleScope scope(CcTest::isolate()); |
| TryCatch catcher(CcTest::isolate()); |
| catcher.SetVerbose(true); |
| Local<Context> context = CcTest::isolate()->GetCurrentContext(); |
| MaybeLocal<Script> script = Script::Compile( |
| context, |
| String::NewFromUtf8(CcTest::isolate(), source, v8::NewStringType::kNormal) |
| .ToLocalChecked()); |
| if (expectations == EXPECT_ERROR) { |
| CHECK(script.IsEmpty()); |
| return; |
| } |
| CHECK(!script.IsEmpty()); |
| MaybeLocal<Value> result = script.ToLocalChecked()->Run(context); |
| CHECK_EQ(get, get_count()); |
| CHECK_EQ(set, set_count()); |
| CHECK_EQ(query, query_count()); |
| if (expectations == EXPECT_RESULT) { |
| CHECK(!catcher.HasCaught()); |
| if (!value.IsEmpty()) { |
| CHECK(value->Equals(context, result.ToLocalChecked()).FromJust()); |
| } |
| } else { |
| CHECK(expectations == EXPECT_EXCEPTION); |
| CHECK(catcher.HasCaught()); |
| if (!value.IsEmpty()) { |
| CHECK(value->Equals(context, catcher.Exception()).FromJust()); |
| } |
| } |
| // Clean slate for the next test. |
| CcTest::CollectAllAvailableGarbage(); |
| } |
| |
| |
| void DeclarationContext::HandleGet( |
| Local<Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) { |
| DeclarationContext* context = GetInstance(info.Data()); |
| context->get_count_++; |
| info.GetReturnValue().Set(context->Get(key)); |
| } |
| |
| |
| void DeclarationContext::HandleSet( |
| Local<Name> key, Local<Value> value, |
| const v8::PropertyCallbackInfo<v8::Value>& info) { |
| DeclarationContext* context = GetInstance(info.Data()); |
| context->set_count_++; |
| info.GetReturnValue().Set(context->Set(key, value)); |
| } |
| |
| |
| void DeclarationContext::HandleQuery( |
| Local<Name> key, const v8::PropertyCallbackInfo<v8::Integer>& info) { |
| DeclarationContext* context = GetInstance(info.Data()); |
| context->query_count_++; |
| info.GetReturnValue().Set(context->Query(key)); |
| } |
| |
| |
| DeclarationContext* DeclarationContext::GetInstance(Local<Value> data) { |
| void* value = Local<External>::Cast(data)->Value(); |
| return static_cast<DeclarationContext*>(value); |
| } |
| |
| |
| v8::Local<Value> DeclarationContext::Get(Local<Name> key) { |
| return v8::Local<Value>(); |
| } |
| |
| |
| v8::Local<Value> DeclarationContext::Set(Local<Name> key, Local<Value> value) { |
| return v8::Local<Value>(); |
| } |
| |
| |
| v8::Local<Integer> DeclarationContext::Query(Local<Name> key) { |
| return v8::Local<Integer>(); |
| } |
| |
| } // namespace |
| |
| // Test global declaration of a property the interceptor doesn't know |
| // about and doesn't handle. |
| TEST(Unknown) { |
| HandleScope scope(CcTest::isolate()); |
| v8::V8::Initialize(); |
| |
| { DeclarationContext context; |
| context.Check("var x; x", |
| 1, // access |
| 0, 0, EXPECT_RESULT, Undefined(CcTest::isolate())); |
| } |
| |
| { DeclarationContext context; |
| context.Check("var x = 0; x", |
| 1, // access |
| 1, // initialization |
| 0, EXPECT_RESULT, Number::New(CcTest::isolate(), 0)); |
| } |
| |
| { DeclarationContext context; |
| context.Check("function x() { }; x", |
| 1, // access |
| 1, 1, EXPECT_RESULT); |
| } |
| } |
| |
| |
| class AbsentPropertyContext: public DeclarationContext { |
| protected: |
| virtual v8::Local<Integer> Query(Local<Name> key) { |
| return v8::Local<Integer>(); |
| } |
| }; |
| |
| |
| TEST(Absent) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| v8::V8::Initialize(); |
| HandleScope scope(isolate); |
| |
| { AbsentPropertyContext context; |
| context.Check("var x; x", |
| 1, // access |
| 0, 0, EXPECT_RESULT, Undefined(isolate)); |
| } |
| |
| { AbsentPropertyContext context; |
| context.Check("var x = 0; x", |
| 1, // access |
| 1, // initialization |
| 0, EXPECT_RESULT, Number::New(isolate, 0)); |
| } |
| |
| { AbsentPropertyContext context; |
| context.Check("function x() { }; x", |
| 1, // access |
| 1, 1, EXPECT_RESULT); |
| } |
| |
| { AbsentPropertyContext context; |
| context.Check("if (false) { var x = 0 }; x", |
| 1, // access |
| 0, 0, EXPECT_RESULT, Undefined(isolate)); |
| } |
| } |
| |
| |
| |
| class AppearingPropertyContext: public DeclarationContext { |
| public: |
| enum State { |
| DECLARE, |
| INITIALIZE_IF_ASSIGN, |
| UNKNOWN |
| }; |
| |
| AppearingPropertyContext() : state_(DECLARE) { } |
| |
| protected: |
| virtual v8::Local<Integer> Query(Local<Name> key) { |
| switch (state_) { |
| case DECLARE: |
| // Force declaration by returning that the |
| // property is absent. |
| state_ = INITIALIZE_IF_ASSIGN; |
| return Local<Integer>(); |
| case INITIALIZE_IF_ASSIGN: |
| // Return that the property is present so we only get the |
| // setter called when initializing with a value. |
| state_ = UNKNOWN; |
| return Integer::New(isolate(), v8::None); |
| default: |
| CHECK(state_ == UNKNOWN); |
| break; |
| } |
| // Do the lookup in the object. |
| return v8::Local<Integer>(); |
| } |
| |
| private: |
| State state_; |
| }; |
| |
| |
| TEST(Appearing) { |
| v8::V8::Initialize(); |
| HandleScope scope(CcTest::isolate()); |
| |
| { AppearingPropertyContext context; |
| context.Check("var x; x", |
| 1, // access |
| 0, 0, EXPECT_RESULT, Undefined(CcTest::isolate())); |
| } |
| |
| { AppearingPropertyContext context; |
| context.Check("var x = 0; x", |
| 1, // access |
| 1, // initialization |
| 0, EXPECT_RESULT, Number::New(CcTest::isolate(), 0)); |
| } |
| |
| { AppearingPropertyContext context; |
| context.Check("function x() { }; x", |
| 1, // access |
| 1, 1, EXPECT_RESULT); |
| } |
| } |
| |
| |
| |
| class ExistsInPrototypeContext: public DeclarationContext { |
| public: |
| ExistsInPrototypeContext() { InitializeIfNeeded(); } |
| protected: |
| virtual v8::Local<Integer> Query(Local<Name> key) { |
| // Let it seem that the property exists in the prototype object. |
| return Integer::New(isolate(), v8::None); |
| } |
| |
| // Use the prototype as the holder for the interceptors. |
| virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) { |
| return function->PrototypeTemplate(); |
| } |
| }; |
| |
| |
| TEST(ExistsInPrototype) { |
| HandleScope scope(CcTest::isolate()); |
| |
| // Sanity check to make sure that the holder of the interceptor |
| // really is the prototype object. |
| { ExistsInPrototypeContext context; |
| context.Check("this.x = 87; this.x", 0, 0, 1, EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 87)); |
| } |
| |
| { ExistsInPrototypeContext context; |
| context.Check("var x; x", |
| 0, |
| 0, |
| 0, |
| EXPECT_RESULT, Undefined(CcTest::isolate())); |
| } |
| |
| { ExistsInPrototypeContext context; |
| context.Check("var x = 0; x", |
| 0, |
| 0, |
| 0, |
| EXPECT_RESULT, Number::New(CcTest::isolate(), 0)); |
| } |
| } |
| |
| |
| |
| class AbsentInPrototypeContext: public DeclarationContext { |
| protected: |
| virtual v8::Local<Integer> Query(Local<Name> key) { |
| // Let it seem that the property is absent in the prototype object. |
| return Local<Integer>(); |
| } |
| |
| // Use the prototype as the holder for the interceptors. |
| virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) { |
| return function->PrototypeTemplate(); |
| } |
| }; |
| |
| |
| TEST(AbsentInPrototype) { |
| v8::V8::Initialize(); |
| HandleScope scope(CcTest::isolate()); |
| |
| { AbsentInPrototypeContext context; |
| context.Check("if (false) { var x = 0; }; x", |
| 0, |
| 0, |
| 0, |
| EXPECT_RESULT, Undefined(CcTest::isolate())); |
| } |
| } |
| |
| |
| |
| class ExistsInHiddenPrototypeContext: public DeclarationContext { |
| public: |
| ExistsInHiddenPrototypeContext() { |
| hidden_proto_ = FunctionTemplate::New(CcTest::isolate()); |
| hidden_proto_->SetHiddenPrototype(true); |
| } |
| |
| protected: |
| virtual v8::Local<Integer> Query(Local<Name> key) { |
| // Let it seem that the property exists in the hidden prototype object. |
| return Integer::New(isolate(), v8::None); |
| } |
| |
| // Install the hidden prototype after the global object has been created. |
| virtual void PostInitializeContext(Local<Context> context) { |
| Local<Object> global_object = context->Global(); |
| Local<Object> hidden_proto = hidden_proto_->GetFunction(context) |
| .ToLocalChecked() |
| ->NewInstance(context) |
| .ToLocalChecked(); |
| Local<Object> inner_global = |
| Local<Object>::Cast(global_object->GetPrototype()); |
| inner_global->SetPrototype(context, hidden_proto).FromJust(); |
| } |
| |
| // Use the hidden prototype as the holder for the interceptors. |
| virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) { |
| return hidden_proto_->InstanceTemplate(); |
| } |
| |
| private: |
| Local<FunctionTemplate> hidden_proto_; |
| }; |
| |
| |
| TEST(ExistsInHiddenPrototype) { |
| HandleScope scope(CcTest::isolate()); |
| |
| { ExistsInHiddenPrototypeContext context; |
| context.Check("var x; x", 0, 0, 0, EXPECT_RESULT, |
| Undefined(CcTest::isolate())); |
| } |
| |
| { ExistsInHiddenPrototypeContext context; |
| context.Check("var x = 0; x", 0, 0, 0, EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 0)); |
| } |
| |
| { ExistsInHiddenPrototypeContext context; |
| context.Check("function x() { }; x", 0, 1, 1, EXPECT_RESULT); |
| } |
| } |
| |
| |
| |
| class SimpleContext { |
| public: |
| SimpleContext() |
| : handle_scope_(CcTest::isolate()), |
| context_(Context::New(CcTest::isolate())) { |
| context_->Enter(); |
| } |
| |
| ~SimpleContext() { |
| context_->Exit(); |
| } |
| |
| void Check(const char* source, Expectations expectations, |
| v8::Local<Value> value = Local<Value>()) { |
| HandleScope scope(context_->GetIsolate()); |
| TryCatch catcher(context_->GetIsolate()); |
| catcher.SetVerbose(true); |
| MaybeLocal<Script> script = Script::Compile( |
| context_, String::NewFromUtf8(context_->GetIsolate(), source, |
| v8::NewStringType::kNormal) |
| .ToLocalChecked()); |
| if (expectations == EXPECT_ERROR) { |
| CHECK(script.IsEmpty()); |
| return; |
| } |
| CHECK(!script.IsEmpty()); |
| MaybeLocal<Value> result = script.ToLocalChecked()->Run(context_); |
| if (expectations == EXPECT_RESULT) { |
| CHECK(!catcher.HasCaught()); |
| if (!value.IsEmpty()) { |
| CHECK(value->Equals(context_, result.ToLocalChecked()).FromJust()); |
| } |
| } else { |
| CHECK(expectations == EXPECT_EXCEPTION); |
| CHECK(catcher.HasCaught()); |
| if (!value.IsEmpty()) { |
| CHECK(value->Equals(context_, catcher.Exception()).FromJust()); |
| } |
| } |
| } |
| |
| private: |
| HandleScope handle_scope_; |
| Local<Context> context_; |
| }; |
| |
| |
| TEST(CrossScriptReferences) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| HandleScope scope(isolate); |
| |
| { SimpleContext context; |
| context.Check("var x = 1; x", |
| EXPECT_RESULT, Number::New(isolate, 1)); |
| context.Check("var x = 2; x", |
| EXPECT_RESULT, Number::New(isolate, 2)); |
| context.Check("x = 5; x", |
| EXPECT_RESULT, Number::New(isolate, 5)); |
| context.Check("var x = 6; x", |
| EXPECT_RESULT, Number::New(isolate, 6)); |
| context.Check("this.x", |
| EXPECT_RESULT, Number::New(isolate, 6)); |
| context.Check("function x() { return 7 }; x()", |
| EXPECT_RESULT, Number::New(isolate, 7)); |
| } |
| } |
| |
| |
| TEST(CrossScriptReferences_Simple) { |
| i::FLAG_use_strict = true; |
| |
| v8::Isolate* isolate = CcTest::isolate(); |
| HandleScope scope(isolate); |
| |
| { |
| SimpleContext context; |
| context.Check("let x = 1; x", EXPECT_RESULT, Number::New(isolate, 1)); |
| context.Check("let x = 5; x", EXPECT_EXCEPTION); |
| } |
| } |
| |
| |
| TEST(CrossScriptReferences_Simple2) { |
| i::FLAG_use_strict = true; |
| |
| v8::Isolate* isolate = CcTest::isolate(); |
| HandleScope scope(isolate); |
| |
| for (int k = 0; k < 100; k++) { |
| SimpleContext context; |
| bool cond = (k % 2) == 0; |
| if (cond) { |
| context.Check("let x = 1; x", EXPECT_RESULT, Number::New(isolate, 1)); |
| context.Check("let z = 4; z", EXPECT_RESULT, Number::New(isolate, 4)); |
| } else { |
| context.Check("let z = 1; z", EXPECT_RESULT, Number::New(isolate, 1)); |
| context.Check("let x = 4; x", EXPECT_RESULT, Number::New(isolate, 4)); |
| } |
| context.Check("let y = 2; x", EXPECT_RESULT, |
| Number::New(isolate, cond ? 1 : 4)); |
| } |
| } |
| |
| |
| TEST(CrossScriptReferencesHarmony) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| HandleScope scope(isolate); |
| |
| // Check that simple cross-script global scope access works. |
| const char* decs[] = {"'use strict'; var x = 1; x", |
| "x", |
| "'use strict'; function x() { return 1 }; x()", |
| "x()", |
| "'use strict'; let x = 1; x", |
| "x", |
| "'use strict'; const x = 1; x", |
| "x", |
| nullptr}; |
| |
| for (int i = 0; decs[i] != nullptr; i += 2) { |
| SimpleContext context; |
| context.Check(decs[i], EXPECT_RESULT, Number::New(isolate, 1)); |
| context.Check(decs[i+1], EXPECT_RESULT, Number::New(isolate, 1)); |
| } |
| |
| // Check that cross-script global scope access works with late declarations. |
| { |
| SimpleContext context; |
| context.Check("function d0() { return x0 }", // dynamic lookup |
| EXPECT_RESULT, Undefined(isolate)); |
| context.Check("this.x0 = -1;" |
| "d0()", |
| EXPECT_RESULT, Number::New(isolate, -1)); |
| context.Check("'use strict';" |
| "function f0() { let y = 10; return x0 + y }" |
| "function g0() { let y = 10; return eval('x0 + y') }" |
| "function h0() { let y = 10; return (1,eval)('x0') + y }" |
| "x0 + f0() + g0() + h0()", |
| EXPECT_RESULT, Number::New(isolate, 26)); |
| |
| context.Check("'use strict';" |
| "let x1 = 1;" |
| "function f1() { let y = 10; return x1 + y }" |
| "function g1() { let y = 10; return eval('x1 + y') }" |
| "function h1() { let y = 10; return (1,eval)('x1') + y }" |
| "function i1() { " |
| " let y = 10; return (typeof x2 === 'undefined' ? 0 : 2) + y" |
| "}" |
| "function j1() { let y = 10; return eval('x2 + y') }" |
| "function k1() { let y = 10; return (1,eval)('x2') + y }" |
| "function cl() { " |
| " let y = 10; " |
| " return { " |
| " f: function(){ return x1 + y }," |
| " g: function(){ return eval('x1 + y') }," |
| " h: function(){ return (1,eval)('x1') + y }," |
| " i: function(){" |
| " return (typeof x2 == 'undefined' ? 0 : 2) + y" |
| " }," |
| " j: function(){ return eval('x2 + y') }," |
| " k: function(){ return (1,eval)('x2') + y }," |
| " }" |
| "}" |
| "let o = cl();" |
| "x1 + eval('x1') + (1,eval)('x1') + f1() + g1() + h1();", |
| EXPECT_RESULT, Number::New(isolate, 36)); |
| context.Check("x1 + eval('x1') + (1,eval)('x1') + f1() + g1() + h1();", |
| EXPECT_RESULT, Number::New(isolate, 36)); |
| context.Check("o.f() + o.g() + o.h();", |
| EXPECT_RESULT, Number::New(isolate, 33)); |
| context.Check("i1() + o.i();", |
| EXPECT_RESULT, Number::New(isolate, 20)); |
| |
| context.Check("'use strict';" |
| "let x2 = 2;" |
| "function f2() { let y = 20; return x2 + y }" |
| "function g2() { let y = 20; return eval('x2 + y') }" |
| "function h2() { let y = 20; return (1,eval)('x2') + y }" |
| "function i2() { let y = 20; return x1 + y }" |
| "function j2() { let y = 20; return eval('x1 + y') }" |
| "function k2() { let y = 20; return (1,eval)('x1') + y }" |
| "x2 + eval('x2') + (1,eval)('x2') + f2() + g2() + h2();", |
| EXPECT_RESULT, Number::New(isolate, 72)); |
| context.Check("x1 + eval('x1') + (1,eval)('x1') + f1() + g1() + h1();", |
| EXPECT_RESULT, Number::New(isolate, 36)); |
| context.Check("i1() + j1() + k1();", |
| EXPECT_RESULT, Number::New(isolate, 36)); |
| context.Check("i2() + j2() + k2();", |
| EXPECT_RESULT, Number::New(isolate, 63)); |
| context.Check("o.f() + o.g() + o.h();", |
| EXPECT_RESULT, Number::New(isolate, 33)); |
| context.Check("o.i() + o.j() + o.k();", |
| EXPECT_RESULT, Number::New(isolate, 36)); |
| context.Check("i1() + o.i();", |
| EXPECT_RESULT, Number::New(isolate, 24)); |
| |
| context.Check("'use strict';" |
| "let x0 = 100;" |
| "x0 + eval('x0') + (1,eval)('x0') + " |
| " d0() + f0() + g0() + h0();", |
| EXPECT_RESULT, Number::New(isolate, 730)); |
| context.Check("x0 + eval('x0') + (1,eval)('x0') + " |
| " d0() + f0() + g0() + h0();", |
| EXPECT_RESULT, Number::New(isolate, 730)); |
| context.Check("delete this.x0;" |
| "x0 + eval('x0') + (1,eval)('x0') + " |
| " d0() + f0() + g0() + h0();", |
| EXPECT_RESULT, Number::New(isolate, 730)); |
| context.Check("this.x1 = 666;" |
| "x1 + eval('x1') + (1,eval)('x1') + f1() + g1() + h1();", |
| EXPECT_RESULT, Number::New(isolate, 36)); |
| context.Check("delete this.x1;" |
| "x1 + eval('x1') + (1,eval)('x1') + f1() + g1() + h1();", |
| EXPECT_RESULT, Number::New(isolate, 36)); |
| } |
| |
| // Check that caching does respect scopes. |
| { |
| SimpleContext context; |
| const char* script1 = "(function(){ return y1 })()"; |
| const char* script2 = "(function(){ return y2 })()"; |
| |
| context.Check(script1, EXPECT_EXCEPTION); |
| context.Check("this.y1 = 1; this.y2 = 2; 0;", |
| EXPECT_RESULT, Number::New(isolate, 0)); |
| context.Check(script1, |
| EXPECT_RESULT, Number::New(isolate, 1)); |
| context.Check("'use strict'; let y1 = 3; 0;", |
| EXPECT_RESULT, Number::New(isolate, 0)); |
| context.Check(script1, |
| EXPECT_RESULT, Number::New(isolate, 3)); |
| context.Check("y1 = 4;", |
| EXPECT_RESULT, Number::New(isolate, 4)); |
| context.Check(script1, |
| EXPECT_RESULT, Number::New(isolate, 4)); |
| |
| context.Check(script2, |
| EXPECT_RESULT, Number::New(isolate, 2)); |
| context.Check("'use strict'; let y2 = 5; 0;", |
| EXPECT_RESULT, Number::New(isolate, 0)); |
| context.Check(script1, |
| EXPECT_RESULT, Number::New(isolate, 4)); |
| context.Check(script2, |
| EXPECT_RESULT, Number::New(isolate, 5)); |
| } |
| } |
| |
| |
| TEST(CrossScriptReferencesHarmonyRegress) { |
| v8::Isolate* isolate = CcTest::isolate(); |
| HandleScope scope(isolate); |
| SimpleContext context; |
| context.Check( |
| "'use strict';" |
| "function i1() { " |
| " let y = 10; return (typeof x2 === 'undefined' ? 0 : 2) + y" |
| "}" |
| "i1();" |
| "i1();", |
| EXPECT_RESULT, Number::New(isolate, 10)); |
| context.Check( |
| "'use strict';" |
| "let x2 = 2; i1();", |
| EXPECT_RESULT, Number::New(isolate, 12)); |
| } |
| |
| |
| TEST(GlobalLexicalOSR) { |
| i::FLAG_use_strict = true; |
| |
| v8::Isolate* isolate = CcTest::isolate(); |
| HandleScope scope(isolate); |
| SimpleContext context; |
| |
| context.Check("'use strict';" |
| "let x = 1; x;", |
| EXPECT_RESULT, Number::New(isolate, 1)); |
| context.Check("'use strict';" |
| "let y = 2*x;" |
| "++x;" |
| "let z = 0;" |
| "const limit = 100000;" |
| "for (var i = 0; i < limit; ++i) {" |
| " z += x + y;" |
| "}" |
| "z;", |
| EXPECT_RESULT, Number::New(isolate, 400000)); |
| } |
| |
| |
| TEST(CrossScriptConflicts) { |
| i::FLAG_use_strict = true; |
| |
| HandleScope scope(CcTest::isolate()); |
| |
| const char* firsts[] = {"var x = 1; x", "function x() { return 1 }; x()", |
| "let x = 1; x", "const x = 1; x", nullptr}; |
| const char* seconds[] = {"var x = 2; x", "function x() { return 2 }; x()", |
| "let x = 2; x", "const x = 2; x", nullptr}; |
| |
| for (int i = 0; firsts[i] != nullptr; ++i) { |
| for (int j = 0; seconds[j] != nullptr; ++j) { |
| SimpleContext context; |
| context.Check(firsts[i], EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 1)); |
| bool success_case = i < 2 && j < 2; |
| Local<Value> success_result; |
| if (success_case) success_result = Number::New(CcTest::isolate(), 2); |
| |
| context.Check(seconds[j], success_case ? EXPECT_RESULT : EXPECT_EXCEPTION, |
| success_result); |
| } |
| } |
| } |
| |
| |
| TEST(CrossScriptDynamicLookup) { |
| HandleScope handle_scope(CcTest::isolate()); |
| |
| { |
| SimpleContext context; |
| Local<String> undefined_string = |
| String::NewFromUtf8(CcTest::isolate(), "undefined", |
| v8::NewStringType::kInternalized) |
| .ToLocalChecked(); |
| Local<String> number_string = |
| String::NewFromUtf8(CcTest::isolate(), "number", |
| v8::NewStringType::kInternalized) |
| .ToLocalChecked(); |
| |
| context.Check( |
| "function f(o) { with(o) { return x; } }" |
| "function g(o) { with(o) { x = 15; } }" |
| "function h(o) { with(o) { return typeof x; } }", |
| EXPECT_RESULT, Undefined(CcTest::isolate())); |
| context.Check("h({})", EXPECT_RESULT, undefined_string); |
| context.Check( |
| "'use strict';" |
| "let x = 1;" |
| "f({})", |
| EXPECT_RESULT, Number::New(CcTest::isolate(), 1)); |
| context.Check( |
| "'use strict';" |
| "g({});0", |
| EXPECT_RESULT, Number::New(CcTest::isolate(), 0)); |
| context.Check("f({})", EXPECT_RESULT, Number::New(CcTest::isolate(), 15)); |
| context.Check("h({})", EXPECT_RESULT, number_string); |
| } |
| } |
| |
| |
| TEST(CrossScriptGlobal) { |
| HandleScope handle_scope(CcTest::isolate()); |
| { |
| SimpleContext context; |
| |
| context.Check( |
| "var global = this;" |
| "global.x = 255;" |
| "x", |
| EXPECT_RESULT, Number::New(CcTest::isolate(), 255)); |
| context.Check( |
| "'use strict';" |
| "let x = 1;" |
| "global.x", |
| EXPECT_RESULT, Number::New(CcTest::isolate(), 255)); |
| context.Check("global.x = 15; x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 1)); |
| context.Check("x = 221; global.x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 15)); |
| context.Check( |
| "z = 15;" |
| "function f() { return z; };" |
| "for (var k = 0; k < 3; k++) { f(); }" |
| "f()", |
| EXPECT_RESULT, Number::New(CcTest::isolate(), 15)); |
| context.Check( |
| "'use strict';" |
| "let z = 5; f()", |
| EXPECT_RESULT, Number::New(CcTest::isolate(), 5)); |
| context.Check( |
| "function f() { konst = 10; return konst; };" |
| "f()", |
| EXPECT_RESULT, Number::New(CcTest::isolate(), 10)); |
| context.Check( |
| "'use strict';" |
| "const konst = 255;" |
| "f()", |
| EXPECT_EXCEPTION); |
| } |
| } |
| |
| |
| TEST(CrossScriptStaticLookupUndeclared) { |
| HandleScope handle_scope(CcTest::isolate()); |
| |
| { |
| SimpleContext context; |
| Local<String> undefined_string = |
| String::NewFromUtf8(CcTest::isolate(), "undefined", |
| v8::NewStringType::kInternalized) |
| .ToLocalChecked(); |
| Local<String> number_string = |
| String::NewFromUtf8(CcTest::isolate(), "number", |
| v8::NewStringType::kInternalized) |
| .ToLocalChecked(); |
| |
| context.Check( |
| "function f(o) { return x; }" |
| "function g(v) { x = v; }" |
| "function h(o) { return typeof x; }", |
| EXPECT_RESULT, Undefined(CcTest::isolate())); |
| context.Check("h({})", EXPECT_RESULT, undefined_string); |
| context.Check( |
| "'use strict';" |
| "let x = 1;" |
| "f({})", |
| EXPECT_RESULT, Number::New(CcTest::isolate(), 1)); |
| context.Check( |
| "'use strict';" |
| "g(15);x", |
| EXPECT_RESULT, Number::New(CcTest::isolate(), 15)); |
| context.Check("h({})", EXPECT_RESULT, number_string); |
| context.Check("f({})", EXPECT_RESULT, Number::New(CcTest::isolate(), 15)); |
| context.Check("h({})", EXPECT_RESULT, number_string); |
| } |
| } |
| |
| |
| TEST(CrossScriptLoadICs) { |
| i::FLAG_allow_natives_syntax = true; |
| |
| HandleScope handle_scope(CcTest::isolate()); |
| |
| { |
| SimpleContext context; |
| context.Check( |
| "x = 15;" |
| "function f() { return x; }" |
| "function g() { return x; }" |
| "f()", |
| EXPECT_RESULT, Number::New(CcTest::isolate(), 15)); |
| context.Check( |
| "'use strict';" |
| "let x = 5;" |
| "f()", |
| EXPECT_RESULT, Number::New(CcTest::isolate(), 5)); |
| for (int k = 0; k < 3; k++) { |
| context.Check("g()", EXPECT_RESULT, Number::New(CcTest::isolate(), 5)); |
| } |
| for (int k = 0; k < 3; k++) { |
| context.Check("f()", EXPECT_RESULT, Number::New(CcTest::isolate(), 5)); |
| } |
| context.Check("%OptimizeFunctionOnNextCall(g); g()", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 5)); |
| context.Check("%OptimizeFunctionOnNextCall(f); f()", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 5)); |
| } |
| { |
| SimpleContext context; |
| context.Check( |
| "x = 15;" |
| "function f() { return x; }" |
| "f()", |
| EXPECT_RESULT, Number::New(CcTest::isolate(), 15)); |
| for (int k = 0; k < 3; k++) { |
| context.Check("f()", EXPECT_RESULT, Number::New(CcTest::isolate(), 15)); |
| } |
| context.Check("%OptimizeFunctionOnNextCall(f); f()", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 15)); |
| context.Check( |
| "'use strict';" |
| "let x = 5;" |
| "f()", |
| EXPECT_RESULT, Number::New(CcTest::isolate(), 5)); |
| for (int k = 0; k < 3; k++) { |
| context.Check("f()", EXPECT_RESULT, Number::New(CcTest::isolate(), 5)); |
| } |
| context.Check("%OptimizeFunctionOnNextCall(f); f()", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 5)); |
| } |
| } |
| |
| |
| TEST(CrossScriptStoreICs) { |
| i::FLAG_allow_natives_syntax = true; |
| |
| HandleScope handle_scope(CcTest::isolate()); |
| |
| { |
| SimpleContext context; |
| context.Check( |
| "var global = this;" |
| "x = 15;" |
| "function f(v) { x = v; }" |
| "function g(v) { x = v; }" |
| "f(10); x", |
| EXPECT_RESULT, Number::New(CcTest::isolate(), 10)); |
| context.Check( |
| "'use strict';" |
| "let x = 5;" |
| "f(7); x", |
| EXPECT_RESULT, Number::New(CcTest::isolate(), 7)); |
| context.Check("global.x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 10)); |
| for (int k = 0; k < 3; k++) { |
| context.Check("g(31); x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 31)); |
| } |
| context.Check("global.x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 10)); |
| for (int k = 0; k < 3; k++) { |
| context.Check("f(32); x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 32)); |
| } |
| context.Check("global.x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 10)); |
| context.Check("%OptimizeFunctionOnNextCall(g); g(18); x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 18)); |
| context.Check("global.x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 10)); |
| context.Check("%OptimizeFunctionOnNextCall(f); f(33); x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 33)); |
| context.Check("global.x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 10)); |
| } |
| { |
| SimpleContext context; |
| context.Check( |
| "var global = this;" |
| "x = 15;" |
| "function f(v) { x = v; }" |
| "f(10); x", |
| EXPECT_RESULT, Number::New(CcTest::isolate(), 10)); |
| for (int k = 0; k < 3; k++) { |
| context.Check("f(18); x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 18)); |
| } |
| context.Check("%OptimizeFunctionOnNextCall(f); f(20); x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 20)); |
| context.Check( |
| "'use strict';" |
| "let x = 5;" |
| "f(8); x", |
| EXPECT_RESULT, Number::New(CcTest::isolate(), 8)); |
| context.Check("global.x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 20)); |
| for (int k = 0; k < 3; k++) { |
| context.Check("f(13); x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 13)); |
| } |
| context.Check("global.x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 20)); |
| context.Check("%OptimizeFunctionOnNextCall(f); f(41); x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 41)); |
| context.Check("global.x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 20)); |
| } |
| } |
| |
| |
| TEST(CrossScriptAssignmentToConst) { |
| i::FLAG_allow_natives_syntax = true; |
| |
| HandleScope handle_scope(CcTest::isolate()); |
| |
| { |
| SimpleContext context; |
| |
| context.Check("function f() { x = 27; }", EXPECT_RESULT, |
| Undefined(CcTest::isolate())); |
| context.Check("'use strict';const x = 1; x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 1)); |
| context.Check("f();", EXPECT_EXCEPTION); |
| context.Check("x", EXPECT_RESULT, Number::New(CcTest::isolate(), 1)); |
| context.Check("f();", EXPECT_EXCEPTION); |
| context.Check("x", EXPECT_RESULT, Number::New(CcTest::isolate(), 1)); |
| context.Check("%OptimizeFunctionOnNextCall(f);f();", EXPECT_EXCEPTION); |
| context.Check("x", EXPECT_RESULT, Number::New(CcTest::isolate(), 1)); |
| } |
| } |
| |
| |
| TEST(Regress425510) { |
| i::FLAG_allow_natives_syntax = true; |
| |
| HandleScope handle_scope(CcTest::isolate()); |
| |
| { |
| SimpleContext context; |
| |
| context.Check("'use strict'; o; const o = 10", EXPECT_EXCEPTION); |
| |
| for (int i = 0; i < 100; i++) { |
| context.Check("o.prototype", EXPECT_EXCEPTION); |
| } |
| } |
| } |
| |
| |
| TEST(Regress3941) { |
| i::FLAG_allow_natives_syntax = true; |
| |
| HandleScope handle_scope(CcTest::isolate()); |
| |
| { |
| SimpleContext context; |
| context.Check("function f() { x = 1; }", EXPECT_RESULT, |
| Undefined(CcTest::isolate())); |
| context.Check("'use strict'; f(); let x = 2; x", EXPECT_EXCEPTION); |
| } |
| |
| |
| { |
| // Train ICs. |
| SimpleContext context; |
| context.Check("function f() { x = 1; }", EXPECT_RESULT, |
| Undefined(CcTest::isolate())); |
| for (int i = 0; i < 4; i++) { |
| context.Check("f(); x", EXPECT_RESULT, Number::New(CcTest::isolate(), 1)); |
| } |
| context.Check("'use strict'; f(); let x = 2; x", EXPECT_EXCEPTION); |
| } |
| |
| |
| { |
| // Optimize. |
| SimpleContext context; |
| context.Check("function f() { x = 1; }", EXPECT_RESULT, |
| Undefined(CcTest::isolate())); |
| for (int i = 0; i < 4; i++) { |
| context.Check("f(); x", EXPECT_RESULT, Number::New(CcTest::isolate(), 1)); |
| } |
| context.Check("%OptimizeFunctionOnNextCall(f); f(); x", EXPECT_RESULT, |
| Number::New(CcTest::isolate(), 1)); |
| |
| context.Check("'use strict'; f(); let x = 2; x", EXPECT_EXCEPTION); |
| } |
| } |
| |
| |
| TEST(Regress3941_Reads) { |
| i::FLAG_allow_natives_syntax = true; |
| |
| HandleScope handle_scope(CcTest::isolate()); |
| |
| { |
| SimpleContext context; |
| context.Check("function f() { return x; }", EXPECT_RESULT, |
| Undefined(CcTest::isolate())); |
| context.Check("'use strict'; f(); let x = 2; x", EXPECT_EXCEPTION); |
| } |
| |
| |
| { |
| // Train ICs. |
| SimpleContext context; |
| context.Check("function f() { return x; }", EXPECT_RESULT, |
| Undefined(CcTest::isolate())); |
| for (int i = 0; i < 4; i++) { |
| context.Check("f()", EXPECT_EXCEPTION); |
| } |
| context.Check("'use strict'; f(); let x = 2; x", EXPECT_EXCEPTION); |
| } |
| |
| |
| { |
| // Optimize. |
| SimpleContext context; |
| context.Check("function f() { return x; }", EXPECT_RESULT, |
| Undefined(CcTest::isolate())); |
| for (int i = 0; i < 4; i++) { |
| context.Check("f()", EXPECT_EXCEPTION); |
| } |
| context.Check("%OptimizeFunctionOnNextCall(f);", EXPECT_RESULT, |
| Undefined(CcTest::isolate())); |
| |
| context.Check("'use strict'; f(); let x = 2; x", EXPECT_EXCEPTION); |
| } |
| } |
| |
| } // namespace v8 |