Next part 2 of the Implement module type scripting.

master
Fedor 2020-09-09 17:29:41 +03:00
parent 2a9d5db8a0
commit a84aca97e2
42 changed files with 1012 additions and 743 deletions

View File

@ -12446,3 +12446,16 @@ nsIDocument::GetSelection(ErrorResult& aRv)
return nsGlobalWindow::Cast(window)->GetSelection(aRv);
}
bool
nsIDocument::ModuleScriptsEnabled()
{
static bool sEnabledForContent = false;
static bool sCachedPref = false;
if (!sCachedPref) {
sCachedPref = true;
Preferences::AddBoolVarCache(&sEnabledForContent, "dom.moduleScripts.enabled", false);
}
return nsContentUtils::IsChromeDoc(this) || sEnabledForContent;
}

View File

@ -2830,6 +2830,8 @@ public:
virtual void ScheduleIntersectionObserverNotification() = 0;
virtual void NotifyIntersectionObservers() = 0;
bool ModuleScriptsEnabled();
bool ShouldThrowOnDynamicMarkupInsertion()
{
return mThrowOnDynamicMarkupInsertionCounter;

View File

@ -280,12 +280,20 @@ HTMLScriptElement::GetScriptCharset(nsAString& charset)
}
void
HTMLScriptElement::FreezeUriAsyncDefer()
HTMLScriptElement::FreezeExecutionAttrs(nsIDocument* aOwnerDoc)
{
if (mFrozen) {
return;
}
MOZ_ASSERT(!mIsModule && !mAsync && !mDefer && !mExternal);
// Determine whether this is a classic script or a module script.
nsAutoString type;
GetScriptType(type);
mIsModule = aOwnerDoc->ModuleScriptsEnabled() &&
!type.IsEmpty() && type.LowerCaseEqualsASCII("module");
// variation of this code in nsSVGScriptElement - check if changes
// need to be transfered when modifying. Note that we don't use GetSrc here
// because it will return the base URL when the attr value is "".
@ -300,14 +308,13 @@ HTMLScriptElement::FreezeUriAsyncDefer()
// At this point mUri will be null for invalid URLs.
mExternal = true;
bool defer, async;
GetAsync(&async);
GetDefer(&defer);
mDefer = !async && defer;
mAsync = async;
}
bool defer, async;
GetAsync(&async);
mAsync = (mExternal || mIsModule) && async;
GetDefer(&defer);
mDefer = mExternal && !async && defer;
mFrozen = true;
}

View File

@ -41,7 +41,7 @@ public:
virtual bool GetScriptType(nsAString& type) override;
virtual void GetScriptText(nsAString& text) override;
virtual void GetScriptCharset(nsAString& charset) override;
virtual void FreezeUriAsyncDefer() override;
virtual void FreezeExecutionAttrs(nsIDocument* aOwnerDoc) override;
virtual CORSMode GetCORSMode() const override;
// nsIContent

View File

@ -17,26 +17,54 @@ NS_INTERFACE_MAP_END_INHERITING(ScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_INHERITED(ModuleLoadRequest, ScriptLoadRequest,
mBaseURL,
mLoader,
mParent,
mModuleScript,
mImports)
NS_IMPL_ADDREF_INHERITED(ModuleLoadRequest, ScriptLoadRequest)
NS_IMPL_RELEASE_INHERITED(ModuleLoadRequest, ScriptLoadRequest)
ModuleLoadRequest::ModuleLoadRequest(nsIScriptElement* aElement,
ModuleLoadRequest::ModuleLoadRequest(nsIURI* aURI,
nsIScriptElement* aElement,
uint32_t aVersion,
CORSMode aCORSMode,
const SRIMetadata &aIntegrity,
nsIURI* aReferrer,
mozilla::net::ReferrerPolicy aReferrerPolicy,
ScriptLoader* aLoader)
: ScriptLoadRequest(ScriptKind::Module,
aURI,
aElement,
aVersion,
aCORSMode,
aIntegrity),
aIntegrity,
aReferrer,
aReferrerPolicy),
mIsTopLevel(true),
mLoader(aLoader)
{}
mLoader(aLoader),
mVisitedSet(new VisitedURLSet())
{
mVisitedSet->PutEntry(aURI);
}
ModuleLoadRequest::ModuleLoadRequest(nsIURI* aURI,
ModuleLoadRequest* aParent)
: ScriptLoadRequest(ScriptKind::Module,
aURI,
aParent->mElement,
aParent->mJSVersion,
aParent->mCORSMode,
SRIMetadata(),
aParent->mURI,
aParent->mReferrerPolicy),
mIsTopLevel(false),
mLoader(aParent->mLoader),
mVisitedSet(aParent->mVisitedSet)
{
MOZ_ASSERT(mVisitedSet->Contains(aURI));
mIsInline = false;
mScriptMode = aParent->mScriptMode;
}
void ModuleLoadRequest::Cancel()
{
@ -83,7 +111,7 @@ ModuleLoadRequest::ModuleLoaded()
// been loaded.
mModuleScript = mLoader->GetFetchedModule(mURI);
if (!mModuleScript || mModuleScript->IsErrored()) {
if (!mModuleScript || mModuleScript->HasParseError()) {
ModuleErrored();
return;
}
@ -95,7 +123,7 @@ void
ModuleLoadRequest::ModuleErrored()
{
mLoader->CheckModuleDependenciesLoaded(this);
MOZ_ASSERT(!mModuleScript || mModuleScript->IsErrored());
MOZ_ASSERT(!mModuleScript || mModuleScript->HasParseError());
CancelImports();
SetReady();
@ -132,8 +160,7 @@ ModuleLoadRequest::LoadFinished()
{
mLoader->ProcessLoadedModuleTree(this);
mLoader = nullptr;
mParent = nullptr;
}
} // dom namespace
} // mozilla namespace
} // mozilla namespace

View File

@ -8,6 +8,7 @@
#define mozilla_dom_ModuleLoadRequest_h
#include "mozilla/dom/ScriptLoader.h"
#include "nsURIHashKey.h"
#include "mozilla/MozPromise.h"
namespace mozilla {
@ -16,6 +17,16 @@ namespace dom {
class ModuleScript;
class ScriptLoader;
// A reference counted set of URLs we have visited in the process of loading a
// module graph.
class VisitedURLSet : public nsTHashtable<nsURIHashKey>
{
NS_INLINE_DECL_REFCOUNTING(VisitedURLSet)
private:
~VisitedURLSet() = default;
};
// A load request for a module, created for every top level module script and
// every module import. Load request can share a ModuleScript if there are
// multiple imports of the same module.
@ -31,12 +42,20 @@ public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ModuleLoadRequest, ScriptLoadRequest)
ModuleLoadRequest(nsIScriptElement* aElement,
// Create a top-level module load request.
ModuleLoadRequest(nsIURI* aURI,
nsIScriptElement* aElement,
uint32_t aVersion,
CORSMode aCORSMode,
const SRIMetadata& aIntegrity,
nsIURI* aReferrer,
mozilla::net::ReferrerPolicy,
ScriptLoader* aLoader);
// Create a module load request for an imported module.
ModuleLoadRequest(nsIURI* aURI,
ModuleLoadRequest* aParent);
bool IsTopLevel() const {
return mIsTopLevel;
}
@ -55,7 +74,7 @@ private:
public:
// Is this a request for a top level module script or an import?
bool mIsTopLevel;
const bool mIsTopLevel;
// The base URL used for resolving relative module imports.
nsCOMPtr<nsIURI> mBaseURL;
@ -64,10 +83,6 @@ public:
// finishes.
RefPtr<ScriptLoader> mLoader;
// The importing module, or nullptr for top level module scripts. Used to
// implement the ancestor list checked when fetching module dependencies.
RefPtr<ModuleLoadRequest> mParent;
// Set to a module script object after a successful load or nullptr on
// failure.
RefPtr<ModuleScript> mModuleScript;
@ -79,9 +94,13 @@ public:
// Array of imported modules.
nsTArray<RefPtr<ModuleLoadRequest>> mImports;
// Set of module URLs visited while fetching the module graph this request is
// part of.
RefPtr<VisitedURLSet> mVisitedSet;
};
} // dom namespace
} // mozilla namespace
#endif // mozilla_dom_ModuleLoadRequest_h
#endif // mozilla_dom_ModuleLoadRequest_h

View File

@ -26,7 +26,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ModuleScript)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoader)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL)
tmp->UnlinkModuleRecord();
tmp->mError.setUndefined();
tmp->mParseError.setUndefined();
tmp->mErrorToRethrow.setUndefined();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ModuleScript)
@ -35,7 +36,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ModuleScript)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mModuleRecord)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mError)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mParseError)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mErrorToRethrow)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleScript)
@ -48,7 +50,8 @@ ModuleScript::ModuleScript(ScriptLoader *aLoader, nsIURI* aBaseURL)
MOZ_ASSERT(mLoader);
MOZ_ASSERT(mBaseURL);
MOZ_ASSERT(!mModuleRecord);
MOZ_ASSERT(mError.isUndefined());
MOZ_ASSERT(!HasParseError());
MOZ_ASSERT(!HasErrorToRethrow());
}
void
@ -74,7 +77,8 @@ void
ModuleScript::SetModuleRecord(JS::Handle<JSObject*> aModuleRecord)
{
MOZ_ASSERT(!mModuleRecord);
MOZ_ASSERT(mError.isUndefined());
MOZ_ASSERT(!HasParseError());
MOZ_ASSERT(!HasErrorToRethrow());
mModuleRecord = aModuleRecord;
@ -85,37 +89,24 @@ ModuleScript::SetModuleRecord(JS::Handle<JSObject*> aModuleRecord)
}
void
ModuleScript::SetPreInstantiationError(const JS::Value& aError)
ModuleScript::SetParseError(const JS::Value& aError)
{
MOZ_ASSERT(!aError.isUndefined());
MOZ_ASSERT(!HasParseError());
MOZ_ASSERT(!HasErrorToRethrow());
UnlinkModuleRecord();
mError = aError;
mParseError = aError;
HoldJSObjects(this);
}
bool
ModuleScript::IsErrored() const
void
ModuleScript::SetErrorToRethrow(const JS::Value& aError)
{
if (!mModuleRecord) {
MOZ_ASSERT(!mError.isUndefined());
return true;
}
MOZ_ASSERT(!aError.isUndefined());
MOZ_ASSERT(!HasErrorToRethrow());
return JS::IsModuleErrored(mModuleRecord);
}
JS::Value
ModuleScript::Error() const
{
MOZ_ASSERT(IsErrored());
if (!mModuleRecord) {
return mError;
}
return JS::GetModuleError(mModuleRecord);
mErrorToRethrow = aError;
}
} // dom namespace

View File

@ -23,7 +23,8 @@ class ModuleScript final : public nsISupports
RefPtr<ScriptLoader> mLoader;
nsCOMPtr<nsIURI> mBaseURL;
JS::Heap<JSObject*> mModuleRecord;
JS::Heap<JS::Value> mError;
JS::Heap<JS::Value> mParseError;
JS::Heap<JS::Value> mErrorToRethrow;
~ModuleScript();
@ -35,14 +36,17 @@ public:
nsIURI* aBaseURL);
void SetModuleRecord(JS::Handle<JSObject*> aModuleRecord);
void SetPreInstantiationError(const JS::Value& aError);
void SetParseError(const JS::Value& aError);
void SetErrorToRethrow(const JS::Value& aError);
ScriptLoader* Loader() const { return mLoader; }
JSObject* ModuleRecord() const { return mModuleRecord; }
nsIURI* BaseURL() const { return mBaseURL; }
bool IsErrored() const;
JS::Value Error() const;
JS::Value ParseError() const { return mParseError; }
JS::Value ErrorToRethrow() const { return mErrorToRethrow; }
bool HasParseError() const { return !mParseError.isUndefined(); }
bool HasErrorToRethrow() const { return !mErrorToRethrow.isUndefined(); }
void UnlinkModuleRecord();
};

View File

@ -21,11 +21,11 @@ using namespace mozilla::dom;
NS_IMETHODIMP
ScriptElement::ScriptAvailable(nsresult aResult,
nsIScriptElement *aElement,
bool aIsInline,
bool aIsInlineClassicScript,
nsIURI *aURI,
int32_t aLineNo)
{
if (!aIsInline && NS_FAILED(aResult)) {
if (!aIsInlineClassicScript && NS_FAILED(aResult)) {
nsCOMPtr<nsIParser> parser = do_QueryReferent(mCreatorParser);
if (parser) {
parser->PushDefinedInsertionPoint();
@ -128,11 +128,11 @@ ScriptElement::MaybeProcessScript()
return false;
}
FreezeUriAsyncDefer();
nsIDocument* ownerDoc = cont->OwnerDoc();
FreezeExecutionAttrs(ownerDoc);
mAlreadyStarted = true;
nsIDocument* ownerDoc = cont->OwnerDoc();
nsCOMPtr<nsIParser> parser = ((nsIScriptElement*) this)->GetCreatorParser();
if (parser) {
nsCOMPtr<nsIContentSink> sink = parser->GetContentSink();

File diff suppressed because it is too large Load Diff

View File

@ -62,17 +62,21 @@ protected:
public:
ScriptLoadRequest(ScriptKind aKind,
nsIURI* aURI,
nsIScriptElement* aElement,
uint32_t aVersion,
mozilla::CORSMode aCORSMode,
const mozilla::dom::SRIMetadata &aIntegrity)
const SRIMetadata& aIntegrity,
nsIURI* aReferrer,
mozilla::net::ReferrerPolicy aReferrerPolicy)
: mKind(aKind),
mElement(aElement),
mProgress(Progress::Loading),
mScriptMode(ScriptMode::eBlocking),
mIsInline(true),
mHasSourceMapURL(false),
mIsDefer(false),
mIsAsync(false),
mInDeferList(false),
mInAsyncList(false),
mIsNonAsyncScriptInserted(false),
mIsXSLT(false),
mIsCanceled(false),
@ -81,10 +85,12 @@ public:
mScriptTextBuf(nullptr),
mScriptTextLength(0),
mJSVersion(aVersion),
mURI(aURI),
mLineNo(1),
mCORSMode(aCORSMode),
mIntegrity(aIntegrity),
mReferrerPolicy(mozilla::net::RP_Default)
mReferrer(aReferrer),
mReferrerPolicy(aReferrerPolicy)
{
}
@ -100,7 +106,8 @@ public:
void FireScriptAvailable(nsresult aResult)
{
mElement->ScriptAvailable(aResult, mElement, mIsInline, mURI, mLineNo);
bool isInlineClassicScript = mIsInline && !IsModuleRequest();
mElement->ScriptAvailable(aResult, mElement, isInlineClassicScript, mURI, mLineNo);
}
void FireScriptEvaluated(nsresult aResult)
{
@ -143,6 +150,29 @@ public:
(IsReadyToRun() && mWasCompiledOMT);
}
enum class ScriptMode : uint8_t {
eBlocking,
eDeferred,
eAsync
};
void SetScriptMode(bool aDeferAttr, bool aAsyncAttr);
bool IsBlockingScript() const
{
return mScriptMode == ScriptMode::eBlocking;
}
bool IsDeferredScript() const
{
return mScriptMode == ScriptMode::eDeferred;
}
bool IsAsyncScript() const
{
return mScriptMode == ScriptMode::eAsync;
}
void MaybeCancelOffThreadScript();
using super::getNext;
@ -151,10 +181,11 @@ public:
const ScriptKind mKind;
nsCOMPtr<nsIScriptElement> mElement;
Progress mProgress; // Are we still waiting for a load to complete?
ScriptMode mScriptMode; // Whether this script is blocking, deferred or async.
bool mIsInline; // Is the script inline or loaded?
bool mHasSourceMapURL; // Does the HTTP header have a source map url?
bool mIsDefer; // True if we live in mDeferRequests.
bool mIsAsync; // True if we live in mLoadingAsyncRequests or mLoadedAsyncRequests.
bool mInDeferList; // True if we live in mDeferRequests.
bool mInAsyncList; // True if we live in mLoadingAsyncRequests or mLoadedAsyncRequests.
bool mIsNonAsyncScriptInserted; // True if we live in mNonAsyncExternalScriptInsertedRequests
bool mIsXSLT; // True if we live in mXSLTRequests.
bool mIsCanceled; // True if we have been explicitly canceled.
@ -164,13 +195,14 @@ public:
char16_t* mScriptTextBuf; // Holds script text for non-inline scripts. Don't
size_t mScriptTextLength; // use nsString so we can give ownership to jsapi.
uint32_t mJSVersion;
nsCOMPtr<nsIURI> mURI;
const nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIPrincipal> mOriginPrincipal;
nsAutoCString mURL; // Keep the URI's filename alive during off thread parsing.
int32_t mLineNo;
const mozilla::CORSMode mCORSMode;
const mozilla::dom::SRIMetadata mIntegrity;
mozilla::net::ReferrerPolicy mReferrerPolicy;
const SRIMetadata mIntegrity;
const nsCOMPtr<nsIURI> mReferrer;
const mozilla::net::ReferrerPolicy mReferrerPolicy;
};
class ScriptLoadRequestList : private mozilla::LinkedList<ScriptLoadRequest>
@ -452,6 +484,8 @@ public:
const nsAString &aCrossOrigin,
const nsAString& aIntegrity,
bool aScriptFromHead,
bool aAsync,
bool aDefer,
const mozilla::net::ReferrerPolicy aReferrerPolicy);
/**
@ -467,12 +501,13 @@ public:
private:
virtual ~ScriptLoader();
ScriptLoadRequest* CreateLoadRequest(
ScriptKind aKind,
nsIScriptElement* aElement,
uint32_t aVersion,
mozilla::CORSMode aCORSMode,
const mozilla::dom::SRIMetadata &aIntegrity);
ScriptLoadRequest* CreateLoadRequest(ScriptKind aKind,
nsIURI* aURI,
nsIScriptElement* aElement,
uint32_t aVersion,
mozilla::CORSMode aCORSMode,
const SRIMetadata& aIntegrity,
mozilla::net::ReferrerPolicy aReferrerPolicy);
/**
* Unblocks the creator parser of the parser-blocking scripts.
@ -500,6 +535,8 @@ private:
nsresult StartLoad(ScriptLoadRequest *aRequest, const nsAString &aType,
bool aScriptFromHead);
void HandleLoadError(ScriptLoadRequest *aRequest, nsresult aResult);
/**
* Process any pending requests asynchronously (i.e. off an event) if there
* are any. Note that this is a no-op if there aren't any currently pending
@ -534,6 +571,11 @@ private:
return mEnabled && !mBlockerCount;
}
nsresult VerifySRI(ScriptLoadRequest *aRequest,
nsIIncrementalStreamLoader* aLoader,
nsresult aSRIStatus,
SRICheckDataVerifier* aSRIDataVerifier) const;
nsresult AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest);
nsresult ProcessRequest(ScriptLoadRequest* aRequest);
nsresult CompileOffThreadOrProcessRequest(ScriptLoadRequest* aRequest);
@ -556,6 +598,7 @@ private:
mozilla::Vector<char16_t> &aString);
void AddDeferRequest(ScriptLoadRequest* aRequest);
void AddAsyncRequest(ScriptLoadRequest* aRequest);
bool MaybeRemovedDeferRequests();
void MaybeMoveToLoadedList(ScriptLoadRequest* aRequest);
@ -563,30 +606,30 @@ private:
JS::SourceBufferHolder GetScriptSource(ScriptLoadRequest* aRequest,
nsAutoString& inlineData);
bool ModuleScriptsEnabled();
void SetModuleFetchStarted(ModuleLoadRequest *aRequest);
void SetModuleFetchFinishedAndResumeWaitingRequests(ModuleLoadRequest *aRequest,
nsresult aResult);
bool IsFetchingModule(ModuleLoadRequest *aRequest) const;
bool ModuleMapContainsModule(ModuleLoadRequest *aRequest) const;
RefPtr<mozilla::GenericPromise> WaitForModuleFetch(ModuleLoadRequest *aRequest);
bool ModuleMapContainsURL(nsIURI* aURL) const;
RefPtr<mozilla::GenericPromise> WaitForModuleFetch(nsIURI* aURL);
ModuleScript* GetFetchedModule(nsIURI* aURL) const;
friend bool
HostResolveImportedModule(JSContext* aCx, unsigned argc, JS::Value* vp);
friend JSObject*
HostResolveImportedModule(JSContext* aCx, JS::Handle<JSObject*> aModule,
JS::Handle<JSString*> aSpecifier);
nsresult CreateModuleScript(ModuleLoadRequest* aRequest);
nsresult ProcessFetchedModuleSource(ModuleLoadRequest* aRequest);
void CheckModuleDependenciesLoaded(ModuleLoadRequest* aRequest);
void ProcessLoadedModuleTree(ModuleLoadRequest* aRequest);
JS::Value FindFirstParseError(ModuleLoadRequest* aRequest);
bool InstantiateModuleTree(ModuleLoadRequest* aRequest);
void StartFetchingModuleDependencies(ModuleLoadRequest* aRequest);
RefPtr<mozilla::GenericPromise>
StartFetchingModuleAndDependencies(ModuleLoadRequest* aRequest, nsIURI* aURI);
StartFetchingModuleAndDependencies(ModuleLoadRequest* aParent, nsIURI* aURI);
nsIDocument* mDocument; // [WEAK]
nsCOMArray<nsIScriptLoaderObserver> mObservers;

View File

@ -13,6 +13,7 @@
#include "nsIScriptLoaderObserver.h"
#include "nsWeakPtr.h"
#include "nsIParser.h"
#include "nsIDocument.h"
#include "nsContentCreatorFunctions.h"
#include "nsIDOMHTMLScriptElement.h"
#include "mozilla/CORSMode.h"
@ -37,6 +38,7 @@ public:
mForceAsync(aFromParser == mozilla::dom::NOT_FROM_PARSER ||
aFromParser == mozilla::dom::FROM_PARSER_FRAGMENT),
mFrozen(false),
mIsModule(false),
mDefer(false),
mAsync(false),
mExternal(false),
@ -73,11 +75,25 @@ public:
virtual void GetScriptCharset(nsAString& charset) = 0;
/**
* Freezes the return values of GetScriptDeferred(), GetScriptAsync() and
* GetScriptURI() so that subsequent modifications to the attributes don't
* change execution behavior.
* Freezes the return values of the following methods so that subsequent
* modifications to the attributes don't change execution behavior:
* - GetScriptIsModule()
* - GetScriptDeferred()
* - GetScriptAsync()
* - GetScriptURI()
* - GetScriptExternal()
*/
virtual void FreezeUriAsyncDefer() = 0;
virtual void FreezeExecutionAttrs(nsIDocument* aOwnerDoc) = 0;
/**
* Is the script a module script?
* Currently only supported by HTML scripts.
*/
bool GetScriptIsModule()
{
NS_PRECONDITION(mFrozen, "Execution attributes not yet frozen: Not ready for this call!");
return mIsModule;
}
/**
* Is the script deferred. Currently only supported by HTML scripts.
@ -292,6 +308,11 @@ protected:
*/
bool mFrozen;
/**
* The effective moduleness.
*/
bool mIsModule;
/**
* The effective deferredness.
*/

View File

@ -20,15 +20,16 @@ interface nsIScriptLoaderObserver : nsISupports {
* a script. If this is a failure code, script evaluation
* will not occur.
* @param aElement The element being processed.
* @param aIsInline Is this an inline script or externally loaded?
* @param aIsInline Is this an inline classic script (as opposed to an
* externally loaded classic script or module script)?
* @param aURI What is the URI of the script (the document URI if
* it is inline).
* @param aLineNo At what line does the script appear (generally 1
* if it is a loaded script).
*/
void scriptAvailable(in nsresult aResult,
void scriptAvailable(in nsresult aResult,
in nsIScriptElement aElement,
in boolean aIsInline,
in boolean aIsInlineClassicScript,
in nsIURI aURI,
in int32_t aLineNo);
@ -40,7 +41,7 @@ interface nsIScriptLoaderObserver : nsISupports {
* @param aElement The element being processed.
* @param aIsInline Is this an inline script or externally loaded?
*/
void scriptEvaluated(in nsresult aResult,
void scriptEvaluated(in nsresult aResult,
in nsIScriptElement aElement,
in boolean aIsInline);

View File

@ -137,7 +137,7 @@ SVGScriptElement::GetScriptCharset(nsAString& charset)
}
void
SVGScriptElement::FreezeUriAsyncDefer()
SVGScriptElement::FreezeExecutionAttrs(nsIDocument* aOwnerDoc)
{
if (mFrozen) {
return;

View File

@ -44,7 +44,7 @@ public:
virtual bool GetScriptType(nsAString& type) override;
virtual void GetScriptText(nsAString& text) override;
virtual void GetScriptCharset(nsAString& charset) override;
virtual void FreezeUriAsyncDefer() override;
virtual void FreezeExecutionAttrs(nsIDocument* aOwnerDoc) override;
virtual CORSMode GetCORSMode() const override;
// ScriptElement

View File

@ -955,7 +955,7 @@ NS_IMPL_ISUPPORTS(txTransformNotifier,
NS_IMETHODIMP
txTransformNotifier::ScriptAvailable(nsresult aResult,
nsIScriptElement *aElement,
bool aIsInline,
bool aIsInlineClassicScript,
nsIURI *aURI,
int32_t aLineNo)
{

View File

@ -779,7 +779,7 @@ struct JSClass {
// application.
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
#define JSCLASS_GLOBAL_SLOT_COUNT \
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 46)
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 45)
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
#define JSCLASS_GLOBAL_FLAGS \

View File

@ -67,10 +67,16 @@ function ModuleGetExportedNames(exportStarSet = [])
function ModuleSetStatus(module, newStatus)
{
assert(newStatus >= MODULE_STATUS_ERRORED && newStatus <= MODULE_STATUS_EVALUATED,
assert(newStatus >= MODULE_STATUS_UNINSTANTIATED &&
newStatus <= MODULE_STATUS_EVALUATED_ERROR,
"Bad new module status in ModuleSetStatus");
if (newStatus !== MODULE_STATUS_ERRORED)
assert(newStatus > module.status, "New module status inconsistent with current status");
// Note that under OOM conditions we can fail the module instantiation
// process even after modules have been marked as instantiated.
assert((module.status <= MODULE_STATUS_INSTANTIATED &&
newStatus === MODULE_STATUS_UNINSTANTIATED) ||
newStatus > module.status,
"New module status inconsistent with current status");
UnsafeSetReservedSlot(module, MODULE_OBJECT_STATUS_SLOT, newStatus);
}
@ -80,20 +86,15 @@ function ModuleSetStatus(module, newStatus)
// Returns an object describing the location of the resolved export or
// indicating a failure.
//
// On success this returns: { resolved: true, module, bindingName }
// On success this returns a resolved binding record: { module, bindingName }
//
// There are three failure cases:
// There are two failure cases:
//
// - The resolution failure can be blamed on a particular module.
// Returns: { resolved: false, module, ambiguous: false }
// - If no definition was found or the request is found to be circular, *null*
// is returned.
//
// - No culprit can be determined and the resolution failure was due to star
// export ambiguity.
// Returns: { resolved: false, module: null, ambiguous: true }
//
// - No culprit can be determined and the resolution failure was not due to
// star export ambiguity.
// Returns: { resolved: false, module: null, ambiguous: false }
// - If the request is found to be ambiguous, the string `"ambiguous"` is
// returned.
//
function ModuleResolveExport(exportName, resolveSet = [])
{
@ -106,53 +107,47 @@ function ModuleResolveExport(exportName, resolveSet = [])
let module = this;
// Step 2
assert(module.status !== MODULE_STATUS_ERRORED, "Bad module status in ResolveExport");
// Step 3
for (let i = 0; i < resolveSet.length; i++) {
let r = resolveSet[i];
if (r.module === module && r.exportName === exportName) {
// This is a circular import request.
return {resolved: false, module: null, ambiguous: false};
return null;
}
}
// Step 4
// Step 3
_DefineDataProperty(resolveSet, resolveSet.length, {module: module, exportName: exportName});
// Step 5
// Step 4
let localExportEntries = module.localExportEntries;
for (let i = 0; i < localExportEntries.length; i++) {
let e = localExportEntries[i];
if (exportName === e.exportName)
return {resolved: true, module, bindingName: e.localName};
return {module, bindingName: e.localName};
}
// Step 6
// Step 5
let indirectExportEntries = module.indirectExportEntries;
for (let i = 0; i < indirectExportEntries.length; i++) {
let e = indirectExportEntries[i];
if (exportName === e.exportName) {
let importedModule = CallModuleResolveHook(module, e.moduleRequest,
MODULE_STATUS_UNINSTANTIATED);
let resolution = callFunction(importedModule.resolveExport, importedModule, e.importName,
resolveSet);
if (!resolution.resolved && !resolution.module)
resolution.module = module;
return resolution;
return callFunction(importedModule.resolveExport, importedModule, e.importName,
resolveSet);
}
}
// Step 7
// Step 6
if (exportName === "default") {
// A default export cannot be provided by an export *.
return {resolved: false, module: null, ambiguous: false};
return null;
}
// Step 8
// Step 7
let starResolution = null;
// Step 9
// Step 8
let starExportEntries = module.starExportEntries;
for (let i = 0; i < starExportEntries.length; i++) {
let e = starExportEntries[i];
@ -160,27 +155,31 @@ function ModuleResolveExport(exportName, resolveSet = [])
MODULE_STATUS_UNINSTANTIATED);
let resolution = callFunction(importedModule.resolveExport, importedModule, exportName,
resolveSet);
if (!resolution.resolved && (resolution.module || resolution.ambiguous))
if (resolution === "ambiguous")
return resolution;
if (resolution.resolved) {
if (resolution !== null) {
if (starResolution === null) {
starResolution = resolution;
} else {
if (resolution.module !== starResolution.module ||
resolution.bindingName !== starResolution.bindingName)
{
return {resolved: false, module: null, ambiguous: true};
return "ambiguous";
}
}
}
}
// Step 10
if (starResolution !== null)
return starResolution;
// Step 9
return starResolution;
}
return {resolved: false, module: null, ambiguous: false};
function IsResolvedBinding(resolution)
{
assert(resolution === "ambiguous" || typeof resolution === "object",
"Bad module resolution result");
return typeof resolution === "object" && resolution !== null;
}
// 15.2.1.18 GetModuleNamespace(module)
@ -189,12 +188,12 @@ function GetModuleNamespace(module)
// Step 1
assert(IsModule(module), "GetModuleNamespace called with non-module");
// Step 2
// Steps 2-3
assert(module.status !== MODULE_STATUS_UNINSTANTIATED &&
module.status !== MODULE_STATUS_ERRORED,
module.status !== MODULE_STATUS_EVALUATED_ERROR,
"Bad module status in GetModuleNamespace");
// Step 3
// Step 4
let namespace = module.namespace;
if (typeof namespace === "undefined") {
@ -203,7 +202,7 @@ function GetModuleNamespace(module)
for (let i = 0; i < exportedNames.length; i++) {
let name = exportedNames[i];
let resolution = callFunction(module.resolveExport, module, name);
if (resolution.resolved)
if (IsResolvedBinding(resolution))
_DefineDataProperty(unambiguousNames, unambiguousNames.length, name);
}
namespace = ModuleNamespaceCreate(module, unambiguousNames);
@ -225,7 +224,7 @@ function ModuleNamespaceCreate(module, exports)
for (let i = 0; i < exports.length; i++) {
let name = exports[i];
let binding = callFunction(module.resolveExport, module, name);
assert(binding.resolved, "Failed to resolve binding");
assert(IsResolvedBinding(binding), "Failed to resolve binding");
AddModuleNamespaceBinding(ns, name, binding.module, binding.bindingName);
}
@ -236,31 +235,16 @@ function GetModuleEnvironment(module)
{
assert(IsModule(module), "Non-module passed to GetModuleEnvironment");
// Check for a previous failed attempt to instantiate this module. This can
// only happen due to a bug in the module loader.
if (module.status === MODULE_STATUS_ERRORED)
ThrowInternalError(JSMSG_MODULE_INSTANTIATE_FAILED, module.status);
assert(module.status >= MODULE_STATUS_INSTANTIATING,
"Attempt to access module environement before instantation");
let env = UnsafeGetReservedSlot(module, MODULE_OBJECT_ENVIRONMENT_SLOT);
assert(env === undefined || IsModuleEnvironment(env),
assert(IsModuleEnvironment(env),
"Module environment slot contains unexpected value");
return env;
}
function RecordModuleError(module, error)
{
// Set the module's status to 'errored' to indicate a failed module
// instantiation and record the exception. The environment slot is also
// reset to 'undefined'.
assert(IsObject(module) && IsModule(module), "Non-module passed to RecordModuleError");
ModuleSetStatus(module, MODULE_STATUS_ERRORED);
UnsafeSetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT, error);
UnsafeSetReservedSlot(module, MODULE_OBJECT_ENVIRONMENT_SLOT, undefined);
}
function CountArrayValues(array, value)
{
let count = 0;
@ -280,6 +264,16 @@ function ArrayContains(array, value)
return false;
}
function HandleModuleInstantiationFailure(module)
{
// Reset the module to the "uninstantiated" state. Don't reset the
// environment slot as the environment object will be required by any
// possible future instantiation attempt.
ModuleSetStatus(module, MODULE_STATUS_UNINSTANTIATED);
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, undefined);
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, undefined);
}
// 15.2.1.16.4 ModuleInstantiate()
function ModuleInstantiate()
{
@ -301,38 +295,30 @@ function ModuleInstantiate()
// Steps 4-5
try {
InnerModuleDeclarationInstantiation(module, stack, 0);
InnerModuleInstantiation(module, stack, 0);
} catch (error) {
for (let i = 0; i < stack.length; i++) {
let m = stack[i];
assert(m.status === MODULE_STATUS_INSTANTIATING ||
m.status === MODULE_STATUS_ERRORED,
"Bad module status after failed instantiation");
RecordModuleError(m, error);
assert(m.status === MODULE_STATUS_INSTANTIATING,
"Expected instantiating status during failed instantiation");
HandleModuleInstantiationFailure(m);
}
if (stack.length === 0 &&
typeof(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT)) === "undefined")
{
// This can happen due to OOM when appending to the stack.
assert(error === "out of memory",
"Stack must contain module unless we hit OOM");
RecordModuleError(module, error);
}
// Handle OOM when appending to the stack or over-recursion errors.
if (stack.length === 0)
HandleModuleInstantiationFailure(module);
assert(module.status === MODULE_STATUS_ERRORED,
"Bad module status after failed instantiation");
assert(SameValue(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT), error),
"Module has different error set after failed instantiation");
assert(module.status === MODULE_STATUS_UNINSTANTIATED,
"Expected uninstantiated status after failed instantiation");
throw error;
}
// Step 6
assert(module.status == MODULE_STATUS_INSTANTIATED ||
module.status == MODULE_STATUS_EVALUATED,
assert(module.status === MODULE_STATUS_INSTANTIATED ||
module.status === MODULE_STATUS_EVALUATED ||
module.status === MODULE_STATUS_EVALUATED_ERROR,
"Bad module status after successful instantiation");
// Step 7
@ -344,8 +330,8 @@ function ModuleInstantiate()
}
_SetCanonicalName(ModuleInstantiate, "ModuleInstantiate");
// 15.2.1.16.4.1 InnerModuleDeclarationInstantiation(module, stack, index)
function InnerModuleDeclarationInstantiation(module, stack, index)
// 15.2.1.16.4.1 InnerModuleInstantiation(module, stack, index)
function InnerModuleInstantiation(module, stack, index)
{
// Step 1
// TODO: Support module records other than source text module records.
@ -353,42 +339,40 @@ function InnerModuleDeclarationInstantiation(module, stack, index)
// Step 2
if (module.status === MODULE_STATUS_INSTANTIATING ||
module.status === MODULE_STATUS_INSTANTIATED ||
module.status === MODULE_STATUS_EVALUATED)
module.status === MODULE_STATUS_EVALUATED ||
module.status === MODULE_STATUS_EVALUATED_ERROR)
{
return index;
}
// Step 3
if (module.status === MODULE_STATUS_ERRORED)
throw module.error;
// Step 4
assert(module.status === MODULE_STATUS_UNINSTANTIATED,
"Bad module status in ModuleDeclarationInstantiation");
// Steps 5
// Step 4
ModuleSetStatus(module, MODULE_STATUS_INSTANTIATING);
// Step 6-8
// Steps 5-7
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, index);
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, index);
index++;
// Step 9
// Step 8
_DefineDataProperty(stack, stack.length, module);
// Step 10
// Step 9
let requestedModules = module.requestedModules;
for (let i = 0; i < requestedModules.length; i++) {
let required = requestedModules[i];
let requiredModule = CallModuleResolveHook(module, required, MODULE_STATUS_ERRORED);
let requiredModule = CallModuleResolveHook(module, required, MODULE_STATUS_UNINSTANTIATED);
index = InnerModuleDeclarationInstantiation(requiredModule, stack, index);
index = InnerModuleInstantiation(requiredModule, stack, index);
assert(requiredModule.status === MODULE_STATUS_INSTANTIATING ||
requiredModule.status === MODULE_STATUS_INSTANTIATED ||
requiredModule.status === MODULE_STATUS_EVALUATED,
"Bad required module status after InnerModuleDeclarationInstantiation");
requiredModule.status === MODULE_STATUS_EVALUATED ||
requiredModule.status === MODULE_STATUS_EVALUATED_ERROR,
"Bad required module status after InnerModuleInstantiation");
assert((requiredModule.status === MODULE_STATUS_INSTANTIATING) ===
ArrayContains(stack, requiredModule),
@ -404,16 +388,16 @@ function InnerModuleDeclarationInstantiation(module, stack, index)
}
}
// Step 11
// Step 10
ModuleDeclarationEnvironmentSetup(module);
// Steps 12-13
// Steps 11-12
assert(CountArrayValues(stack, module) === 1,
"Current module should appear exactly once in the stack");
assert(module.dfsAncestorIndex <= module.dfsIndex,
"Bad DFS ancestor index");
// Step 14
// Step 13
if (module.dfsAncestorIndex === module.dfsIndex) {
let requiredModule;
do {
@ -434,16 +418,14 @@ function ModuleDeclarationEnvironmentSetup(module)
for (let i = 0; i < indirectExportEntries.length; i++) {
let e = indirectExportEntries[i];
let resolution = callFunction(module.resolveExport, module, e.exportName);
assert(resolution.resolved || resolution.module,
"Unexpected failure to resolve export in ModuleDeclarationEnvironmentSetup");
if (!resolution.resolved) {
return ResolutionError(resolution, "indirectExport", e.exportName,
e.lineNumber, e.columnNumber)
if (!IsResolvedBinding(resolution)) {
ThrowResolutionError(module, resolution, "indirectExport", e.exportName,
e.lineNumber, e.columnNumber);
}
}
// Steps 5-6
CreateModuleEnvironment(module);
// Note that we have already created the environment by this point.
let env = GetModuleEnvironment(module);
// Step 8
@ -458,12 +440,9 @@ function ModuleDeclarationEnvironmentSetup(module)
} else {
let resolution = callFunction(importedModule.resolveExport, importedModule,
imp.importName);
if (!resolution.resolved && !resolution.module)
resolution.module = module;
if (!resolution.resolved) {
return ResolutionError(resolution, "import", imp.importName,
imp.lineNumber, imp.columnNumber);
if (!IsResolvedBinding(resolution)) {
ThrowResolutionError(module, resolution, "import", imp.importName,
imp.lineNumber, imp.columnNumber);
}
CreateImportBinding(env, imp.localName, resolution.module, resolution.bindingName);
@ -473,38 +452,58 @@ function ModuleDeclarationEnvironmentSetup(module)
InstantiateModuleFunctionDeclarations(module);
}
// 15.2.1.16.4.3 ResolutionError(module)
function ResolutionError(resolution, kind, name, line, column)
function ThrowResolutionError(module, resolution, kind, name, line, column)
{
let module = resolution.module;
assert(module !== null,
"Null module passed to ResolutionError");
assert(module.status === MODULE_STATUS_UNINSTANTIATED ||
module.status === MODULE_STATUS_INSTANTIATING,
"Unexpected module status in ResolutionError");
assert(module.status === MODULE_STATUS_INSTANTIATING,
"Unexpected module status in ThrowResolutionError");
assert(kind === "import" || kind === "indirectExport",
"Unexpected kind in ResolutionError");
"Unexpected kind in ThrowResolutionError");
assert(line > 0,
"Line number should be present for all imports and indirect exports");
let ambiguous = resolution === "ambiguous";
let errorNumber;
if (kind === "import") {
errorNumber = resolution.ambiguous ? JSMSG_AMBIGUOUS_IMPORT
: JSMSG_MISSING_IMPORT;
} else {
errorNumber = resolution.ambiguous ? JSMSG_AMBIGUOUS_INDIRECT_EXPORT
: JSMSG_MISSING_INDIRECT_EXPORT;
}
if (kind === "import")
errorNumber = ambiguous ? JSMSG_AMBIGUOUS_IMPORT : JSMSG_MISSING_IMPORT;
else
errorNumber = ambiguous ? JSMSG_AMBIGUOUS_INDIRECT_EXPORT : JSMSG_MISSING_INDIRECT_EXPORT;
let message = GetErrorMessage(errorNumber) + ": " + name;
let error = CreateModuleSyntaxError(module, line, column, message);
RecordModuleError(module, error);
throw error;
}
function GetModuleEvaluationError(module)
{
assert(IsObject(module) && IsModule(module),
"Non-module passed to GetModuleEvaluationError");
assert(module.status === MODULE_STATUS_EVALUATED_ERROR,
"Bad module status in GetModuleEvaluationError");
return UnsafeGetReservedSlot(module, MODULE_OBJECT_EVALUATION_ERROR_SLOT);
}
function RecordModuleEvaluationError(module, error)
{
// Set the module's EvaluationError slot to indicate a failed module
// evaluation.
assert(IsObject(module) && IsModule(module),
"Non-module passed to RecordModuleEvaluationError");
if (module.status === MODULE_STATUS_EVALUATED_ERROR) {
// It would be nice to assert that |error| is the same as the one we
// previously recorded, but that's not always true in the case of out of
// memory and over-recursion errors.
return;
}
ModuleSetStatus(module, MODULE_STATUS_EVALUATED_ERROR);
UnsafeSetReservedSlot(module, MODULE_OBJECT_EVALUATION_ERROR_SLOT, error);
}
// 15.2.1.16.5 ModuleEvaluate()
function ModuleEvaluate()
{
@ -515,9 +514,9 @@ function ModuleEvaluate()
let module = this;
// Step 2
if (module.status !== MODULE_STATUS_ERRORED &&
module.status !== MODULE_STATUS_INSTANTIATED &&
module.status !== MODULE_STATUS_EVALUATED)
if (module.status !== MODULE_STATUS_INSTANTIATED &&
module.status !== MODULE_STATUS_EVALUATED &&
module.status !== MODULE_STATUS_EVALUATED_ERROR)
{
ThrowInternalError(JSMSG_BAD_MODULE_STATUS);
}
@ -535,27 +534,20 @@ function ModuleEvaluate()
assert(m.status === MODULE_STATUS_EVALUATING,
"Bad module status after failed evaluation");
RecordModuleError(m, error);
RecordModuleEvaluationError(m, error);
}
if (stack.length === 0 &&
typeof(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT)) === "undefined")
{
// This can happen due to OOM when appending to the stack.
assert(error === "out of memory",
"Stack must contain module unless we hit OOM");
RecordModuleError(module, error);
}
// Handle OOM when appending to the stack or over-recursion errors.
if (stack.length === 0)
RecordModuleEvaluationError(module, error);
assert(module.status === MODULE_STATUS_ERRORED,
assert(module.status === MODULE_STATUS_EVALUATED_ERROR,
"Bad module status after failed evaluation");
assert(SameValue(UnsafeGetReservedSlot(module, MODULE_OBJECT_ERROR_SLOT), error),
"Module has different error set after failed evaluation");
throw error;
}
assert(module.status == MODULE_STATUS_EVALUATED,
assert(module.status === MODULE_STATUS_EVALUATED,
"Bad module status after successful evaluation");
assert(stack.length === 0,
"Stack should be empty after successful evaluation");
@ -571,19 +563,19 @@ function InnerModuleEvaluation(module, stack, index)
// TODO: Support module records other than source text module records.
// Step 2
if (module.status === MODULE_STATUS_EVALUATING ||
module.status === MODULE_STATUS_EVALUATED)
{
if (module.status === MODULE_STATUS_EVALUATED_ERROR)
throw GetModuleEvaluationError(module);
if (module.status === MODULE_STATUS_EVALUATED)
return index;
}
// Step 3
if (module.status === MODULE_STATUS_ERRORED)
throw module.error;
if (module.status === MODULE_STATUS_EVALUATING)
return index;
// Step 4
assert(module.status === MODULE_STATUS_INSTANTIATED,
"Bad module status in ModuleEvaluation");
"Bad module status in InnerModuleEvaluation");
// Step 5
ModuleSetStatus(module, MODULE_STATUS_EVALUATING);
@ -605,8 +597,8 @@ function InnerModuleEvaluation(module, stack, index)
index = InnerModuleEvaluation(requiredModule, stack, index);
assert(requiredModule.status == MODULE_STATUS_EVALUATING ||
requiredModule.status == MODULE_STATUS_EVALUATED,
assert(requiredModule.status === MODULE_STATUS_EVALUATING ||
requiredModule.status === MODULE_STATUS_EVALUATED,
"Bad module status after InnerModuleEvaluation");
assert((requiredModule.status === MODULE_STATUS_EVALUATING) ===

View File

@ -18,10 +18,10 @@
using namespace js;
using namespace js::frontend;
static_assert(MODULE_STATUS_ERRORED < MODULE_STATUS_UNINSTANTIATED &&
MODULE_STATUS_UNINSTANTIATED < MODULE_STATUS_INSTANTIATING &&
static_assert(MODULE_STATUS_UNINSTANTIATED < MODULE_STATUS_INSTANTIATING &&
MODULE_STATUS_INSTANTIATING < MODULE_STATUS_INSTANTIATED &&
MODULE_STATUS_INSTANTIATED < MODULE_STATUS_EVALUATED,
MODULE_STATUS_INSTANTIATED < MODULE_STATUS_EVALUATED &&
MODULE_STATUS_EVALUATED < MODULE_STATUS_EVALUATED_ERROR,
"Module statuses are ordered incorrectly");
template<typename T, Value ValueGetter(const T* obj)>
@ -248,21 +248,13 @@ IndirectBindingMap::Binding::Binding(ModuleEnvironmentObject* environment, Shape
: environment(environment), shape(shape)
{}
IndirectBindingMap::IndirectBindingMap(Zone* zone)
: map_(ZoneAllocPolicy(zone))
{
}
bool
IndirectBindingMap::init()
{
return map_.init();
}
void
IndirectBindingMap::trace(JSTracer* trc)
{
for (Map::Enum e(map_); !e.empty(); e.popFront()) {
if (!map_)
return;
for (Map::Enum e(*map_); !e.empty(); e.popFront()) {
Binding& b = e.front().value();
TraceEdge(trc, &b.environment, "module bindings environment");
TraceEdge(trc, &b.shape, "module bindings shape");
@ -273,12 +265,25 @@ IndirectBindingMap::trace(JSTracer* trc)
}
bool
IndirectBindingMap::putNew(JSContext* cx, HandleId name,
HandleModuleEnvironmentObject environment, HandleId localName)
IndirectBindingMap::put(JSContext* cx, HandleId name,
HandleModuleEnvironmentObject environment, HandleId localName)
{
// This object might have been allocated on the background parsing thread in
// different zone to the final module. Lazily allocate the map so we don't
// have to switch its zone when merging compartments.
if (!map_) {
MOZ_ASSERT(!cx->zone()->usedByExclusiveThread);
map_.emplace(cx->zone());
if (!map_->init()) {
map_.reset();
ReportOutOfMemory(cx);
return false;
}
}
RootedShape shape(cx, environment->lookup(cx, localName));
MOZ_ASSERT(shape);
if (!map_.putNew(name, Binding(environment, shape))) {
if (!map_->put(name, Binding(environment, shape))) {
ReportOutOfMemory(cx);
return false;
}
@ -289,7 +294,10 @@ IndirectBindingMap::putNew(JSContext* cx, HandleId name,
bool
IndirectBindingMap::lookup(jsid name, ModuleEnvironmentObject** envOut, Shape** shapeOut) const
{
auto ptr = map_.lookup(name);
if (!map_)
return false;
auto ptr = map_->lookup(name);
if (!ptr)
return false;
@ -359,7 +367,7 @@ ModuleNamespaceObject::addBinding(JSContext* cx, HandleAtom exportedName,
RootedModuleEnvironmentObject environment(cx, &targetModule->initialEnvironment());
RootedId exportedNameId(cx, AtomToId(exportedName));
RootedId localNameId(cx, AtomToId(localName));
return bindings->putNew(cx, exportedNameId, environment, localNameId);
return bindings->put(cx, exportedNameId, environment, localNameId);
}
const char ModuleNamespaceObject::ProxyHandler::family = 0;
@ -625,10 +633,9 @@ ModuleObject::create(ExclusiveContext* cx)
RootedModuleObject self(cx, &obj->as<ModuleObject>());
Zone* zone = cx->zone();
IndirectBindingMap* bindings = zone->new_<IndirectBindingMap>(zone);
if (!bindings || !bindings->init()) {
IndirectBindingMap* bindings = zone->new_<IndirectBindingMap>();
if (!bindings) {
ReportOutOfMemory(cx);
js_delete<IndirectBindingMap>(bindings);
return nullptr;
}
@ -657,14 +664,24 @@ ModuleObject::finalize(js::FreeOp* fop, JSObject* obj)
fop->delete_(funDecls);
}
ModuleEnvironmentObject&
ModuleObject::initialEnvironment() const
{
Value value = getReservedSlot(EnvironmentSlot);
return value.toObject().as<ModuleEnvironmentObject>();
}
ModuleEnvironmentObject*
ModuleObject::environment() const
{
Value value = getReservedSlot(EnvironmentSlot);
if (value.isUndefined())
MOZ_ASSERT(!hadEvaluationError());
// According to the spec the environment record is created during
// instantiation, but we create it earlier than that.
if (status() < MODULE_STATUS_INSTANTIATED)
return nullptr;
return &value.toObject().as<ModuleEnvironmentObject>();
return &initialEnvironment();
}
bool
@ -723,13 +740,13 @@ void
ModuleObject::init(HandleScript script)
{
initReservedSlot(ScriptSlot, PrivateValue(script));
initReservedSlot(StatusSlot, Int32Value(MODULE_STATUS_ERRORED));
initReservedSlot(StatusSlot, Int32Value(MODULE_STATUS_UNINSTANTIATED));
}
void
ModuleObject::setInitialEnvironment(HandleModuleEnvironmentObject initialEnvironment)
{
initReservedSlot(InitialEnvironmentSlot, ObjectValue(*initialEnvironment));
initReservedSlot(EnvironmentSlot, ObjectValue(*initialEnvironment));
}
void
@ -827,7 +844,8 @@ ModuleObject::script() const
static inline void
AssertValidModuleStatus(ModuleStatus status)
{
MOZ_ASSERT(status >= MODULE_STATUS_ERRORED && status <= MODULE_STATUS_EVALUATED);
MOZ_ASSERT(status >= MODULE_STATUS_UNINSTANTIATED &&
status <= MODULE_STATUS_EVALUATED_ERROR);
}
ModuleStatus
@ -838,11 +856,17 @@ ModuleObject::status() const
return status;
}
Value
ModuleObject::error() const
bool
ModuleObject::hadEvaluationError() const
{
MOZ_ASSERT(status() == MODULE_STATUS_ERRORED);
return getReservedSlot(ErrorSlot);
return status() == MODULE_STATUS_EVALUATED_ERROR;
}
Value
ModuleObject::evaluationError() const
{
MOZ_ASSERT(hadEvaluationError());
return getReservedSlot(EvaluationErrorSlot);
}
Value
@ -857,12 +881,6 @@ ModuleObject::setHostDefinedField(const JS::Value& value)
setReservedSlot(HostDefinedSlot, value);
}
ModuleEnvironmentObject&
ModuleObject::initialEnvironment() const
{
return getReservedSlot(InitialEnvironmentSlot).toObject().as<ModuleEnvironmentObject>();
}
Scope*
ModuleObject::enclosingScope() const
{
@ -888,16 +906,6 @@ ModuleObject::trace(JSTracer* trc, JSObject* obj)
funDecls->trace(trc);
}
void
ModuleObject::createEnvironment()
{
// The environment has already been created, we just neet to set it in the
// right slot.
MOZ_ASSERT(!getReservedSlot(InitialEnvironmentSlot).isUndefined());
MOZ_ASSERT(getReservedSlot(EnvironmentSlot).isUndefined());
setReservedSlot(EnvironmentSlot, getReservedSlot(InitialEnvironmentSlot));
}
bool
ModuleObject::noteFunctionDeclaration(ExclusiveContext* cx, HandleAtom name, HandleFunction fun)
{
@ -913,7 +921,10 @@ ModuleObject::noteFunctionDeclaration(ExclusiveContext* cx, HandleAtom name, Han
/* static */ bool
ModuleObject::instantiateFunctionDeclarations(JSContext* cx, HandleModuleObject self)
{
#ifdef DEBUG
MOZ_ASSERT(self->status() == MODULE_STATUS_INSTANTIATING);
MOZ_ASSERT(IsFrozen(cx, self));
#endif
FunctionDeclarationVector* funDecls = self->functionDeclarations();
if (!funDecls) {
@ -944,7 +955,10 @@ ModuleObject::instantiateFunctionDeclarations(JSContext* cx, HandleModuleObject
/* static */ bool
ModuleObject::execute(JSContext* cx, HandleModuleObject self, MutableHandleValue rval)
{
#ifdef DEBUG
MOZ_ASSERT(self->status() == MODULE_STATUS_EVALUATING);
MOZ_ASSERT(IsFrozen(cx, self));
#endif
RootedScript script(cx, self->script());
RootedModuleEnvironmentObject scope(cx, self->environment());
@ -967,10 +981,9 @@ ModuleObject::createNamespace(JSContext* cx, HandleModuleObject self, HandleObje
return nullptr;
Zone* zone = cx->zone();
IndirectBindingMap* bindings = zone->new_<IndirectBindingMap>(zone);
if (!bindings || !bindings->init()) {
IndirectBindingMap* bindings = zone->new_<IndirectBindingMap>();
if (!bindings) {
ReportOutOfMemory(cx);
js_delete<IndirectBindingMap>(bindings);
return nullptr;
}
@ -1005,7 +1018,7 @@ ModuleObject::Evaluate(JSContext* cx, HandleModuleObject self)
DEFINE_GETTER_FUNCTIONS(ModuleObject, namespace_, NamespaceSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, status, StatusSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, error, ErrorSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, evaluationError, EvaluationErrorSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, requestedModules, RequestedModulesSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, importEntries, ImportEntriesSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, localExportEntries, LocalExportEntriesSlot)
@ -1020,7 +1033,7 @@ GlobalObject::initModuleProto(JSContext* cx, Handle<GlobalObject*> global)
static const JSPropertySpec protoAccessors[] = {
JS_PSG("namespace", ModuleObject_namespace_Getter, 0),
JS_PSG("status", ModuleObject_statusGetter, 0),
JS_PSG("error", ModuleObject_errorGetter, 0),
JS_PSG("evaluationError", ModuleObject_evaluationErrorGetter, 0),
JS_PSG("requestedModules", ModuleObject_requestedModulesGetter, 0),
JS_PSG("importEntries", ModuleObject_importEntriesGetter, 0),
JS_PSG("localExportEntries", ModuleObject_localExportEntriesGetter, 0),

View File

@ -7,6 +7,8 @@
#ifndef builtin_ModuleObject_h
#define builtin_ModuleObject_h
#include "mozilla/Maybe.h"
#include "jsapi.h"
#include "jsatom.h"
@ -102,27 +104,27 @@ typedef Handle<ExportEntryObject*> HandleExportEntryObject;
class IndirectBindingMap
{
public:
explicit IndirectBindingMap(Zone* zone);
bool init();
void trace(JSTracer* trc);
bool putNew(JSContext* cx, HandleId name,
HandleModuleEnvironmentObject environment, HandleId localName);
bool put(JSContext* cx, HandleId name,
HandleModuleEnvironmentObject environment, HandleId localName);
size_t count() const {
return map_.count();
return map_ ? map_->count() : 0;
}
bool has(jsid name) const {
return map_.has(name);
return map_ ? map_->has(name) : false;
}
bool lookup(jsid name, ModuleEnvironmentObject** envOut, Shape** shapeOut) const;
template <typename Func>
void forEachExportedName(Func func) const {
for (auto r = map_.all(); !r.empty(); r.popFront())
if (!map_)
return;
for (auto r = map_->all(); !r.empty(); r.popFront())
func(r.front().key());
}
@ -136,7 +138,7 @@ class IndirectBindingMap
typedef HashMap<jsid, Binding, DefaultHasher<jsid>, ZoneAllocPolicy> Map;
Map map_;
mozilla::Maybe<Map> map_;
};
class ModuleNamespaceObject : public ProxyObject
@ -214,11 +216,10 @@ class ModuleObject : public NativeObject
enum
{
ScriptSlot = 0,
InitialEnvironmentSlot,
EnvironmentSlot,
NamespaceSlot,
StatusSlot,
ErrorSlot,
EvaluationErrorSlot,
HostDefinedSlot,
RequestedModulesSlot,
ImportEntriesSlot,
@ -238,8 +239,8 @@ class ModuleObject : public NativeObject
"EnvironmentSlot must match self-hosting define");
static_assert(StatusSlot == MODULE_OBJECT_STATUS_SLOT,
"StatusSlot must match self-hosting define");
static_assert(ErrorSlot == MODULE_OBJECT_ERROR_SLOT,
"ErrorSlot must match self-hosting define");
static_assert(EvaluationErrorSlot == MODULE_OBJECT_EVALUATION_ERROR_SLOT,
"EvaluationErrorSlot must match self-hosting define");
static_assert(DFSIndexSlot == MODULE_OBJECT_DFS_INDEX_SLOT,
"DFSIndexSlot must match self-hosting define");
static_assert(DFSAncestorIndexSlot == MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT,
@ -269,7 +270,8 @@ class ModuleObject : public NativeObject
ModuleEnvironmentObject* environment() const;
ModuleNamespaceObject* namespace_();
ModuleStatus status() const;
Value error() const;
bool hadEvaluationError() const;
Value evaluationError() const;
Value hostDefinedField() const;
ArrayObject& requestedModules() const;
ArrayObject& importEntries() const;
@ -285,9 +287,6 @@ class ModuleObject : public NativeObject
void setHostDefinedField(const JS::Value& value);
// For intrinsic_CreateModuleEnvironment.
void createEnvironment();
// For BytecodeEmitter.
bool noteFunctionDeclaration(ExclusiveContext* cx, HandleAtom name, HandleFunction fun);

View File

@ -97,17 +97,17 @@
#define REGEXP_STRING_ITERATOR_FLAGS_SLOT 2
#define REGEXP_STRING_ITERATOR_DONE_SLOT 3
#define MODULE_OBJECT_ENVIRONMENT_SLOT 2
#define MODULE_OBJECT_STATUS_SLOT 4
#define MODULE_OBJECT_ERROR_SLOT 5
#define MODULE_OBJECT_DFS_INDEX_SLOT 16
#define MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT 17
#define MODULE_OBJECT_ENVIRONMENT_SLOT 1
#define MODULE_OBJECT_STATUS_SLOT 3
#define MODULE_OBJECT_EVALUATION_ERROR_SLOT 4
#define MODULE_OBJECT_DFS_INDEX_SLOT 15
#define MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT 16
#define MODULE_STATUS_ERRORED 0
#define MODULE_STATUS_UNINSTANTIATED 1
#define MODULE_STATUS_INSTANTIATING 2
#define MODULE_STATUS_INSTANTIATED 3
#define MODULE_STATUS_EVALUATING 4
#define MODULE_STATUS_EVALUATED 5
#define MODULE_STATUS_UNINSTANTIATED 0
#define MODULE_STATUS_INSTANTIATING 1
#define MODULE_STATUS_INSTANTIATED 2
#define MODULE_STATUS_EVALUATING 3
#define MODULE_STATUS_EVALUATED 4
#define MODULE_STATUS_EVALUATED_ERROR 5
#endif

View File

@ -3566,8 +3566,6 @@ GetModuleEnvironment(JSContext* cx, HandleValue moduleValue)
// before they have been instantiated.
RootedModuleEnvironmentObject env(cx, &module->initialEnvironment());
MOZ_ASSERT(env);
MOZ_ASSERT_IF(module->environment(), module->environment() == env);
return env;
}
@ -3585,6 +3583,9 @@ GetModuleEnvironmentNames(JSContext* cx, unsigned argc, Value* vp)
return false;
}
//- if (module->status() == MODULE_STATUS_ERRORED) {
//+ if (module->hadEvaluationError()) {
RootedModuleEnvironmentObject env(cx, GetModuleEnvironment(cx, args[0]));
Rooted<IdVector> ids(cx, IdVector(cx));
if (!JS_Enumerate(cx, env, &ids))
@ -3622,6 +3623,9 @@ GetModuleEnvironmentValue(JSContext* cx, unsigned argc, Value* vp)
return false;
}
//- if (module->status() == MODULE_STATUS_ERRORED) {
//+ if (module->hadEvaluationError()) {
RootedModuleEnvironmentObject env(cx, GetModuleEnvironment(cx, args[0]));
RootedString name(cx, args[1].toString());
RootedId id(cx);

View File

@ -1,9 +1,8 @@
// This tests that attempting to perform ModuleDeclarationInstantation a second
// time after a failure re-throws the same error.
// This tests that module instantiation can succeed when executed a second
// time after a failure.
//
// The first attempt fails becuase module 'a' is not available. The second
// attempt fails because of the previous failure (it would otherwise succeed as
// 'a' is now available).
// attempt succeeds as 'a' is now available.
load(libdir + "dummyModuleResolveHook.js");
@ -25,12 +24,9 @@ let a = moduleRepo['a'] = parseModule("export var a = 1; export var b = 2;");
let d = moduleRepo['d'] = parseModule("import { a } from 'c'; a;");
threw = false;
let e2;
try {
d.declarationInstantiation();
} catch (exc) {
threw = true;
e2 = exc;
}
assertEq(threw, true);
assertEq(e1, e2);
assertEq(threw, false);

View File

@ -0,0 +1,19 @@
// Test re-instantiation module after failure.
load(libdir + "asserts.js");
load(libdir + "dummyModuleResolveHook.js");
moduleRepo["good"] = parseModule(`export let x`);
moduleRepo["y1"] = parseModule(`export let y`);
moduleRepo["y2"] = parseModule(`export let y`);
moduleRepo["bad"] = parseModule(`export* from "y1"; export* from "y2";`);
moduleRepo["a"] = parseModule(`import* as ns from "good"; import {y} from "bad";`);
let b = moduleRepo["b"] = parseModule(`import "a";`);
let c = moduleRepo["c"] = parseModule(`import "a";`);
assertThrowsInstanceOf(() => b.declarationInstantiation(), SyntaxError);
assertThrowsInstanceOf(() => c.declarationInstantiation(), SyntaxError);

View File

@ -0,0 +1,9 @@
if (!('stackTest' in this))
quit();
let a = parseModule(`throw new Error`);
a.declarationInstantiation();
stackTest(function() {
a.evaluation();
});

View File

@ -0,0 +1,16 @@
load(libdir + "asserts.js");
load(libdir + "dummyModuleResolveHook.js");
moduleRepo["a"] = parseModule(`throw undefined`);
let b = moduleRepo["b"] = parseModule(`import "a";`);
let c = moduleRepo["c"] = parseModule(`import "a";`);
b.declarationInstantiation();
c.declarationInstantiation();
let count = 0;
try { b.evaluation() } catch (e) { count++; }
try { c.evaluation() } catch (e) { count++; }
assertEq(count, 2);

View File

@ -0,0 +1,19 @@
// Test re-instantiation module after failure.
load(libdir + "asserts.js");
load(libdir + "dummyModuleResolveHook.js");
moduleRepo["good"] = parseModule(`export let x`);
moduleRepo["y1"] = parseModule(`export let y`);
moduleRepo["y2"] = parseModule(`export let y`);
moduleRepo["bad"] = parseModule(`export* from "y1"; export* from "y2";`);
moduleRepo["a"] = parseModule(`import {x} from "good"; import {y} from "bad";`);
let b = moduleRepo["b"] = parseModule(`import "a";`);
let c = moduleRepo["c"] = parseModule(`import "a";`);
assertThrowsInstanceOf(() => b.declarationInstantiation(), SyntaxError);
assertThrowsInstanceOf(() => c.declarationInstantiation(), SyntaxError);

View File

@ -579,7 +579,6 @@ MSG_DEF(JSMSG_MISSING_IMPORT, 0, JSEXN_SYNTAXERR, "import not found")
MSG_DEF(JSMSG_AMBIGUOUS_IMPORT, 0, JSEXN_SYNTAXERR, "ambiguous import")
MSG_DEF(JSMSG_MISSING_NAMESPACE_EXPORT, 0, JSEXN_SYNTAXERR, "export not found for namespace")
MSG_DEF(JSMSG_MISSING_EXPORT, 1, JSEXN_SYNTAXERR, "local binding for export '{0}' not found")
MSG_DEF(JSMSG_MODULE_INSTANTIATE_FAILED, 0, JSEXN_INTERNALERR, "attempt to re-instantiate module after failure")
MSG_DEF(JSMSG_BAD_MODULE_STATUS, 0, JSEXN_INTERNALERR, "module record has unexpected status")
// Promise

View File

@ -4662,21 +4662,16 @@ JS::Evaluate(JSContext* cx, const ReadOnlyCompileOptions& optionsArg,
return ::Evaluate(cx, optionsArg, filename, rval);
}
JS_PUBLIC_API(JSFunction*)
JS::GetModuleResolveHook(JSContext* cx)
JS_PUBLIC_API(JS::ModuleResolveHook)
JS::GetModuleResolveHook(JSRuntime* rt)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
return cx->global()->moduleResolveHook();
return rt->moduleResolveHook;
}
JS_PUBLIC_API(void)
JS::SetModuleResolveHook(JSContext* cx, HandleFunction func)
JS::SetModuleResolveHook(JSRuntime* rt, JS::ModuleResolveHook func)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, func);
cx->global()->setModuleResolveHook(func);
rt->moduleResolveHook = func;
}
JS_PUBLIC_API(bool)
@ -4739,18 +4734,6 @@ JS::GetModuleScript(JSContext* cx, JS::HandleObject moduleArg)
return moduleArg->as<ModuleObject>().script();
}
JS_PUBLIC_API(bool)
JS::IsModuleErrored(JSObject* moduleArg)
{
return moduleArg->as<ModuleObject>().status() == MODULE_STATUS_ERRORED;
}
JS_PUBLIC_API(JS::Value)
JS::GetModuleError(JSObject* moduleArg)
{
return moduleArg->as<ModuleObject>().error();
}
JS_PUBLIC_API(JSObject*)
JS_New(JSContext* cx, HandleObject ctor, const JS::HandleValueArray& inputArgs)
{

View File

@ -4322,17 +4322,19 @@ extern JS_PUBLIC_API(bool)
Evaluate(JSContext* cx, const ReadOnlyCompileOptions& options,
const char* filename, JS::MutableHandleValue rval);
/**
* Get the HostResolveImportedModule hook for a global.
*/
extern JS_PUBLIC_API(JSFunction*)
GetModuleResolveHook(JSContext* cx);
using ModuleResolveHook = JSObject* (*)(JSContext*, HandleObject, HandleString);
/**
* Set the HostResolveImportedModule hook for a global to the given function.
* Get the HostResolveImportedModule hook for the runtime.
*/
extern JS_PUBLIC_API(ModuleResolveHook)
GetModuleResolveHook(JSRuntime* rt);
/**
* Set the HostResolveImportedModule hook for the runtime to the given function.
*/
extern JS_PUBLIC_API(void)
SetModuleResolveHook(JSContext* cx, JS::HandleFunction func);
SetModuleResolveHook(JSRuntime* rt, ModuleResolveHook func);
/**
* Parse the given source buffer as a module in the scope of the current global
@ -4395,12 +4397,6 @@ GetRequestedModules(JSContext* cx, JS::HandleObject moduleRecord);
extern JS_PUBLIC_API(JSScript*)
GetModuleScript(JSContext* cx, JS::HandleObject moduleRecord);
extern JS_PUBLIC_API(bool)
IsModuleErrored(JSObject* moduleRecord);
extern JS_PUBLIC_API(JS::Value)
GetModuleError(JSObject* moduleRecord);
} /* namespace JS */
extern JS_PUBLIC_API(bool)

View File

@ -274,6 +274,7 @@ struct ShellContext
JS::PersistentRooted<JobQueue> jobQueue;
ExclusiveData<ShellAsyncTasks> asyncTasks;
bool drainingJobQueue;
JS::PersistentRootedFunction moduleResolveHook;
/*
* Watchdog thread state.
@ -439,7 +440,8 @@ ShellContext::ShellContext(JSContext* cx)
exitCode(0),
quitting(false),
readLineBufPos(0),
spsProfilingStackSize(0)
spsProfilingStackSize(0),
moduleResolveHook(cx)
{}
static ShellContext*
@ -4030,13 +4032,34 @@ SetModuleResolveHook(JSContext* cx, unsigned argc, Value* vp)
return false;
}
RootedFunction hook(cx, &args[0].toObject().as<JSFunction>());
Rooted<GlobalObject*> global(cx, cx->global());
global->setModuleResolveHook(hook);
ShellContext* sc = GetShellContext(cx);
sc->moduleResolveHook = &args[0].toObject().as<JSFunction>();
args.rval().setUndefined();
return true;
}
static JSObject*
CallModuleResolveHook(JSContext* cx, HandleObject module, HandleString specifier)
{
ShellContext* sc = GetShellContext(cx);
JS::AutoValueArray<2> args(cx);
args[0].setObject(*module);
args[1].setString(specifier);
RootedValue result(cx);
if (!JS_CallFunction(cx, nullptr, sc->moduleResolveHook, args, &result))
return nullptr;
if (!result.isObject() || !result.toObject().is<ModuleObject>()) {
JS_ReportErrorASCII(cx, "Module resolve hook did not return Module object");
return nullptr;
}
return &result.toObject();
}
static bool
GetModuleLoadPath(JSContext* cx, unsigned argc, Value* vp)
{
@ -7962,6 +7985,8 @@ main(int argc, char** argv, char** envp)
js::SetPreserveWrapperCallback(cx, DummyPreserveWrapperCallback);
JS::SetModuleResolveHook(cx->runtime(), CallModuleResolveHook);
result = Shell(cx, &op, envp);
#ifdef DEBUG

View File

@ -491,7 +491,7 @@ ModuleEnvironmentObject::createImportBinding(JSContext* cx, HandleAtom importNam
RootedId importNameId(cx, AtomToId(importName));
RootedId localNameId(cx, AtomToId(localName));
RootedModuleEnvironmentObject env(cx, &module->initialEnvironment());
if (!importBindings().putNew(cx, importNameId, env, localNameId)) {
if (!importBindings().put(cx, importNameId, env, localNameId)) {
ReportOutOfMemory(cx);
return false;
}

View File

@ -119,7 +119,6 @@ class GlobalObject : public NativeObject
DEBUGGERS,
INTRINSICS,
FOR_OF_PIC_CHAIN,
MODULE_RESOLVE_HOOK,
WINDOW_PROXY,
GLOBAL_THIS_RESOLVED,
@ -879,19 +878,6 @@ class GlobalObject : public NativeObject
setReservedSlot(WINDOW_PROXY, ObjectValue(*windowProxy));
}
void setModuleResolveHook(HandleFunction hook) {
MOZ_ASSERT(hook);
setSlot(MODULE_RESOLVE_HOOK, ObjectValue(*hook));
}
JSFunction* moduleResolveHook() {
Value value = getSlotRef(MODULE_RESOLVE_HOOK);
if (value.isUndefined())
return nullptr;
return &value.toObject().as<JSFunction>();
}
// Returns either this global's star-generator function prototype, or null
// if that object was never created. Dodgy; for use only in also-dodgy
// GlobalHelperThreadState::mergeParseTaskCompartment().

View File

@ -241,8 +241,10 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
lastAnimationTime(0),
performanceMonitoring(thisFromCtor()),
ionLazyLinkListSize_(0),
stackFormat_(parentRuntime ? js::StackFormat::Default
: js::StackFormat::SpiderMonkey)
stackFormat_(parentRuntime ?
js::StackFormat::Default :
js::StackFormat::SpiderMonkey),
moduleResolveHook()
{
setGCStoreBufferPtr(&gc.storeBuffer);

View File

@ -1294,6 +1294,9 @@ struct JSRuntime : public JS::shadow::Runtime,
// For inherited heap state accessors.
friend class js::gc::AutoTraceSession;
friend class JS::AutoEnterCycleCollection;
// The implementation-defined abstract operation HostResolveImportedModule.
JS::ModuleResolveHook moduleResolveHook;
};
namespace js {

View File

@ -2026,36 +2026,26 @@ intrinsic_HostResolveImportedModule(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
MOZ_ASSERT(args[0].toObject().is<ModuleObject>());
MOZ_ASSERT(args[1].isString());
RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
RootedString specifier(cx, args[1].toString());
RootedFunction moduleResolveHook(cx, cx->global()->moduleResolveHook());
JS::ModuleResolveHook moduleResolveHook = cx->runtime()->moduleResolveHook;
if (!moduleResolveHook) {
JS_ReportErrorASCII(cx, "Module resolve hook not set");
return false;
}
RootedValue result(cx);
if (!JS_CallFunction(cx, nullptr, moduleResolveHook, args, &result))
RootedObject result(cx);
result = moduleResolveHook(cx, module, specifier);
if (!result)
return false;
if (!result.isObject() || !result.toObject().is<ModuleObject>()) {
if (!result->is<ModuleObject>()) {
JS_ReportErrorASCII(cx, "Module resolve hook did not return Module object");
return false;
}
args.rval().set(result);
return true;
}
static bool
intrinsic_CreateModuleEnvironment(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
module->createEnvironment();
args.rval().setUndefined();
args.rval().setObject(*result);
return true;
}
@ -2087,7 +2077,6 @@ intrinsic_CreateNamespaceBinding(JSContext* cx, unsigned argc, Value* vp)
// the slot directly.
RootedShape shape(cx, environment->lookup(cx, name));
MOZ_ASSERT(shape);
MOZ_ASSERT(environment->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL));
environment->setSlot(shape->slot(), args[2]);
args.rval().setUndefined();
return true;
@ -2659,7 +2648,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
CallNonGenericSelfhostedMethod<Is<ModuleObject>>, 2, 0),
JS_FN("HostResolveImportedModule", intrinsic_HostResolveImportedModule, 2, 0),
JS_FN("IsModuleEnvironment", intrinsic_IsInstanceOfBuiltin<ModuleEnvironmentObject>, 1, 0),
JS_FN("CreateModuleEnvironment", intrinsic_CreateModuleEnvironment, 1, 0),
JS_FN("CreateImportBinding", intrinsic_CreateImportBinding, 4, 0),
JS_FN("CreateNamespaceBinding", intrinsic_CreateNamespaceBinding, 3, 0),
JS_FN("InstantiateModuleFunctionDeclarations",

View File

@ -6,9 +6,12 @@
#include "nsHtml5TreeOpExecutor.h"
nsHtml5SpeculativeLoad::nsHtml5SpeculativeLoad()
:
#ifdef DEBUG
: mOpCode(eSpeculativeLoadUninitialized)
mOpCode(eSpeculativeLoadUninitialized),
#endif
mIsAsync(false),
mIsDefer(false)
{
MOZ_COUNT_CTOR(nsHtml5SpeculativeLoad);
}
@ -48,11 +51,13 @@ nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor)
break;
case eSpeculativeLoadScript:
aExecutor->PreloadScript(mUrl, mCharset, mTypeOrCharsetSourceOrDocumentMode,
mCrossOrigin, mIntegrity, false);
mCrossOrigin, mIntegrity, false,
mIsAsync, mIsDefer);
break;
case eSpeculativeLoadScriptFromHead:
aExecutor->PreloadScript(mUrl, mCharset, mTypeOrCharsetSourceOrDocumentMode,
mCrossOrigin, mIntegrity, true);
mCrossOrigin, mIntegrity, true,
mIsAsync, mIsDefer);
break;
case eSpeculativeLoadStyle:
aExecutor->PreloadStyle(mUrl, mCharset, mCrossOrigin, mIntegrity);

View File

@ -128,7 +128,9 @@ class nsHtml5SpeculativeLoad {
nsHtml5String aType,
nsHtml5String aCrossOrigin,
nsHtml5String aIntegrity,
bool aParserInHead)
bool aParserInHead,
bool aAsync,
bool aDefer)
{
NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
"Trying to reinitialize a speculative load!");
@ -139,6 +141,8 @@ class nsHtml5SpeculativeLoad {
aType.ToString(mTypeOrCharsetSourceOrDocumentMode);
aCrossOrigin.ToString(mCrossOrigin);
aIntegrity.ToString(mIntegrity);
mIsAsync = aAsync;
mIsDefer = aDefer;
}
inline void InitStyle(nsHtml5String aUrl,
@ -221,6 +225,13 @@ class nsHtml5SpeculativeLoad {
private:
eHtml5SpeculativeLoad mOpCode;
/**
* Whether the refering element has async and/or defer attributes.
*/
bool mIsAsync;
bool mIsDefer;
nsString mUrl;
nsString mReferrerPolicy;
nsString mMetaCSP;

View File

@ -185,16 +185,20 @@ nsHtml5TreeBuilder::createElement(int32_t aNamespace,
aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
nsHtml5String integrity =
aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY);
bool async =
aAttributes->contains(nsHtml5AttributeName::ATTR_ASYNC);
bool defer =
aAttributes->contains(nsHtml5AttributeName::ATTR_DEFER);
mSpeculativeLoadQueue.AppendElement()->InitScript(
url,
charset,
type,
crossOrigin,
integrity,
mode == nsHtml5TreeBuilder::IN_HEAD);
mCurrentHtmlScriptIsAsyncOrDefer =
aAttributes->contains(nsHtml5AttributeName::ATTR_ASYNC) ||
aAttributes->contains(nsHtml5AttributeName::ATTR_DEFER);
mode == nsHtml5TreeBuilder::IN_HEAD,
async,
defer);
mCurrentHtmlScriptIsAsyncOrDefer = async || defer;
}
} else if (nsHtml5Atoms::link == aName) {
nsHtml5String rel =
@ -297,7 +301,9 @@ nsHtml5TreeBuilder::createElement(int32_t aNamespace,
type,
crossOrigin,
integrity,
mode == nsHtml5TreeBuilder::IN_HEAD);
mode == nsHtml5TreeBuilder::IN_HEAD,
false /* async */,
false /* defer */);
}
} else if (nsHtml5Atoms::style == aName) {
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();

View File

@ -921,14 +921,16 @@ nsHtml5TreeOpExecutor::PreloadScript(const nsAString& aURL,
const nsAString& aType,
const nsAString& aCrossOrigin,
const nsAString& aIntegrity,
bool aScriptFromHead)
bool aScriptFromHead,
bool aAsync,
bool aDefer)
{
nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
if (!uri) {
return;
}
mDocument->ScriptLoader()->PreloadURI(uri, aCharset, aType, aCrossOrigin,
aIntegrity, aScriptFromHead,
aIntegrity, aScriptFromHead, aAsync, aDefer,
mSpeculationReferrerPolicy);
}

View File

@ -249,7 +249,9 @@ class nsHtml5TreeOpExecutor final : public nsHtml5DocumentBuilder,
const nsAString& aType,
const nsAString& aCrossOrigin,
const nsAString& aIntegrity,
bool aScriptFromHead);
bool aScriptFromHead,
bool aAsync,
bool aDefer);
void PreloadStyle(const nsAString& aURL, const nsAString& aCharset,
const nsAString& aCrossOrigin,

View File

@ -1076,7 +1076,7 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder,
nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(node);
NS_ASSERTION(sele, "Node didn't QI to script.");
sele->SetScriptLineNumber(mFour.integer);
sele->FreezeUriAsyncDefer();
sele->FreezeExecutionAttrs(node->OwnerDoc());
return NS_OK;
}
case eTreeOpSvgLoad: {