Bug 1320403 - Move JSFunction::EXPR_BODY to JSScript, LazyScript, and FunctionBox. r=jandem
authorTooru Fujisawa <arai_a@mac.com>
Tue, 29 Nov 2016 09:08:43 +0900
changeset 324505 df0caa6a80d9a13333e1bc78b5a9f1919a08ed28
parent 324504 1e932a9badfac50e6dcfa4a4da395c7644cbc73a
child 324506 1b68b4d00610f0628cb5fac27600670815100f42
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersjandem
bugs1320403
milestone53.0a1
Bug 1320403 - Move JSFunction::EXPR_BODY to JSScript, LazyScript, and FunctionBox. r=jandem
js/src/builtin/ReflectParse.cpp
js/src/frontend/Parser.cpp
js/src/frontend/SharedContext.h
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/shell/js.cpp
js/src/wasm/AsmJS.cpp
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -3402,22 +3402,17 @@ ASTSerializer::function(ParseNode* pn, A
     GeneratorStyle generatorStyle =
         pn->pn_funbox->isGenerator()
         ? (pn->pn_funbox->isLegacyGenerator()
            ? GeneratorStyle::Legacy
            : GeneratorStyle::ES6)
         : GeneratorStyle::None;
 
     bool isAsync = pn->pn_funbox->isAsync();
-    bool isExpression =
-#if JS_HAS_EXPR_CLOSURES
-        func->isExprBody();
-#else
-        false;
-#endif
+    bool isExpression = pn->pn_funbox->isExprBody();
 
     RootedValue id(cx);
     RootedAtom funcAtom(cx, func->name());
     if (!optIdentifier(funcAtom, nullptr, &id))
         return false;
 
     NodeVector args(cx);
     NodeVector defaults(cx);
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -461,16 +461,17 @@ FunctionBox::FunctionBox(ExclusiveContex
     isAnnexB(false),
     wasEmitted(false),
     declaredArguments(false),
     usesArguments(false),
     usesApply(false),
     usesThis(false),
     usesReturn(false),
     hasRest_(false),
+    isExprBody_(false),
     funCxFlags()
 {
     // Functions created at parse time may be set singleton after parsing and
     // baked into JIT code, so they must be allocated tenured. They are held by
     // the JSScript so cannot be collected during a minor GC anyway.
     MOZ_ASSERT(fun->isTenured());
 }
 
@@ -2243,16 +2244,18 @@ Parser<SyntaxParseHandler>::finishFuncti
     // Flags that need to be copied into the JSScript when we do the full
     // parse.
     if (pc->sc()->strict())
         lazy->setStrict();
     lazy->setGeneratorKind(funbox->generatorKind());
     lazy->setAsyncKind(funbox->asyncKind());
     if (funbox->hasRest())
         lazy->setHasRest();
+    if (funbox->isExprBody())
+        lazy->setIsExprBody();
     if (funbox->isLikelyConstructorWrapper())
         lazy->setLikelyConstructorWrapper();
     if (funbox->isDerivedClassConstructor())
         lazy->setIsDerivedClassConstructor();
     if (funbox->needsHomeObject())
         lazy->setNeedsHomeObject();
     if (funbox->declaredArguments)
         lazy->setShouldDeclareArguments();
@@ -2984,32 +2987,39 @@ Parser<FullParseHandler>::skipLazyInnerF
     FunctionBox* funbox = newFunctionBox(pn, fun, Directives(/* strict = */ false),
                                          fun->generatorKind(), fun->asyncKind(), tryAnnexB);
     if (!funbox)
         return false;
 
     LazyScript* lazy = fun->lazyScript();
     if (lazy->needsHomeObject())
         funbox->setNeedsHomeObject();
+    if (lazy->isExprBody())
+        funbox->setIsExprBody();
 
     PropagateTransitiveParseFlags(lazy, pc->sc());
 
     // The position passed to tokenStream.advance() is an offset of the sort
     // returned by userbuf.offset() and expected by userbuf.rawCharPtrAt(),
     // while LazyScript::{begin,end} offsets are relative to the outermost
     // script source.
     Rooted<LazyScript*> lazyOuter(context, handler.lazyOuterFunction());
     uint32_t userbufBase = lazyOuter->begin() - lazyOuter->column();
     if (!tokenStream.advance(fun->lazyScript()->end() - userbufBase))
         return false;
 
-    if (kind == Statement && fun->isExprBody()) {
+#if JS_HAS_EXPR_CLOSURES
+    // Only expression closure can be Statement kind.
+    // If we remove expression closure, we can remove isExprBody flag from
+    // LazyScript and JSScript.
+    if (kind == Statement && funbox->isExprBody()) {
         if (!matchOrInsertSemicolonAfterExpression())
             return false;
     }
+#endif
 
     return true;
 }
 
 template <>
 bool
 Parser<SyntaxParseHandler>::skipLazyInnerFunction(Node pn, FunctionSyntaxKind kind,
                                                   bool tryAnnexB)
@@ -3439,19 +3449,17 @@ Parser<ParseHandler>::functionFormalPara
 #else
             error(JSMSG_CURLY_BEFORE_BODY);
             return false;
 #endif
         }
 
         tokenStream.ungetToken();
         bodyType = ExpressionBody;
-#if JS_HAS_EXPR_CLOSURES
-        fun->setIsExprBody();
-#endif
+        funbox->setIsExprBody();
     }
 
     // Arrow function parameters inherit yieldHandling from the enclosing
     // context, but the arrow body doesn't. E.g. in |(a = yield) => yield|,
     // |yield| in the parameters is either a name or keyword, depending on
     // whether the arrow function is enclosed in a generator function or not.
     // Whereas the |yield| in the function body is always parsed as a name.
     YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind(), pc->asyncKind());
@@ -6078,17 +6086,17 @@ Parser<ParseHandler>::yieldExpression(In
             pc->functionBox()->function()->isSetter())
         {
             errorAt(begin, JSMSG_YIELD_IN_METHOD, js_yield_str);
             return null();
         }
 
         if (pc->funHasReturnExpr
 #if JS_HAS_EXPR_CLOSURES
-            || pc->functionBox()->function()->isExprBody()
+            || pc->functionBox()->isExprBody()
 #endif
             )
         {
             /* As in Python (see PEP-255), disallow return v; in generators. */
             errorAt(begin, JSMSG_BAD_FUNCTION_YIELD);
             return null();
         }
 
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -467,16 +467,19 @@ class FunctionBox : public ObjectBox, pu
 
     // Fields for use in heuristics.
     bool            declaredArguments:1;    /* the Parser declared 'arguments' */
     bool            usesArguments:1;        /* contains a free use of 'arguments' */
     bool            usesApply:1;            /* contains an f.apply() call */
     bool            usesThis:1;             /* contains 'this' */
     bool            usesReturn:1;           /* contains a 'return' statement */
     bool            hasRest_:1;             /* has rest parameter */
+    bool            isExprBody_:1;          /* arrow function with expression
+                                             * body or expression closure:
+                                             * function(x) x*x */
 
     FunctionContextFlags funCxFlags;
 
     FunctionBox(ExclusiveContext* cx, LifoAlloc& alloc, ObjectBox* traceListHead, JSFunction* fun,
                 Directives directives, bool extraWarnings, GeneratorKind generatorKind,
                 FunctionAsyncKind asyncKind);
 
     MutableHandle<LexicalScope::Data*> namedLambdaBindings() {
@@ -540,16 +543,21 @@ class FunctionBox : public ObjectBox, pu
     bool isAsync() const { return asyncKind() == AsyncFunction; }
     bool isArrow() const { return function()->isArrow(); }
 
     bool hasRest() const { return hasRest_; }
     void setHasRest() {
         hasRest_ = true;
     }
 
+    bool isExprBody() const { return isExprBody_; }
+    void setIsExprBody() {
+        isExprBody_ = true;
+    }
+
     void setGeneratorKind(GeneratorKind kind) {
         // A generator kind can be set at initialization, or when "yield" is
         // first seen.  In both cases the transition can only happen from
         // NotGenerator.
         MOZ_ASSERT(!isGenerator());
         generatorKindBits_ = GeneratorKindAsBits(kind);
     }
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1015,18 +1015,16 @@ js::FunctionToString(JSContext* cx, Hand
     } else if (fun->isInterpreted() && !fun->isSelfHostedBuiltin()) {
         if (!out.append("() {\n    ") ||
             !out.append("[sourceless code]") ||
             !out.append("\n}"))
         {
             return nullptr;
         }
     } else {
-        MOZ_ASSERT(!fun->isExprBody());
-
         bool derived = fun->infallibleIsDefaultClassConstructor(cx);
         if (derived && fun->isDerivedClassConstructor()) {
             if (!out.append("(...args) {\n    ") ||
                 !out.append("super(...args);\n}"))
             {
                 return nullptr;
             }
         } else {
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -47,18 +47,16 @@ class JSFunction : public js::NativeObje
         FunctionKindLimit
     };
 
     enum Flags {
         INTERPRETED      = 0x0001,  /* function has a JSScript and environment. */
         CONSTRUCTOR      = 0x0002,  /* function that can be called as a constructor */
         EXTENDED         = 0x0004,  /* structure is FunctionExtended */
         BOUND_FUN        = 0x0008,  /* function was created with Function.prototype.bind. */
-        EXPR_BODY        = 0x0010,  /* arrow function with expression body or
-                                     * expression closure: function(x) x*x */
         HAS_GUESSED_ATOM = 0x0020,  /* function had no explicit name, but a
                                        name was guessed for it anyway */
         LAMBDA           = 0x0040,  /* function comes from a FunctionExpression, ArrowFunction, or
                                        Function() call (not a FunctionDeclaration or nonstandard
                                        function-statement) */
         SELF_HOSTED      = 0x0080,  /* function is self-hosted builtin and must not be
                                        decompilable nor constructible. */
         INTERPRETED_LAZY = 0x0200,  /* function is interpreted but doesn't have a script yet */
@@ -88,17 +86,17 @@ class JSFunction : public js::NativeObje
         INTERPRETED_SETTER = INTERPRETED | SETTER_KIND,
         INTERPRETED_LAMBDA = INTERPRETED | LAMBDA | CONSTRUCTOR,
         INTERPRETED_LAMBDA_ARROW = INTERPRETED | LAMBDA | ARROW_KIND,
         INTERPRETED_LAMBDA_GENERATOR = INTERPRETED | LAMBDA,
         INTERPRETED_NORMAL = INTERPRETED | CONSTRUCTOR,
         INTERPRETED_GENERATOR = INTERPRETED,
         NO_XDR_FLAGS = RESOLVED_LENGTH | RESOLVED_NAME,
 
-        STABLE_ACROSS_CLONES = CONSTRUCTOR | EXPR_BODY | HAS_GUESSED_ATOM | LAMBDA |
+        STABLE_ACROSS_CLONES = CONSTRUCTOR | HAS_GUESSED_ATOM | LAMBDA |
                                SELF_HOSTED | FUNCTION_KIND_MASK
     };
 
     static_assert((INTERPRETED | INTERPRETED_LAZY) == js::JS_FUNCTION_INTERPRETED_BITS,
                   "jsfriendapi.h's JSFunction::INTERPRETED-alike is wrong");
     static_assert(((FunctionKindLimit - 1) << FUNCTION_KIND_SHIFT) <= FUNCTION_KIND_MASK,
                   "FunctionKind doesn't fit into flags_");
 
@@ -173,17 +171,16 @@ class JSFunction : public js::NativeObje
     bool isNative()                 const { return !isInterpreted(); }
 
     bool isConstructor()            const { return flags() & CONSTRUCTOR; }
 
     /* Possible attributes of a native function: */
     bool isAsmJSNative()            const { return kind() == AsmJS; }
 
     /* Possible attributes of an interpreted function: */
-    bool isExprBody()               const { return flags() & EXPR_BODY; }
     bool hasGuessedAtom()           const { return flags() & HAS_GUESSED_ATOM; }
     bool isLambda()                 const { return flags() & LAMBDA; }
     bool isBoundFunction()          const { return flags() & BOUND_FUN; }
     bool isInterpretedLazy()        const { return flags() & INTERPRETED_LAZY; }
     bool hasScript()                const { return flags() & INTERPRETED; }
 
     bool infallibleIsDefaultClassConstructor(JSContext* cx) const;
 
@@ -275,21 +272,16 @@ class JSFunction : public js::NativeObje
         flags_ &= ~CONSTRUCTOR;
     }
     void setIsIntrinsic() {
         MOZ_ASSERT(isNative());
         MOZ_ASSERT(!isIntrinsic());
         flags_ |= SELF_HOSTED;
     }
 
-    // Can be called multiple times by the parser.
-    void setIsExprBody() {
-        flags_ |= EXPR_BODY;
-    }
-
     void setArrow() {
         setKind(Arrow);
     }
 
     void setResolvedLength() {
         flags_ |= RESOLVED_LENGTH;
     }
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -313,16 +313,17 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         HasMappedArgsObj,
         FunctionHasThisBinding,
         FunctionHasExtraBodyVarScope,
         IsGeneratorExp,
         IsLegacyGenerator,
         IsStarGenerator,
         IsAsync,
         HasRest,
+        IsExprBody,
         OwnSource,
         ExplicitUseStrict,
         SelfHosted,
         HasSingleton,
         TreatAsRunOnce,
         HasLazyScript,
         HasNonSyntacticScope,
         HasInnerFunctions,
@@ -430,16 +431,18 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         if (script->isLegacyGenerator())
             scriptBits |= (1 << IsLegacyGenerator);
         if (script->isStarGenerator())
             scriptBits |= (1 << IsStarGenerator);
         if (script->asyncKind() == AsyncFunction)
             scriptBits |= (1 << IsAsync);
         if (script->hasRest())
             scriptBits |= (1 << HasRest);
+        if (script->isExprBody())
+            scriptBits |= (1 << IsExprBody);
         if (script->hasSingletons())
             scriptBits |= (1 << HasSingleton);
         if (script->treatAsRunOnce())
             scriptBits |= (1 << TreatAsRunOnce);
         if (script->isRelazifiable())
             scriptBits |= (1 << HasLazyScript);
         if (script->hasNonSyntacticScope())
             scriptBits |= (1 << HasNonSyntacticScope);
@@ -583,16 +586,18 @@ js::XDRScript(XDRState<mode>* xdr, Handl
             script->setGeneratorKind(LegacyGenerator);
         } else if (scriptBits & (1 << IsStarGenerator))
             script->setGeneratorKind(StarGenerator);
 
         if (scriptBits & (1 << IsAsync))
             script->setAsyncKind(AsyncFunction);
         if (scriptBits & (1 << HasRest))
             script->setHasRest();
+        if (scriptBits & (1 << IsExprBody))
+            script->setIsExprBody();
     }
 
     JS_STATIC_ASSERT(sizeof(jsbytecode) == 1);
     JS_STATIC_ASSERT(sizeof(jssrcnote) == 1);
 
     if (scriptBits & (1 << OwnSource)) {
         if (!script->scriptSource()->performXDR<mode>(xdr))
             return false;
@@ -2625,16 +2630,18 @@ JSScript::initFromFunctionBox(ExclusiveC
 
     script->funLength_ = funbox->length;
 
     script->isGeneratorExp_ = funbox->isGenexpLambda;
     script->setGeneratorKind(funbox->generatorKind());
     script->setAsyncKind(funbox->asyncKind());
     if (funbox->hasRest())
         script->setHasRest();
+    if (funbox->isExprBody())
+        script->setIsExprBody();
 
     PositionalFormalParameterIter fi(script);
     while (fi && !fi.closedOver())
         fi++;
     script->funHasAnyAliasedFormal_ = !!fi;
 
     script->setHasInnerFunctions(funbox->hasInnerFunctions());
 }
@@ -3286,16 +3293,17 @@ js::detail::CopyScript(JSContext* cx, Ha
     dst->hasInnerFunctions_ = src->hasInnerFunctions();
     dst->isGeneratorExp_ = src->isGeneratorExp();
     dst->setGeneratorKind(src->generatorKind());
     dst->isDerivedClassConstructor_ = src->isDerivedClassConstructor();
     dst->needsHomeObject_ = src->needsHomeObject();
     dst->isDefaultClassConstructor_ = src->isDefaultClassConstructor();
     dst->isAsync_ = src->asyncKind() == AsyncFunction;
     dst->hasRest_ = src->hasRest_;
+    dst->isExprBody_ = src->isExprBody_;
 
     if (nconsts != 0) {
         GCPtrValue* vector = Rebase<GCPtrValue>(dst, src, src->consts()->vector);
         dst->consts()->vector = vector;
         for (unsigned i = 0; i < nconsts; ++i)
             MOZ_ASSERT_IF(vector[i].isMarkable(), vector[i].toString()->isAtom());
     }
     if (nobjects != 0) {
@@ -4020,16 +4028,17 @@ LazyScript::Create(ExclusiveContext* cx,
         uint64_t packedFields;
     };
 
     p.version = version;
     p.shouldDeclareArguments = false;
     p.hasThisBinding = false;
     p.isAsync = false;
     p.hasRest = false;
+    p.isExprBody = false;
     p.numClosedOverBindings = closedOverBindings.length();
     p.numInnerFunctions = innerFunctions.length();
     p.generatorKindBits = GeneratorKindAsBits(NotGenerator);
     p.strict = false;
     p.bindingsAccessedDynamically = false;
     p.hasDebuggerStatement = false;
     p.hasDirectEval = false;
     p.isLikelyConstructorWrapper = false;
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -1008,16 +1008,17 @@ class JSScript : public js::gc::TenuredC
     bool needsHomeObject_:1;
 
     bool isDerivedClassConstructor_:1;
     bool isDefaultClassConstructor_:1;
 
     bool isAsync_:1;
 
     bool hasRest_:1;
+    bool isExprBody_:1;
 
     // Add padding so JSScript is gc::Cell aligned. Make padding protected
     // instead of private to suppress -Wunused-private-field compiler warnings.
   protected:
 #if JS_BITS_PER_WORD == 32
     // Currently no padding is needed.
 #endif
 
@@ -1312,16 +1313,23 @@ class JSScript : public js::gc::TenuredC
 
     bool hasRest() const {
         return hasRest_;
     }
     void setHasRest() {
         hasRest_ = true;
     }
 
+    bool isExprBody() const {
+        return isExprBody_;
+    }
+    void setIsExprBody() {
+        isExprBody_ = true;
+    }
+
     void setNeedsHomeObject() {
         needsHomeObject_ = true;
     }
     bool needsHomeObject() const {
         return needsHomeObject_;
     }
 
     bool isDerivedClassConstructor() const {
@@ -1917,27 +1925,32 @@ class LazyScript : public gc::TenuredCel
     // Add padding so LazyScript is gc::Cell aligned. Make padding protected
     // instead of private to suppress -Wunused-private-field compiler warnings.
   protected:
 #if JS_BITS_PER_WORD == 32
     uint32_t padding;
 #endif
 
   private:
-    static const uint32_t NumClosedOverBindingsBits = 21;
+    static const uint32_t NumClosedOverBindingsBits = 20;
     static const uint32_t NumInnerFunctionsBits = 20;
 
     struct PackedView {
         // Assorted bits that should really be in ScriptSourceObject.
         uint32_t version : 8;
 
         uint32_t shouldDeclareArguments : 1;
         uint32_t hasThisBinding : 1;
         uint32_t isAsync : 1;
+        uint32_t isExprBody : 1;
+
         uint32_t numClosedOverBindings : NumClosedOverBindingsBits;
+
+        // -- 32bit boundary --
+
         uint32_t numInnerFunctions : NumInnerFunctionsBits;
 
         uint32_t generatorKindBits : 2;
 
         // N.B. These are booleans but need to be uint32_t to pack correctly on MSVC.
         // If you add another boolean here, make sure to initialze it in
         // LazyScript::CreateRaw().
         uint32_t strict : 1;
@@ -2080,16 +2093,23 @@ class LazyScript : public gc::TenuredCel
 
     bool hasRest() const {
         return p_.hasRest;
     }
     void setHasRest() {
         p_.hasRest = true;
     }
 
+    bool isExprBody() const {
+        return p_.isExprBody;
+    }
+    void setIsExprBody() {
+        p_.isExprBody = true;
+    }
+
     bool strict() const {
         return p_.strict;
     }
     void setStrict() {
         p_.strict = true;
     }
 
     bool bindingsAccessedDynamically() const {
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2671,17 +2671,17 @@ DisassembleScript(JSContext* cx, HandleS
         if (fun->needsNamedLambdaEnvironment()) {
             if (sp->put(" NEEDS_NAMEDLAMBDAENV") < 0)
                 return false;
         }
         if (fun->isConstructor()) {
             if (sp->put(" CONSTRUCTOR") < 0)
                 return false;
         }
-        if (fun->isExprBody()) {
+        if (script->isExprBody()) {
             if (sp->put(" EXPRESSION_CLOSURE") < 0)
                 return false;
         }
         if (fun->isSelfHostedBuiltin()) {
             if (sp->put(" SELF_HOSTED") < 0)
                 return false;
         }
         if (fun->isArrow()) {
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -3245,20 +3245,19 @@ CheckModuleLevelName(ModuleValidator& m,
     }
 
     return true;
 }
 
 static bool
 CheckFunctionHead(ModuleValidator& m, ParseNode* fn)
 {
-    JSFunction* fun = FunctionObject(fn);
     if (fn->pn_funbox->hasRest())
         return m.fail(fn, "rest args not allowed");
-    if (fun->isExprBody())
+    if (fn->pn_funbox->isExprBody())
         return m.fail(fn, "expression closures not allowed");
     if (fn->pn_funbox->hasDestructuringArgs)
         return m.fail(fn, "destructuring args not allowed");
     return true;
 }
 
 static bool
 CheckArgument(ModuleValidator& m, ParseNode* arg, PropertyName** name)