Commit 1a64b02d authored by dslomov@chromium.org's avatar dslomov@chromium.org

harmony_scoping: Implement lexical bindings at top level

This implements correct semantics for "extensible" top level lexical scope.
The entire lexical scope is represented at runtime by GlobalContextTable, reachable from native context and accumulating global contexts from every script loaded into the context.

When the new script starts executing, it does the following validation:
- checks the GlobalContextTable and global object (non-configurable own) properties against the set of declarations it introduces and reports potential conflicts.
- invalidates the conflicting PropertyCells on global object, so that any code depending on them will miss/deopt causing any contextual lookups to be reexecuted under the new bindings
- adds the lexical bindings it introduces to the GlobalContextTable

Loads and stores for contextual lookups are modified so that they check the GlobalContextTable before looking up properties on global object, thus implementing the shadowing of global object properties by lexical declarations.

R=adamk@chromium.org, rossberg@chromium.org

Review URL: https://codereview.chromium.org/705663004

Cr-Commit-Position: refs/heads/master@{#25220}
git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@25220 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent e2e9e1d3
......@@ -6110,7 +6110,7 @@ class Internals {
static const int kNullValueRootIndex = 7;
static const int kTrueValueRootIndex = 8;
static const int kFalseValueRootIndex = 9;
static const int kEmptyStringRootIndex = 154;
static const int kEmptyStringRootIndex = 155;
// The external allocation limit should be below 256 MB on all architectures
// to avoid that resource-constrained embedders run low on memory.
......
......@@ -909,6 +909,10 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> global_object,
Factory* factory = isolate->factory();
Heap* heap = isolate->heap();
Handle<GlobalContextTable> global_context_table =
factory->NewGlobalContextTable();
native_context()->set_global_context_table(*global_context_table);
Handle<String> object_name = factory->Object_string();
JSObject::AddProperty(
global_object, object_name, isolate->object_function(), DONT_ENUM);
......
......@@ -11,6 +11,48 @@
namespace v8 {
namespace internal {
Handle<GlobalContextTable> GlobalContextTable::Extend(
Handle<GlobalContextTable> table, Handle<Context> global_context) {
Handle<GlobalContextTable> result;
int used = table->used();
int length = table->length();
CHECK(used >= 0 && length > 0 && used < length);
if (used + 1 == length) {
CHECK(length < Smi::kMaxValue / 2);
result = Handle<GlobalContextTable>::cast(
FixedArray::CopySize(table, length * 2));
} else {
result = table;
}
result->set_used(used + 1);
DCHECK(global_context->IsGlobalContext());
result->set(used + 1, *global_context);
return result;
}
bool GlobalContextTable::Lookup(Handle<GlobalContextTable> table,
Handle<String> name, LookupResult* result) {
for (int i = 0; i < table->used(); i++) {
Handle<Context> context = GetContext(table, i);
DCHECK(context->IsGlobalContext());
Handle<ScopeInfo> scope_info(ScopeInfo::cast(context->extension()));
int slot_index = ScopeInfo::ContextSlotIndex(
scope_info, name, &result->mode, &result->init_flag,
&result->maybe_assigned_flag);
if (slot_index >= 0) {
result->context_index = i;
result->slot_index = slot_index;
return true;
}
}
return false;
}
Context* Context::declaration_context() {
Context* current = this;
while (!current->IsFunctionContext() && !current->IsNativeContext()) {
......@@ -102,6 +144,53 @@ static Maybe<PropertyAttributes> UnscopableLookup(LookupIterator* it) {
return attrs;
}
static void GetAttributesAndBindingFlags(VariableMode mode,
InitializationFlag init_flag,
PropertyAttributes* attributes,
BindingFlags* binding_flags) {
switch (mode) {
case INTERNAL: // Fall through.
case VAR:
*attributes = NONE;
*binding_flags = MUTABLE_IS_INITIALIZED;
break;
case LET:
*attributes = NONE;
*binding_flags = (init_flag == kNeedsInitialization)
? MUTABLE_CHECK_INITIALIZED
: MUTABLE_IS_INITIALIZED;
break;
case CONST_LEGACY:
*attributes = READ_ONLY;
*binding_flags = (init_flag == kNeedsInitialization)
? IMMUTABLE_CHECK_INITIALIZED
: IMMUTABLE_IS_INITIALIZED;
break;
case CONST:
*attributes = READ_ONLY;
*binding_flags = (init_flag == kNeedsInitialization)
? IMMUTABLE_CHECK_INITIALIZED_HARMONY
: IMMUTABLE_IS_INITIALIZED_HARMONY;
break;
case MODULE:
*attributes = READ_ONLY;
*binding_flags = IMMUTABLE_IS_INITIALIZED_HARMONY;
break;
case DYNAMIC:
case DYNAMIC_GLOBAL:
case DYNAMIC_LOCAL:
case TEMPORARY:
// Note: Fixed context slots are statically allocated by the compiler.
// Statically allocated variables always have a statically known mode,
// which is the mode with which they were declared when added to the
// scope. Thus, the DYNAMIC mode (which corresponds to dynamically
// declared variables that were introduced through declaration nodes)
// must not appear here.
UNREACHABLE();
break;
}
}
Handle<Object> Context::Lookup(Handle<String> name,
ContextLookupFlags flags,
......@@ -122,8 +211,6 @@ Handle<Object> Context::Lookup(Handle<String> name,
PrintF(")\n");
}
bool visited_global_context = false;
do {
if (FLAG_trace_contexts) {
PrintF(" - looking in context %p", reinterpret_cast<void*>(*context));
......@@ -132,19 +219,6 @@ Handle<Object> Context::Lookup(Handle<String> name,
PrintF("\n");
}
if (follow_context_chain && FLAG_harmony_scoping &&
!visited_global_context &&
(context->IsGlobalContext() || context->IsNativeContext())) {
// For lexical scoping, on a top level, we might resolve to the
// lexical bindings introduced by later scrips. Therefore we need to
// switch to the the last added global context during lookup here.
context = Handle<Context>(context->global_object()->global_context());
visited_global_context = true;
if (FLAG_trace_contexts) {
PrintF(" - switching to current global context %p\n",
reinterpret_cast<void*>(*context));
}
}
// 1. Check global objects, subjects of with, and extension objects.
if (context->IsNativeContext() ||
......@@ -152,6 +226,30 @@ Handle<Object> Context::Lookup(Handle<String> name,
(context->IsFunctionContext() && context->has_extension())) {
Handle<JSReceiver> object(
JSReceiver::cast(context->extension()), isolate);
if (context->IsNativeContext()) {
if (FLAG_trace_contexts) {
PrintF(" - trying other global contexts\n");
}
// Try other global contexts.
Handle<GlobalContextTable> global_contexts(
context->global_object()->native_context()->global_context_table());
GlobalContextTable::LookupResult r;
if (GlobalContextTable::Lookup(global_contexts, name, &r)) {
if (FLAG_trace_contexts) {
Handle<Context> c = GlobalContextTable::GetContext(global_contexts,
r.context_index);
PrintF("=> found property in global context %d: %p\n",
r.context_index, reinterpret_cast<void*>(*c));
}
*index = r.slot_index;
GetAttributesAndBindingFlags(r.mode, r.init_flag, attributes,
binding_flags);
return GlobalContextTable::GetContext(global_contexts,
r.context_index);
}
}
// Context extension objects needs to behave as if they have no
// prototype. So even if we want to follow prototype chains, we need
// to only do a local lookup for context extension objects.
......@@ -206,45 +304,8 @@ Handle<Object> Context::Lookup(Handle<String> name,
slot_index, mode);
}
*index = slot_index;
// Note: Fixed context slots are statically allocated by the compiler.
// Statically allocated variables always have a statically known mode,
// which is the mode with which they were declared when added to the
// scope. Thus, the DYNAMIC mode (which corresponds to dynamically
// declared variables that were introduced through declaration nodes)
// must not appear here.
switch (mode) {
case INTERNAL: // Fall through.
case VAR:
*attributes = NONE;
*binding_flags = MUTABLE_IS_INITIALIZED;
break;
case LET:
*attributes = NONE;
*binding_flags = (init_flag == kNeedsInitialization)
? MUTABLE_CHECK_INITIALIZED : MUTABLE_IS_INITIALIZED;
break;
case CONST_LEGACY:
*attributes = READ_ONLY;
*binding_flags = (init_flag == kNeedsInitialization)
? IMMUTABLE_CHECK_INITIALIZED : IMMUTABLE_IS_INITIALIZED;
break;
case CONST:
*attributes = READ_ONLY;
*binding_flags = (init_flag == kNeedsInitialization)
? IMMUTABLE_CHECK_INITIALIZED_HARMONY :
IMMUTABLE_IS_INITIALIZED_HARMONY;
break;
case MODULE:
*attributes = READ_ONLY;
*binding_flags = IMMUTABLE_IS_INITIALIZED_HARMONY;
break;
case DYNAMIC:
case DYNAMIC_GLOBAL:
case DYNAMIC_LOCAL:
case TEMPORARY:
UNREACHABLE();
break;
}
GetAttributesAndBindingFlags(mode, init_flag, attributes,
binding_flags);
return context;
}
......
......@@ -183,7 +183,57 @@ enum BindingFlags {
V(SET_ITERATOR_MAP_INDEX, Map, set_iterator_map) \
V(ITERATOR_SYMBOL_INDEX, Symbol, iterator_symbol) \
V(UNSCOPABLES_SYMBOL_INDEX, Symbol, unscopables_symbol) \
V(ARRAY_VALUES_ITERATOR_INDEX, JSFunction, array_values_iterator)
V(ARRAY_VALUES_ITERATOR_INDEX, JSFunction, array_values_iterator) \
V(GLOBAL_CONTEXT_TABLE_INDEX, GlobalContextTable, global_context_table)
// A table of all global contexts. Every loaded top-level script with top-level
// lexical declarations contributes its GlobalContext into this table.
//
// The table is a fixed array, its first slot is the current used count and
// the subsequent slots 1..used contain GlobalContexts.
class GlobalContextTable : public FixedArray {
public:
// Conversions.
static GlobalContextTable* cast(Object* context) {
DCHECK(context->IsGlobalContextTable());
return reinterpret_cast<GlobalContextTable*>(context);
}
struct LookupResult {
int context_index;
int slot_index;
VariableMode mode;
InitializationFlag init_flag;
MaybeAssignedFlag maybe_assigned_flag;
};
int used() const { return Smi::cast(get(kUsedSlot))->value(); }
void set_used(int used) { set(kUsedSlot, Smi::FromInt(used)); }
static Handle<Context> GetContext(Handle<GlobalContextTable> table, int i) {
DCHECK(i < table->used());
return Handle<Context>::cast(FixedArray::get(table, i + 1));
}
// Lookup a variable `name` in a GlobalContextTable.
// If it returns true, the variable is found and `result` contains
// valid information about its location.
// If it returns false, `result` is untouched.
MUST_USE_RESULT
static bool Lookup(Handle<GlobalContextTable> table, Handle<String> name,
LookupResult* result);
MUST_USE_RESULT
static Handle<GlobalContextTable> Extend(Handle<GlobalContextTable> table,
Handle<Context> global_context);
private:
static const int kUsedSlot = 0;
DISALLOW_IMPLICIT_CONSTRUCTORS(GlobalContextTable);
};
// JSFunctions are pairs (context, function code), sometimes also called
// closures. A Context object is used to represent function contexts and
......@@ -229,6 +279,8 @@ enum BindingFlags {
//
// Finally, with Harmony scoping, the JSFunction representing a top level
// script will have the GlobalContext rather than a FunctionContext.
// Global contexts from all top-level scripts are gathered in
// GlobalContextTable.
class Context: public FixedArray {
public:
......@@ -360,6 +412,7 @@ class Context: public FixedArray {
ITERATOR_SYMBOL_INDEX,
UNSCOPABLES_SYMBOL_INDEX,
ARRAY_VALUES_ITERATOR_INDEX,
GLOBAL_CONTEXT_TABLE_INDEX,
// Properties from here are treated as weak references by the full GC.
// Scavenge treats them as strong references.
......
......@@ -708,6 +708,16 @@ Handle<Context> Factory::NewGlobalContext(Handle<JSFunction> function,
}
Handle<GlobalContextTable> Factory::NewGlobalContextTable() {
Handle<FixedArray> array = NewFixedArray(1);
array->set_map_no_write_barrier(*global_context_table_map());
Handle<GlobalContextTable> context_table =
Handle<GlobalContextTable>::cast(array);
context_table->set_used(0);
return context_table;
}
Handle<Context> Factory::NewModuleContext(Handle<ScopeInfo> scope_info) {
Handle<FixedArray> array =
NewFixedArray(scope_info->ContextLength(), TENURED);
......
......@@ -229,6 +229,9 @@ class Factory FINAL {
Handle<Context> NewGlobalContext(Handle<JSFunction> function,
Handle<ScopeInfo> scope_info);
// Create an empty global context table.
Handle<GlobalContextTable> NewGlobalContextTable();
// Create a module context.
Handle<Context> NewModuleContext(Handle<ScopeInfo> scope_info);
......
......@@ -2609,6 +2609,7 @@ bool Heap::CreateInitialMaps() {
ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, block_context)
ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, module_context)
ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, global_context)
ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, global_context_table)
ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, native_context)
native_context_map()->set_dictionary_map(true);
......
......@@ -149,6 +149,7 @@ namespace internal {
V(Map, block_context_map, BlockContextMap) \
V(Map, module_context_map, ModuleContextMap) \
V(Map, global_context_map, GlobalContextMap) \
V(Map, global_context_table_map, GlobalContextTableMap) \
V(Map, undefined_map, UndefinedMap) \
V(Map, the_hole_map, TheHoleMap) \
V(Map, null_map, NullMap) \
......
......@@ -661,6 +661,21 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name) {
bool use_ic = MigrateDeprecated(object) ? false : FLAG_use_ic;
if (FLAG_harmony_scoping && object->IsGlobalObject() && name->IsString()) {
// Look up in global context table.
Handle<String> str_name = Handle<String>::cast(name);
Handle<GlobalObject> global = Handle<GlobalObject>::cast(object);
Handle<GlobalContextTable> global_contexts(
global->native_context()->global_context_table());
GlobalContextTable::LookupResult lookup_result;
if (GlobalContextTable::Lookup(global_contexts, str_name, &lookup_result)) {
return FixedArray::get(GlobalContextTable::GetContext(
global_contexts, lookup_result.context_index),
lookup_result.slot_index);
}
}
// Named lookup in the object.
LookupIterator it(object, name);
LookupForRead(&it);
......@@ -1363,6 +1378,25 @@ bool StoreIC::LookupForWrite(LookupIterator* it, Handle<Object> value,
MaybeHandle<Object> StoreIC::Store(Handle<Object> object, Handle<Name> name,
Handle<Object> value,
JSReceiver::StoreFromKeyed store_mode) {
if (FLAG_harmony_scoping && object->IsGlobalObject() && name->IsString()) {
// Look up in global context table.
Handle<String> str_name = Handle<String>::cast(name);
Handle<GlobalObject> global = Handle<GlobalObject>::cast(object);
Handle<GlobalContextTable> global_contexts(
global->native_context()->global_context_table());
GlobalContextTable::LookupResult lookup_result;
if (GlobalContextTable::Lookup(global_contexts, str_name, &lookup_result)) {
Handle<Context> global_context = GlobalContextTable::GetContext(
global_contexts, lookup_result.context_index);
if (lookup_result.mode == CONST) {
return TypeError("harmony_const_assign", object, name);
}
global_context->set(lookup_result.slot_index, *value);
return value;
}
}
// TODO(verwaest): Let SetProperty do the migration, since storing a property
// might deprecate the current map again, if value does not fit.
if (MigrateDeprecated(object) || object->IsJSProxy()) {
......
......@@ -767,6 +767,14 @@ bool Object::IsNativeContext() const {
}
bool Object::IsGlobalContextTable() const {
if (!Object::IsHeapObject()) return false;
Map* map = HeapObject::cast(this)->map();
Heap* heap = map->GetHeap();
return map == heap->global_context_table_map();
}
bool Object::IsScopeInfo() const {
return Object::IsHeapObject() &&
HeapObject::cast(this)->map() ==
......
......@@ -14773,6 +14773,25 @@ Handle<Object> ExternalFloat64Array::SetValue(
}
void GlobalObject::InvalidatePropertyCell(Handle<GlobalObject> global,
Handle<Name> name) {
DCHECK(!global->HasFastProperties());
Isolate* isolate = global->GetIsolate();
int entry = global->property_dictionary()->FindEntry(name);
if (entry != NameDictionary::kNotFound) {
Handle<PropertyCell> cell(
PropertyCell::cast(global->property_dictionary()->ValueAt(entry)));
Handle<Object> value(cell->value(), isolate);
Handle<PropertyCell> new_cell = isolate->factory()->NewPropertyCell(value);
global->property_dictionary()->ValueAtPut(entry, *new_cell);
Handle<Object> hole = global->GetIsolate()->factory()->the_hole_value();
PropertyCell::SetValueInferType(cell, hole);
}
}
Handle<PropertyCell> JSGlobalObject::EnsurePropertyCell(
Handle<JSGlobalObject> global,
Handle<Name> name) {
......
......@@ -87,6 +87,7 @@
// - JSFunctionResultCache
// - ScopeInfo
// - TransitionArray
// - GlobalContextTable
// - FixedDoubleArray
// - ExternalArray
// - ExternalUint8ClampedArray
......@@ -944,6 +945,7 @@ template <class C> inline bool Is(Object* obj);
V(FixedDoubleArray) \
V(ConstantPoolArray) \
V(Context) \
V(GlobalContextTable) \
V(NativeContext) \
V(ScopeInfo) \
V(JSFunction) \
......@@ -7558,6 +7560,9 @@ class GlobalObject: public JSObject {
DECLARE_CAST(GlobalObject)
static void InvalidatePropertyCell(Handle<GlobalObject> object,
Handle<Name> name);
// Layout description.
static const int kBuiltinsOffset = JSObject::kHeaderSize;
static const int kNativeContextOffset = kBuiltinsOffset + kPointerSize;
......
......@@ -27,6 +27,14 @@ static Object* DeclareGlobals(Isolate* isolate, Handle<GlobalObject> global,
Handle<String> name, Handle<Object> value,
PropertyAttributes attr, bool is_var,
bool is_const, bool is_function) {
Handle<GlobalContextTable> global_contexts(
global->native_context()->global_context_table());
GlobalContextTable::LookupResult lookup;
if (GlobalContextTable::Lookup(global_contexts, name, &lookup) &&
IsLexicalVariableMode(lookup.mode)) {
return ThrowRedeclarationError(isolate, name);
}
// Do the lookup own properties only, see ES5 erratum.
LookupIterator it(global, name, LookupIterator::HIDDEN_SKIP_INTERCEPTOR);
Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it);
......@@ -507,6 +515,35 @@ RUNTIME_FUNCTION(Runtime_NewClosure) {
pretenure_flag);
}
static Object* FindNameClash(Handle<ScopeInfo> scope_info,
Handle<GlobalObject> global_object,
Handle<GlobalContextTable> global_context) {
Isolate* isolate = scope_info->GetIsolate();
for (int var = 0; var < scope_info->ContextLocalCount(); var++) {
Handle<String> name(scope_info->ContextLocalName(var));
VariableMode mode = scope_info->ContextLocalMode(var);
GlobalContextTable::LookupResult lookup;
if (GlobalContextTable::Lookup(global_context, name, &lookup)) {
if (IsLexicalVariableMode(mode) || IsLexicalVariableMode(lookup.mode)) {
return ThrowRedeclarationError(isolate, name);
}
}
if (IsLexicalVariableMode(mode)) {
LookupIterator it(global_object, name,
LookupIterator::HIDDEN_SKIP_INTERCEPTOR);
Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it);
if (!maybe.has_value) return isolate->heap()->exception();
if ((maybe.value & DONT_DELETE) != 0) {
return ThrowRedeclarationError(isolate, name);
}
GlobalObject::InvalidatePropertyCell(global_object, name);
}
}
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_NewGlobalContext) {
HandleScope scope(isolate);
......@@ -514,12 +551,25 @@ RUNTIME_FUNCTION(Runtime_NewGlobalContext) {
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
CONVERT_ARG_HANDLE_CHECKED(ScopeInfo, scope_info, 1);
Handle<GlobalObject> global_object(function->context()->global_object());
Handle<Context> native_context(global_object->native_context());
Handle<GlobalContextTable> global_context_table(
native_context->global_context_table());
Handle<String> clashed_name;
Object* name_clash_result =
FindNameClash(scope_info, global_object, global_context_table);
if (isolate->has_pending_exception()) return name_clash_result;
Handle<Context> result =
isolate->factory()->NewGlobalContext(function, scope_info);
DCHECK(function->context() == isolate->context());
DCHECK(function->context()->global_object() == result->global_object());
result->global_object()->set_global_context(*result);
Handle<GlobalContextTable> new_global_context_table =
GlobalContextTable::Extend(global_context_table, result);
native_context->set_global_context_table(*new_global_context_table);
return *result;
}
......
......@@ -644,6 +644,44 @@ TEST(CrossScriptReferences) {
}
TEST(CrossScriptReferences_Simple) {
i::FLAG_harmony_scoping = true;
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_harmony_scoping = true;
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) {
i::FLAG_use_strict = true;
i::FLAG_harmony_scoping = true;
......@@ -704,12 +742,12 @@ TEST(CrossScriptConflicts) {
SimpleContext context;
context.Check(firsts[i], EXPECT_RESULT,
Number::New(CcTest::isolate(), 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(CcTest::isolate(), 2));
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);
}
}
}
......@@ -740,9 +778,86 @@ TEST(CrossScriptDynamicLookup) {
EXPECT_RESULT, Number::New(CcTest::isolate(), 1));
context.Check(
"'use strict';"
"g({});"
"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) {
i::FLAG_harmony_scoping = true;
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) {
i::FLAG_harmony_scoping = true;
HandleScope handle_scope(CcTest::isolate());
{
SimpleContext context;
Local<String> undefined_string = String::NewFromUtf8(
CcTest::isolate(), "undefined", String::kInternalizedString);
Local<String> number_string = String::NewFromUtf8(
CcTest::isolate(), "number", String::kInternalizedString);
context.Check(
"function f(o) { return x; }"
"function g(o) { x = 15; }"
"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({});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);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment