[INFER] Allow analysis/compilation of top level <script>, bug 662704.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 18 Aug 2011 08:54:36 -0700
changeset 76135 7dae91c263cfbbf503a6230217edba812228404f
parent 76134 79f5cea985f243c95302a21dfcf81c15fbc096ef
child 76136 0534f46a70913bb08d114569359daf54b94c9c4b
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
bugs662704
milestone8.0a1
[INFER] Allow analysis/compilation of top level <script>, bug 662704.
js/src/jsapi.cpp
js/src/jsemit.cpp
js/src/jsinfer.cpp
js/src/jsinferinlines.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/methodjit/Compiler.cpp
js/src/vm/Debugger.cpp
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4960,33 +4960,32 @@ bool
 EvaluateUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj,
                                     JSPrincipals *principals,
                                     const jschar *chars, uintN length,
                                     const char *filename, uintN lineno,
                                     jsval *rval, JSVersion compileVersion)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
 
-    CHECK_REQUEST(cx);
-    JSScript *script = Compiler::compileScript(cx, obj, NULL, principals,
-                                               !rval
-                                               ? TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL
-                                               : TCF_COMPILE_N_GO,
+    uint32 flags = TCF_COMPILE_N_GO | TCF_NEED_SCRIPT_OBJECT;
+    if (!rval)
+        flags |= TCF_NO_SCRIPT_RVAL;
+
+    CHECK_REQUEST(cx);
+    JSScript *script = Compiler::compileScript(cx, obj, NULL, principals, flags,
                                                chars, length, filename, lineno, compileVersion);
     if (!script) {
         LAST_FRAME_CHECKS(cx, script);
         return false;
     }
-    script->isUncachedEval = true;
 
     JS_ASSERT(script->getVersion() == compileVersion);
 
     bool ok = ExternalExecute(cx, script, *obj, Valueify(rval));
     LAST_FRAME_CHECKS(cx, ok);
-    js_DestroyScript(cx, script, 5);
     return ok;
 
 }
 
 JS_PUBLIC_API(JSBool)
 JS_EvaluateUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
                                         JSPrincipals *principals,
                                         const jschar *chars, uintN length,
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -3037,17 +3037,17 @@ EmitNameIncDec(JSContext *cx, JSParseNod
 
     if (post) {
         if (js_Emit2(cx, cg, JSOP_PICK, (jsbytecode)2) < 0) // N? N+1 OBJ
             return false;
         if (js_Emit1(cx, cg, JSOP_SWAP) < 0)                // N? OBJ N+1
             return false;
     }
 
-    if (!EmitAtomOp(cx, pn, JSOP_SETPROP, cg))     // N? N+1
+    if (!EmitAtomOp(cx, pn, global ? JSOP_SETGNAME : JSOP_SETNAME, cg))     // N? N+1
         return false;
     if (post && js_Emit1(cx, cg, JSOP_POP) < 0)    // RESULT
         return false;
 
     UpdateDecomposeLength(cg, start);
 
     return true;
 }
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -1848,24 +1848,21 @@ TypeCompartment::newAllocationSiteTypeOb
     if (!res) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return NULL;
     }
 
     jsbytecode *pc = key.script->code + key.offset;
     UntrapOpcode untrap(cx, key.script, pc);
 
-    if (JSOp(*pc) == JSOP_NEWOBJECT && !key.uncached) {
+    if (JSOp(*pc) == JSOP_NEWOBJECT) {
         /*
          * This object is always constructed the same way and will not be
          * observed by other code before all properties have been added. Mark
          * all the properties as definite properties of the object.
-         * :XXX: skipping for objects from uncached eval scripts, as entries
-         * in the allocation site table may be stale and we could potentially
-         * get a spurious hit. Fix this hack.
          */
         JSObject *baseobj = key.script->getObject(GET_SLOTNO(pc));
 
         if (!res->addDefiniteProperties(cx, baseobj))
             return NULL;
     }
 
     if (!allocationSiteTable->add(p, key, res)) {
@@ -3837,24 +3834,16 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
     return true;
 }
 
 void
 ScriptAnalysis::analyzeTypes(JSContext *cx)
 {
     JS_ASSERT(!ranInference());
 
-    /*
-     * Types in uncached eval scripts cannot be analyzed. These are manually
-     * destroyed, rather than destroyed on GC, and we cannot ensure that type
-     * constraints do not refer to the script after it has been destroyed.are
-     * referring to 
-     */
-    JS_ASSERT(!script->isUncachedEval);
-
     if (OOM()) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
     }
 
     /*
      * Refuse to analyze the types in a script which is compileAndGo but is
      * running against a global with a cleared scope. Per GlobalObject::clear,
@@ -4477,17 +4466,17 @@ ScriptAnalysis::printTypes(JSContext *cx
             }
         }
     }
 
 #ifdef DEBUG
 
     if (script->hasFunction)
         printf("Function");
-    else if (script->isCachedEval || script->isUncachedEval)
+    else if (script->isCachedEval)
         printf("Eval");
     else
         printf("Main");
     printf(" #%u %s (line %d):\n", script->id(), script->filename, script->lineno);
 
     printf("locals:");
     printf("\n    return:");
     TypeScript::ReturnTypes(script)->print(cx);
@@ -5465,17 +5454,17 @@ TypeCompartment::sweep(JSContext *cx)
         }
     }
 
     if (allocationSiteTable) {
         for (AllocationSiteTable::Enum e(*allocationSiteTable); !e.empty(); e.popFront()) {
             const AllocationSiteKey &key = e.front().key;
             TypeObject *object = e.front().value;
 
-            if (key.uncached || key.script->isAboutToBeFinalized(cx) || !object->isMarked())
+            if (key.script->isAboutToBeFinalized(cx) || !object->isMarked())
                 e.removeFront();
         }
     }
 
     /*
      * The pending array is reset on GC, it can grow large (75+ KB) and is easy
      * to reallocate if the compartment becomes active again.
      */
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -503,19 +503,18 @@ TypeScript::StandardType(JSContext *cx, 
     JSObject *proto;
     if (!js_GetClassPrototype(cx, script->global(), key, &proto, NULL))
         return NULL;
     return proto->getNewType(cx);
 }
 
 struct AllocationSiteKey {
     JSScript *script;
-    uint32 offset : 23;
+    uint32 offset : 24;
     JSProtoKey kind : 8;
-    bool uncached : 1;
 
     static const uint32 OFFSET_LIMIT = (1 << 23);
 
     AllocationSiteKey() { PodZero(this); }
 
     typedef AllocationSiteKey Lookup;
 
     static inline uint32 hash(AllocationSiteKey key) {
@@ -534,17 +533,16 @@ TypeScript::InitObject(JSContext *cx, JS
     uint32 offset = pc - script->code;
 
     if (!cx->typeInferenceEnabled() || !script->hasGlobal() || offset >= AllocationSiteKey::OFFSET_LIMIT)
         return GetTypeNewObject(cx, kind);
 
     AllocationSiteKey key;
     key.script = script;
     key.offset = offset;
-    key.uncached = script->isUncachedEval;
     key.kind = kind;
 
     if (!cx->compartment->types.allocationSiteTable)
         return cx->compartment->types.newAllocationSiteTypeObject(cx, key);
 
     AllocationSiteTable::Ptr p = cx->compartment->types.allocationSiteTable->lookup(key);
 
     if (p)
@@ -602,17 +600,17 @@ TypeScript::MonitorAssign(JSContext *cx,
 
 /* static */ inline void
 TypeScript::SetThis(JSContext *cx, JSScript *script, Type type)
 {
     if (!cx->typeInferenceEnabled() || !script->ensureHasTypes(cx))
         return;
 
     /* Analyze the script regardless if -a was used. */
-    bool analyze = cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS) && !script->isUncachedEval;
+    bool analyze = cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS);
 
     if (!ThisTypes(script)->hasType(type) || analyze) {
         AutoEnterTypeInference enter(cx);
 
         InferSpew(ISpewOps, "externalType: setThis #%u: %s",
                   script->id(), TypeString(type));
         ThisTypes(script)->addType(cx, type);
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1503,17 +1503,17 @@ js_TraceScript(JSTracer *trc, JSScript *
         MarkValueRange(trc, constarray->length, constarray->vector, "consts");
     }
 
     /*
      * Mark the object keeping this script alive. The script can be traced
      * separately if, e.g. we are GC'ing while type inference code is active,
      * and we need to make sure both the script and the object survive the GC.
      */
-    if (!script->isCachedEval && !script->isUncachedEval && script->u.object)
+    if (!script->isCachedEval && script->u.object)
         MarkObject(trc, *script->u.object, "object");
     if (script->hasFunction)
         MarkObject(trc, *script->function(), "script_fun");
 
     if (IS_GC_MARKING_TRACER(trc) && script->filename)
         js_MarkScriptFilename(script->filename);
 
     script->bindings.trace(trc);
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -503,17 +503,16 @@ struct JSScript {
                                                      this script */
     bool            warnedAboutUndefinedProp:1; /* have warned about uses of
                                                    undefined properties in this
                                                    script */
     bool            hasSingletons:1;  /* script has singleton objects */
     bool            hasFunction:1;    /* function is active in 'where' union */
     bool            isActiveEval:1;   /* script came from eval(), and is still active */
     bool            isCachedEval:1;   /* script came from eval(), and is in eval cache */
-    bool            isUncachedEval:1; /* script came from EvaluateScript */
     bool            usedLazyArgs:1;   /* script has used lazy arguments at some point */
     bool            createdArgs:1;    /* script has had arguments objects created */
     bool            uninlineable:1;   /* script is considered uninlineable by analysis */
 #ifdef JS_METHODJIT
     bool            debugMode:1;      /* script was compiled in debug mode */
     bool            singleStepMode:1; /* compile script in single-step mode */
     bool            failedBoundsCheck:1; /* script has had hoisted bounds checks fail */
 #endif
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -130,18 +130,16 @@ mjit::Compiler::Compiler(JSContext *cx, 
     addTraceHints(false),
 #endif
     inlining_(false),
     hasGlobalReallocation(false),
     oomInVector(false),
     applyTricks(NoApplyTricks),
     pcLengths(NULL)
 {
-    JS_ASSERT(!outerScript->isUncachedEval);
-
     /* :FIXME: bug 637856 disabling traceJit if inference is enabled */
     if (cx->typeInferenceEnabled())
         addTraceHints = false;
 
     /* Once a script starts getting really hot we will inline calls in it. */
     if (!debugMode() && cx->typeInferenceEnabled() && globalObj &&
         (outerScript->useCount() >= USES_BEFORE_INLINING ||
          cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS))) {
@@ -606,20 +604,16 @@ mjit::TryCompile(JSContext *cx, StackFra
 #if JS_HAS_SHARP_VARS
     if (fp->script()->hasSharps)
         return Compile_Abort;
 #endif
     bool ok = cx->compartment->ensureJaegerCompartmentExists(cx);
     if (!ok)
         return Compile_Abort;
 
-    // Uncached eval scripts are not analyzed or compiled.
-    if (fp->script()->isUncachedEval)
-        return Compile_Abort;
-
     // Ensure that constructors have at least one slot.
     if (fp->isConstructing() && !fp->script()->nslots)
         fp->script()->nslots++;
 
     CompileStatus status;
     {
         types::AutoEnterTypeInference enter(cx, true);
 
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2661,26 +2661,25 @@ EvaluateInScope(JSContext *cx, JSObject 
 
     /*
      * NB: This function breaks the assumption that the compiler can see all
      * calls and properly compute a static level. In order to get around this,
      * we use a static level that will cause us not to attempt to optimize
      * variable references made by this frame.
      */
     JSScript *script = Compiler::compileScript(cx, scobj, fp, fp->scopeChain().principals(cx),
-                                               TCF_COMPILE_N_GO, chars, length,
+                                               TCF_COMPILE_N_GO | TCF_NEED_SCRIPT_OBJECT,
+                                               chars, length,
                                                filename, lineno, cx->findVersion(),
                                                NULL, UpvarCookie::UPVAR_LEVEL_LIMIT);
 
     if (!script)
         return false;
 
-    script->isUncachedEval = true;
     bool ok = Execute(cx, script, *scobj, fp->thisValue(), EXECUTE_DEBUG, fp, rval);
-    js_DestroyScript(cx, script, 6);
     return ok;
 }
 
 }
 
 enum EvalBindingsMode { WithoutBindings, WithBindings };
 
 static JSBool