--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -47,16 +47,17 @@
#include "mozilla/AddonPathService.h"
#include "mozilla/scache/StartupCache.h"
#include "mozilla/scache/StartupCacheUtils.h"
#include "mozilla/MacroForEach.h"
#include "mozilla/Preferences.h"
#include "mozilla/ScriptPreloader.h"
#include "mozilla/dom/DOMPrefs.h"
#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/ResultExtensions.h"
#include "mozilla/UniquePtrExtensions.h"
#include "mozilla/Unused.h"
using namespace mozilla;
using namespace mozilla::scache;
using namespace mozilla::loader;
using namespace xpc;
using namespace JS;
@@ -175,35 +176,16 @@ ReportOnCallerUTF8(JSContext* callerCont
}
JS_ReportErrorUTF8(callerContext, "%s", buf.get());
va_end(ap);
return NS_OK;
}
-static nsresult
-MOZ_FORMAT_PRINTF(2, 3)
-ReportOnCallerUTF8(JSCLContextHelper& helper,
- const char* format, ...)
-{
- va_list ap;
- va_start(ap, format);
-
- UniqueChars buf = JS_vsmprintf(format, ap);
- if (!buf) {
- va_end(ap);
- return NS_ERROR_OUT_OF_MEMORY;
- }
-
- helper.reportErrorAfterPop(Move(buf));
- va_end(ap);
- return NS_OK;
-}
-
mozJSComponentLoader::mozJSComponentLoader()
: mModules(16),
mImports(16),
mInProgressImports(16),
mLocations(16),
mInitialized(false),
mShareLoaderGlobal(false),
mLoaderGlobal(dom::RootingCx())
@@ -282,16 +264,35 @@ class MOZ_STACK_CLASS ComponentLoaderInf
private:
const nsACString& mLocation;
nsCOMPtr<nsIIOService> mIOService;
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIChannel> mScriptChannel;
nsCOMPtr<nsIURI> mResolvedURI;
};
+template <typename ...Args>
+static nsresult
+ReportOnCallerUTF8(JSCLContextHelper& helper,
+ const char* format,
+ ComponentLoaderInfo& info,
+ Args... args)
+{
+ nsCString location;
+ MOZ_TRY(info.GetLocation(location));
+
+ UniqueChars buf = JS_smprintf(format, location.get(), args...);
+ if (!buf) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ helper.reportErrorAfterPop(Move(buf));
+ return NS_ERROR_FAILURE;
+}
+
#undef BEGIN_ENSURE
#undef ENSURE_DEPS
#undef ENSURE_DEP
mozJSComponentLoader::~mozJSComponentLoader()
{
if (mInitialized) {
NS_ERROR("'xpcom-shutdown-loaders' was not fired before cleaning up mozJSComponentLoader");
@@ -923,21 +924,21 @@ mozJSComponentLoader::UnloadModules()
for (auto iter = mModules.Iter(); !iter.Done(); iter.Next()) {
iter.Data()->Clear();
iter.Remove();
}
}
nsresult
-mozJSComponentLoader::Import(const nsACString& registryLocation,
- HandleValue targetValArg,
- JSContext* cx,
- uint8_t optionalArgc,
- MutableHandleValue retval)
+mozJSComponentLoader::ImportInto(const nsACString& registryLocation,
+ HandleValue targetValArg,
+ JSContext* cx,
+ uint8_t optionalArgc,
+ MutableHandleValue retval)
{
MOZ_ASSERT(nsContentUtils::IsCallerChrome());
RootedValue targetVal(cx, targetValArg);
RootedObject targetObject(cx, nullptr);
if (optionalArgc) {
// The caller passed in the optional second argument. Get it.
if (targetVal.isObject()) {
@@ -1092,36 +1093,178 @@ ResolveModuleObjectPropertyById(JSContex
}
}
return aModObj;
}
nsresult
mozJSComponentLoader::ImportInto(const nsACString& aLocation,
HandleObject targetObj,
- JSContext* callercx,
+ JSContext* cx,
MutableHandleObject vp)
{
vp.set(nullptr);
+ JS::RootedObject exports(cx);
+ MOZ_TRY(Import(cx, aLocation, vp, &exports, !targetObj));
+
+ if (targetObj) {
+ JS::Rooted<JS::IdVector> ids(cx, JS::IdVector(cx));
+ if (!JS_Enumerate(cx, exports, &ids)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ JS::RootedValue value(cx);
+ JS::RootedId id(cx);
+ for (jsid idVal : ids) {
+ id = idVal;
+ if (!JS_GetPropertyById(cx, exports, id, &value) ||
+ !JS_SetPropertyById(cx, targetObj, id, value)) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+mozJSComponentLoader::ExtractExports(JSContext* aCx, ComponentLoaderInfo& aInfo,
+ ModuleEntry* aMod,
+ JS::MutableHandleObject aExports)
+{
+ // cxhelper must be created before jsapi, so that jsapi is destroyed and
+ // pops any context it has pushed before we report to the caller context.
+ JSCLContextHelper cxhelper(aCx);
+
+ // Even though we are calling JS_SetPropertyById on targetObj, we want
+ // to ensure that we never run script here, so we use an AutoJSAPI and
+ // not an AutoEntryScript.
+ dom::AutoJSAPI jsapi;
+ jsapi.Init();
+ JSContext* cx = jsapi.cx();
+ JSAutoCompartment ac(cx, aMod->obj);
+
+ RootedValue symbols(cx);
+ {
+ RootedObject obj(cx, ResolveModuleObjectProperty(cx, aMod->obj,
+ "EXPORTED_SYMBOLS"));
+ if (!obj || !JS_GetProperty(cx, obj, "EXPORTED_SYMBOLS", &symbols)) {
+ return ReportOnCallerUTF8(cxhelper, ERROR_NOT_PRESENT, aInfo);
+ }
+ }
+
+ bool isArray;
+ if (!JS_IsArrayObject(cx, symbols, &isArray)) {
+ return NS_ERROR_FAILURE;
+ }
+ if (!isArray) {
+ return ReportOnCallerUTF8(cxhelper, ERROR_NOT_AN_ARRAY, aInfo);
+ }
+
+ RootedObject symbolsObj(cx, &symbols.toObject());
+
+ // Iterate over symbols array, installing symbols on targetObj:
+
+ uint32_t symbolCount = 0;
+ if (!JS_GetArrayLength(cx, symbolsObj, &symbolCount)) {
+ return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_ARRAY_LENGTH,
+ aInfo);
+ }
+
+#ifdef DEBUG
+ nsAutoCString logBuffer;
+#endif
+
+ aExports.set(JS_NewPlainObject(cx));
+ if (!aExports) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ bool missing = false;
+
+ RootedValue value(cx);
+ RootedId symbolId(cx);
+ RootedObject symbolHolder(cx);
+ for (uint32_t i = 0; i < symbolCount; ++i) {
+ if (!JS_GetElement(cx, symbolsObj, i, &value) ||
+ !value.isString() ||
+ !JS_ValueToId(cx, value, &symbolId)) {
+ return ReportOnCallerUTF8(cxhelper, ERROR_ARRAY_ELEMENT, aInfo, i);
+ }
+
+ symbolHolder = ResolveModuleObjectPropertyById(cx, aMod->obj, symbolId);
+ if (!symbolHolder ||
+ !JS_GetPropertyById(cx, symbolHolder, symbolId, &value)) {
+ JSAutoByteString bytes;
+ RootedString symbolStr(cx, JSID_TO_STRING(symbolId));
+ if (!bytes.encodeUtf8(cx, symbolStr))
+ return NS_ERROR_FAILURE;
+ return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_SYMBOL,
+ aInfo, bytes.ptr());
+ }
+
+ if (value.isUndefined()) {
+ missing = true;
+ }
+
+ if (!JS_SetPropertyById(cx, aExports, symbolId, value)) {
+ JSAutoByteString bytes;
+ RootedString symbolStr(cx, JSID_TO_STRING(symbolId));
+ if (!bytes.encodeUtf8(cx, symbolStr))
+ return NS_ERROR_FAILURE;
+ return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_SYMBOL,
+ aInfo, bytes.ptr());
+ }
+#ifdef DEBUG
+ if (i == 0) {
+ logBuffer.AssignLiteral("Installing symbols [ ");
+ }
+ JSAutoByteString bytes(cx, JSID_TO_STRING(symbolId));
+ if (!!bytes)
+ logBuffer.Append(bytes.ptr());
+ logBuffer.Append(' ');
+ if (i == symbolCount - 1) {
+ nsCString location;
+ MOZ_TRY(aInfo.GetLocation(location));
+ LOG(("%s] from %s\n", logBuffer.get(), location.get()));
+ }
+#endif
+ }
+
+ // Don't cache the exports object if any of its exported symbols are
+ // missing. If the module hasn't finished loading yet, they may be
+ // defined the next time we try to import it.
+ if (!missing) {
+ aMod->exports = aExports;
+ }
+ return NS_OK;
+}
+
+nsresult
+mozJSComponentLoader::Import(JSContext* aCx, const nsACString& aLocation,
+ JS::MutableHandleObject aModuleGlobal,
+ JS::MutableHandleObject aModuleExports,
+ bool aIgnoreExports)
+{
nsresult rv;
if (!mInitialized) {
rv = ReallyInit();
NS_ENSURE_SUCCESS(rv, rv);
}
ComponentLoaderInfo info(aLocation);
rv = info.EnsureKey();
NS_ENSURE_SUCCESS(rv, rv);
ModuleEntry* mod;
nsAutoPtr<ModuleEntry> newEntry;
if (!mImports.Get(info.Key(), &mod) && !mInProgressImports.Get(info.Key(), &mod)) {
- newEntry = new ModuleEntry(RootingContext::get(callercx));
+ newEntry = new ModuleEntry(RootingContext::get(aCx));
if (!newEntry)
return NS_ERROR_OUT_OF_MEMORY;
rv = info.EnsureResolvedURI();
NS_ENSURE_SUCCESS(rv, rv);
// get the JAR if there is one
nsCOMPtr<nsIJARURI> jarURI;
@@ -1152,166 +1295,59 @@ mozJSComponentLoader::ImportInto(const n
return NS_ERROR_UNEXPECTED;
}
mLocations.Put(newEntry->resolvedURL, new nsCString(info.Key()));
mInProgressImports.Put(info.Key(), newEntry);
rv = info.EnsureURI();
NS_ENSURE_SUCCESS(rv, rv);
- RootedValue exception(callercx);
+ RootedValue exception(aCx);
rv = ObjectForLocation(info, sourceFile, &newEntry->obj,
&newEntry->thisObjectKey,
&newEntry->location, true, &exception);
mInProgressImports.Remove(info.Key());
if (NS_FAILED(rv)) {
if (!exception.isUndefined()) {
// An exception was thrown during compilation. Propagate it
// out to our caller so they can report it.
- if (!JS_WrapValue(callercx, &exception))
+ if (!JS_WrapValue(aCx, &exception))
return NS_ERROR_OUT_OF_MEMORY;
- JS_SetPendingException(callercx, exception);
- return NS_OK;
+ JS_SetPendingException(aCx, exception);
+ return NS_ERROR_FAILURE;
}
// Something failed, but we don't know what it is, guess.
return NS_ERROR_FILE_NOT_FOUND;
}
#ifdef STARTUP_RECORDER_ENABLED
if (Preferences::GetBool("browser.startup.record", false)) {
newEntry->importStack =
- xpc_PrintJSStack(callercx, false, false, false).get();
+ xpc_PrintJSStack(aCx, false, false, false).get();
}
#endif
mod = newEntry;
}
MOZ_ASSERT(mod->obj, "Import table contains entry with no object");
- vp.set(mod->obj);
-
- if (targetObj) {
- // cxhelper must be created before jsapi, so that jsapi is destroyed and
- // pops any context it has pushed before we report to the caller context.
- JSCLContextHelper cxhelper(callercx);
-
- // Even though we are calling JS_SetPropertyById on targetObj, we want
- // to ensure that we never run script here, so we use an AutoJSAPI and
- // not an AutoEntryScript.
- dom::AutoJSAPI jsapi;
- jsapi.Init();
- JSContext* cx = jsapi.cx();
- JSAutoCompartment ac(cx, mod->obj);
-
- RootedValue symbols(cx);
- RootedObject exportedSymbolsHolder(cx, ResolveModuleObjectProperty(cx, mod->obj,
- "EXPORTED_SYMBOLS"));
- if (!exportedSymbolsHolder ||
- !JS_GetProperty(cx, exportedSymbolsHolder,
- "EXPORTED_SYMBOLS", &symbols)) {
- nsCString location;
- rv = info.GetLocation(location);
- NS_ENSURE_SUCCESS(rv, rv);
- return ReportOnCallerUTF8(cxhelper, ERROR_NOT_PRESENT,
- location.get());
- }
-
- bool isArray;
- if (!JS_IsArrayObject(cx, symbols, &isArray)) {
- return NS_ERROR_FAILURE;
- }
- if (!isArray) {
- nsCString location;
- rv = info.GetLocation(location);
- NS_ENSURE_SUCCESS(rv, rv);
- return ReportOnCallerUTF8(cxhelper, ERROR_NOT_AN_ARRAY,
- location.get());
- }
-
- RootedObject symbolsObj(cx, &symbols.toObject());
-
- // Iterate over symbols array, installing symbols on targetObj:
-
- uint32_t symbolCount = 0;
- if (!JS_GetArrayLength(cx, symbolsObj, &symbolCount)) {
- nsCString location;
- rv = info.GetLocation(location);
- NS_ENSURE_SUCCESS(rv, rv);
- return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_ARRAY_LENGTH,
- location.get());
- }
-
-#ifdef DEBUG
- nsAutoCString logBuffer;
-#endif
+ aModuleGlobal.set(mod->obj);
- RootedValue value(cx);
- RootedId symbolId(cx);
- RootedObject symbolHolder(cx);
- for (uint32_t i = 0; i < symbolCount; ++i) {
- if (!JS_GetElement(cx, symbolsObj, i, &value) ||
- !value.isString() ||
- !JS_ValueToId(cx, value, &symbolId)) {
- nsCString location;
- rv = info.GetLocation(location);
- NS_ENSURE_SUCCESS(rv, rv);
- return ReportOnCallerUTF8(cxhelper, ERROR_ARRAY_ELEMENT,
- location.get(), i);
- }
-
- symbolHolder = ResolveModuleObjectPropertyById(cx, mod->obj, symbolId);
- if (!symbolHolder ||
- !JS_GetPropertyById(cx, symbolHolder, symbolId, &value)) {
- JSAutoByteString bytes;
- RootedString symbolStr(cx, JSID_TO_STRING(symbolId));
- if (!bytes.encodeUtf8(cx, symbolStr))
- return NS_ERROR_FAILURE;
- nsCString location;
- rv = info.GetLocation(location);
- NS_ENSURE_SUCCESS(rv, rv);
- return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_SYMBOL,
- location.get(), bytes.ptr());
- }
-
- JSAutoCompartment target_ac(cx, targetObj);
+ JS::RootedObject exports(aCx, mod->exports);
+ if (!exports && !aIgnoreExports) {
+ MOZ_TRY(ExtractExports(aCx, info, mod, &exports));
+ }
- JS_MarkCrossZoneId(cx, symbolId);
-
- if (!JS_WrapValue(cx, &value) ||
- !JS_SetPropertyById(cx, targetObj, symbolId, value)) {
- JSAutoByteString bytes;
- RootedString symbolStr(cx, JSID_TO_STRING(symbolId));
- if (!bytes.encodeUtf8(cx, symbolStr))
- return NS_ERROR_FAILURE;
- nsCString location;
- rv = info.GetLocation(location);
- NS_ENSURE_SUCCESS(rv, rv);
- return ReportOnCallerUTF8(cxhelper, ERROR_SETTING_SYMBOL,
- location.get(), bytes.ptr());
- }
-#ifdef DEBUG
- if (i == 0) {
- logBuffer.AssignLiteral("Installing symbols [ ");
- }
- JSAutoByteString bytes(cx, JSID_TO_STRING(symbolId));
- if (!!bytes)
- logBuffer.Append(bytes.ptr());
- logBuffer.Append(' ');
- if (i == symbolCount - 1) {
- nsCString location;
- rv = info.GetLocation(location);
- NS_ENSURE_SUCCESS(rv, rv);
- LOG(("%s] from %s\n", logBuffer.get(), location.get()));
- }
-#endif
- }
+ if (exports && !JS_WrapObject(aCx, &exports)) {
+ return NS_ERROR_FAILURE;
}
+ aModuleExports.set(exports);
// Cache this module for later
if (newEntry) {
mImports.Put(info.Key(), newEntry);
newEntry.forget();
}
return NS_OK;
--- a/js/xpconnect/loader/mozJSComponentLoader.h
+++ b/js/xpconnect/loader/mozJSComponentLoader.h
@@ -57,18 +57,24 @@ class mozJSComponentLoader final : publi
void FindTargetObject(JSContext* aCx,
JS::MutableHandleObject aTargetObject);
static already_AddRefed<mozJSComponentLoader> GetOrCreate();
static mozJSComponentLoader* Get() { return sSelf; }
- nsresult Import(const nsACString& aResourceURI, JS::HandleValue aTargetObj,
- JSContext* aCx, uint8_t aArgc, JS::MutableHandleValue aRetval);
+ nsresult ImportInto(const nsACString& aResourceURI, JS::HandleValue aTargetObj,
+ JSContext* aCx, uint8_t aArgc, JS::MutableHandleValue aRetval);
+
+ nsresult Import(JSContext* aCx, const nsACString& aResourceURI,
+ JS::MutableHandleObject aModuleGlobal,
+ JS::MutableHandleObject aModuleExports,
+ bool aIgnoreExports = false);
+
nsresult Unload(const nsACString& aResourceURI);
nsresult IsModuleLoaded(const nsACString& aResourceURI, bool* aRetval);
bool IsLoaderGlobal(JSObject* aObj) {
return mLoaderGlobal == aObj;
}
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
@@ -119,17 +125,18 @@ class mozJSComponentLoader final : publi
JS::MutableHandleObject vp);
nsCOMPtr<nsIComponentManager> mCompMgr;
class ModuleEntry : public mozilla::Module
{
public:
explicit ModuleEntry(JS::RootingContext* aRootingCx)
- : mozilla::Module(), obj(aRootingCx), thisObjectKey(aRootingCx)
+ : mozilla::Module(), obj(aRootingCx), exports(aRootingCx),
+ thisObjectKey(aRootingCx)
{
mVersion = mozilla::Module::kVersion;
mCIDs = nullptr;
mContractIDs = nullptr;
mCategoryEntries = nullptr;
getFactoryProc = GetFactory;
loadProc = nullptr;
unloadProc = nullptr;
@@ -169,24 +176,29 @@ class mozJSComponentLoader final : publi
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
static already_AddRefed<nsIFactory> GetFactory(const mozilla::Module& module,
const mozilla::Module::CIDEntry& entry);
nsCOMPtr<xpcIJSGetFactory> getfactoryobj;
JS::PersistentRootedObject obj;
+ JS::PersistentRootedObject exports;
JS::PersistentRootedScript thisObjectKey;
char* location;
nsCString resolvedURL;
#ifdef STARTUP_RECORDER_ENABLED
nsCString importStack;
#endif
};
+ nsresult ExtractExports(JSContext* aCx, ComponentLoaderInfo& aInfo,
+ ModuleEntry* aMod,
+ JS::MutableHandleObject aExports);
+
static size_t DataEntrySizeOfExcludingThis(const nsACString& aKey, ModuleEntry* const& aData,
mozilla::MallocSizeOf aMallocSizeOf, void* arg);
static size_t ClassEntrySizeOfExcludingThis(const nsACString& aKey,
const nsAutoPtr<ModuleEntry>& aData,
mozilla::MallocSizeOf aMallocSizeOf, void* arg);
// Modules are intentionally leaked, but still cleared.
nsDataHashtable<nsCStringHashKey, ModuleEntry*> mModules;