625874a48c
BUG= R=svenpanne@chromium.org Review URL: https://codereview.chromium.org/23707009 Patch from Marja Hölttä <marja@chromium.org>. git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16493 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
856 lines
24 KiB
C++
856 lines
24 KiB
C++
// 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 "v8.h"
|
|
|
|
#include "heap.h"
|
|
#include "cctest.h"
|
|
|
|
using namespace v8;
|
|
|
|
|
|
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 = Isolate::GetCurrent();
|
|
HandleScope scope(isolate);
|
|
Local<Context> context = Local<Context>::New(isolate, context_);
|
|
context->Exit();
|
|
context_.Dispose();
|
|
}
|
|
}
|
|
|
|
void Check(const char* source,
|
|
int get, int set, int has,
|
|
Expectations expectations,
|
|
v8::Handle<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::Handle<Value> Get(Local<String> key);
|
|
virtual v8::Handle<Value> Set(Local<String> key, Local<Value> value);
|
|
virtual v8::Handle<Integer> Query(Local<String> 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(Handle<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<String> key,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info);
|
|
static void HandleSet(Local<String> key,
|
|
Local<Value> value,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info);
|
|
static void HandleQuery(Local<String> key,
|
|
const v8::PropertyCallbackInfo<v8::Integer>& info);
|
|
|
|
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 = Isolate::GetCurrent();
|
|
HandleScope scope(isolate);
|
|
Local<FunctionTemplate> function = FunctionTemplate::New();
|
|
Local<Value> data = External::New(this);
|
|
GetHolder(function)->SetNamedPropertyHandler(&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;
|
|
PostInitializeContext(context);
|
|
}
|
|
|
|
|
|
void DeclarationContext::Check(const char* source,
|
|
int get, int set, int query,
|
|
Expectations expectations,
|
|
v8::Handle<Value> value) {
|
|
InitializeIfNeeded();
|
|
// A retry after a GC may pollute the counts, so perform gc now
|
|
// to avoid that.
|
|
HEAP->CollectGarbage(v8::internal::NEW_SPACE);
|
|
HandleScope scope(Isolate::GetCurrent());
|
|
TryCatch catcher;
|
|
catcher.SetVerbose(true);
|
|
Local<Script> script = Script::Compile(String::New(source));
|
|
if (expectations == EXPECT_ERROR) {
|
|
CHECK(script.IsEmpty());
|
|
return;
|
|
}
|
|
CHECK(!script.IsEmpty());
|
|
Local<Value> result = script->Run();
|
|
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_EQ(value, result);
|
|
}
|
|
} else {
|
|
CHECK(expectations == EXPECT_EXCEPTION);
|
|
CHECK(catcher.HasCaught());
|
|
if (!value.IsEmpty()) {
|
|
CHECK_EQ(value, catcher.Exception());
|
|
}
|
|
}
|
|
HEAP->CollectAllAvailableGarbage(); // Clean slate for the next test.
|
|
}
|
|
|
|
|
|
void DeclarationContext::HandleGet(
|
|
Local<String> 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<String> 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<String> 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::Handle<Value> DeclarationContext::Get(Local<String> key) {
|
|
return v8::Handle<Value>();
|
|
}
|
|
|
|
|
|
v8::Handle<Value> DeclarationContext::Set(Local<String> key,
|
|
Local<Value> value) {
|
|
return v8::Handle<Value>();
|
|
}
|
|
|
|
|
|
v8::Handle<Integer> DeclarationContext::Query(Local<String> key) {
|
|
return v8::Handle<Integer>();
|
|
}
|
|
|
|
|
|
// Test global declaration of a property the interceptor doesn't know
|
|
// about and doesn't handle.
|
|
TEST(Unknown) {
|
|
HandleScope scope(Isolate::GetCurrent());
|
|
|
|
{ DeclarationContext context;
|
|
context.Check("var x; x",
|
|
1, // access
|
|
1, // declaration
|
|
2, // declaration + initialization
|
|
EXPECT_RESULT, Undefined());
|
|
}
|
|
|
|
{ DeclarationContext context;
|
|
context.Check("var x = 0; x",
|
|
1, // access
|
|
2, // declaration + initialization
|
|
2, // declaration + initialization
|
|
EXPECT_RESULT, Number::New(0));
|
|
}
|
|
|
|
{ DeclarationContext context;
|
|
context.Check("function x() { }; x",
|
|
1, // access
|
|
0,
|
|
0,
|
|
EXPECT_RESULT);
|
|
}
|
|
|
|
{ DeclarationContext context;
|
|
context.Check("const x; x",
|
|
1, // access
|
|
2, // declaration + initialization
|
|
1, // declaration
|
|
EXPECT_RESULT, Undefined());
|
|
}
|
|
|
|
{ DeclarationContext context;
|
|
context.Check("const x = 0; x",
|
|
1, // access
|
|
2, // declaration + initialization
|
|
1, // declaration
|
|
EXPECT_RESULT, Undefined()); // SB 0 - BUG 1213579
|
|
}
|
|
}
|
|
|
|
|
|
|
|
class PresentPropertyContext: public DeclarationContext {
|
|
protected:
|
|
virtual v8::Handle<Integer> Query(Local<String> key) {
|
|
return Integer::New(v8::None);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
TEST(Present) {
|
|
HandleScope scope(Isolate::GetCurrent());
|
|
|
|
{ PresentPropertyContext context;
|
|
context.Check("var x; x",
|
|
1, // access
|
|
0,
|
|
2, // declaration + initialization
|
|
EXPECT_EXCEPTION); // x is not defined!
|
|
}
|
|
|
|
{ PresentPropertyContext context;
|
|
context.Check("var x = 0; x",
|
|
1, // access
|
|
1, // initialization
|
|
2, // declaration + initialization
|
|
EXPECT_RESULT, Number::New(0));
|
|
}
|
|
|
|
{ PresentPropertyContext context;
|
|
context.Check("function x() { }; x",
|
|
1, // access
|
|
0,
|
|
0,
|
|
EXPECT_RESULT);
|
|
}
|
|
|
|
{ PresentPropertyContext context;
|
|
context.Check("const x; x",
|
|
1, // access
|
|
1, // initialization
|
|
1, // (re-)declaration
|
|
EXPECT_RESULT, Undefined());
|
|
}
|
|
|
|
{ PresentPropertyContext context;
|
|
context.Check("const x = 0; x",
|
|
1, // access
|
|
1, // initialization
|
|
1, // (re-)declaration
|
|
EXPECT_RESULT, Number::New(0));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
class AbsentPropertyContext: public DeclarationContext {
|
|
protected:
|
|
virtual v8::Handle<Integer> Query(Local<String> key) {
|
|
return v8::Handle<Integer>();
|
|
}
|
|
};
|
|
|
|
|
|
TEST(Absent) {
|
|
HandleScope scope(Isolate::GetCurrent());
|
|
|
|
{ AbsentPropertyContext context;
|
|
context.Check("var x; x",
|
|
1, // access
|
|
1, // declaration
|
|
2, // declaration + initialization
|
|
EXPECT_RESULT, Undefined());
|
|
}
|
|
|
|
{ AbsentPropertyContext context;
|
|
context.Check("var x = 0; x",
|
|
1, // access
|
|
2, // declaration + initialization
|
|
2, // declaration + initialization
|
|
EXPECT_RESULT, Number::New(0));
|
|
}
|
|
|
|
{ AbsentPropertyContext context;
|
|
context.Check("function x() { }; x",
|
|
1, // access
|
|
0,
|
|
0,
|
|
EXPECT_RESULT);
|
|
}
|
|
|
|
{ AbsentPropertyContext context;
|
|
context.Check("const x; x",
|
|
1, // access
|
|
2, // declaration + initialization
|
|
1, // declaration
|
|
EXPECT_RESULT, Undefined());
|
|
}
|
|
|
|
{ AbsentPropertyContext context;
|
|
context.Check("const x = 0; x",
|
|
1, // access
|
|
2, // declaration + initialization
|
|
1, // declaration
|
|
EXPECT_RESULT, Undefined()); // SB 0 - BUG 1213579
|
|
}
|
|
|
|
{ AbsentPropertyContext context;
|
|
context.Check("if (false) { var x = 0 }; x",
|
|
1, // access
|
|
1, // declaration
|
|
1, // declaration + initialization
|
|
EXPECT_RESULT, Undefined());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
class AppearingPropertyContext: public DeclarationContext {
|
|
public:
|
|
enum State {
|
|
DECLARE,
|
|
INITIALIZE_IF_ASSIGN,
|
|
UNKNOWN
|
|
};
|
|
|
|
AppearingPropertyContext() : state_(DECLARE) { }
|
|
|
|
protected:
|
|
virtual v8::Handle<Integer> Query(Local<String> key) {
|
|
switch (state_) {
|
|
case DECLARE:
|
|
// Force declaration by returning that the
|
|
// property is absent.
|
|
state_ = INITIALIZE_IF_ASSIGN;
|
|
return Handle<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(v8::None);
|
|
default:
|
|
CHECK(state_ == UNKNOWN);
|
|
break;
|
|
}
|
|
// Do the lookup in the object.
|
|
return v8::Handle<Integer>();
|
|
}
|
|
|
|
private:
|
|
State state_;
|
|
};
|
|
|
|
|
|
TEST(Appearing) {
|
|
HandleScope scope(Isolate::GetCurrent());
|
|
|
|
{ AppearingPropertyContext context;
|
|
context.Check("var x; x",
|
|
1, // access
|
|
1, // declaration
|
|
2, // declaration + initialization
|
|
EXPECT_RESULT, Undefined());
|
|
}
|
|
|
|
{ AppearingPropertyContext context;
|
|
context.Check("var x = 0; x",
|
|
1, // access
|
|
2, // declaration + initialization
|
|
2, // declaration + initialization
|
|
EXPECT_RESULT, Number::New(0));
|
|
}
|
|
|
|
{ AppearingPropertyContext context;
|
|
context.Check("function x() { }; x",
|
|
1, // access
|
|
0,
|
|
0,
|
|
EXPECT_RESULT);
|
|
}
|
|
|
|
{ AppearingPropertyContext context;
|
|
context.Check("const x; x",
|
|
1, // access
|
|
2, // declaration + initialization
|
|
1, // declaration
|
|
EXPECT_RESULT, Undefined());
|
|
}
|
|
|
|
{ AppearingPropertyContext context;
|
|
context.Check("const x = 0; x",
|
|
1, // access
|
|
2, // declaration + initialization
|
|
1, // declaration
|
|
EXPECT_RESULT, Undefined());
|
|
// Result is undefined because declaration succeeded but
|
|
// initialization to 0 failed (due to context behavior).
|
|
}
|
|
}
|
|
|
|
|
|
|
|
class ReappearingPropertyContext: public DeclarationContext {
|
|
public:
|
|
enum State {
|
|
DECLARE,
|
|
DONT_DECLARE,
|
|
INITIALIZE,
|
|
UNKNOWN
|
|
};
|
|
|
|
ReappearingPropertyContext() : state_(DECLARE) { }
|
|
|
|
protected:
|
|
virtual v8::Handle<Integer> Query(Local<String> key) {
|
|
switch (state_) {
|
|
case DECLARE:
|
|
// Force the first declaration by returning that
|
|
// the property is absent.
|
|
state_ = DONT_DECLARE;
|
|
return Handle<Integer>();
|
|
case DONT_DECLARE:
|
|
// Ignore the second declaration by returning
|
|
// that the property is already there.
|
|
state_ = INITIALIZE;
|
|
return Integer::New(v8::None);
|
|
case INITIALIZE:
|
|
// Force an initialization by returning that
|
|
// the property is absent. This will make sure
|
|
// that the setter is called and it will not
|
|
// lead to redeclaration conflicts (yet).
|
|
state_ = UNKNOWN;
|
|
return Handle<Integer>();
|
|
default:
|
|
CHECK(state_ == UNKNOWN);
|
|
break;
|
|
}
|
|
// Do the lookup in the object.
|
|
return Handle<Integer>();
|
|
}
|
|
|
|
private:
|
|
State state_;
|
|
};
|
|
|
|
|
|
TEST(Reappearing) {
|
|
HandleScope scope(Isolate::GetCurrent());
|
|
|
|
{ ReappearingPropertyContext context;
|
|
context.Check("const x; var x = 0",
|
|
0,
|
|
3, // const declaration+initialization, var initialization
|
|
3, // 2 x declaration + var initialization
|
|
EXPECT_RESULT, Undefined());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
class ExistsInPrototypeContext: public DeclarationContext {
|
|
protected:
|
|
virtual v8::Handle<Integer> Query(Local<String> key) {
|
|
// Let it seem that the property exists in the prototype object.
|
|
return Integer::New(v8::None);
|
|
}
|
|
|
|
// Use the prototype as the holder for the interceptors.
|
|
virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
|
|
return function->PrototypeTemplate();
|
|
}
|
|
};
|
|
|
|
|
|
TEST(ExistsInPrototype) {
|
|
i::FLAG_es52_globals = true;
|
|
HandleScope scope(Isolate::GetCurrent());
|
|
|
|
// 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,
|
|
0,
|
|
EXPECT_RESULT, Number::New(87));
|
|
}
|
|
|
|
{ ExistsInPrototypeContext context;
|
|
context.Check("var x; x",
|
|
0,
|
|
0,
|
|
0,
|
|
EXPECT_RESULT, Undefined());
|
|
}
|
|
|
|
{ ExistsInPrototypeContext context;
|
|
context.Check("var x = 0; x",
|
|
0,
|
|
0,
|
|
0,
|
|
EXPECT_RESULT, Number::New(0));
|
|
}
|
|
|
|
{ ExistsInPrototypeContext context;
|
|
context.Check("const x; x",
|
|
0,
|
|
0,
|
|
0,
|
|
EXPECT_RESULT, Undefined());
|
|
}
|
|
|
|
{ ExistsInPrototypeContext context;
|
|
context.Check("const x = 0; x",
|
|
0,
|
|
0,
|
|
0,
|
|
EXPECT_RESULT, Number::New(0));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
class AbsentInPrototypeContext: public DeclarationContext {
|
|
protected:
|
|
virtual v8::Handle<Integer> Query(Local<String> key) {
|
|
// Let it seem that the property is absent in the prototype object.
|
|
return Handle<Integer>();
|
|
}
|
|
|
|
// Use the prototype as the holder for the interceptors.
|
|
virtual Local<ObjectTemplate> GetHolder(Local<FunctionTemplate> function) {
|
|
return function->PrototypeTemplate();
|
|
}
|
|
};
|
|
|
|
|
|
TEST(AbsentInPrototype) {
|
|
i::FLAG_es52_globals = true;
|
|
HandleScope scope(Isolate::GetCurrent());
|
|
|
|
{ AbsentInPrototypeContext context;
|
|
context.Check("if (false) { var x = 0; }; x",
|
|
0,
|
|
0,
|
|
0,
|
|
EXPECT_RESULT, Undefined());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
class ExistsInHiddenPrototypeContext: public DeclarationContext {
|
|
public:
|
|
ExistsInHiddenPrototypeContext() {
|
|
hidden_proto_ = FunctionTemplate::New();
|
|
hidden_proto_->SetHiddenPrototype(true);
|
|
}
|
|
|
|
protected:
|
|
virtual v8::Handle<Integer> Query(Local<String> key) {
|
|
// Let it seem that the property exists in the hidden prototype object.
|
|
return Integer::New(v8::None);
|
|
}
|
|
|
|
// Install the hidden prototype after the global object has been created.
|
|
virtual void PostInitializeContext(Handle<Context> context) {
|
|
Local<Object> global_object = context->Global();
|
|
Local<Object> hidden_proto = hidden_proto_->GetFunction()->NewInstance();
|
|
context->DetachGlobal();
|
|
context->Global()->SetPrototype(hidden_proto);
|
|
context->ReattachGlobal(global_object);
|
|
}
|
|
|
|
// 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) {
|
|
i::FLAG_es52_globals = true;
|
|
HandleScope scope(Isolate::GetCurrent());
|
|
|
|
{ ExistsInHiddenPrototypeContext context;
|
|
context.Check("var x; x",
|
|
1, // access
|
|
0,
|
|
2, // declaration + initialization
|
|
EXPECT_EXCEPTION); // x is not defined!
|
|
}
|
|
|
|
{ ExistsInHiddenPrototypeContext context;
|
|
context.Check("var x = 0; x",
|
|
1, // access
|
|
1, // initialization
|
|
2, // declaration + initialization
|
|
EXPECT_RESULT, Number::New(0));
|
|
}
|
|
|
|
{ ExistsInHiddenPrototypeContext context;
|
|
context.Check("function x() { }; x",
|
|
0,
|
|
0,
|
|
0,
|
|
EXPECT_RESULT);
|
|
}
|
|
|
|
// TODO(mstarzinger): The semantics of global const is vague.
|
|
{ ExistsInHiddenPrototypeContext context;
|
|
context.Check("const x; x",
|
|
0,
|
|
0,
|
|
1, // (re-)declaration
|
|
EXPECT_RESULT, Undefined());
|
|
}
|
|
|
|
// TODO(mstarzinger): The semantics of global const is vague.
|
|
{ ExistsInHiddenPrototypeContext context;
|
|
context.Check("const x = 0; x",
|
|
0,
|
|
0,
|
|
1, // (re-)declaration
|
|
EXPECT_RESULT, Number::New(0));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
class SimpleContext {
|
|
public:
|
|
SimpleContext()
|
|
: handle_scope_(Isolate::GetCurrent()),
|
|
context_(Context::New(Isolate::GetCurrent())) {
|
|
context_->Enter();
|
|
}
|
|
|
|
~SimpleContext() {
|
|
context_->Exit();
|
|
}
|
|
|
|
void Check(const char* source,
|
|
Expectations expectations,
|
|
v8::Handle<Value> value = Local<Value>()) {
|
|
HandleScope scope(context_->GetIsolate());
|
|
TryCatch catcher;
|
|
catcher.SetVerbose(true);
|
|
Local<Script> script = Script::Compile(String::New(source));
|
|
if (expectations == EXPECT_ERROR) {
|
|
CHECK(script.IsEmpty());
|
|
return;
|
|
}
|
|
CHECK(!script.IsEmpty());
|
|
Local<Value> result = script->Run();
|
|
if (expectations == EXPECT_RESULT) {
|
|
CHECK(!catcher.HasCaught());
|
|
if (!value.IsEmpty()) {
|
|
CHECK_EQ(value, result);
|
|
}
|
|
} else {
|
|
CHECK(expectations == EXPECT_EXCEPTION);
|
|
CHECK(catcher.HasCaught());
|
|
if (!value.IsEmpty()) {
|
|
CHECK_EQ(value, catcher.Exception());
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
HandleScope handle_scope_;
|
|
Local<Context> context_;
|
|
};
|
|
|
|
|
|
TEST(CrossScriptReferences) {
|
|
HandleScope scope(Isolate::GetCurrent());
|
|
|
|
{ SimpleContext context;
|
|
context.Check("var x = 1; x",
|
|
EXPECT_RESULT, Number::New(1));
|
|
context.Check("var x = 2; x",
|
|
EXPECT_RESULT, Number::New(2));
|
|
context.Check("const x = 3; x",
|
|
EXPECT_RESULT, Number::New(3));
|
|
context.Check("const x = 4; x",
|
|
EXPECT_RESULT, Number::New(4));
|
|
context.Check("x = 5; x",
|
|
EXPECT_RESULT, Number::New(5));
|
|
context.Check("var x = 6; x",
|
|
EXPECT_RESULT, Number::New(6));
|
|
context.Check("this.x",
|
|
EXPECT_RESULT, Number::New(6));
|
|
context.Check("function x() { return 7 }; x()",
|
|
EXPECT_RESULT, Number::New(7));
|
|
}
|
|
|
|
{ SimpleContext context;
|
|
context.Check("const x = 1; x",
|
|
EXPECT_RESULT, Number::New(1));
|
|
context.Check("var x = 2; x", // assignment ignored
|
|
EXPECT_RESULT, Number::New(1));
|
|
context.Check("const x = 3; x",
|
|
EXPECT_RESULT, Number::New(1));
|
|
context.Check("x = 4; x", // assignment ignored
|
|
EXPECT_RESULT, Number::New(1));
|
|
context.Check("var x = 5; x", // assignment ignored
|
|
EXPECT_RESULT, Number::New(1));
|
|
context.Check("this.x",
|
|
EXPECT_RESULT, Number::New(1));
|
|
context.Check("function x() { return 7 }; x",
|
|
EXPECT_EXCEPTION);
|
|
}
|
|
}
|
|
|
|
|
|
TEST(CrossScriptReferencesHarmony) {
|
|
i::FLAG_use_strict = true;
|
|
i::FLAG_harmony_scoping = true;
|
|
i::FLAG_harmony_modules = true;
|
|
|
|
HandleScope scope(Isolate::GetCurrent());
|
|
|
|
const char* decs[] = {
|
|
"var x = 1; x", "x", "this.x",
|
|
"function x() { return 1 }; x()", "x()", "this.x()",
|
|
"let x = 1; x", "x", "this.x",
|
|
"const x = 1; x", "x", "this.x",
|
|
"module x { export let a = 1 }; x.a", "x.a", "this.x.a",
|
|
NULL
|
|
};
|
|
|
|
for (int i = 0; decs[i] != NULL; i += 3) {
|
|
SimpleContext context;
|
|
context.Check(decs[i], EXPECT_RESULT, Number::New(1));
|
|
context.Check(decs[i+1], EXPECT_RESULT, Number::New(1));
|
|
// TODO(rossberg): The current ES6 draft spec does not reflect lexical
|
|
// bindings on the global object. However, this will probably change, in
|
|
// which case we reactivate the following test.
|
|
if (i/3 < 2) context.Check(decs[i+2], EXPECT_RESULT, Number::New(1));
|
|
}
|
|
}
|
|
|
|
|
|
TEST(CrossScriptConflicts) {
|
|
i::FLAG_use_strict = true;
|
|
i::FLAG_harmony_scoping = true;
|
|
i::FLAG_harmony_modules = true;
|
|
|
|
HandleScope scope(Isolate::GetCurrent());
|
|
|
|
const char* firsts[] = {
|
|
"var x = 1; x",
|
|
"function x() { return 1 }; x()",
|
|
"let x = 1; x",
|
|
"const x = 1; x",
|
|
"module x { export let a = 1 }; x.a",
|
|
NULL
|
|
};
|
|
const char* seconds[] = {
|
|
"var x = 2; x",
|
|
"function x() { return 2 }; x()",
|
|
"let x = 2; x",
|
|
"const x = 2; x",
|
|
"module x { export let a = 2 }; x.a",
|
|
NULL
|
|
};
|
|
|
|
for (int i = 0; firsts[i] != NULL; ++i) {
|
|
for (int j = 0; seconds[j] != NULL; ++j) {
|
|
SimpleContext context;
|
|
context.Check(firsts[i], EXPECT_RESULT, Number::New(1));
|
|
// TODO(rossberg): All tests should actually be errors in Harmony,
|
|
// but we currently do not detect the cases where the first declaration
|
|
// is not lexical.
|
|
context.Check(seconds[j],
|
|
i < 2 ? EXPECT_RESULT : EXPECT_ERROR, Number::New(2));
|
|
}
|
|
}
|
|
}
|