Bug 1293205 - Part 1: Warn about non-standard for-each regardless of JS version number. r=evilpie
authorTooru Fujisawa <arai_a@mac.com>
Fri, 02 Sep 2016 04:16:16 +0900
changeset 312403 4ab9576d4c6280d6f7dbbc75ea100d133ace1aad
parent 312402 586730aa152198ece8f016f3098ad2fbf055090b
child 312404 3613d167d065e344e81fa6aab23492f71dd996d1
push id20447
push userkwierso@gmail.com
push dateFri, 02 Sep 2016 20:36:44 +0000
treeherderfx-team@969397f22187 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersevilpie
bugs1293205
milestone51.0a1
Bug 1293205 - Part 1: Warn about non-standard for-each regardless of JS version number. r=evilpie
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/jit-test/tests/parser/for-each-warn.js
js/src/jscompartment.cpp
js/src/jscompartment.h
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -5215,20 +5215,18 @@ Parser<ParseHandler>::forStatement(Yield
     if (allowsForEachIn()) {
         bool matched;
         if (!tokenStream.matchContextualKeyword(&matched, context->names().each))
             return null();
         if (matched) {
             iflags = JSITER_FOREACH;
             isForEach = true;
             addTelemetry(JSCompartment::DeprecatedForEach);
-            if (versionNumber() < JSVERSION_LATEST) {
-                if (!report(ParseWarning, pc->sc()->strict(), null(), JSMSG_DEPRECATED_FOR_EACH))
-                    return null();
-            }
+            if (!warnOnceAboutForEach())
+                return null();
         }
     }
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
 
     // PNK_FORHEAD, PNK_FORIN, or PNK_FOROF depending on the loop type.
     ParseNodeKind headKind;
 
@@ -9027,13 +9025,29 @@ Parser<ParseHandler>::warnOnceAboutExprC
         if (!report(ParseWarning, false, null(), JSMSG_DEPRECATED_EXPR_CLOSURE))
             return false;
         cx->compartment()->warnedAboutExprClosure = true;
     }
 #endif
     return true;
 }
 
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::warnOnceAboutForEach()
+{
+    JSContext* cx = context->maybeJSContext();
+    if (!cx)
+        return true;
+
+    if (!cx->compartment()->warnedAboutForEach) {
+        if (!report(ParseWarning, false, null(), JSMSG_DEPRECATED_FOR_EACH))
+            return false;
+        cx->compartment()->warnedAboutForEach = true;
+    }
+    return true;
+}
+
 template class Parser<FullParseHandler>;
 template class Parser<SyntaxParseHandler>;
 
 } /* namespace frontend */
 } /* namespace js */
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -1335,16 +1335,17 @@ class Parser final : private JS::AutoGCR
 
     TokenPos pos() const { return tokenStream.currentToken().pos; }
 
     bool asmJS(Node list);
 
     void addTelemetry(JSCompartment::DeprecatedLanguageExtension e);
 
     bool warnOnceAboutExprClosure();
+    bool warnOnceAboutForEach();
 };
 
 } /* namespace frontend */
 } /* namespace js */
 
 /*
  * Convenience macro to access Parser.tokenStream as a pointer.
  */
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parser/for-each-warn.js
@@ -0,0 +1,36 @@
+// for-each should be warned once and only once.
+
+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();
+}
+
+testWarn("for each (var x in {}) {}");
+testWarn("for each (x in {}) {}");
+testWarn("for each (let y in {}) {}");
+testWarn("for each (const y in {}) {}");
+testWarn("for each ([x, y] in {}) {}");
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -48,16 +48,17 @@ JSCompartment::JSCompartment(Zone* zone,
     behaviors_(options.behaviors()),
     zone_(zone),
     runtime_(zone->runtimeFromMainThread()),
     principals_(nullptr),
     isSystem_(false),
     isSelfHosting(false),
     marked(true),
     warnedAboutExprClosure(false),
+    warnedAboutForEach(false),
 #ifdef DEBUG
     firedOnNewGlobalObject(false),
 #endif
     global_(nullptr),
     enterCompartmentDepth(0),
     performanceMonitoring(runtime_),
     data(nullptr),
     allocationMetadataBuilder(nullptr),
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -333,16 +333,17 @@ struct JSCompartment
     }
   private:
     JSPrincipals*                principals_;
     bool                         isSystem_;
   public:
     bool                         isSelfHosting;
     bool                         marked;
     bool                         warnedAboutExprClosure;
+    bool                         warnedAboutForEach;
 
 #ifdef DEBUG
     bool                         firedOnNewGlobalObject;
 #endif
 
     void mark() { marked = true; }
 
   private: