Bug 990353 - Make the decision to discard source entirely per-global, rather than per-script. r=luke
authorBobby Holley <bobbyholley@gmail.com>
Tue, 22 Apr 2014 14:08:27 -0700
changeset 179659 95391b25bc110b66652ced35c86f993a776b4952
parent 179658 31b79b2c4a7a1afb3c521a74564a520bf044cbd8
child 179660 26a22df461a295204f77b122230aec0bf00da687
push id26635
push usercbook@mozilla.com
push dateWed, 23 Apr 2014 12:51:44 +0000
treeherdermozilla-central@b747c35ac54b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs990353
milestone31.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 990353 - Make the decision to discard source entirely per-global, rather than per-script. r=luke This is effectively a policy decision based on the kind of code we expect to be running somewhere. This is in contrast to lazy source, which is often a practical per-script consideration of whether or not we can retrieve the source if requested. More importantly, tracking this information on the global is much easier to get right than tracking it on the script.
content/xul/content/src/nsXULElement.cpp
js/src/frontend/BytecodeCompiler.cpp
js/src/jit-test/tests/basic/withSourceHook.js
js/src/jit-test/tests/debug/Source-text-lazy.js
js/src/jsapi-tests/testSourcePolicy.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsfriendapi.h
js/src/shell/js.cpp
js/src/vm/SelfHosting.cpp
js/xpconnect/loader/mozJSComponentLoader.cpp
js/xpconnect/loader/mozJSSubScriptLoader.cpp
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -2640,18 +2640,17 @@ nsXULPrototypeScript::Compile(const char
     NS_ENSURE_TRUE(JSVersion(mLangVersion) != JSVERSION_UNKNOWN, NS_OK);
     JS::CompileOptions options(cx);
     options.setIntroductionType("scriptElement")
            .setFileAndLine(urlspec.get(), aLineNo)
            .setVersion(JSVersion(mLangVersion));
     // If the script was inline, tell the JS parser to save source for
     // Function.prototype.toSource(). If it's out of line, we retrieve the
     // source from the files on demand.
-    options.setSourcePolicy(mOutOfLine ? JS::CompileOptions::LAZY_SOURCE
-                                       : JS::CompileOptions::SAVE_SOURCE);
+    options.setSourceIsLazy(mOutOfLine);
     JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
     if (scope) {
       JS::ExposeObjectToActiveJS(scope);
     }
 
     if (aOffThreadReceiver && JS::CanCompileOffThread(cx, options, aTextLength)) {
         if (!JS::CompileOffThread(cx, options,
                                   static_cast<const jschar*>(aText), aTextLength,
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -141,17 +141,18 @@ MaybeCheckEvalFreeVariables(ExclusiveCon
     return true;
 }
 
 static inline bool
 CanLazilyParse(ExclusiveContext *cx, const ReadOnlyCompileOptions &options)
 {
     return options.canLazilyParse &&
         options.compileAndGo &&
-        options.sourcePolicy == CompileOptions::SAVE_SOURCE &&
+        !cx->compartment()->options().discardSource() &&
+        !options.sourceIsLazy &&
         !(cx->compartment()->debugMode() &&
           cx->compartment()->runtimeFromAnyThread()->debugHooks.newScriptHook);
 }
 
 void
 frontend::MaybeCallSourceHandler(JSContext *cx, const ReadOnlyCompileOptions &options,
                                  const jschar *chars, size_t length)
 {
@@ -207,37 +208,32 @@ frontend::CompileScript(ExclusiveContext
      * and non-zero static level requires callerFrame.
      */
     JS_ASSERT_IF(evalCaller, options.compileAndGo);
     JS_ASSERT_IF(evalCaller, options.forEval);
     JS_ASSERT_IF(staticLevel != 0, evalCaller);
 
     if (!CheckLength(cx, length))
         return nullptr;
-    JS_ASSERT_IF(staticLevel != 0, options.sourcePolicy != CompileOptions::LAZY_SOURCE);
+    JS_ASSERT_IF(staticLevel != 0, !options.sourceIsLazy);
 
     RootedScriptSource sourceObject(cx, CreateScriptSourceObject(cx, options));
     if (!sourceObject)
         return nullptr;
 
     ScriptSource *ss = sourceObject->source();
 
     SourceCompressionTask mysct(cx);
     SourceCompressionTask *sct = extraSct ? extraSct : &mysct;
 
-    switch (options.sourcePolicy) {
-      case CompileOptions::SAVE_SOURCE:
-        if (!ss->setSourceCopy(cx, chars, length, false, sct))
+    if (!cx->compartment()->options().discardSource()) {
+        if (options.sourceIsLazy)
+            ss->setSourceRetrievable();
+        else if (!ss->setSourceCopy(cx, chars, length, false, sct))
             return nullptr;
-        break;
-      case CompileOptions::LAZY_SOURCE:
-        ss->setSourceRetrievable();
-        break;
-      case CompileOptions::NO_SOURCE:
-        break;
     }
 
     bool canLazilyParse = CanLazilyParse(cx, options);
 
     Maybe<Parser<SyntaxParseHandler> > syntaxParser;
     if (canLazilyParse) {
         syntaxParser.construct(cx, alloc, options, chars, length, /* foldConstants = */ false,
                                (Parser<SyntaxParseHandler> *) nullptr,
@@ -512,18 +508,18 @@ CompileFunctionBody(JSContext *cx, Mutab
         return false;
 
     RootedScriptSource sourceObject(cx, CreateScriptSourceObject(cx, options));
     if (!sourceObject)
         return nullptr;
     ScriptSource *ss = sourceObject->source();
 
     SourceCompressionTask sct(cx);
-    JS_ASSERT(options.sourcePolicy != CompileOptions::LAZY_SOURCE);
-    if (options.sourcePolicy == CompileOptions::SAVE_SOURCE) {
+    JS_ASSERT(!options.sourceIsLazy);
+    if (!cx->compartment()->options().discardSource()) {
         if (!ss->setSourceCopy(cx, chars, length, true, &sct))
             return false;
     }
 
     bool canLazilyParse = CanLazilyParse(cx, options);
 
     Maybe<Parser<SyntaxParseHandler> > syntaxParser;
     if (canLazilyParse) {
--- a/js/src/jit-test/tests/basic/withSourceHook.js
+++ b/js/src/jit-test/tests/basic/withSourceHook.js
@@ -30,27 +30,27 @@ withSourceHook(function (url) {
       // and verify that it is in force.
       assertEq(withSourceHook(function (url) {
                                 log += 'i';
                                 assertEq(url, 'inner');
                                 return '(function inner() { 1; })';
                               }, function () {
                                 log += 'I';
                                 return evaluate('(function inner() { 2; })',
-                                                { fileName: 'inner', sourcePolicy: 'LAZY_SOURCE' })
+                                                { fileName: 'inner', sourceIsLazy: true })
                                        .toSource();
                               }),
                '(function inner() { 1; })');
       // Verify that the source hook that throws has been reinstated.
       evaluate('(function middle() { })',
-               { fileName: 'middle', sourcePolicy: 'LAZY_SOURCE' })
+               { fileName: 'middle', sourceIsLazy: true })
       .toSource();
     });
   }, 'borborygmus');
 
   // Verify that the outermost source hook has been restored.
   assertEq(evaluate('(function outer() { 4; })',
-                    { fileName: 'outer', sourcePolicy: 'LAZY_SOURCE' })
+                    { fileName: 'outer', sourceIsLazy: true })
            .toSource(),
            '(function outer() { 3; })');
 });
 
 assertEq(log, 'OMIimo');
--- a/js/src/jit-test/tests/debug/Source-text-lazy.js
+++ b/js/src/jit-test/tests/debug/Source-text-lazy.js
@@ -23,17 +23,17 @@ function test(source) {
     return frobbed;
   }, () => {
     dbg.onDebuggerStatement = function (frame) {
       log += 'd';
       assertEq(frame.script.source.text, frobbed);
     }
 
     g.evaluate(source, { fileName: "BanalBivalve.jsm",
-                         sourcePolicy: "LAZY_SOURCE"});
+                         sourceIsLazy: true });
   });
 
   assertEq(log, 'ds');
 }
 
 test("debugger; // Ignominious Iguana");
 test("(function () { debugger; /* Meretricious Marmoset */})();");
 test("(() => { debugger; })(); // Gaunt Gibbon");
--- a/js/src/jsapi-tests/testSourcePolicy.cpp
+++ b/js/src/jsapi-tests/testSourcePolicy.cpp
@@ -4,17 +4,17 @@
 
 #include "jsscript.h"
 
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testBug795104)
 {
     JS::CompileOptions opts(cx);
-    opts.setSourcePolicy(JS::CompileOptions::NO_SOURCE);
+    JS::CompartmentOptionsRef(cx->compartment()).setDiscardSource(true);
     const size_t strLen = 60002;
     char *s = static_cast<char *>(JS_malloc(cx, strLen));
     CHECK(s);
     s[0] = '"';
     memset(s + 1, 'x', strLen - 2);
     s[strLen - 1] = '"';
     CHECK(JS::Evaluate(cx, global, opts, s, strLen));
     CHECK(JS::CompileFunction(cx, global, opts, "f", 0, nullptr, s, strLen));
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2475,16 +2475,22 @@ JS::CompartmentOptions::setSameZoneAs(JS
 
 JS::CompartmentOptions &
 JS::CompartmentOptionsRef(JSCompartment *compartment)
 {
     return compartment->options();
 }
 
 JS::CompartmentOptions &
+JS::CompartmentOptionsRef(JSObject *obj)
+{
+    return obj->compartment()->options();
+}
+
+JS::CompartmentOptions &
 JS::CompartmentOptionsRef(JSContext *cx)
 {
     return cx->compartment()->options();
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewGlobalObject(JSContext *cx, const JSClass *clasp, JSPrincipals *principals,
                    JS::OnNewGlobalHookOption hookOption,
@@ -4343,17 +4349,17 @@ JS::ReadOnlyCompileOptions::copyPODOptio
     selfHostingMode = rhs.selfHostingMode;
     canLazilyParse = rhs.canLazilyParse;
     strictOption = rhs.strictOption;
     extraWarningsOption = rhs.extraWarningsOption;
     werrorOption = rhs.werrorOption;
     asmJSOption = rhs.asmJSOption;
     forceAsync = rhs.forceAsync;
     installedFile = rhs.installedFile;
-    sourcePolicy = rhs.sourcePolicy;
+    sourceIsLazy = rhs.sourceIsLazy;
     introductionType = rhs.introductionType;
     introductionLineno = rhs.introductionLineno;
     introductionOffset = rhs.introductionOffset;
     hasIntroductionInfo = rhs.hasIntroductionInfo;
 }
 
 JSPrincipals *
 JS::ReadOnlyCompileOptions::originPrincipals(ExclusiveContext *cx) const
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2530,16 +2530,17 @@ class JS_PUBLIC_API(CompartmentOptions)
 
         Mode mode_;
     };
 
     explicit CompartmentOptions()
       : version_(JSVERSION_UNKNOWN)
       , invisibleToDebugger_(false)
       , mergeable_(false)
+      , discardSource_(false)
       , traceGlobal_(nullptr)
       , singletonsAsTemplates_(true)
     {
         zone_.spec = JS::FreshZone;
     }
 
     JSVersion version() const { return version_; }
     CompartmentOptions &setVersion(JSVersion aVersion) {
@@ -2563,16 +2564,25 @@ class JS_PUBLIC_API(CompartmentOptions)
     // allowed if this flag is set.  The invisibleToDebugger flag must also be
     // set for such compartments.
     bool mergeable() const { return mergeable_; }
     CompartmentOptions &setMergeable(bool flag) {
         mergeable_ = flag;
         return *this;
     }
 
+    // For certain globals, we know enough about the code that will run in them
+    // that we can discard script source entirely.
+    bool discardSource() const { return discardSource_; }
+    CompartmentOptions &setDiscardSource(bool flag) {
+        discardSource_ = flag;
+        return *this;
+    }
+
+
     bool cloneSingletons(JSContext *cx) const;
     Override &cloneSingletonsOverride() { return cloneSingletonsOverride_; }
 
     void *zonePointer() const {
         JS_ASSERT(uintptr_t(zone_.pointer) > uintptr_t(JS::SystemZone));
         return zone_.pointer;
     }
     ZoneSpecifier zoneSpecifier() const { return zone_.spec; }
@@ -2593,16 +2603,17 @@ class JS_PUBLIC_API(CompartmentOptions)
     JSTraceOp getTrace() const {
         return traceGlobal_;
     }
 
   private:
     JSVersion version_;
     bool invisibleToDebugger_;
     bool mergeable_;
+    bool discardSource_;
     Override cloneSingletonsOverride_;
     union {
         ZoneSpecifier spec;
         void *pointer; // js::Zone* is not exposed in the API.
     } zone_;
     JSTraceOp traceGlobal_;
 
     // To XDR singletons, we need to ensure that all singletons are all used as
@@ -2610,16 +2621,19 @@ class JS_PUBLIC_API(CompartmentOptions)
     // singleton, instead of returning the value which is baked in the JSScript.
     bool singletonsAsTemplates_;
 };
 
 JS_PUBLIC_API(CompartmentOptions &)
 CompartmentOptionsRef(JSCompartment *compartment);
 
 JS_PUBLIC_API(CompartmentOptions &)
+CompartmentOptionsRef(JSObject *obj);
+
+JS_PUBLIC_API(CompartmentOptions &)
 CompartmentOptionsRef(JSContext *cx);
 
 // During global creation, we fire notifications to callbacks registered
 // via the Debugger API. These callbacks are arbitrary script, and can touch
 // the global in arbitrary ways. When that happens, the global should not be
 // in a half-baked state. But this creates a problem for consumers that need
 // to set slots on the global to put it in a consistent state.
 //
@@ -3383,17 +3397,17 @@ class JS_FRIEND_API(ReadOnlyCompileOptio
         selfHostingMode(false),
         canLazilyParse(true),
         strictOption(false),
         extraWarningsOption(false),
         werrorOption(false),
         asmJSOption(false),
         forceAsync(false),
         installedFile(false),
-        sourcePolicy(SAVE_SOURCE),
+        sourceIsLazy(false),
         introductionType(nullptr),
         introductionLineno(0),
         introductionOffset(0),
         hasIntroductionInfo(false)
     { }
 
     // Set all POD options (those not requiring reference counts, copies,
     // rooting, or other hand-holding) to their values in |rhs|.
@@ -3423,21 +3437,17 @@ class JS_FRIEND_API(ReadOnlyCompileOptio
     bool selfHostingMode;
     bool canLazilyParse;
     bool strictOption;
     bool extraWarningsOption;
     bool werrorOption;
     bool asmJSOption;
     bool forceAsync;
     bool installedFile;  // 'true' iff pre-compiling js file in packaged app
-    enum SourcePolicy {
-        NO_SOURCE,
-        LAZY_SOURCE,
-        SAVE_SOURCE
-    } sourcePolicy;
+    bool sourceIsLazy;
 
     // |introductionType| is a statically allocated C string:
     // one of "eval", "Function", or "GeneratorFunction".
     const char *introductionType;
     unsigned introductionLineno;
     uint32_t introductionOffset;
     bool hasIntroductionInfo;
 
@@ -3519,17 +3529,17 @@ class JS_FRIEND_API(OwningCompileOptions
     OwningCompileOptions &setUTF8(bool u) { utf8 = u; return *this; }
     OwningCompileOptions &setColumn(unsigned c) { column = c; return *this; }
     OwningCompileOptions &setCompileAndGo(bool cng) { compileAndGo = cng; return *this; }
     OwningCompileOptions &setForEval(bool eval) { forEval = eval; return *this; }
     OwningCompileOptions &setDefineOnScope(bool define) { defineOnScope = define; return *this; }
     OwningCompileOptions &setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; }
     OwningCompileOptions &setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
     OwningCompileOptions &setCanLazilyParse(bool clp) { canLazilyParse = clp; return *this; }
-    OwningCompileOptions &setSourcePolicy(SourcePolicy sp) { sourcePolicy = sp; return *this; }
+    OwningCompileOptions &setSourceIsLazy(bool l) { sourceIsLazy = l; return *this; }
     OwningCompileOptions &setIntroductionType(const char *t) { introductionType = t; return *this; }
     bool setIntroductionInfo(JSContext *cx, const char *introducerFn, const char *intro,
                              unsigned line, JSScript *script, uint32_t offset)
     {
         if (!setIntroducerFilename(cx, introducerFn))
             return false;
         introductionType = intro;
         introductionLineno = line;
@@ -3605,17 +3615,17 @@ class MOZ_STACK_CLASS JS_FRIEND_API(Comp
     CompileOptions &setUTF8(bool u) { utf8 = u; return *this; }
     CompileOptions &setColumn(unsigned c) { column = c; return *this; }
     CompileOptions &setCompileAndGo(bool cng) { compileAndGo = cng; return *this; }
     CompileOptions &setForEval(bool eval) { forEval = eval; return *this; }
     CompileOptions &setDefineOnScope(bool define) { defineOnScope = define; return *this; }
     CompileOptions &setNoScriptRval(bool nsr) { noScriptRval = nsr; return *this; }
     CompileOptions &setSelfHostingMode(bool shm) { selfHostingMode = shm; return *this; }
     CompileOptions &setCanLazilyParse(bool clp) { canLazilyParse = clp; return *this; }
-    CompileOptions &setSourcePolicy(SourcePolicy sp) { sourcePolicy = sp; return *this; }
+    CompileOptions &setSourceIsLazy(bool l) { sourceIsLazy = l; return *this; }
     CompileOptions &setIntroductionType(const char *t) { introductionType = t; return *this; }
     CompileOptions &setIntroductionInfo(const char *introducerFn, const char *intro,
                                         unsigned line, JSScript *script, uint32_t offset)
     {
         introducerFilename_ = introducerFn;
         introductionType = intro;
         introductionLineno = line;
         introductionScriptRoot = script;
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -378,38 +378,38 @@ extern JS_FRIEND_API(bool)
 proxy_Unwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id);
 extern JS_FRIEND_API(bool)
 proxy_Slice(JSContext *cx, JS::HandleObject proxy, uint32_t begin, uint32_t end,
             JS::HandleObject result);
 
 /*
  * A class of objects that return source code on demand.
  *
- * When code is compiled with CompileOptions::LAZY_SOURCE, SpiderMonkey
- * doesn't retain the source code (and doesn't do lazy bytecode
- * generation). If we ever need the source code, say, in response to a call
- * to Function.prototype.toSource or Debugger.Source.prototype.text, then
- * we call the 'load' member function of the instance of this class that
- * has hopefully been registered with the runtime, passing the code's URL,
- * and hope that it will be able to find the source.
+ * When code is compiled with setSourceIsLazy(true), SpiderMonkey doesn't
+ * retain the source code (and doesn't do lazy bytecode generation). If we ever
+ * need the source code, say, in response to a call to Function.prototype.
+ * toSource or Debugger.Source.prototype.text, then we call the 'load' member
+ * function of the instance of this class that has hopefully been registered
+ * with the runtime, passing the code's URL, and hope that it will be able to
+ * find the source.
  */
 class SourceHook {
   public:
     virtual ~SourceHook() { }
 
     /*
      * Set |*src| and |*length| to refer to the source code for |filename|.
      * On success, the caller owns the buffer to which |*src| points, and
      * should use JS_free to free it.
      */
     virtual bool load(JSContext *cx, const char *filename, jschar **src, size_t *length) = 0;
 };
 
 /*
- * Have |rt| use |hook| to retrieve LAZY_SOURCE source code. See the
+ * Have |rt| use |hook| to retrieve lazily-retrieved source code. See the
  * comments for SourceHook. The runtime takes ownership of the hook, and
  * will delete it when the runtime itself is deleted, or when a new hook is
  * set.
  */
 extern JS_FRIEND_API(void)
 SetSourceHook(JSRuntime *rt, SourceHook *hook);
 
 /* Remove |rt|'s source hook, and return it. The caller now owns the hook. */
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -861,38 +861,20 @@ ParseCompileOptions(JSContext *cx, Compi
         return false;
     if (!v.isUndefined()) {
         uint32_t u;
         if (!ToUint32(cx, v, &u))
             return false;
         options.setLine(u);
     }
 
-    if (!JS_GetProperty(cx, opts, "sourcePolicy", &v))
-        return false;
-    if (!v.isUndefined()) {
-        RootedString s(cx, ToString(cx, v));
-        if (!s)
-            return false;
-
-        JSAutoByteString bytes;
-        char *policy = bytes.encodeUtf8(cx, s);
-        if (!policy)
-            return false;
-        if (strcmp(policy, "NO_SOURCE") == 0) {
-            options.setSourcePolicy(CompileOptions::NO_SOURCE);
-        } else if (strcmp(policy, "LAZY_SOURCE") == 0) {
-            options.setSourcePolicy(CompileOptions::LAZY_SOURCE);
-        } else if (strcmp(policy, "SAVE_SOURCE") == 0) {
-            options.setSourcePolicy(CompileOptions::SAVE_SOURCE);
-        } else {
-            JS_ReportError(cx, "bad 'sourcePolicy' option: '%s'", policy);
-            return false;
-        }
-    }
+    if (!JS_GetProperty(cx, opts, "sourceIsLazy", &v))
+        return false;
+    if (v.isBoolean())
+        options.setSourceIsLazy(v.toBoolean());
 
     return true;
 }
 
 class AutoNewContext
 {
   private:
     JSContext *oldcx;
@@ -3631,17 +3613,17 @@ OffThreadCompileScript(JSContext *cx, un
 
         RootedObject opts(cx, &args[1].toObject());
         if (!ParseCompileOptions(cx, options, opts, fileNameBytes))
             return false;
     }
 
     // These option settings must override whatever the caller requested.
     options.setCompileAndGo(true)
-           .setSourcePolicy(CompileOptions::SAVE_SOURCE);
+           .setSourceIsLazy(false);
 
     // We assume the caller wants caching if at all possible, ignoring
     // heuristics that make sense for a real browser.
     options.forceAsync = true;
 
     JSString *scriptContents = args[0].toString();
     const jschar *chars = JS_GetStringCharsZ(cx, scriptContents);
     if (!chars)
@@ -4384,19 +4366,19 @@ static const JSFunctionSpecWithHelp shel
 "         any DOM element.\n"
 "      elementAttributeName: if present and not undefined, the name of\n"
 "         property of 'element' that holds this code. This is what\n"
 "         Debugger.Source.prototype.elementAttributeName returns.\n"
 "      sourceMapURL: if present with value |v|, convert |v| to a string, and\n"
 "         provide that as the code's source map URL. If omitted, attach no\n"
 "         source map URL to the code (although the code may provide one itself,\n"
 "         via a //#sourceMappingURL comment).\n"
-"      sourcePolicy: if present, the value converted to a string must be either\n"
-"         'NO_SOURCE', 'LAZY_SOURCE', or 'SAVE_SOURCE'; use the given source\n"
-"         retention policy for this compilation.\n"
+"      sourceIsLazy: if present and true, indicates that, after compilation, \n"
+          "script source should not be cached by the JS engine and should be \n"
+          "lazily loaded from the embedding as-needed.\n"
 "      loadBytecode: if true, and if the source is a CacheEntryObject,\n"
 "         the bytecode would be loaded and decoded from the cache entry instead\n"
 "         of being parsed, then it would be executed as usual.\n"
 "      saveBytecode: if true, and if the source is a CacheEntryObject,\n"
 "         the bytecode would be encoded and saved into the cache entry after\n"
 "         the script execution.\n"
 "      assertEqBytecode: if true, and if both loadBytecode and saveBytecode are \n"
 "         true, then the loaded bytecode and the encoded bytecode are compared.\n"
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -899,17 +899,16 @@ js::FillSelfHostingCompileOptions(Compil
      * Additionally, the special syntax callFunction(fun, receiver, ...args)
      * is supported, for which bytecode is emitted that invokes |fun| with
      * |receiver| as the this-object and ...args as the arguments.
      */
     options.setIntroductionType("self-hosted");
     options.setFileAndLine("self-hosted", 1);
     options.setSelfHostingMode(true);
     options.setCanLazilyParse(false);
-    options.setSourcePolicy(CompileOptions::NO_SOURCE);
     options.setVersion(JSVERSION_LATEST);
     options.werrorOption = true;
     options.strictOption = true;
 
 #ifdef DEBUG
     options.extraWarningsOption = true;
 #endif
 }
@@ -929,18 +928,21 @@ JSRuntime::initSelfHosting(JSContext *cx
      * parented to this one, so cannot include state in the nursery.
      */
     JS::AutoDisableGenerationalGC disable(cx->runtime());
 
     bool receivesDefaultObject = !cx->options().noDefaultCompartmentObject();
     RootedObject savedGlobal(cx, receivesDefaultObject
                                  ? js::DefaultObjectForContextOrNull(cx)
                                  : nullptr);
+    JS::CompartmentOptions compartmentOptions;
+    compartmentOptions.setDiscardSource(true);
     if (!(selfHostingGlobal_ = JS_NewGlobalObject(cx, &self_hosting_global_class,
-                                                  nullptr, JS::DontFireOnNewGlobalHook)))
+                                                  nullptr, JS::DontFireOnNewGlobalHook,
+                                                  compartmentOptions)))
         return false;
     JSAutoCompartment ac(cx, selfHostingGlobal_);
     if (receivesDefaultObject)
         js::SetDefaultObjectForContext(cx, selfHostingGlobal_);
     Rooted<GlobalObject*> shg(cx, &selfHostingGlobal_->as<GlobalObject>());
     selfHostingGlobal_->compartment()->isSelfHosting = true;
     selfHostingGlobal_->compartment()->isSystem = true;
     /*
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -809,23 +809,26 @@ mozJSComponentLoader::ObjectForLocation(
 
         // If aPropagateExceptions is true, then our caller wants us to propagate
         // any exceptions out to our caller. Ensure that the engine doesn't
         // eagerly report the exception.
         AutoSaveContextOptions asco(cx);
         if (aPropagateExceptions)
             ContextOptionsRef(cx).setDontReportUncaught(true);
 
+        // Note - if mReuseLoaderGlobal is true, then we can't do lazy source,
+        // because we compile things as functions (rather than script), and lazy
+        // source isn't supported in that configuration. That's ok though,
+        // because we only do mReuseLoaderGlobal on b2g, where we invoke
+        // setDiscardSource(true) on the entire global.
         CompileOptions options(cx);
         options.setNoScriptRval(mReuseLoaderGlobal ? false : true)
                .setVersion(JSVERSION_LATEST)
                .setFileAndLine(nativePath.get(), 1)
-               .setSourcePolicy(mReuseLoaderGlobal ?
-                                CompileOptions::NO_SOURCE :
-                                CompileOptions::LAZY_SOURCE);
+               .setSourceIsLazy(!mReuseLoaderGlobal);
 
         if (realFile) {
 #ifdef HAVE_PR_MEMMAP
             int64_t fileSize;
             rv = aComponentFile->GetFileSize(&fileSize);
             if (NS_FAILED(rv)) {
                 return rv;
             }
--- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -159,20 +159,20 @@ mozJSSubScriptLoader::ReadScript(nsIURI 
                                    script.Length());
         } else {
             *functionp = JS::CompileFunction(cx, target_obj, options,
                                              nullptr, 0, nullptr,
                                              script.get(),
                                              script.Length());
         }
     } else {
-        // We only use LAZY_SOURCE when no special encoding is specified because
+        // We only use lazy source when no special encoding is specified because
         // the lazy source loader doesn't know the encoding.
         if (!reuseGlobal) {
-            options.setSourcePolicy(JS::CompileOptions::LAZY_SOURCE);
+            options.setSourceIsLazy(true);
             *scriptp = JS::Compile(cx, target_obj, options, buf.get(), len);
         } else {
             *functionp = JS::CompileFunction(cx, target_obj, options,
                                              nullptr, 0, nullptr, buf.get(),
                                              len);
         }
     }
 
@@ -493,17 +493,16 @@ ScriptPrecompiler::OnStreamComplete(nsIS
     sandboxOptions.sandboxName.AssignASCII("asm.js precompilation");
     sandboxOptions.invisibleToDebugger = true;
     rv = CreateSandboxObject(cx, &v, mPrincipal, sandboxOptions);
     NS_ENSURE_SUCCESS(rv, NS_OK);
 
     JSAutoCompartment ac(cx, js::UncheckedUnwrap(&v.toObject()));
 
     JS::CompileOptions options(cx, JSVERSION_DEFAULT);
-    options.setSourcePolicy(CompileOptions::NO_SOURCE);
     options.forceAsync = true;
     options.compileAndGo = true;
     options.installedFile = true;
 
     nsCOMPtr<nsIURI> uri;
     mChannel->GetURI(getter_AddRefs(uri));
     nsAutoCString spec;
     uri->GetSpec(spec);