Bug 969786: Record the introduction script in ScriptSourceObjects. r=sfink
authorJim Blandy <jimb@mozilla.com>
Wed, 26 Feb 2014 15:20:00 -0800
changeset 171361 6f174fcd2b20bbad88f9f019a6c97afc034b45fb
parent 171360 38e421359f5e373dba726e2ee023724448bea63a
child 171362 6e3b9da3d83f812d595f80447c4efa1c32ea419d
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewerssfink
bugs969786
milestone30.0a1
Bug 969786: Record the introduction script in ScriptSourceObjects. r=sfink
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsworkers.cpp
js/src/jsworkers.h
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1265,16 +1265,41 @@ const Value &
 ScriptSourceObject::elementAttributeName() const
 {
     const Value &prop = getReservedSlot(ELEMENT_PROPERTY_SLOT);
     JS_ASSERT(prop.isUndefined() || prop.isString());
     return prop;
 }
 
 void
+ScriptSourceObject::initIntroductionScript(JSScript *script)
+{
+    JS_ASSERT(!getReservedSlot(INTRODUCTION_SCRIPT_SLOT).toPrivate());
+
+    // There is no equivalent of cross-compartment wrappers for scripts. If
+    // the introduction script would be in a different compartment from the
+    // compiled code, we would be creating a cross-compartment script
+    // reference, which would be bogus. In that case, just don't bother to
+    // retain the introduction script.
+    if (script && script->compartment() == compartment())
+        setReservedSlot(INTRODUCTION_SCRIPT_SLOT, PrivateValue(script));
+}
+
+void
+ScriptSourceObject::trace(JSTracer *trc, JSObject *obj)
+{
+    ScriptSourceObject *sso = static_cast<ScriptSourceObject *>(obj);
+
+    if (JSScript *script = sso->introductionScript()) {
+        MarkScriptUnbarriered(trc, &script, "ScriptSourceObject introductionScript");
+        sso->setReservedSlot(INTRODUCTION_SCRIPT_SLOT, PrivateValue(script));
+    }
+}
+
+void
 ScriptSourceObject::finalize(FreeOp *fop, JSObject *obj)
 {
     // ScriptSource::setSource automatically takes care of the refcount
     obj->as<ScriptSourceObject>().setSource(nullptr);
 }
 
 const Class ScriptSourceObject::class_ = {
     "ScriptSource",
@@ -1282,17 +1307,21 @@ const Class ScriptSourceObject::class_ =
     JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_IS_ANONYMOUS,
     JS_PropertyStub,        /* addProperty */
     JS_DeletePropertyStub,  /* delProperty */
     JS_PropertyStub,        /* getProperty */
     JS_StrictPropertyStub,  /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
-    ScriptSourceObject::finalize
+    finalize,
+    nullptr,                /* call        */
+    nullptr,                /* hasInstance */
+    nullptr,                /* construct   */
+    trace
 };
 
 ScriptSourceObject *
 ScriptSourceObject::create(ExclusiveContext *cx, ScriptSource *source,
                            const ReadOnlyCompileOptions &options)
 {
     RootedObject object(cx, NewObjectWithGivenProto(cx, &class_, nullptr, cx->global()));
     if (!object)
@@ -1302,16 +1331,19 @@ ScriptSourceObject::create(ExclusiveCont
     source->incref();
     sourceObject->initSlot(SOURCE_SLOT, PrivateValue(source));
     sourceObject->initSlot(ELEMENT_SLOT, ObjectOrNullValue(options.element()));
     if (options.elementAttributeName())
         sourceObject->initSlot(ELEMENT_PROPERTY_SLOT, StringValue(options.elementAttributeName()));
     else
         sourceObject->initSlot(ELEMENT_PROPERTY_SLOT, UndefinedValue());
 
+    sourceObject->initSlot(INTRODUCTION_SCRIPT_SLOT, PrivateValue(nullptr));
+    sourceObject->initIntroductionScript(options.introductionScript());
+
     return sourceObject;
 }
 
 static const unsigned char emptySource[] = "";
 
 /* Adjust the amount of memory this script source uses for source data,
    reallocating if needed. */
 bool
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -552,35 +552,43 @@ class ScriptSourceHolder
     }
 };
 
 class ScriptSourceObject : public JSObject
 {
   public:
     static const Class class_;
 
+    static void trace(JSTracer *trc, JSObject *obj);
     static void finalize(FreeOp *fop, JSObject *obj);
     static ScriptSourceObject *create(ExclusiveContext *cx, ScriptSource *source,
                                       const ReadOnlyCompileOptions &options);
 
     ScriptSource *source() {
         return static_cast<ScriptSource *>(getReservedSlot(SOURCE_SLOT).toPrivate());
     }
 
     void setSource(ScriptSource *source);
 
     JSObject *element() const;
     void initElement(HandleObject element);
     const Value &elementAttributeName() const;
 
+    JSScript *introductionScript() const {
+        void *untyped = getReservedSlot(INTRODUCTION_SCRIPT_SLOT).toPrivate();
+        return static_cast<JSScript *>(untyped);
+    }
+    void initIntroductionScript(JSScript *script);
+
   private:
     static const uint32_t SOURCE_SLOT = 0;
     static const uint32_t ELEMENT_SLOT = 1;
     static const uint32_t ELEMENT_PROPERTY_SLOT = 2;
-    static const uint32_t RESERVED_SLOTS = 3;
+    static const uint32_t INTRODUCTION_SCRIPT_SLOT = 3;
+    static const uint32_t RESERVED_SLOTS = 4;
 };
 
 enum GeneratorKind { NotGenerator, LegacyGenerator, StarGenerator };
 
 static inline unsigned
 GeneratorKindAsBits(GeneratorKind generatorKind) {
     return static_cast<unsigned>(generatorKind);
 }
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -172,31 +172,34 @@ static const JSClass workerGlobalClass =
     JS_ConvertStub,   nullptr
 };
 
 ParseTask::ParseTask(ExclusiveContext *cx, JSObject *exclusiveContextGlobal, JSContext *initCx,
                      const jschar *chars, size_t length, JSObject *scopeChain,
                      JS::OffThreadCompileCallback callback, void *callbackData)
   : cx(cx), options(initCx), chars(chars), length(length),
     alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), scopeChain(initCx, scopeChain),
-    exclusiveContextGlobal(initCx, exclusiveContextGlobal), optionsElement(initCx), callback(callback),
-    callbackData(callbackData), script(nullptr), errors(cx), overRecursed(false)
+    exclusiveContextGlobal(initCx, exclusiveContextGlobal), optionsElement(initCx),
+    optionsIntroductionScript(initCx), callback(callback), callbackData(callbackData),
+    script(nullptr), errors(cx), overRecursed(false)
 {
 }
 
 bool
 ParseTask::init(JSContext *cx, const ReadOnlyCompileOptions &options)
 {
     if (!this->options.copy(cx, options))
         return false;
 
     // Save those compilation options that the ScriptSourceObject can't
     // point at while it's in the compilation's temporary compartment.
     optionsElement = this->options.element();
     this->options.setElement(nullptr);
+    optionsIntroductionScript = this->options.introductionScript();
+    this->options.setIntroductionScript(nullptr);
 
     return true;
 }
 
 void
 ParseTask::activate(JSRuntime *rt)
 {
     rt->setUsedByExclusiveThread(exclusiveContextGlobal->zone());
@@ -206,16 +209,17 @@ ParseTask::activate(JSRuntime *rt)
 void
 ParseTask::finish()
 {
     if (script) {
         // Initialize the ScriptSourceObject slots that we couldn't while the SSO
         // was in the temporary compartment.
         ScriptSourceObject &sso = script->sourceObject()->as<ScriptSourceObject>();
         sso.initElement(optionsElement);
+        sso.initIntroductionScript(optionsIntroductionScript);
     }
 }
 
 ParseTask::~ParseTask()
 {
     // ParseTask takes over ownership of its input exclusive context.
     js_delete(cx);
 
--- a/js/src/jsworkers.h
+++ b/js/src/jsworkers.h
@@ -400,16 +400,17 @@ struct ParseTask
     PersistentRootedObject exclusiveContextGlobal;
 
     // Saved GC-managed CompileOptions fields that will populate slots in
     // the ScriptSourceObject. We create the ScriptSourceObject in the
     // compilation's temporary compartment, so storing these values there
     // at that point would create cross-compartment references. Instead we
     // hold them here, and install them after merging the compartments.
     PersistentRootedObject optionsElement;
+    PersistentRootedScript optionsIntroductionScript;
 
     // Callback invoked off the main thread when the parse finishes.
     JS::OffThreadCompileCallback callback;
     void *callbackData;
 
     // Holds the final script between the invocation of the callback and the
     // point where FinishOffThreadScript is called, which will destroy the
     // ParseTask.