Revision: 25220
Author:   [email protected]
Date:     Fri Nov  7 16:29:13 2014 UTC
Log:      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.

[email protected], [email protected]

Review URL: https://codereview.chromium.org/705663004
https://code.google.com/p/v8/source/detail?r=25220

Modified:
 /branches/bleeding_edge/include/v8.h
 /branches/bleeding_edge/src/bootstrapper.cc
 /branches/bleeding_edge/src/contexts.cc
 /branches/bleeding_edge/src/contexts.h
 /branches/bleeding_edge/src/factory.cc
 /branches/bleeding_edge/src/factory.h
 /branches/bleeding_edge/src/heap/heap.cc
 /branches/bleeding_edge/src/heap/heap.h
 /branches/bleeding_edge/src/ic/ic.cc
 /branches/bleeding_edge/src/objects-inl.h
 /branches/bleeding_edge/src/objects.cc
 /branches/bleeding_edge/src/objects.h
 /branches/bleeding_edge/src/runtime/runtime-scopes.cc
 /branches/bleeding_edge/test/cctest/test-decls.cc

=======================================
--- /branches/bleeding_edge/include/v8.h        Tue Nov  4 10:02:25 2014 UTC
+++ /branches/bleeding_edge/include/v8.h        Fri Nov  7 16:29:13 2014 UTC
@@ -6110,7 +6110,7 @@
   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.
=======================================
--- /branches/bleeding_edge/src/bootstrapper.cc Fri Nov  7 16:03:11 2014 UTC
+++ /branches/bleeding_edge/src/bootstrapper.cc Fri Nov  7 16:29:13 2014 UTC
@@ -909,6 +909,10 @@
   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);
=======================================
--- /branches/bleeding_edge/src/contexts.cc     Wed Oct 29 10:59:41 2014 UTC
+++ /branches/bleeding_edge/src/contexts.cc     Fri Nov  7 16:29:13 2014 UTC
@@ -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()) {
@@ -101,6 +143,53 @@
   if (blacklist.value) return maybe(ABSENT);
   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,
@@ -121,8 +210,6 @@
     name->ShortPrint();
     PrintF(")\n");
   }
-
-  bool visited_global_context = false;

   do {
     if (FLAG_trace_contexts) {
@@ -132,19 +219,6 @@
       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 @@
         (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 @@
                  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;
       }

=======================================
--- /branches/bleeding_edge/src/contexts.h      Wed Oct  8 14:42:31 2014 UTC
+++ /branches/bleeding_edge/src/contexts.h      Fri Nov  7 16:29:13 2014 UTC
@@ -183,7 +183,57 @@
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 @@
 //
 // 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 @@
     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.
=======================================
--- /branches/bleeding_edge/src/factory.cc      Fri Nov  7 16:03:11 2014 UTC
+++ /branches/bleeding_edge/src/factory.cc      Fri Nov  7 16:29:13 2014 UTC
@@ -706,6 +706,16 @@
   DCHECK(context->IsGlobalContext());
   return context;
 }
+
+
+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) {
=======================================
--- /branches/bleeding_edge/src/factory.h       Tue Nov  4 10:02:25 2014 UTC
+++ /branches/bleeding_edge/src/factory.h       Fri Nov  7 16:29:13 2014 UTC
@@ -229,6 +229,9 @@
   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);

=======================================
--- /branches/bleeding_edge/src/heap/heap.cc    Fri Nov  7 09:36:49 2014 UTC
+++ /branches/bleeding_edge/src/heap/heap.cc    Fri Nov  7 16:29:13 2014 UTC
@@ -2609,6 +2609,7 @@
     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);
=======================================
--- /branches/bleeding_edge/src/heap/heap.h     Fri Oct 24 12:40:05 2014 UTC
+++ /branches/bleeding_edge/src/heap/heap.h     Fri Nov  7 16:29:13 2014 UTC
@@ -149,6 +149,7 @@
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) \
=======================================
--- /branches/bleeding_edge/src/ic/ic.cc        Tue Nov  4 09:22:42 2014 UTC
+++ /branches/bleeding_edge/src/ic/ic.cc        Fri Nov  7 16:29:13 2014 UTC
@@ -661,6 +661,21 @@

   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 @@
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()) {
=======================================
--- /branches/bleeding_edge/src/objects-inl.h   Fri Nov  7 16:03:11 2014 UTC
+++ /branches/bleeding_edge/src/objects-inl.h   Fri Nov  7 16:29:13 2014 UTC
@@ -765,6 +765,14 @@
       HeapObject::cast(this)->map() ==
       HeapObject::cast(this)->GetHeap()->native_context_map();
 }
+
+
+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 {
=======================================
--- /branches/bleeding_edge/src/objects.cc      Fri Nov  7 16:03:11 2014 UTC
+++ /branches/bleeding_edge/src/objects.cc      Fri Nov  7 16:29:13 2014 UTC
@@ -14771,6 +14771,25 @@
   }
   return array->GetIsolate()->factory()->NewNumber(double_value);
 }
+
+
+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(
=======================================
--- /branches/bleeding_edge/src/objects.h       Fri Nov  7 16:03:11 2014 UTC
+++ /branches/bleeding_edge/src/objects.h       Fri Nov  7 16:29:13 2014 UTC
@@ -87,6 +87,7 @@
 //         - JSFunctionResultCache
 //         - ScopeInfo
 //         - TransitionArray
+//         - GlobalContextTable
 //       - FixedDoubleArray
 //       - ExternalArray
 //         - ExternalUint8ClampedArray
@@ -944,6 +945,7 @@
   V(FixedDoubleArray)              \
   V(ConstantPoolArray)             \
   V(Context)                       \
+  V(GlobalContextTable)            \
   V(NativeContext)                 \
   V(ScopeInfo)                     \
   V(JSFunction)                    \
@@ -7558,6 +7560,9 @@

   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;
=======================================
--- /branches/bleeding_edge/src/runtime/runtime-scopes.cc Fri Nov 7 16:03:11 2014 UTC +++ /branches/bleeding_edge/src/runtime/runtime-scopes.cc Fri Nov 7 16:29:13 2014 UTC
@@ -27,6 +27,14 @@
                               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 @@
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 @@

   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;
 }

=======================================
--- /branches/bleeding_edge/test/cctest/test-decls.cc Wed Oct 29 10:59:41 2014 UTC +++ /branches/bleeding_edge/test/cctest/test-decls.cc Fri Nov 7 16:29:13 2014 UTC
@@ -642,6 +642,44 @@
                   EXPECT_EXCEPTION);
   }
 }
+
+
+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) {
@@ -704,12 +742,12 @@
       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 @@
         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);
   }

--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to