Skip to content

Commit

Permalink
[[FIX]] Restrict super usage to valid forms
Browse files Browse the repository at this point in the history
Previously, the `super` keyword was parsed as an identifier and thus
allowed in a large number of invalid positions. Ensure that the keyword
is only accepted as part of a SuperCall or SuperProperty and only within
appropriate method contexts.
  • Loading branch information
jugglinmike authored and rwaldron committed Jan 9, 2018
1 parent b420aed commit 8f3f880
Show file tree
Hide file tree
Showing 4 changed files with 246 additions and 23 deletions.
66 changes: 61 additions & 5 deletions src/jshint.js
Original file line number Diff line number Diff line change
Expand Up @@ -2970,7 +2970,8 @@ var JSHINT = (function() {
* the body of member functions.
*/
function doFunction(options) {
var f, token, name, statement, classExprBinding, isGenerator, isArrow, ignoreLoopFunc;
var f, token, name, statement, classExprBinding, isGenerator, isArrow,
isMethod, ignoreLoopFunc;
var oldOption = state.option;
var oldIgnored = state.ignored;

Expand All @@ -2980,6 +2981,7 @@ var JSHINT = (function() {
classExprBinding = options.classExprBinding;
isGenerator = options.type === "generator";
isArrow = options.type === "arrow";
isMethod = options.isMethod;
ignoreLoopFunc = options.ignoreLoopFunc;
}

Expand All @@ -2990,6 +2992,7 @@ var JSHINT = (function() {
"(statement)": statement,
"(context)": state.funct,
"(arrow)": isArrow,
"(method)": isMethod,
"(generator)": isGenerator
});

Expand Down Expand Up @@ -3236,7 +3239,7 @@ var JSHINT = (function() {
}

t = state.tokens.next;
f = doFunction();
f = doFunction({ isMethod: true });
p = f["(params)"];

// Don't warn about getter/setter pairs if this is an ES6 concise method
Expand Down Expand Up @@ -3273,7 +3276,10 @@ var JSHINT = (function() {
if (!state.inES6()) {
warning("W104", state.tokens.curr, "concise methods", "6");
}
doFunction({ type: isGeneratorMethod ? "generator" : null });
doFunction({
isMethod: true,
type: isGeneratorMethod ? "generator" : null
});
} else {
advance(":");
expression(10);
Expand Down Expand Up @@ -3801,7 +3807,10 @@ var JSHINT = (function() {
advance();
}
if (state.tokens.next.value !== "(") {
doFunction({ statement: c });
doFunction({
isMethod: true,
statement: c
});
}
}

Expand Down Expand Up @@ -3831,6 +3840,7 @@ var JSHINT = (function() {

doFunction({
statement: c,
isMethod: true,
type: isGenerator ? "generator" : null,
classExprBinding: c.namedExpr ? c.name : null
});
Expand Down Expand Up @@ -4813,6 +4823,52 @@ var JSHINT = (function() {
return this;
}).exps = true;

/**
* Determine if SuperCall or SuperProperty may be used in the current context
* (as described by the provided "functor" object).
*
* @param {string} type - one of "property" or "call"
* @param {object} funct - a "functor" object describing the current function
* context
*
* @returns {boolean}
*/
function supportsSuper(type, funct) {
if (type === "property" && funct["(method)"]) {
return true;
}

if (type === "call" && funct["(statement)"] &&
funct["(statement)"].id === "class") {
return true;
}

if (funct["(arrow)"]) {
return supportsSuper(type, funct["(context)"]);
}

return false;
}

var superNud = function(context) {
var next = state.tokens.next;
var type = null;

if (checkPunctuators(next, ["[", "."])) {
if (!supportsSuper("property", state.funct)) {
error("E063", this);
}
} else if (checkPunctuator(next, "(")) {
if (!supportsSuper("call", state.funct)) {
error("E064", this);
}
} else {
error("E024", next, next.value || next.id);
}

return this;
};

// Future Reserved Words

FutureReservedWord("abstract");
Expand Down Expand Up @@ -4840,7 +4896,7 @@ var JSHINT = (function() {
FutureReservedWord("public", { es5: true, strictOnly: true });
FutureReservedWord("short");
FutureReservedWord("static", { es5: true, strictOnly: true });
FutureReservedWord("super", { es5: true });
FutureReservedWord("super", { es5: true, nud: superNud });
FutureReservedWord("synchronized");
FutureReservedWord("transient");
FutureReservedWord("volatile");
Expand Down
4 changes: 3 additions & 1 deletion src/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ var errors = {
E059: "Incompatible values for the '{a}' and '{b}' linting options.",
E060: "Non-callable values cannot be used as the second operand to instanceof.",
E061: "Invalid position for 'yield' expression (consider wrapping in parenthesis).",
E062: "Rest parameter does not a support default value."
E062: "Rest parameter does not a support default value.",
E063: "Super property may only be used within method bodies.",
E064: "Super call may only be used within class method bodies."
};

var warnings = {
Expand Down
17 changes: 0 additions & 17 deletions tests/test262/expectations.txt
Original file line number Diff line number Diff line change
Expand Up @@ -271,10 +271,6 @@ language/asi/S7.9_A7_T9.js
language/global-code/export.js
language/global-code/import.js
language/global-code/return.js
language/global-code/super-call-arrow.js
language/global-code/super-call.js
language/global-code/super-prop-arrow.js
language/global-code/super-prop.js
language/global-code/yield-non-strict.js
language/line-terminators/S7.3_A3.3_T1.js
language/line-terminators/S7.3_A3.4_T1.js
Expand Down Expand Up @@ -396,7 +392,6 @@ language/module-code/early-dup-export-id-as.js
language/module-code/early-dup-export-id.js
language/module-code/early-export-global.js
language/module-code/early-export-unresolvable.js
language/module-code/early-super.js
language/module-code/instn-named-id-name.js
language/module-code/instn-resolve-empty-import.js
language/module-code/instn-resolve-err-reference.js
Expand Down Expand Up @@ -480,10 +475,6 @@ language/expressions/function/dstr-dflt-ary-ptrn-rest-ary-empty.js
language/expressions/function/dstr-dflt-ary-ptrn-rest-init-id.js
language/expressions/function/dstr-dflt-ary-ptrn-rest-obj-id.js
language/expressions/function/dstr-dflt-ary-ptrn-rest-obj-prop-id.js
language/expressions/function/early-body-super-call.js
language/expressions/function/early-body-super-prop.js
language/expressions/function/early-params-super-call.js
language/expressions/function/early-params-super-prop.js
language/expressions/function/param-dflt-yield-non-strict.js
language/expressions/function/params-dflt-args-unmapped.js
language/expressions/function/scope-param-rest-elem-var-close.js
Expand Down Expand Up @@ -670,15 +661,11 @@ language/expressions/object/method-definition/early-errors-object-method-NSPL-wi
language/expressions/object/method-definition/generator-param-init-yield.js
language/expressions/object/method-definition/generator-prop-name-yield-expr.js
language/expressions/object/method-definition/generator-prop-name-yield-id.js
language/expressions/object/method-definition/generator-super-call-body.js
language/expressions/object/method-definition/generator-super-call-param.js
language/expressions/object/method-definition/generator-use-strict-with-non-simple-param.js
language/expressions/object/method-definition/name-param-id-yield.js
language/expressions/object/method-definition/name-param-init-yield.js
language/expressions/object/method-definition/name-prop-name-yield-expr.js
language/expressions/object/method-definition/name-prop-name-yield-id.js
language/expressions/object/method-definition/name-super-call-body.js
language/expressions/object/method-definition/name-super-call-param.js
language/expressions/object/method-definition/object-method-returns-promise.js
language/expressions/object/method-definition/params-dflt-gen-meth-args-unmapped.js
language/expressions/object/method-definition/params-dflt-meth-args-unmapped.js
Expand Down Expand Up @@ -903,10 +890,6 @@ language/statements/function/dstr-dflt-ary-ptrn-rest-ary-rest.js
language/statements/function/dstr-dflt-ary-ptrn-rest-init-id.js
language/statements/function/dstr-dflt-ary-ptrn-rest-obj-id.js
language/statements/function/dstr-dflt-ary-ptrn-rest-obj-prop-id.js
language/statements/function/early-body-super-call.js
language/statements/function/early-body-super-prop.js
language/statements/function/early-params-super-call.js
language/statements/function/early-params-super-prop.js
language/statements/function/param-dflt-yield-non-strict.js
language/statements/function/params-dflt-args-unmapped.js
language/statements/function/scope-param-rest-elem-var-close.js
Expand Down
182 changes: 182 additions & 0 deletions tests/unit/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -6251,6 +6251,188 @@ exports.classExpression = function (test) {
test.done();
};

exports.super = {};

exports.super.invalid = function (test) {
TestRun(test, "as identifier")
.addError(3, 10, "Unexpected ';'.")
.addError(3, 5, "Expected an assignment or function call and instead saw an expression.")
.test([
"void {",
" m() {",
" super;",
" }",
"};"
], { esversion: 6 });

test.done();
};

exports.super.superProperty = function (test) {
TestRun(test, "bracket notation")
.addError(3, 14, "Expected an assignment or function call and instead saw an expression.")
.addError(4, 20, "['ab'] is better written in dot notation.")
.test([
"void {",
" m() {",
" super['4'];",
" void super['a' + 'b'];",
" }",
"};"
], { esversion: 6 });

TestRun(test, "dot operator")
.addError(3, 11, "Expected an assignment or function call and instead saw an expression.")
.addError(4, 26, "'hasOwnProperty' is a really bad name.")
.test([
"void {",
" m() {",
" super.x;",
" super.hasOwnProperty = 0;",
" }",
"};"
], { esversion: 6 });

TestRun(test, "within arrow functions")
.test([
"void {",
" m() {",
" void (() => {",
" void (() => {",
" void super.x;",
" });",
" });",
" },",
" *g() {",
" void (() => {",
" void (() => {",
" void super.x;",
" });",
" });",
" yield;",
" }",
"};",
"class C {",
" m() {",
" void (() => {",
" void (() => {",
" void super.x;",
" });",
" });",
" }",
" static m() {",
" void (() => {",
" void (() => {",
" void super.x;",
" });",
" });",
" }",
"}"
], { esversion: 6 });

TestRun(test, "outside of method")
.addError(5, 14, "Super property may only be used within method bodies.")
.addError(6, 14, "Super property may only be used within method bodies.")
.addError(12, 8, "Super property may only be used within method bodies.")
.addError(13, 8, "Super property may only be used within method bodies.")
.addError(15, 6, "Super property may only be used within method bodies.")
.addError(16, 6, "Super property may only be used within method bodies.")
.test([
"void {",
" m() {",
" function f() {",
" void (() => {",
" void super.x;",
" void super[x];",
" });",
" }",
" }",
"};",
"function f() {",
" void super.x;",
" void super[x];",
"}",
"void super.x;",
"void super[x];"
], { esversion: 6 });

test.done();
};

exports.super.superCall = function (test) {
TestRun(test)
.test([
"class C {",
" m() {",
" super();",
" super(1);",
" super(...x);",
" }",
"}"
], { esversion: 6 });

TestRun(test, "within arrow functions")
.test([
"class C {",
" m() {",
" void (() => {",
" void (() => {",
" super();",
" });",
" });",
" }",
" *g() {",
" void (() => {",
" void (() => {",
" super();",
" });",
" });",
" yield;",
" }",
" static m() {",
" void (() => {",
" void (() => {",
" super();",
" });",
" });",
" }",
"}"
], { esversion: 6 });

TestRun(test, "outside of class method")
.addError(5, 9, "Super call may only be used within class method bodies.")
.addError(14, 9, "Super call may only be used within class method bodies.")
.addError(20, 3, "Super call may only be used within class method bodies.")
.addError(22, 1, "Super call may only be used within class method bodies.")
.test([
"class C {",
" m() {",
" function f() {",
" void (() => {",
" super();",
" });",
" }",
" }",
"}",
"void {",
" m() {",
" function f() {",
" void (() => {",
" super();",
" });",
" }",
" }",
"};",
"function f() {",
" super();",
"}",
"super();"
], { esversion: 6 });

test.done();
};


exports.functionReassignment = function (test) {
var src = [
"function f() {}",
Expand Down

0 comments on commit 8f3f880

Please sign in to comment.