[es6] Parameter scopes for sloppy eval
authorrossberg <rossberg@chromium.org>
Fri, 21 Aug 2015 10:58:35 +0000 (03:58 -0700)
committerCommit bot <commit-bot@chromium.org>
Fri, 21 Aug 2015 10:58:47 +0000 (10:58 +0000)
This CL is a nightmare! For the utterly irrelevant edge case of a sloppy function with non-simple parameters and a call to direct eval, like here,

  let x = 1;
  function f(g = () => x) {
    var y
    eval("var x = 2")
    return g() + x  // f() = 3
  }

we have to do all of the following, on top of the declaration block ("varblock") contexts we already introduce around the body:

- Introduce the ability for varblock contexts to have both a ScopeInfo and an extension object (e.g., the body varblock in the example will contain both a static var y and a dynamic var x). No other scope needs that. Since there are no context slots left, a special new struct is introduced that pairs up scope info and extension object.

- When declaring lookup slots in the runtime, this new struct is allocated in the case where an extension object has to be added to a block scope (at which point the block's extension slot still contains a plain ScopeInfo).

- While at it, introduce some abstraction to access context extension slots in a more controlled manner, in order to keep special-casing to a minimum.

- Make sure that even empty varblock contexts do not get optimised away when they contain a sloppy eval, so that they can host the potential extension object.

- Extend dynamic search for declaration contexts (used by sloppy direct eval) to recognize varblock contexts.

- In the parser, if a function has a sloppy direct eval, introduce an additional varblock scope around each non-simple (desugared) parameter, as required by the spec to contain possible dynamic var bindings.

- In the pattern rewriter, add the ability to hoist the named variables the pattern declares to an outer scope. That is required because the actual destructuring has to be evaluated inside the protecting varblock scope, but the bindings that the desugaring introduces are in the outer scope.

- ScopeInfos need to save the information whether a block is a varblock, to make sloppy eval calls work correctly that deserialise them as part of the scope chain.

- Add the ability to materialize block scopes with extension objects in the debugger. Likewise, enable setting extension variables in block scopes via the debugger interface.

- While at it, refactor and unify some respective code in the debugger.

Sorry, this CL is large. I could try to split it up, but everything is rather entangled.

@mstarzinger: Please review the changes to contexts.
@yangguo: Please have a look at the debugger stuff.

R=littledan@chromium.org, mstarzinger@chromium.org, yangguo@chromium.org
BUG=v8:811,v8:2160
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#30295}

25 files changed:
include/v8.h
src/bootstrapper.cc
src/compiler/ast-graph-builder.cc
src/contexts.cc
src/contexts.h
src/debug/debug-scopes.cc
src/debug/debug-scopes.h
src/factory.cc
src/factory.h
src/full-codegen/full-codegen.cc
src/hydrogen.cc
src/objects-debug.cc
src/objects-inl.h
src/objects-printer.cc
src/objects.cc
src/objects.h
src/parser.cc
src/parser.h
src/pattern-rewriter.cc
src/runtime/runtime-object.cc
src/runtime/runtime-scopes.cc
src/scopeinfo.cc
src/scopes.cc
src/scopes.h
test/mjsunit/harmony/default-parameters.js

index 9a577765b439ae90c50f0cd866603924c087528f..a25557f36ca4b1e233381a82cb36324a64e2a7c4 100644 (file)
@@ -6981,7 +6981,7 @@ class Internals {
   static const int kNodeIsIndependentShift = 3;
   static const int kNodeIsPartiallyDependentShift = 4;
 
-  static const int kJSObjectType = 0xb6;
+  static const int kJSObjectType = 0xb7;
   static const int kFirstNonstringType = 0x80;
   static const int kOddballType = 0x83;
   static const int kForeignType = 0x87;
index 30e10e63b57d9a4c02c6b271ce02fead54a8086e..4b8077e100c845a185d2f4c4e2ef8d52cffdb586 100644 (file)
@@ -843,7 +843,7 @@ void Genesis::HookUpGlobalThisBinding(Handle<FixedArray> outdated_contexts) {
   for (int i = 0; i < outdated_contexts->length(); ++i) {
     Context* context = Context::cast(outdated_contexts->get(i));
     if (context->IsScriptContext()) {
-      ScopeInfo* scope_info = ScopeInfo::cast(context->extension());
+      ScopeInfo* scope_info = context->scope_info();
       int slot = scope_info->ReceiverContextSlotIndex();
       if (slot >= 0) {
         DCHECK_EQ(slot, Context::MIN_CONTEXT_SLOTS);
index e7725799233cbb63136c4bc73473c440d7463f95..11dc456e3dba721637e06a9283906a6db530255c 100644 (file)
@@ -1139,7 +1139,7 @@ void AstGraphBuilder::VisitBlock(Block* stmt) {
     VisitStatements(stmt->statements());
   } else {
     // Visit declarations and statements in a block scope.
-    if (stmt->scope()->ContextLocalCount() > 0) {
+    if (stmt->scope()->NeedsContext()) {
       Node* context = BuildLocalBlockContext(stmt->scope());
       ContextScope scope(this, stmt->scope(), context);
       VisitDeclarations(stmt->scope()->declarations());
index ef850452ceefce3ae4551887d7def030b982145d..e3ace3c8c1e967a87d049578b6dc082e1bf8c6cf 100644 (file)
@@ -41,7 +41,7 @@ bool ScriptContextTable::Lookup(Handle<ScriptContextTable> table,
   for (int i = 0; i < table->used(); i++) {
     Handle<Context> context = GetContext(table, i);
     DCHECK(context->IsScriptContext());
-    Handle<ScopeInfo> scope_info(ScopeInfo::cast(context->extension()));
+    Handle<ScopeInfo> scope_info(context->scope_info());
     int slot_index = ScopeInfo::ContextSlotIndex(
         scope_info, name, &result->mode, &result->location, &result->init_flag,
         &result->maybe_assigned_flag);
@@ -56,10 +56,22 @@ bool ScriptContextTable::Lookup(Handle<ScriptContextTable> table,
 }
 
 
+bool Context::is_declaration_context() {
+  if (IsFunctionContext() || IsNativeContext() || IsScriptContext()) {
+    return true;
+  }
+  if (!IsBlockContext()) return false;
+  Object* ext = extension();
+  // If we have the special extension, we immediately know it must be a
+  // declaration scope. That's just a small performance shortcut.
+  return ext->IsSloppyBlockWithEvalContextExtension()
+      || ScopeInfo::cast(ext)->is_declaration_scope();
+}
+
+
 Context* Context::declaration_context() {
   Context* current = this;
-  while (!current->IsFunctionContext() && !current->IsNativeContext() &&
-         !current->IsScriptContext()) {
+  while (!current->is_declaration_context()) {
     current = current->previous();
     DCHECK(current->closure() == closure());
   }
@@ -67,6 +79,44 @@ Context* Context::declaration_context() {
 }
 
 
+JSObject* Context::extension_object() {
+  DCHECK(IsNativeContext() || IsFunctionContext() || IsBlockContext());
+  Object* object = extension();
+  if (object == nullptr) return nullptr;
+  if (IsBlockContext()) {
+    if (!object->IsSloppyBlockWithEvalContextExtension()) return nullptr;
+    object = SloppyBlockWithEvalContextExtension::cast(object)->extension();
+  }
+  DCHECK(object->IsJSContextExtensionObject() ||
+         (IsNativeContext() && object->IsJSGlobalObject()));
+  return JSObject::cast(object);
+}
+
+
+JSReceiver* Context::extension_receiver() {
+  DCHECK(IsNativeContext() || IsWithContext() ||
+         IsFunctionContext() || IsBlockContext());
+  return IsWithContext() ? JSReceiver::cast(extension()) : extension_object();
+}
+
+
+ScopeInfo* Context::scope_info() {
+  DCHECK(IsModuleContext() || IsScriptContext() || IsBlockContext());
+  Object* object = extension();
+  if (object->IsSloppyBlockWithEvalContextExtension()) {
+    DCHECK(IsBlockContext());
+    object = SloppyBlockWithEvalContextExtension::cast(object)->scope_info();
+  }
+  return ScopeInfo::cast(object);
+}
+
+
+String* Context::catch_name() {
+  DCHECK(IsCatchContext());
+  return String::cast(extension());
+}
+
+
 JSBuiltinsObject* Context::builtins() {
   GlobalObject* object = global_object();
   if (object->IsJSGlobalObject()) {
@@ -212,13 +262,11 @@ Handle<Object> Context::Lookup(Handle<String> name,
       PrintF("\n");
     }
 
-
     // 1. Check global objects, subjects of with, and extension objects.
-    if (context->IsNativeContext() ||
-        context->IsWithContext() ||
-        (context->IsFunctionContext() && context->has_extension())) {
-      Handle<JSReceiver> object(
-          JSReceiver::cast(context->extension()), isolate);
+    if ((context->IsNativeContext() || context->IsWithContext() ||
+         context->IsFunctionContext() || context->IsBlockContext()) &&
+        context->extension_receiver() != nullptr) {
+      Handle<JSReceiver> object(context->extension_receiver());
 
       if (context->IsNativeContext()) {
         if (FLAG_trace_contexts) {
@@ -280,14 +328,9 @@ Handle<Object> Context::Lookup(Handle<String> name,
         context->IsScriptContext()) {
       // Use serialized scope information of functions and blocks to search
       // for the context index.
-      Handle<ScopeInfo> scope_info;
-      if (context->IsFunctionContext()) {
-        scope_info = Handle<ScopeInfo>(
-            context->closure()->shared()->scope_info(), isolate);
-      } else {
-        scope_info = Handle<ScopeInfo>(
-            ScopeInfo::cast(context->extension()), isolate);
-      }
+      Handle<ScopeInfo> scope_info(context->IsFunctionContext()
+          ? context->closure()->shared()->scope_info()
+          : context->scope_info());
       VariableMode mode;
       VariableLocation location;
       InitializationFlag init_flag;
@@ -329,7 +372,7 @@ Handle<Object> Context::Lookup(Handle<String> name,
 
     } else if (context->IsCatchContext()) {
       // Catch contexts have the variable name in the extension slot.
-      if (String::Equals(name, handle(String::cast(context->extension())))) {
+      if (String::Equals(name, handle(context->catch_name()))) {
         if (FLAG_trace_contexts) {
           PrintF("=> found in catch context\n");
         }
@@ -359,7 +402,7 @@ void Context::InitializeGlobalSlots() {
   DCHECK(IsScriptContext());
   DisallowHeapAllocation no_gc;
 
-  ScopeInfo* scope_info = ScopeInfo::cast(extension());
+  ScopeInfo* scope_info = this->scope_info();
 
   int context_globals = scope_info->ContextGlobalCount();
   if (context_globals > 0) {
index edc5091a93bf7fa34af6507dfe28d4ffbfbb5276..78c2048f2b29dd3084966c2c2545e610b2fc3c91 100644 (file)
@@ -299,8 +299,11 @@ class ScriptContextTable : public FixedArray {
 //                Dynamically declared variables/functions are also added
 //                to lazily allocated extension object. Context::Lookup
 //                searches the extension object for properties.
-//                For global and block contexts, contains the respective
-//                ScopeInfo.
+//                For script and block contexts, contains the respective
+//                ScopeInfo. For block contexts representing sloppy declaration
+//                block scopes, it may also be a struct being a
+//                SloppyBlockWithEvalContextExtension, pairing the ScopeInfo
+//                with an extension object.
 //                For module contexts, points back to the respective JSModule.
 //
 // [ global_object ]  A pointer to the global object. Provided for quick
@@ -370,9 +373,13 @@ class Context: public FixedArray {
   }
   void set_previous(Context* context) { set(PREVIOUS_INDEX, context); }
 
-  bool has_extension() { return extension() != NULL; }
+  bool has_extension() { return extension() != nullptr; }
   Object* extension() { return get(EXTENSION_INDEX); }
   void set_extension(Object* object) { set(EXTENSION_INDEX, object); }
+  JSObject* extension_object();
+  JSReceiver* extension_receiver();
+  ScopeInfo* scope_info();
+  String* catch_name();
 
   JSModule* module() { return JSModule::cast(get(EXTENSION_INDEX)); }
   void set_module(JSModule* module) { set(EXTENSION_INDEX, module); }
@@ -380,6 +387,7 @@ class Context: public FixedArray {
   // Get the context where var declarations will be hoisted to, which
   // may be the context itself.
   Context* declaration_context();
+  bool is_declaration_context();
 
   GlobalObject* global_object() {
     Object* result = get(GLOBAL_OBJECT_INDEX);
index 4703e9c3c450ad047f4c1b4e73de6fa53270b69b..8502c483bd86613388076cfc7bac56361bda6dee 100644 (file)
@@ -237,7 +237,8 @@ MaybeHandle<JSObject> ScopeIterator::ScopeObject() {
       return MaterializeLocalScope();
     case ScopeIterator::ScopeTypeWith:
       // Return the with object.
-      return Handle<JSObject>(JSObject::cast(CurrentContext()->extension()));
+      // TODO(neis): This breaks for proxies.
+      return handle(JSObject::cast(CurrentContext()->extension_receiver()));
     case ScopeIterator::ScopeTypeCatch:
       return MaterializeCatchScope();
     case ScopeIterator::ScopeTypeClosure:
@@ -295,7 +296,7 @@ Handle<ScopeInfo> ScopeIterator::CurrentScopeInfo() {
   if (!nested_scope_chain_.is_empty()) {
     return nested_scope_chain_.last();
   } else if (context_->IsBlockContext()) {
-    return Handle<ScopeInfo>(ScopeInfo::cast(context_->extension()));
+    return Handle<ScopeInfo>(context_->scope_info());
   } else if (context_->IsFunctionContext()) {
     return Handle<ScopeInfo>(context_->closure()->shared()->scope_info());
   }
@@ -410,7 +411,7 @@ MaybeHandle<JSObject> ScopeIterator::MaterializeScriptScope() {
        context_index++) {
     Handle<Context> context =
         ScriptContextTable::GetContext(script_contexts, context_index);
-    Handle<ScopeInfo> scope_info(ScopeInfo::cast(context->extension()));
+    Handle<ScopeInfo> scope_info(context->scope_info());
     CopyContextLocalsToScopeObject(scope_info, context, script_scope);
   }
   return script_scope;
@@ -438,28 +439,13 @@ MaybeHandle<JSObject> ScopeIterator::MaterializeLocalScope() {
 
   // Finally copy any properties from the function context extension.
   // These will be variables introduced by eval.
-  if (function_context->closure() == *function) {
-    if (function_context->has_extension() &&
-        !function_context->IsNativeContext()) {
-      Handle<JSObject> ext(JSObject::cast(function_context->extension()));
-      Handle<FixedArray> keys;
-      ASSIGN_RETURN_ON_EXCEPTION(
-          isolate_, keys, JSReceiver::GetKeys(ext, JSReceiver::INCLUDE_PROTOS),
-          JSObject);
-
-      for (int i = 0; i < keys->length(); i++) {
-        // Names of variables introduced by eval are strings.
-        DCHECK(keys->get(i)->IsString());
-        Handle<String> key(String::cast(keys->get(i)));
-        Handle<Object> value;
-        ASSIGN_RETURN_ON_EXCEPTION(
-            isolate_, value, Object::GetPropertyOrElement(ext, key), JSObject);
-        RETURN_ON_EXCEPTION(isolate_,
-                            Runtime::SetObjectProperty(isolate_, local_scope,
-                                                       key, value, SLOPPY),
-                            JSObject);
-      }
-    }
+  if (function_context->closure() == *function &&
+      function_context->has_extension() &&
+      !function_context->IsNativeContext()) {
+    bool success = CopyContextExtensionToScopeObject(
+        handle(function_context->extension_object(), isolate_),
+        local_scope, JSReceiver::INCLUDE_PROTOS);
+    if (!success) return MaybeHandle<JSObject>();
   }
 
   return local_scope;
@@ -486,20 +472,11 @@ Handle<JSObject> ScopeIterator::MaterializeClosure() {
   // Finally copy any properties from the function context extension. This will
   // be variables introduced by eval.
   if (context->has_extension()) {
-    Handle<JSObject> ext(JSObject::cast(context->extension()));
-    DCHECK(ext->IsJSContextExtensionObject());
-    Handle<FixedArray> keys =
-        JSReceiver::GetKeys(ext, JSReceiver::OWN_ONLY).ToHandleChecked();
-
-    for (int i = 0; i < keys->length(); i++) {
-      HandleScope scope(isolate_);
-      // Names of variables introduced by eval are strings.
-      DCHECK(keys->get(i)->IsString());
-      Handle<String> key(String::cast(keys->get(i)));
-      Handle<Object> value = Object::GetProperty(ext, key).ToHandleChecked();
-      JSObject::SetOwnPropertyIgnoreAttributes(closure_scope, key, value, NONE)
-          .Check();
-    }
+    bool success = CopyContextExtensionToScopeObject(
+        handle(context->extension_object(), isolate_), closure_scope,
+        JSReceiver::OWN_ONLY);
+    DCHECK(success);
+    USE(success);
   }
 
   return closure_scope;
@@ -511,7 +488,7 @@ Handle<JSObject> ScopeIterator::MaterializeClosure() {
 Handle<JSObject> ScopeIterator::MaterializeCatchScope() {
   Handle<Context> context = CurrentContext();
   DCHECK(context->IsCatchContext());
-  Handle<String> name(String::cast(context->extension()));
+  Handle<String> name(context->catch_name());
   Handle<Object> thrown_object(context->get(Context::THROWN_OBJECT_INDEX),
                                isolate_);
   Handle<JSObject> catch_scope =
@@ -539,11 +516,17 @@ Handle<JSObject> ScopeIterator::MaterializeBlockScope() {
   }
 
   if (!context.is_null()) {
-    Handle<ScopeInfo> scope_info_from_context(
-        ScopeInfo::cast(context->extension()));
     // Fill all context locals.
-    CopyContextLocalsToScopeObject(scope_info_from_context, context,
-                                   block_scope);
+    CopyContextLocalsToScopeObject(handle(context->scope_info()),
+                                   context, block_scope);
+    // Fill all extension variables.
+    if (context->extension_object() != nullptr) {
+      bool success = CopyContextExtensionToScopeObject(
+          handle(context->extension_object()), block_scope,
+          JSReceiver::OWN_ONLY);
+      DCHECK(success);
+      USE(success);
+    }
   }
   return block_scope;
 }
@@ -554,7 +537,7 @@ Handle<JSObject> ScopeIterator::MaterializeBlockScope() {
 MaybeHandle<JSObject> ScopeIterator::MaterializeModuleScope() {
   Handle<Context> context = CurrentContext();
   DCHECK(context->IsModuleContext());
-  Handle<ScopeInfo> scope_info(ScopeInfo::cast(context->extension()));
+  Handle<ScopeInfo> scope_info(context->scope_info());
 
   // Allocate and initialize a JSObject with all the members of the debugged
   // module.
@@ -636,7 +619,7 @@ bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name,
     if (function_context->closure() == *function) {
       if (function_context->has_extension() &&
           !function_context->IsNativeContext()) {
-        Handle<JSObject> ext(JSObject::cast(function_context->extension()));
+        Handle<JSObject> ext(function_context->extension_object());
 
         Maybe<bool> maybe = JSReceiver::HasProperty(ext, variable_name);
         DCHECK(maybe.IsJust());
@@ -670,9 +653,25 @@ bool ScopeIterator::SetBlockVariableValue(Handle<String> variable_name,
   }
 
   if (HasContext()) {
-    return SetContextLocalValue(scope_info, CurrentContext(), variable_name,
-                                new_value);
+    Handle<Context> context = CurrentContext();
+    if (SetContextLocalValue(scope_info, context, variable_name, new_value)) {
+      return true;
+    }
+
+    Handle<JSObject> ext(context->extension_object(), isolate_);
+    if (!ext.is_null()) {
+      Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name);
+      DCHECK(maybe.IsJust());
+      if (maybe.FromJust()) {
+        // We don't expect this to do anything except replacing property value.
+        JSObject::SetOwnPropertyIgnoreAttributes(ext, variable_name, new_value,
+                                                 NONE)
+            .Check();
+        return true;
+      }
+    }
   }
+
   return false;
 }
 
@@ -693,8 +692,7 @@ bool ScopeIterator::SetClosureVariableValue(Handle<String> variable_name,
   // Properties from the function context extension. This will
   // be variables introduced by eval.
   if (context->has_extension()) {
-    Handle<JSObject> ext(JSObject::cast(context->extension()));
-    DCHECK(ext->IsJSContextExtensionObject());
+    Handle<JSObject> ext(JSObject::cast(context->extension_object()));
     Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name);
     DCHECK(maybe.IsJust());
     if (maybe.FromJust()) {
@@ -732,7 +730,7 @@ bool ScopeIterator::SetCatchVariableValue(Handle<String> variable_name,
                                           Handle<Object> new_value) {
   Handle<Context> context = CurrentContext();
   DCHECK(context->IsCatchContext());
-  Handle<String> name(String::cast(context->extension()));
+  Handle<String> name(context->catch_name());
   if (!String::Equals(name, variable_name)) {
     return false;
   }
@@ -765,5 +763,27 @@ void ScopeIterator::CopyContextLocalsToScopeObject(
   }
 }
 
+
+bool ScopeIterator::CopyContextExtensionToScopeObject(
+    Handle<JSObject> extension, Handle<JSObject> scope_object,
+    JSReceiver::KeyCollectionType type) {
+  Handle<FixedArray> keys;
+  ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+      isolate_, keys, JSReceiver::GetKeys(extension, type), false);
+
+  for (int i = 0; i < keys->length(); i++) {
+    // Names of variables introduced by eval are strings.
+    DCHECK(keys->get(i)->IsString());
+    Handle<String> key(String::cast(keys->get(i)));
+    Handle<Object> value;
+    ASSIGN_RETURN_ON_EXCEPTION_VALUE(
+        isolate_, value, Object::GetPropertyOrElement(extension, key), false);
+    RETURN_ON_EXCEPTION_VALUE(
+        isolate_, JSObject::SetOwnPropertyIgnoreAttributes(
+            scope_object, key, value, NONE), false);
+  }
+  return true;
+}
+
 }  // namespace internal
 }  // namespace v8
index 0247cc4bcecfd77bd60e85e1a7920d10f855639f..20cd0336dc9907e8b741d0d3e1b902475795606b 100644 (file)
@@ -116,6 +116,9 @@ class ScopeIterator {
   void CopyContextLocalsToScopeObject(Handle<ScopeInfo> scope_info,
                                       Handle<Context> context,
                                       Handle<JSObject> scope_object);
+  bool CopyContextExtensionToScopeObject(Handle<JSObject> extension,
+                                         Handle<JSObject> scope_object,
+                                         JSReceiver::KeyCollectionType type);
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
 };
index a8bd0bf8e8667291e3a1da1a6c28402d86e7873c..34dfb2925ec47b9d99095d0ebf1e4dc3719b3edf 100644 (file)
@@ -62,6 +62,19 @@ Handle<PrototypeInfo> Factory::NewPrototypeInfo() {
 }
 
 
+Handle<SloppyBlockWithEvalContextExtension>
+Factory::NewSloppyBlockWithEvalContextExtension(
+    Handle<ScopeInfo> scope_info, Handle<JSObject> extension) {
+  DCHECK(scope_info->is_declaration_scope());
+  Handle<SloppyBlockWithEvalContextExtension> result =
+      Handle<SloppyBlockWithEvalContextExtension>::cast(
+          NewStruct(SLOPPY_BLOCK_WITH_EVAL_CONTEXT_EXTENSION_TYPE));
+  result->set_scope_info(*scope_info);
+  result->set_extension(*extension);
+  return result;
+}
+
+
 Handle<Oddball> Factory::NewOddball(Handle<Map> map, const char* to_string,
                                     Handle<Object> to_number,
                                     const char* type_of, byte kind) {
index 0828e81db5177e02b909eaf3c40e1e2cde36acfe..aa49b99b54a2d743afc822433319ef1eab3aa873 100644 (file)
@@ -54,6 +54,11 @@ class Factory final {
   // Create a new PrototypeInfo struct.
   Handle<PrototypeInfo> NewPrototypeInfo();
 
+  // Create a new SloppyBlockWithEvalContextExtension struct.
+  Handle<SloppyBlockWithEvalContextExtension>
+  NewSloppyBlockWithEvalContextExtension(Handle<ScopeInfo> scope_info,
+                                         Handle<JSObject> extension);
+
   // Create a pre-tenured empty AccessorPair.
   Handle<AccessorPair> NewAccessorPair();
 
index bb7b637d4224e0764e1f72d803d46f318b9dc472..51fa09e56b56d89e787060c8868569f70cf2fd58 100644 (file)
@@ -1548,7 +1548,7 @@ FullCodeGenerator::EnterBlockScopeIfNeeded::EnterBlockScopeIfNeeded(
     codegen_->PrepareForBailoutForId(entry_id, NO_REGISTERS);
     needs_block_context_ = false;
   } else {
-    needs_block_context_ = scope->ContextLocalCount() > 0;
+    needs_block_context_ = scope->NeedsContext();
     codegen_->scope_ = scope;
     {
       if (needs_block_context_) {
index 8c59ce86c9fe11f4c8ed1cfb76a8ed69cfcd3f2f..a51cfc0fe849f41ec224bec01539a5c01185f5f6 100644 (file)
@@ -4688,7 +4688,7 @@ void HOptimizedGraphBuilder::VisitBlock(Block* stmt) {
 
   { BreakAndContinueScope push(&break_info, this);
     if (scope != NULL) {
-      if (scope->ContextLocalCount() > 0) {
+      if (scope->NeedsContext()) {
         // Load the function object.
         Scope* declaration_scope = scope->DeclarationScope();
         HInstruction* function;
index 543a528594e05eb27b0dae45cd14eaf47007a087..93da2ed3874960606958888ae0255eaf00745a4a 100644 (file)
@@ -885,6 +885,14 @@ void AccessorInfo::AccessorInfoVerify() {
 }
 
 
+void SloppyBlockWithEvalContextExtension::
+    SloppyBlockWithEvalContextExtensionVerify() {
+  CHECK(IsSloppyBlockWithEvalContextExtension());
+  VerifyObjectField(kScopeInfoOffset);
+  VerifyObjectField(kExtensionOffset);
+}
+
+
 void ExecutableAccessorInfo::ExecutableAccessorInfoVerify() {
   CHECK(IsExecutableAccessorInfo());
   AccessorInfoVerify();
index 440ce8c7872c569ed4faaf250fdd9da9923eb202..f1309ece199a5d5d2556506cf96d7bddb03cb135 100644 (file)
@@ -5465,6 +5465,11 @@ SMI_ACCESSORS(PrototypeInfo, registry_slot, kRegistrySlotOffset)
 ACCESSORS(PrototypeInfo, validity_cell, Object, kValidityCellOffset)
 ACCESSORS(PrototypeInfo, constructor_name, Object, kConstructorNameOffset)
 
+ACCESSORS(SloppyBlockWithEvalContextExtension, scope_info, ScopeInfo,
+          kScopeInfoOffset)
+ACCESSORS(SloppyBlockWithEvalContextExtension, extension, JSObject,
+          kExtensionOffset)
+
 ACCESSORS(AccessorPair, getter, Object, kGetterOffset)
 ACCESSORS(AccessorPair, setter, Object, kSetterOffset)
 
index b3a0e47116a2050ebef6dabb762d2e850eb6bc17..de8aa444d45d7eeecf0c8ec843a2534abd9b9f56 100644 (file)
@@ -939,6 +939,15 @@ void PrototypeInfo::PrototypeInfoPrint(std::ostream& os) {  // NOLINT
 }
 
 
+void SloppyBlockWithEvalContextExtension::
+    SloppyBlockWithEvalContextExtensionPrint(std::ostream& os) {  // NOLINT
+  HeapObject::PrintHeader(os, "SloppyBlockWithEvalContextExtension");
+  os << "\n - scope_info: " << Brief(scope_info());
+  os << "\n - extension: " << Brief(extension());
+  os << "\n";
+}
+
+
 void AccessorPair::AccessorPairPrint(std::ostream& os) {  // NOLINT
   HeapObject::PrintHeader(os, "AccessorPair");
   os << "\n - getter: " << Brief(getter());
index 9618c9dccd723f9640af71e219bff7aba0323207..e5aa28b6dca158efc8dc5bc34faaa46bcbf9fa79 100644 (file)
@@ -5364,13 +5364,13 @@ bool JSObject::ReferencesObject(Object* obj) {
 
     // Check the context extension (if any) if it can have references.
     if (context->has_extension() && !context->IsCatchContext()) {
-      // With harmony scoping, a JSFunction may have a global context.
+      // With harmony scoping, a JSFunction may have a script context.
       // TODO(mvstanton): walk into the ScopeInfo.
       if (context->IsScriptContext()) {
         return false;
       }
 
-      return JSObject::cast(context->extension())->ReferencesObject(obj);
+      return context->extension_object()->ReferencesObject(obj);
     }
   }
 
index a04ae66bb61649a19b6499083108f8729acd387e..4fe42cdc81c6c0aa30317c96daf47d843bc54faa 100644 (file)
@@ -391,6 +391,7 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
   V(ALIASED_ARGUMENTS_ENTRY_TYPE)                               \
   V(BOX_TYPE)                                                   \
   V(PROTOTYPE_INFO_TYPE)                                        \
+  V(SLOPPY_BLOCK_WITH_EVAL_CONTEXT_EXTENSION_TYPE)              \
                                                                 \
   V(FIXED_ARRAY_TYPE)                                           \
   V(FIXED_DOUBLE_ARRAY_TYPE)                                    \
@@ -507,7 +508,10 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
   V(ALIASED_ARGUMENTS_ENTRY, AliasedArgumentsEntry, aliased_arguments_entry) \
   V(DEBUG_INFO, DebugInfo, debug_info)                                       \
   V(BREAK_POINT_INFO, BreakPointInfo, break_point_info)                      \
-  V(PROTOTYPE_INFO, PrototypeInfo, prototype_info)
+  V(PROTOTYPE_INFO, PrototypeInfo, prototype_info)                           \
+  V(SLOPPY_BLOCK_WITH_EVAL_CONTEXT_EXTENSION,                                \
+    SloppyBlockWithEvalContextExtension,                                     \
+    sloppy_block_with_eval_context_extension)
 
 // We use the full 8 bits of the instance_type field to encode heap object
 // instance types.  The high-order bit (bit 7) is set if the object is not a
@@ -685,6 +689,7 @@ enum InstanceType {
   WEAK_CELL_TYPE,
   PROPERTY_CELL_TYPE,
   PROTOTYPE_INFO_TYPE,
+  SLOPPY_BLOCK_WITH_EVAL_CONTEXT_EXTENSION_TYPE,
 
   // All the following types are subtypes of JSReceiver, which corresponds to
   // objects in the JS sense. The first and the last type in this range are
@@ -3748,6 +3753,9 @@ class ScopeInfo : public FixedArray {
   // Return the language mode of this scope.
   LanguageMode language_mode();
 
+  // True if this scope is a (var) declaration scope.
+  bool is_declaration_scope();
+
   // Does this scope make a sloppy eval call?
   bool CallsSloppyEval() { return CallsEval() && is_sloppy(language_mode()); }
 
@@ -3971,8 +3979,11 @@ class ScopeInfo : public FixedArray {
   STATIC_ASSERT(LANGUAGE_END == 3);
   class LanguageModeField
       : public BitField<LanguageMode, CallsEvalField::kNext, 2> {};
+  class DeclarationScopeField
+      : public BitField<bool, LanguageModeField::kNext, 1> {};
   class ReceiverVariableField
-      : public BitField<VariableAllocationInfo, LanguageModeField::kNext, 2> {};
+      : public BitField<VariableAllocationInfo, DeclarationScopeField::kNext,
+                        2> {};
   class FunctionVariableField
       : public BitField<VariableAllocationInfo, ReceiverVariableField::kNext,
                         2> {};
@@ -5835,6 +5846,32 @@ class PrototypeInfo : public Struct {
 };
 
 
+// Pair used to store both a ScopeInfo and an extension object in the extension
+// slot of a block context. Needed in the rare case where a declaration block
+// scope (a "varblock" as used to desugar parameter destructuring) also contains
+// a sloppy direct eval. (In no other case both are needed at the same time.)
+class SloppyBlockWithEvalContextExtension : public Struct {
+ public:
+  // [scope_info]: Scope info.
+  DECL_ACCESSORS(scope_info, ScopeInfo)
+  // [extension]: Extension object.
+  DECL_ACCESSORS(extension, JSObject)
+
+  DECLARE_CAST(SloppyBlockWithEvalContextExtension)
+
+  // Dispatched behavior.
+  DECLARE_PRINTER(SloppyBlockWithEvalContextExtension)
+  DECLARE_VERIFIER(SloppyBlockWithEvalContextExtension)
+
+  static const int kScopeInfoOffset = HeapObject::kHeaderSize;
+  static const int kExtensionOffset = kScopeInfoOffset + kPointerSize;
+  static const int kSize = kExtensionOffset + kPointerSize;
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(SloppyBlockWithEvalContextExtension);
+};
+
+
 // Script describes a script which has been added to the VM.
 class Script: public Struct {
  public:
index ecc65301350c9e61805e5bb7ca71aae054704961..96b5d9466c380aeea747343d2e905a1716a31266 100644 (file)
@@ -1998,12 +1998,14 @@ VariableProxy* Parser::NewUnresolved(const AstRawString* name,
 
 Variable* Parser::Declare(Declaration* declaration,
                           DeclarationDescriptor::Kind declaration_kind,
-                          bool resolve, bool* ok) {
+                          bool resolve, bool* ok, Scope* scope) {
   VariableProxy* proxy = declaration->proxy();
   DCHECK(proxy->raw_name() != NULL);
   const AstRawString* name = proxy->raw_name();
   VariableMode mode = declaration->mode();
-  Scope* declaration_scope = DeclarationScope(mode);
+  if (scope == nullptr) scope = scope_;
+  Scope* declaration_scope =
+      IsLexicalVariableMode(mode) ? scope : scope->DeclarationScope();
   Variable* var = NULL;
 
   // If a suitable scope exists, then we can statically declare this
@@ -2477,6 +2479,7 @@ void Parser::ParseVariableDeclarations(VariableDeclarationContext var_context,
   parsing_result->descriptor.declaration_scope =
       DeclarationScope(parsing_result->descriptor.mode);
   parsing_result->descriptor.scope = scope_;
+  parsing_result->descriptor.hoist_scope = nullptr;
 
 
   bool first_declaration = true;
@@ -4336,6 +4339,7 @@ Block* Parser::BuildParameterInitializationBlock(
     descriptor.parser = this;
     descriptor.declaration_scope = scope_;
     descriptor.scope = scope_;
+    descriptor.hoist_scope = nullptr;
     descriptor.mode = LET;
     descriptor.is_const = false;
     descriptor.needs_init = true;
@@ -4356,10 +4360,35 @@ Block* Parser::BuildParameterInitializationBlock(
           RelocInfo::kNoPosition);
       descriptor.initialization_pos = parameter.initializer->position();
     }
-    DeclarationParsingResult::Declaration decl(
-        parameter.pattern, parameter.pattern->position(), initial_value);
-    PatternRewriter::DeclareAndInitializeVariables(init_block, &descriptor,
-                                                   &decl, nullptr, CHECK_OK);
+
+    Scope* param_scope = scope_;
+    Block* param_block = init_block;
+    if (parameter.initializer != nullptr && scope_->calls_sloppy_eval()) {
+      param_scope = NewScope(scope_, BLOCK_SCOPE);
+      param_scope->set_is_declaration_scope();
+      param_scope->set_start_position(parameter.pattern->position());
+      param_scope->set_end_position(RelocInfo::kNoPosition);
+      param_scope->RecordEvalCall();
+      param_block = factory()->NewBlock(NULL, 8, true, RelocInfo::kNoPosition);
+      param_block->set_scope(param_scope);
+      descriptor.hoist_scope = scope_;
+    }
+
+    {
+      BlockState block_state(&scope_, param_scope);
+      DeclarationParsingResult::Declaration decl(
+          parameter.pattern, parameter.pattern->position(), initial_value);
+      PatternRewriter::DeclareAndInitializeVariables(param_block, &descriptor,
+                                                     &decl, nullptr, CHECK_OK);
+    }
+
+    if (parameter.initializer != nullptr && scope_->calls_sloppy_eval()) {
+      param_scope = param_scope->FinalizeBlockScope();
+      if (param_scope != nullptr) {
+        CheckConflictingVarDeclarations(param_scope, CHECK_OK);
+      }
+      init_block->AddStatement(param_block, zone());
+    }
   }
   return init_block;
 }
@@ -4393,7 +4422,7 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody(
   }
 
   ZoneList<Statement*>* body = result;
-  Scope* inner_scope = nullptr;
+  Scope* inner_scope = scope_;
   Block* inner_block = nullptr;
   if (!parameters.is_simple) {
     inner_scope = NewScope(scope_, BLOCK_SCOPE);
@@ -4405,7 +4434,7 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody(
   }
 
   {
-    BlockState block_state(&scope_, inner_scope ? inner_scope : scope_);
+    BlockState block_state(&scope_, inner_scope);
 
     // For generators, allocate and yield an iterator on function entry.
     if (IsGeneratorFunction(kind)) {
index a0be1dfe7ef4c2e77746f7fe21c9b1315eb361d5..602b5278ea813cb20083df5e99465c7466975abd 100644 (file)
@@ -974,6 +974,7 @@ class Parser : public ParserBase<ParserTraits> {
     Parser* parser;
     Scope* declaration_scope;
     Scope* scope;
+    Scope* hoist_scope;
     VariableMode mode;
     bool is_const;
     bool needs_init;
@@ -1134,7 +1135,7 @@ class Parser : public ParserBase<ParserTraits> {
   VariableProxy* NewUnresolved(const AstRawString* name, VariableMode mode);
   Variable* Declare(Declaration* declaration,
                     DeclarationDescriptor::Kind declaration_kind, bool resolve,
-                    bool* ok);
+                    bool* ok, Scope* declaration_scope = nullptr);
 
   bool TargetStackContainsLabel(const AstRawString* label);
   BreakableStatement* LookupBreakTarget(const AstRawString* label, bool* ok);
index 10702d65cee6a49c1db4aa0029cff66a868d496e..f3ecf7f1542dd658f3f2bbfc789f7d19381b4efc 100644 (file)
@@ -54,7 +54,8 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
       proxy, descriptor_->mode, descriptor_->scope,
       descriptor_->declaration_pos);
   Variable* var = parser->Declare(declaration, descriptor_->declaration_kind,
-                                  descriptor_->mode != VAR, ok_);
+                                  descriptor_->mode != VAR, ok_,
+                                  descriptor_->hoist_scope);
   if (!*ok_) return;
   DCHECK_NOT_NULL(var);
   DCHECK(!proxy->is_resolved() || proxy->var() == var);
index be688d2a32cf5dfa901b7e222cb03bee2dc99ae3..c7d4fee44f438475b10ca771d96abe91adf4dad8 100644 (file)
@@ -423,8 +423,7 @@ RUNTIME_FUNCTION(Runtime_LoadGlobalViaContext) {
   DCHECK(script_context->get(slot)->IsPropertyCell());
 
   // Lookup the named property on the global object.
-  Handle<ScopeInfo> scope_info(ScopeInfo::cast(script_context->extension()),
-                               isolate);
+  Handle<ScopeInfo> scope_info(script_context->scope_info(), isolate);
   Handle<Name> name(scope_info->ContextSlotName(slot), isolate);
   Handle<GlobalObject> global_object(script_context->global_object(), isolate);
   LookupIterator it(global_object, name, LookupIterator::HIDDEN);
@@ -458,8 +457,7 @@ Object* StoreGlobalViaContext(Isolate* isolate, int slot, Handle<Object> value,
   DCHECK(script_context->get(slot)->IsPropertyCell());
 
   // Lookup the named property on the global object.
-  Handle<ScopeInfo> scope_info(ScopeInfo::cast(script_context->extension()),
-                               isolate);
+  Handle<ScopeInfo> scope_info(script_context->scope_info(), isolate);
   Handle<Name> name(scope_info->ContextSlotName(slot), isolate);
   Handle<GlobalObject> global_object(script_context->global_object(), isolate);
   LookupIterator it(global_object, name, LookupIterator::HIDDEN);
index 01c828bf40430cead10ed5134e675ea03a896256..17ae043ae8e1e7470c54c8bc6768f071e9720da1 100644 (file)
@@ -206,8 +206,9 @@ namespace {
 Object* DeclareLookupSlot(Isolate* isolate, Handle<String> name,
                           Handle<Object> initial_value,
                           PropertyAttributes attr) {
-  // Declarations are always made in a function, eval or script context. In
-  // the case of eval code, the context passed is the context of the caller,
+  // Declarations are always made in a function, eval or script context, or
+  // a declaration block scope.
+  // In the case of eval code, the context passed is the context of the caller,
   // which may be some nested context and not the declaration context.
   Handle<Context> context_arg(isolate->context(), isolate);
   Handle<Context> context(context_arg->declaration_context(), isolate);
@@ -241,8 +242,7 @@ Object* DeclareLookupSlot(Isolate* isolate, Handle<String> name,
     return DeclareGlobals(isolate, Handle<JSGlobalObject>::cast(holder), name,
                           value, attr, is_var, is_const, is_function);
   }
-  if (context_arg->has_extension() &&
-      context_arg->extension()->IsJSGlobalObject()) {
+  if (context_arg->extension()->IsJSGlobalObject()) {
     Handle<JSGlobalObject> global(
         JSGlobalObject::cast(context_arg->extension()), isolate);
     return DeclareGlobals(isolate, global, name, value, attr, is_var, is_const,
@@ -274,7 +274,19 @@ Object* DeclareLookupSlot(Isolate* isolate, Handle<String> name,
     object = Handle<JSObject>::cast(holder);
 
   } else if (context->has_extension()) {
-    object = handle(JSObject::cast(context->extension()));
+    // Sloppy varblock contexts might not have an extension object yet,
+    // in which case their extension is a ScopeInfo.
+    if (context->extension()->IsScopeInfo()) {
+      DCHECK(context->IsBlockContext());
+      object = isolate->factory()->NewJSObject(
+          isolate->context_extension_function());
+      Handle<Object> extension =
+          isolate->factory()->NewSloppyBlockWithEvalContextExtension(
+              handle(context->scope_info()), object);
+      context->set_extension(*extension);
+    } else {
+      object = handle(context->extension_object(), isolate);
+    }
     DCHECK(object->IsJSContextExtensionObject() || object->IsJSGlobalObject());
   } else {
     DCHECK(context->IsFunctionContext());
@@ -357,8 +369,8 @@ RUNTIME_FUNCTION(Runtime_InitializeLegacyConstLookupSlot) {
     if (declaration_context->IsScriptContext()) {
       holder = handle(declaration_context->global_object(), isolate);
     } else {
-      DCHECK(declaration_context->has_extension());
-      holder = handle(declaration_context->extension(), isolate);
+      holder = handle(declaration_context->extension_object(), isolate);
+      DCHECK(!holder.is_null());
     }
     CHECK(holder->IsJSObject());
   } else {
index da146c3d17da638d6d783f8d500f985b29f35c08..5492922c455843acc2b3b2097bbe76526a2b6c01 100644 (file)
@@ -88,6 +88,7 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
   int flags = ScopeTypeField::encode(scope->scope_type()) |
               CallsEvalField::encode(scope->calls_eval()) |
               LanguageModeField::encode(scope->language_mode()) |
+              DeclarationScopeField::encode(scope->is_declaration_scope()) |
               ReceiverVariableField::encode(receiver_info) |
               FunctionVariableField::encode(function_name_info) |
               FunctionVariableMode::encode(function_variable_mode) |
@@ -243,6 +244,7 @@ Handle<ScopeInfo> ScopeInfo::CreateGlobalThisBinding(Isolate* isolate) {
   int flags = ScopeTypeField::encode(SCRIPT_SCOPE) |
               CallsEvalField::encode(false) |
               LanguageModeField::encode(SLOPPY) |
+              DeclarationScopeField::encode(true) |
               ReceiverVariableField::encode(receiver_info) |
               FunctionVariableField::encode(function_name_info) |
               FunctionVariableMode::encode(function_variable_mode) |
@@ -310,6 +312,11 @@ LanguageMode ScopeInfo::language_mode() {
 }
 
 
+bool ScopeInfo::is_declaration_scope() {
+  return DeclarationScopeField::decode(Flags());
+}
+
+
 int ScopeInfo::LocalCount() {
   return StackLocalCount() + ContextLocalCount();
 }
@@ -334,6 +341,8 @@ int ScopeInfo::ContextLength() {
     bool has_context = context_locals > 0 || context_globals > 0 ||
                        function_name_context_slot ||
                        scope_type() == WITH_SCOPE ||
+                       (scope_type() == BLOCK_SCOPE && CallsSloppyEval() &&
+                           is_declaration_scope()) ||
                        (scope_type() == ARROW_SCOPE && CallsSloppyEval()) ||
                        (scope_type() == FUNCTION_SCOPE && CallsSloppyEval()) ||
                        scope_type() == MODULE_SCOPE;
index 8f821461d16949ceb2151ac55ae4787a4c454ebd..9a6e3aae6da4d72b705970990826e3fd78308f90 100644 (file)
@@ -189,6 +189,7 @@ void Scope::SetDefaults(ScopeType scope_type, Scope* outer_scope,
   if (!scope_info.is_null()) {
     scope_calls_eval_ = scope_info->CallsEval();
     language_mode_ = scope_info->language_mode();
+    is_declaration_scope_ = scope_info->is_declaration_scope();
     function_kind_ = scope_info->function_kind();
   }
 }
@@ -212,12 +213,12 @@ Scope* Scope::DeserializeScopeChain(Isolate* isolate, Zone* zone,
         s->scope_inside_with_ = true;
       }
     } else if (context->IsScriptContext()) {
-      ScopeInfo* scope_info = ScopeInfo::cast(context->extension());
+      ScopeInfo* scope_info = context->scope_info();
       current_scope = new (zone) Scope(zone, current_scope, SCRIPT_SCOPE,
                                        Handle<ScopeInfo>(scope_info),
                                        script_scope->ast_value_factory_);
     } else if (context->IsModuleContext()) {
-      ScopeInfo* scope_info = ScopeInfo::cast(context->module()->scope_info());
+      ScopeInfo* scope_info = context->module()->scope_info();
       current_scope = new (zone) Scope(zone, current_scope, MODULE_SCOPE,
                                        Handle<ScopeInfo>(scope_info),
                                        script_scope->ast_value_factory_);
@@ -229,13 +230,13 @@ Scope* Scope::DeserializeScopeChain(Isolate* isolate, Zone* zone,
       if (scope_info->IsAsmFunction()) current_scope->asm_function_ = true;
       if (scope_info->IsAsmModule()) current_scope->asm_module_ = true;
     } else if (context->IsBlockContext()) {
-      ScopeInfo* scope_info = ScopeInfo::cast(context->extension());
+      ScopeInfo* scope_info = context->scope_info();
       current_scope = new (zone)
           Scope(zone, current_scope, BLOCK_SCOPE, Handle<ScopeInfo>(scope_info),
                 script_scope->ast_value_factory_);
     } else {
       DCHECK(context->IsCatchContext());
-      String* name = String::cast(context->extension());
+      String* name = context->catch_name();
       current_scope = new (zone) Scope(
           zone, current_scope,
           script_scope->ast_value_factory_->GetString(Handle<String>(name)),
@@ -344,7 +345,10 @@ Scope* Scope::FinalizeBlockScope() {
   DCHECK(temps_.is_empty());
   DCHECK(params_.is_empty());
 
-  if (num_var_or_const() > 0) return this;
+  if (num_var_or_const() > 0 ||
+      (is_declaration_scope() && calls_sloppy_eval())) {
+    return this;
+  }
 
   // Remove this scope from outer scope.
   for (int i = 0; i < outer_scope_->inner_scopes_.length(); i++) {
@@ -811,14 +815,14 @@ void Scope::ReportMessage(int start_position, int end_position,
 
 
 #ifdef DEBUG
-static const char* Header(ScopeType scope_type) {
+static const char* Header(ScopeType scope_type, bool is_declaration_scope) {
   switch (scope_type) {
     case EVAL_SCOPE: return "eval";
     case FUNCTION_SCOPE: return "function";
     case MODULE_SCOPE: return "module";
     case SCRIPT_SCOPE: return "global";
     case CATCH_SCOPE: return "catch";
-    case BLOCK_SCOPE: return "block";
+    case BLOCK_SCOPE: return is_declaration_scope ? "varblock" : "block";
     case WITH_SCOPE: return "with";
     case ARROW_SCOPE: return "arrow";
   }
@@ -902,7 +906,7 @@ void Scope::Print(int n) {
   int n1 = n0 + 2;  // indentation
 
   // Print header.
-  Indent(n0, Header(scope_type_));
+  Indent(n0, Header(scope_type_, is_declaration_scope()));
   if (!scope_name_->IsEmpty()) {
     PrintF(" ");
     PrintName(scope_name_);
@@ -1567,8 +1571,10 @@ void Scope::AllocateVariablesRecursively(Isolate* isolate) {
   // scope and for a function scope that makes an 'eval' call we need a context,
   // even if no local variables were statically allocated in the scope.
   // Likewise for modules.
-  bool must_have_context = is_with_scope() || is_module_scope() ||
-                           (is_function_scope() && calls_sloppy_eval());
+  bool must_have_context =
+      is_with_scope() || is_module_scope() ||
+      (is_function_scope() && calls_sloppy_eval()) ||
+      (is_block_scope() && is_declaration_scope() && calls_sloppy_eval());
 
   // If we didn't allocate any locals in the local context, then we only
   // need the minimal number of slots if we must have a context.
index 2f600b9f0cbc3aa244a94840e6450a96419a549e..4990fab994574a9fb9fbb27814ba93faf3ad5c86 100644 (file)
@@ -284,7 +284,7 @@ class Scope: public ZoneObject {
 
   // Information about which scopes calls eval.
   bool calls_eval() const { return scope_calls_eval_; }
-  bool calls_sloppy_eval() {
+  bool calls_sloppy_eval() const {
     return scope_calls_eval_ && is_sloppy(language_mode_);
   }
   bool outer_scope_calls_sloppy_eval() const {
@@ -305,6 +305,9 @@ class Scope: public ZoneObject {
   // Does this scope access "super" property (super.foo).
   bool uses_super_property() const { return scope_uses_super_property_; }
 
+  // Whether this needs to be represented by a runtime context.
+  bool NeedsContext() const { return num_heap_slots() > 0; }
+
   bool NeedsHomeObject() const {
     return scope_uses_super_property_ ||
            (scope_calls_eval_ && (IsConciseMethod(function_kind()) ||
index 43a7acd1c6dbc5b77f04c37cb8d39d992b04855d..bd9cb42db9b33808cba6b151489698e16db93875 100644 (file)
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 // Flags: --harmony-default-parameters --harmony-arrow-functions
-// Flags: --harmony-rest-parameters
+// Flags: --harmony-rest-parameters --harmony-destructuring
 
 
 (function TestDefaults() {
@@ -82,7 +82,6 @@
 
 
 (function TestParameterScoping() {
-  // TODO(rossberg): Add checks for variable declarations in defaults.
   var x = 1;
 
   function f1(a = x) { var x = 2; return a; }
 })();
 
 
+(function TestSloppyEvalScoping() {
+  var x = 1;
+
+  function f1(y = eval("var x = 2")) { with ({}) { return x; } }
+  assertEquals(1, f1());
+  function f2(y = eval("var x = 2"), z = x) { return z; }
+  assertEquals(1, f2());
+  assertEquals(1, f2(0));
+  function f3(y = eval("var x = 2"), z = eval("x")) { return z; }
+  assertEquals(1, f3());
+  assertEquals(1, f3(0));
+  // TODO(rossberg): eval inside patterns is not recognized yet.
+  // function f41({[eval("var x = 2; 'a'")]: w}, z = x)) { return z; }
+  // assertEquals(1, f41({}));
+  // assertEquals(1, f41({a: 0}));
+  // function f42({[eval("var x = 2; 'a'")]: w}, z = eval("x")) { return z; }
+  // assertEquals(1, f42({}));
+  // assertEquals(1, f42({a: 0}));
+  // function f43({a: w = eval("var x = 2")}, z = x) { return z; }
+  // assertEquals(1, f43({}));
+  // assertEquals(1, f43({a: 0}));
+  // function f44({a: w = eval("var x = 2")}, z = eval("x")) { return z; }
+  // assertEquals(1, f44({}));
+  // assertEquals(1, f44({a: 0}));
+
+  function f5({a = eval("var x = 2"), b = x}) { return b; }
+  assertEquals(2, f5({}));
+  assertEquals(1, f5({a: 0}));
+  function f6({a = eval("var x = 2"), b = eval("x")}) { return b; }
+  assertEquals(2, f6({}));
+  assertEquals(1, f6({a: 0}));
+  function f71({[eval("var x = 2; 'a'")]: w, b = x}) { return b; }
+  assertEquals(2, f71({}));
+  assertEquals(2, f71({a: 0}));
+  function f72({[eval("var x = 2; 'a'")]: w, b = eval("x")}) { return b; }
+  assertEquals(2, f72({}));
+  assertEquals(2, f72({a: 0}));
+  function f73({a: w = eval("var x = 2"), b = x}) { return b; }
+  assertEquals(2, f73({}));
+  assertEquals(1, f73({a: 0}));
+  function f74({a: w = eval("var x = 2"), b = eval("x")}) { return b; }
+  assertEquals(2, f74({}));
+  assertEquals(1, f74({a: 0}));
+  function f8(y = (eval("var x = 2"), x)) { return y; }
+  assertEquals(2, f8());
+  assertEquals(0, f8(0));
+
+  function f11(z = eval("var y = 2")) { return y; }
+  assertThrows(f11, ReferenceError);
+  function f12(z = eval("var y = 2"), b = y) {}
+  assertThrows(f12, ReferenceError);
+  function f13(z = eval("var y = 2"), b = eval("y")) {}
+  assertThrows(f13, ReferenceError);
+
+  function f21(f = () => x) { eval("var x = 2"); return f() }
+  assertEquals(1, f21());
+  assertEquals(3, f21(() => 3));
+  function f22(f = () => eval("x")) { eval("var x = 2"); return f() }
+  assertEquals(1, f22());
+  assertEquals(3, f22(() => 3));
+})();
+
+
+(function TestStrictEvalScoping() {
+  'use strict';
+  var x = 1;
+
+  function f1(y = eval("var x = 2")) { return x; }
+  assertEquals(1, f1());
+  function f2(y = eval("var x = 2"), z = x) { return z; }
+  assertEquals(1, f2());
+  assertEquals(1, f2(0));
+  function f3(y = eval("var x = 2"), z = eval("x")) { return z; }
+  assertEquals(1, f3());
+  assertEquals(1, f3(0));
+  function f41({[eval("var x = 2; 'a'")]: w}, z = x) { return z; }
+  assertEquals(1, f41({}));
+  assertEquals(1, f41({a: 0}));
+  function f42({[eval("var x = 2; 'a'")]: w}, z = eval("x")) { return z; }
+  assertEquals(1, f42({}));
+  assertEquals(1, f42({a: 0}));
+  function f43({a: w = eval("var x = 2")}, z = x) { return z; }
+  assertEquals(1, f43({}));
+  assertEquals(1, f43({a: 0}));
+  function f44({a: w = eval("var x = 2")}, z = eval("x")) { return z; }
+  assertEquals(1, f44({}));
+  assertEquals(1, f44({a: 0}));
+
+  function f5({a = eval("var x = 2"), b = x}) { return b; }
+  assertEquals(1, f5({}));
+  assertEquals(1, f5({a: 0}));
+  function f6({a = eval("var x = 2"), b = eval("x")}) { return b; }
+  assertEquals(1, f6({}));
+  assertEquals(1, f6({a: 0}));
+  function f71({[eval("var x = 2; 'a'")]: w, b = x}) { return b; }
+  assertEquals(1, f71({}));
+  assertEquals(1, f71({a: 0}));
+  function f72({[eval("var x = 2; 'a'")]: w, b = eval("x")}) { return b; }
+  assertEquals(1, f72({}));
+  assertEquals(1, f72({a: 0}));
+  function f73({a: w = eval("var x = 2"), b = x}) { return b; }
+  assertEquals(1, f73({}));
+  assertEquals(1, f73({a: 0}));
+  function f74({a: w = eval("var x = 2"), b = eval("x")}) { return b; }
+  assertEquals(1, f74({}));
+  assertEquals(1, f74({a: 0}));
+  function f8(y = (eval("var x = 2"), x)) { return y; }
+  assertEquals(1, f8());
+  assertEquals(0, f8(0));
+
+  function f11(z = eval("var y = 2")) { return y; }
+  assertThrows(f11, ReferenceError);
+  function f12(z = eval("var y = 2"), b = y) {}
+  assertThrows(f12, ReferenceError);
+  function f13(z = eval("var y = 2"), b = eval("y")) {}
+  assertThrows(f13, ReferenceError);
+
+  function f21(f = () => x) { eval("var x = 2"); return f() }
+  assertEquals(1, f21());
+  assertEquals(3, f21(() => 3));
+  function f22(f = () => eval("x")) { eval("var x = 2"); return f() }
+  assertEquals(1, f22());
+  assertEquals(3, f22(() => 3));
+})();
+
+
 (function TestParameterTDZ() {
   function f1(a = x, x) { return a }
   assertThrows(() => f1(undefined, 4), ReferenceError);