Allocate block-scoped global bindings to global context.
authorrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 28 Aug 2012 11:25:08 +0000 (11:25 +0000)
committerrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 28 Aug 2012 11:25:08 +0000 (11:25 +0000)
- The global object has a reference to the current global scope chain.
  Running a script adds to the chain if it contains global lexical declarations.
- Scripts are executed relative to a global, not a native context.
- Harmony let and const bindings are allocated to the innermost global context;
  var and function still live on the global object.
  (Lexical bindings are not reflected on the global object at all,
  but that will probably change later using accessors, as for modules.)
- Compilation of scripts now needs a (global) context (previously only eval did).
- The global scope chain represents one logical scope, so collision tests take
  the chain into account.

R=svenpanne@chromium.org
BUG=

Review URL: https://chromiumcodereview.appspot.com/10872084

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12398 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

19 files changed:
src/api.cc
src/bootstrapper.cc
src/bootstrapper.h
src/compiler.cc
src/compiler.h
src/heap.cc
src/isolate.cc
src/isolate.h
src/objects-inl.h
src/objects.cc
src/objects.h
src/parser.cc
src/rewriter.cc
src/runtime.cc
src/scopes.cc
src/variables.cc
src/variables.h
test/cctest/test-decls.cc
test/mjsunit/harmony/block-conflicts.js

index dafeed3572b36493d2309fc883548c82c3d9f7cc..23e8c571caaea1140645b5bf2daa577b41a9d04f 100644 (file)
@@ -1539,7 +1539,7 @@ Local<Script> Script::New(v8::Handle<String> source,
                            name_obj,
                            line_offset,
                            column_offset,
-                           isolate->native_context(),
+                           isolate->global_context(),
                            NULL,
                            pre_data_impl,
                            Utils::OpenHandle(*script_data),
@@ -1577,7 +1577,7 @@ Local<Script> Script::Compile(v8::Handle<String> source,
   i::Handle<i::JSFunction> result =
       isolate->factory()->NewFunctionFromSharedFunctionInfo(
           function,
-          isolate->native_context());
+          isolate->global_context());
   return Local<Script>(ToApi<Script>(result));
 }
 
@@ -1604,7 +1604,7 @@ Local<Value> Script::Run() {
       i::Handle<i::SharedFunctionInfo>
           function_info(i::SharedFunctionInfo::cast(*obj), isolate);
       fun = isolate->factory()->NewFunctionFromSharedFunctionInfo(
-          function_info, isolate->native_context());
+          function_info, isolate->global_context());
     } else {
       fun = i::Handle<i::JSFunction>(i::JSFunction::cast(*obj), isolate);
     }
@@ -4536,9 +4536,9 @@ void Context::ReattachGlobal(Handle<Object> global_object) {
   i::Object** ctx = reinterpret_cast<i::Object**>(this);
   i::Handle<i::Context> context =
       i::Handle<i::Context>::cast(i::Handle<i::Object>(ctx));
-  isolate->bootstrapper()->ReattachGlobal(
-      context,
-      Utils::OpenHandle(*global_object));
+  i::Handle<i::JSGlobalProxy> global_proxy =
+      i::Handle<i::JSGlobalProxy>::cast(Utils::OpenHandle(*global_object));
+  isolate->bootstrapper()->ReattachGlobal(context, global_proxy);
 }
 
 
index 45e73d582d7e9ca7872ff0ab5848b95d06075206..992659edce9bbc46c5de8bfd843607f6f769dcdc 100644 (file)
@@ -327,23 +327,20 @@ static void SetObjectPrototype(Handle<JSObject> object, Handle<Object> proto) {
 
 void Bootstrapper::DetachGlobal(Handle<Context> env) {
   Factory* factory = env->GetIsolate()->factory();
-  JSGlobalProxy::cast(env->global_proxy())->
-      set_native_context(*factory->null_value());
-  SetObjectPrototype(Handle<JSObject>(env->global_proxy()),
-                     factory->null_value());
+  Handle<JSGlobalProxy> global_proxy(JSGlobalProxy::cast(env->global_proxy()));
+  global_proxy->set_native_context(*factory->null_value());
+  SetObjectPrototype(global_proxy, factory->null_value());
   env->set_global_proxy(env->global_object());
   env->global_object()->set_global_receiver(env->global_object());
 }
 
 
 void Bootstrapper::ReattachGlobal(Handle<Context> env,
-                                  Handle<Object> global_object) {
-  ASSERT(global_object->IsJSGlobalProxy());
-  Handle<JSGlobalProxy> global = Handle<JSGlobalProxy>::cast(global_object);
-  env->global_object()->set_global_receiver(*global);
-  env->set_global_proxy(*global);
-  SetObjectPrototype(global, Handle<JSObject>(env->global_object()));
-  global->set_native_context(*env);
+                                  Handle<JSGlobalProxy> global_proxy) {
+  env->global_object()->set_global_receiver(*global_proxy);
+  env->set_global_proxy(*global_proxy);
+  SetObjectPrototype(global_proxy, Handle<JSObject>(env->global_object()));
+  global_proxy->set_native_context(*env);
 }
 
 
@@ -797,6 +794,7 @@ void Genesis::HookUpGlobalProxy(Handle<GlobalObject> inner_global,
                                 Handle<JSGlobalProxy> global_proxy) {
   // Set the native context for the global object.
   inner_global->set_native_context(*native_context());
+  inner_global->set_global_context(*native_context());
   inner_global->set_global_receiver(*global_proxy);
   global_proxy->set_native_context(*native_context());
   native_context()->set_global_proxy(*global_proxy);
@@ -1443,6 +1441,7 @@ bool Genesis::InstallNatives() {
       Handle<JSBuiltinsObject>::cast(factory()->NewGlobalObject(builtins_fun));
   builtins->set_builtins(*builtins);
   builtins->set_native_context(*native_context());
+  builtins->set_global_context(*native_context());
   builtins->set_global_receiver(*builtins);
 
   // Set up the 'global' properties of the builtins object. The
index f2043d0f7a162f2483ec08e3d365224846fef5b6..179e65c35414852b30f221ee38e1823072a8a67c 100644 (file)
@@ -104,7 +104,7 @@ class Bootstrapper {
   void DetachGlobal(Handle<Context> env);
 
   // Reattach an outer global object to an environment.
-  void ReattachGlobal(Handle<Context> env, Handle<Object> global_object);
+  void ReattachGlobal(Handle<Context> env, Handle<JSGlobalProxy> global_proxy);
 
   // Traverses the pointers for memory management.
   void Iterate(ObjectVisitor* v);
index 61efe26df3cf6cf6fe89001f4d642555892ea15e..fb9d5cb948ac4036f4636c73343d933be90c5326 100644 (file)
@@ -98,6 +98,7 @@ CompilationInfo::CompilationInfo(Handle<JSFunction> closure, Zone* zone)
       script_(Handle<Script>(Script::cast(shared_info_->script()))),
       extension_(NULL),
       pre_parse_data_(NULL),
+      context_(closure->context()),
       osr_ast_id_(BailoutId::None()),
       zone_(zone),
       deferred_handles_(NULL) {
@@ -292,6 +293,7 @@ OptimizingCompiler::Status OptimizingCompiler::CreateGraph() {
     // optimized code.
     unoptimized.SetFunction(info()->function());
     unoptimized.SetScope(info()->scope());
+    unoptimized.SetContext(info()->context());
     if (should_recompile) unoptimized.EnableDeoptimizationSupport();
     bool succeeded = FullCodeGenerator::MakeCode(&unoptimized);
     if (should_recompile) {
@@ -595,6 +597,7 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source,
     info.MarkAsGlobal();
     info.SetExtension(extension);
     info.SetPreParseData(pre_data);
+    info.SetContext(context);
     if (FLAG_use_strict) {
       info.SetLanguageMode(FLAG_harmony_scoping ? EXTENDED_MODE : STRICT_MODE);
     }
@@ -643,7 +646,7 @@ Handle<SharedFunctionInfo> Compiler::CompileEval(Handle<String> source,
     info.MarkAsEval();
     if (is_global) info.MarkAsGlobal();
     info.SetLanguageMode(language_mode);
-    info.SetCallingContext(context);
+    info.SetContext(context);
     result = MakeFunctionInfo(&info);
     if (!result.is_null()) {
       // Explicitly disable optimization for eval code. We're not yet prepared
index cb5745e93a56a1055bfaaa9c673877abb46a2768..af9459566dd06f862e38a7837d4c60ab4d62fabc 100644 (file)
@@ -72,7 +72,7 @@ class CompilationInfo {
   Handle<Script> script() const { return script_; }
   v8::Extension* extension() const { return extension_; }
   ScriptDataImpl* pre_parse_data() const { return pre_parse_data_; }
-  Handle<Context> calling_context() const { return calling_context_; }
+  Handle<Context> context() const { return context_; }
   BailoutId osr_ast_id() const { return osr_ast_id_; }
 
   void MarkAsEval() {
@@ -120,9 +120,8 @@ class CompilationInfo {
     ASSERT(!is_lazy());
     pre_parse_data_ = pre_parse_data;
   }
-  void SetCallingContext(Handle<Context> context) {
-    ASSERT(is_eval());
-    calling_context_ = context;
+  void SetContext(Handle<Context> context) {
+    context_ = context;
   }
   void MarkCompilingForDebugging(Handle<Code> current_code) {
     ASSERT(mode_ != OPTIMIZE);
@@ -180,7 +179,7 @@ class CompilationInfo {
   void SaveHandles() {
     SaveHandle(&closure_);
     SaveHandle(&shared_info_);
-    SaveHandle(&calling_context_);
+    SaveHandle(&context_);
     SaveHandle(&script_);
   }
 
@@ -262,9 +261,9 @@ class CompilationInfo {
   v8::Extension* extension_;
   ScriptDataImpl* pre_parse_data_;
 
-  // The context of the caller is needed for eval code, and will be a null
-  // handle otherwise.
-  Handle<Context> calling_context_;
+  // The context of the caller for eval code, and the global context for a
+  // global script. Will be a null handle otherwise.
+  Handle<Context> context_;
 
   // Compilation mode flag and whether deoptimization is allowed.
   Mode mode_;
index 694d9fd2361b8302118e5712e91df33462b8d531..9ef8a60d9c57d0109662a604875537daeb7a2611 100644 (file)
@@ -4919,7 +4919,9 @@ MaybeObject* Heap::AllocateGlobalContext(JSFunction* function,
   Context* context = reinterpret_cast<Context*>(result);
   context->set_map_no_write_barrier(global_context_map());
   context->set_closure(function);
+  context->set_previous(function->context());
   context->set_extension(scope_info);
+  context->set_global_object(function->context()->global_object());
   ASSERT(context->IsGlobalContext());
   ASSERT(result->IsContext());
   return context;
index 76bfc9b459923d5f0369ebad873713e1167afb92..84d23e4315a8d29008515aae81525d60cdb06f0a 100644 (file)
@@ -1330,6 +1330,12 @@ Handle<Context> Isolate::native_context() {
 }
 
 
+Handle<Context> Isolate::global_context() {
+  GlobalObject* global = thread_local_top()->context_->global_object();
+  return Handle<Context>(global->global_context());
+}
+
+
 Handle<Context> Isolate::GetCallingNativeContext() {
   JavaScriptFrameIterator it;
 #ifdef ENABLE_DEBUGGER_SUPPORT
index 3461f9791695ce3a4ba488ae17a7ce7ed73d58ff..93b9fb3361dacec621b96b9912b9bb66b4c8a008 100644 (file)
@@ -764,8 +764,9 @@ class Isolate {
   void IterateThread(ThreadVisitor* v, char* t);
 
 
-  // Returns the current native context.
+  // Returns the current native and global context.
   Handle<Context> native_context();
+  Handle<Context> global_context();
 
   // Returns the native context of the calling JavaScript code.  That
   // is, the native context of the top-most JavaScript frame.
index 7083fbaaf3c687d158c284aa51855a7a7090930d..a7978338058b7e9b37dee0d0c17c90d734d04c1f 100644 (file)
@@ -3689,6 +3689,7 @@ ACCESSORS(JSFunction, next_function_link, Object, kNextFunctionLinkOffset)
 
 ACCESSORS(GlobalObject, builtins, JSBuiltinsObject, kBuiltinsOffset)
 ACCESSORS(GlobalObject, native_context, Context, kNativeContextOffset)
+ACCESSORS(GlobalObject, global_context, Context, kGlobalContextOffset)
 ACCESSORS(GlobalObject, global_receiver, JSObject, kGlobalReceiverOffset)
 
 ACCESSORS(JSGlobalProxy, native_context, Object, kNativeContextOffset)
index c25955b46c42b6d89822c189632142ddd5f51239..2fea4849ad5354bb42e52a572470ea240c0f3d5d 100644 (file)
@@ -11998,20 +11998,15 @@ MaybeObject* CompilationCacheTable::Put(String* src,
                       shared,
                       CurrentGlobalLanguageMode(),
                       RelocInfo::kNoPosition);
-  Object* obj;
-  { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
-    if (!maybe_obj->ToObject(&obj)) return maybe_obj;
-  }
-
-  CompilationCacheTable* cache =
-      reinterpret_cast<CompilationCacheTable*>(obj);
-  int entry = cache->FindInsertionEntry(key.Hash());
+  CompilationCacheTable* cache;
+  MaybeObject* maybe_cache = EnsureCapacity(1, &key);
+  if (!maybe_cache->To(&cache)) return maybe_cache;
 
   Object* k;
-  { MaybeObject* maybe_k = key.AsObject();
-    if (!maybe_k->ToObject(&k)) return maybe_k;
-  }
+  MaybeObject* maybe_k = key.AsObject();
+  if (!maybe_k->To(&k)) return maybe_k;
 
+  int entry = cache->FindInsertionEntry(key.Hash());
   cache->set(EntryToIndex(entry), k);
   cache->set(EntryToIndex(entry) + 1, value);
   cache->ElementAdded();
index d9900c916cd8e5ba01b9c1ac2e9f1e04f428c1c7..64a8f3cd3dde7ed4ca540efe33b816d20fe3c1f2 100644 (file)
@@ -6198,6 +6198,9 @@ class GlobalObject: public JSObject {
   // [native context]: the natives corresponding to this global object.
   DECL_ACCESSORS(native_context, Context)
 
+  // [global context]: the most recent (i.e. innermost) global context.
+  DECL_ACCESSORS(global_context, Context)
+
   // [global receiver]: the global receiver object of the context
   DECL_ACCESSORS(global_receiver, JSObject)
 
@@ -6227,7 +6230,8 @@ class GlobalObject: public JSObject {
   // Layout description.
   static const int kBuiltinsOffset = JSObject::kHeaderSize;
   static const int kNativeContextOffset = kBuiltinsOffset + kPointerSize;
-  static const int kGlobalReceiverOffset = kNativeContextOffset + kPointerSize;
+  static const int kGlobalContextOffset = kNativeContextOffset + kPointerSize;
+  static const int kGlobalReceiverOffset = kGlobalContextOffset + kPointerSize;
   static const int kHeaderSize = kGlobalReceiverOffset + kPointerSize;
 
  private:
index 472740e8931c2295334fc136247ecce9af0882c2..1ec133876c07cc20e5a084b101edeef36cb6a4e3 100644 (file)
@@ -624,19 +624,20 @@ FunctionLiteral* Parser::DoParseProgram(CompilationInfo* info,
   FunctionLiteral* result = NULL;
   { Scope* scope = NewScope(top_scope_, GLOBAL_SCOPE);
     info->SetGlobalScope(scope);
+    if (!info->context().is_null()) {
+      scope = Scope::DeserializeScopeChain(*info->context(), scope, zone());
+    }
     if (info->is_eval()) {
-      Handle<SharedFunctionInfo> shared = info->shared_info();
-      if (!info->is_global() && (shared.is_null() || shared->is_function())) {
-        scope = Scope::DeserializeScopeChain(*info->calling_context(), scope,
-                                             zone());
-      }
       if (!scope->is_global_scope() || info->language_mode() != CLASSIC_MODE) {
         scope = NewScope(scope, EVAL_SCOPE);
       }
+    } else if (info->is_global()) {
+      scope = NewScope(scope, GLOBAL_SCOPE);
     }
     scope->set_start_position(0);
     scope->set_end_position(source->length());
-    FunctionState function_state(this, scope, isolate());
+
+    FunctionState function_state(this, scope, isolate());  // Enters 'scope'.
     top_scope_->SetLanguageMode(info->language_mode());
     ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(16, zone());
     bool ok = true;
@@ -1787,50 +1788,58 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
       declaration_scope->is_strict_or_extended_eval_scope() ||
       declaration_scope->is_block_scope() ||
       declaration_scope->is_module_scope() ||
-      declaration->AsModuleDeclaration() != NULL) {
+      declaration_scope->is_global_scope()) {
     // Declare the variable in the declaration scope.
-    var = declaration_scope->LocalLookup(name);
+    // For the global scope, we have to check for collisions with earlier
+    // (i.e., enclosing) global scopes, to maintain the illusion of a single
+    // global scope.
+    var = declaration_scope->is_global_scope()
+        ? declaration_scope->Lookup(name)
+        : declaration_scope->LocalLookup(name);
     if (var == NULL) {
       // Declare the name.
       var = declaration_scope->DeclareLocal(
           name, mode, declaration->initialization(), proxy->interface());
-    } else {
+    } else if ((mode != VAR || var->mode() != VAR) &&
+               (!declaration_scope->is_global_scope() ||
+                (mode != VAR && mode != CONST) ||
+                (var->mode() != VAR && var->mode() != CONST))) {
       // The name was declared in this scope before; check for conflicting
       // re-declarations. We have a conflict if either of the declarations is
-      // not a var. There is similar code in runtime.cc in the Declare
+      // not a var (in the global scope, we also have to ignore legacy const for
+      // compatibility). There is similar code in runtime.cc in the Declare
       // functions. The function CheckNonConflictingScope checks for conflicting
       // var and let bindings from different scopes whereas this is a check for
       // conflicting declarations within the same scope. This check also covers
+      // the special case
       //
       // function () { let x; { var x; } }
       //
       // because the var declaration is hoisted to the function scope where 'x'
       // is already bound.
-      if ((mode != VAR) || (var->mode() != VAR)) {
-        // We only have vars, consts and lets in declarations.
-        ASSERT(var->mode() == VAR ||
-               var->mode() == CONST ||
-               var->mode() == CONST_HARMONY ||
-               var->mode() == LET);
-        if (is_extended_mode()) {
-          // In harmony mode we treat re-declarations as early errors. See
-          // ES5 16 for a definition of early errors.
-          SmartArrayPointer<char> c_string = name->ToCString(DISALLOW_NULLS);
-          const char* elms[2] = { "Variable", *c_string };
-          Vector<const char*> args(elms, 2);
-          ReportMessage("redeclaration", args);
-          *ok = false;
-          return;
-        }
-        const char* type = (var->mode() == VAR)
-            ? "var" : var->is_const_mode() ? "const" : "let";
-        Handle<String> type_string =
-            isolate()->factory()->NewStringFromUtf8(CStrVector(type), TENURED);
-        Expression* expression =
-            NewThrowTypeError(isolate()->factory()->redeclaration_symbol(),
-                              type_string, name);
-        declaration_scope->SetIllegalRedeclaration(expression);
+      // We only have vars, consts and lets in declarations.
+      ASSERT(var->mode() == VAR ||
+             var->mode() == CONST ||
+             var->mode() == CONST_HARMONY ||
+             var->mode() == LET);
+      if (is_extended_mode()) {
+        // In harmony mode we treat re-declarations as early errors. See
+        // ES5 16 for a definition of early errors.
+        SmartArrayPointer<char> c_string = name->ToCString(DISALLOW_NULLS);
+        const char* elms[2] = { "Variable", *c_string };
+        Vector<const char*> args(elms, 2);
+        ReportMessage("redeclaration", args);
+        *ok = false;
+        return;
       }
+      const char* type =
+          (var->mode() == VAR) ? "var" : var->is_const_mode() ? "const" : "let";
+      Handle<String> type_string =
+          isolate()->factory()->NewStringFromUtf8(CStrVector(type), TENURED);
+      Expression* expression =
+          NewThrowTypeError(isolate()->factory()->redeclaration_symbol(),
+                            type_string, name);
+      declaration_scope->SetIllegalRedeclaration(expression);
     }
   }
 
@@ -1852,8 +1861,7 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
   // Runtime::DeclareContextSlot() calls.
   declaration_scope->AddDeclaration(declaration);
 
-  if ((mode == CONST || mode == CONST_HARMONY) &&
-      declaration_scope->is_global_scope()) {
+  if (mode == CONST && declaration_scope->is_global_scope()) {
     // For global const variables we bind the proxy to a variable.
     ASSERT(resolve);  // should be set by all callers
     Variable::Kind kind = Variable::NORMAL;
@@ -2004,9 +2012,12 @@ Statement* Parser::ParseFunctionDeclaration(ZoneStringList* names, bool* ok) {
                                               FunctionLiteral::DECLARATION,
                                               CHECK_OK);
   // Even if we're not at the top-level of the global or a function
-  // scope, we treat is as such and introduce the function with it's
+  // scope, we treat it as such and introduce the function with its
   // initial value upon entering the corresponding scope.
-  VariableMode mode = is_extended_mode() ? LET : VAR;
+  // In extended mode, a function behaves as a lexical binding, except in the
+  // global scope.
+  VariableMode mode =
+      is_extended_mode() && !top_scope_->is_global_scope() ? LET : VAR;
   VariableProxy* proxy = NewUnresolved(name, mode, Interface::NewValue());
   Declaration* declaration =
       factory()->NewFunctionDeclaration(proxy, mode, fun, top_scope_);
@@ -2329,7 +2340,8 @@ Block* Parser::ParseVariableDeclarations(
     // declaration statement has been executed. This is important in
     // browsers where the global object (window) has lots of
     // properties defined in prototype objects.
-    if (initialization_scope->is_global_scope()) {
+    if (initialization_scope->is_global_scope() &&
+        mode != LET && mode != CONST_HARMONY) {
       // Compute the arguments for the runtime call.
       ZoneList<Expression*>* arguments =
           new(zone()) ZoneList<Expression*>(3, zone());
index d5f7527aed3035693221beb156d2fc5867108cd8..6541546cb6023f8a72e918b83952dacd46b5e8c0 100644 (file)
@@ -230,8 +230,8 @@ EXPRESSION_NODE_LIST(DEF_VISIT)
 #undef DEF_VISIT
 
 
-// Assumes code has been parsed and scopes have been analyzed.  Mutates the
-// AST, so the AST should not continue to be used in the case of failure.
+// Assumes code has been parsed.  Mutates the AST, so the AST should not
+// continue to be used in the case of failure.
 bool Rewriter::Rewrite(CompilationInfo* info) {
   FunctionLiteral* function = info->function();
   ASSERT(function != NULL);
index 466ee324948f9ad61e18f5718f2c8c3a6d58c0e0..9718ada58ed6b06580c3e150071738ed22f7fc90 100644 (file)
@@ -8378,7 +8378,10 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NewGlobalContext) {
       isolate->heap()->AllocateGlobalContext(function, scope_info);
   if (!maybe_result->To(&result)) return maybe_result;
 
+  ASSERT(function->context() == isolate->context());
+  ASSERT(function->context()->global_object() == result->global_object());
   isolate->set_context(result);
+  result->global_object()->set_global_context(result);
 
   return result;  // non-failure
 }
@@ -10901,7 +10904,7 @@ class ScopeIterator {
         } else {
           ASSERT(scope_info->Type() == EVAL_SCOPE);
           info.MarkAsEval();
-          info.SetCallingContext(Handle<Context>(function_->context()));
+          info.SetContext(Handle<Context>(function_->context()));
         }
         if (ParserApi::Parse(&info, kNoParsingFlags) && Scope::Analyze(&info)) {
           scope = info.function()->scope();
index e577d78743443133d9ce19f6d12715b12d11d625..be9c9b6857397d633c50fb0eb5684c56108a73cd 100644 (file)
@@ -118,10 +118,8 @@ Scope::Scope(Scope* outer_scope, ScopeType type, Zone* zone)
       already_resolved_(false),
       zone_(zone) {
   SetDefaults(type, outer_scope, Handle<ScopeInfo>::null());
-  // At some point we might want to provide outer scopes to
-  // eval scopes (by walking the stack and reading the scope info).
-  // In that case, the ASSERT below needs to be adjusted.
-  ASSERT_EQ(type == GLOBAL_SCOPE, outer_scope == NULL);
+  // The outermost scope must be a global scope.
+  ASSERT(type == GLOBAL_SCOPE || outer_scope != NULL);
   ASSERT(!HasIllegalRedeclaration());
 }
 
@@ -1041,7 +1039,7 @@ bool Scope::ResolveVariable(CompilationInfo* info,
       // gave up on it (e.g. by encountering a local with the same in the outer
       // scope which was not promoted to a context, this can happen if we use
       // debugger to evaluate arbitrary expressions at a break point).
-      if (var->is_global()) {
+      if (var->IsGlobalObjectProperty()) {
         var = NonLocal(proxy->name(), DYNAMIC_GLOBAL);
       } else if (var->is_dynamic()) {
         var = NonLocal(proxy->name(), DYNAMIC);
@@ -1162,11 +1160,12 @@ bool Scope::MustAllocate(Variable* var) {
        scope_contains_with_ ||
        is_catch_scope() ||
        is_block_scope() ||
-       is_module_scope())) {
+       is_module_scope() ||
+       is_global_scope())) {
     var->set_is_used(true);
   }
   // Global variables do not need to be allocated.
-  return !var->is_global() && var->is_used();
+  return !var->IsGlobalObjectProperty() && var->is_used();
 }
 
 
@@ -1180,11 +1179,12 @@ bool Scope::MustAllocateInContext(Variable* var) {
   // catch-bound variables are always allocated in a context.
   if (var->mode() == TEMPORARY) return false;
   if (is_catch_scope() || is_block_scope() || is_module_scope()) return true;
+  if (is_global_scope() && (var->mode() == LET || var->mode() == CONST_HARMONY))
+    return true;
   return var->has_forced_context_allocation() ||
       scope_calls_eval_ ||
       inner_scope_calls_eval_ ||
-      scope_contains_with_ ||
-      var->is_global();
+      scope_contains_with_;
 }
 
 
index 32ad5bc5dd4d09329ed1bd702106d3e0ee6dbcf9..64311d925d92c16bd04a6a2316a69b1db0e7d814 100644 (file)
@@ -81,10 +81,11 @@ Variable::Variable(Scope* scope,
 }
 
 
-bool Variable::is_global() const {
+bool Variable::IsGlobalObjectProperty() const {
   // Temporaries are never global, they must always be allocated in the
   // activation frame.
-  return mode_ != TEMPORARY && scope_ != NULL && scope_->is_global_scope();
+  return mode_ != TEMPORARY && mode_ != LET && mode_ != CONST_HARMONY
+      && scope_ != NULL && scope_->is_global_scope();
 }
 
 
index e5aee2d3e615189d54c126ddac7902a9d4320ecd..c49bab95da2a7aa9cfab87432bd4089ce2b3349f 100644 (file)
@@ -118,6 +118,7 @@ class Variable: public ZoneObject {
   bool IsStackAllocated() const { return IsParameter() || IsStackLocal(); }
   bool IsContextSlot() const { return location_ == CONTEXT; }
   bool IsLookupSlot() const { return location_ == LOOKUP; }
+  bool IsGlobalObjectProperty() const;
 
   bool is_dynamic() const {
     return (mode_ == DYNAMIC ||
@@ -132,7 +133,6 @@ class Variable: public ZoneObject {
     return initialization_flag_ == kNeedsInitialization;
   }
 
-  bool is_global() const;
   bool is_this() const { return kind_ == THIS; }
   bool is_arguments() const { return kind_ == ARGUMENTS; }
 
index 63048680fb0daf23ff8d53a05cbabd21bb8f3e9d..6fc601213c67bc32e268a10493e442b102b373e8 100644 (file)
@@ -785,13 +785,25 @@ TEST(MultiScriptConflicts) {
                   EXPECT_RESULT, Number::New(1));
   }
 
+  { SimpleContext context;
+    context.Check("function x() { return 4 }; x()",
+                  EXPECT_RESULT, Number::New(4));
+    context.Check("x()",
+                  EXPECT_RESULT, Number::New(4));
+    context.Check("this.x()",
+                  EXPECT_RESULT, Number::New(4));
+  }
+
   { SimpleContext context;
     context.Check("let x = 2; x",
                   EXPECT_RESULT, Number::New(2));
     context.Check("x",
                   EXPECT_RESULT, Number::New(2));
-    context.Check("this.x",
-                  EXPECT_RESULT, Number::New(2));
+    // 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.
+    // context.Check("this.x",
+    //               EXPECT_RESULT, Number::New(2));
   }
 
   { SimpleContext context;
@@ -799,17 +811,11 @@ TEST(MultiScriptConflicts) {
                   EXPECT_RESULT, Number::New(3));
     context.Check("x",
                   EXPECT_RESULT, Number::New(3));
-    context.Check("this.x",
-                  EXPECT_RESULT, Number::New(3));
-  }
-
-  { SimpleContext context;
-    context.Check("function x() { return 4 }; x()",
-                  EXPECT_RESULT, Number::New(4));
-    context.Check("x()",
-                  EXPECT_RESULT, Number::New(4));
-    context.Check("this.x()",
-                  EXPECT_RESULT, Number::New(4));
+    // 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.
+    // context.Check("this.x",
+    //              EXPECT_RESULT, Number::New(3));
   }
 
   // TODO(rossberg): All of the below should actually be errors in Harmony.
@@ -846,55 +852,55 @@ TEST(MultiScriptConflicts) {
     context.Check("let x = 1; x",
                   EXPECT_RESULT, Number::New(1));
     context.Check("var x = 2; x",
-                  EXPECT_RESULT, Number::New(2));
+                  EXPECT_ERROR);
   }
 
   { SimpleContext context;
     context.Check("let x = 1; x",
                   EXPECT_RESULT, Number::New(1));
     context.Check("let x = 2; x",
-                  EXPECT_RESULT, Number::New(2));
+                  EXPECT_ERROR);
   }
 
   { SimpleContext context;
     context.Check("let x = 1; x",
                   EXPECT_RESULT, Number::New(1));
     context.Check("const x = 2; x",
-                  EXPECT_RESULT, Number::New(2));
+                  EXPECT_ERROR);
   }
 
   { SimpleContext context;
     context.Check("let x = 1; x",
                   EXPECT_RESULT, Number::New(1));
     context.Check("function x() { return 2 }; x()",
-                  EXPECT_RESULT, Number::New(2));
+                  EXPECT_ERROR);
   }
 
   { SimpleContext context;
     context.Check("const x = 1; x",
                   EXPECT_RESULT, Number::New(1));
     context.Check("var x = 2; x",
-                  EXPECT_EXCEPTION);
+                  EXPECT_ERROR);
   }
 
   { SimpleContext context;
     context.Check("const x = 1; x",
                   EXPECT_RESULT, Number::New(1));
     context.Check("let x = 2; x",
-                  EXPECT_EXCEPTION);
+                  EXPECT_ERROR);
   }
 
   { SimpleContext context;
     context.Check("const x = 1; x",
                   EXPECT_RESULT, Number::New(1));
     context.Check("const x = 2; x",
-                  EXPECT_RESULT, Number::New(1));
+                  EXPECT_ERROR);
   }
 
   { SimpleContext context;
     context.Check("const x = 1; x",
                   EXPECT_RESULT, Number::New(1));
     context.Check("function x() { return 2 }; x()",
-                  EXPECT_EXCEPTION);
+                  EXPECT_ERROR);
   }
 }
index 8388504bcd6d073a5c027010e96f19228686c115..3aa9d2222303d1fccdbdb9387acb33b517f5a3de 100644 (file)
@@ -35,7 +35,8 @@
 function CheckException(e) {
   var string = e.toString();
   assertTrue(string.indexOf("has already been declared") >= 0 ||
-             string.indexOf("redeclaration") >= 0);  return 'Conflict';
+             string.indexOf("redeclaration") >= 0);
+  return 'Conflict';
 }