Reapply "Resolve references to "this" the same way as normal variables""
authorwingo <wingo@igalia.com>
Tue, 19 May 2015 14:51:10 +0000 (07:51 -0700)
committerCommit bot <commit-bot@chromium.org>
Tue, 19 May 2015 14:50:59 +0000 (14:50 +0000)
This reapplies https://codereview.chromium.org/1136073002, along with
the followups:

  Remove Scope::scope_uses_this_ flag
  https://codereview.chromium.org/1128963005

and

  PPC: Resolve references to "this" the same way as normal variables
  https://codereview.chromium.org/1134073003

R=rossberg@chromium.org
LOG=N
BUG=

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

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

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

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

34 files changed:
src/arm/full-codegen-arm.cc
src/arm/lithium-codegen-arm.cc
src/arm64/full-codegen-arm64.cc
src/arm64/lithium-codegen-arm64.cc
src/compiler.cc
src/compiler/ast-graph-builder.cc
src/compiler/ast-graph-builder.h
src/contexts.cc
src/heap/heap.h
src/ia32/full-codegen-ia32.cc
src/ia32/lithium-codegen-ia32.cc
src/mips/full-codegen-mips.cc
src/mips/lithium-codegen-mips.cc
src/mips64/full-codegen-mips64.cc
src/mips64/lithium-codegen-mips64.cc
src/objects.h
src/parser.cc
src/ppc/full-codegen-ppc.cc
src/ppc/lithium-codegen-ppc.cc
src/preparser.h
src/runtime/runtime-debug.cc
src/runtime/runtime-scopes.cc
src/scopeinfo.cc
src/scopes.cc
src/scopes.h
src/variables.cc
src/x64/full-codegen-x64.cc
src/x64/lithium-codegen-x64.cc
test/cctest/test-parsing.cc
test/cctest/test-serialize.cc
test/mjsunit/debug-scopes.js
test/mjsunit/harmony/arrow-functions-this.js [new file with mode: 0644]
test/mjsunit/testcfg.py
test/mjsunit/this-dynamic-lookup.js [new file with mode: 0644]

index dbf8151..70a76e1 100644 (file)
@@ -127,7 +127,7 @@ void FullCodeGenerator::Generate() {
   // global proxy when called as functions (without an explicit receiver
   // object).
   if (is_sloppy(info->language_mode()) && !info->is_native() &&
-      info->MayUseThis()) {
+      info->MayUseThis() && info->scope()->has_this_declaration()) {
     Label ok;
     int receiver_offset = info->scope()->num_parameters() * kPointerSize;
     __ ldr(r2, MemOperand(sp, receiver_offset));
@@ -216,8 +216,9 @@ void FullCodeGenerator::Generate() {
     __ str(r0, MemOperand(fp, StandardFrameConstants::kContextOffset));
     // Copy any necessary parameters into the context.
     int num_parameters = info->scope()->num_parameters();
-    for (int i = 0; i < num_parameters; i++) {
-      Variable* var = scope()->parameter(i);
+    int first_parameter = info->scope()->has_this_declaration() ? -1 : 0;
+    for (int i = first_parameter; i < num_parameters; i++) {
+      Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
       if (var->IsContextSlot()) {
         int parameter_offset = StandardFrameConstants::kCallerSPOffset +
             (num_parameters - 1 - i) * kPointerSize;
@@ -3045,8 +3046,9 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
   __ ldr(r4, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
 
   // r3: the receiver of the enclosing function.
-  int receiver_offset = 2 + info_->scope()->num_parameters();
-  __ ldr(r3, MemOperand(fp, receiver_offset * kPointerSize));
+  Variable* this_var = scope()->LookupThis();
+  DCHECK_NOT_NULL(this_var);
+  __ ldr(r3, VarOperand(this_var, r3));
 
   // r2: language mode.
   __ mov(r2, Operand(Smi::FromInt(language_mode())));
index 90eb4f7..a5707c3 100644 (file)
@@ -121,7 +121,7 @@ bool LCodeGen::GeneratePrologue() {
     // global proxy when called as functions (without an explicit receiver
     // object).
     if (is_sloppy(info_->language_mode()) && info()->MayUseThis() &&
-        !info_->is_native()) {
+        !info_->is_native() && info_->scope()->has_this_declaration()) {
       Label ok;
       int receiver_offset = info_->scope()->num_parameters() * kPointerSize;
       __ ldr(r2, MemOperand(sp, receiver_offset));
@@ -197,8 +197,9 @@ bool LCodeGen::GeneratePrologue() {
     __ str(r0, MemOperand(fp, StandardFrameConstants::kContextOffset));
     // Copy any necessary parameters into the context.
     int num_parameters = scope()->num_parameters();
-    for (int i = 0; i < num_parameters; i++) {
-      Variable* var = scope()->parameter(i);
+    int first_parameter = scope()->has_this_declaration() ? -1 : 0;
+    for (int i = first_parameter; i < num_parameters; i++) {
+      Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
       if (var->IsContextSlot()) {
         int parameter_offset = StandardFrameConstants::kCallerSPOffset +
             (num_parameters - 1 - i) * kPointerSize;
index 6692c12..2557de1 100644 (file)
@@ -125,7 +125,7 @@ void FullCodeGenerator::Generate() {
   // global proxy when called as functions (without an explicit receiver
   // object).
   if (is_sloppy(info->language_mode()) && !info->is_native() &&
-      info->MayUseThis()) {
+      info->MayUseThis() && info->scope()->has_this_declaration()) {
     Label ok;
     int receiver_offset = info->scope()->num_parameters() * kXRegSize;
     __ Peek(x10, receiver_offset);
@@ -217,8 +217,9 @@ void FullCodeGenerator::Generate() {
     __ Str(x0, MemOperand(fp, StandardFrameConstants::kContextOffset));
     // Copy any necessary parameters into the context.
     int num_parameters = info->scope()->num_parameters();
-    for (int i = 0; i < num_parameters; i++) {
-      Variable* var = scope()->parameter(i);
+    int first_parameter = info->scope()->has_this_declaration() ? -1 : 0;
+    for (int i = first_parameter; i < num_parameters; i++) {
+      Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
       if (var->IsContextSlot()) {
         int parameter_offset = StandardFrameConstants::kCallerSPOffset +
             (num_parameters - 1 - i) * kPointerSize;
@@ -2733,8 +2734,9 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
 
   __ Ldr(x10, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
   // Prepare to push the receiver of the enclosing function.
-  int receiver_offset = 2 + info_->scope()->num_parameters();
-  __ Ldr(x11, MemOperand(fp, receiver_offset * kPointerSize));
+  Variable* this_var = scope()->LookupThis();
+  DCHECK_NOT_NULL(this_var);
+  __ Ldr(x11, VarOperand(this_var, x11));
 
   // Prepare to push the language mode.
   __ Mov(x12, Smi::FromInt(language_mode()));
index c833872..5782529 100644 (file)
@@ -669,7 +669,7 @@ bool LCodeGen::GeneratePrologue() {
     // global proxy when called as functions (without an explicit receiver
     // object).
     if (is_sloppy(info_->language_mode()) && info()->MayUseThis() &&
-        !info_->is_native()) {
+        !info()->is_native() && info()->scope()->has_this_declaration()) {
       Label ok;
       int receiver_offset = info_->scope()->num_parameters() * kXRegSize;
       __ Peek(x10, receiver_offset);
@@ -728,8 +728,9 @@ bool LCodeGen::GeneratePrologue() {
     __ Str(x0, MemOperand(fp, StandardFrameConstants::kContextOffset));
     // Copy any necessary parameters into the context.
     int num_parameters = scope()->num_parameters();
-    for (int i = 0; i < num_parameters; i++) {
-      Variable* var = scope()->parameter(i);
+    int first_parameter = scope()->has_this_declaration() ? -1 : 0;
+    for (int i = first_parameter; i < num_parameters; i++) {
+      Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
       if (var->IsContextSlot()) {
         Register value = x0;
         Register scratch = x3;
index a0e93d7..1517cdb 100644 (file)
@@ -223,8 +223,7 @@ bool CompilationInfo::is_simple_parameter_list() {
 
 
 bool CompilationInfo::MayUseThis() const {
-  return scope()->uses_this() || scope()->inner_uses_this() ||
-         scope()->calls_sloppy_eval();
+  return scope()->has_this_declaration() && scope()->receiver()->is_used();
 }
 
 
index 57773d1..2d92ca5 100644 (file)
@@ -512,15 +512,21 @@ bool AstGraphBuilder::CreateGraph(bool constant_context, bool stack_check) {
 
   // Build receiver check for sloppy mode if necessary.
   // TODO(mstarzinger/verwaest): Should this be moved back into the CallIC?
-  Node* original_receiver = env.Lookup(scope->receiver());
-  Node* patched_receiver = BuildPatchReceiverToGlobalProxy(original_receiver);
-  env.Bind(scope->receiver(), patched_receiver);
+  Node* patched_receiver = nullptr;
+  if (scope->has_this_declaration()) {
+    Node* original_receiver = NewNode(common()->Parameter(0), graph()->start());
+    patched_receiver = BuildPatchReceiverToGlobalProxy(original_receiver);
+    if (scope->receiver()->IsStackAllocated()) {
+      env.Bind(scope->receiver(), patched_receiver);
+    }
+  }
 
   // Build function context only if there are context allocated variables.
   int heap_slots = info()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
   if (heap_slots > 0) {
     // Push a new inner context scope for the function.
-    Node* inner_context = BuildLocalFunctionContext(function_context_.get());
+    Node* inner_context =
+        BuildLocalFunctionContext(function_context_.get(), patched_receiver);
     ContextScope top_context(this, scope, inner_context);
     CreateGraphBody(stack_check);
   } else {
@@ -2251,7 +2257,23 @@ void AstGraphBuilder::VisitCall(Call* expr) {
     // Create node to ask for help resolving potential eval call. This will
     // provide a fully resolved callee and the corresponding receiver.
     Node* function = GetFunctionClosure();
-    Node* receiver = environment()->Lookup(info()->scope()->receiver());
+    // TODO(wingo): ResolvePossibleDirectEval doesn't really need a receiver,
+    // now that eval scopes don't have "this" declarations.  Remove this hack
+    // once ResolvePossibleDirectEval changes.
+    Node* receiver;
+    {
+      Variable* variable = info()->scope()->LookupThis();
+      if (variable->IsStackAllocated()) {
+        receiver = environment()->Lookup(variable);
+      } else {
+        DCHECK(variable->IsContextSlot());
+        int depth = current_scope()->ContextChainLength(variable->scope());
+        bool immutable = variable->maybe_assigned() == kNotAssigned;
+        const Operator* op =
+            javascript()->LoadContext(depth, variable->index(), immutable);
+        receiver = NewNode(op, current_context());
+      }
+    }
     Node* language = jsgraph()->Constant(language_mode());
     Node* position = jsgraph()->Constant(info()->scope()->start_position());
     const Operator* op =
@@ -2731,9 +2753,7 @@ Node* AstGraphBuilder::BuildPatchReceiverToGlobalProxy(Node* receiver) {
   // object). Otherwise there is nothing left to do here.
   if (is_strict(language_mode()) || info()->is_native()) return receiver;
 
-  // There is no need to perform patching if the receiver is never used. Note
-  // that scope predicates are purely syntactical, a call to eval might still
-  // inspect the receiver value.
+  // There is no need to perform patching if the receiver will never be used.
   if (!info()->MayUseThis()) return receiver;
 
   IfBuilder receiver_check(this);
@@ -2750,25 +2770,36 @@ Node* AstGraphBuilder::BuildPatchReceiverToGlobalProxy(Node* receiver) {
 }
 
 
-Node* AstGraphBuilder::BuildLocalFunctionContext(Node* context) {
+Node* AstGraphBuilder::BuildLocalFunctionContext(Node* context,
+                                                 Node* patched_receiver) {
+  Scope* scope = info()->scope();
   Node* closure = GetFunctionClosure();
 
   // Allocate a new local context.
   Node* local_context =
-      info()->scope()->is_script_scope()
-          ? BuildLocalScriptContext(info()->scope())
+      scope->is_script_scope()
+          ? BuildLocalScriptContext(scope)
           : NewNode(javascript()->CreateFunctionContext(), closure);
 
+  if (scope->has_this_declaration() && scope->receiver()->IsContextSlot()) {
+    DCHECK_NOT_NULL(patched_receiver);
+    // Context variable (at bottom of the context chain).
+    Variable* variable = scope->receiver();
+    DCHECK_EQ(0, scope->ContextChainLength(variable->scope()));
+    const Operator* op = javascript()->StoreContext(0, variable->index());
+    NewNode(op, local_context, patched_receiver);
+  }
+
   // Copy parameters into context if necessary.
-  int num_parameters = info()->scope()->num_parameters();
+  int num_parameters = scope->num_parameters();
   for (int i = 0; i < num_parameters; i++) {
-    Variable* variable = info()->scope()->parameter(i);
+    Variable* variable = scope->parameter(i);
     if (!variable->IsContextSlot()) continue;
     // Temporary parameter node. The parameter indices are shifted by 1
     // (receiver is parameter index -1 but environment index 0).
     Node* parameter = NewNode(common()->Parameter(i + 1), graph()->start());
     // Context variable (at bottom of the context chain).
-    DCHECK_EQ(0, info()->scope()->ContextChainLength(variable->scope()));
+    DCHECK_EQ(0, scope->ContextChainLength(variable->scope()));
     const Operator* op = javascript()->StoreContext(0, variable->index());
     NewNode(op, local_context, parameter);
   }
index 5b4ac98..ce959b2 100644 (file)
@@ -244,7 +244,7 @@ class AstGraphBuilder : public AstVisitor {
   Node* BuildPatchReceiverToGlobalProxy(Node* receiver);
 
   // Builders to create local function, script and block contexts.
-  Node* BuildLocalFunctionContext(Node* context);
+  Node* BuildLocalFunctionContext(Node* context, Node* patched_receiver);
   Node* BuildLocalScriptContext(Scope* scope);
   Node* BuildLocalBlockContext(Scope* scope);
 
index a6a61df..12eb871 100644 (file)
@@ -258,8 +258,13 @@ Handle<Object> Context::Lookup(Handle<String> name,
           object->IsJSContextExtensionObject()) {
         maybe = JSReceiver::GetOwnPropertyAttributes(object, name);
       } else if (context->IsWithContext()) {
-        LookupIterator it(object, name);
-        maybe = UnscopableLookup(&it);
+        // A with context will never bind "this".
+        if (name->Equals(*isolate->factory()->this_string())) {
+          maybe = Just(ABSENT);
+        } else {
+          LookupIterator it(object, name);
+          maybe = UnscopableLookup(&it);
+        }
       } else {
         maybe = JSReceiver::GetPropertyAttributes(object, name);
       }
index d1e80e1..34c5c60 100644 (file)
@@ -232,6 +232,7 @@ namespace internal {
   V(source_string, "source")                                   \
   V(source_url_string, "source_url")                           \
   V(source_mapping_url_string, "source_mapping_url")           \
+  V(this_string, "this")                                       \
   V(global_string, "global")                                   \
   V(ignore_case_string, "ignoreCase")                          \
   V(multiline_string, "multiline")                             \
index 0c77d81..eaa1c48 100644 (file)
@@ -210,8 +210,9 @@ void FullCodeGenerator::Generate() {
 
     // Copy parameters into context if necessary.
     int num_parameters = info->scope()->num_parameters();
-    for (int i = 0; i < num_parameters; i++) {
-      Variable* var = scope()->parameter(i);
+    int first_parameter = info->scope()->has_this_declaration() ? -1 : 0;
+    for (int i = first_parameter; i < num_parameters; i++) {
+      Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
       if (var->IsContextSlot()) {
         int parameter_offset = StandardFrameConstants::kCallerSPOffset +
             (num_parameters - 1 - i) * kPointerSize;
@@ -2938,7 +2939,9 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
   // Push the enclosing function.
   __ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
   // Push the receiver of the enclosing function.
-  __ push(Operand(ebp, (2 + info_->scope()->num_parameters()) * kPointerSize));
+  Variable* this_var = scope()->LookupThis();
+  DCHECK_NOT_NULL(this_var);
+  __ push(VarOperand(this_var, ecx));
   // Push the language mode.
   __ push(Immediate(Smi::FromInt(language_mode())));
 
index 08ace7f..d25ff12 100644 (file)
@@ -141,8 +141,8 @@ bool LCodeGen::GeneratePrologue() {
     // Sloppy mode functions and builtins need to replace the receiver with the
     // global proxy when called as functions (without an explicit receiver
     // object).
-    if (is_sloppy(info_->language_mode()) && info()->MayUseThis() &&
-        !info_->is_native()) {
+    if (is_sloppy(info()->language_mode()) && info()->MayUseThis() &&
+        !info()->is_native() && info()->scope()->has_this_declaration()) {
       Label ok;
       // +1 for return address.
       int receiver_offset = (scope()->num_parameters() + 1) * kPointerSize;
@@ -275,8 +275,9 @@ bool LCodeGen::GeneratePrologue() {
 
     // Copy parameters into context if necessary.
     int num_parameters = scope()->num_parameters();
-    for (int i = 0; i < num_parameters; i++) {
-      Variable* var = scope()->parameter(i);
+    int first_parameter = scope()->has_this_declaration() ? -1 : 0;
+    for (int i = first_parameter; i < num_parameters; i++) {
+      Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
       if (var->IsContextSlot()) {
         int parameter_offset = StandardFrameConstants::kCallerSPOffset +
             (num_parameters - 1 - i) * kPointerSize;
index 061cfcf..bce34ce 100644 (file)
@@ -135,7 +135,7 @@ void FullCodeGenerator::Generate() {
   // global proxy when called as functions (without an explicit receiver
   // object).
   if (is_sloppy(info->language_mode()) && !info->is_native() &&
-      info->MayUseThis()) {
+      info->MayUseThis() && info->scope()->has_this_declaration()) {
     Label ok;
     int receiver_offset = info->scope()->num_parameters() * kPointerSize;
     __ lw(at, MemOperand(sp, receiver_offset));
@@ -225,8 +225,9 @@ void FullCodeGenerator::Generate() {
     __ sw(v0, MemOperand(fp, StandardFrameConstants::kContextOffset));
     // Copy any necessary parameters into the context.
     int num_parameters = info->scope()->num_parameters();
-    for (int i = 0; i < num_parameters; i++) {
-      Variable* var = scope()->parameter(i);
+    int first_parameter = info->scope()->has_this_declaration() ? -1 : 0;
+    for (int i = first_parameter; i < num_parameters; i++) {
+      Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
       if (var->IsContextSlot()) {
         int parameter_offset = StandardFrameConstants::kCallerSPOffset +
                                  (num_parameters - 1 - i) * kPointerSize;
@@ -3016,8 +3017,9 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
   __ lw(t2, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
 
   // t1: the receiver of the enclosing function.
-  int receiver_offset = 2 + info_->scope()->num_parameters();
-  __ lw(t1, MemOperand(fp, receiver_offset * kPointerSize));
+  Variable* this_var = scope()->LookupThis();
+  DCHECK_NOT_NULL(this_var);
+  __ lw(t1, VarOperand(this_var, t1));
 
   // t0: the language mode.
   __ li(t0, Operand(Smi::FromInt(language_mode())));
index ee05156..f070ffb 100644 (file)
@@ -143,8 +143,8 @@ bool LCodeGen::GeneratePrologue() {
     // Sloppy mode functions and builtins need to replace the receiver with the
     // global proxy when called as functions (without an explicit receiver
     // object).
-    if (is_sloppy(info_->language_mode()) && info()->MayUseThis() &&
-        !info_->is_native()) {
+    if (is_sloppy(info()->language_mode()) && info()->MayUseThis() &&
+        !info()->is_native() && info()->scope()->has_this_declaration()) {
       Label ok;
       int receiver_offset = info_->scope()->num_parameters() * kPointerSize;
       __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
@@ -217,8 +217,9 @@ bool LCodeGen::GeneratePrologue() {
     __ sw(v0, MemOperand(fp, StandardFrameConstants::kContextOffset));
     // Copy any necessary parameters into the context.
     int num_parameters = scope()->num_parameters();
-    for (int i = 0; i < num_parameters; i++) {
-      Variable* var = scope()->parameter(i);
+    int first_parameter = scope()->has_this_declaration() ? -1 : 0;
+    for (int i = first_parameter; i < num_parameters; i++) {
+      Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
       if (var->IsContextSlot()) {
         int parameter_offset = StandardFrameConstants::kCallerSPOffset +
             (num_parameters - 1 - i) * kPointerSize;
index 7e1ade4..438d355 100644 (file)
@@ -135,7 +135,7 @@ void FullCodeGenerator::Generate() {
   // global proxy when called as functions (without an explicit receiver
   // object).
   if (is_sloppy(info->language_mode()) && !info->is_native() &&
-      info->MayUseThis()) {
+      info->MayUseThis() && info->scope()->has_this_declaration()) {
     Label ok;
     int receiver_offset = info->scope()->num_parameters() * kPointerSize;
     __ ld(at, MemOperand(sp, receiver_offset));
@@ -222,8 +222,9 @@ void FullCodeGenerator::Generate() {
     __ sd(v0, MemOperand(fp, StandardFrameConstants::kContextOffset));
     // Copy any necessary parameters into the context.
     int num_parameters = info->scope()->num_parameters();
-    for (int i = 0; i < num_parameters; i++) {
-      Variable* var = scope()->parameter(i);
+    int first_parameter = info->scope()->has_this_declaration() ? -1 : 0;
+    for (int i = first_parameter; i < num_parameters; i++) {
+      Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
       if (var->IsContextSlot()) {
         int parameter_offset = StandardFrameConstants::kCallerSPOffset +
                                  (num_parameters - 1 - i) * kPointerSize;
@@ -3018,8 +3019,9 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
   __ ld(a6, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
 
   // a5: the receiver of the enclosing function.
-  int receiver_offset = 2 + info_->scope()->num_parameters();
-  __ ld(a5, MemOperand(fp, receiver_offset * kPointerSize));
+  Variable* this_var = scope()->LookupThis();
+  DCHECK_NOT_NULL(this_var);
+  __ ld(a5, VarOperand(this_var, a5));
 
   // a4: the language mode.
   __ li(a4, Operand(Smi::FromInt(language_mode())));
index b0a0a8f..edc2d44 100644 (file)
@@ -118,8 +118,8 @@ bool LCodeGen::GeneratePrologue() {
     // Sloppy mode functions and builtins need to replace the receiver with the
     // global proxy when called as functions (without an explicit receiver
     // object).
-    if (is_sloppy(info_->language_mode()) && info()->MayUseThis() &&
-        !info_->is_native()) {
+    if (is_sloppy(info()->language_mode()) && info()->MayUseThis() &&
+        !info()->is_native() && info()->scope()->has_this_declaration()) {
       Label ok;
       int receiver_offset = info_->scope()->num_parameters() * kPointerSize;
       __ LoadRoot(at, Heap::kUndefinedValueRootIndex);
@@ -192,8 +192,9 @@ bool LCodeGen::GeneratePrologue() {
     __ sd(v0, MemOperand(fp, StandardFrameConstants::kContextOffset));
     // Copy any necessary parameters into the context.
     int num_parameters = scope()->num_parameters();
-    for (int i = 0; i < num_parameters; i++) {
-      Variable* var = scope()->parameter(i);
+    int first_parameter = scope()->has_this_declaration() ? -1 : 0;
+    for (int i = first_parameter; i < num_parameters; i++) {
+      Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
       if (var->IsContextSlot()) {
         int parameter_offset = StandardFrameConstants::kCallerSPOffset +
             (num_parameters - 1 - i) * kPointerSize;
index d6cbd4c..9aec541 100644 (file)
@@ -4269,6 +4269,13 @@ class ScopeInfo : public FixedArray {
   // no contexts are allocated for this scope ContextLength returns 0.
   int ContextLength();
 
+  // Does this scope declare a "this" binding?
+  bool HasReceiver();
+
+  // Does this scope declare a "this" binding, and the "this" binding is stack-
+  // or context-allocated?
+  bool HasAllocatedReceiver();
+
   // Is this scope the scope of a named function expression?
   bool HasFunctionName();
 
@@ -4349,6 +4356,11 @@ class ScopeInfo : public FixedArray {
   // must be an internalized string.
   int FunctionContextSlotIndex(String* name, VariableMode* mode);
 
+  // Lookup support for serialized scope info.  Returns the receiver context
+  // slot index if scope has a "this" binding, and the binding is
+  // context-allocated.  Otherwise returns a value < 0.
+  int ReceiverContextSlotIndex();
+
   bool block_scope_is_class_scope();
   FunctionKind function_kind();
 
@@ -4436,7 +4448,10 @@ class ScopeInfo : public FixedArray {
   // 7. StrongModeFreeVariablePositionEntries:
   //    Stores the locations (start and end position) of strong mode free
   //    variables.
-  // 8. FunctionNameEntryIndex:
+  // 8. RecieverEntryIndex:
+  //    If the scope binds a "this" value, one slot is reserved to hold the
+  //    context or stack slot index for the variable.
+  // 9. FunctionNameEntryIndex:
   //    If the scope belongs to a named function expression this part contains
   //    information about the function variable. It always occupies two array
   //    slots:  a. The name of the function variable.
@@ -4448,25 +4463,29 @@ class ScopeInfo : public FixedArray {
   int ContextLocalInfoEntriesIndex();
   int StrongModeFreeVariableNameEntriesIndex();
   int StrongModeFreeVariablePositionEntriesIndex();
+  int ReceiverEntryIndex();
   int FunctionNameEntryIndex();
 
-  // Location of the function variable for named function expressions.
-  enum FunctionVariableInfo {
-    NONE,     // No function name present.
-    STACK,    // Function
-    CONTEXT,
-    UNUSED
-  };
+  // Used for the function name variable for named function expressions, and for
+  // the receiver.
+  enum VariableAllocationInfo { NONE, STACK, CONTEXT, UNUSED };
 
   // Properties of scopes.
   class ScopeTypeField : public BitField<ScopeType, 0, 4> {};
-  class CallsEvalField : public BitField<bool, 4, 1> {};
+  class CallsEvalField : public BitField<bool, ScopeTypeField::kNext, 1> {};
   STATIC_ASSERT(LANGUAGE_END == 3);
-  class LanguageModeField : public BitField<LanguageMode, 5, 2> {};
-  class FunctionVariableField : public BitField<FunctionVariableInfo, 7, 2> {};
-  class FunctionVariableMode : public BitField<VariableMode, 9, 3> {};
-  class AsmModuleField : public BitField<bool, 12, 1> {};
-  class AsmFunctionField : public BitField<bool, 13, 1> {};
+  class LanguageModeField
+      : public BitField<LanguageMode, CallsEvalField::kNext, 2> {};
+  class ReceiverVariableField
+      : public BitField<VariableAllocationInfo, LanguageModeField::kNext, 2> {};
+  class FunctionVariableField
+      : public BitField<VariableAllocationInfo, ReceiverVariableField::kNext,
+                        2> {};
+  class FunctionVariableMode
+      : public BitField<VariableMode, FunctionVariableField::kNext, 3> {};
+  class AsmModuleField : public BitField<bool, FunctionVariableMode::kNext, 1> {
+  };
+  class AsmFunctionField : public BitField<bool, AsmModuleField::kNext, 1> {};
   class IsSimpleParameterListField
       : public BitField<bool, AsmFunctionField::kNext, 1> {};
   class BlockScopeIsClassScopeField
index aec87ca..374b59c 100644 (file)
@@ -742,7 +742,9 @@ const AstRawString* ParserTraits::GetNextSymbol(Scanner* scanner) {
 
 Expression* ParserTraits::ThisExpression(Scope* scope, AstNodeFactory* factory,
                                          int pos) {
-  return factory->NewVariableProxy(scope->receiver(), pos);
+  return scope->NewUnresolved(factory,
+                              parser_->ast_value_factory()->this_string(),
+                              Variable::THIS, pos, pos + 4);
 }
 
 Expression* ParserTraits::SuperReference(Scope* scope, AstNodeFactory* factory,
@@ -790,7 +792,8 @@ Expression* ParserTraits::ExpressionFromIdentifier(const AstRawString* name,
                                                    Scope* scope,
                                                    AstNodeFactory* factory) {
   if (parser_->fni_ != NULL) parser_->fni_->PushVariableName(name);
-  return scope->NewUnresolved(factory, name, start_position, end_position);
+  return scope->NewUnresolved(factory, name, Variable::NORMAL, start_position,
+                              end_position);
 }
 
 
@@ -969,6 +972,8 @@ FunctionLiteral* Parser::DoParseProgram(ParseInfo* info) {
 
   FunctionLiteral* result = NULL;
   {
+    // TODO(wingo): Add an outer GLOBAL_SCOPE corresponding to the native
+    // context, which will have the "this" binding for script scopes.
     Scope* scope = NewScope(scope_, SCRIPT_SCOPE);
     info->set_script_scope(scope);
     if (!info->context().is_null() && !info->context()->IsNativeContext()) {
@@ -1923,9 +1928,9 @@ VariableProxy* Parser::NewUnresolved(const AstRawString* name,
   // scope.
   // Let/const variables in harmony mode are always added to the immediately
   // enclosing scope.
-  return DeclarationScope(mode)->NewUnresolved(factory(), name,
-                                               scanner()->location().beg_pos,
-                                               scanner()->location().end_pos);
+  return DeclarationScope(mode)->NewUnresolved(
+      factory(), name, Variable::NORMAL, scanner()->location().beg_pos,
+      scanner()->location().end_pos);
 }
 
 
@@ -3446,8 +3451,9 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
         Expression* enumerable = ParseExpression(true, CHECK_OK);
         Expect(Token::RPAREN, CHECK_OK);
 
-        VariableProxy* each = scope_->NewUnresolved(
-            factory(), parsing_result.SingleName(), each_beg_pos, each_end_pos);
+        VariableProxy* each =
+            scope_->NewUnresolved(factory(), parsing_result.SingleName(),
+                                  Variable::NORMAL, each_beg_pos, each_end_pos);
         Statement* body = ParseSubStatement(NULL, CHECK_OK);
         InitializeForEachStatement(loop, each, enumerable, body);
         Block* result =
@@ -3530,8 +3536,9 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
         scope_ = for_scope;
         Expect(Token::RPAREN, CHECK_OK);
 
-        VariableProxy* each = scope_->NewUnresolved(
-            factory(), parsing_result.SingleName(), each_end_pos);
+        VariableProxy* each =
+            scope_->NewUnresolved(factory(), parsing_result.SingleName(),
+                                  Variable::NORMAL, each_end_pos);
         Statement* body = ParseSubStatement(NULL, CHECK_OK);
         Block* body_block =
             factory()->NewBlock(NULL, 3, false, RelocInfo::kNoPosition);
@@ -5754,9 +5761,8 @@ Expression* Parser::SpreadCall(Expression* function,
     if (function->IsProperty()) {
       // Method calls
       if (function->AsProperty()->IsSuperAccess()) {
-        VariableProxy* original_home =
-            function->AsProperty()->obj()->AsSuperReference()->this_var();
-        VariableProxy* home = factory()->NewVariableProxy(original_home->var());
+        Expression* home =
+            ThisExpression(scope_, factory(), RelocInfo::kNoPosition);
         args->InsertAt(0, function, zone());
         args->InsertAt(1, home, zone());
       } else {
index 877d4d0..bf8424d 100644 (file)
@@ -124,7 +124,7 @@ void FullCodeGenerator::Generate() {
   // global proxy when called as functions (without an explicit receiver
   // object).
   if (is_sloppy(info->language_mode()) && !info->is_native() &&
-      info->MayUseThis()) {
+      info->MayUseThis() && info->scope()->has_this_declaration()) {
     Label ok;
     int receiver_offset = info->scope()->num_parameters() * kPointerSize;
     __ LoadP(r5, MemOperand(sp, receiver_offset), r0);
@@ -221,8 +221,9 @@ void FullCodeGenerator::Generate() {
     __ StoreP(r3, MemOperand(fp, StandardFrameConstants::kContextOffset));
     // Copy any necessary parameters into the context.
     int num_parameters = info->scope()->num_parameters();
-    for (int i = 0; i < num_parameters; i++) {
-      Variable* var = scope()->parameter(i);
+    int first_parameter = info->scope()->has_this_declaration() ? -1 : 0;
+    for (int i = first_parameter; i < num_parameters; i++) {
+      Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
       if (var->IsContextSlot()) {
         int parameter_offset = StandardFrameConstants::kCallerSPOffset +
                                (num_parameters - 1 - i) * kPointerSize;
@@ -3028,8 +3029,9 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
   __ LoadP(r7, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
 
   // r6: the receiver of the enclosing function.
-  int receiver_offset = 2 + info_->scope()->num_parameters();
-  __ LoadP(r6, MemOperand(fp, receiver_offset * kPointerSize), r0);
+  Variable* this_var = scope()->LookupThis();
+  DCHECK_NOT_NULL(this_var);
+  GetVar(r6, this_var);
 
   // r5: language mode.
   __ LoadSmiLiteral(r5, Smi::FromInt(language_mode()));
index 5b5a58b..6676909 100644 (file)
@@ -118,8 +118,8 @@ bool LCodeGen::GeneratePrologue() {
     // Sloppy mode functions and builtins need to replace the receiver with the
     // global proxy when called as functions (without an explicit receiver
     // object).
-    if (is_sloppy(info_->language_mode()) && info()->MayUseThis() &&
-        !info_->is_native()) {
+    if (is_sloppy(info_->language_mode()) && info_->MayUseThis() &&
+        !info_->is_native() && info_->scope()->has_this_declaration()) {
       Label ok;
       int receiver_offset = info_->scope()->num_parameters() * kPointerSize;
       __ LoadP(r5, MemOperand(sp, receiver_offset));
@@ -199,8 +199,9 @@ bool LCodeGen::GeneratePrologue() {
     __ StoreP(r3, MemOperand(fp, StandardFrameConstants::kContextOffset));
     // Copy any necessary parameters into the context.
     int num_parameters = scope()->num_parameters();
-    for (int i = 0; i < num_parameters; i++) {
-      Variable* var = scope()->parameter(i);
+    int first_parameter = scope()->has_this_declaration() ? -1 : 0;
+    for (int i = first_parameter; i < num_parameters; i++) {
+      Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
       if (var->IsContextSlot()) {
         int parameter_offset = StandardFrameConstants::kCallerSPOffset +
                                (num_parameters - 1 - i) * kPointerSize;
index 443354f..c870c9b 100644 (file)
@@ -2304,7 +2304,6 @@ ParserBase<Traits>::ParsePrimaryExpression(ExpressionClassifier* classifier,
           break;
         }
       }
-      scope_->RecordThisUsage();
       result = this->ThisExpression(scope_, factory(), beg_pos);
       break;
     }
@@ -3401,7 +3400,6 @@ ParserBase<Traits>::ParseStrongInitializationExpression(
   Consume(Token::THIS);
   int pos = position();
   function_state_->set_this_location(scanner()->location());
-  scope_->RecordThisUsage();
   ExpressionT this_expr = this->ThisExpression(scope_, factory(), pos);
 
   ExpressionT left = this->EmptyExpression();
index b7664ad..3376e8c 100644 (file)
@@ -859,6 +859,47 @@ static bool ParameterIsShadowedByContextLocal(Handle<ScopeInfo> info,
 }
 
 
+MUST_USE_RESULT
+static MaybeHandle<Context> MaterializeReceiver(Isolate* isolate,
+                                                Handle<Context> target,
+                                                Handle<JSFunction> function,
+                                                JavaScriptFrame* frame) {
+  Handle<SharedFunctionInfo> shared(function->shared());
+  Handle<ScopeInfo> scope_info(shared->scope_info());
+  Handle<Object> receiver;
+  switch (scope_info->scope_type()) {
+    case FUNCTION_SCOPE: {
+      VariableMode variable_mode;
+      InitializationFlag init_flag;
+      MaybeAssignedFlag maybe_assigned_flag;
+
+      // Don't bother creating a fake context node if "this" is in the context
+      // already.
+      if (ScopeInfo::ContextSlotIndex(
+              scope_info, isolate->factory()->this_string(), &variable_mode,
+              &init_flag, &maybe_assigned_flag) >= 0) {
+        return target;
+      }
+      receiver = Handle<Object>(frame->receiver(), isolate);
+      break;
+    }
+    case MODULE_SCOPE:
+      receiver = isolate->factory()->undefined_value();
+      break;
+    case SCRIPT_SCOPE:
+      receiver = Handle<Object>(function->global_proxy(), isolate);
+      break;
+    default:
+      // For eval code, arrow functions, and the like, there's no "this" binding
+      // to materialize.
+      return target;
+  }
+
+  return isolate->factory()->NewCatchContext(
+      function, target, isolate->factory()->this_string(), receiver);
+}
+
+
 // Create a plain JSObject which materializes the local scope for the specified
 // frame.
 MUST_USE_RESULT
@@ -1475,16 +1516,21 @@ class ScopeIterator {
       context_ = Handle<Context>();
       return;
     }
-    if (scope_type == ScopeTypeScript) seen_script_scope_ = true;
-    if (nested_scope_chain_.is_empty()) {
-      if (scope_type == ScopeTypeScript) {
-        if (context_->IsScriptContext()) {
-          context_ = Handle<Context>(context_->previous(), isolate_);
-        }
-        CHECK(context_->IsNativeContext());
-      } else {
+    if (scope_type == ScopeTypeScript) {
+      seen_script_scope_ = true;
+      if (context_->IsScriptContext()) {
         context_ = Handle<Context>(context_->previous(), isolate_);
       }
+      if (!nested_scope_chain_.is_empty()) {
+        DCHECK_EQ(nested_scope_chain_.last()->scope_type(), SCRIPT_SCOPE);
+        nested_scope_chain_.RemoveLast();
+        DCHECK(nested_scope_chain_.is_empty());
+      }
+      CHECK(context_->IsNativeContext());
+      return;
+    }
+    if (nested_scope_chain_.is_empty()) {
+      context_ = Handle<Context>(context_->previous(), isolate_);
     } else {
       if (nested_scope_chain_.last()->HasContext()) {
         DCHECK(context_->previous() != NULL);
@@ -2451,6 +2497,12 @@ class EvaluationContextBuilder {
     outer_info_ = handle(function->shared());
     Handle<Context> inner_context;
 
+    // The "this" binding, if any, can't be bound via "with".  If we need to,
+    // add another node onto the outer context to bind "this".
+    if (!MaterializeReceiver(isolate, outer_context, function, frame)
+             .ToHandle(&outer_context))
+      return;
+
     bool stop = false;
     for (ScopeIterator it(isolate, frame, inlined_jsframe_index);
          !it.Failed() && !it.Done() && !stop; it.Next()) {
index bdb6fca..b5c46bf 100644 (file)
@@ -248,6 +248,12 @@ RUNTIME_FUNCTION(Runtime_DeclareLookupSlot) {
         JSGlobalObject::cast(context_arg->extension()), isolate);
     return DeclareGlobals(isolate, global, name, value, attr, is_var, is_const,
                           is_function);
+  } else if (context->IsScriptContext()) {
+    DCHECK(context->global_object()->IsJSGlobalObject());
+    Handle<JSGlobalObject> global(
+        JSGlobalObject::cast(context->global_object()), isolate);
+    return DeclareGlobals(isolate, global, name, value, attr, is_var, is_const,
+                          is_function);
   }
 
   if (attributes != ABSENT) {
@@ -323,8 +329,12 @@ RUNTIME_FUNCTION(Runtime_InitializeLegacyConstLookupSlot) {
   // meanwhile. If so, re-introduce the variable in the context extension.
   if (attributes == ABSENT) {
     Handle<Context> declaration_context(context_arg->declaration_context());
-    DCHECK(declaration_context->has_extension());
-    holder = handle(declaration_context->extension(), isolate);
+    if (declaration_context->IsScriptContext()) {
+      holder = handle(declaration_context->global_object(), isolate);
+    } else {
+      DCHECK(declaration_context->has_extension());
+      holder = handle(declaration_context->extension(), isolate);
+    }
     CHECK(holder->IsJSObject());
   } else {
     // For JSContextExtensionObjects, the initializer can be run multiple times
@@ -628,8 +638,12 @@ RUNTIME_FUNCTION(Runtime_NewScriptContext) {
       FindNameClash(scope_info, global_object, script_context_table);
   if (isolate->has_pending_exception()) return name_clash_result;
 
+  // Script contexts have a canonical empty function as their closure, not the
+  // anonymous closure containing the global code.  See
+  // FullCodeGenerator::PushFunctionArgumentForContextAllocation.
+  Handle<JSFunction> closure(native_context->closure());
   Handle<Context> result =
-      isolate->factory()->NewScriptContext(function, scope_info);
+      isolate->factory()->NewScriptContext(closure, scope_info);
 
   DCHECK(function->context() == isolate->context());
   DCHECK(function->context()->global_object() == result->global_object());
index 0127449..4a0254b 100644 (file)
@@ -32,8 +32,24 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
   bool simple_parameter_list =
       scope->is_function_scope() ? scope->is_simple_parameter_list() : true;
 
+  // Determine use and location of the "this" binding if it is present.
+  VariableAllocationInfo receiver_info;
+  if (scope->has_this_declaration()) {
+    Variable* var = scope->receiver();
+    if (!var->is_used()) {
+      receiver_info = UNUSED;
+    } else if (var->IsContextSlot()) {
+      receiver_info = CONTEXT;
+    } else {
+      DCHECK(var->IsParameter());
+      receiver_info = STACK;
+    }
+  } else {
+    receiver_info = NONE;
+  }
+
   // Determine use and location of the function variable if it is present.
-  FunctionVariableInfo function_name_info;
+  VariableAllocationInfo function_name_info;
   VariableMode function_variable_mode;
   if (scope->is_function_scope() && scope->function() != NULL) {
     Variable* var = scope->function()->proxy()->var();
@@ -52,11 +68,12 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
   }
 
   const bool has_function_name = function_name_info != NONE;
+  const bool has_receiver = receiver_info == STACK || receiver_info == CONTEXT;
   const int parameter_count = scope->num_parameters();
   const int length = kVariablePartIndex + parameter_count +
                      (1 + stack_local_count) + 2 * context_local_count +
                      3 * strong_mode_free_variable_count +
-                     (has_function_name ? 2 : 0);
+                     (has_receiver ? 1 : 0) + (has_function_name ? 2 : 0);
 
   Factory* factory = isolate->factory();
   Handle<ScopeInfo> scope_info = factory->NewScopeInfo(length);
@@ -65,6 +82,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()) |
+              ReceiverVariableField::encode(receiver_info) |
               FunctionVariableField::encode(function_name_info) |
               FunctionVariableMode::encode(function_variable_mode) |
               AsmModuleField::encode(scope->asm_module()) |
@@ -146,6 +164,15 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
     scope_info->set(index++, *end_position);
   }
 
+  // If the receiver is allocated, add its index.
+  DCHECK(index == scope_info->ReceiverEntryIndex());
+  if (has_receiver) {
+    int var_index = scope->receiver()->index();
+    scope_info->set(index++, Smi::FromInt(var_index));
+    // ?? DCHECK(receiver_info != CONTEXT || var_index ==
+    // scope_info->ContextLength() - 1);
+  }
+
   // If present, add the function variable name and its index.
   DCHECK(index == scope_info->FunctionNameEntryIndex());
   if (has_function_name) {
@@ -220,6 +247,25 @@ int ScopeInfo::ContextLength() {
 }
 
 
+bool ScopeInfo::HasReceiver() {
+  if (length() > 0) {
+    return NONE != ReceiverVariableField::decode(Flags());
+  } else {
+    return false;
+  }
+}
+
+
+bool ScopeInfo::HasAllocatedReceiver() {
+  if (length() > 0) {
+    VariableAllocationInfo allocation = ReceiverVariableField::decode(Flags());
+    return allocation == STACK || allocation == CONTEXT;
+  } else {
+    return false;
+  }
+}
+
+
 bool ScopeInfo::HasFunctionName() {
   if (length() > 0) {
     return NONE != FunctionVariableField::decode(Flags());
@@ -317,7 +363,8 @@ bool ScopeInfo::LocalIsSynthetic(int var) {
   // with user declarations, the current temporaries like .generator_object and
   // .result start with a dot, so we can use that as a flag. It's a hack!
   Handle<String> name(LocalName(var));
-  return name->length() > 0 && name->Get(0) == '.';
+  return (name->length() > 0 && name->Get(0) == '.') ||
+         name->Equals(*GetIsolate()->factory()->this_string());
 }
 
 
@@ -427,6 +474,13 @@ int ScopeInfo::ParameterIndex(String* name) {
 }
 
 
+int ScopeInfo::ReceiverContextSlotIndex() {
+  if (length() > 0 && ReceiverVariableField::decode(Flags()) == CONTEXT)
+    return Smi::cast(get(ReceiverEntryIndex()))->value();
+  return -1;
+}
+
+
 int ScopeInfo::FunctionContextSlotIndex(String* name, VariableMode* mode) {
   DCHECK(name->IsInternalizedString());
   DCHECK(mode != NULL);
@@ -514,12 +568,17 @@ int ScopeInfo::StrongModeFreeVariablePositionEntriesIndex() {
 }
 
 
-int ScopeInfo::FunctionNameEntryIndex() {
+int ScopeInfo::ReceiverEntryIndex() {
   return StrongModeFreeVariablePositionEntriesIndex() +
          2 * StrongModeFreeVariableCount();
 }
 
 
+int ScopeInfo::FunctionNameEntryIndex() {
+  return ReceiverEntryIndex() + (HasAllocatedReceiver() ? 1 : 0);
+}
+
+
 int ContextSlotCache::Hash(Object* data, String* name) {
   // Uses only lower 32 bits if pointers are larger.
   uintptr_t addr_hash =
index b3c163c..60c14db 100644 (file)
@@ -165,7 +165,6 @@ void Scope::SetDefaults(ScopeType scope_type, Scope* outer_scope,
   scope_calls_eval_ = false;
   scope_uses_arguments_ = false;
   scope_uses_super_property_ = false;
-  scope_uses_this_ = false;
   asm_module_ = false;
   asm_function_ = outer_scope != NULL && outer_scope->asm_module_;
   // Inherit the language mode from the parent scope.
@@ -173,7 +172,6 @@ void Scope::SetDefaults(ScopeType scope_type, Scope* outer_scope,
   outer_scope_calls_sloppy_eval_ = false;
   inner_scope_calls_eval_ = false;
   inner_scope_uses_arguments_ = false;
-  inner_scope_uses_this_ = false;
   inner_scope_uses_super_property_ = false;
   force_eager_compilation_ = false;
   force_context_allocation_ = (outer_scope != NULL && !is_function_scope())
@@ -309,22 +307,16 @@ void Scope::Initialize() {
     scope_inside_with_ = is_with_scope();
   }
 
-  // Declare convenience variables.
-  // Declare and allocate receiver (even for the script scope, and even
-  // if naccesses_ == 0).
-  // NOTE: When loading parameters in the script scope, we must take
-  // care not to access them as properties of the global object, but
-  // instead load them directly from the stack. Currently, the only
-  // such parameter is 'this' which is passed on the stack when
-  // invoking scripts
+  // Declare convenience variables and the receiver.
   if (is_declaration_scope()) {
     DCHECK(!subclass_constructor || is_function_scope());
-    Variable* var = variables_.Declare(
-        this, ast_value_factory_->this_string(),
-        subclass_constructor ? CONST : VAR, Variable::THIS,
-        subclass_constructor ? kNeedsInitialization : kCreatedInitialized);
-    var->AllocateTo(Variable::PARAMETER, -1);
-    receiver_ = var;
+    if (has_this_declaration()) {
+      Variable* var = variables_.Declare(
+          this, ast_value_factory_->this_string(),
+          subclass_constructor ? CONST : VAR, Variable::THIS,
+          subclass_constructor ? kNeedsInitialization : kCreatedInitialized);
+      receiver_ = var;
+    }
 
     if (subclass_constructor) {
       new_target_ =
@@ -333,9 +325,6 @@ void Scope::Initialize() {
       new_target_->AllocateTo(Variable::PARAMETER, -2);
       new_target_->set_is_used();
     }
-  } else {
-    DCHECK(outer_scope() != NULL);
-    receiver_ = outer_scope()->receiver();
   }
 
   if (is_function_scope() && !is_arrow_scope()) {
@@ -380,7 +369,6 @@ Scope* Scope::FinalizeBlockScope() {
   // Propagate usage flags to outer scope.
   if (uses_arguments()) outer_scope_->RecordArgumentsUsage();
   if (uses_super_property()) outer_scope_->RecordSuperPropertyUsage();
-  if (uses_this()) outer_scope_->RecordThisUsage();
   if (scope_calls_eval_) outer_scope_->RecordEvalCall();
 
   return NULL;
@@ -421,9 +409,16 @@ Variable* Scope::LookupLocal(const AstRawString* name) {
     maybe_assigned_flag = kMaybeAssigned;
   }
 
-  // TODO(marja, rossberg): Declare variables of the right Kind.
-  Variable* var = variables_.Declare(this, name, mode, Variable::NORMAL,
-                                     init_flag, maybe_assigned_flag);
+  Variable::Kind kind = Variable::NORMAL;
+  if (location == Variable::CONTEXT &&
+      index == scope_info_->ReceiverContextSlotIndex()) {
+    kind = Variable::THIS;
+  }
+  // TODO(marja, rossberg): Correctly declare FUNCTION, CLASS, NEW_TARGET, and
+  // ARGUMENTS bindings as their corresponding Variable::Kind.
+
+  Variable* var = variables_.Declare(this, name, mode, kind, init_flag,
+                                     maybe_assigned_flag);
   var->AllocateTo(location, index);
   return var;
 }
@@ -931,13 +926,11 @@ void Scope::Print(int n) {
   if (scope_uses_arguments_) Indent(n1, "// scope uses 'arguments'\n");
   if (scope_uses_super_property_)
     Indent(n1, "// scope uses 'super' property\n");
-  if (scope_uses_this_) Indent(n1, "// scope uses 'this'\n");
   if (inner_scope_uses_arguments_) {
     Indent(n1, "// inner scope uses 'arguments'\n");
   }
   if (inner_scope_uses_super_property_)
     Indent(n1, "// inner scope uses 'super' property\n");
-  if (inner_scope_uses_this_) Indent(n1, "// inner scope uses 'this'\n");
   if (outer_scope_calls_sloppy_eval_) {
     Indent(n1, "// outer scope calls 'eval' in sloppy context\n");
   }
@@ -1050,7 +1043,11 @@ Variable* Scope::LookupRecursive(VariableProxy* proxy,
     DCHECK(is_script_scope());
   }
 
-  if (is_with_scope()) {
+  // "this" can't be shadowed by "eval"-introduced bindings or by "with" scopes.
+  // TODO(wingo): There are other variables in this category; add them.
+  bool name_can_be_shadowed = var == nullptr || !var->is_this();
+
+  if (is_with_scope() && name_can_be_shadowed) {
     DCHECK(!already_resolved());
     // The current scope is a with scope, so the variable binding can not be
     // statically resolved. However, note that it was necessary to do a lookup
@@ -1061,7 +1058,7 @@ Variable* Scope::LookupRecursive(VariableProxy* proxy,
     if (var != NULL && proxy->is_assigned()) var->set_maybe_assigned();
     *binding_kind = DYNAMIC_LOOKUP;
     return NULL;
-  } else if (calls_sloppy_eval()) {
+  } else if (calls_sloppy_eval() && name_can_be_shadowed) {
     // A variable binding may have been found in an outer scope, but the current
     // scope makes a sloppy 'eval' call, so the found variable may not be
     // the correct one (the 'eval' may introduce a binding with the same name).
@@ -1294,9 +1291,6 @@ void Scope::PropagateScopeInfo(bool outer_scope_calls_sloppy_eval ) {
           inner->inner_scope_uses_super_property_) {
         inner_scope_uses_super_property_ = true;
       }
-      if (inner->scope_uses_this_ || inner->inner_scope_uses_this_) {
-        inner_scope_uses_this_ = true;
-      }
     }
     if (inner->force_eager_compilation_) {
       force_eager_compilation_ = true;
@@ -1417,24 +1411,40 @@ void Scope::AllocateParameterLocals(Isolate* isolate) {
       // Force context allocation of the parameter.
       var->ForceContextAllocation();
     }
+    AllocateParameter(var, i);
+  }
+}
 
-    if (MustAllocate(var)) {
-      if (MustAllocateInContext(var)) {
-        DCHECK(var->IsUnallocated() || var->IsContextSlot());
-        if (var->IsUnallocated()) {
-          AllocateHeapSlot(var);
-        }
-      } else {
-        DCHECK(var->IsUnallocated() || var->IsParameter());
-        if (var->IsUnallocated()) {
-          var->AllocateTo(Variable::PARAMETER, i);
-        }
+
+void Scope::AllocateParameter(Variable* var, int index) {
+  if (MustAllocate(var)) {
+    if (MustAllocateInContext(var)) {
+      DCHECK(var->IsUnallocated() || var->IsContextSlot());
+      if (var->IsUnallocated()) {
+        AllocateHeapSlot(var);
+      }
+    } else {
+      DCHECK(var->IsUnallocated() || var->IsParameter());
+      if (var->IsUnallocated()) {
+        var->AllocateTo(Variable::PARAMETER, index);
       }
     }
   }
 }
 
 
+void Scope::AllocateReceiver() {
+  DCHECK_NOT_NULL(receiver());
+  DCHECK_EQ(receiver()->scope(), this);
+
+  if (has_forced_context_allocation()) {
+    // Force context allocation of the receiver.
+    receiver()->ForceContextAllocation();
+  }
+  AllocateParameter(receiver(), -1);
+}
+
+
 void Scope::AllocateNonParameterLocal(Isolate* isolate, Variable* var) {
   DCHECK(var->scope() == this);
   DCHECK(!var->IsVariable(isolate->factory()->dot_result_string()) ||
@@ -1504,6 +1514,7 @@ void Scope::AllocateVariablesRecursively(Isolate* isolate) {
   // Allocate variables for this scope.
   // Parameters must be allocated first, if any.
   if (is_function_scope()) AllocateParameterLocals(isolate);
+  if (has_this_declaration()) AllocateReceiver();
   AllocateNonParameterLocals(isolate);
 
   // Force allocation of a context for this scope if necessary. For a 'with'
index 9e22d1c..01df8f9 100644 (file)
@@ -144,14 +144,15 @@ class Scope: public ZoneObject {
   // Create a new unresolved variable.
   VariableProxy* NewUnresolved(AstNodeFactory* factory,
                                const AstRawString* name,
+                               Variable::Kind kind = Variable::NORMAL,
                                int start_position = RelocInfo::kNoPosition,
                                int end_position = RelocInfo::kNoPosition) {
     // Note that we must not share the unresolved variables with
     // the same name because they may be removed selectively via
     // RemoveUnresolved().
     DCHECK(!already_resolved());
-    VariableProxy* proxy = factory->NewVariableProxy(
-        name, Variable::NORMAL, start_position, end_position);
+    VariableProxy* proxy =
+        factory->NewVariableProxy(name, kind, start_position, end_position);
     unresolved_.Add(proxy, zone_);
     return proxy;
   }
@@ -217,9 +218,6 @@ class Scope: public ZoneObject {
   // Inform the scope that the corresponding code uses "super".
   void RecordSuperPropertyUsage() { scope_uses_super_property_ = true; }
 
-  // Inform the scope that the corresponding code uses "this".
-  void RecordThisUsage() { scope_uses_this_ = true; }
-
   // Set the language mode flag (unless disabled by a global flag).
   void SetLanguageMode(LanguageMode language_mode) {
     language_mode_ = language_mode;
@@ -323,10 +321,6 @@ class Scope: public ZoneObject {
   bool inner_uses_super_property() const {
     return inner_scope_uses_super_property_;
   }
-  // Does this scope access "this".
-  bool uses_this() const { return scope_uses_this_; }
-  // Does any inner scope access "this".
-  bool inner_uses_this() const { return inner_scope_uses_this_; }
 
   const Scope* NearestOuterEvalScope() const {
     if (is_eval_scope()) return this;
@@ -346,7 +340,21 @@ class Scope: public ZoneObject {
   LanguageMode language_mode() const { return language_mode_; }
 
   // The variable corresponding to the 'this' value.
-  Variable* receiver() { return receiver_; }
+  Variable* receiver() {
+    DCHECK(has_this_declaration());
+    DCHECK_NOT_NULL(receiver_);
+    return receiver_;
+  }
+
+  Variable* LookupThis() { return Lookup(ast_value_factory_->this_string()); }
+
+  // TODO(wingo): Add a GLOBAL_SCOPE scope type which will lexically allocate
+  // "this" (and no other variable) on the native context.  Script scopes then
+  // will not have a "this" declaration.
+  bool has_this_declaration() const {
+    return (is_function_scope() && !is_arrow_scope()) || is_module_scope() ||
+           is_script_scope();
+  }
 
   // The variable corresponding to the 'new.target' value.
   Variable* new_target_var() { return new_target_; }
@@ -578,8 +586,6 @@ class Scope: public ZoneObject {
   bool scope_uses_arguments_;
   // This scope uses "super" property ('super.foo').
   bool scope_uses_super_property_;
-  // This scope uses "this".
-  bool scope_uses_this_;
   // This scope contains an "use asm" annotation.
   bool asm_module_;
   // This scope's outer context is an asm module.
@@ -595,7 +601,6 @@ class Scope: public ZoneObject {
   bool inner_scope_calls_eval_;
   bool inner_scope_uses_arguments_;
   bool inner_scope_uses_super_property_;
-  bool inner_scope_uses_this_;
   bool force_eager_compilation_;
   bool force_context_allocation_;
 
@@ -707,6 +712,8 @@ class Scope: public ZoneObject {
   void AllocateNonParameterLocal(Isolate* isolate, Variable* var);
   void AllocateNonParameterLocals(Isolate* isolate);
   void AllocateVariablesRecursively(Isolate* isolate);
+  void AllocateParameter(Variable* var, int index);
+  void AllocateReceiver();
   void AllocateModules();
 
   // Resolve and fill in the allocation information for all variables
index c0b8bd7..0c94d75 100644 (file)
@@ -59,8 +59,8 @@ bool Variable::IsGlobalObjectProperty() const {
   // Temporaries are never global, they must always be allocated in the
   // activation frame.
   return (IsDynamicVariableMode(mode_) ||
-          (IsDeclaredVariableMode(mode_) && !IsLexicalVariableMode(mode_)))
-      && scope_ != NULL && scope_->is_script_scope();
+          (IsDeclaredVariableMode(mode_) && !IsLexicalVariableMode(mode_))) &&
+         scope_ != NULL && scope_->is_script_scope() && !is_this();
 }
 
 
index 494b269..cf99785 100644 (file)
@@ -115,7 +115,7 @@ void FullCodeGenerator::Generate() {
   // global proxy when called as functions (without an explicit receiver
   // object).
   if (is_sloppy(info->language_mode()) && !info->is_native() &&
-      info->MayUseThis()) {
+      info->MayUseThis() && info->scope()->has_this_declaration()) {
     Label ok;
     // +1 for return address.
     StackArgumentsAccessor args(rsp, info->scope()->num_parameters());
@@ -209,8 +209,9 @@ void FullCodeGenerator::Generate() {
 
     // Copy any necessary parameters into the context.
     int num_parameters = info->scope()->num_parameters();
-    for (int i = 0; i < num_parameters; i++) {
-      Variable* var = scope()->parameter(i);
+    int first_parameter = info->scope()->has_this_declaration() ? -1 : 0;
+    for (int i = first_parameter; i < num_parameters; i++) {
+      Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
       if (var->IsContextSlot()) {
         int parameter_offset = StandardFrameConstants::kCallerSPOffset +
             (num_parameters - 1 - i) * kPointerSize;
@@ -2938,8 +2939,9 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
   __ Push(Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
 
   // Push the receiver of the enclosing function and do runtime call.
-  StackArgumentsAccessor args(rbp, info_->scope()->num_parameters());
-  __ Push(args.GetReceiverOperand());
+  Variable* this_var = scope()->LookupThis();
+  DCHECK_NOT_NULL(this_var);
+  __ Push(VarOperand(this_var, rcx));
 
   // Push the language mode.
   __ Push(Smi::FromInt(language_mode()));
index 2d8721e..086fd85 100644 (file)
@@ -129,8 +129,8 @@ bool LCodeGen::GeneratePrologue() {
 
     // Sloppy mode functions need to replace the receiver with the global proxy
     // when called as functions (without an explicit receiver object).
-    if (is_sloppy(info_->language_mode()) && info()->MayUseThis() &&
-        !info_->is_native()) {
+    if (is_sloppy(info()->language_mode()) && info()->MayUseThis() &&
+        !info()->is_native() && info()->scope()->has_this_declaration()) {
       Label ok;
       StackArgumentsAccessor args(rsp, scope()->num_parameters());
       __ movp(rcx, args.GetReceiverOperand());
@@ -213,8 +213,9 @@ bool LCodeGen::GeneratePrologue() {
 
     // Copy any necessary parameters into the context.
     int num_parameters = scope()->num_parameters();
-    for (int i = 0; i < num_parameters; i++) {
-      Variable* var = scope()->parameter(i);
+    int first_parameter = scope()->has_this_declaration() ? -1 : 0;
+    for (int i = first_parameter; i < num_parameters; i++) {
+      Variable* var = (i == -1) ? scope()->receiver() : scope()->parameter(i);
       if (var->IsContextSlot()) {
         int parameter_offset = StandardFrameConstants::kCallerSPOffset +
             (num_parameters - 1 - i) * kPointerSize;
index 2ad657e..16f0cf3 100644 (file)
@@ -962,7 +962,6 @@ TEST(ScopeUsesArgumentsSuperThis) {
     THIS = 1 << 2,
     INNER_ARGUMENTS = 1 << 3,
     INNER_SUPER_PROPERTY = 1 << 4,
-    INNER_THIS = 1 << 5
   };
 
   static const struct {
@@ -977,7 +976,7 @@ TEST(ScopeUsesArgumentsSuperThis) {
     {"return this + arguments[0]", ARGUMENTS | THIS},
     {"return this + arguments[0] + super.x",
      ARGUMENTS | SUPER_PROPERTY | THIS},
-    {"return x => this + x", INNER_THIS},
+    {"return x => this + x", THIS},
     {"return x => super.f() + x", INNER_SUPER_PROPERTY},
     {"this.foo = 42;", THIS},
     {"this.foo();", THIS},
@@ -991,9 +990,7 @@ TEST(ScopeUsesArgumentsSuperThis) {
     {"while (true) { while (true) { while (true) return this } }", THIS},
     {"while (true) { while (true) { while (true) return super.f() } }",
      SUPER_PROPERTY},
-    {"if (1) { return () => { while (true) new this() } }", INNER_THIS},
-    // Note that propagation of the inner_uses_this() value does not
-    // cross boundaries of normal functions onto parent scopes.
+    {"if (1) { return () => { while (true) new this() } }", THIS},
     {"return function (x) { return this + x }", NONE},
     {"return { m(x) { return super.m() + x } }", NONE},
     {"var x = function () { this.foo = 42 };", NONE},
@@ -1004,10 +1001,10 @@ TEST(ScopeUsesArgumentsSuperThis) {
     {"return { m(x) { return () => super.m() } }", NONE},
     // Flags must be correctly set when using block scoping.
     {"\"use strict\"; while (true) { let x; this, arguments; }",
-     INNER_ARGUMENTS | INNER_THIS},
+     INNER_ARGUMENTS | THIS},
     {"\"use strict\"; while (true) { let x; this, super.f(), arguments; }",
-     INNER_ARGUMENTS | INNER_SUPER_PROPERTY | INNER_THIS},
-    {"\"use strict\"; if (foo()) { let x; this.f() }", INNER_THIS},
+     INNER_ARGUMENTS | INNER_SUPER_PROPERTY | THIS},
+    {"\"use strict\"; if (foo()) { let x; this.f() }", THIS},
     {"\"use strict\"; if (foo()) { let x; super.f() }",
      INNER_SUPER_PROPERTY},
     {"\"use strict\"; if (1) {"
@@ -1071,13 +1068,15 @@ TEST(ScopeUsesArgumentsSuperThis) {
                scope->uses_arguments());
       CHECK_EQ((source_data[i].expected & SUPER_PROPERTY) != 0,
                scope->uses_super_property());
-      CHECK_EQ((source_data[i].expected & THIS) != 0, scope->uses_this());
+      if ((source_data[i].expected & THIS) != 0) {
+        // Currently the is_used() flag is conservative; all variables in a
+        // script scope are marked as used.
+        CHECK(scope->LookupThis()->is_used());
+      }
       CHECK_EQ((source_data[i].expected & INNER_ARGUMENTS) != 0,
                scope->inner_uses_arguments());
       CHECK_EQ((source_data[i].expected & INNER_SUPER_PROPERTY) != 0,
                scope->inner_uses_super_property());
-      CHECK_EQ((source_data[i].expected & INNER_THIS) != 0,
-               scope->inner_uses_this());
     }
   }
 }
index 04034a9..ae3596c 100644 (file)
@@ -628,7 +628,7 @@ UNINITIALIZED_DEPENDENT_TEST(CustomContextDeserialization,
       root =
           deserializer.DeserializePartial(isolate, global_proxy,
                                           &outdated_contexts).ToHandleChecked();
-      CHECK_EQ(2, outdated_contexts->length());
+      CHECK_EQ(3, outdated_contexts->length());
       CHECK(root->IsContext());
       Handle<Context> context = Handle<Context>::cast(root);
       CHECK(context->global_proxy() == *global_proxy);
index ad137de..1e1e22a 100644 (file)
@@ -167,6 +167,10 @@ function CheckScopeContent(content, number, exec_state) {
   if (!scope.scopeObject().property('arguments').isUndefined()) {
     scope_size--;
   }
+  // Ditto for 'this'.
+  if (!scope.scopeObject().property('this').isUndefined()) {
+    scope_size--;
+  }
   // Skip property with empty name.
   if (!scope.scopeObject().property('').isUndefined()) {
     scope_size--;
diff --git a/test/mjsunit/harmony/arrow-functions-this.js b/test/mjsunit/harmony/arrow-functions-this.js
new file mode 100644 (file)
index 0000000..2c2ad07
--- /dev/null
@@ -0,0 +1,81 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --harmony-arrow-functions
+
+var object = {};
+var global = this;
+var call = Function.call.bind(Function.call);
+
+var globalSloppyArrow = () => this;
+var globalStrictArrow = () => { "use strict"; return this; };
+var globalSloppyArrowEval = (s) => eval(s);
+var globalStrictArrowEval = (s) => { "use strict"; return eval(s); };
+
+var sloppyFunctionArrow = function() {
+  return (() => this)();
+};
+var strictFunctionArrow = function() {
+  "use strict";
+  return (() => this)();
+};
+var sloppyFunctionEvalArrow = function() {
+  return eval("(() => this)()");
+};
+var strictFunctionEvalArrow = function() {
+  "use strict";
+  return eval("(() => this)()");
+};
+var sloppyFunctionArrowEval = function(s) {
+  return (() => eval(s))();
+};
+var strictFunctionArrowEval = function(s) {
+  "use strict";
+  return (() => eval(s))();
+};
+
+var withObject = { 'this': object }
+var arrowInsideWith, arrowInsideWithEval;
+with (withObject) {
+  arrowInsideWith = () => this;
+  arrowInsideWithEval = (s) => eval(s);
+}
+
+assertEquals(global, call(globalSloppyArrow, object));
+assertEquals(global, call(globalStrictArrow, object));
+assertEquals(global, call(globalSloppyArrowEval, object, "this"));
+assertEquals(global, call(globalStrictArrowEval, object, "this"));
+assertEquals(global, call(globalSloppyArrowEval, object, "(() => this)()"));
+assertEquals(global, call(globalStrictArrowEval, object, "(() => this)()"));
+
+assertEquals(object, call(sloppyFunctionArrow, object));
+assertEquals(global, call(sloppyFunctionArrow, undefined));
+assertEquals(object, call(strictFunctionArrow, object));
+assertEquals(undefined, call(strictFunctionArrow, undefined));
+
+assertEquals(object, call(sloppyFunctionEvalArrow, object));
+assertEquals(global, call(sloppyFunctionEvalArrow, undefined));
+assertEquals(object, call(strictFunctionEvalArrow, object));
+assertEquals(undefined, call(strictFunctionEvalArrow, undefined));
+
+assertEquals(object, call(sloppyFunctionArrowEval, object, "this"));
+assertEquals(global, call(sloppyFunctionArrowEval, undefined, "this"));
+assertEquals(object, call(strictFunctionArrowEval, object, "this"));
+assertEquals(undefined, call(strictFunctionArrowEval, undefined, "this"));
+
+assertEquals(object,
+             call(sloppyFunctionArrowEval, object, "(() => this)()"));
+assertEquals(global,
+             call(sloppyFunctionArrowEval, undefined, "(() => this)()"));
+assertEquals(object,
+             call(strictFunctionArrowEval, object, "(() => this)()"));
+assertEquals(undefined,
+             call(strictFunctionArrowEval, undefined, "(() => this)()"));
+
+assertEquals(global, call(arrowInsideWith, undefined));
+assertEquals(global, call(arrowInsideWith, object));
+assertEquals(global, call(arrowInsideWithEval, undefined, "this"));
+assertEquals(global, call(arrowInsideWithEval, object, "this"));
+assertEquals(global, call(arrowInsideWithEval, undefined, "(() => this)()"));
+assertEquals(global, call(arrowInsideWithEval, object, "(() => this)()"));
index 8389696..cf4b627 100644 (file)
@@ -35,6 +35,7 @@ FLAGS_PATTERN = re.compile(r"//\s+Flags:(.*)")
 FILES_PATTERN = re.compile(r"//\s+Files:(.*)")
 SELF_SCRIPT_PATTERN = re.compile(r"//\s+Env: TEST_FILE_NAME")
 MODULE_PATTERN = re.compile(r"^// MODULE$", flags=re.MULTILINE)
+NO_HARNESS_PATTERN = re.compile(r"^// NO HARNESS$", flags=re.MULTILINE)
 
 
 class MjsunitTestSuite(testsuite.TestSuite):
@@ -79,7 +80,7 @@ class MjsunitTestSuite(testsuite.TestSuite):
       env = ["-e", "TEST_FILE_NAME=\"%s\"" % testfilename.replace("\\", "\\\\")]
       files = env + files
 
-    if not context.no_harness:
+    if not context.no_harness and not NO_HARNESS_PATTERN.search(source):
       files.append(os.path.join(self.root, "mjsunit.js"))
 
     if MODULE_PATTERN.search(source):
diff --git a/test/mjsunit/this-dynamic-lookup.js b/test/mjsunit/this-dynamic-lookup.js
new file mode 100644 (file)
index 0000000..c5fa7a2
--- /dev/null
@@ -0,0 +1,10 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --harmony-arrow-functions
+// NO HARNESS
+
+var globalEval = eval;
+globalEval("this; eval('42')");
+globalEval("eval('42'); this");