From 120c76aee1d96242a6e67b7c63bee223d90fe910 Mon Sep 17 00:00:00 2001 From: tbreisacher Date: Mon, 16 Oct 2017 14:09:17 -0700 Subject: [PATCH] Disallow 'new.target' when not in a function. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=172375523 --- .../javascript/jscomp/parsing/IRFactory.java | 19 ++++++++++++--- .../javascript/jscomp/CodePrinterTest.java | 13 +++++++---- .../jscomp/Es6ToEs3ConverterTest.java | 1 + .../javascript/jscomp/parsing/ParserTest.java | 23 ++++++++++++------- 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/com/google/javascript/jscomp/parsing/IRFactory.java b/src/com/google/javascript/jscomp/parsing/IRFactory.java index 8b37a6b644f..054b31f21a2 100644 --- a/src/com/google/javascript/jscomp/parsing/IRFactory.java +++ b/src/com/google/javascript/jscomp/parsing/IRFactory.java @@ -222,11 +222,11 @@ class IRFactory { static final String UNEXPECTED_CONTINUE = "continue must be inside loop"; - static final String UNEXPECTED_LABLED_CONTINUE = + static final String UNEXPECTED_LABELLED_CONTINUE = "continue can only use labeles of iteration statements"; static final String UNEXPECTED_RETURN = "return must be inside function"; - + static final String UNEXPECTED_NEW_DOT_TARGET = "new.target must be inside a function"; static final String UNDEFINED_LABEL = "undefined label \"%s\""; private final String sourceString; @@ -433,6 +433,7 @@ private void validate(Node n) { validateParameters(n); validateBreakContinue(n); validateReturn(n); + validateNewDotTarget(n); validateLabel(n); } @@ -449,6 +450,18 @@ private void validateReturn(Node n) { } } + private void validateNewDotTarget(Node n) { + if (n.getToken() == Token.NEW_TARGET) { + Node parent = n; + while ((parent = parent.getParent()) != null) { + if (parent.isFunction()) { + return; + } + } + errorReporter.error(UNEXPECTED_NEW_DOT_TARGET, sourceName, n.getLineno(), n.getCharno()); + } + } + private void validateBreakContinue(Node n) { if (n.isBreak() || n.isContinue()) { Node labelName = n.getFirstChild(); @@ -469,7 +482,7 @@ private void validateBreakContinue(Node n) { if (n.isContinue() && !isContinueTarget(parent.getLastChild())) { // report invalid continue target errorReporter.error( - UNEXPECTED_LABLED_CONTINUE, + UNEXPECTED_LABELLED_CONTINUE, sourceName, n.getLineno(), n.getCharno()); } diff --git a/test/com/google/javascript/jscomp/CodePrinterTest.java b/test/com/google/javascript/jscomp/CodePrinterTest.java index 12c2fd765b8..7defda6ae04 100644 --- a/test/com/google/javascript/jscomp/CodePrinterTest.java +++ b/test/com/google/javascript/jscomp/CodePrinterTest.java @@ -2772,18 +2772,23 @@ public void testEs6ArrowFunctionSetsOriginalNameForArguments() { public void testEs6NewTargetBare() { languageMode = LanguageMode.ECMASCRIPT_2015; - assertPrintSame("new.target.prototype"); + assertPrintSame("class C{constructor(){new.target.prototype}}"); } public void testEs6NewTargetPrototype() { languageMode = LanguageMode.ECMASCRIPT_2015; - assertPrintSame("var callable=Object.setPrototypeOf(obj,new.target.prototype)"); + assertPrintSame( + "class C{constructor(){var callable=Object.setPrototypeOf(obj,new.target.prototype)}}"); } public void testEs6NewTargetConditional() { languageMode = LanguageMode.ECMASCRIPT_2015; - assertPrint("if (!new.target) throw 'Must be called with new!';", - "if(!new.target)throw\"Must be called with new!\";"); + assertPrint( + LINE_JOINER.join( + "function f() {", + " if (!new.target) throw 'Must be called with new!';", + "}"), + "function f(){if(!new.target)throw\"Must be called with new!\";}"); } public void testGoogScope() { diff --git a/test/com/google/javascript/jscomp/Es6ToEs3ConverterTest.java b/test/com/google/javascript/jscomp/Es6ToEs3ConverterTest.java index af24c05a28b..16e5b102592 100644 --- a/test/com/google/javascript/jscomp/Es6ToEs3ConverterTest.java +++ b/test/com/google/javascript/jscomp/Es6ToEs3ConverterTest.java @@ -257,6 +257,7 @@ public void testAnonymousSuper() { public void testNewTarget() { testError("function Foo() { new.target; }", CANNOT_CONVERT_YET); + testError("class Example { foo() { new.target; } }", CANNOT_CONVERT_YET); } public void testClassWithJsDoc() { diff --git a/test/com/google/javascript/jscomp/parsing/ParserTest.java b/test/com/google/javascript/jscomp/parsing/ParserTest.java index cad7ecfe4a5..fff0c7279bd 100644 --- a/test/com/google/javascript/jscomp/parsing/ParserTest.java +++ b/test/com/google/javascript/jscomp/parsing/ParserTest.java @@ -57,7 +57,7 @@ public final class ParserTest extends BaseJSTypeTestCase { private static final String UNEXPECTED_RETURN = "return must be inside function"; - private static final String UNEXPECTED_LABELED_CONTINUE = + private static final String UNEXPECTED_LABELLED_CONTINUE = "continue can only use labeles of iteration statements"; private static final String UNDEFINED_LABEL = "undefined label"; @@ -220,7 +220,7 @@ public void testContinueToSwitchWithDefault() { public void testContinueToLabelSwitch() { parseError( "while(1) {a: switch(1) {case(1): continue a; }}", - UNEXPECTED_LABELED_CONTINUE); + UNEXPECTED_LABELLED_CONTINUE); } public void testContinueOutsideSwitch() { @@ -3058,20 +3058,27 @@ public void testNewTarget() { mode = LanguageMode.ECMASCRIPT6; strictMode = SLOPPY; - // TODO(bradfordcsmith): new.target in global scope should be a syntax error - parse("new.target;"); + parseError("new.target;", "new.target must be inside a function"); parse("function f() { new.target; };"); - mode = LanguageMode.ECMASCRIPT5; + mode = LanguageMode.ECMASCRIPT3; parseWarning( - "function f() { new.target; }", + "class C { f() { new.target; } }", + getRequiresEs6Message(Feature.CLASSES), + getRequiresEs6Message(Feature.MEMBER_DECLARATIONS), getRequiresEs6Message(Feature.NEW_TARGET)); - mode = LanguageMode.ECMASCRIPT3; + mode = LanguageMode.ECMASCRIPT5; parseWarning( - "function f() { new.target; }", + "class C { f() { new.target; } }", + getRequiresEs6Message(Feature.CLASSES), + getRequiresEs6Message(Feature.MEMBER_DECLARATIONS), getRequiresEs6Message(Feature.NEW_TARGET)); + + mode = LanguageMode.ECMASCRIPT6; + expectFeatures(Feature.CLASSES, Feature.MEMBER_DECLARATIONS, Feature.NEW_TARGET); + parse("class C { f() { new.target; } }"); } public void testNewDotSomethingInvalid() {