update a class extending null w.r.t the ES7 spec
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 3 Aug 2016 01:28:16 +0000 (01:28 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 3 Aug 2016 01:28:16 +0000 (01:28 +0000)
https://bugs.webkit.org/show_bug.cgi?id=160417

Reviewed by Keith Miller.

Source/JavaScriptCore:

When a class extends null, it should not be marked as a derived class.
This was changed in the ES2016 spec, and this patch makes the needed
changes in JSC to follow the spec. This allows classes to extend
null and have their default constructor invoked without throwing an exception.
This also prevents |this| from being under TDZ at the start of the constructor.
Because ES6 allows arbitrary expressions in the `class <ident> extends <expr>`
syntax, we don't know statically if a constructor is extending null or not.
Therefore, we don't always know statically if it's a base or derived constructor.
I solved this by putting a boolean on the constructor function under a private
symbol named isDerivedConstructor when doing class construction. We only need
to put this boolean on constructors that may extend null. Constructors that are
declared in a class with no extends syntax can tell statically that they are a base constructor.

I've also renamed the ConstructorKind::Derived enum value to be
ConstructorKind::Extends to better indicate that we can't answer
the "am I a derived constructor?" question statically.

* builtins/BuiltinExecutables.cpp:
(JSC::BuiltinExecutables::createDefaultConstructor):
* builtins/BuiltinNames.h:
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::initializeArrowFunctionContextScopeIfNeeded):
(JSC::BytecodeGenerator::emitReturn):
(JSC::BytecodeGenerator::emitLoadArrowFunctionLexicalEnvironment):
(JSC::BytecodeGenerator::ensureThis):
(JSC::BytecodeGenerator::emitPutDerivedConstructorToArrowFunctionContextScope):
* bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::makeFunction):
* bytecompiler/NodesCodegen.cpp:
(JSC::EvalFunctionCallNode::emitBytecode):
(JSC::FunctionCallValueNode::emitBytecode):
(JSC::FunctionNode::emitBytecode):
(JSC::ClassExprNode::emitBytecode):
* parser/Parser.cpp:
(JSC::Parser<LexerType>::Parser):
(JSC::Parser<LexerType>::parseFunctionInfo):
(JSC::Parser<LexerType>::parseClass):
(JSC::Parser<LexerType>::parseMemberExpression):
* parser/ParserModes.h:

LayoutTests:

* js/class-syntax-extends-expected.txt:
* js/class-syntax-super-expected.txt:
* js/script-tests/class-syntax-extends.js:
* js/script-tests/class-syntax-super.js:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@204058 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
JSTests/Changelog
JSTests/stress/class-derived-from-null.js [new file with mode: 0644]
JSTests/test262.yaml
LayoutTests/ChangeLog
LayoutTests/js/class-syntax-extends-expected.txt
LayoutTests/js/class-syntax-super-expected.txt
LayoutTests/js/script-tests/class-syntax-extends.js
LayoutTests/js/script-tests/class-syntax-super.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/builtins/BuiltinExecutables.cpp
Source/JavaScriptCore/builtins/BuiltinNames.h
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/ParserModes.h

index c698eb6..8ee11eb 100644 (file)
@@ -1,3 +1,35 @@
+2016-08-02  Saam Barati  <sbarati@apple.com>
+
+        update a class extending null w.r.t the ES7 spec
+        https://bugs.webkit.org/show_bug.cgi?id=160417
+
+        Reviewed by Keith Miller.
+
+        * Changelog:
+        * stress/class-derived-from-null.js: Added.
+        (assert):
+        (test):
+        (test1.C):
+        (test1):
+        (jsNull):
+        (test2.C):
+        (test2):
+        (test3.C):
+        (test3.D):
+        (test3.E):
+        (test3):
+        (test4.E):
+        (test4):
+        (test5.E):
+        (test5):
+        (test6.Base):
+        (test6.D):
+        (test6.E):
+        (test6.F):
+        (test6.G):
+        (test6):
+        * test262.yaml:
+
 2016-08-01  Filip Pizlo  <fpizlo@apple.com>
 
         Rationalize varargs stack overflow checks
diff --git a/JSTests/stress/class-derived-from-null.js b/JSTests/stress/class-derived-from-null.js
new file mode 100644 (file)
index 0000000..0fe61be
--- /dev/null
@@ -0,0 +1,128 @@
+function assert(b) {
+    if (!b)
+        throw new Error("Bad!")
+}
+
+function test(f, count = 1000) {
+    for (let i = 0; i < count; i++)
+        f();
+}
+
+function test1() {
+    class C extends null { }
+    assert((new C) instanceof C);
+    assert(!((new C) instanceof Object));
+    assert(Reflect.getPrototypeOf(C.prototype) === null);
+
+    let o = {}
+    class D extends null {
+        constructor() {
+            return o;
+        }
+    }
+    assert(new D === o);
+    assert(Reflect.getPrototypeOf(D.prototype) === null);
+
+    class E extends null {
+        constructor() {
+            return this;
+        }
+    }
+    assert((new E) instanceof E);
+    assert(!((new E) instanceof Object));
+    assert(Reflect.getPrototypeOf(E.prototype) === null);
+}
+test(test1);
+
+function jsNull() { return null; }
+function test2() {
+    class C extends jsNull() { }
+    assert((new C) instanceof C);
+    assert(!((new C) instanceof Object));
+    assert(Reflect.getPrototypeOf(C.prototype) === null);
+
+    let o = {}
+    class D extends jsNull() {
+        constructor() {
+            return o;
+        }
+    }
+    assert(new D === o);
+    assert(Reflect.getPrototypeOf(D.prototype) === null);
+
+    class E extends jsNull() {
+        constructor() {
+            return this;
+        }
+    }
+    assert((new E) instanceof E);
+    assert(!((new E) instanceof Object));
+    assert(Reflect.getPrototypeOf(E.prototype) === null);
+}
+test(test2);
+
+function test3() {
+    class C extends jsNull() { constructor() { super(); } }
+    let threw = false;
+    try {
+        new C;
+    } catch(e) {
+        threw = e.toString() === "TypeError: function is not a constructor (evaluating 'super()')";
+    }
+    assert(threw);
+
+    class D extends jsNull() { constructor() { let arr = ()=>super(); arr(); } }
+    threw = false;
+    try {
+        new D;
+    } catch(e) {
+        threw = e.toString() === "TypeError: function is not a constructor (evaluating 'super()')";
+    }
+    assert(threw);
+
+    class E extends jsNull() { constructor() { let arr = ()=>super(); return this; } }
+    assert((new E) instanceof E);
+    assert(!((new E) instanceof Object));
+    assert(Reflect.getPrototypeOf(E.prototype) === null);
+}
+test(test3);
+
+function test4() {
+    class E extends jsNull() { constructor() { return 25; } }
+    assert((new E) instanceof E);
+    assert(!((new E) instanceof Object));
+    assert(Reflect.getPrototypeOf(E.prototype) === null);
+}
+test(test4);
+
+function test5() {
+    class E extends jsNull() { constructor() { let arr = ()=>this; return arr(); } }
+    assert((new E) instanceof E);
+    assert(!((new E) instanceof Object));
+    assert(Reflect.getPrototypeOf(E.prototype) === null);
+}
+test(test5);
+
+function test6() {
+    class Base { }
+    class D extends Base { }
+    class E extends jsNull() { constructor() { let ret = this; return ret; } }
+    class F extends jsNull() { constructor() { return 25; } }
+    class G extends jsNull() { constructor() { super(); } }
+    let result = Reflect.construct(E, [], D);
+    assert(result instanceof D);
+    assert(result instanceof Object);
+
+    result = Reflect.construct(F, [], D);
+    assert(result instanceof D);
+    assert(result instanceof Object);
+
+    let threw = false;
+    try {
+        Reflect.construct(G, [], D);
+    } catch(e) {
+        threw = e.toString() === "TypeError: function is not a constructor (evaluating 'super()')";
+    }
+    assert(threw);
+}
+test(test6);
index fc8ae4c..c64d9f9 100644 (file)
 - path: test262/test/language/statements/class/subclass/class-definition-null-proto-super.js
   cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/statements/class/subclass/class-definition-null-proto-this.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js"], []
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js"], []
 - path: test262/test/language/statements/class/subclass/class-definition-null-proto-this.js
-  cmd: runTest262 :fail, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js"], [:strict]
+  cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js"], [:strict]
 - path: test262/test/language/statements/class/subclass/class-definition-null-proto.js
   cmd: runTest262 :normal, "NoException", ["../../../../../harness/assert.js", "../../../../../harness/sta.js"], []
 - path: test262/test/language/statements/class/subclass/class-definition-null-proto.js
index b3a415a..b06febe 100644 (file)
@@ -1,3 +1,15 @@
+2016-08-02  Saam Barati  <sbarati@apple.com>
+
+        update a class extending null w.r.t the ES7 spec
+        https://bugs.webkit.org/show_bug.cgi?id=160417
+
+        Reviewed by Keith Miller.
+
+        * js/class-syntax-extends-expected.txt:
+        * js/class-syntax-super-expected.txt:
+        * js/script-tests/class-syntax-extends.js:
+        * js/script-tests/class-syntax-super.js:
+
 2016-08-02  Ryan Haddad  <ryanhaddad@apple.com>
 
         Skip media/track/track-remove-crash.html on ios-simulator-wk2
index c47f5fc..ab923ae 100644 (file)
@@ -57,10 +57,10 @@ PASS Object.getPrototypeOf((class extends null { constructor () { super(); } }).
 PASS new (class extends undefined { constructor () { this } }):::TypeError: The superclass is not an object.
 PASS x = undefined; new (class extends x { constructor () { super(); } }):::TypeError: The superclass is not an object.
 PASS class x {}; new (class extends null { constructor () { return new x; } }) instanceof x
-PASS new (class extends null { constructor () { this; } }):::ReferenceError: Cannot access uninitialized variable.
+PASS new (class extends null { constructor () { this; } })
 PASS new (class extends null { constructor () { super(); } }):::TypeError: function is not a constructor (evaluating 'super()')
 PASS x = {}; new (class extends null { constructor () { return x } }):::x
-PASS y = 12; new (class extends null { constructor () { return y; } }):::TypeError: Cannot return a non-object type in the constructor of a derived class.
+PASS y = 12; class C extends null { constructor () { return y; } }; new C instanceof C;
 PASS class x {}; new (class extends null { constructor () { return new x; } }) instanceof x
 PASS x = null; Object.getPrototypeOf((class extends x { }).prototype):::null
 PASS Object.prototype.isPrototypeOf(class { })
index 3931ad9..2177bdb 100644 (file)
@@ -30,12 +30,12 @@ PASS x = { }; new (class extends Base { constructor() { return x } });:::x
 PASS x instanceof Base
 PASS new (class extends Base { constructor() { } }):::ReferenceError: Cannot access uninitialized variable.
 PASS new (class extends Base { constructor() { return 1; } }):::TypeError: Cannot return a non-object type in the constructor of a derived class.
-PASS new (class extends null { constructor() { return undefined } }):::ReferenceError: Cannot access uninitialized variable.
+PASS new (class extends null { constructor() { return undefined } })
 PASS new (class extends null { constructor() { super(); return undefined } }):::TypeError: function is not a constructor (evaluating 'super()')
 PASS x = { }; new (class extends null { constructor() { return x } });:::x
 PASS x instanceof Object
-PASS new (class extends null { constructor() { } }):::ReferenceError: Cannot access uninitialized variable.
-PASS new (class extends null { constructor() { return 1; } }):::TypeError: Cannot return a non-object type in the constructor of a derived class.
+PASS new (class extends null { constructor() { } })
+PASS new (class extends null { constructor() { return 1; } })
 PASS new (class extends null { constructor() { super() } }):::TypeError: function is not a constructor (evaluating 'super()')
 PASS new (class { constructor() { super() } }):::SyntaxError: super is not valid in this context.
 PASS function x() { super(); }:::SyntaxError: super is not valid in this context.
index 38e5e5c..672d31a 100644 (file)
@@ -115,10 +115,10 @@ shouldBe('Object.getPrototypeOf((class extends null { constructor () { super();
 shouldThrow('new (class extends undefined { constructor () { this } })', '"TypeError: The superclass is not an object."');
 shouldThrow('x = undefined; new (class extends x { constructor () { super(); } })', '"TypeError: The superclass is not an object."');
 shouldBeTrue ('class x {}; new (class extends null { constructor () { return new x; } }) instanceof x');
-shouldThrow('new (class extends null { constructor () { this; } })', '"ReferenceError: Cannot access uninitialized variable."');
+shouldNotThrow('new (class extends null { constructor () { this; } })');
 shouldThrow('new (class extends null { constructor () { super(); } })', '"TypeError: function is not a constructor (evaluating \'super()\')"');
 shouldBe('x = {}; new (class extends null { constructor () { return x } })', 'x');
-shouldThrow('y = 12; new (class extends null { constructor () { return y; } })', '"TypeError: Cannot return a non-object type in the constructor of a derived class."');
+shouldBeTrue('y = 12; class C extends null { constructor () { return y; } }; new C instanceof C;', '"TypeError: Cannot return a non-object type in the constructor of a derived class."');
 shouldBeTrue ('class x {}; new (class extends null { constructor () { return new x; } }) instanceof x');
 shouldBe('x = null; Object.getPrototypeOf((class extends x { }).prototype)', 'null');
 shouldBeTrue('Object.prototype.isPrototypeOf(class { })');
index d3aca98..aad7a62 100644 (file)
@@ -113,12 +113,12 @@ shouldBe('x = { }; new (class extends Base { constructor() { return x } });', 'x
 shouldBeFalse('x instanceof Base');
 shouldThrow('new (class extends Base { constructor() { } })', '"ReferenceError: Cannot access uninitialized variable."');
 shouldThrow('new (class extends Base { constructor() { return 1; } })', '"TypeError: Cannot return a non-object type in the constructor of a derived class."');
-shouldThrow('new (class extends null { constructor() { return undefined } })');
+shouldNotThrow('new (class extends null { constructor() { return undefined } })');
 shouldThrow('new (class extends null { constructor() { super(); return undefined } })', '"TypeError: function is not a constructor (evaluating \'super()\')"');
 shouldBe('x = { }; new (class extends null { constructor() { return x } });', 'x');
 shouldBeTrue('x instanceof Object');
-shouldThrow('new (class extends null { constructor() { } })', '"ReferenceError: Cannot access uninitialized variable."');
-shouldThrow('new (class extends null { constructor() { return 1; } })', '"TypeError: Cannot return a non-object type in the constructor of a derived class."');
+shouldNotThrow('new (class extends null { constructor() { } })');
+shouldNotThrow('new (class extends null { constructor() { return 1; } })');
 shouldThrow('new (class extends null { constructor() { super() } })', '"TypeError: function is not a constructor (evaluating \'super()\')"');
 shouldThrow('new (class { constructor() { super() } })', '"SyntaxError: super is not valid in this context."');
 shouldThrow('function x() { super(); }', '"SyntaxError: super is not valid in this context."');
index 19fa1ba..a92cf21 100644 (file)
@@ -1,3 +1,51 @@
+2016-08-02  Saam Barati  <sbarati@apple.com>
+
+        update a class extending null w.r.t the ES7 spec
+        https://bugs.webkit.org/show_bug.cgi?id=160417
+
+        Reviewed by Keith Miller.
+
+        When a class extends null, it should not be marked as a derived class.
+        This was changed in the ES2016 spec, and this patch makes the needed
+        changes in JSC to follow the spec. This allows classes to extend
+        null and have their default constructor invoked without throwing an exception.
+        This also prevents |this| from being under TDZ at the start of the constructor.
+        Because ES6 allows arbitrary expressions in the `class <ident> extends <expr>`
+        syntax, we don't know statically if a constructor is extending null or not.
+        Therefore, we don't always know statically if it's a base or derived constructor.
+        I solved this by putting a boolean on the constructor function under a private
+        symbol named isDerivedConstructor when doing class construction. We only need
+        to put this boolean on constructors that may extend null. Constructors that are
+        declared in a class with no extends syntax can tell statically that they are a base constructor.
+
+        I've also renamed the ConstructorKind::Derived enum value to be
+        ConstructorKind::Extends to better indicate that we can't answer
+        the "am I a derived constructor?" question statically.
+
+        * builtins/BuiltinExecutables.cpp:
+        (JSC::BuiltinExecutables::createDefaultConstructor):
+        * builtins/BuiltinNames.h:
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::BytecodeGenerator):
+        (JSC::BytecodeGenerator::initializeArrowFunctionContextScopeIfNeeded):
+        (JSC::BytecodeGenerator::emitReturn):
+        (JSC::BytecodeGenerator::emitLoadArrowFunctionLexicalEnvironment):
+        (JSC::BytecodeGenerator::ensureThis):
+        (JSC::BytecodeGenerator::emitPutDerivedConstructorToArrowFunctionContextScope):
+        * bytecompiler/BytecodeGenerator.h:
+        (JSC::BytecodeGenerator::makeFunction):
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::EvalFunctionCallNode::emitBytecode):
+        (JSC::FunctionCallValueNode::emitBytecode):
+        (JSC::FunctionNode::emitBytecode):
+        (JSC::ClassExprNode::emitBytecode):
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::Parser):
+        (JSC::Parser<LexerType>::parseFunctionInfo):
+        (JSC::Parser<LexerType>::parseClass):
+        (JSC::Parser<LexerType>::parseMemberExpression):
+        * parser/ParserModes.h:
+
 2016-08-02  Enrica Casucci  <enrica@apple.com>
 
         Allow building with content filtering disabled.
index 4c43b04..92670e8 100644 (file)
@@ -53,7 +53,7 @@ UnlinkedFunctionExecutable* BuiltinExecutables::createDefaultConstructor(Constru
         break;
     case ConstructorKind::Base:
         return createExecutable(m_vm, makeSource(baseConstructorCode), name, constructorKind, ConstructAbility::CanConstruct);
-    case ConstructorKind::Derived:
+    case ConstructorKind::Extends:
         return createExecutable(m_vm, makeSource(derivedConstructorCode), name, constructorKind, ConstructAbility::CanConstruct);
     }
     ASSERT_NOT_REACHED();
index 79dc9fb..3abac2f 100644 (file)
@@ -127,6 +127,7 @@ namespace JSC {
     macro(isArray) \
     macro(isArrayConstructor) \
     macro(isConstructor) \
+    macro(isDerivedConstructor) \
     macro(isRegExpObject) \
     macro(concatMemcpy) \
     macro(appendMemcpy) \
index 585af73..6524be6 100644 (file)
@@ -560,9 +560,18 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke
         if (SourceParseMode::ArrowFunctionMode != parseMode) {
             if (isConstructor()) {
                 emitMove(m_newTargetRegister, &m_thisRegister);
-                if (constructorKind() == ConstructorKind::Derived)
+                if (constructorKind() == ConstructorKind::Extends) {
+                    RefPtr<Label> isDerived = newLabel();
+                    RefPtr<Label> done = newLabel();
+                    m_isDerivedConstuctor = addVar();
+                    emitGetById(m_isDerivedConstuctor, &m_calleeRegister, propertyNames().builtinNames().isDerivedConstructorPrivateName());
+                    emitJumpIfTrue(m_isDerivedConstuctor, isDerived.get());
+                    emitCreateThis(&m_thisRegister);
+                    emitJump(done.get());
+                    emitLabel(isDerived.get());
                     emitMoveEmptyValue(&m_thisRegister);
-                else
+                    emitLabel(done.get());
+                } else
                     emitCreateThis(&m_thisRegister);
             } else if (constructorKind() != ConstructorKind::None)
                 emitThrowTypeError("Cannot call a class constructor without |new|");
@@ -948,7 +957,7 @@ void BytecodeGenerator::initializeArrowFunctionContextScopeIfNeeded(SymbolTable*
             functionSymbolTable->set(NoLockingNecessary, propertyNames().builtinNames().newTargetLocalPrivateName().impl(), SymbolTableEntry(VarOffset(offset)));
         }
         
-        if (isConstructor() && constructorKind() == ConstructorKind::Derived && isSuperUsedInInnerArrowFunction()) {
+        if (isConstructor() && constructorKind() == ConstructorKind::Extends && isSuperUsedInInnerArrowFunction()) {
             offset = functionSymbolTable->takeNextScopeOffset(NoLockingNecessary);
             functionSymbolTable->set(NoLockingNecessary, propertyNames().builtinNames().derivedConstructorPrivateName().impl(), SymbolTableEntry(VarOffset(offset)));
         }
@@ -970,7 +979,7 @@ void BytecodeGenerator::initializeArrowFunctionContextScopeIfNeeded(SymbolTable*
         addTarget.iterator->value.setIsLet();
     }
 
-    if (isConstructor() && constructorKind() == ConstructorKind::Derived && isSuperUsedInInnerArrowFunction()) {
+    if (isConstructor() && constructorKind() == ConstructorKind::Extends && isSuperUsedInInnerArrowFunction()) {
         auto derivedConstructor = environment.add(propertyNames().builtinNames().derivedConstructorPrivateName());
         derivedConstructor.iterator->value.setIsCaptured();
         derivedConstructor.iterator->value.setIsLet();
@@ -3265,24 +3274,28 @@ void BytecodeGenerator::emitCallDefineProperty(RegisterID* newObj, RegisterID* p
 RegisterID* BytecodeGenerator::emitReturn(RegisterID* src)
 {
     if (isConstructor()) {
-        bool derived = constructorKind() == ConstructorKind::Derived;
+        bool mightBeDerived = constructorKind() == ConstructorKind::Extends;
         bool srcIsThis = src->index() == m_thisRegister.index();
 
-        if (derived && srcIsThis)
+        if (mightBeDerived && srcIsThis)
             emitTDZCheck(src);
 
         if (!srcIsThis) {
             RefPtr<Label> isObjectLabel = newLabel();
             emitJumpIfTrue(emitIsObject(newTemporary(), src), isObjectLabel.get());
 
-            if (derived) {
+            if (mightBeDerived) {
+                ASSERT(m_isDerivedConstuctor);
+                RefPtr<Label> returnThis = newLabel();
+                emitJumpIfFalse(m_isDerivedConstuctor, returnThis.get());
+                // Else, we're a derived constructor here.
                 RefPtr<Label> isUndefinedLabel = newLabel();
                 emitJumpIfTrue(emitIsUndefined(newTemporary(), src), isUndefinedLabel.get());
                 emitThrowTypeError("Cannot return a non-object type in the constructor of a derived class.");
                 emitLabel(isUndefinedLabel.get());
                 emitTDZCheck(&m_thisRegister);
+                emitLabel(returnThis.get());
             }
-
             emitUnaryNoDstOp(op_ret, &m_thisRegister);
             emitLabel(isObjectLabel.get());
         }
@@ -4285,7 +4298,7 @@ void BytecodeGenerator::popIndexedForInScope(RegisterID* localRegister)
 
 RegisterID* BytecodeGenerator::emitLoadArrowFunctionLexicalEnvironment(const Identifier& identifier)
 {
-    ASSERT(m_codeBlock->isArrowFunction() || m_codeBlock->isArrowFunctionContext() || constructorKind() == ConstructorKind::Derived || m_codeType == EvalCode);
+    ASSERT(m_codeBlock->isArrowFunction() || m_codeBlock->isArrowFunctionContext() || constructorKind() == ConstructorKind::Extends || m_codeType == EvalCode);
 
     return emitResolveScope(nullptr, variable(identifier, ThisResolutionType::Scoped));
 }
@@ -4311,10 +4324,10 @@ RegisterID* BytecodeGenerator::emitLoadDerivedConstructorFromArrowFunctionLexica
 
 RegisterID* BytecodeGenerator::ensureThis()
 {
-    if (constructorKind() == ConstructorKind::Derived && needsToUpdateArrowFunctionContext() && isSuperCallUsedInInnerArrowFunction())
+    if (constructorKind() == ConstructorKind::Extends && needsToUpdateArrowFunctionContext() && isSuperCallUsedInInnerArrowFunction())
         emitLoadThisFromArrowFunctionLexicalEnvironment();
 
-    if (constructorKind() == ConstructorKind::Derived || isDerivedConstructorContext())
+    if (constructorKind() == ConstructorKind::Extends || isDerivedConstructorContext())
         emitTDZCheck(thisRegister());
 
     return thisRegister();
@@ -4357,7 +4370,7 @@ void BytecodeGenerator::emitPutNewTargetToArrowFunctionContextScope()
     
 void BytecodeGenerator::emitPutDerivedConstructorToArrowFunctionContextScope()
 {
-    if ((isConstructor() && constructorKind() == ConstructorKind::Derived) || m_codeBlock->isClassContext()) {
+    if ((isConstructor() && constructorKind() == ConstructorKind::Extends) || m_codeBlock->isClassContext()) {
         if (isSuperUsedInInnerArrowFunction()) {
             ASSERT(m_arrowFunctionContextLexicalEnvironmentRegister);
             
index 5faf04f..ba9a94d 100644 (file)
@@ -829,7 +829,7 @@ namespace JSC {
             DerivedContextType newDerivedContextType = DerivedContextType::None;
 
             if (metadata->parseMode() == SourceParseMode::ArrowFunctionMode) {
-                if (constructorKind() == ConstructorKind::Derived || isDerivedConstructorContext())
+                if (constructorKind() == ConstructorKind::Extends || isDerivedConstructorContext())
                     newDerivedContextType = DerivedContextType::DerivedConstructorContext;
                 else if (m_codeBlock->isClassContext() || isDerivedClassContext())
                     newDerivedContextType = DerivedContextType::DerivedMethodContext;
@@ -910,6 +910,7 @@ namespace JSC {
         RegisterID* m_emptyValueRegister { nullptr };
         RegisterID* m_globalObjectRegister { nullptr };
         RegisterID* m_newTargetRegister { nullptr };
+        RegisterID* m_isDerivedConstuctor { nullptr };
         RegisterID* m_linkTimeConstantRegisters[LinkTimeConstantCount];
         RegisterID* m_arrowFunctionContextLexicalEnvironmentRegister { nullptr };
 
index 5c8f93f..e217fa5 100644 (file)
@@ -751,7 +751,7 @@ RegisterID* EvalFunctionCallNode::emitBytecode(BytecodeGenerator& generator, Reg
     //       eval("this.id = 'B'");
     //    }
     // }
-    if (generator.constructorKind() == ConstructorKind::Derived && generator.needsToUpdateArrowFunctionContext() && generator.isThisUsedInInnerArrowFunction())
+    if (generator.constructorKind() == ConstructorKind::Extends && generator.needsToUpdateArrowFunctionContext() && generator.isThisUsedInInnerArrowFunction())
         generator.emitLoadThisFromArrowFunctionLexicalEnvironment();
 
     Variable var = generator.variable(generator.propertyNames().eval);
@@ -785,11 +785,11 @@ RegisterID* FunctionCallValueNode::emitBytecode(BytecodeGenerator& generator, Re
         CallArguments callArguments(generator, m_args);
 
         ASSERT(generator.isConstructor() || generator.derivedContextType() == DerivedContextType::DerivedConstructorContext);
-        ASSERT(generator.constructorKind() == ConstructorKind::Derived || generator.derivedContextType() == DerivedContextType::DerivedConstructorContext);
+        ASSERT(generator.constructorKind() == ConstructorKind::Extends || generator.derivedContextType() == DerivedContextType::DerivedConstructorContext);
         generator.emitMove(callArguments.thisRegister(), generator.newTarget());
         RegisterID* ret = generator.emitConstruct(returnValue.get(), func.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd());
 
-        bool isConstructorKindDerived = generator.constructorKind() == ConstructorKind::Derived;
+        bool isConstructorKindDerived = generator.constructorKind() == ConstructorKind::Extends;
         bool doWeUseArrowFunctionInConstructor = isConstructorKindDerived && generator.needsToUpdateArrowFunctionContext();
 
         if (generator.isDerivedConstructorContext() || (doWeUseArrowFunctionInConstructor && generator.isSuperCallUsedInInnerArrowFunction()))
@@ -3364,7 +3364,7 @@ void FunctionNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)
 
         // If there is no return we must automatically insert one.
         if (!returnNode) {
-            if (generator.constructorKind() == ConstructorKind::Derived && generator.needsToUpdateArrowFunctionContext() && generator.isSuperCallUsedInInnerArrowFunction())
+            if (generator.constructorKind() == ConstructorKind::Extends && generator.needsToUpdateArrowFunctionContext() && generator.isSuperCallUsedInInnerArrowFunction())
                 generator.emitLoadThisFromArrowFunctionLexicalEnvironment(); // Arrow function can invoke 'super' in constructor and before leave constructor we need load 'this' from lexical arrow function environment
             
             RegisterID* r0 = generator.isConstructor() ? generator.thisRegister() : generator.emitLoad(0, jsUndefined());
@@ -3460,10 +3460,32 @@ RegisterID* ClassExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID
         metadata->setEcmaName(ecmaName());
         metadata->setClassSource(m_classSource);
         constructor = generator.emitNode(dst, m_constructorExpression);
+        if (m_classHeritage) {
+            RefPtr<RegisterID> isDerivedConstructor = generator.newTemporary();
+            generator.emitUnaryOp(op_not, isDerivedConstructor.get(),
+                generator.emitUnaryOp(op_eq_null, isDerivedConstructor.get(), superclass.get()));
+            generator.emitDirectPutById(constructor.get(), generator.propertyNames().builtinNames().isDerivedConstructorPrivateName(), isDerivedConstructor.get(), PropertyNode::Unknown);
+        }
     } else {
-        constructor = generator.emitNewDefaultConstructor(generator.finalDestination(dst),
-            m_classHeritage ? ConstructorKind::Derived : ConstructorKind::Base,
-            m_name, ecmaName(), m_classSource);
+        if (m_classHeritage) {
+            constructor = generator.finalDestination(dst);
+            RefPtr<RegisterID> tempRegister = generator.newTemporary();
+            RefPtr<Label> superclassIsNullLabel = generator.newLabel();
+            RefPtr<Label> done = generator.newLabel();
+
+            generator.emitJumpIfTrue(generator.emitUnaryOp(op_eq_null, tempRegister.get(), superclass.get()), superclassIsNullLabel.get());
+            generator.emitNewDefaultConstructor(constructor.get(), ConstructorKind::Extends, m_name, ecmaName(), m_classSource);
+            generator.emitLoad(tempRegister.get(), jsBoolean(true));
+            generator.emitJump(done.get());
+            generator.emitLabel(superclassIsNullLabel.get());
+            generator.emitNewDefaultConstructor(constructor.get(), ConstructorKind::Base, m_name, ecmaName(), m_classSource);
+            generator.emitLoad(tempRegister.get(), jsBoolean(false));
+            generator.emitLabel(done.get());
+            generator.emitDirectPutById(constructor.get(), generator.propertyNames().builtinNames().isDerivedConstructorPrivateName(), tempRegister.get(), PropertyNode::Unknown);
+        } else {
+            constructor = generator.emitNewDefaultConstructor(generator.finalDestination(dst),
+                ConstructorKind::Base, m_name, ecmaName(), m_classSource);
+        }
     }
 
     const auto& propertyNames = generator.propertyNames();
index 4a1c1e5..a056b0b 100644 (file)
@@ -224,7 +224,7 @@ Parser<LexerType>::Parser(VM* vm, const SourceCode& source, JSParserBuiltinMode
         scope->setEvalContextType(evalContextType);
     
     if (derivedContextType == DerivedContextType::DerivedConstructorContext) {
-        scope->setConstructorKind(ConstructorKind::Derived);
+        scope->setConstructorKind(ConstructorKind::Extends);
         scope->setExpectedSuperBinding(SuperBinding::Needed);
     }
     
@@ -2101,7 +2101,7 @@ template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuild
     
         if (m_defaultConstructorKind != ConstructorKind::None) {
             constructorKind = m_defaultConstructorKind;
-            expectedSuperBinding = m_defaultConstructorKind == ConstructorKind::Derived ? SuperBinding::Needed : SuperBinding::NotNeeded;
+            expectedSuperBinding = m_defaultConstructorKind == ConstructorKind::Extends ? SuperBinding::Needed : SuperBinding::NotNeeded;
         }
 
         functionBodyType = StandardFunctionBodyBlock;
@@ -2303,7 +2303,7 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T
         parentClass = parseMemberExpression(context);
         failIfFalse(parentClass, "Cannot parse the parent class name");
     }
-    const ConstructorKind constructorKind = parentClass ? ConstructorKind::Derived : ConstructorKind::Base;
+    const ConstructorKind constructorKind = parentClass ? ConstructorKind::Extends : ConstructorKind::Base;
 
     consumeOrFail(OPENBRACE, "Expected opening '{' at the start of a class body");
 
@@ -4017,7 +4017,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres
                                 ? functionScope->constructorKind()
                                 : closestOrdinaryFunctionScope->constructorKind();
                             semanticFailIfTrue(functionConstructorKind == ConstructorKind::None, "super is not valid in this context");
-                            semanticFailIfTrue(functionConstructorKind != ConstructorKind::Derived, "super is not valid in this context");
+                            semanticFailIfTrue(functionConstructorKind != ConstructorKind::Extends, "super is not valid in this context");
                         }
                     }
                     if (currentScope()->isArrowFunction())
index d2571e5..4cfad7a 100644 (file)
@@ -35,7 +35,7 @@ enum class JSParserStrictMode { NotStrict, Strict };
 enum class JSParserBuiltinMode { NotBuiltin, Builtin };
 enum class JSParserCodeType { Program, Function, Module };
 
-enum class ConstructorKind { None, Base, Derived };
+enum class ConstructorKind { None, Base, Extends };
 enum class SuperBinding { Needed, NotNeeded };
 
 enum DebuggerMode { DebuggerOff, DebuggerOn };