Bug 1208835 - Add telemetry for RegExp static property access after String.prototype.replace with function argument and RegExp static property is changed inside it. r=till,bsmedberg
authorTooru Fujisawa <arai_a@mac.com>
Tue, 29 Sep 2015 16:59:10 +0900
changeset 303924 644ac87b6f7472f14b34bc277af219ab9d28f1e3
parent 303923 01cdd3e5bd7153dfdc47d865538e15e4754dc0cf
child 303925 0ee21e8d5ca60bae3930bc126040b2c4aed2d3cb
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill, bsmedberg
bugs1208835
milestone44.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1208835 - Add telemetry for RegExp static property access after String.prototype.replace with function argument and RegExp static property is changed inside it. r=till,bsmedberg
js/src/builtin/RegExp.cpp
js/src/jscompartment.h
js/src/vm/RegExpStatics.cpp
js/src/vm/RegExpStatics.h
toolkit/components/telemetry/Histograms.json
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -596,16 +596,18 @@ const JSFunctionSpec js::regexp_methods[
 #define DEFINE_STATIC_GETTER(name, code)                                        \
     static bool                                                                 \
     name(JSContext* cx, unsigned argc, Value* vp)                               \
     {                                                                           \
         CallArgs args = CallArgsFromVp(argc, vp);                               \
         RegExpStatics* res = cx->global()->getRegExpStatics(cx);                \
         if (!res)                                                               \
             return false;                                                       \
+        if (!res->checkRestoredFromModifiedMatch(cx))                           \
+            return false;                                                       \
         code;                                                                   \
     }
 
 DEFINE_STATIC_GETTER(static_input_getter,        return res->createPendingInput(cx, args.rval()))
 DEFINE_STATIC_GETTER(static_multiline_getter,    args.rval().setBoolean(res->multiline());
                                                  return true)
 DEFINE_STATIC_GETTER(static_lastMatch_getter,    return res->createLastMatch(cx, args.rval()))
 DEFINE_STATIC_GETTER(static_lastParen_getter,    return res->createLastParen(cx, args.rval()))
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -749,16 +749,17 @@ struct JSCompartment
         // NO LONGER USING 1
         DeprecatedLegacyGenerator = 2,      // JS 1.7+
         DeprecatedExpressionClosure = 3,    // Added in JS 1.8
         DeprecatedLetBlock = 4,             // Added in JS 1.7
         // NO LONGER USING 5
         DeprecatedNoSuchMethod = 6,         // JS 1.7+
         DeprecatedFlagsArgument = 7,        // JS 1.3 or older
         // NO LONGER USING 8
+        DeprecatedRestoredRegExpStatics = 9,// Unknown
         DeprecatedLanguageExtensionCount
     };
 
     js::ArgumentsObject* getOrCreateArgumentsTemplateObject(JSContext* cx, bool mapped);
 
   private:
     // Used for collecting telemetry on SpiderMonkey's deprecated language extensions.
     bool sawDeprecatedLanguageExtension[DeprecatedLanguageExtensionCount];
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -113,8 +113,20 @@ RegExpStatics::executeLazy(JSContext* cx
 
     /* Unset lazy state and remove rooted values that now have no use. */
     pendingLazyEvaluation = false;
     lazySource = nullptr;
     lazyIndex = size_t(-1);
 
     return true;
 }
+
+bool
+RegExpStatics::checkRestoredFromModifiedMatch(JSContext* cx)
+{
+    if (isRestoredFromModifiedMatch) {
+        if (JSScript* script = cx->currentScript()) {
+            const char* filename = script->filename();
+            cx->compartment()->addTelemetry(filename, JSCompartment::DeprecatedRestoredRegExpStatics);
+        }
+    }
+    return true;
+}
--- a/js/src/vm/RegExpStatics.h
+++ b/js/src/vm/RegExpStatics.h
@@ -41,18 +41,35 @@ class RegExpStatics
      * to replay the last executed RegExp, and |matches| is invalid.
      */
     int32_t                 pendingLazyEvaluation;
 
     /* Linkage for preserving RegExpStatics during nested RegExp execution. */
     RegExpStatics*          bufferLink;
     bool                    copied;
 
+    /* ID for each match result, incrementing on each update. */
+    size_t matchId;
+
+    /*
+     * Match result is restored from differnt ID in String.prototype.replace
+     * with function argument.  While this value is true, the value of each
+     * RegExp static properties are different from other engines.  We are going
+     * to remove PreserveRegExpStatics and make those properties  match to
+     * other engines. (See bug 1208835)
+     */
+    bool isRestoredFromModifiedMatch;
+
   public:
-    RegExpStatics() : bufferLink(nullptr), copied(false) { clear(); }
+    RegExpStatics()
+      : bufferLink(nullptr), copied(false),
+        matchId(0), isRestoredFromModifiedMatch(false)
+    {
+        clear();
+    }
     static RegExpStaticsObject* create(ExclusiveContext* cx, Handle<GlobalObject*> parent);
 
   private:
     bool executeLazy(JSContext* cx);
 
     inline void aboutToWrite();
     inline void copyTo(RegExpStatics& dst);
 
@@ -75,17 +92,20 @@ class RegExpStatics
      * store the match string in |*out|; otherwise place |undefined| in |*out|.
      */
     bool makeMatch(JSContext* cx, size_t pairNum, MutableHandleValue out);
     bool createDependent(JSContext* cx, size_t start, size_t end, MutableHandleValue out);
 
     void markFlagsSet(JSContext* cx);
 
     struct InitBuffer {};
-    explicit RegExpStatics(InitBuffer) : bufferLink(nullptr), copied(false) {}
+    explicit RegExpStatics(InitBuffer)
+      : bufferLink(nullptr), copied(false),
+        matchId(0), isRestoredFromModifiedMatch(false)
+    {}
 
     friend class PreserveRegExpStatics;
     friend class AutoRegExpStaticsBuffer;
 
   public:
     /* Mutators. */
     inline void updateLazily(JSContext* cx, JSLinearString* input,
                              RegExpShared* shared, size_t lastIndex);
@@ -117,16 +137,18 @@ class RegExpStatics
   public:
     /* Default match accessor. */
     const MatchPairs& getMatches() const {
         /* Safe: only used by String methods, which do not set lazy mode. */
         MOZ_ASSERT(!pendingLazyEvaluation);
         return matches;
     }
 
+    bool checkRestoredFromModifiedMatch(JSContext* cx);
+
     JSString* getPendingInput() const { return pendingInput; }
 
     RegExpFlag getFlags() const { return flags; }
     bool multiline() const { return flags & MultilineFlag; }
 
     void mark(JSTracer* trc) {
         /*
          * Changes to this function must also be reflected in
@@ -217,28 +239,35 @@ class MOZ_RAII AutoRegExpStaticsBuffer :
     RegExpStatics statics;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 class PreserveRegExpStatics
 {
     RegExpStatics * const original;
     AutoRegExpStaticsBuffer buffer;
+    size_t beforeId;
 
   public:
     explicit PreserveRegExpStatics(JSContext* cx, RegExpStatics* original)
      : original(original),
-       buffer(cx)
+       buffer(cx),
+       beforeId(original->matchId)
     {}
 
     bool init(JSContext* cx) {
         return original->save(cx, &buffer.getStatics());
     }
 
-    ~PreserveRegExpStatics() { original->restore(); }
+    ~PreserveRegExpStatics() {
+        if (original->matchId != beforeId)
+            original->isRestoredFromModifiedMatch = true;
+
+        original->restore();
+    }
 };
 
 inline bool
 RegExpStatics::createDependent(JSContext* cx, size_t start, size_t end, MutableHandleValue out)
 {
     /* Private function: caller must perform lazy evaluation. */
     MOZ_ASSERT(!pendingLazyEvaluation);
 
@@ -463,28 +492,32 @@ RegExpStatics::updateLazily(JSContext* c
     BarrieredSetPair<JSString, JSLinearString>(cx->zone(),
                                                pendingInput, input,
                                                matchesInput, input);
 
     lazySource = shared->source;
     lazyFlags = shared->flags;
     lazyIndex = lastIndex;
     pendingLazyEvaluation = 1;
+    matchId++;
+    isRestoredFromModifiedMatch = false;
 }
 
 inline bool
 RegExpStatics::updateFromMatchPairs(JSContext* cx, JSLinearString* input, MatchPairs& newPairs)
 {
     MOZ_ASSERT(input);
     aboutToWrite();
 
     /* Unset all lazy state. */
     pendingLazyEvaluation = false;
     this->lazySource = nullptr;
     this->lazyIndex = size_t(-1);
+    matchId++;
+    isRestoredFromModifiedMatch = false;
 
     BarrieredSetPair<JSString, JSLinearString>(cx->zone(),
                                                pendingInput, input,
                                                matchesInput, input);
 
     if (!matches.initArrayFrom(newPairs)) {
         ReportOutOfMemory(cx);
         return false;
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -432,24 +432,24 @@
     "kind": "boolean",
     "description": "Geolocation on OS X is either MLS or CoreLocation"
   },
   "JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT": {
     "alert_emails": ["jdemooij@mozilla.com"],
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 10,
-    "description": "Use of SpiderMonkey's deprecated language extensions in web content: ForEach=0, DestructuringForIn=1, LegacyGenerator=2, ExpressionClosure=3, LetBlock=4, LetExpression=5, NoSuchMethod=6, FlagsArgument=7, RegExpSourceProp=8"
+    "description": "Use of SpiderMonkey's deprecated language extensions in web content: ForEach=0, DestructuringForIn=1, LegacyGenerator=2, ExpressionClosure=3, LetBlock=4, LetExpression=5, NoSuchMethod=6, FlagsArgument=7, RegExpSourceProp=8, RestoredRegExpStatics=9"
   },
   "JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS": {
     "alert_emails": ["jdemooij@mozilla.com"],
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 10,
-    "description": "Use of SpiderMonkey's deprecated language extensions in add-ons: ForEach=0, DestructuringForIn=1, LegacyGenerator=2, ExpressionClosure=3, LetBlock=4, LetExpression=5, NoSuchMethod=6, FlagsArgument=7, RegExpSourceProp=8"
+    "description": "Use of SpiderMonkey's deprecated language extensions in add-ons: ForEach=0, DestructuringForIn=1, LegacyGenerator=2, ExpressionClosure=3, LetBlock=4, LetExpression=5, NoSuchMethod=6, FlagsArgument=7, RegExpSourceProp=8, RestoredRegExpStatics=9"
   },
   "XUL_CACHE_DISABLED": {
     "expires_in_version": "default",
     "kind": "flag",
     "description": "XUL cache was disabled"
   },
   "MEMORY_RESIDENT": {
     "alert_emails": ["memshrink-telemetry-alerts@mozilla.com"],