new classes: special construct stub for derived classs and TDZ for `this`.
authordslomov <dslomov@chromium.org>
Tue, 3 Feb 2015 17:42:41 +0000 (09:42 -0800)
committerCommit bot <commit-bot@chromium.org>
Tue, 3 Feb 2015 17:43:03 +0000 (17:43 +0000)
R=arv@chromium.org,rossberg@chromium.org
BUG=v8:3834
LOG=N

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

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

20 files changed:
src/arm/builtins-arm.cc
src/arm/full-codegen-arm.cc
src/arm64/builtins-arm64.cc
src/arm64/full-codegen-arm64.cc
src/ast.h
src/builtins.h
src/globals.h
src/ia32/builtins-ia32.cc
src/ia32/full-codegen-ia32.cc
src/objects.h
src/parser.cc
src/parser.h
src/preparser.cc
src/preparser.h
src/runtime/runtime-classes.cc
src/scopes.cc
src/scopes.h
src/x64/builtins-x64.cc
src/x64/full-codegen-x64.cc
test/mjsunit/harmony/classes-experimental.js

index 1a98803..2c08b80 100644 (file)
@@ -741,6 +741,70 @@ void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
 }
 
 
+void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) {
+  // ----------- S t a t e -------------
+  //  -- r0     : number of arguments
+  //  -- r1     : constructor function
+  //  -- r2     : allocation site or undefined
+  //  -- r3     : original constructor
+  //  -- lr     : return address
+  //  -- sp[...]: constructor arguments
+  // -----------------------------------
+
+  // TODO(dslomov): support pretenuring
+  CHECK(!FLAG_pretenuring_call_new);
+
+  {
+    FrameScope frame_scope(masm, StackFrame::CONSTRUCT);
+
+    __ mov(r4, r0);
+    __ SmiTag(r4);
+    __ push(r4);  // Smi-tagged arguments count.
+
+    // receiver is the hole.
+    __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+    __ push(ip);
+
+    // Set up pointer to last argument.
+    __ add(r2, fp, Operand(StandardFrameConstants::kCallerSPOffset));
+
+    // Copy arguments and receiver to the expression stack.
+    // r0: number of arguments
+    // r1: constructor function
+    // r2: address of last argument (caller sp)
+    // r4: number of arguments (smi-tagged)
+    // sp[0]: receiver
+    // sp[1]: number of arguments (smi-tagged)
+    Label loop, entry;
+    __ b(&entry);
+    __ bind(&loop);
+    __ ldr(ip, MemOperand(r2, r4, LSL, kPointerSizeLog2 - 1));
+    __ push(ip);
+    __ bind(&entry);
+    __ sub(r4, r4, Operand(2), SetCC);
+    __ b(ge, &loop);
+
+    // Call the function.
+    // r0: number of arguments
+    // r1: constructor function
+    ParameterCount actual(r0);
+    __ InvokeFunction(r1, actual, CALL_FUNCTION, NullCallWrapper());
+
+    // Restore context from the frame.
+    // r0: result
+    // sp[0]: number of arguments (smi-tagged)
+    __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+    __ ldr(r1, MemOperand(sp, 0));
+
+    // Leave construct frame.
+  }
+
+  __ add(sp, sp, Operand(r1, LSL, kPointerSizeLog2 - 1));
+  __ add(sp, sp, Operand(kPointerSize));
+  __ Jump(lr);
+}
+
+
 static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
                                              bool is_construct) {
   // Called from Generate_JS_Entry
index 6139806..051d3a1 100644 (file)
@@ -1538,6 +1538,10 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) {
         bool skip_init_check;
         if (var->scope()->DeclarationScope() != scope()->DeclarationScope()) {
           skip_init_check = false;
+        } else if (var->is_this()) {
+          CHECK((info_->shared_info()->kind() & kSubclassConstructor) != 0);
+          // TODO(dslomov): implement 'this' hole check elimination.
+          skip_init_check = false;
         } else {
           // Check that we always have valid source position.
           DCHECK(var->initializer_position() != RelocInfo::kNoPosition);
@@ -3249,6 +3253,17 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
   EmitLoadSuperConstructor(super_ref);
   __ push(result_register());
 
+  Variable* this_var = super_ref->this_var()->var();
+
+  GetVar(r0, this_var);
+  __ CompareRoot(r0, Heap::kTheHoleValueRootIndex);
+  Label uninitialized_this;
+  __ b(eq, &uninitialized_this);
+  __ mov(r0, Operand(this_var->name()));
+  __ Push(r0);
+  __ CallRuntime(Runtime::kThrowReferenceError, 1);
+  __ bind(&uninitialized_this);
+
   // Push the arguments ("left-to-right") on the stack.
   ZoneList<Expression*>* args = expr->arguments();
   int arg_count = args->length();
@@ -3283,8 +3298,7 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
 
   RecordJSReturnSite(expr);
 
-  // TODO(dslomov): implement TDZ for `this`.
-  EmitVariableAssignment(super_ref->this_var()->var(), Token::ASSIGN);
+  EmitVariableAssignment(this_var, Token::INIT_CONST);
   context()->Plug(r0);
 }
 
index 37329ab..726e984 100644 (file)
@@ -706,6 +706,80 @@ void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
 }
 
 
+void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) {
+  // ----------- S t a t e -------------
+  //  -- x0     : number of arguments
+  //  -- x1     : constructor function
+  //  -- x2     : allocation site or undefined
+  //  -- x3    : original constructor
+  //  -- lr     : return address
+  //  -- sp[...]: constructor arguments
+  // -----------------------------------
+  ASM_LOCATION("Builtins::Generate_JSConstructStubForDerived");
+
+  // TODO(dslomov): support pretenuring
+  CHECK(!FLAG_pretenuring_call_new);
+
+  {
+    FrameScope frame_scope(masm, StackFrame::CONSTRUCT);
+    __ Mov(x4, x0);
+    __ SmiTag(x4);
+    __ LoadRoot(x10, Heap::kTheHoleValueRootIndex);
+    __ Push(x4, x10);
+    // sp[0]: number of arguments
+    // sp[1]: receiver (the hole)
+
+
+    // Set up pointer to last argument.
+    __ Add(x2, fp, StandardFrameConstants::kCallerSPOffset);
+
+    // Copy arguments and receiver to the expression stack.
+    // Copy 2 values every loop to use ldp/stp.
+    // x0: number of arguments
+    // x1: constructor function
+    // x2: address of last argument (caller sp)
+    // jssp[0]: receiver
+    // jssp[1]: 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;
+    __ B(&entry);
+    __ Bind(&loop);
+    __ Ldp(x10, x11, MemOperand(x4, -2 * kPointerSize, PreIndex));
+    __ Push(x11, x10);
+    __ Bind(&entry);
+    __ Cmp(x4, x2);
+    __ B(gt, &loop);
+    // Because we copied values 2 by 2 we may have copied one extra value.
+    // Drop it if that is the case.
+    __ B(eq, &done_copying_arguments);
+    __ Drop(1);
+    __ Bind(&done_copying_arguments);
+
+    // Call the function.
+    // x0: number of arguments
+    // x1: constructor function
+    ParameterCount actual(x0);
+    __ InvokeFunction(x1, actual, CALL_FUNCTION, NullCallWrapper());
+
+
+    // Restore the context from the frame.
+    // x0: result
+    // jssp[0]: number of arguments (smi-tagged)
+    __ Ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+
+    // Load number of arguments (smi).
+    __ Peek(x1, 0);
+
+    // Leave construct frame
+  }
+
+  __ DropBySMI(x1);
+  __ Drop(1);
+  __ Ret();
+}
+
+
 // Input:
 //   x0: code entry.
 //   x1: function.
index 491e900..62b5451 100644 (file)
@@ -1518,6 +1518,10 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) {
         bool skip_init_check;
         if (var->scope()->DeclarationScope() != scope()->DeclarationScope()) {
           skip_init_check = false;
+        } else if (var->is_this()) {
+          CHECK((info_->shared_info()->kind() & kSubclassConstructor) != 0);
+          // TODO(dslomov): implement 'this' hole check elimination.
+          skip_init_check = false;
         } else {
           // Check that we always have valid source position.
           DCHECK(var->initializer_position() != RelocInfo::kNoPosition);
@@ -2938,6 +2942,16 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
   EmitLoadSuperConstructor(super_ref);
   __ push(result_register());
 
+  Variable* this_var = super_ref->this_var()->var();
+
+  GetVar(x0, this_var);
+  Label uninitialized_this;
+  __ JumpIfRoot(x0, Heap::kTheHoleValueRootIndex, &uninitialized_this);
+  __ Mov(x0, Operand(this_var->name()));
+  __ Push(x0);
+  __ CallRuntime(Runtime::kThrowReferenceError, 1);
+  __ bind(&uninitialized_this);
+
   // Push the arguments ("left-to-right") on the stack.
   ZoneList<Expression*>* args = expr->arguments();
   int arg_count = args->length();
@@ -2972,8 +2986,7 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
 
   RecordJSReturnSite(expr);
 
-  // TODO(dslomov): implement TDZ for `this`.
-  EmitVariableAssignment(super_ref->this_var()->var(), Token::ASSIGN);
+  EmitVariableAssignment(this_var, Token::INIT_CONST);
   context()->Plug(x0);
 }
 
index 02f7df6..ba1875c 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -2626,7 +2626,7 @@ class FunctionLiteral FINAL : public Expression {
   class HasDuplicateParameters : public BitField<ParameterFlag, 3, 1> {};
   class IsFunction : public BitField<IsFunctionFlag, 4, 1> {};
   class IsParenthesized : public BitField<IsParenthesizedFlag, 5, 1> {};
-  class FunctionKindBits : public BitField<FunctionKind, 6, 4> {};
+  class FunctionKindBits : public BitField<FunctionKind, 6, 5> {};
 };
 
 
index 8e4149f..cfbb77d 100644 (file)
@@ -67,6 +67,7 @@ enum BuiltinExtraArguments {
   V(ArgumentsAdaptorTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState)       \
   V(InOptimizationQueue, BUILTIN, UNINITIALIZED, kNoExtraICState)              \
   V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED, kNoExtraICState)           \
+  V(JSConstructStubForDerived, BUILTIN, UNINITIALIZED, kNoExtraICState)        \
   V(JSConstructStubApi, BUILTIN, UNINITIALIZED, kNoExtraICState)               \
   V(JSEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState)                \
   V(JSConstructEntryTrampoline, BUILTIN, UNINITIALIZED, kNoExtraICState)       \
@@ -302,6 +303,7 @@ class Builtins {
   static void Generate_CompileOptimized(MacroAssembler* masm);
   static void Generate_CompileOptimizedConcurrent(MacroAssembler* masm);
   static void Generate_JSConstructStubGeneric(MacroAssembler* masm);
+  static void Generate_JSConstructStubForDerived(MacroAssembler* masm);
   static void Generate_JSConstructStubApi(MacroAssembler* masm);
   static void Generate_JSEntryTrampoline(MacroAssembler* masm);
   static void Generate_JSConstructEntryTrampoline(MacroAssembler* masm);
index 6cdd354..5bc0ec2 100644 (file)
@@ -778,7 +778,8 @@ enum FunctionKind {
   kGeneratorFunction = 2,
   kConciseMethod = 4,
   kConciseGeneratorMethod = kGeneratorFunction | kConciseMethod,
-  kDefaultConstructor = 8
+  kDefaultConstructor = 8,
+  kSubclassConstructor = 16
 };
 
 
@@ -788,7 +789,8 @@ inline bool IsValidFunctionKind(FunctionKind kind) {
          kind == FunctionKind::kGeneratorFunction ||
          kind == FunctionKind::kConciseMethod ||
          kind == FunctionKind::kConciseGeneratorMethod ||
-         kind == FunctionKind::kDefaultConstructor;
+         kind == FunctionKind::kDefaultConstructor ||
+         kind == FunctionKind::kSubclassConstructor;
 }
 
 
@@ -815,7 +817,10 @@ inline bool IsDefaultConstructor(FunctionKind kind) {
   return kind & FunctionKind::kDefaultConstructor;
 }
 
-
+inline bool IsSubclassConstructor(FunctionKind kind) {
+  DCHECK(IsValidFunctionKind(kind));
+  return kind & FunctionKind::kSubclassConstructor;
+}
 } }  // namespace v8::internal
 
 namespace i = v8::internal;
index 675d862..18966c2 100644 (file)
@@ -500,6 +500,57 @@ void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
 }
 
 
+void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) {
+  // ----------- S t a t e -------------
+  //  -- eax: number of arguments
+  //  -- edi: constructor function
+  //  -- ebx: allocation site or undefined
+  //  -- edx: original constructor
+  // -----------------------------------
+
+  // TODO(dslomov): support pretenuring
+  CHECK(!FLAG_pretenuring_call_new);
+
+  {
+    FrameScope frame_scope(masm, StackFrame::CONSTRUCT);
+
+    // Preserve actual arguments count.
+    __ SmiTag(eax);
+    __ push(eax);
+    __ SmiUntag(eax);
+
+    // receiver is the hole.
+    __ push(Immediate(masm->isolate()->factory()->the_hole_value()));
+
+    // Set up pointer to last argument.
+    __ lea(ebx, Operand(ebp, StandardFrameConstants::kCallerSPOffset));
+
+    // Copy arguments and receiver to the expression stack.
+    Label loop, entry;
+    __ mov(ecx, eax);
+    __ jmp(&entry);
+    __ bind(&loop);
+    __ push(Operand(ebx, ecx, times_4, 0));
+    __ bind(&entry);
+    __ dec(ecx);
+    __ j(greater_equal, &loop);
+
+    ParameterCount actual(eax);
+    __ InvokeFunction(edi, actual, CALL_FUNCTION, NullCallWrapper());
+
+    // Restore context from the frame.
+    __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
+
+    __ mov(ebx, Operand(esp, 0));
+  }
+
+  __ pop(ecx);  // Return address.
+  __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize));
+  __ push(ecx);
+  __ ret(0);
+}
+
+
 static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
                                              bool is_construct) {
   ProfileEntryHookStub::MaybeCallEntryHook(masm);
index 5a161e9..178c394 100644 (file)
@@ -1461,6 +1461,10 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) {
         bool skip_init_check;
         if (var->scope()->DeclarationScope() != scope()->DeclarationScope()) {
           skip_init_check = false;
+        } else if (var->is_this()) {
+          CHECK((info_->shared_info()->kind() & kSubclassConstructor) != 0);
+          // TODO(dslomov): implement 'this' hole check elimination.
+          skip_init_check = false;
         } else {
           // Check that we always have valid source position.
           DCHECK(var->initializer_position() != RelocInfo::kNoPosition);
@@ -3133,6 +3137,15 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
   EmitLoadSuperConstructor(super_ref);
   __ push(result_register());
 
+  Variable* this_var = super_ref->this_var()->var();
+  GetVar(eax, this_var);
+  __ cmp(eax, isolate()->factory()->the_hole_value());
+  Label uninitialized_this;
+  __ j(equal, &uninitialized_this);
+  __ push(Immediate(this_var->name()));
+  __ CallRuntime(Runtime::kThrowReferenceError, 1);
+  __ bind(&uninitialized_this);
+
   // Push the arguments ("left-to-right") on the stack.
   ZoneList<Expression*>* args = expr->arguments();
   int arg_count = args->length();
@@ -3167,8 +3180,7 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
 
   RecordJSReturnSite(expr);
 
-  // TODO(dslomov): implement TDZ for `this`.
-  EmitVariableAssignment(super_ref->this_var()->var(), Token::ASSIGN);
+  EmitVariableAssignment(this_var, Token::INIT_CONST);
   context()->Plug(eax);
 }
 
index d1e5acf..e5c55a9 100644 (file)
@@ -7216,12 +7216,13 @@ class SharedFunctionInfo: public HeapObject {
     kIsGenerator,
     kIsConciseMethod,
     kIsDefaultConstructor,
+    kIsSubclassConstructor,
     kIsAsmFunction,
     kDeserialized,
     kCompilerHintsCount  // Pseudo entry
   };
 
-  class FunctionKindBits : public BitField<FunctionKind, kIsArrow, 4> {};
+  class FunctionKindBits : public BitField<FunctionKind, kIsArrow, 5> {};
 
   class DeoptCountBits : public BitField<int, 0, 4> {};
   class OptReenableTriesBits : public BitField<int, 4, 18> {};
index f16473c..1817944 100644 (file)
@@ -263,12 +263,17 @@ void Parser::SetCachedData() {
 }
 
 
-Scope* Parser::NewScope(Scope* parent, ScopeType scope_type) {
+Scope* Parser::NewScope(Scope* parent, ScopeType scope_type,
+                        FunctionKind kind) {
   DCHECK(ast_value_factory());
   DCHECK(scope_type != MODULE_SCOPE || allow_harmony_modules());
+  DCHECK((scope_type == FUNCTION_SCOPE && IsValidFunctionKind(kind)) ||
+         kind == kNormalFunction);
   Scope* result = new (zone())
       Scope(isolate(), zone(), parent, scope_type, ast_value_factory());
-  result->Initialize();
+  bool uninitialized_this =
+      FLAG_experimental_classes && IsSubclassConstructor(kind);
+  result->Initialize(uninitialized_this);
   return result;
 }
 
@@ -281,7 +286,8 @@ FunctionLiteral* Parser::DefaultConstructor(bool call_super, Scope* scope,
   int parameter_count = 0;
   const AstRawString* name = ast_value_factory()->empty_string();
 
-  Scope* function_scope = NewScope(scope, FUNCTION_SCOPE);
+  Scope* function_scope =
+      NewScope(scope, FUNCTION_SCOPE, FunctionKind::kDefaultConstructor);
   function_scope->SetStrictMode(STRICT);
   // Set start and end position to the same value
   function_scope->set_start_position(pos);
@@ -3629,11 +3635,11 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
   Scope* original_declaration_scope = original_scope_->DeclarationScope();
   Scope* scope =
       function_type == FunctionLiteral::DECLARATION &&
-      (!allow_harmony_scoping() || strict_mode() == SLOPPY) &&
-      (original_scope_ == original_declaration_scope ||
-       declaration_scope != original_declaration_scope)
-          ? NewScope(declaration_scope, FUNCTION_SCOPE)
-          : NewScope(scope_, FUNCTION_SCOPE);
+              (!allow_harmony_scoping() || strict_mode() == SLOPPY) &&
+              (original_scope_ == original_declaration_scope ||
+               declaration_scope != original_declaration_scope)
+          ? NewScope(declaration_scope, FUNCTION_SCOPE, kind)
+          : NewScope(scope_, FUNCTION_SCOPE, kind);
   ZoneList<Statement*>* body = NULL;
   int materialized_literal_count = -1;
   int expected_property_count = -1;
@@ -4053,6 +4059,7 @@ ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name,
   bool has_seen_constructor = false;
 
   Expect(Token::LBRACE, CHECK_OK);
+  const bool has_extends = extends != nullptr;
   while (peek() != Token::RBRACE) {
     if (Check(Token::SEMICOLON)) continue;
     if (fni_ != NULL) fni_->Enter();
@@ -4061,8 +4068,8 @@ ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name,
     bool is_computed_name = false;  // Classes do not care about computed
                                     // property names here.
     ObjectLiteral::Property* property = ParsePropertyDefinition(
-        &checker, in_class, is_static, &is_computed_name, &has_seen_constructor,
-        CHECK_OK);
+        &checker, in_class, has_extends, is_static, &is_computed_name,
+        &has_seen_constructor, CHECK_OK);
 
     if (has_seen_constructor && constructor == NULL) {
       constructor = GetPropertyValue(property)->AsFunctionLiteral();
index 23b12e1..15a9a0e 100644 (file)
@@ -563,7 +563,8 @@ class ParserTraits {
   ZoneList<v8::internal::Statement*>* NewStatementList(int size, Zone* zone) {
     return new(zone) ZoneList<v8::internal::Statement*>(size, zone);
   }
-  V8_INLINE Scope* NewScope(Scope* parent_scope, ScopeType scope_type);
+  V8_INLINE Scope* NewScope(Scope* parent_scope, ScopeType scope_type,
+                            FunctionKind kind = kNormalFunction);
 
   // Utility functions
   int DeclareArrowParametersFromExpression(Expression* expression, Scope* scope,
@@ -852,7 +853,8 @@ class Parser : public ParserBase<ParserTraits> {
 
   // Factory methods.
 
-  Scope* NewScope(Scope* parent, ScopeType type);
+  Scope* NewScope(Scope* parent, ScopeType type,
+                  FunctionKind kind = kNormalFunction);
 
   FunctionLiteral* DefaultConstructor(bool call_super, Scope* scope, int pos,
                                       int end_pos);
@@ -913,8 +915,9 @@ bool ParserTraits::IsFutureStrictReserved(
 }
 
 
-Scope* ParserTraits::NewScope(Scope* parent_scope, ScopeType scope_type) {
-  return parser_->NewScope(parent_scope, scope_type);
+Scope* ParserTraits::NewScope(Scope* parent_scope, ScopeType scope_type,
+                              FunctionKind kind) {
+  return parser_->NewScope(parent_scope, scope_type, kind);
 }
 
 
index 6cf0f1d..68d9109 100644 (file)
@@ -1004,7 +1004,8 @@ PreParserExpression PreParser::ParseClassLiteral(
   scope_->SetStrictMode(STRICT);
   scope_->SetScopeName(name);
 
-  if (Check(Token::EXTENDS)) {
+  bool has_extends = Check(Token::EXTENDS);
+  if (has_extends) {
     ParseLeftHandSideExpression(CHECK_OK);
   }
 
@@ -1018,8 +1019,8 @@ PreParserExpression PreParser::ParseClassLiteral(
     const bool is_static = false;
     bool is_computed_name = false;  // Classes do not care about computed
                                     // property names here.
-    ParsePropertyDefinition(&checker, in_class, is_static, &is_computed_name,
-                            &has_seen_constructor, CHECK_OK);
+    ParsePropertyDefinition(&checker, in_class, has_extends, is_static,
+                            &is_computed_name, &has_seen_constructor, CHECK_OK);
   }
 
   Expect(Token::RBRACE, CHECK_OK);
index 67a559c..9faaca1 100644 (file)
@@ -520,8 +520,9 @@ class ParserBase : public Traits {
                                 bool* ok);
   ExpressionT ParseObjectLiteral(bool* ok);
   ObjectLiteralPropertyT ParsePropertyDefinition(
-      ObjectLiteralCheckerBase* checker, bool in_class, bool is_static,
-      bool* is_computed_name, bool* has_seen_constructor, bool* ok);
+      ObjectLiteralCheckerBase* checker, bool in_class, bool has_extends,
+      bool is_static, bool* is_computed_name, bool* has_seen_constructor,
+      bool* ok);
   typename Traits::Type::ExpressionList ParseArguments(bool* ok);
   ExpressionT ParseAssignmentExpression(bool accept_IN, bool* ok);
   ExpressionT ParseYieldExpression(bool* ok);
@@ -1315,7 +1316,8 @@ class PreParserTraits {
       const char* type, Handle<Object> arg, int pos) {
     return PreParserExpression::Default();
   }
-  PreParserScope NewScope(PreParserScope* outer_scope, ScopeType scope_type) {
+  PreParserScope NewScope(PreParserScope* outer_scope, ScopeType scope_type,
+                          FunctionKind kind = kNormalFunction) {
     return PreParserScope(outer_scope, scope_type);
   }
 
@@ -2082,7 +2084,8 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParsePropertyName(
 template <class Traits>
 typename ParserBase<Traits>::ObjectLiteralPropertyT
 ParserBase<Traits>::ParsePropertyDefinition(ObjectLiteralCheckerBase* checker,
-                                            bool in_class, bool is_static,
+                                            bool in_class, bool has_extends,
+                                            bool is_static,
                                             bool* is_computed_name,
                                             bool* has_seen_constructor,
                                             bool* ok) {
@@ -2129,7 +2132,8 @@ ParserBase<Traits>::ParsePropertyDefinition(ObjectLiteralCheckerBase* checker,
 
     if (in_class && !is_static && this->IsConstructor(name)) {
       *has_seen_constructor = true;
-      kind = FunctionKind::kNormalFunction;
+      kind = has_extends ? FunctionKind::kSubclassConstructor
+                         : FunctionKind::kNormalFunction;
     }
 
     value = this->ParseFunctionLiteral(
@@ -2145,9 +2149,8 @@ ParserBase<Traits>::ParsePropertyDefinition(ObjectLiteralCheckerBase* checker,
 
   } else if (in_class && name_is_static && !is_static) {
     // static MethodDefinition
-    return ParsePropertyDefinition(checker, true, true, is_computed_name,
-                                   nullptr, ok);
-
+    return ParsePropertyDefinition(checker, true, has_extends, true,
+                                   is_computed_name, nullptr, ok);
   } else if (is_get || is_set) {
     // Accessor
     name = this->EmptyIdentifier();
@@ -2227,9 +2230,11 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseObjectLiteral(
 
     const bool in_class = false;
     const bool is_static = false;
+    const bool has_extends = false;
     bool is_computed_name = false;
     ObjectLiteralPropertyT property = this->ParsePropertyDefinition(
-        &checker, in_class, is_static, &is_computed_name, nullptr, CHECK_OK);
+        &checker, in_class, has_extends, is_static, &is_computed_name, NULL,
+        CHECK_OK);
 
     if (is_computed_name) {
       has_computed_names = true;
index 00567d4..846fd1a 100644 (file)
@@ -107,6 +107,13 @@ RUNTIME_FUNCTION(Runtime_DefineClass) {
                                    : isolate->factory()->empty_string();
   constructor->shared()->set_name(*name_string);
 
+  if (FLAG_experimental_classes) {
+    if (!super_class->IsTheHole() && !super_class->IsNull()) {
+      Handle<Code> stub(isolate->builtins()->JSConstructStubForDerived());
+      constructor->shared()->set_construct_stub(*stub);
+    }
+  }
+
   JSFunction::SetPrototype(constructor, prototype);
   PropertyAttributes attribs =
       static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY);
index 80bf82b..1553431 100644 (file)
@@ -291,7 +291,7 @@ bool Scope::Analyze(CompilationInfo* info) {
 }
 
 
-void Scope::Initialize() {
+void Scope::Initialize(bool uninitialized_this) {
   DCHECK(!already_resolved());
 
   // Add this scope as a new inner scope of the outer scope.
@@ -311,13 +311,12 @@ void Scope::Initialize() {
   // such parameter is 'this' which is passed on the stack when
   // invoking scripts
   if (is_declaration_scope()) {
-    Variable* var =
-        variables_.Declare(this,
-                           ast_value_factory_->this_string(),
-                           VAR,
-                           false,
-                           Variable::THIS,
-                           kCreatedInitialized);
+    DCHECK(!uninitialized_this || is_function_scope());
+    DCHECK(FLAG_experimental_classes || !uninitialized_this);
+    Variable* var = variables_.Declare(
+        this, ast_value_factory_->this_string(),
+        uninitialized_this ? CONST : VAR, false, Variable::THIS,
+        uninitialized_this ? kNeedsInitialization : kCreatedInitialized);
     var->AllocateTo(Variable::PARAMETER, -1);
     receiver_ = var;
   } else {
index 2e418be..9cb7e65 100644 (file)
@@ -88,7 +88,7 @@ class Scope: public ZoneObject {
     scope_name_ = scope_name;
   }
 
-  void Initialize();
+  void Initialize(bool uninitialized_this = 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
index ff0a349..80a41dd 100644 (file)
@@ -498,6 +498,59 @@ void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
 }
 
 
+void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) {
+  // ----------- S t a t e -------------
+  //  -- rax: number of arguments
+  //  -- rdi: constructor function
+  //  -- rbx: allocation site or undefined
+  //  -- rdx: original constructor
+  // -----------------------------------
+  // TODO(dslomov): support pretenuring
+  CHECK(!FLAG_pretenuring_call_new);
+
+  {
+    FrameScope frame_scope(masm, StackFrame::CONSTRUCT);
+
+    // Store a smi-tagged arguments count on the stack.
+    __ Integer32ToSmi(rax, rax);
+    __ Push(rax);
+    __ SmiToInteger32(rax, rax);
+
+    // receiver is the hole.
+    __ Push(masm->isolate()->factory()->the_hole_value());
+
+    // Set up pointer to last argument.
+    __ leap(rbx, Operand(rbp, StandardFrameConstants::kCallerSPOffset));
+
+    // Copy arguments and receiver to the expression stack.
+    Label loop, entry;
+    __ movp(rcx, rax);
+    __ jmp(&entry);
+    __ bind(&loop);
+    __ Push(Operand(rbx, rcx, times_pointer_size, 0));
+    __ bind(&entry);
+    __ decp(rcx);
+    __ j(greater_equal, &loop);
+
+    // Call the function.
+    ParameterCount actual(rax);
+    __ InvokeFunction(rdi, actual, CALL_FUNCTION, NullCallWrapper());
+
+    // Restore context from the frame.
+    __ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+
+    __ movp(rbx, Operand(rsp, 0));  // Get arguments count.
+  }                                 // Leave construct frame.
+
+  // Remove caller arguments from the stack and return.
+  __ PopReturnAddressTo(rcx);
+  SmiIndex index = masm->SmiToIndex(rbx, rbx, kPointerSizeLog2);
+  __ leap(rsp, Operand(rsp, index.reg, index.scale, 1 * kPointerSize));
+  __ PushReturnAddressFrom(rcx);
+  __ ret(0);
+}
+
+
 static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
                                              bool is_construct) {
   ProfileEntryHookStub::MaybeCallEntryHook(masm);
index 3a93117..6c77274 100644 (file)
@@ -1496,6 +1496,10 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) {
         bool skip_init_check;
         if (var->scope()->DeclarationScope() != scope()->DeclarationScope()) {
           skip_init_check = false;
+        } else if (var->is_this()) {
+          CHECK((info_->shared_info()->kind() & kSubclassConstructor) != 0);
+          // TODO(dslomov): implement 'this' hole check elimination.
+          skip_init_check = false;
         } else {
           // Check that we always have valid source position.
           DCHECK(var->initializer_position() != RelocInfo::kNoPosition);
@@ -3138,6 +3142,17 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
   EmitLoadSuperConstructor(super_ref);
   __ Push(result_register());
 
+  Variable* this_var = super_ref->this_var()->var();
+
+  GetVar(rax, this_var);
+  __ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
+  Label uninitialized_this;
+  __ j(equal, &uninitialized_this);
+  __ Push(this_var->name());
+  __ CallRuntime(Runtime::kThrowReferenceError, 1);
+  __ bind(&uninitialized_this);
+
+
   // Push the arguments ("left-to-right") on the stack.
   ZoneList<Expression*>* args = expr->arguments();
   int arg_count = args->length();
@@ -3172,8 +3187,7 @@ void FullCodeGenerator::EmitSuperConstructorCall(Call* expr) {
 
   RecordJSReturnSite(expr);
 
-  // TODO(dslomov): implement TDZ for `this`.
-  EmitVariableAssignment(super_ref->this_var()->var(), Token::ASSIGN);
+  EmitVariableAssignment(this_var, Token::INIT_CONST);
   context()->Plug(rax);
 }
 
index 73944b1..5a3c3e5 100644 (file)
@@ -7,29 +7,51 @@
 'use strict';
 
 class Base {
-  constructor() {
+  constructor(a, b) {
     let o = new Object();
-    o.prp = 1;
+    o.prp = a + b;
     return o;
   }
 }
 
 class Subclass extends Base {
-  constructor() {
+  constructor(a, b) {
+    var exn;
     try {
       this.prp1 = 3;
     } catch (e) {
-      // TODO(dslomov): actually test the exception once TDZ is implemented.
+      exn = e;
     }
-    super();
-    assertSame(1, this.prp);
+    assertTrue(exn instanceof ReferenceError);
+    super(a, b);
+    assertSame(a + b, this.prp);
     assertSame(undefined, this.prp1);
     assertFalse(this.hasOwnProperty("prp1"));
     return this;
   }
 }
 
-let s = new Subclass();
+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() {
+    super(1,2);
+
+    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);
+  }
+}
+
+new Subclass2();