Bug 1383343 part 1 - Avoid StringBuffer overhead in FunctionToString in the common case. r=anba
authorJan de Mooij <jdemooij@mozilla.com>
Tue, 25 Jul 2017 14:55:06 +0200
changeset 421983 3d0a9de378ccae2e704832e6528267e090e4c071
parent 421982 4b4a6f82e3e8e9b4ecee284666eeb397f31aa8fc
child 421984 80bb069a8fd565c147403f94b0e583954f70abc1
push id1517
push userjlorenzo@mozilla.com
push dateThu, 14 Sep 2017 16:50:54 +0000
treeherdermozilla-release@3b41fd564418 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersanba
bugs1383343
milestone56.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 1383343 part 1 - Avoid StringBuffer overhead in FunctionToString in the common case. r=anba
js/src/jsfun.cpp
js/src/jsscript.cpp
js/src/jsscript.h
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1005,30 +1005,22 @@ js::FunctionToString(JSContext* cx, Hand
         RootedFunction unwrapped(cx, GetUnwrappedAsyncFunction(fun));
         return FunctionToString(cx, unwrapped, isToSource);
     }
     if (IsWrappedAsyncGenerator(fun)) {
         RootedFunction unwrapped(cx, GetUnwrappedAsyncGenerator(fun));
         return FunctionToString(cx, unwrapped, isToSource);
     }
 
-    StringBuffer out(cx);
     RootedScript script(cx);
 
     if (fun->hasScript()) {
         script = fun->nonLazyScript();
-        if (script->isGeneratorExp()) {
-            if (!out.append("function genexp() {") ||
-                !out.append("\n    [generator expression]\n") ||
-                !out.append("}"))
-            {
-                return nullptr;
-            }
-            return out.finishString();
-        }
+        if (MOZ_UNLIKELY(script->isGeneratorExp()))
+            return NewStringCopyZ<CanGC>(cx, "function genexp() {\n    [generator expression]\n}");
     }
 
     // Default class constructors are self-hosted, but have their source
     // objects overridden to refer to the span of the class statement or
     // expression. Non-default class constructors are never self-hosted. So,
     // all class constructors always have source.
     bool haveSource = fun->isInterpreted() && (fun->isClassConstructor() ||
                                                !fun->isSelfHostedBuiltin());
@@ -1038,16 +1030,25 @@ js::FunctionToString(JSContext* cx, Hand
     bool addParentheses = haveSource && isToSource && (fun->isLambda() && !fun->isArrow());
 
     if (haveSource && !script->scriptSource()->hasSourceData() &&
         !JSScript::loadSource(cx, script->scriptSource(), &haveSource))
     {
         return nullptr;
     }
 
+    // Fast path for the common case, to avoid StringBuffer overhead.
+    if (!addParentheses && haveSource) {
+        size_t start = script->toStringStart(), end = script->toStringEnd();
+        if (end - start <= ScriptSource::SourceDeflateLimit)
+            return script->scriptSource()->substring(cx, start, end);
+        return script->scriptSource()->substringDontDeflate(cx, start, end);
+    }
+
+    StringBuffer out(cx);
     if (addParentheses) {
         if (!out.append('('))
             return nullptr;
     }
 
     if (haveSource) {
         if (!script->appendSourceDataForToString(cx, out))
             return nullptr;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1829,19 +1829,17 @@ bool
 ScriptSource::appendSubstring(JSContext* cx, StringBuffer& buf, size_t start, size_t stop)
 {
     MOZ_ASSERT(start <= stop);
     size_t len = stop - start;
     UncompressedSourceCache::AutoHoldEntry holder;
     PinnedChars chars(cx, this, holder, start, len);
     if (!chars.get())
         return false;
-    // Sources can be large and we don't want to check "is this char Latin1"
-    // for each source code character, so inflate the buffer here.
-    if (len > 100 && !buf.ensureTwoByteChars())
+    if (len > SourceDeflateLimit && !buf.ensureTwoByteChars())
         return false;
     return buf.append(chars.get(), len);
 }
 
 JSFlatString*
 ScriptSource::functionBodyString(JSContext* cx)
 {
     MOZ_ASSERT(isFunctionBody());
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -497,16 +497,20 @@ class ScriptSource
     // Warning: this is *not* GC-safe! Any chars to be handed out should use
     // PinnedChars. See comment below.
     const char16_t* chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& asp,
                           size_t begin, size_t len);
 
     void movePendingCompressedSource();
 
   public:
+    // When creating a JSString* from TwoByte source characters, we don't try to
+    // to deflate to Latin1 for longer strings, because this can be slow.
+    static const size_t SourceDeflateLimit = 100;
+
     explicit ScriptSource()
       : refs(0),
         data(SourceType(Missing())),
         pinnedCharsStack_(nullptr),
         filename_(nullptr),
         displayURL_(nullptr),
         sourceMapURL_(nullptr),
         mutedErrors_(false),