new classes: implement new.target passing to superclass constructor.
authordslomov <dslomov@chromium.org>
Wed, 11 Feb 2015 09:47:32 +0000 (01:47 -0800)
committerCommit bot <commit-bot@chromium.org>
Wed, 11 Feb 2015 09:47:51 +0000 (09:47 +0000)
R=arv@chromium.org,rossberg@chromium.org
BUG=v8:3834
LOG=N

Committed: https://crrev.com/8aed43e82c6d2742fe5988603cb8841324cc942b
Cr-Commit-Position: refs/heads/master@{#26560}

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

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

40 files changed:
src/accessors.cc
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/ast-value-factory.h
src/bootstrapper.cc
src/code-stubs.h
src/code.h
src/compiler.cc
src/compiler/js-generic-lowering.cc
src/compiler/linkage.cc
src/compiler/pipeline.cc
src/deoptimizer.cc
src/factory.cc
src/frames.cc
src/full-codegen.cc
src/globals.h
src/hydrogen-instructions.h
src/hydrogen.cc
src/ia32/builtins-ia32.cc
src/ia32/code-stubs-ia32.cc
src/ia32/deoptimizer-ia32.cc
src/ia32/full-codegen-ia32.cc
src/ic/ic.cc
src/objects-inl.h
src/objects.h
src/runtime/runtime-classes.cc
src/runtime/runtime-function.cc
src/runtime/runtime-object.cc
src/runtime/runtime-scopes.cc
src/scopes.cc
src/scopes.h
src/variables.h
src/x64/builtins-x64.cc
src/x64/code-stubs-x64.cc
src/x64/full-codegen-x64.cc
test/mjsunit/harmony/classes-experimental.js

index cee26520772de48aedf616fe8c87b1225a3f8dd6..0e6a083910e01e5f5a089334b25b8546348805d3 100644 (file)
@@ -1186,9 +1186,8 @@ static Handle<Object> ArgumentsForInlinedFunction(
   Isolate* isolate = inlined_function->GetIsolate();
   Factory* factory = isolate->factory();
   SlotRefValueBuilder slot_refs(
-      frame,
-      inlined_frame_index,
-      inlined_function->shared()->formal_parameter_count());
+      frame, inlined_frame_index,
+      inlined_function->shared()->internal_formal_parameter_count());
 
   int args_count = slot_refs.args_length();
   Handle<JSObject> arguments =
index 2c08b801018ef52663e09452a7f7bd18ad9c1860..1203ec42b3d3523ce46f66f28a7aed4be271bf79 100644 (file)
@@ -761,6 +761,9 @@ void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) {
     __ SmiTag(r4);
     __ push(r4);  // Smi-tagged arguments count.
 
+    // Push new.target.
+    __ push(r3);
+
     // receiver is the hole.
     __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
     __ push(ip);
@@ -774,7 +777,8 @@ void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) {
     // r2: address of last argument (caller sp)
     // r4: number of arguments (smi-tagged)
     // sp[0]: receiver
-    // sp[1]: number of arguments (smi-tagged)
+    // sp[1]: new.target
+    // sp[2]: number of arguments (smi-tagged)
     Label loop, entry;
     __ b(&entry);
     __ bind(&loop);
@@ -787,6 +791,7 @@ void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) {
     // Call the function.
     // r0: number of arguments
     // r1: constructor function
+    __ add(r0, r0, Operand(1));
     ParameterCount actual(r0);
     __ InvokeFunction(r1, actual, CALL_FUNCTION, NullCallWrapper());
 
index 35f835bbd4149641080158762526611ab22494a8..e55069a604947287589d3e71e3df57ed5183e7da 100644 (file)
@@ -1488,6 +1488,7 @@ void LoadIndexedStringStub::Generate(MacroAssembler* masm) {
 
 
 void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
+  CHECK(!has_new_target());
   // The displacement is the offset of the last parameter (if any)
   // relative to the frame pointer.
   const int kDisplacement =
@@ -1545,6 +1546,8 @@ void ArgumentsAccessStub::GenerateNewSloppySlow(MacroAssembler* masm) {
   // sp[4] : receiver displacement
   // sp[8] : function
 
+  CHECK(!has_new_target());
+
   // Check if the calling frame is an arguments adaptor frame.
   Label runtime;
   __ ldr(r3, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
@@ -1573,6 +1576,8 @@ void ArgumentsAccessStub::GenerateNewSloppyFast(MacroAssembler* masm) {
   //  r6 : allocated object (tagged)
   //  r9 : mapped parameter count (tagged)
 
+  CHECK(!has_new_target());
+
   __ ldr(r1, MemOperand(sp, 0 * kPointerSize));
   // r1 = parameter count (tagged)
 
@@ -1813,6 +1818,10 @@ void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
   // Patch the arguments.length and the parameters pointer.
   __ bind(&adaptor_frame);
   __ ldr(r1, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
+  if (has_new_target()) {
+    // Subtract 1 from smi-tagged arguments count.
+    __ sub(r1, r1, Operand(2));
+  }
   __ str(r1, MemOperand(sp, 0));
   __ add(r3, r2, Operand::PointerOffsetFromSmiKey(r1));
   __ add(r3, r3, Operand(StandardFrameConstants::kCallerSPOffset));
@@ -2564,7 +2573,13 @@ void CallConstructStub::Generate(MacroAssembler* masm) {
   }
 
   // Pass function as original constructor.
-  __ mov(r3, r1);
+  if (IsSuperConstructorCall()) {
+    __ mov(r4, Operand(1 * kPointerSize));
+    __ add(r4, r4, Operand(r0, LSL, kPointerSizeLog2));
+    __ ldr(r3, MemOperand(sp, r4));
+  } else {
+    __ mov(r3, r1);
+  }
 
   // Jump to the function-specific construct stub.
   Register jmp_reg = r4;
index 7508c01e4998473eb00ffd20ab7ce4700241e6a3..599231ef7fb1f4f1a98bfd6233b7c216180e2394 100644 (file)
@@ -262,6 +262,10 @@ void FullCodeGenerator::Generate() {
     //   function, receiver address, parameter count.
     // The stub will rewrite receiever and parameter count if the previous
     // stack frame was an arguments adapter frame.
+    ArgumentsAccessStub::HasNewTarget has_new_target =
+        IsSubclassConstructor(info->function()->kind())
+            ? ArgumentsAccessStub::HAS_NEW_TARGET
+            : ArgumentsAccessStub::NO_NEW_TARGET;
     ArgumentsAccessStub::Type type;
     if (is_strict(language_mode())) {
       type = ArgumentsAccessStub::NEW_STRICT;
@@ -270,7 +274,7 @@ void FullCodeGenerator::Generate() {
     } else {
       type = ArgumentsAccessStub::NEW_SLOPPY_FAST;
     }
-    ArgumentsAccessStub stub(isolate(), type);
+    ArgumentsAccessStub stub(isolate(), type, has_new_target);
     __ CallStub(&stub);
 
     SetVar(arguments, r0, r1, r2);
@@ -450,7 +454,12 @@ void FullCodeGenerator::EmitReturnSequence() {
     // Make sure that the constant pool is not emitted inside of the return
     // sequence.
     { Assembler::BlockConstPoolScope block_const_pool(masm_);
-      int32_t sp_delta = (info_->scope()->num_parameters() + 1) * kPointerSize;
+      int32_t arg_count = info_->scope()->num_parameters() + 1;
+      if (FLAG_experimental_classes &&
+          IsSubclassConstructor(info_->function()->kind())) {
+        arg_count++;
+      }
+      int32_t sp_delta = arg_count * kPointerSize;
       CodeGenerator::RecordPositions(masm_, function()->end_position() - 1);
       // TODO(svenpanne) The code below is sometimes 4 words, sometimes 5!
       PredictableCodeSizeScope predictable(masm_, -1);
@@ -3257,6 +3266,11 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
 
 
 void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
+  Comment cmnt(masm_, "[ SuperConstructorCall");
+  Variable* new_target_var = scope()->DeclarationScope()->new_target_var();
+  GetVar(result_register(), new_target_var);
+  __ Push(result_register());
+
   SuperReference* super_ref = expr->expression()->AsSuperReference();
   EmitLoadSuperConstructor(super_ref);
   __ push(result_register());
@@ -3300,10 +3314,11 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
   __ 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);
+  CallConstructStub stub(isolate(), SUPER_CALL_RECORD_TARGET);
   __ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
 
+  __ Drop(1);
+
   RecordJSReturnSite(expr);
 
   EmitVariableAssignment(this_var, Token::INIT_CONST);
index 726e9847d2fee0053bfd36525c3c6ddf453d3a9a..6dd1dff64bbc5375cb03f95a25aa08af02226d35 100644 (file)
@@ -725,9 +725,10 @@ void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) {
     __ Mov(x4, x0);
     __ SmiTag(x4);
     __ LoadRoot(x10, Heap::kTheHoleValueRootIndex);
-    __ Push(x4, x10);
+    __ Push(x4, x3, x10);
     // sp[0]: number of arguments
-    // sp[1]: receiver (the hole)
+    // sp[1]: new.target
+    // sp[2]: receiver (the hole)
 
 
     // Set up pointer to last argument.
@@ -739,7 +740,8 @@ void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) {
     // x1: constructor function
     // x2: address of last argument (caller sp)
     // jssp[0]: receiver
-    // jssp[1]: number of arguments (smi-tagged)
+    // jssp[1]: new.target
+    // jssp[2]: number of arguments (smi-tagged)
     // Compute the start address of the copy in x4.
     __ Add(x4, x2, Operand(x0, LSL, kPointerSizeLog2));
     Label loop, entry, done_copying_arguments;
@@ -759,6 +761,7 @@ void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) {
     // Call the function.
     // x0: number of arguments
     // x1: constructor function
+    __ Add(x0, x0, Operand(1));  // new.target
     ParameterCount actual(x0);
     __ InvokeFunction(x1, actual, CALL_FUNCTION, NullCallWrapper());
 
index 9a26c3d60b720c733870031ee79f9fc1f868d1ff..28cbf7cde647fcc1afb7737d6fdd92ddd4b815a8 100644 (file)
@@ -1643,6 +1643,7 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
 
 
 void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
+  CHECK(!has_new_target());
   Register arg_count = ArgumentsAccessReadDescriptor::parameter_count();
   Register key = ArgumentsAccessReadDescriptor::index();
   DCHECK(arg_count.is(x0));
@@ -1699,6 +1700,8 @@ void ArgumentsAccessStub::GenerateNewSloppySlow(MacroAssembler* masm) {
   //  jssp[8]:  address of receiver argument
   //  jssp[16]: function
 
+  CHECK(!has_new_target());
+
   // Check if the calling frame is an arguments adaptor frame.
   Label runtime;
   Register caller_fp = x10;
@@ -1730,6 +1733,8 @@ void ArgumentsAccessStub::GenerateNewSloppyFast(MacroAssembler* masm) {
   //
   // Returns pointer to result object in x0.
 
+  CHECK(!has_new_target());
+
   // Note: arg_count_smi is an alias of param_count_smi.
   Register arg_count_smi = x3;
   Register param_count_smi = x3;
@@ -2056,6 +2061,11 @@ void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
          MemOperand(caller_fp,
                     ArgumentsAdaptorFrameConstants::kLengthOffset));
   __ SmiUntag(param_count, param_count_smi);
+  if (has_new_target()) {
+    // Skip new.target: it is not a part of arguments.
+    __ Sub(param_count, param_count, Operand(1));
+    __ SmiTag(param_count_smi, param_count);
+  }
   __ Add(x10, caller_fp, Operand(param_count, LSL, kPointerSizeLog2));
   __ Add(params, x10, StandardFrameConstants::kCallerSPOffset);
 
@@ -2939,7 +2949,13 @@ void CallConstructStub::Generate(MacroAssembler* masm) {
     __ AssertUndefinedOrAllocationSite(x2, x5);
   }
 
-  __ Mov(x3, function);
+  if (IsSuperConstructorCall()) {
+    __ Mov(x4, Operand(1 * kPointerSize));
+    __ Add(x4, x4, Operand(x0, LSL, kPointerSizeLog2));
+    __ Peek(x3, x4);
+  } else {
+    __ Mov(x3, function);
+  }
 
   // Jump to the function-specific construct stub.
   Register jump_reg = x4;
index 1d24b91dbb846fd0e85300c7405c2b143c41f2f0..d49b06b77b35cba9abd3e958f76359da0932902a 100644 (file)
@@ -262,6 +262,10 @@ void FullCodeGenerator::Generate() {
     //   function, receiver address, parameter count.
     // The stub will rewrite receiver and parameter count if the previous
     // stack frame was an arguments adapter frame.
+    ArgumentsAccessStub::HasNewTarget has_new_target =
+        IsSubclassConstructor(info->function()->kind())
+            ? ArgumentsAccessStub::HAS_NEW_TARGET
+            : ArgumentsAccessStub::NO_NEW_TARGET;
     ArgumentsAccessStub::Type type;
     if (is_strict(language_mode())) {
       type = ArgumentsAccessStub::NEW_STRICT;
@@ -270,7 +274,7 @@ void FullCodeGenerator::Generate() {
     } else {
       type = ArgumentsAccessStub::NEW_SLOPPY_FAST;
     }
-    ArgumentsAccessStub stub(isolate(), type);
+    ArgumentsAccessStub stub(isolate(), type, has_new_target);
     __ CallStub(&stub);
 
     SetVar(arguments, x0, x1, x2);
@@ -455,7 +459,12 @@ void FullCodeGenerator::EmitReturnSequence() {
       __ ldr_pcrel(ip0, (3 * kInstructionSize) >> kLoadLiteralScaleLog2);
       __ add(current_sp, current_sp, ip0);
       __ ret();
-      __ dc64(kXRegSize * (info_->scope()->num_parameters() + 1));
+      int32_t arg_count = info_->scope()->num_parameters() + 1;
+      if (FLAG_experimental_classes &&
+          IsSubclassConstructor(info_->function()->kind())) {
+        arg_count++;
+      }
+      __ dc64(kXRegSize * arg_count);
       info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
     }
   }
@@ -2946,6 +2955,11 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
 
 
 void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
+  Comment cmnt(masm_, "[ SuperConstructorCall");
+  Variable* new_target_var = scope()->DeclarationScope()->new_target_var();
+  GetVar(result_register(), new_target_var);
+  __ Push(result_register());
+
   SuperReference* super_ref = expr->expression()->AsSuperReference();
   EmitLoadSuperConstructor(super_ref);
   __ push(result_register());
@@ -2988,10 +3002,11 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
   __ LoadObject(x2, FeedbackVector());
   __ Mov(x3, SmiFromSlot(expr->CallFeedbackSlot()));
 
-  // TODO(dslomov): use a different stub and propagate new.target.
-  CallConstructStub stub(isolate(), RECORD_CONSTRUCTOR_TARGET);
+  CallConstructStub stub(isolate(), SUPER_CALL_RECORD_TARGET);
   __ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
 
+  __ Drop(1);
+
   RecordJSReturnSite(expr);
 
   EmitVariableAssignment(this_var, Token::INIT_CONST);
index 70a29f14e3d51c73f953a56c7bed2ef8c9598e07..294a2818590d6486e3e4c0a4d197ea5d57d3d27b 100644 (file)
@@ -249,11 +249,13 @@ class AstValue : public ZoneObject {
   F(get_template_callsite, "GetTemplateCallSite")       \
   F(initialize_const_global, "initializeConstGlobal")   \
   F(initialize_var_global, "initializeVarGlobal")       \
+  F(is_construct_call, "_IsConstructCall")              \
   F(let, "let")                                         \
   F(make_reference_error, "MakeReferenceErrorEmbedded") \
   F(make_syntax_error, "MakeSyntaxErrorEmbedded")       \
   F(make_type_error, "MakeTypeErrorEmbedded")           \
   F(native, "native")                                   \
+  F(new_target, "new.target")                           \
   F(next, "next")                                       \
   F(proto, "__proto__")                                 \
   F(prototype, "prototype")                             \
@@ -261,8 +263,7 @@ class AstValue : public ZoneObject {
   F(use_asm, "use asm")                                 \
   F(use_strong, "use strong")                           \
   F(use_strict, "use strict")                           \
-  F(value, "value")                                     \
-  F(is_construct_call, "_IsConstructCall")
+  F(value, "value")
 
 #define OTHER_CONSTANTS(F) \
   F(true_value)            \
index 4e3cfc48d7a991aa4d42c21f4b74c45194baeac8..31d6e3e00c2cc355bc8b2caaface11a6989d55bd 100644 (file)
@@ -2102,7 +2102,7 @@ bool Genesis::InstallNatives() {
     DCHECK(call->is_compiled());
 
     // Set the expected parameters for apply to 2; required by builtin.
-    apply->shared()->set_formal_parameter_count(2);
+    apply->shared()->set_internal_formal_parameter_count(2);
 
     // Set the lengths for the functions to satisfy ECMA-262.
     call->shared()->set_length(1);
index dbdd3c73577709f884ab1b8ef534edd0e905f2bd..fb2fbe55df5398d9ed4d7c0351b5a888e9b8d0c3 100644 (file)
@@ -1590,8 +1590,13 @@ class ArgumentsAccessStub: public PlatformCodeStub {
     NEW_STRICT
   };
 
-  ArgumentsAccessStub(Isolate* isolate, Type type) : PlatformCodeStub(isolate) {
-    minor_key_ = TypeBits::encode(type);
+  enum HasNewTarget { NO_NEW_TARGET, HAS_NEW_TARGET };
+
+  ArgumentsAccessStub(Isolate* isolate, Type type,
+                      HasNewTarget has_new_target = NO_NEW_TARGET)
+      : PlatformCodeStub(isolate) {
+    minor_key_ =
+        TypeBits::encode(type) | HasNewTargetBits::encode(has_new_target);
   }
 
   CallInterfaceDescriptor GetCallInterfaceDescriptor() OVERRIDE {
@@ -1603,6 +1608,9 @@ class ArgumentsAccessStub: public PlatformCodeStub {
 
  private:
   Type type() const { return TypeBits::decode(minor_key_); }
+  bool has_new_target() const {
+    return HasNewTargetBits::decode(minor_key_) == HAS_NEW_TARGET;
+  }
 
   void GenerateReadElement(MacroAssembler* masm);
   void GenerateNewStrict(MacroAssembler* masm);
@@ -1612,6 +1620,7 @@ class ArgumentsAccessStub: public PlatformCodeStub {
   void PrintName(std::ostream& os) const OVERRIDE;  // NOLINT
 
   class TypeBits : public BitField<Type, 0, 2> {};
+  class HasNewTargetBits : public BitField<HasNewTarget, 2, 1> {};
 
   DEFINE_PLATFORM_CODE_STUB(ArgumentsAccess, PlatformCodeStub);
 };
@@ -1693,9 +1702,13 @@ class CallConstructStub: public PlatformCodeStub {
     return (flags() & RECORD_CONSTRUCTOR_TARGET) != 0;
   }
 
+  bool IsSuperConstructorCall() const {
+    return (flags() & SUPER_CONSTRUCTOR_CALL) != 0;
+  }
+
   void PrintName(std::ostream& os) const OVERRIDE;  // NOLINT
 
-  class FlagBits : public BitField<CallConstructorFlags, 0, 1> {};
+  class FlagBits : public BitField<CallConstructorFlags, 0, 2> {};
 
   DEFINE_CALL_INTERFACE_DESCRIPTOR(CallConstruct);
   DEFINE_PLATFORM_CODE_STUB(CallConstruct, PlatformCodeStub);
index d0a5fec61ffc6a70494a1520022313f6121f2801..a0639e8deb8ae4e946e1e35a330aea03eee7a895 100644 (file)
@@ -24,7 +24,8 @@ class ParameterCount BASE_EMBEDDED {
   explicit ParameterCount(int immediate)
       : reg_(no_reg), immediate_(immediate) { }
   explicit ParameterCount(Handle<JSFunction> f)
-      : reg_(no_reg), immediate_(f->shared()->formal_parameter_count()) { }
+      : reg_(no_reg),
+        immediate_(f->shared()->internal_formal_parameter_count()) {}
 
   bool is_reg() const { return !reg_.is(no_reg); }
   bool is_immediate() const { return !is_reg(); }
index 5d605fd3e2ecbf026bdc65fe4cb486d4f9f10d95..4b4c9105908cb337869f5d273d8dd541f9f5148a 100644 (file)
@@ -618,7 +618,12 @@ static void SetFunctionInfo(Handle<SharedFunctionInfo> function_info,
                             bool is_toplevel,
                             Handle<Script> script) {
   function_info->set_length(lit->parameter_count());
-  function_info->set_formal_parameter_count(lit->parameter_count());
+  if (FLAG_experimental_classes && IsSubclassConstructor(lit->kind())) {
+    function_info->set_internal_formal_parameter_count(lit->parameter_count() +
+                                                       1);
+  } else {
+    function_info->set_internal_formal_parameter_count(lit->parameter_count());
+  }
   function_info->set_script(*script);
   function_info->set_function_token_position(lit->function_token_position());
   function_info->set_start_position(lit->start_position());
index d326e806078f83197c5734406a872fa5e360c3d3..cf9ef1717177b593119b5326bc214938cbc91a95 100644 (file)
@@ -397,7 +397,9 @@ bool JSGenericLowering::TryLowerDirectJSCall(Node* node) {
   Handle<Object> func = function_const.Value().handle();
   if (!func->IsJSFunction()) return false;  // not a function.
   Handle<JSFunction> function = Handle<JSFunction>::cast(func);
-  if (arg_count != function->shared()->formal_parameter_count()) return false;
+  if (arg_count != function->shared()->internal_formal_parameter_count()) {
+    return false;
+  }
 
   // Check the receiver doesn't need to be wrapped.
   Node* receiver = node->InputAt(1);
index e0fd05d1c6bf84a98bdcb5afc547581ed1ac9c7b..71a669510bd9e44ed25fbb7f66d7982e7e1f6e7f 100644 (file)
@@ -51,7 +51,7 @@ CallDescriptor* Linkage::ComputeIncoming(Zone* zone, CompilationInfo* info) {
     // plus the receiver.
     SharedFunctionInfo* shared = info->closure()->shared();
     return GetJSCallDescriptor(zone, info->is_osr(),
-                               1 + shared->formal_parameter_count(),
+                               1 + shared->internal_formal_parameter_count(),
                                CallDescriptor::kNoFlags);
   }
   if (info->code_stub() != NULL) {
index 59089e03d618efc5a0914e02e1ccb9aa06c4c58f..2ddfbd053255f0049b753b37f59af0281c358252 100644 (file)
@@ -810,6 +810,12 @@ Handle<Code> Pipeline::GenerateCode() {
     return Handle<Code>::null();
   }
 
+  // TODO(dslomov): support turbo optimization of subclass constructors.
+  if (IsSubclassConstructor(shared->kind())) {
+    shared->DisableOptimization(kSuperReference);
+    return Handle<Code>::null();
+  }
+
   ZonePool zone_pool;
   SmartPointer<PipelineStatistics> pipeline_statistics;
 
index 5ce3fa3aa4c069b77143491e058d26176131a9c5..7c79c89078b98a74e8d65bfb766e2670a8850ba4 100644 (file)
@@ -927,7 +927,8 @@ void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
   output_frame->SetTop(top_address);
 
   // Compute the incoming parameter translation.
-  int parameter_count = function->shared()->formal_parameter_count() + 1;
+  int parameter_count =
+      function->shared()->internal_formal_parameter_count() + 1;
   unsigned output_offset = output_frame_size;
   unsigned input_offset = input_frame_size;
   for (int i = 0; i < parameter_count; ++i) {
@@ -2724,7 +2725,8 @@ unsigned Deoptimizer::ComputeIncomingArgumentSize(JSFunction* function) const {
     CHECK_EQ(Smi::cast(function), Smi::FromInt(StackFrame::STUB));
     return 0;
   }
-  unsigned arguments = function->shared()->formal_parameter_count() + 1;
+  unsigned arguments =
+      function->shared()->internal_formal_parameter_count() + 1;
   return arguments * kPointerSize;
 }
 
@@ -2861,7 +2863,7 @@ unsigned FrameDescription::GetOffsetFromSlotIndex(int slot_index) {
 int FrameDescription::ComputeParametersCount() {
   switch (type_) {
     case StackFrame::JAVA_SCRIPT:
-      return function_->shared()->formal_parameter_count();
+      return function_->shared()->internal_formal_parameter_count();
     case StackFrame::ARGUMENTS_ADAPTOR: {
       // Last slot contains number of incomming arguments as a smi.
       // Can't use GetExpression(0) because it would cause infinite recursion.
index b3779f291af96bf7cefd769c838fc1534aa9a665..b888389926b88cd3d0b550ba708b5ba25eaaa235 100644 (file)
@@ -2103,7 +2103,7 @@ Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfo(
 
   // Set integer fields (smi or int, depending on the architecture).
   share->set_length(0);
-  share->set_formal_parameter_count(0);
+  share->set_internal_formal_parameter_count(0);
   share->set_expected_nof_properties(0);
   share->set_num_literals(0);
   share->set_start_position_and_type(0);
index 0ba8ea0020ed379dc2d70fe423b6ac00d983f6f1..b7fba653d527eb3d788e72b5db64f618a60f2cb0 100644 (file)
@@ -754,7 +754,7 @@ int JavaScriptFrame::GetNumberOfIncomingArguments() const {
   DCHECK(can_access_heap_objects() &&
          isolate()->heap()->gc_state() == Heap::NOT_IN_GC);
 
-  return function()->shared()->formal_parameter_count();
+  return function()->shared()->internal_formal_parameter_count();
 }
 
 
@@ -1310,7 +1310,7 @@ void ArgumentsAdaptorFrame::Print(StringStream* accumulator,
   int actual = ComputeParametersCount();
   int expected = -1;
   JSFunction* function = this->function();
-  expected = function->shared()->formal_parameter_count();
+  expected = function->shared()->internal_formal_parameter_count();
 
   PrintIndex(accumulator, mode, index);
   accumulator->Add("arguments adaptor frame: %d->%d", actual, expected);
index 0896e429d6b1325274beb5f4abf0cf0e1c823b10..44a04ef2a5d93eb7ca10f0bbc7a74f96625180a4 100644 (file)
@@ -1652,8 +1652,8 @@ void FullCodeGenerator::VisitNativeFunctionLiteral(
 
   // Copy the function data to the shared function info.
   shared->set_function_data(fun->shared()->function_data());
-  int parameters = fun->shared()->formal_parameter_count();
-  shared->set_formal_parameter_count(parameters);
+  int parameters = fun->shared()->internal_formal_parameter_count();
+  shared->set_internal_formal_parameter_count(parameters);
 
   EmitNewClosure(shared, false);
 }
index 740ccf8431e306c66c0f8aefffa00740d1c00e19..bb9a417867e927a9c247b33eefc520ddb9655979 100644 (file)
@@ -527,9 +527,11 @@ enum CallFunctionFlags {
 
 
 enum CallConstructorFlags {
-  NO_CALL_CONSTRUCTOR_FLAGS,
+  NO_CALL_CONSTRUCTOR_FLAGS = 0,
   // The call target is cached in the instruction stream.
-  RECORD_CONSTRUCTOR_TARGET
+  RECORD_CONSTRUCTOR_TARGET = 1,
+  SUPER_CONSTRUCTOR_CALL = 1 << 1,
+  SUPER_CALL_RECORD_TARGET = SUPER_CONSTRUCTOR_CALL | RECORD_CONSTRUCTOR_TARGET
 };
 
 
index 1c54fa5ba6464f7cc052bf5ee356ebb565635e8e..6237fc94d7d4e89a7bc1e50f0f7f6642401305cd 100644 (file)
@@ -2393,8 +2393,10 @@ class HInvokeFunction FINAL : public HBinaryCall {
                   int argument_count)
       : HBinaryCall(context, function, argument_count),
         known_function_(known_function) {
-    formal_parameter_count_ = known_function.is_null()
-        ? 0 : known_function->shared()->formal_parameter_count();
+    formal_parameter_count_ =
+        known_function.is_null()
+            ? 0
+            : known_function->shared()->internal_formal_parameter_count();
     has_stack_check_ = !known_function.is_null() &&
         (known_function->code()->kind() == Code::FUNCTION ||
          known_function->code()->kind() == Code::OPTIMIZED_FUNCTION);
index d7619e411fc146cd1d443f26d94f3f331acb8c34..7d154642ceeef58a093bc4724a99a07d24f5ef28 100644 (file)
@@ -4281,6 +4281,11 @@ void HOptimizedGraphBuilder::VisitExpressions(ZoneList<Expression*>* exprs,
 
 
 bool HOptimizedGraphBuilder::BuildGraph() {
+  if (IsSubclassConstructor(current_info()->function()->kind())) {
+    Bailout(kSuperReference);
+    return false;
+  }
+
   Scope* scope = current_info()->scope();
   SetUpScope(scope);
 
@@ -7565,7 +7570,8 @@ HInstruction* HOptimizedGraphBuilder::BuildCallConstantFunction(
   HValue* target = Add<HConstant>(jsfun);
   // For constant functions, we try to avoid calling the
   // argument adaptor and instead call the function directly
-  int formal_parameter_count = jsfun->shared()->formal_parameter_count();
+  int formal_parameter_count =
+      jsfun->shared()->internal_formal_parameter_count();
   bool dont_adapt_arguments =
       (formal_parameter_count ==
        SharedFunctionInfo::kDontAdaptArgumentsSentinel);
index 18966c293f238569eeac345b75ec2d5a7c093bf4..b9a27c37b7d293982c2dda0c3e7964185fd338ed 100644 (file)
@@ -519,6 +519,9 @@ void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) {
     __ push(eax);
     __ SmiUntag(eax);
 
+    // Push new.target.
+    __ push(edx);
+
     // receiver is the hole.
     __ push(Immediate(masm->isolate()->factory()->the_hole_value()));
 
@@ -535,6 +538,7 @@ void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) {
     __ dec(ecx);
     __ j(greater_equal, &loop);
 
+    __ inc(eax);  // Pushed new.target.
     ParameterCount actual(eax);
     __ InvokeFunction(edi, actual, CALL_FUNCTION, NullCallWrapper());
 
index 819a30587a791c03a80983815f44965d8584f0ad..d42c0e4416e426407a60c866dedfb68d974f53c2 100644 (file)
@@ -738,6 +738,7 @@ void LoadIndexedStringStub::Generate(MacroAssembler* masm) {
 
 
 void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
+  CHECK(!has_new_target());
   // The key is in edx and the parameter count is in eax.
   DCHECK(edx.is(ArgumentsAccessReadDescriptor::index()));
   DCHECK(eax.is(ArgumentsAccessReadDescriptor::parameter_count()));
@@ -804,6 +805,8 @@ void ArgumentsAccessStub::GenerateNewSloppySlow(MacroAssembler* masm) {
   // esp[8] : receiver displacement
   // esp[12] : function
 
+  CHECK(!has_new_target());
+
   // Check if the calling frame is an arguments adaptor frame.
   Label runtime;
   __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
@@ -832,6 +835,8 @@ void ArgumentsAccessStub::GenerateNewSloppyFast(MacroAssembler* masm) {
   // ebx = parameter count (tagged)
   __ mov(ebx, Operand(esp, 1 * kPointerSize));
 
+  CHECK(!has_new_target());
+
   // Check if the calling frame is an arguments adaptor frame.
   // TODO(rossberg): Factor out some of the bits that are shared with the other
   // Generate* functions.
@@ -1071,9 +1076,15 @@ void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
   // Patch the arguments.length and the parameters pointer.
   __ bind(&adaptor_frame);
   __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
-  __ mov(Operand(esp, 1 * kPointerSize), ecx);
+
+  if (has_new_target()) {
+    // Subtract 1 from smi-tagged arguments count.
+    __ sub(ecx, Immediate(2));
+  }
+
   __ lea(edx, Operand(edx, ecx, times_2,
                       StandardFrameConstants::kCallerSPOffset));
+  __ mov(Operand(esp, 1 * kPointerSize), ecx);
   __ mov(Operand(esp, 2 * kPointerSize), edx);
 
   // Try the new space allocation. Start out with computing the size of
@@ -2149,8 +2160,12 @@ void CallConstructStub::Generate(MacroAssembler* masm) {
     __ AssertUndefinedOrAllocationSite(ebx);
   }
 
-  // Pass original constructor to construct stub.
-  __ mov(edx, edi);
+  if (IsSuperConstructorCall()) {
+    __ mov(edx, Operand(esp, eax, times_pointer_size, 2 * kPointerSize));
+  } else {
+    // Pass original constructor to construct stub.
+    __ mov(edx, edi);
+  }
 
   // Jump to the function-specific construct stub.
   Register jmp_reg = ecx;
index 4c1475b9d25c17f82698a027effc88fd059c39bd..27e308d2989c16d3e1205c16db23370fc60212f4 100644 (file)
@@ -212,7 +212,8 @@ void Deoptimizer::CopyDoubleRegisters(FrameDescription* output_frame) {
 
 
 bool Deoptimizer::HasAlignmentPadding(JSFunction* function) {
-  int parameter_count = function->shared()->formal_parameter_count() + 1;
+  int parameter_count =
+      function->shared()->internal_formal_parameter_count() + 1;
   unsigned input_frame_size = input_->GetFrameSize();
   unsigned alignment_state_offset =
       input_frame_size - parameter_count * kPointerSize -
index 950a129499126f0e6f42d58afe7d08ee8571114f..ca51dc32e64b52ded204a2028971eddf71550955 100644 (file)
@@ -264,7 +264,11 @@ void FullCodeGenerator::Generate() {
     } else {
       type = ArgumentsAccessStub::NEW_SLOPPY_FAST;
     }
-    ArgumentsAccessStub stub(isolate(), type);
+    ArgumentsAccessStub::HasNewTarget has_new_target =
+        IsSubclassConstructor(info->function()->kind())
+            ? ArgumentsAccessStub::HAS_NEW_TARGET
+            : ArgumentsAccessStub::NO_NEW_TARGET;
+    ArgumentsAccessStub stub(isolate(), type, has_new_target);
     __ CallStub(&stub);
 
     SetVar(arguments, eax, ebx, edx);
@@ -413,7 +417,12 @@ void FullCodeGenerator::EmitReturnSequence() {
     int no_frame_start = masm_->pc_offset();
     __ pop(ebp);
 
-    int arguments_bytes = (info_->scope()->num_parameters() + 1) * kPointerSize;
+    int arg_count = info_->scope()->num_parameters() + 1;
+    if (FLAG_experimental_classes &&
+        IsSubclassConstructor(info_->function()->kind())) {
+      arg_count++;
+    }
+    int arguments_bytes = arg_count * kPointerSize;
     __ Ret(arguments_bytes, ecx);
     // Check that the size of the code used for returning is large enough
     // for the debugger's requirements.
@@ -3137,6 +3146,10 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
 
 
 void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
+  Variable* new_target_var = scope()->DeclarationScope()->new_target_var();
+  GetVar(eax, new_target_var);
+  __ push(eax);
+
   SuperReference* super_ref = expr->expression()->AsSuperReference();
   EmitLoadSuperConstructor(super_ref);
   __ push(result_register());
@@ -3178,10 +3191,11 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
   __ 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);
+  CallConstructStub stub(isolate(), SUPER_CALL_RECORD_TARGET);
   __ call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
 
+  __ Drop(1);
+
   RecordJSReturnSite(expr);
 
   EmitVariableAssignment(this_var, Token::INIT_CONST);
index 60e03b770eba8c2cf429be92eea9ef5c1ba20f8f..ce5cc78b4391c0a1dca9f2361169d809e2f53fc4 100644 (file)
@@ -1237,7 +1237,8 @@ Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
           return compiler.CompileLoadCallback(lookup->name(), call_optimization,
                                               lookup->GetAccessorIndex());
         }
-        int expected_arguments = function->shared()->formal_parameter_count();
+        int expected_arguments =
+            function->shared()->internal_formal_parameter_count();
         return compiler.CompileLoadViaGetter(
             lookup->name(), lookup->GetAccessorIndex(), expected_arguments);
       }
@@ -1763,7 +1764,8 @@ Handle<Code> StoreIC::CompileHandler(LookupIterator* lookup,
                                                call_optimization,
                                                lookup->GetAccessorIndex());
         }
-        int expected_arguments = function->shared()->formal_parameter_count();
+        int expected_arguments =
+            function->shared()->internal_formal_parameter_count();
         return compiler.CompileStoreViaSetter(receiver, lookup->name(),
                                               lookup->GetAccessorIndex(),
                                               expected_arguments);
index 75e5ae39b8ca7f7f8a1e0662326f067865f90b99..4aadf12d78e17876a7928c934e1376f9d607dfb7 100644 (file)
@@ -5718,7 +5718,7 @@ BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, deserialized, kDeserialized)
 
 #if V8_HOST_ARCH_32_BIT
 SMI_ACCESSORS(SharedFunctionInfo, length, kLengthOffset)
-SMI_ACCESSORS(SharedFunctionInfo, formal_parameter_count,
+SMI_ACCESSORS(SharedFunctionInfo, internal_formal_parameter_count,
               kFormalParameterCountOffset)
 SMI_ACCESSORS(SharedFunctionInfo, expected_nof_properties,
               kExpectedNofPropertiesOffset)
@@ -5766,8 +5766,7 @@ SMI_ACCESSORS(SharedFunctionInfo, profiler_ticks, kProfilerTicksOffset)
 
 
 PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, length, kLengthOffset)
-PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo,
-                        formal_parameter_count,
+PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, internal_formal_parameter_count,
                         kFormalParameterCountOffset)
 
 PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo,
@@ -5899,7 +5898,7 @@ bool Script::HasValidSource() {
 
 void SharedFunctionInfo::DontAdaptArguments() {
   DCHECK(code()->kind() == Code::BUILTIN);
-  set_formal_parameter_count(kDontAdaptArgumentsSentinel);
+  set_internal_formal_parameter_count(kDontAdaptArgumentsSentinel);
 }
 
 
@@ -6080,8 +6079,8 @@ bool JSFunction::IsFromExtensionScript() {
 
 
 bool JSFunction::NeedsArgumentsAdaption() {
-  return shared()->formal_parameter_count() !=
-      SharedFunctionInfo::kDontAdaptArgumentsSentinel;
+  return shared()->internal_formal_parameter_count() !=
+         SharedFunctionInfo::kDontAdaptArgumentsSentinel;
 }
 
 
index a86e6b10b482523908cc30b49bd8a4764a3f880d..4df1966229346ecd5e522f88c2aeb429937300b6 100644 (file)
@@ -6770,9 +6770,11 @@ class SharedFunctionInfo: public HeapObject {
   inline int length() const;
   inline void set_length(int value);
 
-  // [formal parameter count]: The declared number of parameters.
-  inline int formal_parameter_count() const;
-  inline void set_formal_parameter_count(int value);
+  // [internal formal parameter count]: The declared number of parameters.
+  // For subclass constructors, also includes new.target.
+  // The size of function's frame is internal_formal_parameter_count + 1.
+  inline int internal_formal_parameter_count() const;
+  inline void set_internal_formal_parameter_count(int value);
 
   // Set the formal parameter count so the function code will be
   // called without using argument adaptor frames.
index ffd121e612ef4d3dd6572ce7dff205cf02139153..077c3d5abee68d05655f5f87a7d053d1864633a1 100644 (file)
@@ -117,7 +117,7 @@ RUNTIME_FUNCTION(Runtime_DefineClass) {
   constructor->shared()->set_name(*name_string);
 
   if (FLAG_experimental_classes) {
-    if (!super_class->IsTheHole() && !super_class->IsNull()) {
+    if (!super_class->IsTheHole()) {
       Handle<Code> stub(isolate->builtins()->JSConstructStubForDerived());
       constructor->shared()->set_construct_stub(*stub);
     }
index 0a9cd6313f53fc505f5c671cb9219649d289006f..18fe57738dc3276291248bd0da8c4672f5c74109 100644 (file)
@@ -271,8 +271,8 @@ RUNTIME_FUNCTION(Runtime_SetCode) {
   target_shared->set_scope_info(source_shared->scope_info());
   target_shared->set_length(source_shared->length());
   target_shared->set_feedback_vector(source_shared->feedback_vector());
-  target_shared->set_formal_parameter_count(
-      source_shared->formal_parameter_count());
+  target_shared->set_internal_formal_parameter_count(
+      source_shared->internal_formal_parameter_count());
   target_shared->set_script(source_shared->script());
   target_shared->set_start_position_and_type(
       source_shared->start_position_and_type());
@@ -383,7 +383,7 @@ static SmartArrayPointer<Handle<Object> > GetCallerArguments(Isolate* isolate,
     JSFunction* inlined_function = functions[inlined_jsframe_index];
     SlotRefValueBuilder slot_refs(
         frame, inlined_jsframe_index,
-        inlined_function->shared()->formal_parameter_count());
+        inlined_function->shared()->internal_formal_parameter_count());
 
     int args_count = slot_refs.args_length();
 
index bde3021df16653280c3576aa15552c39764d6bda..844c633bdb82d6438ac4a830be021251007b814c 100644 (file)
@@ -1245,10 +1245,6 @@ 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);
@@ -1258,6 +1254,11 @@ static Object* Runtime_NewObjectHelper(Isolate* isolate,
 
   Handle<JSFunction> function = Handle<JSFunction>::cast(constructor);
 
+  CHECK(original_constructor->IsJSFunction());
+  Handle<JSFunction> original_function =
+      Handle<JSFunction>::cast(original_constructor);
+
+
   // If function should not have prototype, construction is not allowed. In this
   // case generated code bailouts here, since function has no initial_map.
   if (!function->should_have_prototype() && !function->shared()->bound()) {
@@ -1300,6 +1301,18 @@ static Object* Runtime_NewObjectHelper(Isolate* isolate,
     result = isolate->factory()->NewJSObjectWithMemento(function, site);
   }
 
+  // Set up the prototoype using original function.
+  // TODO(dslomov): instead of setting the __proto__,
+  // use and cache the correct map.
+  if (*original_function != *function) {
+    if (original_function->has_instance_prototype()) {
+      Handle<Object> prototype =
+          handle(original_function->instance_prototype(), isolate);
+      RETURN_FAILURE_ON_EXCEPTION(
+          isolate, JSObject::SetPrototype(result, prototype, false));
+    }
+  }
+
   isolate->counters()->constructed_objects()->Increment();
   isolate->counters()->constructed_objects_runtime()->Increment();
 
index e3a0d52d569e13de5daf97473155c0cc7aef630e..b1c0fe33d95bba15b70810b8b9e7ab13b068555b 100644 (file)
@@ -356,11 +356,12 @@ static Handle<JSObject> NewSloppyArguments(Isolate* isolate,
                                            Handle<JSFunction> callee,
                                            Object** parameters,
                                            int argument_count) {
+  CHECK(!IsSubclassConstructor(callee->shared()->kind()));
   Handle<JSObject> result =
       isolate->factory()->NewArgumentsObject(callee, argument_count);
 
   // Allocate the elements if needed.
-  int parameter_count = callee->shared()->formal_parameter_count();
+  int parameter_count = callee->shared()->internal_formal_parameter_count();
   if (argument_count > 0) {
     if (parameter_count > 0) {
       int mapped_count = Min(argument_count, parameter_count);
index fa95bde2b2ff0f6062427003850bf94d2af5db8b..16d2c111b9a86b179562839e61a32dee928824aa 100644 (file)
@@ -148,6 +148,7 @@ void Scope::SetDefaults(ScopeType scope_type,
   scope_name_ = ast_value_factory_->empty_string();
   dynamics_ = NULL;
   receiver_ = NULL;
+  new_target_ = nullptr;
   function_ = NULL;
   arguments_ = NULL;
   illegal_redecl_ = NULL;
@@ -287,7 +288,7 @@ bool Scope::Analyze(CompilationInfo* info) {
 }
 
 
-void Scope::Initialize(bool uninitialized_this) {
+void Scope::Initialize(bool subclass_constructor) {
   DCHECK(!already_resolved());
 
   // Add this scope as a new inner scope of the outer scope.
@@ -307,14 +308,22 @@ void Scope::Initialize(bool uninitialized_this) {
   // such parameter is 'this' which is passed on the stack when
   // invoking scripts
   if (is_declaration_scope()) {
-    DCHECK(!uninitialized_this || is_function_scope());
-    DCHECK(FLAG_experimental_classes || !uninitialized_this);
+    DCHECK(!subclass_constructor || is_function_scope());
+    DCHECK(FLAG_experimental_classes || !subclass_constructor);
     Variable* var = variables_.Declare(
         this, ast_value_factory_->this_string(),
-        uninitialized_this ? CONST : VAR, false, Variable::THIS,
-        uninitialized_this ? kNeedsInitialization : kCreatedInitialized);
+        subclass_constructor ? CONST : VAR, false, Variable::THIS,
+        subclass_constructor ? kNeedsInitialization : kCreatedInitialized);
     var->AllocateTo(Variable::PARAMETER, -1);
     receiver_ = var;
+
+    if (subclass_constructor) {
+      new_target_ = variables_.Declare(
+          this, ast_value_factory_->new_target_string(), CONST, false,
+          Variable::NEW_TARGET, kCreatedInitialized);
+      new_target_->AllocateTo(Variable::PARAMETER, -2);
+      new_target_->set_is_used();
+    }
   } else {
     DCHECK(outer_scope() != NULL);
     receiver_ = outer_scope()->receiver();
@@ -1201,15 +1210,10 @@ bool Scope::MustAllocate(Variable* var) {
   // Give var a read/write use if there is a chance it might be accessed
   // via an eval() call.  This is only possible if the variable has a
   // visible name.
-  if ((var->is_this() || !var->raw_name()->IsEmpty()) &&
-      (var->has_forced_context_allocation() ||
-       scope_calls_eval_ ||
-       inner_scope_calls_eval_ ||
-       scope_contains_with_ ||
-       is_catch_scope() ||
-       is_block_scope() ||
-       is_module_scope() ||
-       is_script_scope())) {
+  if ((var->is_this() || var->is_new_target() || !var->raw_name()->IsEmpty()) &&
+      (var->has_forced_context_allocation() || scope_calls_eval_ ||
+       inner_scope_calls_eval_ || scope_contains_with_ || is_catch_scope() ||
+       is_block_scope() || is_module_scope() || is_script_scope())) {
     var->set_is_used();
     if (scope_calls_eval_ || inner_scope_calls_eval_) var->set_maybe_assigned();
   }
index cd4658d37bca7ae3fbf7ac007323c90217074ed8..2cc8b3e9569bf80bc11d49dfdda733d134ef7f73 100644 (file)
@@ -88,7 +88,7 @@ class Scope: public ZoneObject {
     scope_name_ = scope_name;
   }
 
-  void Initialize(bool uninitialized_this = false);
+  void Initialize(bool subclass_constructor = false);
 
   // Checks if the block scope is redundant, i.e. it does not contain any
   // block scoped declarations. In that case it is removed from the scope
@@ -343,9 +343,12 @@ class Scope: public ZoneObject {
   // The language mode of this scope.
   LanguageMode language_mode() const { return language_mode_; }
 
-  // The variable corresponding the 'this' value.
+  // The variable corresponding to the 'this' value.
   Variable* receiver() { return receiver_; }
 
+  // The variable corresponding to the 'new.target' value.
+  Variable* new_target_var() { return new_target_; }
+
   // The variable holding the function literal for named function
   // literals, or NULL.  Only valid for function scopes.
   VariableDeclaration* function() const {
@@ -517,6 +520,8 @@ class Scope: public ZoneObject {
   Variable* receiver_;
   // Function variable, if any; function scopes only.
   VariableDeclaration* function_;
+  // new.target variable, function scopes only.
+  Variable* new_target_;
   // Convenience variable; function scopes only.
   Variable* arguments_;
   // Interface; module scopes only.
index 93bfb4a1816aeb3fb9f370c847ace86da6f2d6b3..e35ae7d1cc377f823cf57837574c00b1cf14f953 100644 (file)
@@ -19,11 +19,7 @@ namespace internal {
 
 class Variable: public ZoneObject {
  public:
-  enum Kind {
-    NORMAL,
-    THIS,
-    ARGUMENTS
-  };
+  enum Kind { NORMAL, THIS, NEW_TARGET, ARGUMENTS };
 
   enum Location {
     // Before and during variable allocation, a variable whose location is
@@ -105,6 +101,7 @@ class Variable: public ZoneObject {
   }
 
   bool is_this() const { return kind_ == THIS; }
+  bool is_new_target() const { return kind_ == NEW_TARGET; }
   bool is_arguments() const { return kind_ == ARGUMENTS; }
 
   // True if the variable is named eval and not known to be shadowed.
index 80a41dde16c6d06b570347c9d2772fb1795db1b3..ca23b369234953fca11edbe9658e53f78f539969 100644 (file)
@@ -516,6 +516,9 @@ void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) {
     __ Push(rax);
     __ SmiToInteger32(rax, rax);
 
+    // Push new.target
+    __ Push(rdx);
+
     // receiver is the hole.
     __ Push(masm->isolate()->factory()->the_hole_value());
 
@@ -533,6 +536,7 @@ void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) {
     __ j(greater_equal, &loop);
 
     // Call the function.
+    __ incp(rax);  // Pushed new.target.
     ParameterCount actual(rax);
     __ InvokeFunction(rdi, actual, CALL_FUNCTION, NullCallWrapper());
 
index abb3d4a988e58c1cb7ab7e6b16deaaa4892b8007..e56fc4a5702f3662125b6b911d8bd61d88418c0c 100644 (file)
@@ -539,6 +539,7 @@ void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
 
 
 void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
+  CHECK(!has_new_target());
   // The key is in rdx and the parameter count is in rax.
   DCHECK(rdx.is(ArgumentsAccessReadDescriptor::index()));
   DCHECK(rax.is(ArgumentsAccessReadDescriptor::parameter_count()));
@@ -606,6 +607,8 @@ void ArgumentsAccessStub::GenerateNewSloppyFast(MacroAssembler* masm) {
   //  rbx: the mapped parameter count (untagged)
   //  rax: the allocated object (tagged).
 
+  CHECK(!has_new_target());
+
   Factory* factory = isolate()->factory();
 
   StackArgumentsAccessor args(rsp, 3, ARGUMENTS_DONT_CONTAIN_RECEIVER);
@@ -819,6 +822,7 @@ void ArgumentsAccessStub::GenerateNewSloppySlow(MacroAssembler* masm) {
   // rsp[8]  : number of parameters
   // rsp[16] : receiver displacement
   // rsp[24] : function
+  CHECK(!has_new_target());
 
   // Check if the calling frame is an arguments adaptor frame.
   Label runtime;
@@ -928,6 +932,13 @@ void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
   // Patch the arguments.length and the parameters pointer.
   __ bind(&adaptor_frame);
   __ movp(rcx, Operand(rdx, ArgumentsAdaptorFrameConstants::kLengthOffset));
+
+  if (has_new_target()) {
+    // Subtract 1 from smi-tagged arguments count.
+    __ SmiToInteger32(rcx, rcx);
+    __ decp(rcx);
+    __ Integer32ToSmi(rcx, rcx);
+  }
   __ movp(args.GetArgumentOperand(2), rcx);
   __ SmiToInteger64(rcx, rcx);
   __ leap(rdx, Operand(rdx, rcx, times_pointer_size,
@@ -2018,7 +2029,11 @@ void CallConstructStub::Generate(MacroAssembler* masm) {
   }
 
   // Pass original constructor to construct stub.
-  __ movp(rdx, rdi);
+  if (IsSuperConstructorCall()) {
+    __ movp(rdx, Operand(rsp, rax, times_pointer_size, 2 * kPointerSize));
+  } else {
+    __ movp(rdx, rdi);
+  }
 
   // Jump to the function-specific construct stub.
   Register jmp_reg = rcx;
index 65e60fb855368667302c9cab27e7dce5c1a3bca7..63357e95f591de24f907152f0853e67dd8087cf9 100644 (file)
@@ -254,6 +254,11 @@ void FullCodeGenerator::Generate() {
     //   function, receiver address, parameter count.
     // The stub will rewrite receiver and parameter count if the previous
     // stack frame was an arguments adapter frame.
+
+    ArgumentsAccessStub::HasNewTarget has_new_target =
+        IsSubclassConstructor(info->function()->kind())
+            ? ArgumentsAccessStub::HAS_NEW_TARGET
+            : ArgumentsAccessStub::NO_NEW_TARGET;
     ArgumentsAccessStub::Type type;
     if (is_strict(language_mode())) {
       type = ArgumentsAccessStub::NEW_STRICT;
@@ -262,7 +267,7 @@ void FullCodeGenerator::Generate() {
     } else {
       type = ArgumentsAccessStub::NEW_SLOPPY_FAST;
     }
-    ArgumentsAccessStub stub(isolate(), type);
+    ArgumentsAccessStub stub(isolate(), type, has_new_target);
     __ CallStub(&stub);
 
     SetVar(arguments, rax, rbx, rdx);
@@ -416,7 +421,12 @@ void FullCodeGenerator::EmitReturnSequence() {
     __ popq(rbp);
     int no_frame_start = masm_->pc_offset();
 
-    int arguments_bytes = (info_->scope()->num_parameters() + 1) * kPointerSize;
+    int arg_count = info_->scope()->num_parameters() + 1;
+    if (FLAG_experimental_classes &&
+        IsSubclassConstructor(info_->function()->kind())) {
+      arg_count++;
+    }
+    int arguments_bytes = arg_count * kPointerSize;
     __ Ret(arguments_bytes, rcx);
 
     // Add padding that will be overwritten by a debugger breakpoint.  We
@@ -3142,6 +3152,10 @@ void FullCodeGenerator::VisitCallNew(CallNew* expr) {
 
 
 void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
+  Variable* new_target_var = scope()->DeclarationScope()->new_target_var();
+  GetVar(result_register(), new_target_var);
+  __ Push(result_register());
+
   SuperReference* super_ref = expr->expression()->AsSuperReference();
   EmitLoadSuperConstructor(super_ref);
   __ Push(result_register());
@@ -3186,11 +3200,14 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
   __ Move(rdx, SmiFromSlot(expr->CallFeedbackSlot()));
 
   // TODO(dslomov): use a different stub and propagate new.target.
-  CallConstructStub stub(isolate(), RECORD_CONSTRUCTOR_TARGET);
+  CallConstructStub stub(isolate(), SUPER_CALL_RECORD_TARGET);
   __ call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
 
+  __ Drop(1);
+
   RecordJSReturnSite(expr);
 
+
   EmitVariableAssignment(this_var, Token::INIT_CONST);
   context()->Plug(rax);
 }
index ed720d97b6275462e397d3f55e81746a450839f1..36c4dd82465130b767c460bf67fcae9f2077eabc 100644 (file)
 // Flags: --experimental-classes --harmony-classes
 
 'use strict';
+(function TestArgumentsAccess() {
+  class Base {
+    constructor() {
+      assertEquals(2, arguments.length);
+      assertEquals(1, arguments[0]);
+      assertEquals(2, arguments[1]);
+    }
+  }
+
+  let b = new Base(1,2);
+
+  class Subclass extends Base {
+    constructor() {
+      assertEquals(2, arguments.length);
+      assertEquals(3, arguments[0]);
+      assertEquals(4, arguments[1]);
+      super(1,2);
+    }
+  }
+
+  let s = new Subclass(3,4);
+  assertEquals(0, Subclass.length);
+
+  class Subclass2 extends Base {
+    constructor(x,y) {
+      assertEquals(2, arguments.length);
+      assertEquals(3, arguments[0]);
+      assertEquals(4, arguments[1]);
+      super(1,2);
+    }
+  }
+
+  let s2 = new Subclass2(3,4);
+  assertEquals(2, Subclass2.length);
+}());
+
+(function TestThisAccessRestriction() {
+  class Base {
+    constructor(a, b) {
+      let o = new Object();
+      o.prp = a + b;
+      return o;
+    }
+  }
+
+  class Subclass extends Base {
+    constructor(a, b) {
+      var exn;
+      try {
+        this.prp1 = 3;
+      } catch (e) {
+        exn = e;
+      }
+      assertTrue(exn instanceof ReferenceError);
+      super(a, b);
+      assertSame(a + b, this.prp);
+      assertSame(undefined, this.prp1);
+      assertFalse(this.hasOwnProperty("prp1"));
+      return this;
+    }
+  }
+
+  let b = new Base(1, 2);
+  assertSame(3, b.prp);
+
+
+  let s = new Subclass(2, -1);
+  assertSame(1, s.prp);
+  assertSame(undefined, s.prp1);
+  assertFalse(s.hasOwnProperty("prp1"));
+
+  class Subclass2 extends Base {
+    constructor(x) {
+      super(1,2);
+
+      if (x < 0) return;
+
+      let called = false;
+      function tmp() { called = true; return 3; }
+      var exn = null;
+      try {
+        super(tmp(),4);
+      } catch (e) { exn = e; }
+      assertTrue(exn instanceof ReferenceError);
+      // TODO(dslomov): should be 'true'.
+      assertFalse(called);
+    }
+  }
+
+  var s2 = new Subclass2(1);
+  assertSame(3, s2.prp);
 
-class Base {
-  constructor(a, b) {
-    let o = new Object();
-    o.prp = a + b;
-    return o;
+  var s3 = new Subclass2(-1);
+  assertSame(3, s3.prp);
+
+  assertThrows(function() { Subclass.call(new Object(), 1, 2); }, TypeError);
+  assertThrows(function() { Base.call(new Object(), 1, 2); }, TypeError);
+
+  class BadSubclass extends Base {
+    constructor() {}
+  }
+
+  assertThrows(function() { new BadSubclass(); }, ReferenceError);
+}());
+
+(function TestPrototypeWiring() {
+  class Base {
+    constructor(x) {
+      this.foobar = x;
+    }
+  }
+
+  class Subclass extends Base {
+    constructor(x) {
+      super(x);
+    }
+  }
+
+  let s = new Subclass(1);
+  assertSame(1, s.foobar);
+  assertSame(Subclass.prototype, s.__proto__);
+
+  let s1 = new Subclass(1, 2);
+  assertSame(1, s1.foobar);
+  assertTrue(s1.__proto__ === Subclass.prototype);
+
+  let s2 = new Subclass();
+  assertSame(undefined, s2.foobar);
+  assertSame(Subclass.prototype, s2.__proto__);
+  assertThrows(function() { Subclass(1); }, TypeError);
+  assertThrows(function() { Subclass(1,2,3,4); }, TypeError);
+
+  class Subclass2 extends Subclass {
+    constructor() {
+      super(5, 6, 7);
+    }
   }
-}
-
-class Subclass extends Base {
-  constructor(a, b) {
-    var exn;
-    try {
-      this.prp1 = 3;
-    } catch (e) {
-      exn = e;
+
+  let ss2 = new Subclass2();
+  assertSame(5, ss2.foobar);
+  assertSame(Subclass2.prototype, ss2.__proto__);
+
+  class Subclass3 extends Base {
+    constructor(x,y) {
+      super(x + y);
     }
-    assertTrue(exn instanceof ReferenceError);
-    super(a, b);
-    assertSame(a + b, this.prp);
-    assertSame(undefined, this.prp1);
-    assertFalse(this.hasOwnProperty("prp1"));
-    return this;
   }
-}
 
-let b = new Base(1, 2);
-assertSame(3, b.prp);
+  let ss3 = new Subclass3(27,42-27);
+  assertSame(42, ss3.foobar);
+  assertSame(Subclass3.prototype, ss3.__proto__);
+}());
 
+(function TestSublclassingBuiltins() {
+  class ExtendedUint8Array extends Uint8Array {
+    constructor() {
+      super(10);
+      this[0] = 255;
+      this[1] = 0xFFA;
+    }
+  }
 
-let s = new Subclass(2, -1);
-assertSame(1, s.prp);
-assertSame(undefined, s.prp1);
-assertFalse(s.hasOwnProperty("prp1"));
+  var eua = new ExtendedUint8Array();
+  assertEquals(10, eua.length);
+  assertEquals(10, eua.byteLength);
+  assertEquals(0xFF, eua[0]);
+  assertEquals(0xFA, eua[1]);
+  assertTrue(eua.__proto__ === ExtendedUint8Array.prototype);
+  assertEquals("[object Uint8Array]", Object.prototype.toString.call(eua));
+}());
 
-class Subclass2 extends Base {
-  constructor(x) {
-    super(1,2);
+(function TestSubclassingNull() {
+  let N = null;
 
-    if (x < 0) return;
+  class Foo extends N {
+    constructor(x,y) {
+      assertSame(1, x);
+      assertSame(2, y);
+      return {};
+    }
+  }
+
+  new Foo(1,2);
+}());
+
+(function TestSubclassBinding() {
+  class Base {
+    constructor(x, y) {
+      this.x = x;
+      this.y = y;
+    }
+  }
 
-    let called = false;
-    function tmp() { called = true; return 3; }
-    var exn = null;
-    try {
-      super(tmp(),4);
-    } catch(e) { exn = e; }
-    assertTrue(exn !== null);
-    assertFalse(called);
+  let obj = {};
+  class Subclass extends Base {
+    constructor(x,y) {
+      super(x,y);
+      assertTrue(this !== obj);
+    }
   }
-}
 
-var s2 = new Subclass2(1);
-assertSame(3, s2.prp);
+  let f = Subclass.bind(obj);
+  assertThrows(function () { f(1, 2); }, TypeError);
+  let s = new f(1, 2);
+  assertSame(1, s.x);
+  assertSame(2, s.y);
+  assertSame(Subclass.prototype, s.__proto__);
 
-var s3 = new Subclass2(-1);
-assertSame(3, s3.prp);
+  let s1 = new f(1);
+  assertSame(1, s1.x);
+  assertSame(undefined, s1.y);
+  assertSame(Subclass.prototype, s1.__proto__);
 
-assertThrows(function() { Subclass.call(new Object(), 1, 2); }, TypeError);
-assertThrows(function() { Base.call(new Object(), 1, 2); }, TypeError);
+  let g = Subclass.bind(obj, 1);
+  assertThrows(function () { g(8); }, TypeError);
+  let s2 = new g(8);
+  assertSame(1, s2.x);
+  assertSame(8, s2.y);
+  assertSame(Subclass.prototype, s.__proto__);
+}());