Static resolution of outer variables in eval code.
authorkeuchel@chromium.org <keuchel@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 15 Nov 2011 13:48:40 +0000 (13:48 +0000)
committerkeuchel@chromium.org <keuchel@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 15 Nov 2011 13:48:40 +0000 (13:48 +0000)
So far free variables references in eval code are not statically
resolved. For example in
    function foo() { var x = 1; eval("y = x"); }
the variable x will get mode DYNAMIC and y will get mode DYNAMIC_GLOBAL,
i.e. free variable references trigger dynamic lookups with a fast case
handling for global variables.

The CL introduces static resolution of free variables references in eval
code. If possible variable references are resolved to bindings belonging to
outer scopes of the eval call site.

This is achieved by deserializing the outer scope chain using
Scope::DeserializeScopeChain prior to parsing the eval code similar to lazy
parsing of functions. The existing code for variable resolution is used,
however resolution starts at the first outer unresolved scope instead of
always starting at the root of the scope tree.

This is a prerequisite for statically checking validity of assignments in
the extended code as specified by the current ES.next draft which will be
introduced by a subsequent CL. More specifically section 11.13 of revision 4
of the ES.next draft reads:
* It is a Syntax Error if the AssignmentExpression is contained in extended
  code and the LeftHandSideExpression is an Identifier that does not
  statically resolve to a declarative environment record binding or if the
  resolved binding is an immutable binding.

TEST=existing tests in mjsunit

Review URL: http://codereview.chromium.org/8508052

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

src/compiler.cc
src/compiler.h
src/contexts.cc
src/contexts.h
src/parser.cc
src/parser.h
src/runtime.cc
src/scopes.cc
src/scopes.h
test/cctest/test-parsing.cc

index 1e90469..8b34f02 100644 (file)
@@ -56,6 +56,7 @@ CompilationInfo::CompilationInfo(Handle<Script> script)
       flags_(0),
       function_(NULL),
       scope_(NULL),
+      global_scope_(NULL),
       script_(script),
       extension_(NULL),
       pre_parse_data_(NULL),
@@ -69,6 +70,7 @@ CompilationInfo::CompilationInfo(Handle<SharedFunctionInfo> shared_info)
       flags_(IsLazy::encode(true)),
       function_(NULL),
       scope_(NULL),
+      global_scope_(NULL),
       shared_info_(shared_info),
       script_(Handle<Script>(Script::cast(shared_info->script()))),
       extension_(NULL),
@@ -83,6 +85,7 @@ CompilationInfo::CompilationInfo(Handle<JSFunction> closure)
       flags_(IsLazy::encode(true)),
       function_(NULL),
       scope_(NULL),
+      global_scope_(NULL),
       closure_(closure),
       shared_info_(Handle<SharedFunctionInfo>(closure->shared())),
       script_(Handle<Script>(Script::cast(shared_info_->script()))),
index ecf1120..ef8f073 100644 (file)
@@ -59,6 +59,7 @@ class CompilationInfo BASE_EMBEDDED {
   bool is_in_loop() const { return IsInLoop::decode(flags_); }
   FunctionLiteral* function() const { return function_; }
   Scope* scope() const { return scope_; }
+  Scope* global_scope() const { return global_scope_; }
   Handle<Code> code() const { return code_; }
   Handle<JSFunction> closure() const { return closure_; }
   Handle<SharedFunctionInfo> shared_info() const { return shared_info_; }
@@ -99,6 +100,10 @@ class CompilationInfo BASE_EMBEDDED {
     ASSERT(scope_ == NULL);
     scope_ = scope;
   }
+  void SetGlobalScope(Scope* global_scope) {
+    ASSERT(global_scope_ == NULL);
+    global_scope_ = global_scope;
+  }
   void SetCode(Handle<Code> code) { code_ = code; }
   void SetExtension(v8::Extension* extension) {
     ASSERT(!is_lazy());
@@ -228,6 +233,8 @@ class CompilationInfo BASE_EMBEDDED {
   // The scope of the function literal as a convenience.  Set to indicate
   // that scopes have been analyzed.
   Scope* scope_;
+  // The global scope provided as a convenience.
+  Scope* global_scope_;
   // The compiled code.
   Handle<Code> code_;
 
index aee963b..76784bd 100644 (file)
@@ -240,62 +240,6 @@ Handle<Object> Context::Lookup(Handle<String> name,
 }
 
 
-bool Context::GlobalIfNotShadowedByEval(Handle<String> name) {
-  Context* context = this;
-
-  // Check that there is no local with the given name in contexts
-  // before the global context and check that there are no context
-  // extension objects (conservative check for with statements).
-  while (!context->IsGlobalContext()) {
-    // Check if the context is a catch or with context, or has introduced
-    // bindings by calling non-strict eval.
-    if (context->has_extension()) return false;
-
-    // Not a with context so it must be a function context.
-    ASSERT(context->IsFunctionContext());
-
-    // Check non-parameter locals.
-    Handle<ScopeInfo> scope_info(context->closure()->shared()->scope_info());
-    VariableMode mode;
-    InitializationFlag init_flag;
-    int index = scope_info->ContextSlotIndex(*name, &mode, &init_flag);
-    ASSERT(index < 0 || index >= MIN_CONTEXT_SLOTS);
-    if (index >= 0) return false;
-
-    // Check parameter locals.
-    int param_index = scope_info->ParameterIndex(*name);
-    if (param_index >= 0) return false;
-
-    // Check context only holding the function name variable.
-    index = scope_info->FunctionContextSlotIndex(*name, &mode);
-    if (index >= 0) return false;
-    context = context->previous();
-  }
-
-  // No local or potential with statement found so the variable is
-  // global unless it is shadowed by an eval-introduced variable.
-  return true;
-}
-
-
-void Context::ComputeEvalScopeInfo(bool* outer_scope_calls_non_strict_eval) {
-  // Skip up the context chain checking all the function contexts to see
-  // whether they call eval.
-  Context* context = this;
-  while (!context->IsGlobalContext()) {
-    if (context->IsFunctionContext()) {
-      if (context->closure()->shared()->scope_info()->CallsNonStrictEval()) {
-        // No need to go further since the answers will not change from
-        // here.
-        *outer_scope_calls_non_strict_eval = true;
-        return;
-      }
-    }
-    context = context->previous();
-  }
-}
-
-
 void Context::AddOptimizedFunction(JSFunction* function) {
   ASSERT(IsGlobalContext());
 #ifdef DEBUG
index 7021ff8..10ef33d 100644 (file)
@@ -398,19 +398,6 @@ class Context: public FixedArray {
                         PropertyAttributes* attributes,
                         BindingFlags* binding_flags);
 
-  // Determine if a local variable with the given name exists in a
-  // context.  Do not consider context extension objects.  This is
-  // used for compiling code using eval.  If the context surrounding
-  // the eval call does not have a local variable with this name and
-  // does not contain a with statement the property is global unless
-  // it is shadowed by a property in an extension object introduced by
-  // eval.
-  bool GlobalIfNotShadowedByEval(Handle<String> name);
-
-  // Determine if any function scope in the context call eval and if
-  // any of those calls are in non-strict mode.
-  void ComputeEvalScopeInfo(bool* outer_scope_calls_non_strict_eval);
-
   // Code generation support.
   static int SlotOffset(int index) {
     return kHeaderSize + index * kPointerSize - kHeapObjectTag;
index 0839510..e19af1c 100644 (file)
@@ -607,12 +607,11 @@ Parser::Parser(Handle<Script> script,
 }
 
 
-FunctionLiteral* Parser::ParseProgram(Handle<String> source,
-                                      bool in_global_context,
-                                      StrictModeFlag strict_mode) {
+FunctionLiteral* Parser::ParseProgram(CompilationInfo* info) {
   ZoneScope zone_scope(isolate(), DONT_DELETE_ON_EXIT);
 
   HistogramTimerScope timer(isolate()->counters()->parse());
+  Handle<String> source(String::cast(script_->source()));
   isolate()->counters()->total_parse_size()->Increment(source->length());
   fni_ = new(zone()) FuncNameInferrer(isolate());
 
@@ -625,18 +624,17 @@ FunctionLiteral* Parser::ParseProgram(Handle<String> source,
     ExternalTwoByteStringUC16CharacterStream stream(
         Handle<ExternalTwoByteString>::cast(source), 0, source->length());
     scanner_.Initialize(&stream);
-    return DoParseProgram(source, in_global_context, strict_mode, &zone_scope);
+    return DoParseProgram(info, source, &zone_scope);
   } else {
     GenericStringUC16CharacterStream stream(source, 0, source->length());
     scanner_.Initialize(&stream);
-    return DoParseProgram(source, in_global_context, strict_mode, &zone_scope);
+    return DoParseProgram(info, source, &zone_scope);
   }
 }
 
 
-FunctionLiteral* Parser::DoParseProgram(Handle<String> source,
-                                        bool in_global_context,
-                                        StrictModeFlag strict_mode,
+FunctionLiteral* Parser::DoParseProgram(CompilationInfo* info,
+                                        Handle<String> source,
                                         ZoneScope* zone_scope) {
   ASSERT(top_scope_ == NULL);
   ASSERT(target_stack_ == NULL);
@@ -646,16 +644,19 @@ FunctionLiteral* Parser::DoParseProgram(Handle<String> source,
   mode_ = FLAG_lazy ? PARSE_LAZILY : PARSE_EAGERLY;
   if (allow_natives_syntax_ || extension_ != NULL) mode_ = PARSE_EAGERLY;
 
-  ScopeType type = in_global_context ? GLOBAL_SCOPE : EVAL_SCOPE;
   Handle<String> no_name = isolate()->factory()->empty_symbol();
 
   FunctionLiteral* result = NULL;
-  { Scope* scope = NewScope(top_scope_, type);
+  { Scope* scope = NewScope(top_scope_, GLOBAL_SCOPE);
+    info->SetGlobalScope(scope);
+    if (!info->is_global()) {
+      scope = Scope::DeserializeScopeChain(*info->calling_context(), scope);
+      scope = NewScope(scope, EVAL_SCOPE);
+    }
     scope->set_start_position(0);
     scope->set_end_position(source->length());
     FunctionState function_state(this, scope, isolate());
-    ASSERT(top_scope_->strict_mode_flag() == kNonStrictMode);
-    top_scope_->SetStrictModeFlag(strict_mode);
+    top_scope_->SetStrictModeFlag(info->strict_mode_flag());
     ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(16);
     bool ok = true;
     int beg_loc = scanner().location().beg_pos;
@@ -742,8 +743,9 @@ FunctionLiteral* Parser::ParseLazy(CompilationInfo* info,
   {
     // Parse the function literal.
     Scope* scope = NewScope(top_scope_, GLOBAL_SCOPE);
+    info->SetGlobalScope(scope);
     if (!info->closure().is_null()) {
-      scope = Scope::DeserializeScopeChain(info, scope);
+      scope = Scope::DeserializeScopeChain(info->closure()->context(), scope);
     }
     FunctionState function_state(this, scope, isolate());
     ASSERT(scope->strict_mode_flag() == kNonStrictMode ||
@@ -1370,6 +1372,8 @@ VariableProxy* Parser::Declare(Handle<String> name,
   // enclosing scope.
   Scope* declaration_scope = (mode == LET || mode == CONST_HARMONY)
       ? top_scope_ : top_scope_->DeclarationScope();
+  InitializationFlag init_flag = (fun != NULL || mode == VAR)
+      ? kCreatedInitialized : kNeedsInitialization;
 
   // If a function scope exists, then we can statically declare this
   // variable and also set its mode. In any case, a Declaration node
@@ -1388,8 +1392,6 @@ VariableProxy* Parser::Declare(Handle<String> name,
     var = declaration_scope->LocalLookup(name);
     if (var == NULL) {
       // Declare the name.
-      InitializationFlag init_flag = (fun != NULL || mode == VAR)
-          ? kCreatedInitialized : kNeedsInitialization;
       var = declaration_scope->DeclareLocal(name, mode, init_flag);
     } else {
       // The name was declared in this scope before; check for conflicting
@@ -1452,17 +1454,31 @@ VariableProxy* Parser::Declare(Handle<String> name,
   declaration_scope->AddDeclaration(
       new(zone()) Declaration(proxy, mode, fun, top_scope_));
 
-  // For global const variables we bind the proxy to a variable.
   if ((mode == CONST || mode == CONST_HARMONY) &&
       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;
     var = new(zone()) Variable(declaration_scope,
                                name,
-                               CONST,
+                               mode,
                                true,
                                kind,
                                kNeedsInitialization);
+  } else if (declaration_scope->is_eval_scope() &&
+             !declaration_scope->is_strict_mode()) {
+    // For variable declarations in a non-strict eval scope the proxy is bound
+    // to a lookup variable to force a dynamic declaration using the
+    // DeclareContextSlot runtime function.
+    Variable::Kind kind = Variable::NORMAL;
+    var = new(zone()) Variable(declaration_scope,
+                               name,
+                               mode,
+                               true,
+                               kind,
+                               init_flag);
+    var->AllocateTo(Variable::LOOKUP, -1);
+    resolve = true;
   }
 
   // If requested and we have a local variable, bind the proxy to the variable
@@ -1909,22 +1925,30 @@ Block* Parser::ParseVariableDeclarations(
       }
 
       block->AddStatement(new(zone()) ExpressionStatement(initialize));
+    } else if (needs_init) {
+      // Constant initializations always assign to the declared constant which
+      // is always at the function scope level. This is only relevant for
+      // dynamically looked-up variables and constants (the start context for
+      // constant lookups is always the function context, while it is the top
+      // context for var declared variables). Sigh...
+      // For 'let' and 'const' declared variables in harmony mode the
+      // initialization also always assigns to the declared variable.
+      ASSERT(proxy != NULL);
+      ASSERT(proxy->var() != NULL);
+      ASSERT(value != NULL);
+      Assignment* assignment =
+          new(zone()) Assignment(isolate(), init_op, proxy, value, position);
+      block->AddStatement(new(zone()) ExpressionStatement(assignment));
+      value = NULL;
     }
 
     // Add an assignment node to the initialization statement block if we still
-    // have a pending initialization value. We must distinguish between
-    // different kinds of declarations: 'var' initializations are simply
-    // assignments (with all the consequences if they are inside a 'with'
-    // statement - they may change a 'with' object property). Constant
-    // initializations always assign to the declared constant which is
-    // always at the function scope level. This is only relevant for
-    // dynamically looked-up variables and constants (the start context
-    // for constant lookups is always the function context, while it is
-    // the top context for var declared variables). Sigh...
-    // For 'let' and 'const' declared variables in harmony mode the
-    // initialization is in the same scope as the declaration. Thus dynamic
-    // lookups are unnecessary even if the block scope is inside a with.
+    // have a pending initialization value.
     if (value != NULL) {
+      ASSERT(mode == VAR);
+      // 'var' initializations are simply assignments (with all the consequences
+      // if they are inside a 'with' statement - they may change a 'with' object
+      // property).
       VariableProxy* proxy = initialization_scope->NewUnresolved(name);
       Assignment* assignment =
           new(zone()) Assignment(isolate(), init_op, proxy, value, position);
@@ -5405,6 +5429,7 @@ bool ParserApi::Parse(CompilationInfo* info) {
   Handle<Script> script = info->script();
   bool harmony_scoping = !info->is_native() && FLAG_harmony_scoping;
   if (info->is_lazy()) {
+    ASSERT(!info->is_eval());
     bool allow_natives_syntax =
         FLAG_allow_natives_syntax ||
         info->is_native();
@@ -5433,10 +5458,7 @@ bool ParserApi::Parse(CompilationInfo* info) {
       DeleteArray(args.start());
       ASSERT(info->isolate()->has_pending_exception());
     } else {
-      Handle<String> source = Handle<String>(String::cast(script->source()));
-      result = parser.ParseProgram(source,
-                                   info->is_global(),
-                                   info->strict_mode_flag());
+      result = parser.ParseProgram(info);
     }
   }
   info->SetFunction(result);
index 588624b..1436bf9 100644 (file)
@@ -430,10 +430,7 @@ class Parser {
   virtual ~Parser() { }
 
   // Returns NULL if parsing failed.
-  FunctionLiteral* ParseProgram(Handle<String> source,
-                                bool in_global_context,
-                                StrictModeFlag strict_mode);
-
+  FunctionLiteral* ParseProgram(CompilationInfo* info);
   FunctionLiteral* ParseLazy(CompilationInfo* info);
 
   void ReportMessageAt(Scanner::Location loc,
@@ -480,9 +477,8 @@ class Parser {
   Zone* zone() { return isolate_->zone(); }
 
   // Called by ParseProgram after setting up the scanner.
-  FunctionLiteral* DoParseProgram(Handle<String> source,
-                                  bool in_global_context,
-                                  StrictModeFlag strict_mode,
+  FunctionLiteral* DoParseProgram(CompilationInfo* info,
+                                  Handle<String> source,
                                   ZoneScope* zone_scope);
 
   // Report syntax error
index 15f091c..62c38aa 100644 (file)
@@ -12191,15 +12191,12 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluateGlobal) {
   bool is_global = true;
 
   if (additional_context->IsJSObject()) {
-    // Create a function context first, than put 'with' context on top of it.
-    Handle<JSFunction> go_between = isolate->factory()->NewFunction(
-        isolate->factory()->empty_string(),
-        isolate->factory()->undefined_value());
-    go_between->set_context(*context);
-    context =
-        isolate->factory()->NewFunctionContext(
-            Context::MIN_CONTEXT_SLOTS, go_between);
-    context->set_extension(JSObject::cast(*additional_context));
+    // Create a new with context with the additional context information between
+    // the context of the debugged function and the eval code to be executed.
+    context = isolate->factory()->NewWithContext(
+        Handle<JSFunction>(context->closure()),
+        context,
+        Handle<JSObject>::cast(additional_context));
     is_global = false;
   }
 
index f9cebcd..f9cd620 100644 (file)
@@ -129,7 +129,7 @@ Scope::Scope(Scope* outer_scope, ScopeType type)
   // 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((type == GLOBAL_SCOPE || type == EVAL_SCOPE) == (outer_scope == NULL));
+  ASSERT_EQ(type == GLOBAL_SCOPE, outer_scope == NULL);
   ASSERT(!HasIllegalRedeclaration());
 }
 
@@ -148,6 +148,8 @@ Scope::Scope(Scope* inner_scope,
   SetDefaults(type, NULL, scope_info);
   if (!scope_info.is_null()) {
     num_heap_slots_ = scope_info_->ContextLength();
+  } else if (is_with_scope()) {
+    num_heap_slots_ = Context::MIN_CONTEXT_SLOTS;
   }
   AddInnerScope(inner_scope);
 }
@@ -165,6 +167,7 @@ Scope::Scope(Scope* inner_scope, Handle<String> catch_variable_name)
   SetDefaults(CATCH_SCOPE, NULL, Handle<ScopeInfo>::null());
   AddInnerScope(inner_scope);
   ++num_var_or_const_;
+  num_heap_slots_ = Context::MIN_CONTEXT_SLOTS;
   Variable* variable = variables_.Declare(this,
                                           catch_variable_name,
                                           VAR,
@@ -201,14 +204,16 @@ void Scope::SetDefaults(ScopeType type,
   scope_info_ = scope_info;
   start_position_ = RelocInfo::kNoPosition;
   end_position_ = RelocInfo::kNoPosition;
+  if (!scope_info.is_null()) {
+    scope_calls_eval_ = scope_info->CallsEval();
+    strict_mode_flag_ =
+        scope_info->IsStrictMode() ? kStrictMode : kNonStrictMode;
+  }
 }
 
 
-Scope* Scope::DeserializeScopeChain(CompilationInfo* info,
-                                    Scope* global_scope) {
+Scope* Scope::DeserializeScopeChain(Context* context, Scope* global_scope) {
   // Reconstruct the outer scope chain from a closure's context chain.
-  ASSERT(!info->closure().is_null());
-  Context* context = info->closure()->context();
   Scope* current_scope = NULL;
   Scope* innermost_scope = NULL;
   bool contains_with = false;
@@ -249,26 +254,35 @@ Scope* Scope::DeserializeScopeChain(CompilationInfo* info,
   }
 
   global_scope->AddInnerScope(current_scope);
+  global_scope->PropagateScopeInfo(false);
   return (innermost_scope == NULL) ? global_scope : innermost_scope;
 }
 
 
 bool Scope::Analyze(CompilationInfo* info) {
   ASSERT(info->function() != NULL);
-  Scope* top = info->function()->scope();
+  Scope* scope = info->function()->scope();
+  Scope* top = scope;
+
+  // Traverse the scope tree up to the first unresolved scope or the global
+  // scope and start scope resolution and variable allocation from that scope.
+  while (!top->is_global_scope() &&
+         !top->outer_scope()->already_resolved()) {
+    top = top->outer_scope();
+  }
 
-  while (top->outer_scope() != NULL) top = top->outer_scope();
-  top->AllocateVariables(info->calling_context());
+  // Allocated the variables.
+  top->AllocateVariables(info->global_scope());
 
 #ifdef DEBUG
   if (info->isolate()->bootstrapper()->IsActive()
           ? FLAG_print_builtin_scopes
           : FLAG_print_scopes) {
-    info->function()->scope()->Print();
+    scope->Print();
   }
 #endif
 
-  info->SetScope(info->function()->scope());
+  info->SetScope(scope);
   return true;  // Can not fail.
 }
 
@@ -356,10 +370,6 @@ Variable* Scope::LocalLookup(Handle<String> name) {
     return result;
   }
   // If we have a serialized scope info, we might find the variable there.
-  //
-  // We should never lookup 'arguments' in this scope as it is implicitly
-  // present in every scope.
-  ASSERT(*name != *isolate_->factory()->arguments_symbol());
   // There should be no local slot with the given name.
   ASSERT(scope_info_->StackSlotIndex(*name) < 0);
 
@@ -372,11 +382,7 @@ Variable* Scope::LocalLookup(Handle<String> name) {
     mode = VAR;
     init_flag = kCreatedInitialized;
     index = scope_info_->ParameterIndex(*name);
-    if (index < 0) {
-      // Check the function name.
-      index = scope_info_->FunctionContextSlotIndex(*name, &mode);
-      if (index < 0) return NULL;
-    }
+    if (index < 0) return NULL;
   }
 
   Variable* var =
@@ -391,6 +397,23 @@ Variable* Scope::LocalLookup(Handle<String> name) {
 }
 
 
+Variable* Scope::LookupFunctionVar(Handle<String> name) {
+  if (function_ != NULL && function_->name().is_identical_to(name)) {
+    return function_->var();
+  } else if (!scope_info_.is_null()) {
+    // If we are backed by a scope info, try to lookup the variable there.
+    VariableMode mode;
+    int index = scope_info_->FunctionContextSlotIndex(*name, &mode);
+    if (index < 0) return NULL;
+    Variable* var = DeclareFunctionVar(name, mode);
+    var->AllocateTo(Variable::CONTEXT, index);
+    return var;
+  } else {
+    return NULL;
+  }
+}
+
+
 Variable* Scope::Lookup(Handle<String> name) {
   for (Scope* scope = this;
        scope != NULL;
@@ -559,26 +582,18 @@ void Scope::CollectStackAndContextLocals(ZoneList<Variable*>* stack_locals,
 }
 
 
-void Scope::AllocateVariables(Handle<Context> context) {
-  ASSERT(outer_scope_ == NULL);  // eval or global scopes only
-
+void Scope::AllocateVariables(Scope* global_scope) {
   // 1) Propagate scope information.
-  // If we are in an eval scope, we may have other outer scopes about
-  // which we don't know anything at this point. Thus we must be conservative
-  // and assume they may invoke eval themselves. Eventually we could capture
-  // this information in the ScopeInfo and then use it here (by traversing
-  // the call chain stack, at compile time).
-
   bool outer_scope_calls_non_strict_eval = false;
-  if (!is_global_scope()) {
-    context->ComputeEvalScopeInfo(&outer_scope_calls_non_strict_eval);
+  if (outer_scope_ != NULL) {
+    outer_scope_calls_non_strict_eval =
+        outer_scope_->outer_scope_calls_non_strict_eval() |
+        outer_scope_->calls_non_strict_eval();
   }
   PropagateScopeInfo(outer_scope_calls_non_strict_eval);
 
   // 2) Resolve variables.
-  Scope* global_scope = NULL;
-  if (is_global_scope()) global_scope = this;
-  ResolveVariablesRecursively(global_scope, context);
+  ResolveVariablesRecursively(global_scope);
 
   // 3) Allocate variables.
   AllocateVariablesRecursively();
@@ -832,7 +847,6 @@ Variable* Scope::NonLocal(Handle<String> name, VariableMode mode) {
 
 
 Variable* Scope::LookupRecursive(Handle<String> name,
-                                 Handle<Context> context,
                                  BindingKind* binding_kind) {
   ASSERT(binding_kind != NULL);
   // Try to find the variable in this scope.
@@ -849,20 +863,17 @@ Variable* Scope::LookupRecursive(Handle<String> name,
   // We did not find a variable locally. Check against the function variable,
   // if any. We can do this for all scopes, since the function variable is
   // only present - if at all - for function scopes.
-  //
-  // This lookup corresponds to a lookup in the "intermediate" scope sitting
-  // between this scope and the outer scope. (ECMA-262, 3rd., requires that
-  // the name of named function literal is kept in an intermediate scope
-  // in between this scope and the next outer scope.)
   *binding_kind = UNBOUND;
-  if (function_ != NULL && function_->name().is_identical_to(name)) {
-    var = function_->var();
+  var = LookupFunctionVar(name);
+  if (var != NULL) {
     *binding_kind = BOUND;
   } else if (outer_scope_ != NULL) {
-    var = outer_scope_->LookupRecursive(name, context, binding_kind);
+    var = outer_scope_->LookupRecursive(name, binding_kind);
     if (*binding_kind == BOUND && (is_function_scope() || is_with_scope())) {
       var->ForceContextAllocation();
     }
+  } else {
+    ASSERT(is_global_scope());
   }
 
   if (is_with_scope()) {
@@ -874,16 +885,6 @@ Variable* Scope::LookupRecursive(Handle<String> name,
     // object).
     *binding_kind = DYNAMIC_LOOKUP;
     return NULL;
-  } else if (is_eval_scope()) {
-    // No local binding was found, no 'with' statements have been encountered
-    // and the code is executed as part of a call to 'eval'. The calling context
-    // contains scope information that we can use to determine if the variable
-    // is global, i.e. the calling context chain does not contain a binding and
-    // no 'with' contexts.
-    ASSERT(*binding_kind == UNBOUND);
-    *binding_kind = context->GlobalIfNotShadowedByEval(name)
-        ? UNBOUND_EVAL_SHADOWED : DYNAMIC_LOOKUP;
-    return NULL;
   } else if (calls_non_strict_eval()) {
     // A variable binding may have been found in an outer scope, but the current
     // scope makes a non-strict 'eval' call, so the found variable may not be
@@ -900,7 +901,6 @@ Variable* Scope::LookupRecursive(Handle<String> name,
 
 
 void Scope::ResolveVariable(Scope* global_scope,
-                            Handle<Context> context,
                             VariableProxy* proxy) {
   ASSERT(global_scope == NULL || global_scope->is_global_scope());
 
@@ -910,7 +910,7 @@ void Scope::ResolveVariable(Scope* global_scope,
 
   // Otherwise, try to resolve the variable.
   BindingKind binding_kind;
-  Variable* var = LookupRecursive(proxy->name(), context, &binding_kind);
+  Variable* var = LookupRecursive(proxy->name(), &binding_kind);
   switch (binding_kind) {
     case BOUND:
       // We found a variable binding.
@@ -951,18 +951,17 @@ void Scope::ResolveVariable(Scope* global_scope,
 }
 
 
-void Scope::ResolveVariablesRecursively(Scope* global_scope,
-                                        Handle<Context> context) {
+void Scope::ResolveVariablesRecursively(Scope* global_scope) {
   ASSERT(global_scope == NULL || global_scope->is_global_scope());
 
   // Resolve unresolved variables for this scope.
   for (int i = 0; i < unresolved_.length(); i++) {
-    ResolveVariable(global_scope, context, unresolved_[i]);
+    ResolveVariable(global_scope, unresolved_[i]);
   }
 
   // Resolve unresolved variables for inner scopes.
   for (int i = 0; i < inner_scopes_.length(); i++) {
-    inner_scopes_[i]->ResolveVariablesRecursively(global_scope, context);
+    inner_scopes_[i]->ResolveVariablesRecursively(global_scope);
   }
 }
 
@@ -973,8 +972,7 @@ bool Scope::PropagateScopeInfo(bool outer_scope_calls_non_strict_eval ) {
   }
 
   bool calls_non_strict_eval =
-      (scope_calls_eval_ && !is_strict_mode()) ||
-      outer_scope_calls_non_strict_eval_;
+      this->calls_non_strict_eval() || outer_scope_calls_non_strict_eval_;
   for (int i = 0; i < inner_scopes_.length(); i++) {
     Scope* inner_scope = inner_scopes_[i];
     if (inner_scope->PropagateScopeInfo(calls_non_strict_eval)) {
index d08e294..209cd0e 100644 (file)
@@ -93,8 +93,7 @@ class Scope: public ZoneObject {
   // doesn't re-allocate variables repeatedly.
   static bool Analyze(CompilationInfo* info);
 
-  static Scope* DeserializeScopeChain(CompilationInfo* info,
-                                      Scope* innermost_scope);
+  static Scope* DeserializeScopeChain(Context* context, Scope* global_scope);
 
   // The scope name is only used for printing/debugging.
   void SetScopeName(Handle<String> scope_name) { scope_name_ = scope_name; }
@@ -112,6 +111,12 @@ class Scope: public ZoneObject {
   // Lookup a variable in this scope. Returns the variable or NULL if not found.
   Variable* LocalLookup(Handle<String> name);
 
+  // This lookup corresponds to a lookup in the "intermediate" scope sitting
+  // between this scope and the outer scope. (ECMA-262, 3rd., requires that
+  // the name of named function literal is kept in an intermediate scope
+  // in between this scope and the next outer scope.)
+  Variable* LookupFunctionVar(Handle<String> name);
+
   // Lookup a variable in this scope or outer scopes.
   // Returns the variable or NULL if not found.
   Variable* Lookup(Handle<String> name);
@@ -317,7 +322,7 @@ class Scope: public ZoneObject {
   // In the case of code compiled and run using 'eval', the context
   // parameter is the context in which eval was called.  In all other
   // cases the context parameter is an empty handle.
-  void AllocateVariables(Handle<Context> context);
+  void AllocateVariables(Scope* global_scope);
 
   // Current number of var or const locals.
   int num_var_or_const() { return num_var_or_const_; }
@@ -504,13 +509,10 @@ class Scope: public ZoneObject {
   // scope. If the code is executed because of a call to 'eval', the context
   // parameter should be set to the calling context of 'eval'.
   Variable* LookupRecursive(Handle<String> name,
-                            Handle<Context> context,
                             BindingKind* binding_kind);
   void ResolveVariable(Scope* global_scope,
-                       Handle<Context> context,
                        VariableProxy* proxy);
-  void ResolveVariablesRecursively(Scope* global_scope,
-                                   Handle<Context> context);
+  void ResolveVariablesRecursively(Scope* global_scope);
 
   // Scope analysis.
   bool PropagateScopeInfo(bool outer_scope_calls_non_strict_eval);
index 00d200a..7901edc 100755 (executable)
@@ -32,6 +32,7 @@
 #include "v8.h"
 
 #include "cctest.h"
+#include "compiler.h"
 #include "execution.h"
 #include "isolate.h"
 #include "parser.h"
@@ -856,9 +857,10 @@ TEST(ScopePositions) {
     i::Handle<i::Script> script = FACTORY->NewScript(source);
     i::Parser parser(script, false, NULL, NULL);
     parser.SetHarmonyScoping(true);
-    i::FunctionLiteral* function =
-        parser.ParseProgram(source, true, i::kNonStrictMode);
-    ASSERT(function != NULL);
+    i::CompilationInfo info(script);
+    info.MarkAsGlobal();
+    i::FunctionLiteral* function = parser.ParseProgram(&info);
+    CHECK(function != NULL);
 
     // Check scope types and positions.
     i::Scope* scope = function->scope();