Calling super() in a base class results in a crash
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 11 Mar 2015 21:03:24 +0000 (21:03 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 11 Mar 2015 21:03:24 +0000 (21:03 +0000)
https://bugs.webkit.org/show_bug.cgi?id=142563

Reviewed by Filip Pizlo.

Source/JavaScriptCore:

The bug was caused by BytecodeGenerator trying to generate "super" expression inside the constructor of a base class.
Disallow that by keeping track of whether "super" has been used in the current scope or not (needsSuperBinding flag)
and then throwing a syntax error in parseFunctionInfo if it was used and the current scope wasn't the constructor of
a derived class.

* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseFunctionInfo): Don't allow super() or super.foo outside the constructor of a derived class.
(JSC::Parser<LexerType>::parseClass): Pass in the constructor kind to parseGetterSetter.
(JSC::Parser<LexerType>::parseGetterSetter): Ditto to parseFunctionInfo.
(JSC::Parser<LexerType>::parseMemberExpression): Set needsSuperBinding flag true on the containing scope.
* parser/Parser.h:
(JSC::Scope::Scope):
(JSC::Scope::needsSuperBinding): Added.
(JSC::Scope::setNeedsSuperBinding): Added.

LayoutTests:

Added more test cases to an existing test.

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

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

LayoutTests/ChangeLog
LayoutTests/js/class-syntax-super-expected.txt
LayoutTests/js/script-tests/class-syntax-super.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/parser/Parser.cpp
Source/JavaScriptCore/parser/Parser.h

index adeee48..bc43ec1 100644 (file)
@@ -1,3 +1,15 @@
+2015-03-11  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Calling super() in a base class results in a crash
+        https://bugs.webkit.org/show_bug.cgi?id=142563
+
+        Reviewed by Filip Pizlo.
+
+        Added more test cases to an existing test.
+
+        * js/class-syntax-super-expected.txt:
+        * js/script-tests/class-syntax-super.js:
+
 2015-03-11  Said Abou-Hallawa  <sabouhallawa@apple.com>
 
         svg/animations/smil-leak-*.svg tests are flaky.
index c3e5861..1f42791 100644 (file)
@@ -35,6 +35,12 @@ 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 new (class { constructor() { super() } }) threw exception SyntaxError: Cannot call super() in a base class constructor..
+PASS function x() { super(); } threw exception SyntaxError: Cannot call super() outside of a class constructor..
+PASS new (class extends Object { constructor() { function x() { super() } } }) threw exception SyntaxError: Cannot call super() outside of a class constructor..
+PASS new (class extends Object { constructor() { function x() { super.method } } }) threw exception SyntaxError: super can only be used in a method of a derived class..
+PASS function x() { super.method(); } threw exception SyntaxError: super can only be used in a method of a derived class..
+PASS function x() { super(); } threw exception SyntaxError: Cannot call super() outside of a class constructor..
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 3a164ae..d1531bb 100644 (file)
@@ -61,5 +61,11 @@ 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() } })');
+shouldThrow('new (class { constructor() { super() } })', '"SyntaxError: Cannot call super() in a base class constructor."');
+shouldThrow('function x() { super(); }', '"SyntaxError: Cannot call super() outside of a class constructor."');
+shouldThrow('new (class extends Object { constructor() { function x() { super() } } })', '"SyntaxError: Cannot call super() outside of a class constructor."');
+shouldThrow('new (class extends Object { constructor() { function x() { super.method } } })', '"SyntaxError: super can only be used in a method of a derived class."');
+shouldThrow('function x() { super.method(); }', '"SyntaxError: super can only be used in a method of a derived class."');
+shouldThrow('function x() { super(); }', '"SyntaxError: Cannot call super() outside of a class constructor."');
 
 var successfullyParsed = true;
index f54b504..f78434a 100644 (file)
@@ -1,3 +1,25 @@
+2015-03-11  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Calling super() in a base class results in a crash
+        https://bugs.webkit.org/show_bug.cgi?id=142563
+
+        Reviewed by Filip Pizlo.
+
+        The bug was caused by BytecodeGenerator trying to generate "super" expression inside the constructor of a base class.
+        Disallow that by keeping track of whether "super" has been used in the current scope or not (needsSuperBinding flag)
+        and then throwing a syntax error in parseFunctionInfo if it was used and the current scope wasn't the constructor of
+        a derived class.
+
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::parseFunctionInfo): Don't allow super() or super.foo outside the constructor of a derived class.
+        (JSC::Parser<LexerType>::parseClass): Pass in the constructor kind to parseGetterSetter.
+        (JSC::Parser<LexerType>::parseGetterSetter): Ditto to parseFunctionInfo.
+        (JSC::Parser<LexerType>::parseMemberExpression): Set needsSuperBinding flag true on the containing scope.
+        * parser/Parser.h:
+        (JSC::Scope::Scope):
+        (JSC::Scope::needsSuperBinding): Added.
+        (JSC::Scope::setNeedsSuperBinding): Added.
+
 2015-03-10  Darin Adler  <darin@apple.com>
 
         Some event handler fixes
index 16e143c..12e483b 100644 (file)
@@ -1377,9 +1377,13 @@ template <class TreeBuilder> bool Parser<LexerType>::parseFunctionInfo(TreeBuild
         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");
+        bool isClassConstructor = mode == MethodMode && info.name && *info.name == m_vm->propertyNames->constructor;
+        semanticFailIfTrue(!isClassConstructor, "Cannot call super() outside of a class constructor");
+        semanticFailIfTrue(constructorKind == ConstructorKind::Base, "Cannot call super() in a base class constructor");
     }
+    if (functionScope->needsSuperBinding())
+        semanticFailIfTrue(constructorKind == ConstructorKind::Base, "super can only be used in a method of a derived class");
+
     info.closeBraceOffset = m_token.m_data.offset;
     unsigned closeBraceLine = m_token.m_data.line;
     unsigned closeBraceLineStartOffset = m_token.m_data.lineStartOffset;
@@ -1503,7 +1507,7 @@ 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, SuperBinding::Needed);
+            property = parseGetterSetter(context, alwaysStrictInsideClass, isGetter ? PropertyNode::Getter : PropertyNode::Setter, methodStart, constructorKind, SuperBinding::Needed);
             failIfFalse(property, "Cannot parse this method");
         } else {
             ParserFunctionInfo<TreeBuilder> methodInfo;
@@ -2018,7 +2022,8 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePropertyMeth
 }
 
 template <typename LexerType>
-template <class TreeBuilder> TreeProperty Parser<LexerType>::parseGetterSetter(TreeBuilder& context, bool strict, PropertyNode::Type type, unsigned getterOrSetterStartOffset, SuperBinding superBinding)
+template <class TreeBuilder> TreeProperty Parser<LexerType>::parseGetterSetter(TreeBuilder& context, bool strict, PropertyNode::Type type, unsigned getterOrSetterStartOffset,
+    ConstructorKind constructorKind, SuperBinding superBinding)
 {
     const Identifier* stringPropertyName = 0;
     double numericPropertyName = 0;
@@ -2033,10 +2038,10 @@ 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, ConstructorKind::Base, info)), "Cannot parse getter definition");
+        failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, GetterMode, false, constructorKind, info)), "Cannot parse getter definition");
     } else {
         failIfFalse(match(OPENPAREN), "Expected a parameter list for setter definition");
-        failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, SetterMode, false, ConstructorKind::Base, info)), "Cannot parse setter definition");
+        failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, SetterMode, false, constructorKind, info)), "Cannot parse setter definition");
     }
     if (stringPropertyName)
         return context.createGetterOrSetterProperty(location, type, strict, stringPropertyName, info, getterOrSetterStartOffset, superBinding);
@@ -2384,6 +2389,7 @@ template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres
     if (baseIsSuper) {
         base = context.superExpr(location);
         next();
+        currentScope()->setNeedsSuperBinding();
     } else
         base = parsePrimaryExpression(context);
 
index a1cb295..7a8f4b0 100644 (file)
@@ -114,6 +114,7 @@ struct Scope {
         , m_usesEval(false)
         , m_needsFullActivation(false)
         , m_hasDirectSuper(false)
+        , m_needsSuperBinding(false)
         , m_allowsNewDecls(true)
         , m_strictMode(strictMode)
         , m_isFunction(isFunction)
@@ -130,6 +131,7 @@ struct Scope {
         , m_usesEval(rhs.m_usesEval)
         , m_needsFullActivation(rhs.m_needsFullActivation)
         , m_hasDirectSuper(rhs.m_hasDirectSuper)
+        , m_needsSuperBinding(rhs.m_needsSuperBinding)
         , m_allowsNewDecls(rhs.m_allowsNewDecls)
         , m_strictMode(rhs.m_strictMode)
         , m_isFunction(rhs.m_isFunction)
@@ -272,6 +274,13 @@ struct Scope {
 #endif
     void setHasDirectSuper() { m_hasDirectSuper = true; }
 
+#if ENABLE(ES6_CLASS_SYNTAX)
+    bool needsSuperBinding() { return m_needsSuperBinding; }
+#else
+    bool needsSuperBinding() { return false; }
+#endif
+    void setNeedsSuperBinding() { m_needsSuperBinding = true; }
+
     bool collectFreeVariables(Scope* nestedScope, bool shouldTrackClosedVariables)
     {
         if (nestedScope->m_usesEval)
@@ -366,6 +375,7 @@ private:
     bool m_usesEval : 1;
     bool m_needsFullActivation : 1;
     bool m_hasDirectSuper : 1;
+    bool m_needsSuperBinding : 1;
     bool m_allowsNewDecls : 1;
     bool m_strictMode : 1;
     bool m_isFunction : 1;
@@ -748,7 +758,7 @@ 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, SuperBinding = SuperBinding::NotNeeded);
+    template <class TreeBuilder> TreeProperty parseGetterSetter(TreeBuilder&, bool strict, PropertyNode::Type, unsigned getterOrSetterStartOffset, ConstructorKind = ConstructorKind::Base, SuperBinding = SuperBinding::NotNeeded);
     template <class TreeBuilder> ALWAYS_INLINE TreeFunctionBody parseFunctionBody(TreeBuilder&, ConstructorKind);
     template <class TreeBuilder> ALWAYS_INLINE TreeFormalParameterList parseFormalParameters(TreeBuilder&);
     enum VarDeclarationListContext { ForLoopContext, VarDeclarationContext };