Support extends and super keywords
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 9 Mar 2015 23:47:06 +0000 (23:47 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 9 Mar 2015 23:47:06 +0000 (23:47 +0000)
https://bugs.webkit.org/show_bug.cgi?id=142200

Reviewed by Filip Pizlo.

Source/JavaScriptCore:

Added the support for ES6 class syntax inheritance.

Added ConstructorKind as well as boolean flags indicating the constructor kind to
various classes in UnlinkedCodeBlock as well as AST nodes.

Each method stores the associated class as its homeObjectPrivateName. This value is used to
make super calls.

* bytecode/UnlinkedCodeBlock.cpp:
(JSC::generateFunctionCodeBlock):
(JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):
(JSC::UnlinkedCodeBlock::UnlinkedCodeBlock):

* bytecode/UnlinkedCodeBlock.h:
(JSC::ExecutableInfo::ExecutableInfo):
(JSC::UnlinkedFunctionExecutable::constructorKindIsDerived): Added.
(JSC::UnlinkedCodeBlock::constructorKindIsDerived): Added.

* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator): Don't emit op_create_this in a derived class
as the object is allocated by the highest base class's constructor. Also set "this" to null
and store the original value in m_newTargetRegister. "this" is supposed to be in TDZ but
that will be implemented in a separate patch.
(JSC::BytecodeGenerator::emitReturn): Allow "undefined" to be returned from a derived class.
In a derived class's constructor, not returning "undefined" or an object results in a type
error instead of "this" being returned.
(JSC::BytecodeGenerator::emitThrowTypeError): Added.

* bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::constructorKindIsDerived): Added.
(JSC::BytecodeGenerator::newTarget): Added.

* bytecompiler/NodesCodegen.cpp:
(JSC::SuperNode::emitBytecode): Added. Emits the code to obtain the callee's parent class.
(JSC::emitSuperBaseForCallee): Added. Emits the code to obtain the parent class's prototype.
(JSC::emitPutHomeObject): Added.
(JSC::PropertyListNode::emitBytecode): Stores the home object when adding methods.
(JSC::PropertyListNode::emitPutConstantProperty): Ditto.
(JSC::BracketAccessorNode::emitBytecode): Added the support for super['foo'].
(JSC::DotAccessorNode::emitBytecode): Added the support for super.foo.
(JSC::FunctionCallValueNode::emitBytecode): Added the support for super().
(JSC::FunctionCallBracketNode::emitBytecode): Added the support for super['foo']().
(JSC::FunctionCallDotNode::emitBytecode): Added the support for super.foo().
(JSC::DeleteBracketNode::emitBytecode): Forbid "delete super.foo".
(JSC::DeleteDotNode::emitBytecode): Forbid "delete super['foo']".
(JSC::ClassExprNode::emitBytecode): Added the support for "classHeritage". This is the main
logic for inheritance. When a class B inherits from a class A, set B.__proto__ to A and set
B.prototype.__proto__ to A.prototype. Throw exceptions when either A or A.__proto__ is not
an object.

* parser/ASTBuilder.h:
(JSC::ASTBuilder::superExpr): Added.

* parser/NodeConstructors.h:
(JSC::SuperNode::SuperNode): Added.

* parser/Nodes.cpp:
(JSC::FunctionBodyNode::FunctionBodyNode):

* parser/Nodes.h:
(JSC::ExpressionNode::isSuperNode):
(JSC::PropertyNode::type):
(JSC::PropertyNode::needsSuperBinding):

* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseFunctionBody):
(JSC::Parser<LexerType>::parseFunctionInfo): Throw a parser error if super() is used outside
of class constructors.
(JSC::Parser<LexerType>::parseFunctionDeclaration):
(JSC::Parser<LexerType>::parseClass): ConstructorKind is "derived" if and only if the parent
class is specified in the declaration / expression.
(JSC::Parser<LexerType>::parseGetterSetter):
(JSC::Parser<LexerType>::parsePrimaryExpression):
(JSC::Parser<LexerType>::parseMemberExpression): Added the support for "super()", "super.foo",
and "super['foo']". Throw a semantic error if "super" appears by itself.

* parser/Parser.h:
(JSC::Scope::Scope): Added m_hasDirectSuper. This variable keeps track of the use of "super()"
so that parseFunctionInfo can spit an error if it's used outside of class constructors.
(JSC::Scope::hasDirectSuper): Added.
(JSC::Scope::setHasDirectSuper): Added.

* parser/ParserModes.h:
(JSC::ConstructorKind): Added.

* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::superExpr): Added.

* runtime/CommonIdentifiers.h: Added homeObjectPrivateName.

* runtime/Executable.h:
(JSC::EvalExecutable::executableInfo):
(JSC::ProgramExecutable::executableInfo):

LayoutTests:

Added tests for "extends" and "super" keywords.

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

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

24 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/js/class-syntax-extends-expected.txt [new file with mode: 0644]
LayoutTests/js/class-syntax-extends.html [new file with mode: 0644]
LayoutTests/js/class-syntax-super-expected.txt [new file with mode: 0644]
LayoutTests/js/class-syntax-super.html [new file with mode: 0644]
LayoutTests/js/script-tests/class-syntax-extends.js [new file with mode: 0644]
LayoutTests/js/script-tests/class-syntax-super.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp
Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/parser/ASTBuilder.h
Source/JavaScriptCore/parser/NodeConstructors.h
Source/JavaScriptCore/parser/Nodes.cpp
Source/JavaScriptCore/parser/Nodes.h
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/Parser.h
Source/JavaScriptCore/parser/ParserModes.h
Source/JavaScriptCore/parser/SyntaxChecker.h
Source/JavaScriptCore/runtime/CommonIdentifiers.h
Source/JavaScriptCore/runtime/Executable.h

index b8b9799ffe3490d8cb06c1f3414d6ea2874ca8ea..2d31f960bd8268c63665a7d69c05efad1e1baa19 100644 (file)
@@ -1,3 +1,20 @@
+2015-03-09  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Support extends and super keywords
+        https://bugs.webkit.org/show_bug.cgi?id=142200
+
+        Reviewed by Filip Pizlo.
+
+        Added tests for "extends" and "super" keywords.
+
+        * TestExpectations:
+        * js/class-syntax-extends-expected.txt: Added.
+        * js/class-syntax-extends.html: Added.
+        * js/class-syntax-super-expected.txt: Added.
+        * js/class-syntax-super.html: Added.
+        * js/script-tests/class-syntax-extends.js: Added.
+        * js/script-tests/class-syntax-super.js: Added.
+
 2015-03-09  Myles C. Maxfield  <mmaxfield@apple.com>
 
         REGRESSION(r176978): Inline-blocks with overflowing contents have ascents that are too large
index 1f1ee4e9f4e303981e9b7ea0237980a190eb41aa..61e0e25e3f9e6af60c435f04633e8d78d30c2600 100644 (file)
@@ -69,6 +69,8 @@ webkit.org/b/127860 [ Debug ] js/function-apply-aliased.html [ Skip ]
 # ES6 class syntax hasn't been enabled yet.
 webkit.org/b/140491 js/class-syntax-declaration.html [ Failure ]
 webkit.org/b/140491 js/class-syntax-expression.html [ Failure ]
+webkit.org/b/140491 js/class-syntax-extends.html [ Failure ]
+webkit.org/b/140491 js/class-syntax-super.html [ Failure ]
 
 # This test verifies dynamic manipulation of the mroot and msqrt elements.
 mathml/roots-removeChild.html [ ImageOnlyFailure ]
diff --git a/LayoutTests/js/class-syntax-extends-expected.txt b/LayoutTests/js/class-syntax-extends-expected.txt
new file mode 100644 (file)
index 0000000..c054bef
--- /dev/null
@@ -0,0 +1,31 @@
+Tests for ES6 class syntax "extends"
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS (new Base) instanceof Base is true
+PASS (new Derived) instanceof Derived is true
+PASS (new Derived).baseMethod() is "base"
+PASS (new Derived).overridenMethod() is "derived"
+PASS Derived.staticBaseMethod() is "base"
+PASS Derived.staticOverridenMethod() is "derived"
+PASS x = class extends threw exception SyntaxError: Unexpected end of script.
+PASS x = class extends threw exception SyntaxError: Unexpected end of script.
+PASS x = class extends Base { threw exception SyntaxError: Unexpected end of script.
+PASS x = class extends Base {} threw exception SyntaxError: Class declaration without a constructor is not supported yet..
+PASS x = class extends Base { constructor() { } } did not throw exception.
+PASS x.__proto__ is Base
+PASS x.prototype.__proto__ is Base.prototype
+PASS x = class extends null { constructor() { } }; x.__proto__ is Function.prototype
+PASS x.__proto__ is Function.prototype
+PASS x = class extends 3 { constructor() { } }; x.__proto__ threw exception TypeError: The superclass is not an object..
+PASS x = class extends "abc" { constructor() { } }; x.__proto__ threw exception TypeError: The superclass is not an object..
+baseWithBadPrototype = class { constructor() { } }; baseWithBadPrototype.prototype = 3
+PASS x = class extends baseWithBadPrototype { constructor() { } } threw exception TypeError: The superclass's prototype is not an object..
+baseWithBadPrototype.prototype = "abc"
+PASS x = class extends baseWithBadPrototype { constructor() { } } threw exception TypeError: The superclass's prototype is not an object..
+PASS baseWithBadPrototype.prototype = null; x = class extends baseWithBadPrototype { constructor() { } } did not throw exception.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/class-syntax-extends.html b/LayoutTests/js/class-syntax-extends.html
new file mode 100644 (file)
index 0000000..09006c5
--- /dev/null
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../resources/js-test-pre.js"></script>
+<script src="script-tests/class-syntax-extends.js"></script>
+<script src="../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/class-syntax-super-expected.txt b/LayoutTests/js/class-syntax-super-expected.txt
new file mode 100644 (file)
index 0000000..c3e5861
--- /dev/null
@@ -0,0 +1,41 @@
+Tests for ES6 class syntax "super"
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS (new Base) instanceof Base is true
+PASS (new Derived) instanceof Derived is true
+PASS (new Derived).callBaseMethod() is baseMethodValue
+PASS x = (new Derived).callBaseMethod; x() is baseMethodValue
+PASS (new Derived).callBaseMethodInGetter is baseMethodValue
+PASS (new Derived).callBaseMethodInSetter = 1; valueInSetter is baseMethodValue
+PASS (new Derived).baseMethodInGetterSetter is (new Base).baseMethod
+PASS (new Derived).baseMethodInGetterSetter = 1; valueInSetter is (new Base).baseMethod
+PASS Derived.staticMethod() is "base3"
+PASS (new SecondDerived).chainMethod() is ["base", "derived", "secondDerived"]
+PASS x = class extends Base { constructor() { super(); } super() {} } threw exception SyntaxError: Unexpected keyword 'super'. Expected an indentifier..
+PASS x = class extends Base { constructor() { super(); } method() { super() } } threw exception SyntaxError: Cannot call super() outside of a class constructor..
+PASS x = class extends Base { constructor() { super(); } method() { super } } threw exception SyntaxError: Cannot reference super..
+PASS x = class extends Base { constructor() { super(); } method() { return new super } } did not throw exception.
+PASS (new x).method() instanceof Base is true
+PASS (new x).method() instanceof x is false
+PASS x = class extends Base { constructor() { super(); } method1() { delete (super.foo) } method2() { delete super["foo"] } } did not throw exception.
+PASS (new x).method1() threw exception ReferenceError: Cannot delete a super property.
+PASS (new x).method2() threw exception ReferenceError: Cannot delete a super property.
+PASS new (class { constructor() { return undefined; } }) instanceof Object is true
+PASS new (class { constructor() { return 1; } }) instanceof Object is true
+PASS new (class extends Base { constructor() { return undefined } }) is undefined
+PASS x = { }; new (class extends Base { constructor() { return x } }); is x
+PASS x instanceof Base is false
+PASS new (class extends Base { constructor() { } }) threw exception TypeError: Cannot return a non-object type in the constructor of a derived class..
+PASS new (class extends Base { constructor() { return 1; } }) threw exception TypeError: Cannot return a non-object type in the constructor of a derived class..
+PASS new (class extends null { constructor() { return undefined } }) is undefined
+PASS x = { }; new (class extends null { constructor() { return x } }); is x
+PASS x instanceof Object is true
+PASS new (class extends null { constructor() { } }) threw exception TypeError: Cannot return a non-object type in the constructor of a derived class..
+PASS new (class extends null { constructor() { return 1; } }) threw exception TypeError: Cannot return a non-object type in the constructor of a derived class..
+PASS new (class extends null { constructor() { super() } }) did not throw exception.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/class-syntax-super.html b/LayoutTests/js/class-syntax-super.html
new file mode 100644 (file)
index 0000000..9ca1887
--- /dev/null
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../resources/js-test-pre.js"></script>
+<script src="script-tests/class-syntax-super.js"></script>
+<script src="../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/script-tests/class-syntax-extends.js b/LayoutTests/js/script-tests/class-syntax-extends.js
new file mode 100644 (file)
index 0000000..8776940
--- /dev/null
@@ -0,0 +1,43 @@
+//@ skip
+
+description('Tests for ES6 class syntax "extends"');
+
+class Base {
+    constructor() { }
+    baseMethod() { return 'base'; }
+    overridenMethod() { return 'base'; }
+    static staticBaseMethod() { return 'base'; }
+    static staticOverridenMethod() { return 'base'; }
+}
+
+class Derived extends Base {
+    constructor() { super(); }
+    overridenMethod() { return 'derived'; }
+    static staticOverridenMethod() { return 'derived'; }
+}
+
+shouldBeTrue('(new Base) instanceof Base');
+shouldBeTrue('(new Derived) instanceof Derived');
+shouldBe('(new Derived).baseMethod()', '"base"');
+shouldBe('(new Derived).overridenMethod()', '"derived"');
+shouldBe('Derived.staticBaseMethod()', '"base"');
+shouldBe('Derived.staticOverridenMethod()', '"derived"');
+
+shouldThrow('x = class extends', '"SyntaxError: Unexpected end of script"');
+shouldThrow('x = class extends', '"SyntaxError: Unexpected end of script"');
+shouldThrow('x = class extends Base {', '"SyntaxError: Unexpected end of script"');
+shouldThrow('x = class extends Base {}', '"SyntaxError: Class declaration without a constructor is not supported yet."');
+shouldNotThrow('x = class extends Base { constructor() { } }');
+shouldBe('x.__proto__', 'Base');
+shouldBe('x.prototype.__proto__', 'Base.prototype');
+shouldBe('x = class extends null { constructor() { } }; x.__proto__', 'Function.prototype');
+shouldBe('x.__proto__', 'Function.prototype');
+shouldThrow('x = class extends 3 { constructor() { } }; x.__proto__', '"TypeError: The superclass is not an object."');
+shouldThrow('x = class extends "abc" { constructor() { } }; x.__proto__', '"TypeError: The superclass is not an object."');
+evalAndLog('baseWithBadPrototype = class { constructor() { } }; baseWithBadPrototype.prototype = 3');
+shouldThrow('x = class extends baseWithBadPrototype { constructor() { } }', '"TypeError: The superclass\'s prototype is not an object."');
+evalAndLog('baseWithBadPrototype.prototype = "abc"');
+shouldThrow('x = class extends baseWithBadPrototype { constructor() { } }', '"TypeError: The superclass\'s prototype is not an object."');
+shouldNotThrow('baseWithBadPrototype.prototype = null; x = class extends baseWithBadPrototype { constructor() { } }');
+
+var successfullyParsed = true;
diff --git a/LayoutTests/js/script-tests/class-syntax-super.js b/LayoutTests/js/script-tests/class-syntax-super.js
new file mode 100644 (file)
index 0000000..3a164ae
--- /dev/null
@@ -0,0 +1,65 @@
+//@ skip
+
+description('Tests for ES6 class syntax "super"');
+
+var baseMethodValue = {};
+var valueInSetter = null;
+
+class Base {
+    constructor() { }
+    baseMethod() { return baseMethodValue; }
+    chainMethod() { return 'base'; }
+    static staticMethod() { return 'base3'; }
+}
+
+class Derived extends Base {
+    constructor() { super(); }
+    chainMethod() { return [super.chainMethod(), 'derived']; }
+    callBaseMethod() { return super.baseMethod(); }
+    get callBaseMethodInGetter() { return super['baseMethod'](); }
+    set callBaseMethodInSetter() { window.valueInSetter = super.baseMethod(); }
+    get baseMethodInGetterSetter() { return super.baseMethod; }
+    set baseMethodInGetterSetter() { window.valueInSetter = super['baseMethod']; }
+    static staticMethod() { return super.staticMethod(); }
+}
+
+class SecondDerived extends Derived {
+    constructor() { super(); }
+    chainMethod() { return super.chainMethod().concat(['secondDerived']); }
+}
+
+shouldBeTrue('(new Base) instanceof Base');
+shouldBeTrue('(new Derived) instanceof Derived');
+shouldBe('(new Derived).callBaseMethod()', 'baseMethodValue');
+shouldBe('x = (new Derived).callBaseMethod; x()', 'baseMethodValue');
+shouldBe('(new Derived).callBaseMethodInGetter', 'baseMethodValue');
+shouldBe('(new Derived).callBaseMethodInSetter = 1; valueInSetter', 'baseMethodValue');
+shouldBe('(new Derived).baseMethodInGetterSetter', '(new Base).baseMethod');
+shouldBe('(new Derived).baseMethodInGetterSetter = 1; valueInSetter', '(new Base).baseMethod');
+shouldBe('Derived.staticMethod()', '"base3"');
+shouldBe('(new SecondDerived).chainMethod()', '["base", "derived", "secondDerived"]');
+shouldThrow('x = class extends Base { constructor() { super(); } super() {} }', '"SyntaxError: Unexpected keyword \'super\'. Expected an indentifier."');
+shouldThrow('x = class extends Base { constructor() { super(); } method() { super() } }',
+    '"SyntaxError: Cannot call super() outside of a class constructor."');
+shouldThrow('x = class extends Base { constructor() { super(); } method() { super } }', '"SyntaxError: Cannot reference super."');
+shouldNotThrow('x = class extends Base { constructor() { super(); } method() { return new super } }');
+shouldBeTrue('(new x).method() instanceof Base');
+shouldBeFalse('(new x).method() instanceof x');
+shouldNotThrow('x = class extends Base { constructor() { super(); } method1() { delete (super.foo) } method2() { delete super["foo"] } }');
+shouldThrow('(new x).method1()', '"ReferenceError: Cannot delete a super property"');
+shouldThrow('(new x).method2()', '"ReferenceError: Cannot delete a super property"');
+shouldBeTrue('new (class { constructor() { return undefined; } }) instanceof Object');
+shouldBeTrue('new (class { constructor() { return 1; } }) instanceof Object');
+shouldBe('new (class extends Base { constructor() { return undefined } })', 'undefined');
+shouldBe('x = { }; new (class extends Base { constructor() { return x } });', 'x');
+shouldBeFalse('x instanceof Base');
+shouldThrow('new (class extends Base { constructor() { } })', '"TypeError: Cannot return a non-object type in the constructor of a derived class."');
+shouldThrow('new (class extends Base { constructor() { return 1; } })', '"TypeError: Cannot return a non-object type in the constructor of a derived class."');
+shouldBe('new (class extends null { constructor() { return undefined } })', 'undefined');
+shouldBe('x = { }; new (class extends null { constructor() { return x } });', 'x');
+shouldBeTrue('x instanceof Object');
+shouldThrow('new (class extends null { constructor() { } })', '"TypeError: Cannot return a non-object type in the constructor of a derived class."');
+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() { super() } })');
+
+var successfullyParsed = true;
index 4f6408e0bd1769c23972fb05ec1b58672c170095..734934e7fb48f80f79111db829c1e1376be7ba0b 100644 (file)
@@ -1,3 +1,104 @@
+2015-03-09  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Support extends and super keywords
+        https://bugs.webkit.org/show_bug.cgi?id=142200
+
+        Reviewed by Filip Pizlo.
+
+        Added the support for ES6 class syntax inheritance.
+
+        Added ConstructorKind as well as boolean flags indicating the constructor kind to
+        various classes in UnlinkedCodeBlock as well as AST nodes.
+
+        Each method stores the associated class as its homeObjectPrivateName. This value is used to
+        make super calls.
+
+        * bytecode/UnlinkedCodeBlock.cpp:
+        (JSC::generateFunctionCodeBlock):
+        (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):
+        (JSC::UnlinkedCodeBlock::UnlinkedCodeBlock):
+
+        * bytecode/UnlinkedCodeBlock.h:
+        (JSC::ExecutableInfo::ExecutableInfo):
+        (JSC::UnlinkedFunctionExecutable::constructorKindIsDerived): Added.
+        (JSC::UnlinkedCodeBlock::constructorKindIsDerived): Added.
+
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::BytecodeGenerator): Don't emit op_create_this in a derived class
+        as the object is allocated by the highest base class's constructor. Also set "this" to null
+        and store the original value in m_newTargetRegister. "this" is supposed to be in TDZ but
+        that will be implemented in a separate patch.
+        (JSC::BytecodeGenerator::emitReturn): Allow "undefined" to be returned from a derived class.
+        In a derived class's constructor, not returning "undefined" or an object results in a type
+        error instead of "this" being returned.
+        (JSC::BytecodeGenerator::emitThrowTypeError): Added.
+
+        * bytecompiler/BytecodeGenerator.h:
+        (JSC::BytecodeGenerator::constructorKindIsDerived): Added.
+        (JSC::BytecodeGenerator::newTarget): Added.
+
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::SuperNode::emitBytecode): Added. Emits the code to obtain the callee's parent class.
+        (JSC::emitSuperBaseForCallee): Added. Emits the code to obtain the parent class's prototype.
+        (JSC::emitPutHomeObject): Added.
+        (JSC::PropertyListNode::emitBytecode): Stores the home object when adding methods.
+        (JSC::PropertyListNode::emitPutConstantProperty): Ditto.
+        (JSC::BracketAccessorNode::emitBytecode): Added the support for super['foo'].
+        (JSC::DotAccessorNode::emitBytecode): Added the support for super.foo.
+        (JSC::FunctionCallValueNode::emitBytecode): Added the support for super().
+        (JSC::FunctionCallBracketNode::emitBytecode): Added the support for super['foo']().
+        (JSC::FunctionCallDotNode::emitBytecode): Added the support for super.foo().
+        (JSC::DeleteBracketNode::emitBytecode): Forbid "delete super.foo".
+        (JSC::DeleteDotNode::emitBytecode): Forbid "delete super['foo']".
+        (JSC::ClassExprNode::emitBytecode): Added the support for "classHeritage". This is the main
+        logic for inheritance. When a class B inherits from a class A, set B.__proto__ to A and set
+        B.prototype.__proto__ to A.prototype. Throw exceptions when either A or A.__proto__ is not
+        an object.
+
+        * parser/ASTBuilder.h:
+        (JSC::ASTBuilder::superExpr): Added.
+
+        * parser/NodeConstructors.h:
+        (JSC::SuperNode::SuperNode): Added.
+
+        * parser/Nodes.cpp:
+        (JSC::FunctionBodyNode::FunctionBodyNode):
+
+        * parser/Nodes.h:
+        (JSC::ExpressionNode::isSuperNode):
+        (JSC::PropertyNode::type):
+        (JSC::PropertyNode::needsSuperBinding):
+
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::parseFunctionBody):
+        (JSC::Parser<LexerType>::parseFunctionInfo): Throw a parser error if super() is used outside
+        of class constructors.
+        (JSC::Parser<LexerType>::parseFunctionDeclaration):
+        (JSC::Parser<LexerType>::parseClass): ConstructorKind is "derived" if and only if the parent
+        class is specified in the declaration / expression.
+        (JSC::Parser<LexerType>::parseGetterSetter):
+        (JSC::Parser<LexerType>::parsePrimaryExpression):
+        (JSC::Parser<LexerType>::parseMemberExpression): Added the support for "super()", "super.foo",
+        and "super['foo']". Throw a semantic error if "super" appears by itself.
+
+        * parser/Parser.h:
+        (JSC::Scope::Scope): Added m_hasDirectSuper. This variable keeps track of the use of "super()"
+        so that parseFunctionInfo can spit an error if it's used outside of class constructors.
+        (JSC::Scope::hasDirectSuper): Added.
+        (JSC::Scope::setHasDirectSuper): Added.
+
+        * parser/ParserModes.h:
+        (JSC::ConstructorKind): Added.
+
+        * parser/SyntaxChecker.h:
+        (JSC::SyntaxChecker::superExpr): Added.
+
+        * runtime/CommonIdentifiers.h: Added homeObjectPrivateName.
+
+        * runtime/Executable.h:
+        (JSC::EvalExecutable::executableInfo): 
+        (JSC::ProgramExecutable::executableInfo):
+
 2015-03-08  Andreas Kling  <akling@apple.com>
 
         JITThunks keeps finalized Weaks around, pinning WeakBlocks.
index 764587579c0e4f6b71a6a5b21966bad79860313d..22601dcc6a086c1ef58c4ec2de75fe54ae848efe 100644 (file)
@@ -61,7 +61,8 @@ static UnlinkedFunctionCodeBlock* generateFunctionCodeBlock(VM& vm, UnlinkedFunc
     function->finishParsing(executable->parameters(), executable->name(), executable->functionMode());
     executable->recordParse(function->features(), function->hasCapturedVariables());
     
-    UnlinkedFunctionCodeBlock* result = UnlinkedFunctionCodeBlock::create(&vm, FunctionCode, ExecutableInfo(function->needsActivation(), function->usesEval(), function->isStrictMode(), kind == CodeForConstruct, functionKind == UnlinkedBuiltinFunction));
+    UnlinkedFunctionCodeBlock* result = UnlinkedFunctionCodeBlock::create(&vm, FunctionCode,
+        ExecutableInfo(function->needsActivation(), function->usesEval(), function->isStrictMode(), kind == CodeForConstruct, functionKind == UnlinkedBuiltinFunction, executable->constructorKindIsDerived()));
     auto generator(std::make_unique<BytecodeGenerator>(vm, function.get(), result, debuggerMode, profilerMode));
     error = generator->generate();
     if (error.isValid())
@@ -84,6 +85,7 @@ UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(VM* vm, Structure* struct
     , m_isInStrictContext(node->isInStrictContext())
     , m_hasCapturedVariables(false)
     , m_isBuiltinFunction(kind == UnlinkedBuiltinFunction)
+    , m_constructorKindIsDerived(node->constructorKindIsDerived())
     , m_name(node->ident())
     , m_inferredName(node->inferredName())
     , m_parameters(node->parameters())
@@ -229,6 +231,7 @@ UnlinkedCodeBlock::UnlinkedCodeBlock(VM* vm, Structure* structure, CodeType code
     , m_isConstructor(info.m_isConstructor)
     , m_hasCapturedVariables(false)
     , m_isBuiltinFunction(info.m_isBuiltinFunction)
+    , m_constructorKindIsDerived(info.m_constructorKindIsDerived)
     , m_firstLine(0)
     , m_lineCount(0)
     , m_endColumn(UINT_MAX)
index 16d4cab0069ae9883370a378eb1f1fdfe972df0f..64633946b2129f2a2727d748fc7b988963810650 100644 (file)
@@ -65,12 +65,13 @@ typedef unsigned UnlinkedObjectAllocationProfile;
 typedef unsigned UnlinkedLLIntCallLinkInfo;
 
 struct ExecutableInfo {
-    ExecutableInfo(bool needsActivation, bool usesEval, bool isStrictMode, bool isConstructor, bool isBuiltinFunction)
+    ExecutableInfo(bool needsActivation, bool usesEval, bool isStrictMode, bool isConstructor, bool isBuiltinFunction, bool constructorKindIsDerived)
         : m_needsActivation(needsActivation)
         , m_usesEval(usesEval)
         , m_isStrictMode(isStrictMode)
         , m_isConstructor(isConstructor)
         , m_isBuiltinFunction(isBuiltinFunction)
+        , m_constructorKindIsDerived(constructorKindIsDerived)
     {
     }
     bool m_needsActivation : 1;
@@ -78,6 +79,7 @@ struct ExecutableInfo {
     bool m_isStrictMode : 1;
     bool m_isConstructor : 1;
     bool m_isBuiltinFunction : 1;
+    bool m_constructorKindIsDerived : 1;
 };
 
 enum UnlinkedFunctionKind {
@@ -116,6 +118,7 @@ public:
             return JSParseStrict;
         return JSParseNormal;
     }
+    bool constructorKindIsDerived() const { return m_constructorKindIsDerived; }
 
     unsigned unlinkedFunctionNameStart() const { return m_unlinkedFunctionNameStart; }
     unsigned unlinkedBodyStartColumn() const { return m_unlinkedBodyStartColumn; }
@@ -167,6 +170,7 @@ private:
     bool m_isInStrictContext : 1;
     bool m_hasCapturedVariables : 1;
     bool m_isBuiltinFunction : 1;
+    bool m_constructorKindIsDerived : 1;
 
     Identifier m_name;
     Identifier m_inferredName;
@@ -341,7 +345,9 @@ public:
     bool isNumericCompareFunction() const { return m_isNumericCompareFunction; }
 
     bool isBuiltinFunction() const { return m_isBuiltinFunction; }
-    
+
+    bool constructorKindIsDerived() const { return m_constructorKindIsDerived; }
+
     void shrinkToFit()
     {
         m_jumpTargets.shrinkToFit();
@@ -533,6 +539,7 @@ private:
     bool m_isConstructor : 1;
     bool m_hasCapturedVariables : 1;
     bool m_isBuiltinFunction : 1;
+    bool m_constructorKindIsDerived : 1;
     unsigned m_firstLine;
     unsigned m_lineCount;
     unsigned m_endColumn;
index 787c2509d214ed6f10b9b6506fe947f749c2753a..fb165bf55bbb7c6a991c9aa0ca7ec07b8a4962c1 100644 (file)
@@ -401,7 +401,12 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke
     addCallee(functionNode, calleeRegister);
 
     if (isConstructor()) {
-        emitCreateThis(&m_thisRegister);
+        if (constructorKindIsDerived()) {
+            m_newTargetRegister = addVar();
+            emitMove(m_newTargetRegister, &m_thisRegister);
+            emitLoad(&m_thisRegister, jsNull());
+        } else
+            emitCreateThis(&m_thisRegister);
     } else if (functionNode->usesThis() || codeBlock->usesEval()) {
         m_codeBlock->addPropertyAccessInstruction(instructions().size());
         emitOpcode(op_to_this);
@@ -1901,14 +1906,19 @@ RegisterID* BytecodeGenerator::emitReturn(RegisterID* src)
         instructions().append(m_lexicalEnvironmentRegister ? m_lexicalEnvironmentRegister->index() : emitLoad(0, JSValue())->index());
     }
 
-    if (isConstructor() && src->index() != m_thisRegister.index()) {
-        RefPtr<Label> isObjectLabel = newLabel();
+    bool thisMightBeUninitialized = constructorKindIsDerived();
+    if (isConstructor() && (src->index() != m_thisRegister.index() || thisMightBeUninitialized)) {
+        RefPtr<Label> isObjectOrUndefinedLabel = newLabel();
 
-        emitJumpIfTrue(emitIsObject(newTemporary(), src), isObjectLabel.get());
+        emitJumpIfTrue(emitIsObject(newTemporary(), src), isObjectOrUndefinedLabel.get());
 
-        emitUnaryNoDstOp(op_ret, &m_thisRegister);
+        if (constructorKindIsDerived()) {
+            emitJumpIfTrue(emitIsUndefined(newTemporary(), src), isObjectOrUndefinedLabel.get());
+            emitThrowTypeError("Cannot return a non-object type in the constructor of a derived class.");
+        } else
+            emitUnaryNoDstOp(op_ret, &m_thisRegister);
 
-        emitLabel(isObjectLabel.get());
+        emitLabel(isObjectOrUndefinedLabel.get());
     }
     return emitUnaryNoDstOp(op_ret, src);
 }
index b83f4fdedca7063908322672703d22830ded637f..c9b88755c92d6a9798afb6b6ad8a6df1db349b79 100644 (file)
@@ -269,7 +269,12 @@ namespace JSC {
         ParserArena& parserArena() const { return m_scopeNode->parserArena(); }
         const CommonIdentifiers& propertyNames() const { return *m_vm->propertyNames; }
 
-        bool isConstructor() { return m_codeBlock->isConstructor(); }
+        bool isConstructor() const { return m_codeBlock->isConstructor(); }
+#if ENABLE(ES6_CLASS_SYNTAX)
+        bool constructorKindIsDerived() const { return m_codeBlock->constructorKindIsDerived(); }
+#else
+        bool constructorKindIsDerived() const { return false; }
+#endif
 
         ParserError generate();
 
@@ -290,7 +295,8 @@ namespace JSC {
 
         // Returns the register storing "this"
         RegisterID* thisRegister() { return &m_thisRegister; }
-        
+        RegisterID* newTarget() { return m_newTargetRegister; }
+
         RegisterID* scopeRegister() { return m_scopeRegister; }
 
         // Returns the next available temporary register. Registers returned by
@@ -763,6 +769,7 @@ namespace JSC {
         RegisterID* m_emptyValueRegister { nullptr };
         RegisterID* m_globalObjectRegister { nullptr };
         RegisterID* m_localArgumentsRegister { nullptr };
+        RegisterID* m_newTargetRegister { nullptr };
 
         Vector<Identifier, 16> m_watchableVariables;
         SegmentedVector<RegisterID, 32> m_constantPoolRegisters;
index d0233c1108ae07dbe80ca84bd090522a0b8baca2..5581d15af46e29bcb84348321eb8ac19daa3b1f2 100644 (file)
@@ -156,6 +156,30 @@ RegisterID* ThisNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst
     return result;
 }
 
+// ------------------------------ SuperNode -------------------------------------
+
+RegisterID* SuperNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
+{
+    if (dst == generator.ignoredResult())
+        return 0;
+
+    RegisterID callee;
+    callee.setIndex(JSStack::Callee);
+
+    RefPtr<RegisterID> homeObject = generator.emitGetById(generator.newTemporary(), &callee, generator.propertyNames().homeObjectPrivateName);
+    RefPtr<RegisterID> protoParent = generator.emitGetById(generator.newTemporary(), homeObject.get(), generator.propertyNames().underscoreProto);
+    return generator.emitGetById(generator.finalDestination(dst), protoParent.get(), generator.propertyNames().constructor);
+}
+
+static RegisterID* emitSuperBaseForCallee(BytecodeGenerator& generator)
+{
+    RegisterID callee;
+    callee.setIndex(JSStack::Callee);
+
+    RefPtr<RegisterID> homeObject = generator.emitGetById(generator.newTemporary(), &callee, generator.propertyNames().homeObjectPrivateName);
+    return generator.emitGetById(generator.newTemporary(), homeObject.get(), generator.propertyNames().underscoreProto);
+}
+
 // ------------------------------ ResolveNode ----------------------------------
 
 bool ResolveNode::isPure(BytecodeGenerator& generator) const
@@ -294,6 +318,11 @@ RegisterID* ObjectLiteralNode::emitBytecode(BytecodeGenerator& generator, Regist
 
 // ------------------------------ PropertyListNode -----------------------------
 
+static inline void emitPutHomeObject(BytecodeGenerator& generator, RegisterID* function, RegisterID* homeObject)
+{
+    generator.emitPutById(function, generator.propertyNames().homeObjectPrivateName, homeObject);
+}
+
 RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 {
     // Fast case: this loop just handles regular value properties.
@@ -330,6 +359,8 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe
             }
 
             RegisterID* value = generator.emitNode(node->m_assign);
+            if (node->needsSuperBinding())
+                emitPutHomeObject(generator, value, dst);
 
             // This is a get/set property, find its entry in the map.
             ASSERT(node->m_type == PropertyNode::Getter || node->m_type == PropertyNode::Setter);
@@ -344,12 +375,14 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe
             // Generate the paired node now.
             RefPtr<RegisterID> getterReg;
             RefPtr<RegisterID> setterReg;
+            RegisterID* secondReg = nullptr;
 
             if (node->m_type == PropertyNode::Getter) {
                 getterReg = value;
                 if (pair.second) {
                     ASSERT(pair.second->m_type == PropertyNode::Setter);
                     setterReg = generator.emitNode(pair.second->m_assign);
+                    secondReg = setterReg.get();
                 } else {
                     setterReg = generator.newTemporary();
                     generator.emitLoad(setterReg.get(), jsUndefined());
@@ -360,12 +393,16 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe
                 if (pair.second) {
                     ASSERT(pair.second->m_type == PropertyNode::Getter);
                     getterReg = generator.emitNode(pair.second->m_assign);
+                    secondReg = getterReg.get();
                 } else {
                     getterReg = generator.newTemporary();
                     generator.emitLoad(getterReg.get(), jsUndefined());
                 }
             }
 
+            if (pair.second && pair.second->needsSuperBinding())
+                emitPutHomeObject(generator, secondReg, dst);
+
             generator.emitPutGetterSetter(dst, *node->name(), getterReg.get(), setterReg.get());
         }
     }
@@ -375,18 +412,30 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe
 
 void PropertyListNode::emitPutConstantProperty(BytecodeGenerator& generator, RegisterID* newObj, PropertyNode& node)
 {
+    RefPtr<RegisterID> value = generator.emitNode(node.m_assign);
+    if (node.needsSuperBinding())
+        emitPutHomeObject(generator, value.get(), newObj);
     if (node.name()) {
-        generator.emitDirectPutById(newObj, *node.name(), generator.emitNode(node.m_assign), node.putType());
+        generator.emitDirectPutById(newObj, *node.name(), value.get(), node.putType());
         return;
     }
     RefPtr<RegisterID> propertyName = generator.emitNode(node.m_expression);
-    generator.emitDirectPutByVal(newObj, propertyName.get(), generator.emitNode(node.m_assign));
+    generator.emitDirectPutByVal(newObj, propertyName.get(), value.get());
 }
 
 // ------------------------------ BracketAccessorNode --------------------------------
 
 RegisterID* BracketAccessorNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 {
+    if (m_base->isSuperNode()) {
+        // FIXME: Should we generate the profiler info?
+        if (m_subscript->isString()) {
+            const Identifier& id = static_cast<StringNode*>(m_subscript)->value();
+            return generator.emitGetById(generator.finalDestination(dst), emitSuperBaseForCallee(generator), id);
+        }
+        return generator.emitGetByVal(generator.finalDestination(dst), emitSuperBaseForCallee(generator), generator.emitNode(m_subscript));
+    }
+
     if (m_base->isResolveNode() 
         && generator.willResolveToArgumentsRegister(static_cast<ResolveNode*>(m_base)->identifier())
         && !generator.symbolTable().slowArguments()) {
@@ -431,7 +480,7 @@ RegisterID* DotAccessorNode::emitBytecode(BytecodeGenerator& generator, Register
     }
 
 nonArgumentsPath:
-    RefPtr<RegisterID> base = generator.emitNode(m_base);
+    RefPtr<RegisterID> base = m_base->isSuperNode() ? emitSuperBaseForCallee(generator) : generator.emitNode(m_base);
     generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
     RegisterID* finalDest = generator.finalDestination(dst);
     RegisterID* ret = generator.emitGetById(finalDest, base.get(), m_ident);
@@ -521,6 +570,13 @@ RegisterID* FunctionCallValueNode::emitBytecode(BytecodeGenerator& generator, Re
     RefPtr<RegisterID> func = generator.emitNode(m_expr);
     RefPtr<RegisterID> returnValue = generator.finalDestination(dst, func.get());
     CallArguments callArguments(generator, m_args);
+    if (m_expr->isSuperNode()) {
+        ASSERT(generator.constructorKindIsDerived());
+        generator.emitMove(callArguments.thisRegister(), generator.newTarget());
+        RegisterID* ret = generator.emitConstruct(returnValue.get(), func.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd());
+        generator.emitMove(generator.thisRegister(), ret);
+        return ret;
+    }
     generator.emitLoad(callArguments.thisRegister(), jsUndefined());
     RegisterID* ret = generator.emitCall(returnValue.get(), func.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd());
     if (generator.vm()->typeProfiler()) {
@@ -574,13 +630,17 @@ RegisterID* FunctionCallResolveNode::emitBytecode(BytecodeGenerator& generator,
 
 RegisterID* FunctionCallBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 {
-    RefPtr<RegisterID> base = generator.emitNode(m_base);
+    bool baseIsSuper = m_base->isSuperNode();
+    RefPtr<RegisterID> base = baseIsSuper ? emitSuperBaseForCallee(generator) : generator.emitNode(m_base);
     RefPtr<RegisterID> property = generator.emitNode(m_subscript);
     generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd());
     RefPtr<RegisterID> function = generator.emitGetByVal(generator.tempDestination(dst), base.get(), property.get());
     RefPtr<RegisterID> returnValue = generator.finalDestination(dst, function.get());
     CallArguments callArguments(generator, m_args);
-    generator.emitMove(callArguments.thisRegister(), base.get());
+    if (baseIsSuper)
+        generator.emitMove(callArguments.thisRegister(), generator.thisRegister());
+    else
+        generator.emitMove(callArguments.thisRegister(), base.get());
     RegisterID* ret = generator.emitCall(returnValue.get(), function.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd());
     if (generator.vm()->typeProfiler()) {
         generator.emitProfileType(returnValue.get(), ProfileTypeBytecodeDoesNotHaveGlobalID, nullptr);
@@ -596,9 +656,13 @@ RegisterID* FunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, Regi
     RefPtr<RegisterID> function = generator.tempDestination(dst);
     RefPtr<RegisterID> returnValue = generator.finalDestination(dst, function.get());
     CallArguments callArguments(generator, m_args);
-    generator.emitNode(callArguments.thisRegister(), m_base);
+    bool baseIsSuper = m_base->isSuperNode();
+    if (baseIsSuper)
+        generator.emitMove(callArguments.thisRegister(), generator.thisRegister());
+    else
+        generator.emitNode(callArguments.thisRegister(), m_base);
     generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd());
-    generator.emitGetById(function.get(), callArguments.thisRegister(), m_ident);
+    generator.emitGetById(function.get(), baseIsSuper ? emitSuperBaseForCallee(generator) : callArguments.thisRegister(), m_ident);
     RegisterID* ret = generator.emitCall(returnValue.get(), function.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd());
     if (generator.vm()->typeProfiler()) {
         generator.emitProfileType(returnValue.get(), ProfileTypeBytecodeDoesNotHaveGlobalID, nullptr);
@@ -940,6 +1004,8 @@ RegisterID* DeleteBracketNode::emitBytecode(BytecodeGenerator& generator, Regist
     RefPtr<RegisterID> r1 = generator.emitNode(m_subscript);
 
     generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
+    if (m_base->isSuperNode())
+        return emitThrowReferenceError(generator, "Cannot delete a super property");
     return generator.emitDeleteByVal(generator.finalDestination(dst), r0.get(), r1.get());
 }
 
@@ -950,6 +1016,8 @@ RegisterID* DeleteDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID
     RefPtr<RegisterID> r0 = generator.emitNode(m_base);
 
     generator.emitExpressionInfo(divot(), divotStart(), divotEnd());
+    if (m_base->isSuperNode())
+        return emitThrowReferenceError(generator, "Cannot delete a super property");
     return generator.emitDeleteById(generator.finalDestination(dst), r0.get(), m_ident);
 }
 
@@ -2807,11 +2875,42 @@ void ClassDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 
 RegisterID* ClassExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
 {
-    ASSERT(!m_parentClassExpression);
+    RefPtr<RegisterID> superclass;
+    if (m_classHeritage) {
+        superclass = generator.newTemporary();
+        generator.emitNode(superclass.get(), m_classHeritage);
+    }
 
     RefPtr<RegisterID> constructor = generator.emitNode(dst, m_constructorExpression);
+    // FIXME: Make the prototype non-configurable & non-writable.
     RefPtr<RegisterID> prototype = generator.emitGetById(generator.newTemporary(), constructor.get(), generator.propertyNames().prototype);
 
+    if (superclass) {
+        RefPtr<RegisterID> tempRegister = generator.newTemporary();
+        RefPtr<Label> superclassIsNullLabel = generator.newLabel();
+        generator.emitJumpIfTrue(generator.emitUnaryOp(op_eq_null, tempRegister.get(), superclass.get()), superclassIsNullLabel.get());
+
+        // FIXME: Throw TypeError if it's a generator function.
+        RefPtr<Label> superclassIsObjectLabel = generator.newLabel();
+        generator.emitJumpIfTrue(generator.emitIsObject(tempRegister.get(), superclass.get()), superclassIsObjectLabel.get());
+        generator.emitThrowTypeError(ASCIILiteral("The superclass is not an object."));
+        generator.emitLabel(superclassIsObjectLabel.get());
+
+        RefPtr<RegisterID> protoParent = generator.newTemporary();
+        generator.emitGetById(protoParent.get(), superclass.get(), generator.propertyNames().prototype);
+
+        RefPtr<Label> protoParentIsObjectOrNullLabel = generator.newLabel();
+        generator.emitJumpIfTrue(generator.emitUnaryOp(op_is_object_or_null, tempRegister.get(), protoParent.get()), protoParentIsObjectOrNullLabel.get());
+        generator.emitThrowTypeError(ASCIILiteral("The superclass's prototype is not an object."));
+        generator.emitLabel(protoParentIsObjectOrNullLabel.get());
+
+        generator.emitDirectPutById(constructor.get(), generator.propertyNames().underscoreProto, superclass.get(), PropertyNode::Unknown);
+        generator.emitDirectPutById(prototype.get(), generator.propertyNames().underscoreProto, protoParent.get(), PropertyNode::Unknown);
+
+        generator.emitLabel(superclassIsNullLabel.get());
+        emitPutHomeObject(generator, constructor.get(), prototype.get());
+    }
+
     if (m_staticMethods)
         generator.emitNode(constructor.get(), m_staticMethods);
 
index 1b0dc8a23b236037745d1ec066ab6a58dc5684bc..df3be1e9410fdcf5ebeaccd85158760e5af86f39 100644 (file)
@@ -169,6 +169,10 @@ public:
         usesThis();
         return new (m_parserArena) ThisNode(location);
     }
+    ExpressionNode* superExpr(const JSTokenLocation& location)
+    {
+        return new (m_parserArena) SuperNode(location);
+    }
     ExpressionNode* createResolve(const JSTokenLocation& location, const Identifier* ident, const JSTextPosition& start)
     {
         if (m_vm->propertyNames->arguments == *ident)
@@ -298,9 +302,9 @@ public:
         return result;
     }
 
-    FunctionBodyNode* createFunctionBody(const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, bool inStrictContext)
+    FunctionBodyNode* createFunctionBody(const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, bool inStrictContext, ConstructorKind constructorKind)
     {
-        return new (m_parserArena) FunctionBodyNode(m_parserArena, startLocation, endLocation, startColumn, endColumn, inStrictContext);
+        return new (m_parserArena) FunctionBodyNode(m_parserArena, startLocation, endLocation, startColumn, endColumn, inStrictContext, constructorKind);
     }
 
     void setFunctionNameStart(FunctionBodyNode* body, int functionNameStart)
@@ -309,23 +313,26 @@ public:
     }
     
     NEVER_INLINE PropertyNode* createGetterOrSetterProperty(const JSTokenLocation& location, PropertyNode::Type type, bool,
-        const Identifier* name, const ParserFunctionInfo<ASTBuilder>& info, unsigned getOrSetStartOffset)
+        const Identifier* name, const ParserFunctionInfo<ASTBuilder>& info, unsigned getOrSetStartOffset, SuperBinding superBinding)
     {
         ASSERT(name);
         info.body->setLoc(info.bodyStartLine, info.bodyEndLine, location.startOffset, location.lineStartOffset);
         info.body->setInferredName(*name);
         info.body->setFunctionKeywordStart(getOrSetStartOffset);
-        return new (m_parserArena) PropertyNode(*name, new (m_parserArena) FuncExprNode(location, m_vm->propertyNames->nullIdentifier,
-            info.body, m_sourceCode->subExpression(info.openBraceOffset, info.closeBraceOffset, info.bodyStartLine, info.bodyStartColumn), info.parameters), type, PropertyNode::Unknown);
+        SourceCode source = m_sourceCode->subExpression(info.openBraceOffset, info.closeBraceOffset, info.bodyStartLine, info.bodyStartColumn);
+        FuncExprNode* funcExpr = new (m_parserArena) FuncExprNode(location, m_vm->propertyNames->nullIdentifier, info.body, source, info.parameters);
+        return new (m_parserArena) PropertyNode(*name, funcExpr, type, PropertyNode::Unknown, superBinding);
     }
     
-    NEVER_INLINE PropertyNode* createGetterOrSetterProperty(VM* vm, ParserArena& parserArena, const JSTokenLocation& location, PropertyNode::Type type, bool, double name, const ParserFunctionInfo<ASTBuilder>& info, unsigned getOrSetStartOffset)
+    NEVER_INLINE PropertyNode* createGetterOrSetterProperty(VM* vm, ParserArena& parserArena, const JSTokenLocation& location, PropertyNode::Type type, bool,
+        double name, const ParserFunctionInfo<ASTBuilder>& info, unsigned getOrSetStartOffset, SuperBinding superBinding)
     {
         info.body->setLoc(info.bodyStartLine, info.bodyEndLine, location.startOffset, location.lineStartOffset);
         info.body->setFunctionKeywordStart(getOrSetStartOffset);
         const Identifier& ident = parserArena.identifierArena().makeNumericIdentifier(vm, name);
-        return new (m_parserArena) PropertyNode(ident, new (m_parserArena) FuncExprNode(location, vm->propertyNames->nullIdentifier,
-            info.body, m_sourceCode->subExpression(info.openBraceOffset, info.closeBraceOffset, info.bodyStartLine, info.bodyStartColumn), info.parameters), type, PropertyNode::Unknown);
+        SourceCode source = m_sourceCode->subExpression(info.openBraceOffset, info.closeBraceOffset, info.bodyStartLine, info.bodyStartColumn);
+        FuncExprNode* funcExpr = new (m_parserArena) FuncExprNode(location, vm->propertyNames->nullIdentifier, info.body, source, info.parameters);
+        return new (m_parserArena) PropertyNode(ident, funcExpr, type, PropertyNode::Unknown, superBinding);
     }
 
     ArgumentsNode* createArguments() { return new (m_parserArena) ArgumentsNode(); }
@@ -333,11 +340,11 @@ public:
     ArgumentListNode* createArgumentsList(const JSTokenLocation& location, ExpressionNode* arg) { return new (m_parserArena) ArgumentListNode(location, arg); }
     ArgumentListNode* createArgumentsList(const JSTokenLocation& location, ArgumentListNode* args, ExpressionNode* arg) { return new (m_parserArena) ArgumentListNode(location, args, arg); }
 
-    PropertyNode* createProperty(const Identifier* propertyName, ExpressionNode* node, PropertyNode::Type type, PropertyNode::PutType putType, bool)
+    PropertyNode* createProperty(const Identifier* propertyName, ExpressionNode* node, PropertyNode::Type type, PropertyNode::PutType putType, bool, SuperBinding superBinding = SuperBinding::NotNeeded)
     {
         if (node->isFuncExprNode())
             static_cast<FuncExprNode*>(node)->body()->setInferredName(*propertyName);
-        return new (m_parserArena) PropertyNode(*propertyName, node, type, putType);
+        return new (m_parserArena) PropertyNode(*propertyName, node, type, putType, superBinding);
     }
     PropertyNode* createProperty(VM* vm, ParserArena& parserArena, double propertyName, ExpressionNode* node, PropertyNode::Type type, PropertyNode::PutType putType, bool)
     {
index b4d9b1dfc61dae8e4740ae8cd64d6f839a140f73..057bc3d92809f46b06271ac012abecd6c88a684a 100644 (file)
@@ -112,7 +112,12 @@ namespace JSC {
     {
     }
 
-inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifier& ident, const JSTextPosition& start)
+    inline SuperNode::SuperNode(const JSTokenLocation& location)
+        : ExpressionNode(location)
+    {
+    }
+
+    inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifier& ident, const JSTextPosition& start)
         : ExpressionNode(location)
         , m_ident(ident)
         , m_start(start)
@@ -159,10 +164,11 @@ inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifie
     {
     }
 
-    inline PropertyNode::PropertyNode(const Identifier& name, ExpressionNode* assign, Type type, PutType putType)
+    inline PropertyNode::PropertyNode(const Identifier& name, ExpressionNode* assign, Type type, PutType putType, SuperBinding superBinding = SuperBinding::NotNeeded)
         : m_name(&name)
         , m_assign(assign)
         , m_type(type)
+        , m_needsSuperBinding(superBinding == SuperBinding::Needed)
         , m_putType(putType)
     {
     }
@@ -172,6 +178,7 @@ inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifie
         , m_expression(name)
         , m_assign(assign)
         , m_type(type)
+        , m_needsSuperBinding(false)
         , m_putType(putType)
     {
     }
@@ -787,11 +794,11 @@ inline ResolveNode::ResolveNode(const JSTokenLocation& location, const Identifie
     {
     }
 
-    inline ClassExprNode::ClassExprNode(const JSTokenLocation& location, const Identifier& name, ExpressionNode* constructorExpression, ExpressionNode* parentClassExpression, PropertyListNode* instanceMethods, PropertyListNode* staticMethods)
+    inline ClassExprNode::ClassExprNode(const JSTokenLocation& location, const Identifier& name, ExpressionNode* constructorExpression, ExpressionNode* classHeritage, PropertyListNode* instanceMethods, PropertyListNode* staticMethods)
         : ExpressionNode(location)
         , m_name(name)
         , m_constructorExpression(constructorExpression)
-        , m_parentClassExpression(parentClassExpression)
+        , m_classHeritage(classHeritage)
         , m_instanceMethods(instanceMethods)
         , m_staticMethods(staticMethods)
     {
index 04af61d9aba8f42500a8de6cef508839c178b1c4..123921487532694c75dc42878ac7cd83a35bff3f 100644 (file)
@@ -167,12 +167,13 @@ FunctionParameters::~FunctionParameters()
         patterns()[i]->deref();
 }
 
-FunctionBodyNode::FunctionBodyNode(ParserArena&, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, bool isInStrictContext)
+FunctionBodyNode::FunctionBodyNode(ParserArena&, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, bool isInStrictContext, ConstructorKind constructorKind)
     : StatementNode(endLocation)
     , m_startColumn(startColumn)
     , m_endColumn(endColumn)
     , m_startStartOffset(startLocation.startOffset)
     , m_isInStrictContext(isInStrictContext)
+    , m_constructorKindIsDerived(constructorKind == ConstructorKind::Derived)
 {
 }
 
index 98319c4bf1f44db7a032ca48326c916810263dda..40bc1bed8c1c6afee8262954d43e56f405938cbd 100644 (file)
@@ -164,6 +164,7 @@ namespace JSC {
         virtual bool isSubtract() const { return false; }
         virtual bool isBoolean() const { return false; }
         virtual bool isSpreadExpression() const { return false; }
+        virtual bool isSuperNode() const { return false; }
 
         virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label*, Label*, FallThroughMode);
 
@@ -445,6 +446,15 @@ namespace JSC {
         virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
     };
 
+    class SuperNode final : public ExpressionNode {
+    public:
+        SuperNode(const JSTokenLocation&);
+
+    private:
+        virtual bool isSuperNode() const override { return true; }
+        virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
+    };
+
     class ResolveNode : public ExpressionNode {
     public:
         ResolveNode(const JSTokenLocation&, const Identifier&, const JSTextPosition& start);
@@ -501,22 +511,24 @@ namespace JSC {
         enum Type { Constant = 1, Getter = 2, Setter = 4 };
         enum PutType { Unknown, KnownDirect };
 
-        PropertyNode(const Identifier&, ExpressionNode*, Type, PutType);
+        PropertyNode(const Identifier&, ExpressionNode*, Type, PutType, SuperBinding);
         PropertyNode(ExpressionNode* propertyName, ExpressionNode*, Type, PutType);
-        
+
         ExpressionNode* expressionName() const { return m_expression; }
         const Identifier* name() const { return m_name; }
 
-        Type type() const { return m_type; }
-        PutType putType() const { return m_putType; }
+        Type type() const { return static_cast<Type>(m_type); }
+        bool needsSuperBinding() const { return m_needsSuperBinding; }
+        PutType putType() const { return static_cast<PutType>(m_putType); }
 
     private:
         friend class PropertyListNode;
         const Identifier* m_name;
         ExpressionNode* m_expression;
         ExpressionNode* m_assign;
-        Type m_type;
-        PutType m_putType;
+        unsigned m_type : 3;
+        unsigned m_needsSuperBinding : 1;
+        unsigned m_putType : 1;
     };
 
     class PropertyListNode : public ExpressionNode {
@@ -1547,7 +1559,7 @@ namespace JSC {
     public:
         using ParserArenaDeletable::operator new;
 
-        FunctionBodyNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, bool isInStrictContext);
+        FunctionBodyNode(ParserArena&, const JSTokenLocation& start, const JSTokenLocation& end, unsigned startColumn, unsigned endColumn, bool isInStrictContext, ConstructorKind);
 
         FunctionParameters* parameters() const { return m_parameters.get(); }
 
@@ -1575,6 +1587,7 @@ namespace JSC {
 
         int startStartOffset() const { return m_startStartOffset; }
         bool isInStrictContext() const { return m_isInStrictContext; }
+        bool constructorKindIsDerived() { return m_constructorKindIsDerived; }
 
     protected:
         Identifier m_ident;
@@ -1587,7 +1600,8 @@ namespace JSC {
         unsigned m_endColumn;
         SourceCode m_source;
         int m_startStartOffset;
-        bool m_isInStrictContext;
+        bool m_isInStrictContext : 1;
+        bool m_constructorKindIsDerived : 1;
     };
 
     class FunctionNode final : public ScopeNode {
@@ -1644,7 +1658,7 @@ namespace JSC {
 
         const Identifier& m_name;
         ExpressionNode* m_constructorExpression;
-        ExpressionNode* m_parentClassExpression;
+        ExpressionNode* m_classHeritage;
         PropertyListNode* m_instanceMethods;
         PropertyListNode* m_staticMethods;
     };
index 10c7aec08e9adba03249a01f5b9c57ae5a4fdde3..16e143c5fa49a3168f771061fcb262d92676aa8d 100644 (file)
@@ -1255,7 +1255,7 @@ template <class TreeBuilder> TreeFormalParameterList Parser<LexerType>::parseFor
 }
 
 template <typename LexerType>
-template <class TreeBuilder> TreeFunctionBody Parser<LexerType>::parseFunctionBody(TreeBuilder& context)
+template <class TreeBuilder> TreeFunctionBody Parser<LexerType>::parseFunctionBody(TreeBuilder& context, ConstructorKind constructorKind)
 {
     JSTokenLocation startLocation(tokenLocation());
     unsigned startColumn = tokenColumn();
@@ -1263,14 +1263,14 @@ template <class TreeBuilder> TreeFunctionBody Parser<LexerType>::parseFunctionBo
 
     if (match(CLOSEBRACE)) {
         unsigned endColumn = tokenColumn();
-        return context.createFunctionBody(startLocation, tokenLocation(), startColumn, endColumn, strictMode());
+        return context.createFunctionBody(startLocation, tokenLocation(), startColumn, endColumn, strictMode(), constructorKind);
     }
     DepthManager statementDepth(&m_statementDepth);
     m_statementDepth = 0;
     typename TreeBuilder::FunctionBodyBuilder bodyBuilder(const_cast<VM*>(m_vm), m_lexer.get());
     failIfFalse(parseSourceElements(bodyBuilder, CheckForStrictMode), "Cannot parse body of this function");
     unsigned endColumn = tokenColumn();
-    return context.createFunctionBody(startLocation, tokenLocation(), startColumn, endColumn, strictMode());
+    return context.createFunctionBody(startLocation, tokenLocation(), startColumn, endColumn, strictMode(), constructorKind);
 }
 
 static const char* stringForFunctionMode(FunctionParseMode mode)
@@ -1290,7 +1290,8 @@ static const char* stringForFunctionMode(FunctionParseMode mode)
 }
 
 template <typename LexerType>
-template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuilder& context, FunctionRequirements requirements, FunctionParseMode mode, bool nameIsInContainingScope, ParserFunctionInfo<TreeBuilder>& info)
+template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuilder& context, FunctionRequirements requirements, FunctionParseMode mode,
+    bool nameIsInContainingScope, ConstructorKind constructorKind, ParserFunctionInfo<TreeBuilder>& info)
 {
     AutoPopScopeRef functionScope(this, pushScope());
     functionScope->setIsFunction();
@@ -1343,7 +1344,7 @@ template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuild
             endLocation.startOffset - endLocation.lineStartOffset;
         unsigned currentLineStartOffset = m_token.m_location.lineStartOffset;
 
-        info.body = context.createFunctionBody(startLocation, endLocation, info.bodyStartColumn, bodyEndColumn, cachedInfo->strictMode);
+        info.body = context.createFunctionBody(startLocation, endLocation, info.bodyStartColumn, bodyEndColumn, cachedInfo->strictMode, constructorKind);
         
         functionScope->restoreFromSourceProviderCache(cachedInfo);
         failIfFalse(popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo), "Parser error");
@@ -1366,15 +1367,19 @@ template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuild
     }
     m_lastFunctionName = lastFunctionName;
     ParserState oldState = saveState();
-    info.body = parseFunctionBody(context);
+    info.body = parseFunctionBody(context, constructorKind);
     restoreState(oldState);
     failIfFalse(info.body, "Cannot parse the body of this ", stringForFunctionMode(mode));
     context.setEndOffset(info.body, m_lexer->currentOffset());
     if (functionScope->strictMode() && info.name) {
-        RELEASE_ASSERT(mode == FunctionMode);
+        RELEASE_ASSERT(mode == FunctionMode || mode == MethodMode);
         semanticFailIfTrue(m_vm->propertyNames->arguments == *info.name, "'", info.name->impl(), "' is not a valid function name in strict mode");
         semanticFailIfTrue(m_vm->propertyNames->eval == *info.name, "'", info.name->impl(), "' is not a valid function name in strict mode");
     }
+    if (functionScope->hasDirectSuper()) {
+        bool nameIsConstructor = info.name && *info.name == m_vm->propertyNames->constructor;
+        semanticFailIfTrue(mode != MethodMode || !nameIsConstructor, "Cannot call super() outside of a class constructor");
+    }
     info.closeBraceOffset = m_token.m_data.offset;
     unsigned closeBraceLine = m_token.m_data.line;
     unsigned closeBraceLineStartOffset = m_token.m_data.lineStartOffset;
@@ -1415,7 +1420,7 @@ template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDecla
     unsigned functionKeywordStart = tokenStart();
     next();
     ParserFunctionInfo<TreeBuilder> info;
-    failIfFalse((parseFunctionInfo(context, FunctionNeedsName, FunctionMode, true, info)), "Cannot parse this function");
+    failIfFalse((parseFunctionInfo(context, FunctionNeedsName, FunctionMode, true, ConstructorKind::Base, info)), "Cannot parse this function");
     failIfFalse(info.name, "Function statements must have a name");
     failIfFalseIfStrict(declareVariable(info.name), "Cannot declare a function named '", info.name->impl(), "' in strict mode");
     return context.createFuncDeclStatement(location, info, functionKeywordStart);
@@ -1465,8 +1470,8 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T
     if (consume(EXTENDS)) {
         parentClass = parsePrimaryExpression(context);
         failIfFalse(parentClass, "Cannot parse the parent class name");
-        failWithMessage("Inheritance is not supported yet");
     }
+    const ConstructorKind constructorKind = parentClass ? ConstructorKind::Derived : ConstructorKind::Base;
 
     consumeOrFailWithFlags(OPENBRACE, TreeBuilder::DontBuildStrings, "Expected opening '{' at the start of a class body");
 
@@ -1498,11 +1503,11 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T
         if (isGetter || isSetter) {
             semanticFailIfTrue(isStaticMethod, "Cannot declare a static", stringForFunctionMode(isGetter ? GetterMode : SetterMode));
             nextExpectIdentifier(LexerFlagsIgnoreReservedWords);
-            property = parseGetterSetter(context, alwaysStrictInsideClass, isGetter ? PropertyNode::Getter : PropertyNode::Setter, methodStart);
+            property = parseGetterSetter(context, alwaysStrictInsideClass, isGetter ? PropertyNode::Getter : PropertyNode::Setter, methodStart, SuperBinding::Needed);
             failIfFalse(property, "Cannot parse this method");
         } else {
             ParserFunctionInfo<TreeBuilder> methodInfo;
-            failIfFalse((parseFunctionInfo(context, FunctionNeedsName, FunctionMode, false, methodInfo)), "Cannot parse this method");
+            failIfFalse((parseFunctionInfo(context, FunctionNeedsName, MethodMode, false, constructorKind, methodInfo)), "Cannot parse this method");
             failIfFalse(methodInfo.name, "method must have a name");
             failIfFalse(declareVariable(methodInfo.name), "Cannot declare a method named '", methodInfo.name->impl(), "'");
 
@@ -1520,7 +1525,7 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T
             // FIXME: Syntax error when super() is called
             semanticFailIfTrue(isStaticMethod && *methodInfo.name == propertyNames.prototype,
                 "Cannot declare a static method named 'prototype'");
-            property = context.createProperty(methodInfo.name, method, PropertyNode::Constant, PropertyNode::Unknown, alwaysStrictInsideClass);
+            property = context.createProperty(methodInfo.name, method, PropertyNode::Constant, PropertyNode::Unknown, alwaysStrictInsideClass, SuperBinding::Needed);
         }
 
         TreePropertyList& tail = isStaticMethod ? staticMethodsTail : instanceMethodsTail;
@@ -2007,13 +2012,13 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePropertyMeth
     JSTokenLocation methodLocation(tokenLocation());
     unsigned methodStart = tokenStart();
     ParserFunctionInfo<TreeBuilder> methodInfo;
-    failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, MethodMode, false, methodInfo)), "Cannot parse this method");
+    failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, MethodMode, false, ConstructorKind::Base, methodInfo)), "Cannot parse this method");
     methodInfo.name = methodName;
     return context.createFunctionExpr(methodLocation, methodInfo, methodStart);
 }
 
 template <typename LexerType>
-template <class TreeBuilder> TreeProperty Parser<LexerType>::parseGetterSetter(TreeBuilder& context, bool strict, PropertyNode::Type type, unsigned getterOrSetterStartOffset)
+template <class TreeBuilder> TreeProperty Parser<LexerType>::parseGetterSetter(TreeBuilder& context, bool strict, PropertyNode::Type type, unsigned getterOrSetterStartOffset, SuperBinding superBinding)
 {
     const Identifier* stringPropertyName = 0;
     double numericPropertyName = 0;
@@ -2028,14 +2033,14 @@ template <class TreeBuilder> TreeProperty Parser<LexerType>::parseGetterSetter(T
     ParserFunctionInfo<TreeBuilder> info;
     if (type == PropertyNode::Getter) {
         failIfFalse(match(OPENPAREN), "Expected a parameter list for getter definition");
-        failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, GetterMode, false, info)), "Cannot parse getter definition");
+        failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, GetterMode, false, ConstructorKind::Base, info)), "Cannot parse getter definition");
     } else {
         failIfFalse(match(OPENPAREN), "Expected a parameter list for setter definition");
-        failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, SetterMode, false, info)), "Cannot parse setter definition");
+        failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, SetterMode, false, ConstructorKind::Base, info)), "Cannot parse setter definition");
     }
     if (stringPropertyName)
-        return context.createGetterOrSetterProperty(location, type, strict, stringPropertyName, info, getterOrSetterStartOffset);
-    return context.createGetterOrSetterProperty(const_cast<VM*>(m_vm), m_parserArena, location, type, strict, numericPropertyName, info, getterOrSetterStartOffset);
+        return context.createGetterOrSetterProperty(location, type, strict, stringPropertyName, info, getterOrSetterStartOffset, superBinding);
+    return context.createGetterOrSetterProperty(const_cast<VM*>(m_vm), m_parserArena, location, type, strict, numericPropertyName, info, getterOrSetterStartOffset, superBinding);
 }
 
 template <typename LexerType>
@@ -2220,7 +2225,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpre
         next();
         ParserFunctionInfo<TreeBuilder> info;
         info.name = &m_vm->propertyNames->nullIdentifier;
-        failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, FunctionMode, false, info)), "Cannot parse function expression");
+        failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, FunctionMode, false, ConstructorKind::Base, info)), "Cannot parse function expression");
         return context.createFunctionExpr(location, info, functionKeywordStart);
     }
 #if ENABLE(ES6_CLASS_SYNTAX)
@@ -2370,8 +2375,18 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres
         newCount++;
     }
 
-    base = parsePrimaryExpression(context);
-    
+#if ENABLE(ES6_CLASS_SYNTAX)
+    bool baseIsSuper = match(SUPER);
+#else
+    bool baseIsSuper = false;
+#endif
+
+    if (baseIsSuper) {
+        base = context.superExpr(location);
+        next();
+    } else
+        base = parsePrimaryExpression(context);
+
     failIfFalse(base, "Cannot parse base expression");
     while (true) {
         location = tokenLocation();
@@ -2402,6 +2417,8 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres
                 JSTextPosition expressionEnd = lastTokenEndPosition();
                 TreeArguments arguments = parseArguments(context, AllowSpread);
                 failIfFalse(arguments, "Cannot parse call arguments");
+                if (baseIsSuper)
+                    currentScope()->setHasDirectSuper();
                 base = context.makeFunctionCallNode(startLocation, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition());
             }
             m_nonLHSCount = nonLHSCount;
@@ -2419,8 +2436,10 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres
         default:
             goto endMemberExpression;
         }
+        baseIsSuper = false;
     }
 endMemberExpression:
+    semanticFailIfTrue(baseIsSuper && !newCount, "Cannot reference super");
     while (newCount--)
         base = context.createNewExpr(location, base, expressionStart, lastTokenEndPosition());
     return base;
index 1f75a5accdf31d29d1b58f268f8d4a23f750a25c..a1cb2950123aef3f85098daa557795a9ecfefd5f 100644 (file)
@@ -113,6 +113,7 @@ struct Scope {
         , m_shadowsArguments(false)
         , m_usesEval(false)
         , m_needsFullActivation(false)
+        , m_hasDirectSuper(false)
         , m_allowsNewDecls(true)
         , m_strictMode(strictMode)
         , m_isFunction(isFunction)
@@ -128,6 +129,7 @@ struct Scope {
         , m_shadowsArguments(rhs.m_shadowsArguments)
         , m_usesEval(rhs.m_usesEval)
         , m_needsFullActivation(rhs.m_needsFullActivation)
+        , m_hasDirectSuper(rhs.m_hasDirectSuper)
         , m_allowsNewDecls(rhs.m_allowsNewDecls)
         , m_strictMode(rhs.m_strictMode)
         , m_isFunction(rhs.m_isFunction)
@@ -263,6 +265,13 @@ struct Scope {
 
     void setNeedsFullActivation() { m_needsFullActivation = true; }
 
+#if ENABLE(ES6_CLASS_SYNTAX)
+    bool hasDirectSuper() { return m_hasDirectSuper; }
+#else
+    bool hasDirectSuper() { return false; }
+#endif
+    void setHasDirectSuper() { m_hasDirectSuper = true; }
+
     bool collectFreeVariables(Scope* nestedScope, bool shouldTrackClosedVariables)
     {
         if (nestedScope->m_usesEval)
@@ -356,6 +365,7 @@ private:
     bool m_shadowsArguments : 1;
     bool m_usesEval : 1;
     bool m_needsFullActivation : 1;
+    bool m_hasDirectSuper : 1;
     bool m_allowsNewDecls : 1;
     bool m_strictMode : 1;
     bool m_isFunction : 1;
@@ -738,8 +748,8 @@ private:
     template <class TreeBuilder> ALWAYS_INLINE TreeArguments parseArguments(TreeBuilder&, SpreadMode);
     template <class TreeBuilder> TreeProperty parseProperty(TreeBuilder&, bool strict);
     template <class TreeBuilder> TreeExpression parsePropertyMethod(TreeBuilder& context, const Identifier* methodName);
-    template <class TreeBuilder> TreeProperty parseGetterSetter(TreeBuilder&, bool strict, PropertyNode::Type, unsigned getterOrSetterStartOffset);
-    template <class TreeBuilder> ALWAYS_INLINE TreeFunctionBody parseFunctionBody(TreeBuilder&);
+    template <class TreeBuilder> TreeProperty parseGetterSetter(TreeBuilder&, bool strict, PropertyNode::Type, unsigned getterOrSetterStartOffset, SuperBinding = SuperBinding::NotNeeded);
+    template <class TreeBuilder> ALWAYS_INLINE TreeFunctionBody parseFunctionBody(TreeBuilder&, ConstructorKind);
     template <class TreeBuilder> ALWAYS_INLINE TreeFormalParameterList parseFormalParameters(TreeBuilder&);
     enum VarDeclarationListContext { ForLoopContext, VarDeclarationContext };
     template <class TreeBuilder> TreeExpression parseVarDeclarationList(TreeBuilder&, int& declarations, TreeDeconstructionPattern& lastPattern, TreeExpression& lastInitializer, JSTextPosition& identStart, JSTextPosition& initStart, JSTextPosition& initEnd, VarDeclarationListContext);
@@ -749,7 +759,7 @@ private:
     template <class TreeBuilder> NEVER_INLINE TreeDeconstructionPattern parseDeconstructionPattern(TreeBuilder&, DeconstructionKind, int depth = 0);
     template <class TreeBuilder> NEVER_INLINE TreeDeconstructionPattern tryParseDeconstructionPatternExpression(TreeBuilder&);
 
-    template <class TreeBuilder> NEVER_INLINE bool parseFunctionInfo(TreeBuilder&, FunctionRequirements, FunctionParseMode, bool nameIsInContainingScope, ParserFunctionInfo<TreeBuilder>&);
+    template <class TreeBuilder> NEVER_INLINE bool parseFunctionInfo(TreeBuilder&, FunctionRequirements, FunctionParseMode, bool nameIsInContainingScope, ConstructorKind, ParserFunctionInfo<TreeBuilder>&);
 #if ENABLE(ES6_CLASS_SYNTAX)
     template <class TreeBuilder> NEVER_INLINE TreeClassExpression parseClass(TreeBuilder&, FunctionRequirements);
 #endif
index 0611b88e70cea658cc835278cb4670fa211be117..e209748265b2cf03741d8b10353c31044a157dd5 100644 (file)
@@ -34,6 +34,9 @@ namespace JSC {
 enum JSParserStrictness { JSParseNormal, JSParseBuiltin, JSParseStrict };
 enum JSParserMode { JSParseProgramCode, JSParseFunctionCode };
 
+enum class ConstructorKind { Base, Derived };
+enum class SuperBinding { Needed, NotNeeded };
+
 enum ProfilerMode { ProfilerOff, ProfilerOn };
 enum DebuggerMode { DebuggerOff, DebuggerOn };
 
index 441d30b5a4b3068fdc0f3450dea198ad7682db95..aa38e32db166a42001ca2326808738ed200b8b5a 100644 (file)
@@ -73,7 +73,7 @@ public:
     enum { NoneExpr = 0,
         ResolveEvalExpr, ResolveExpr, IntegerExpr, DoubleExpr, StringExpr,
         ThisExpr, NullExpr, BoolExpr, RegExpExpr, ObjectLiteralExpr,
-        FunctionExpr, ClassExpr, BracketExpr, DotExpr, CallExpr,
+        FunctionExpr, ClassExpr, SuperExpr, BracketExpr, DotExpr, CallExpr,
         NewExpr, PreExpr, PostExpr, UnaryExpr, BinaryExpr,
         ConditionalExpr, AssignmentExpr, TypeofExpr,
         DeleteExpr, ArrayLiteralExpr, BindingDeconstruction,
@@ -146,6 +146,7 @@ public:
     ExpressionType createUnaryPlus(const JSTokenLocation&, ExpressionType) { return UnaryExpr; }
     ExpressionType createVoid(const JSTokenLocation&, ExpressionType) { return UnaryExpr; }
     ExpressionType thisExpr(const JSTokenLocation&) { return ThisExpr; }
+    ExpressionType superExpr(const JSTokenLocation&) { return SuperExpr; }
     ExpressionType createResolve(const JSTokenLocation&, const Identifier*, int) { return ResolveExpr; }
     ExpressionType createObjectLiteral(const JSTokenLocation&) { return ObjectLiteralExpr; }
     ExpressionType createObjectLiteral(const JSTokenLocation&, int) { return ObjectLiteralExpr; }
@@ -168,14 +169,14 @@ public:
     ClassExpression createClassExpr(const JSTokenLocation&, const Identifier&, ExpressionType, ExpressionType, PropertyList, PropertyList) { return ClassExpr; }
 #endif
     ExpressionType createFunctionExpr(const JSTokenLocation&, const ParserFunctionInfo<SyntaxChecker>&, int) { return FunctionExpr; }
-    int createFunctionBody(const JSTokenLocation&, const JSTokenLocation&, int, int, bool) { return FunctionBodyResult; }
+    int createFunctionBody(const JSTokenLocation&, const JSTokenLocation&, int, int, bool, ConstructorKind) { return FunctionBodyResult; }
     void setFunctionNameStart(int, int) { }
     int createArguments() { return ArgumentsResult; }
     int createArguments(int) { return ArgumentsResult; }
     ExpressionType createSpreadExpression(const JSTokenLocation&, ExpressionType, int, int, int) { return SpreadExpr; }
     int createArgumentsList(const JSTokenLocation&, int) { return ArgumentsListResult; }
     int createArgumentsList(const JSTokenLocation&, int, int) { return ArgumentsListResult; }
-    Property createProperty(const Identifier* name, int, PropertyNode::Type type, PropertyNode::PutType, bool complete)
+    Property createProperty(const Identifier* name, int, PropertyNode::Type type, PropertyNode::PutType, bool complete, SuperBinding = SuperBinding::NotNeeded)
     {
         if (!complete)
             return Property(type);
@@ -230,14 +231,14 @@ public:
     int createDebugger(const JSTokenLocation&, int, int) { return StatementResult; }
     int createConstStatement(const JSTokenLocation&, int, int, int) { return StatementResult; }
     int appendConstDecl(const JSTokenLocation&, int, const Identifier*, int) { return StatementResult; }
-    Property createGetterOrSetterProperty(const JSTokenLocation&, PropertyNode::Type type, bool strict, const Identifier* name, const ParserFunctionInfo<SyntaxChecker>&, unsigned)
+    Property createGetterOrSetterProperty(const JSTokenLocation&, PropertyNode::Type type, bool strict, const Identifier* name, const ParserFunctionInfo<SyntaxChecker>&, unsigned, SuperBinding)
     {
         ASSERT(name);
         if (!strict)
             return Property(type);
         return Property(name, type);
     }
-    Property createGetterOrSetterProperty(VM* vm, ParserArena& parserArena, const JSTokenLocation&, PropertyNode::Type type, bool strict, double name, const ParserFunctionInfo<SyntaxChecker>&, unsigned)
+    Property createGetterOrSetterProperty(VM* vm, ParserArena& parserArena, const JSTokenLocation&, PropertyNode::Type type, bool strict, double name, const ParserFunctionInfo<SyntaxChecker>&, unsigned, SuperBinding)
     {
         if (!strict)
             return Property(type);
index e95c976361712e2d23e115a2d9f2d0103c862e93..6f34fa930d3b16287580ba45dd5b57d407ed3e46 100644 (file)
     macro(isFinite) \
     macro(TypeError) \
     macro(undefined) \
-    macro(BuiltinLog)
+    macro(BuiltinLog) \
+    macro(homeObject)
 
 namespace JSC {
     
index a1beee462664238ba4d1c9ee667a30efb9cac175..bf94a1d7024078adb0819fbcf3f7e2d61c3ce94f 100644 (file)
@@ -467,7 +467,7 @@ public:
 
     void clearCode();
 
-    ExecutableInfo executableInfo() const { return ExecutableInfo(needsActivation(), usesEval(), isStrictMode(), false, false); }
+    ExecutableInfo executableInfo() const { return ExecutableInfo(needsActivation(), usesEval(), isStrictMode(), false, false, false); }
 
     unsigned numVariables() { return m_unlinkedEvalCodeBlock->numVariables(); }
     unsigned numberOfFunctionDecls() { return m_unlinkedEvalCodeBlock->numberOfFunctionDecls(); }
@@ -522,7 +522,7 @@ public:
 
     void clearCode();
 
-    ExecutableInfo executableInfo() const { return ExecutableInfo(needsActivation(), usesEval(), isStrictMode(), false, false); }
+    ExecutableInfo executableInfo() const { return ExecutableInfo(needsActivation(), usesEval(), isStrictMode(), false, false, false); }
 
 private:
     friend class ScriptExecutable;