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.
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);
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()) {
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,
PrintF(")\n");
}
- bool visited_global_context = false;
-
do {
if (FLAG_trace_contexts) {
PrintF(" - looking in context %p", reinterpret_cast<void*>(*context));
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() ||
(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.
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;
}
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
//
// 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:
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.
}
+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);
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);
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);
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) \
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);
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()) {
}
+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() ==
}
+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) {
// - JSFunctionResultCache
// - ScopeInfo
// - TransitionArray
+// - GlobalContextTable
// - FixedDoubleArray
// - ExternalArray
// - ExternalUint8ClampedArray
V(FixedDoubleArray) \
V(ConstantPoolArray) \
V(Context) \
+ V(GlobalContextTable) \
V(NativeContext) \
V(ScopeInfo) \
V(JSFunction) \
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;
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);
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);
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;
}
}
+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;
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);
}
}
}
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);
}