Bug 1566607 - Add JSFunction::hasLazyScript. r=jandem
authorTed Campbell <tcampbell@mozilla.com>
Wed, 17 Jul 2019 13:24:14 +0000
changeset 483298 3f2f72c37e4ee29bbfabb71a9cff72f05c8578e1
parent 483297 a3b58e34bf2b314cb98953ef82fd7ddd0dd20b33
child 483299 e4bf7a4c4bbfafdf2a973b8d541cba0c50308f43
push id36313
push useropoprus@mozilla.com
push dateThu, 18 Jul 2019 21:50:51 +0000
treeherdermozilla-central@5fceb8c496bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1566607
milestone70.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 1566607 - Add JSFunction::hasLazyScript. r=jandem This helper distinguishes normal lazy functions from lazy self-hosted funtions which have a nullptr LazyScript. A function may return true for hasLazyScript but still return a null lazy-script if it has been partially initialized and is still being accessed such as by GC. Differential Revision: https://phabricator.services.mozilla.com/D38252
js/src/jit/OptimizationTracking.cpp
js/src/vm/JSFunction.cpp
js/src/vm/JSFunction.h
js/src/vm/Realm.cpp
--- a/js/src/jit/OptimizationTracking.cpp
+++ b/js/src/jit/OptimizationTracking.cpp
@@ -791,17 +791,17 @@ static JSFunction* MaybeConstructorFromT
 }
 
 static void InterpretedFunctionFilenameAndLineNumber(JSFunction* fun,
                                                      const char** filename,
                                                      Maybe<unsigned>* lineno) {
   if (fun->hasScript()) {
     *filename = fun->nonLazyScript()->maybeForwardedScriptSource()->filename();
     *lineno = Some((unsigned)fun->nonLazyScript()->lineno());
-  } else if (fun->lazyScriptOrNull()) {
+  } else if (fun->hasLazyScript()) {
     *filename = fun->lazyScript()->maybeForwardedScriptSource()->filename();
     *lineno = Some((unsigned)fun->lazyScript()->lineno());
   } else {
     *filename = "(self-hosted builtin)";
     *lineno = Nothing();
   }
 }
 
--- a/js/src/vm/JSFunction.cpp
+++ b/js/src/vm/JSFunction.cpp
@@ -769,17 +769,17 @@ inline void JSFunction::trace(JSTracer* 
   TraceNullableEdge(trc, &atom_, "atom");
 
   if (isInterpreted()) {
     // Functions can be be marked as interpreted despite having no script
     // yet at some points when parsing, and can be lazy with no lazy script
     // for self-hosted code.
     if (hasScript() && !hasUncompletedScript()) {
       TraceManuallyBarrieredEdge(trc, &u.scripted.s.script_, "script");
-    } else if (isInterpretedLazy() && u.scripted.s.lazy_) {
+    } else if (hasLazyScript() && u.scripted.s.lazy_) {
       TraceManuallyBarrieredEdge(trc, &u.scripted.s.lazy_, "lazyScript");
     }
 
     if (u.scripted.env_) {
       TraceManuallyBarrieredEdge(trc, &u.scripted.env_, "fun_environment");
     }
   }
 }
@@ -1543,18 +1543,18 @@ bool JSFunction::createScriptForLazilyIn
                                                           HandleFunction fun) {
   MOZ_ASSERT(fun->isInterpretedLazy());
   MOZ_ASSERT(cx->compartment() == fun->compartment());
 
   // The function must be same-compartment but might be cross-realm. Make sure
   // the script is created in the function's realm.
   AutoRealm ar(cx, fun);
 
-  Rooted<LazyScript*> lazy(cx, fun->lazyScriptOrNull());
-  if (lazy) {
+  if (fun->hasLazyScript()) {
+    Rooted<LazyScript*> lazy(cx, fun->lazyScript());
     RootedScript script(cx, lazy->maybeScript());
 
     // Only functions without inner functions or direct eval are
     // re-lazified. Functions with either of those are on the static scope
     // chain of their inner functions, or in the case of eval, possibly
     // eval'd inner functions. This prohibits re-lazification as
     // StaticScopeIter queries needsCallObject of those functions, which
     // requires a non-lazy script.  Note that if this ever changes,
@@ -2241,22 +2241,26 @@ JSFunction* js::CloneFunctionReuseScript
                        NewFunctionClone(cx, fun, newKind, allocKind, proto));
   if (!clone) {
     return nullptr;
   }
 
   if (fun->hasScript()) {
     clone->initScript(fun->nonLazyScript());
     clone->initEnvironment(enclosingEnv);
-  } else {
-    MOZ_ASSERT(fun->isInterpretedLazy());
+  } else if (fun->hasLazyScript()) {
     MOZ_ASSERT(fun->compartment() == clone->compartment());
-    LazyScript* lazy = fun->lazyScriptOrNull();
+    LazyScript* lazy = fun->lazyScript();
     clone->initLazyScript(lazy);
     clone->initEnvironment(enclosingEnv);
+  } else {
+    MOZ_ASSERT(fun->isSelfHostedBuiltin());
+    MOZ_ASSERT(fun->compartment() == clone->compartment());
+    clone->initLazyScript(nullptr);
+    clone->initEnvironment(enclosingEnv);
   }
 
   /*
    * Clone the function, reusing its script. We can use the same group as
    * the original function provided that its prototype is correct.
    */
   if (fun->staticPrototype() == clone->staticPrototype()) {
     clone->setGroup(fun->group());
--- a/js/src/vm/JSFunction.h
+++ b/js/src/vm/JSFunction.h
@@ -290,24 +290,24 @@ class JSFunction : public js::NativeObje
         HAS_BOUND_FUNCTION_NAME_PREFIX == HAS_GUESSED_ATOM,
         "HAS_BOUND_FUNCTION_NAME_PREFIX is only used for bound functions");
     MOZ_ASSERT(isBoundFunction());
     return flags() & HAS_BOUND_FUNCTION_NAME_PREFIX;
   }
   bool isLambda() const { return flags() & LAMBDA; }
   bool isInterpretedLazy() const { return flags() & INTERPRETED_LAZY; }
 
-  // This method doesn't check the non-nullness of u.scripted.s.script_,
-  // because it's guaranteed to be non-null when this has INTERPRETED flag,
-  // for live JSFunctions.
-  //
-  // When this JSFunction instance is reached via GC iteration, the above
-  // doesn't hold, and hasUncompletedScript should also be checked.
-  // (see the comment above hasUncompletedScript for more details).
+  // These methods determine which of the u.scripted.s union arms are active.
+  // For live JSFunctions the pointer values will always be non-null, but due
+  // to partial initialization the GC (and other features that scan the heap
+  // directly) may still return a null pointer.
   bool hasScript() const { return flags() & INTERPRETED; }
+  bool hasLazyScript() const {
+    return isInterpretedLazy() && !isSelfHostedOrIntrinsic();
+  }
 
   // Arrow functions store their lexical new.target in the first extended slot.
   bool isArrow() const { return kind() == Arrow; }
   // Every class-constructor is also a method.
   bool isMethod() const {
     return kind() == Method || kind() == ClassConstructor;
   }
   bool isClassConstructor() const { return kind() == ClassConstructor; }
@@ -589,17 +589,17 @@ class JSFunction : public js::NativeObje
 
   // If this is a scripted function, returns its canonical function (the
   // original function allocated by the frontend). Note that lazy self-hosted
   // builtins don't have a lazy script so in that case we also return nullptr.
   JSFunction* maybeCanonicalFunction() const {
     if (hasScript()) {
       return nonLazyScript()->functionNonDelazifying();
     }
-    if (isInterpretedLazy() && !isSelfHostedBuiltin()) {
+    if (hasLazyScript()) {
       return lazyScript()->functionNonDelazifying();
     }
     return nullptr;
   }
 
   // The state of a JSFunction whose script errored out during bytecode
   // compilation. Such JSFunctions are only reachable via GC iteration and
   // not from script.
@@ -615,52 +615,51 @@ class JSFunction : public js::NativeObje
     MOZ_ASSERT(!hasUncompletedScript());
     return u.scripted.s.script_;
   }
 
   static bool getLength(JSContext* cx, js::HandleFunction fun,
                         uint16_t* length);
 
   js::LazyScript* lazyScript() const {
-    MOZ_ASSERT(isInterpretedLazy() && u.scripted.s.lazy_);
+    MOZ_ASSERT(hasLazyScript() && u.scripted.s.lazy_);
     return u.scripted.s.lazy_;
   }
-
-  js::LazyScript* lazyScriptOrNull() const {
-    MOZ_ASSERT(isInterpretedLazy());
+  js::LazyScript* maybeLazyScript() const {
+    MOZ_ASSERT(hasLazyScript());
     return u.scripted.s.lazy_;
   }
 
   js::GeneratorKind generatorKind() const {
     if (!isInterpreted()) {
       return js::GeneratorKind::NotGenerator;
     }
     if (hasScript()) {
       return nonLazyScript()->generatorKind();
     }
-    if (js::LazyScript* lazy = lazyScriptOrNull()) {
-      return lazy->generatorKind();
+    if (hasLazyScript()) {
+      return lazyScript()->generatorKind();
     }
     MOZ_ASSERT(isSelfHostedBuiltin());
     return js::GeneratorKind::NotGenerator;
   }
 
   bool isGenerator() const {
     return generatorKind() == js::GeneratorKind::Generator;
   }
 
   js::FunctionAsyncKind asyncKind() const {
     if (!isInterpreted()) {
       return js::FunctionAsyncKind::SyncFunction;
     }
     if (hasScript()) {
       return nonLazyScript()->asyncKind();
     }
-    if (js::LazyScript* lazy = lazyScriptOrNull()) {
-      return lazy->asyncKind();
+    if (hasLazyScript()) {
+      return lazyScript()->asyncKind();
     }
     MOZ_ASSERT(isSelfHostedBuiltin());
     return js::FunctionAsyncKind::SyncFunction;
   }
 
   bool isAsync() const {
     return asyncKind() == js::FunctionAsyncKind::AsyncFunction;
   }
@@ -672,19 +671,19 @@ class JSFunction : public js::NativeObje
 
   void initScript(JSScript* script) {
     MOZ_ASSERT_IF(script, realm() == script->realm());
     mutableScript().init(script);
   }
 
   void setUnlazifiedScript(JSScript* script) {
     MOZ_ASSERT(isInterpretedLazy());
-    if (lazyScriptOrNull()) {
+    if (hasLazyScript()) {
       // Trigger a pre barrier on the lazy script being overwritten.
-      js::LazyScript::writeBarrierPre(lazyScriptOrNull());
+      js::LazyScript::writeBarrierPre(lazyScript());
       if (!lazyScript()->maybeScript()) {
         lazyScript()->initScript(script);
       }
     }
     flags_ &= ~INTERPRETED_LAZY;
     flags_ |= INTERPRETED;
     initScript(script);
   }
--- a/js/src/vm/Realm.cpp
+++ b/js/src/vm/Realm.cpp
@@ -697,18 +697,18 @@ static bool AddLazyFunctionsForRealm(JSC
 
   for (auto i = cx->zone()->cellIter<JSObject>(kind); !i.done(); i.next()) {
     JSFunction* fun = &i->as<JSFunction>();
 
     if (fun->realm() != cx->realm()) {
       continue;
     }
 
-    if (fun->isInterpretedLazy()) {
-      LazyScript* lazy = fun->lazyScriptOrNull();
+    if (fun->hasLazyScript()) {
+      LazyScript* lazy = fun->maybeLazyScript();
       if (lazy && lazy->enclosingScriptHasEverBeenCompiled()) {
         if (!lazyFunctions.append(fun)) {
           return false;
         }
       }
     }
   }