new classes: change semantics of super(...) call and add new.target to construct...
authordslomov <dslomov@chromium.org>
Thu, 22 Jan 2015 18:39:34 +0000 (10:39 -0800)
committerCommit bot <commit-bot@chromium.org>
Thu, 22 Jan 2015 18:39:47 +0000 (18:39 +0000)
R=arv@chromium.org,rossberg@chromium.org
BUG=v8:3834
LOG=N

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

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

18 files changed:
src/arm/builtins-arm.cc
src/arm/code-stubs-arm.cc
src/arm/full-codegen-arm.cc
src/arm64/builtins-arm64.cc
src/arm64/code-stubs-arm64.cc
src/arm64/full-codegen-arm64.cc
src/compiler.cc
src/flag-definitions.h
src/full-codegen.h
src/ia32/builtins-ia32.cc
src/ia32/code-stubs-ia32.cc
src/ia32/full-codegen-ia32.cc
src/runtime/runtime-object.cc
src/runtime/runtime.h
src/x64/builtins-x64.cc
src/x64/code-stubs-x64.cc
src/x64/full-codegen-x64.cc
test/mjsunit/harmony/classes-experimental.js [new file with mode: 0644]

index 8ce57d5..1a98803 100644 (file)
@@ -310,6 +310,36 @@ void Builtins::Generate_InOptimizationQueue(MacroAssembler* masm) {
 }
 
 
+static void Generate_Runtime_NewObject(MacroAssembler* masm,
+                                       bool create_memento,
+                                       Register original_constructor,
+                                       Label* count_incremented,
+                                       Label* allocated) {
+  if (create_memento) {
+    // Get the cell or allocation site.
+    __ ldr(r2, MemOperand(sp, 2 * kPointerSize));
+    __ push(r2);
+  }
+
+  __ push(r1);                    // argument for Runtime_NewObject
+  __ push(original_constructor);  // original constructor
+  if (create_memento) {
+    __ CallRuntime(Runtime::kNewObjectWithAllocationSite, 3);
+  } else {
+    __ CallRuntime(Runtime::kNewObject, 2);
+  }
+  __ mov(r4, r0);
+
+  // Runtime_NewObjectWithAllocationSite increments allocation count.
+  // Skip the increment.
+  if (create_memento) {
+    __ jmp(count_incremented);
+  } else {
+    __ jmp(allocated);
+  }
+}
+
+
 static void Generate_JSConstructStubHelper(MacroAssembler* masm,
                                            bool is_api_function,
                                            bool create_memento) {
@@ -317,6 +347,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
   //  -- r0     : number of arguments
   //  -- r1     : constructor function
   //  -- r2     : allocation site or undefined
+  //  -- r3     : original constructor
   //  -- lr     : return address
   //  -- sp[...]: constructor arguments
   // -----------------------------------
@@ -331,7 +362,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
     FrameAndConstantPoolScope scope(masm, StackFrame::CONSTRUCT);
 
     if (create_memento) {
-      __ AssertUndefinedOrAllocationSite(r2, r3);
+      __ AssertUndefinedOrAllocationSite(r2, r4);
       __ push(r2);
     }
 
@@ -340,9 +371,17 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
     __ push(r0);  // Smi-tagged arguments count.
     __ push(r1);  // Constructor function.
 
+    Label rt_call, allocated, normal_new, count_incremented;
+    __ cmp(r1, r3);
+    __ b(eq, &normal_new);
+
+    // Original constructor and function are different.
+    Generate_Runtime_NewObject(masm, create_memento, r3, &count_incremented,
+                               &allocated);
+    __ bind(&normal_new);
+
     // Try to allocate the object without transitioning into C code. If any of
     // the preconditions is not met, the code bails out to the runtime call.
-    Label rt_call, allocated;
     if (FLAG_inline_new) {
       Label undo_allocation;
       ExternalReference debug_step_in_fp =
@@ -569,27 +608,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
     // Allocate the new receiver object using the runtime call.
     // r1: constructor function
     __ bind(&rt_call);
-    if (create_memento) {
-      // Get the cell or allocation site.
-      __ ldr(r2, MemOperand(sp, 2 * kPointerSize));
-      __ push(r2);
-    }
-
-    __ push(r1);  // argument for Runtime_NewObject
-    if (create_memento) {
-      __ CallRuntime(Runtime::kNewObjectWithAllocationSite, 2);
-    } else {
-      __ CallRuntime(Runtime::kNewObject, 1);
-    }
-    __ mov(r4, r0);
-
-    // If we ended up using the runtime, and we want a memento, then the
-    // runtime call made it for us, and we shouldn't do create count
-    // increment.
-    Label count_incremented;
-    if (create_memento) {
-      __ jmp(&count_incremented);
-    }
+    Generate_Runtime_NewObject(masm, create_memento, r1, &count_incremented,
+                               &allocated);
 
     // Receiver for constructor call allocated.
     // r4: JSObject
index f19941d..f6b9f5a 100644 (file)
@@ -2562,6 +2562,9 @@ void CallConstructStub::Generate(MacroAssembler* masm) {
     __ AssertUndefinedOrAllocationSite(r2, r5);
   }
 
+  // Pass function as original constructor.
+  __ mov(r3, r1);
+
   // Jump to the function-specific construct stub.
   Register jmp_reg = r4;
   __ ldr(jmp_reg, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
index 451d43d..e5b25e3 100644 (file)
@@ -3173,11 +3173,15 @@ void FullCodeGenerator::VisitCall(Call* expr) {
       }
     }
   } else if (call_type == Call::SUPER_CALL) {
-    SuperReference* super_ref = callee->AsSuperReference();
-    EmitLoadSuperConstructor(super_ref);
-    __ Push(result_register());
-    VisitForStackValue(super_ref->this_var());
-    EmitCall(expr, CallICState::METHOD);
+    if (FLAG_experimental_classes) {
+      EmitSuperConstructorCall(expr);
+    } else {
+      SuperReference* super_ref = callee->AsSuperReference();
+      EmitLoadSuperConstructor(super_ref);
+      __ Push(result_register());
+      VisitForStackValue(super_ref->this_var());
+      EmitCall(expr, CallICState::METHOD);
+    }
   } else {
     DCHECK(call_type == Call::OTHER_CALL);
     // Call to an arbitrary expression not handled specially above.
@@ -3245,6 +3249,51 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
 }
 
 
+void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
+  SuperReference* super_ref = expr->expression()->AsSuperReference();
+  EmitLoadSuperConstructor(super_ref);
+  __ push(result_register());
+
+  // Push the arguments ("left-to-right") on the stack.
+  ZoneList<Expression*>* args = expr->arguments();
+  int arg_count = args->length();
+  for (int i = 0; i < arg_count; i++) {
+    VisitForStackValue(args->at(i));
+  }
+
+  // Call the construct call builtin that handles allocation and
+  // constructor invocation.
+  SetSourcePosition(expr->position());
+
+  // Load function and argument count into r1 and r0.
+  __ mov(r0, Operand(arg_count));
+  __ ldr(r1, MemOperand(sp, arg_count * kPointerSize));
+
+  // Record call targets in unoptimized code.
+  if (FLAG_pretenuring_call_new) {
+    UNREACHABLE();
+    /* TODO(dslomov): support pretenuring.
+    EnsureSlotContainsAllocationSite(expr->AllocationSiteFeedbackSlot());
+    DCHECK(expr->AllocationSiteFeedbackSlot().ToInt() ==
+           expr->CallNewFeedbackSlot().ToInt() + 1);
+    */
+  }
+
+  __ Move(r2, FeedbackVector());
+  __ mov(r3, Operand(SmiFromSlot(expr->CallFeedbackSlot())));
+
+  // TODO(dslomov): use a different stub and propagate new.target.
+  CallConstructStub stub(isolate(), RECORD_CONSTRUCTOR_TARGET);
+  __ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
+
+  RecordJSReturnSite(expr);
+
+  // TODO(dslomov): implement TDZ for `this`.
+  EmitVariableAssignment(super_ref->this_var()->var(), Token::ASSIGN);
+  context()->Plug(r0);
+}
+
+
 void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) {
   ZoneList<Expression*>* args = expr->arguments();
   DCHECK(args->length() == 1);
index 9f140c2..37329ab 100644 (file)
@@ -301,6 +301,33 @@ void Builtins::Generate_InOptimizationQueue(MacroAssembler* masm) {
 }
 
 
+static void Generate_Runtime_NewObject(MacroAssembler* masm,
+                                       bool create_memento,
+                                       Register original_constructor,
+                                       Label* count_incremented,
+                                       Label* allocated) {
+  if (create_memento) {
+    // Get the cell or allocation site.
+    __ Peek(x4, 2 * kXRegSize);
+    __ Push(x4);
+    __ Push(x1);  // Argument for Runtime_NewObject.
+    __ Push(original_constructor);
+    __ CallRuntime(Runtime::kNewObjectWithAllocationSite, 3);
+    __ Mov(x4, x0);
+    // If we ended up using the runtime, and we want a memento, then the
+    // runtime call made it for us, and we shouldn't do create count
+    // increment.
+    __ jmp(count_incremented);
+  } else {
+    __ Push(x1);  // Argument for Runtime_NewObject.
+    __ Push(original_constructor);
+    __ CallRuntime(Runtime::kNewObject, 2);
+    __ Mov(x4, x0);
+    __ jmp(allocated);
+  }
+}
+
+
 static void Generate_JSConstructStubHelper(MacroAssembler* masm,
                                            bool is_api_function,
                                            bool create_memento) {
@@ -308,6 +335,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
   //  -- x0     : number of arguments
   //  -- x1     : constructor function
   //  -- x2     : allocation site or undefined
+  //  -- x3    : original constructor
   //  -- lr     : return address
   //  -- sp[...]: constructor arguments
   // -----------------------------------
@@ -330,15 +358,23 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
 
     Register argc = x0;
     Register constructor = x1;
+    Register original_constructor = x3;
     // x1: constructor function
     __ SmiTag(argc);
     __ Push(argc, constructor);
     // sp[0] : Constructor function.
     // sp[1]: number of arguments (smi-tagged)
 
+    Label rt_call, count_incremented, allocated, normal_new;
+    __ Cmp(constructor, original_constructor);
+    __ B(eq, &normal_new);
+    Generate_Runtime_NewObject(masm, create_memento, original_constructor,
+                               &count_incremented, &allocated);
+
+    __ Bind(&normal_new);
+
     // Try to allocate the object without transitioning into C code. If any of
     // the preconditions is not met, the code bails out to the runtime call.
-    Label rt_call, allocated;
     if (FLAG_inline_new) {
       Label undo_allocation;
       ExternalReference debug_step_in_fp =
@@ -535,23 +571,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
 
     // Allocate the new receiver object using the runtime call.
     __ Bind(&rt_call);
-    Label count_incremented;
-    if (create_memento) {
-      // Get the cell or allocation site.
-      __ Peek(x4, 2 * kXRegSize);
-      __ Push(x4);
-      __ Push(constructor);  // Argument for Runtime_NewObject.
-      __ CallRuntime(Runtime::kNewObjectWithAllocationSite, 2);
-      __ Mov(x4, x0);
-      // If we ended up using the runtime, and we want a memento, then the
-      // runtime call made it for us, and we shouldn't do create count
-      // increment.
-      __ jmp(&count_incremented);
-    } else {
-      __ Push(constructor);  // Argument for Runtime_NewObject.
-      __ CallRuntime(Runtime::kNewObject, 1);
-      __ Mov(x4, x0);
-    }
+    Generate_Runtime_NewObject(masm, create_memento, constructor,
+                               &count_incremented, &allocated);
 
     // Receiver for constructor call allocated.
     // x4: JSObject
index 89fcaf5..822d988 100644 (file)
@@ -2938,6 +2938,8 @@ void CallConstructStub::Generate(MacroAssembler* masm) {
     __ AssertUndefinedOrAllocationSite(x2, x5);
   }
 
+  __ Mov(x3, function);
+
   // Jump to the function-specific construct stub.
   Register jump_reg = x4;
   Register shared_func_info = jump_reg;
index 5dcde45..72bd507 100644 (file)
@@ -2862,11 +2862,15 @@ void FullCodeGenerator::VisitCall(Call* expr) {
       }
     }
   } else if (call_type == Call::SUPER_CALL) {
-    SuperReference* super_ref = callee->AsSuperReference();
-    EmitLoadSuperConstructor(super_ref);
-    __ Push(result_register());
-    VisitForStackValue(super_ref->this_var());
-    EmitCall(expr, CallICState::METHOD);
+    if (FLAG_experimental_classes) {
+      EmitSuperConstructorCall(expr);
+    } else {
+      SuperReference* super_ref = callee->AsSuperReference();
+      EmitLoadSuperConstructor(super_ref);
+      __ Push(result_register());
+      VisitForStackValue(super_ref->this_var());
+      EmitCall(expr, CallICState::METHOD);
+    }
   } else {
     DCHECK(call_type == Call::OTHER_CALL);
     // Call to an arbitrary expression not handled specially above.
@@ -2934,6 +2938,51 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
 }
 
 
+void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
+  SuperReference* super_ref = expr->expression()->AsSuperReference();
+  EmitLoadSuperConstructor(super_ref);
+  __ push(result_register());
+
+  // Push the arguments ("left-to-right") on the stack.
+  ZoneList<Expression*>* args = expr->arguments();
+  int arg_count = args->length();
+  for (int i = 0; i < arg_count; i++) {
+    VisitForStackValue(args->at(i));
+  }
+
+  // Call the construct call builtin that handles allocation and
+  // constructor invocation.
+  SetSourcePosition(expr->position());
+
+  // Load function and argument count into x1 and x0.
+  __ Mov(x0, arg_count);
+  __ Peek(x1, arg_count * kXRegSize);
+
+  // Record call targets in unoptimized code.
+  if (FLAG_pretenuring_call_new) {
+    UNREACHABLE();
+    /* TODO(dslomov): support pretenuring.
+    EnsureSlotContainsAllocationSite(expr->AllocationSiteFeedbackSlot());
+    DCHECK(expr->AllocationSiteFeedbackSlot().ToInt() ==
+           expr->CallNewFeedbackSlot().ToInt() + 1);
+    */
+  }
+
+  __ LoadObject(x2, FeedbackVector());
+  __ Mov(x3, SmiFromSlot(expr->CallFeedbackSlot()));
+
+  // TODO(dslomov): use a different stub and propagate new.target.
+  CallConstructStub stub(isolate(), RECORD_CONSTRUCTOR_TARGET);
+  __ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
+
+  RecordJSReturnSite(expr);
+
+  // TODO(dslomov): implement TDZ for `this`.
+  EmitVariableAssignment(super_ref->this_var()->var(), Token::ASSIGN);
+  context()->Plug(x0);
+}
+
+
 void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) {
   ZoneList<Expression*>* args = expr->arguments();
   DCHECK(args->length() == 1);
index 07ed3df..e7fa603 100644 (file)
@@ -786,8 +786,8 @@ static void ThrowSuperConstructorCheckError(CompilationInfo* info,
 
 static bool CheckSuperConstructorCall(CompilationInfo* info) {
   FunctionLiteral* function = info->function();
+  if (FLAG_experimental_classes) return true;
   if (!function->uses_super_constructor_call()) return true;
-
   if (function->is_default_constructor()) return true;
 
   ZoneList<Statement*>* body = function->body();
@@ -837,7 +837,6 @@ static bool CheckSuperConstructorCall(CompilationInfo* info) {
     ThrowSuperConstructorCheckError(info, stmt);
     return false;
   }
-
   return true;
 }
 
index 2cf77a3..d633426 100644 (file)
@@ -256,6 +256,10 @@ DEFINE_IMPLICATION(track_field_types, track_fields)
 DEFINE_IMPLICATION(track_field_types, track_heap_object_fields)
 DEFINE_BOOL(smi_binop, true, "support smi representation in binary operations")
 DEFINE_BOOL(vector_ics, false, "support vector-based ics")
+DEFINE_BOOL(experimental_classes, false,
+            "experimental new semantics for super() calls")
+DEFINE_IMPLICATION(experimental_classes, harmony_classes)
+DEFINE_IMPLICATION(experimental_classes, harmony_object_literals)
 
 // Flags for optimization types.
 DEFINE_BOOL(optimize_for_size, false,
index 9681c8a..c431e10 100644 (file)
@@ -496,6 +496,7 @@ class FullCodeGenerator: public AstVisitor {
 
   // Platform-specific code sequences for calls
   void EmitCall(Call* expr, CallICState::CallType = CallICState::FUNCTION);
+  void EmitSuperConstructorCall(Call* expr);
   void EmitCallWithLoadIC(Call* expr);
   void EmitSuperCallWithLoadIC(Call* expr);
   void EmitKeyedCallWithLoadIC(Call* expr, Expression* key);
index 5767489..675d862 100644 (file)
@@ -100,6 +100,42 @@ void Builtins::Generate_InOptimizationQueue(MacroAssembler* masm) {
 }
 
 
+static void Generate_Runtime_NewObject(MacroAssembler* masm,
+                                       bool create_memento,
+                                       Register original_constructor,
+                                       Label* count_incremented,
+                                       Label* allocated) {
+  int offset = 0;
+  if (create_memento) {
+    // Get the cell or allocation site.
+    __ mov(edi, Operand(esp, kPointerSize * 2));
+    __ push(edi);
+    offset = kPointerSize;
+  }
+
+  // Must restore esi (context) and edi (constructor) before calling
+  // runtime.
+  __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+  __ mov(edi, Operand(esp, offset));
+  __ push(edi);
+  __ push(original_constructor);
+  if (create_memento) {
+    __ CallRuntime(Runtime::kNewObjectWithAllocationSite, 3);
+  } else {
+    __ CallRuntime(Runtime::kNewObject, 2);
+  }
+  __ mov(ebx, eax);  // store result in ebx
+
+  // Runtime_NewObjectWithAllocationSite increments allocation count.
+  // Skip the increment.
+  if (create_memento) {
+    __ jmp(count_incremented);
+  } else {
+    __ jmp(allocated);
+  }
+}
+
+
 static void Generate_JSConstructStubHelper(MacroAssembler* masm,
                                            bool is_api_function,
                                            bool create_memento) {
@@ -107,6 +143,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
   //  -- eax: number of arguments
   //  -- edi: constructor function
   //  -- ebx: allocation site or undefined
+  //  -- edx: original constructor
   // -----------------------------------
 
   // Should never create mementos for api functions.
@@ -128,9 +165,20 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
     // Push the function to invoke on the stack.
     __ push(edi);
 
+    __ cmp(edx, edi);
+    Label normal_new;
+    Label count_incremented;
+    Label allocated;
+    __ j(equal, &normal_new);
+
+    // Original constructor and function are different.
+    Generate_Runtime_NewObject(masm, create_memento, edx, &count_incremented,
+                               &allocated);
+    __ bind(&normal_new);
+
     // Try to allocate the object without transitioning into C code. If any of
     // the preconditions is not met, the code bails out to the runtime call.
-    Label rt_call, allocated;
+    Label rt_call;
     if (FLAG_inline_new) {
       Label undo_allocation;
       ExternalReference debug_step_in_fp =
@@ -344,34 +392,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
 
     // Allocate the new receiver object using the runtime call.
     __ bind(&rt_call);
-    int offset = 0;
-    if (create_memento) {
-      // Get the cell or allocation site.
-      __ mov(edi, Operand(esp, kPointerSize * 2));
-      __ push(edi);
-      offset = kPointerSize;
-    }
-
-    // Must restore esi (context) and edi (constructor) before calling runtime.
-    __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
-    __ mov(edi, Operand(esp, offset));
-    // edi: function (constructor)
-    __ push(edi);
-    if (create_memento) {
-      __ CallRuntime(Runtime::kNewObjectWithAllocationSite, 2);
-    } else {
-      __ CallRuntime(Runtime::kNewObject, 1);
-    }
-    __ mov(ebx, eax);  // store result in ebx
-
-    // If we ended up using the runtime, and we want a memento, then the
-    // runtime call made it for us, and we shouldn't do create count
-    // increment.
-    Label count_incremented;
-    if (create_memento) {
-      __ jmp(&count_incremented);
-    }
-
+    Generate_Runtime_NewObject(masm, create_memento, edi, &count_incremented,
+                               &allocated);
     // New object allocated.
     // ebx: newly allocated object
     __ bind(&allocated);
index 8235868..130311e 100644 (file)
@@ -2149,6 +2149,9 @@ void CallConstructStub::Generate(MacroAssembler* masm) {
     __ AssertUndefinedOrAllocationSite(ebx);
   }
 
+  // Pass original constructor to construct stub.
+  __ mov(edx, edi);
+
   // Jump to the function-specific construct stub.
   Register jmp_reg = ecx;
   __ mov(jmp_reg, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
index 69fcb4d..b419535 100644 (file)
@@ -3001,7 +3001,6 @@ void FullCodeGenerator::VisitCall(Call* expr) {
 
   } else if (call_type == Call::GLOBAL_CALL) {
     EmitCallWithLoadIC(expr);
-
   } else if (call_type == Call::LOOKUP_SLOT_CALL) {
     // Call to a lookup slot (dynamically introduced variable).
     VariableProxy* proxy = callee->AsVariableProxy();
@@ -3060,11 +3059,15 @@ void FullCodeGenerator::VisitCall(Call* expr) {
       }
     }
   } else if (call_type == Call::SUPER_CALL) {
-    SuperReference* super_ref = callee->AsSuperReference();
-    EmitLoadSuperConstructor(super_ref);
-    __ push(result_register());
-    VisitForStackValue(super_ref->this_var());
-    EmitCall(expr, CallICState::METHOD);
+    if (FLAG_experimental_classes) {
+      EmitSuperConstructorCall(expr);
+    } else {
+      SuperReference* super_ref = callee->AsSuperReference();
+      EmitLoadSuperConstructor(super_ref);
+      __ push(result_register());
+      VisitForStackValue(super_ref->this_var());
+      EmitCall(expr, CallICState::METHOD);
+    }
   } else {
     DCHECK(call_type == Call::OTHER_CALL);
     // Call to an arbitrary expression not handled specially above.
@@ -3131,6 +3134,51 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
 }
 
 
+void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
+  SuperReference* super_ref = expr->expression()->AsSuperReference();
+  EmitLoadSuperConstructor(super_ref);
+  __ push(result_register());
+
+  // Push the arguments ("left-to-right") on the stack.
+  ZoneList<Expression*>* args = expr->arguments();
+  int arg_count = args->length();
+  for (int i = 0; i < arg_count; i++) {
+    VisitForStackValue(args->at(i));
+  }
+
+  // Call the construct call builtin that handles allocation and
+  // constructor invocation.
+  SetSourcePosition(expr->position());
+
+  // Load function and argument count into edi and eax.
+  __ Move(eax, Immediate(arg_count));
+  __ mov(edi, Operand(esp, arg_count * kPointerSize));
+
+  // Record call targets in unoptimized code.
+  if (FLAG_pretenuring_call_new) {
+    UNREACHABLE();
+    /* TODO(dslomov): support pretenuring.
+    EnsureSlotContainsAllocationSite(expr->AllocationSiteFeedbackSlot());
+    DCHECK(expr->AllocationSiteFeedbackSlot().ToInt() ==
+           expr->CallNewFeedbackSlot().ToInt() + 1);
+    */
+  }
+
+  __ LoadHeapObject(ebx, FeedbackVector());
+  __ mov(edx, Immediate(SmiFromSlot(expr->CallFeedbackSlot())));
+
+  // TODO(dslomov): use a different stub and propagate new.target.
+  CallConstructStub stub(isolate(), RECORD_CONSTRUCTOR_TARGET);
+  __ call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
+
+  RecordJSReturnSite(expr);
+
+  // TODO(dslomov): implement TDZ for `this`.
+  EmitVariableAssignment(super_ref->this_var()->var(), Token::ASSIGN);
+  context()->Plug(eax);
+}
+
+
 void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) {
   ZoneList<Expression*>* args = expr->arguments();
   DCHECK(args->length() == 1);
index 0b212a7..340a645 100644 (file)
@@ -1279,7 +1279,12 @@ RUNTIME_FUNCTION(Runtime_AllocateHeapNumber) {
 
 static Object* Runtime_NewObjectHelper(Isolate* isolate,
                                        Handle<Object> constructor,
+                                       Handle<Object> original_constructor,
                                        Handle<AllocationSite> site) {
+  // TODO(dslomov): implement prototype rewiring.
+  // The check below is a sanity check.
+  CHECK(*constructor == *original_constructor);
+
   // If the constructor isn't a proper function we throw a type error.
   if (!constructor->IsJSFunction()) {
     Vector<Handle<Object> > arguments = HandleVector(&constructor, 1);
@@ -1340,16 +1345,18 @@ static Object* Runtime_NewObjectHelper(Isolate* isolate,
 
 RUNTIME_FUNCTION(Runtime_NewObject) {
   HandleScope scope(isolate);
-  DCHECK(args.length() == 1);
+  DCHECK(args.length() == 2);
   CONVERT_ARG_HANDLE_CHECKED(Object, constructor, 0);
-  return Runtime_NewObjectHelper(isolate, constructor,
+  CONVERT_ARG_HANDLE_CHECKED(Object, original_constructor, 1);
+  return Runtime_NewObjectHelper(isolate, constructor, original_constructor,
                                  Handle<AllocationSite>::null());
 }
 
 
 RUNTIME_FUNCTION(Runtime_NewObjectWithAllocationSite) {
   HandleScope scope(isolate);
-  DCHECK(args.length() == 2);
+  DCHECK(args.length() == 3);
+  CONVERT_ARG_HANDLE_CHECKED(Object, original_constructor, 2);
   CONVERT_ARG_HANDLE_CHECKED(Object, constructor, 1);
   CONVERT_ARG_HANDLE_CHECKED(Object, feedback, 0);
   Handle<AllocationSite> site;
@@ -1357,7 +1364,8 @@ RUNTIME_FUNCTION(Runtime_NewObjectWithAllocationSite) {
     // The feedback can be an AllocationSite or undefined.
     site = Handle<AllocationSite>::cast(feedback);
   }
-  return Runtime_NewObjectHelper(isolate, constructor, site);
+  return Runtime_NewObjectHelper(isolate, constructor, original_constructor,
+                                 site);
 }
 
 
index 0b89104..dca28f1 100644 (file)
@@ -477,8 +477,8 @@ namespace internal {
   /* Statements */                                           \
   F(NewClosure, 3, 1)                                        \
   F(NewClosureFromStubFailure, 1, 1)                         \
-  F(NewObject, 1, 1)                                         \
-  F(NewObjectWithAllocationSite, 2, 1)                       \
+  F(NewObject, 2, 1)                                         \
+  F(NewObjectWithAllocationSite, 3, 1)                       \
   F(FinalizeInstanceSize, 1, 1)                              \
   F(Throw, 1, 1)                                             \
   F(ReThrow, 1, 1)                                           \
index ef3df65..ff0a349 100644 (file)
@@ -99,6 +99,41 @@ void Builtins::Generate_InOptimizationQueue(MacroAssembler* masm) {
 }
 
 
+static void Generate_Runtime_NewObject(MacroAssembler* masm,
+                                       bool create_memento,
+                                       Register original_constructor,
+                                       Label* count_incremented,
+                                       Label* allocated) {
+  int offset = 0;
+  if (create_memento) {
+    // Get the cell or allocation site.
+    __ movp(rdi, Operand(rsp, kPointerSize * 2));
+    __ Push(rdi);
+    offset = kPointerSize;
+  }
+
+  // Must restore rsi (context) and rdi (constructor) before calling runtime.
+  __ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+  __ movp(rdi, Operand(rsp, offset));
+  __ Push(rdi);
+  __ Push(original_constructor);
+  if (create_memento) {
+    __ CallRuntime(Runtime::kNewObjectWithAllocationSite, 3);
+  } else {
+    __ CallRuntime(Runtime::kNewObject, 2);
+  }
+  __ movp(rbx, rax);  // store result in rbx
+
+  // Runtime_NewObjectWithAllocationSite increments allocation count.
+  // Skip the increment.
+  if (create_memento) {
+    __ jmp(count_incremented);
+  } else {
+    __ jmp(allocated);
+  }
+}
+
+
 static void Generate_JSConstructStubHelper(MacroAssembler* masm,
                                            bool is_api_function,
                                            bool create_memento) {
@@ -106,6 +141,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
   //  -- rax: number of arguments
   //  -- rdi: constructor function
   //  -- rbx: allocation site or undefined
+  //  -- rdx: original constructor
   // -----------------------------------
 
   // Should never create mementos for api functions.
@@ -127,9 +163,16 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
     // Push the function to invoke on the stack.
     __ Push(rdi);
 
+    Label rt_call, normal_new, allocated, count_incremented;
+    __ cmpp(rdx, rdi);
+    __ j(equal, &normal_new);
+
+    Generate_Runtime_NewObject(masm, create_memento, rdx, &count_incremented,
+                               &allocated);
+
+    __ bind(&normal_new);
     // Try to allocate the object without transitioning into C code. If any of
     // the preconditions is not met, the code bails out to the runtime call.
-    Label rt_call, allocated;
     if (FLAG_inline_new) {
       Label undo_allocation;
 
@@ -345,32 +388,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
     // Allocate the new receiver object using the runtime call.
     // rdi: function (constructor)
     __ bind(&rt_call);
-    int offset = 0;
-    if (create_memento) {
-      // Get the cell or allocation site.
-      __ movp(rdi, Operand(rsp, kPointerSize*2));
-      __ Push(rdi);
-      offset = kPointerSize;
-    }
-
-    // Must restore rsi (context) and rdi (constructor) before calling runtime.
-    __ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
-    __ movp(rdi, Operand(rsp, offset));
-    __ Push(rdi);
-    if (create_memento) {
-      __ CallRuntime(Runtime::kNewObjectWithAllocationSite, 2);
-    } else {
-      __ CallRuntime(Runtime::kNewObject, 1);
-    }
-    __ movp(rbx, rax);  // store result in rbx
-
-    // If we ended up using the runtime, and we want a memento, then the
-    // runtime call made it for us, and we shouldn't do create count
-    // increment.
-    Label count_incremented;
-    if (create_memento) {
-      __ jmp(&count_incremented);
-    }
+    Generate_Runtime_NewObject(masm, create_memento, rdi, &count_incremented,
+                               &allocated);
 
     // New object allocated.
     // rbx: newly allocated object
index f7bd9b6..66dfe1f 100644 (file)
@@ -2017,6 +2017,9 @@ void CallConstructStub::Generate(MacroAssembler* masm) {
     __ AssertUndefinedOrAllocationSite(rbx);
   }
 
+  // Pass original constructor to construct stub.
+  __ movp(rdx, rdi);
+
   // Jump to the function-specific construct stub.
   Register jmp_reg = rcx;
   __ movp(jmp_reg, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
index 4b16c69..a8d20be 100644 (file)
@@ -3063,11 +3063,15 @@ void FullCodeGenerator::VisitCall(Call* expr) {
       }
     }
   } else if (call_type == Call::SUPER_CALL) {
-    SuperReference* super_ref = callee->AsSuperReference();
-    EmitLoadSuperConstructor(super_ref);
-    __ Push(result_register());
-    VisitForStackValue(super_ref->this_var());
-    EmitCall(expr, CallICState::METHOD);
+    if (FLAG_experimental_classes) {
+      EmitSuperConstructorCall(expr);
+    } else {
+      SuperReference* super_ref = callee->AsSuperReference();
+      EmitLoadSuperConstructor(super_ref);
+      __ Push(result_register());
+      VisitForStackValue(super_ref->this_var());
+      EmitCall(expr, CallICState::METHOD);
+    }
   } else {
     DCHECK(call_type == Call::OTHER_CALL);
     // Call to an arbitrary expression not handled specially above.
@@ -3134,6 +3138,51 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
 }
 
 
+void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
+  SuperReference* super_ref = expr->expression()->AsSuperReference();
+  EmitLoadSuperConstructor(super_ref);
+  __ Push(result_register());
+
+  // Push the arguments ("left-to-right") on the stack.
+  ZoneList<Expression*>* args = expr->arguments();
+  int arg_count = args->length();
+  for (int i = 0; i < arg_count; i++) {
+    VisitForStackValue(args->at(i));
+  }
+
+  // Call the construct call builtin that handles allocation and
+  // constructor invocation.
+  SetSourcePosition(expr->position());
+
+  // Load function and argument count into edi and eax.
+  __ Set(rax, arg_count);
+  __ movp(rdi, Operand(rsp, arg_count * kPointerSize));
+
+  // Record call targets in unoptimized code.
+  if (FLAG_pretenuring_call_new) {
+    UNREACHABLE();
+    /* TODO(dslomov): support pretenuring.
+    EnsureSlotContainsAllocationSite(expr->AllocationSiteFeedbackSlot());
+    DCHECK(expr->AllocationSiteFeedbackSlot().ToInt() ==
+           expr->CallNewFeedbackSlot().ToInt() + 1);
+    */
+  }
+
+  __ Move(rbx, FeedbackVector());
+  __ Move(rdx, SmiFromSlot(expr->CallFeedbackSlot()));
+
+  // TODO(dslomov): use a different stub and propagate new.target.
+  CallConstructStub stub(isolate(), RECORD_CONSTRUCTOR_TARGET);
+  __ call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
+
+  RecordJSReturnSite(expr);
+
+  // TODO(dslomov): implement TDZ for `this`.
+  EmitVariableAssignment(super_ref->this_var()->var(), Token::ASSIGN);
+  context()->Plug(rax);
+}
+
+
 void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) {
   ZoneList<Expression*>* args = expr->arguments();
   DCHECK(args->length() == 1);
diff --git a/test/mjsunit/harmony/classes-experimental.js b/test/mjsunit/harmony/classes-experimental.js
new file mode 100644 (file)
index 0000000..73944b1
--- /dev/null
@@ -0,0 +1,35 @@
+// Copyright 2014 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: --experimental-classes --harmony-classes
+
+'use strict';
+
+class Base {
+  constructor() {
+    let o = new Object();
+    o.prp = 1;
+    return o;
+  }
+}
+
+class Subclass extends Base {
+  constructor() {
+    try {
+      this.prp1 = 3;
+    } catch (e) {
+      // TODO(dslomov): actually test the exception once TDZ is implemented.
+    }
+    super();
+    assertSame(1, this.prp);
+    assertSame(undefined, this.prp1);
+    assertFalse(this.hasOwnProperty("prp1"));
+    return this;
+  }
+}
+
+let s = new Subclass();
+assertSame(1, s.prp);
+assertSame(undefined, s.prp1);
+assertFalse(s.hasOwnProperty("prp1"));