Bug 1083476 - Add console warnings for JS1.7 legacy generators. r=arai
authorMasatoshi Kimura <VYV03354@nifty.ne.jp>
Sun, 06 Aug 2017 18:48:39 +0900
changeset 642225 3f4d9d490af28c3bf7e69b936ecf728f50fbeaca
parent 642224 1ef6760807b1c5100c7e8b88065bf578842eb17f
child 642226 bf5ee532b95d53ce36c3f58ceb8e19a55de86319
push id72685
push userbmo:emilio+bugs@crisal.io
push dateMon, 07 Aug 2017 22:41:31 +0000
reviewersarai
bugs1083476
milestone57.0a1
Bug 1083476 - Add console warnings for JS1.7 legacy generators. r=arai MozReview-Commit-ID: LnctQK4EEOM
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/jit-test/tests/parser/legacy-generator-warn.js
js/src/jit-test/tests/parser/yield-without-operand.js
js/src/js.msg
js/src/jscompartment.cpp
js/src/jscompartment.h
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -6795,16 +6795,18 @@ Parser<ParseHandler, CharT>::yieldExpres
         {
             /* As in Python (see PEP-255), disallow return v; in generators. */
             errorAt(begin, JSMSG_BAD_FUNCTION_YIELD);
             return null();
         }
 
         pc->functionBox()->setGeneratorKind(LegacyGenerator);
         addTelemetry(DeprecatedLanguageExtension::LegacyGenerator);
+        if (!warnOnceAboutLegacyGenerator())
+            return null();
 
         MOZ_FALLTHROUGH;
 
       case LegacyGenerator:
       {
         // We are in a legacy generator: a function that has already seen a
         // yield.
         MOZ_ASSERT(pc->isFunctionBox());
@@ -10436,13 +10438,27 @@ ParserBase::warnOnceAboutForEach()
     if (!context->compartment()->warnedAboutForEach) {
         if (!warning(JSMSG_DEPRECATED_FOR_EACH))
             return false;
         context->compartment()->warnedAboutForEach = true;
     }
     return true;
 }
 
+bool
+ParserBase::warnOnceAboutLegacyGenerator()
+{
+    if (context->helperThread())
+        return true;
+
+    if (!context->compartment()->warnedAboutLegacyGenerator) {
+        if (!warning(JSMSG_DEPRECATED_LEGACY_GENERATOR))
+            return false;
+        context->compartment()->warnedAboutLegacyGenerator = true;
+    }
+    return true;
+}
+
 template class Parser<FullParseHandler, char16_t>;
 template class Parser<SyntaxParseHandler, char16_t>;
 
 } /* namespace frontend */
 } /* namespace js */
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -246,16 +246,17 @@ class ParserBase : public StrictModeGett
     MOZ_MUST_USE bool extraWarningAt(uint32_t offset, unsigned errorNumber, ...);
 
     bool isValidStrictBinding(PropertyName* name);
 
     void addTelemetry(DeprecatedLanguageExtension e);
 
     bool warnOnceAboutExprClosure();
     bool warnOnceAboutForEach();
+    bool warnOnceAboutLegacyGenerator();
 
     bool allowsForEachIn() {
 #if !JS_HAS_FOR_EACH_IN
         return false;
 #else
         return options().forEachStatementOption && versionNumber() >= JSVERSION_1_6;
 #endif
     }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parser/legacy-generator-warn.js
@@ -0,0 +1,55 @@
+// Legacy generators should be warned once and only once.
+
+version(170);
+
+function testWarn(code) {
+  enableLastWarning();
+  var g = newGlobal();
+  g.code = code;
+  g.eval('eval(code)');
+  var warning = getLastWarning();
+  assertEq(warning !== null, true, "warning should be caught for " + code);
+  assertEq(warning.name, "Warning");
+
+  clearLastWarning();
+  g.eval('eval(code)');
+  warning = getLastWarning();
+  assertEq(warning, null, "warning should not be caught for 2nd ocurrence");
+
+  clearLastWarning();
+  g = newGlobal();
+  g.code = code;
+  g.eval('Reflect.parse(code);');
+  warning = getLastWarning();
+  assertEq(warning !== null, true, "warning should be caught for " + code);
+  assertEq(warning.name, "Warning");
+
+  clearLastWarning();
+  g.eval('Reflect.parse(code);');
+  warning = getLastWarning();
+  assertEq(warning, null, "warning should not be caught for 2nd ocurrence");
+  disableLastWarning();
+}
+
+function testPass(code) {
+  enableLastWarning();
+  var g = newGlobal();
+  g.code = code;
+  g.eval('eval(code)');
+  var warning = getLastWarning();
+  assertEq(warning, null, "warning should not be caught for " + code);
+
+  clearLastWarning();
+  g = newGlobal();
+  g.code = code;
+  g.eval('Reflect.parse(code);');
+  warning = getLastWarning();
+  assertEq(warning, null, "warning should not be caught for " + code);
+  disableLastWarning();
+}
+
+testWarn("(function() { yield; })");
+testWarn("function a() { yield; }");
+
+testPass("(function*() { yield; })");
+testPass("function* a() { yield; }");
deleted file mode 100644
--- a/js/src/jit-test/tests/parser/yield-without-operand.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// yield without an operand is fine and dandy.
-
-load(libdir + "asserts.js");
-
-assertNoWarning(() => Function("yield"), SyntaxError,
-                "yield followed by EOF is fine");
-assertNoWarning(() => Function("yield;"), SyntaxError,
-                "yield followed by semicolon is fine");
-assertNoWarning(() => Function("yield\n"), SyntaxError,
-                "yield followed by newline is fine");
-assertNoWarning(() => Function("yield\n  print('ok');"), SyntaxError,
-                "yield followed by newline and statement is fine");
-assertNoWarning(() => Function("yield\n  /x/;"), SyntaxError,
-                "yield followed by newline and regexp is fine");
-assertThrowsInstanceOf(() => Function("yield\n  /"), SyntaxError,
-                       "yield followed by newline and slash is fine");
-
-assertNoWarning(() => eval("(function () { yield; })"), SyntaxError,
-                "yield followed by semicolon in eval code is fine");
-assertNoWarning(() => eval("(function () { yield })"), SyntaxError,
-                "yield followed by } in eval code is fine");
-
-assertNoWarning(() => Function("yield 0;"),
-                "yield with an operand should be fine");
-assertNoWarning(() => Function("yield 0"),
-                "yield with an operand should be fine, even without a semicolon");
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -246,16 +246,17 @@ MSG_DEF(JSMSG_CURLY_BEFORE_FINALLY,    0
 MSG_DEF(JSMSG_CURLY_BEFORE_SWITCH,     0, JSEXN_SYNTAXERR, "missing { before switch body")
 MSG_DEF(JSMSG_CURLY_BEFORE_TRY,        0, JSEXN_SYNTAXERR, "missing { before try block")
 MSG_DEF(JSMSG_CURLY_IN_COMPOUND,       0, JSEXN_SYNTAXERR, "missing } in compound statement")
 MSG_DEF(JSMSG_DECLARATION_AFTER_EXPORT,0, JSEXN_SYNTAXERR, "missing declaration after 'export' keyword")
 MSG_DEF(JSMSG_DECLARATION_AFTER_IMPORT,0, JSEXN_SYNTAXERR, "missing declaration after 'import' keyword")
 MSG_DEF(JSMSG_DEPRECATED_DELETE_OPERAND, 0, JSEXN_SYNTAXERR, "applying the 'delete' operator to an unqualified name is deprecated")
 MSG_DEF(JSMSG_DEPRECATED_EXPR_CLOSURE, 0, JSEXN_WARN, "expression closures are deprecated")
 MSG_DEF(JSMSG_DEPRECATED_FOR_EACH,     0, JSEXN_WARN, "JavaScript 1.6's for-each-in loops are deprecated; consider using ES6 for-of instead")
+MSG_DEF(JSMSG_DEPRECATED_LEGACY_GENERATOR, 0, JSEXN_WARN, "JavaScript 1.7's legacy generators are deprecated; consider using ES6 generators instead. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function* for details.")
 MSG_DEF(JSMSG_DEPRECATED_OCTAL,        0, JSEXN_SYNTAXERR, "\"0\"-prefixed octal literals and octal escape sequences are deprecated; for octal literals use the \"0o\" prefix instead")
 MSG_DEF(JSMSG_DEPRECATED_PRAGMA,       1, JSEXN_WARN, "Using //@ to indicate {0} pragmas is deprecated. Use //# instead")
 MSG_DEF(JSMSG_DEPRECATED_BLOCK_SCOPE_FUN_REDECL, 1, JSEXN_WARN, "redeclaration of block-scoped function `{0}' is deprecated")
 MSG_DEF(JSMSG_DUPLICATE_EXPORT_NAME,   1, JSEXN_SYNTAXERR, "duplicate export name '{0}'")
 MSG_DEF(JSMSG_DUPLICATE_FORMAL,        1, JSEXN_SYNTAXERR, "duplicate formal argument {0}")
 MSG_DEF(JSMSG_DUPLICATE_LABEL,         0, JSEXN_SYNTAXERR, "duplicate label")
 MSG_DEF(JSMSG_DUPLICATE_PROPERTY,      1, JSEXN_SYNTAXERR, "property name {0} appears more than once in object literal")
 MSG_DEF(JSMSG_DUPLICATE_PROTO_PROPERTY, 0, JSEXN_SYNTAXERR, "property name __proto__ appears more than once in object literal")
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -54,16 +54,17 @@ JSCompartment::JSCompartment(Zone* zone,
     principals_(nullptr),
     isSystem_(false),
     isAtomsCompartment_(false),
     isSelfHosting(false),
     marked(true),
     warnedAboutDateToLocaleFormat(false),
     warnedAboutExprClosure(false),
     warnedAboutForEach(false),
+    warnedAboutLegacyGenerator(false),
     warnedAboutStringGenericsMethods(0),
 #ifdef DEBUG
     firedOnNewGlobalObject(false),
 #endif
     global_(nullptr),
     enterCompartmentDepth(0),
     performanceMonitoring(runtime_),
     data(nullptr),
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -616,19 +616,20 @@ struct JSCompartment
   private:
     JSPrincipals*                principals_;
     bool                         isSystem_;
     bool                         isAtomsCompartment_;
 
   public:
     bool                         isSelfHosting;
     bool                         marked;
-    bool                         warnedAboutDateToLocaleFormat;
-    bool                         warnedAboutExprClosure;
-    bool                         warnedAboutForEach;
+    bool                         warnedAboutDateToLocaleFormat : 1;
+    bool                         warnedAboutExprClosure : 1;
+    bool                         warnedAboutForEach : 1;
+    bool                         warnedAboutLegacyGenerator : 1;
     uint32_t                     warnedAboutStringGenericsMethods;
 
 #ifdef DEBUG
     bool                         firedOnNewGlobalObject;
 #endif
 
     void mark() { marked = true; }