Implement module type scripting.

master
Fedor 2020-07-16 03:48:35 +03:00
parent 68fc602db6
commit ef217a94a8
33 changed files with 1113 additions and 464 deletions

View File

@ -308,17 +308,18 @@ nsJSUtils::CompileModule(JSContext* aCx,
} }
nsresult nsresult
nsJSUtils::ModuleDeclarationInstantiation(JSContext* aCx, JS::Handle<JSObject*> aModule) nsJSUtils::ModuleInstantiate(JSContext* aCx, JS::Handle<JSObject*> aModule)
{ {
PROFILER_LABEL("nsJSUtils", "ModuleDeclarationInstantiation", PROFILER_LABEL("nsJSUtils", "ModuleInstantiate",
js::ProfileEntry::Category::JS); js::ProfileEntry::Category::JS);
MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext()); MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(nsContentUtils::IsInMicroTask());
NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule).Allowed(), NS_OK); NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule).Allowed(), NS_OK);
if (!JS::ModuleDeclarationInstantiation(aCx, aModule)) { if (!JS::ModuleInstantiate(aCx, aModule)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -326,9 +327,9 @@ nsJSUtils::ModuleDeclarationInstantiation(JSContext* aCx, JS::Handle<JSObject*>
} }
nsresult nsresult
nsJSUtils::ModuleEvaluation(JSContext* aCx, JS::Handle<JSObject*> aModule) nsJSUtils::ModuleEvaluate(JSContext* aCx, JS::Handle<JSObject*> aModule)
{ {
PROFILER_LABEL("nsJSUtils", "ModuleEvaluation", PROFILER_LABEL("nsJSUtils", "ModuleEvaluate",
js::ProfileEntry::Category::JS); js::ProfileEntry::Category::JS);
MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext()); MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
@ -338,7 +339,7 @@ nsJSUtils::ModuleEvaluation(JSContext* aCx, JS::Handle<JSObject*> aModule)
NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule).Allowed(), NS_OK); NS_ENSURE_TRUE(xpc::Scriptability::Get(aModule).Allowed(), NS_OK);
if (!JS::ModuleEvaluation(aCx, aModule)) { if (!JS::ModuleEvaluate(aCx, aModule)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }

View File

@ -116,11 +116,11 @@ public:
JS::CompileOptions &aCompileOptions, JS::CompileOptions &aCompileOptions,
JS::MutableHandle<JSObject*> aModule); JS::MutableHandle<JSObject*> aModule);
static nsresult ModuleDeclarationInstantiation(JSContext* aCx, static nsresult ModuleInstantiate(JSContext* aCx,
JS::Handle<JSObject*> aModule); JS::Handle<JSObject*> aModule);
static nsresult ModuleEvaluation(JSContext* aCx, static nsresult ModuleEvaluate(JSContext* aCx,
JS::Handle<JSObject*> aModule); JS::Handle<JSObject*> aModule);
// Returns false if an exception got thrown on aCx. Passing a null // Returns false if an exception got thrown on aCx. Passing a null
// aElement is allowed; that wil produce an empty aScopeChain. // aElement is allowed; that wil produce an empty aScopeChain.

View File

@ -4,20 +4,27 @@
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script> <script>
var wasRun = false; var wasRun = false;
var hadSyntaxError = false; var errorCount = 0;
var syntaxErrorCount = 0;
var eventCount = 0;
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
window.onerror = handleError; window.onerror = handleError;
function handleError(message, url, line, column, error) { function handleError(message, url, line, column, error) {
hadSyntaxError = error instanceof SyntaxError; errorCount++;
if (error instanceof SyntaxError) {
syntaxErrorCount++;
}
} }
function testError() { function testError() {
ok(!wasRun, 'Check script was not run'); ok(!wasRun, 'Check script was not run');
ok(hadSyntaxError, 'Check that a SyntaxError was thrown'); ok(errorCount == 1, 'Check that an error was reported');
ok(syntaxErrorCount == 1, 'Check that a syntax error was reported');
ok(eventCount == 0, 'Check that no error event was fired');
SimpleTest.finish(); SimpleTest.finish();
} }
</script> </script>
<script type="module" src="module_badSyntax.js"></script> <script type="module" src="module_badSyntax.js" onerror="eventCount++"></script>
<body onload='testError()'></body> <body onload='testError()'></body>

View File

@ -4,20 +4,27 @@
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script> <script>
var wasRun = false; var wasRun = false;
var hadSyntaxError = false; var errorCount = 0;
var syntaxErrorCount = 0;
var eventCount = 0;
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
window.onerror = handleError; window.onerror = handleError;
function handleError(message, url, line, column, error) { function handleError(message, url, line, column, error) {
hadSyntaxError = error instanceof SyntaxError; errorCount++;
if (error instanceof SyntaxError) {
syntaxErrorCount++;
}
} }
function testError() { function testError() {
ok(!wasRun, 'Check script was not run'); ok(!wasRun, 'Check script was not run');
ok(hadSyntaxError, 'Check that a SyntaxError was thrown'); ok(errorCount == 1, 'Check that an error was reported');
ok(syntaxErrorCount == 1, 'Check that a syntax error was reported');
ok(eventCount == 0, 'Check that no error event was fired');
SimpleTest.finish(); SimpleTest.finish();
} }
</script> </script>
<script type="module" src="module_badSyntax.js" async></script> <script type="module" src="module_badSyntax.js" async onerror="eventCount++"></script>
<body onload='testError()'></body> <body onload='testError()'></body>

View File

@ -4,22 +4,29 @@
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script> <script>
var wasRun = false; var wasRun = false;
var hadSyntaxError = false; var errorCount = 0;
var syntaxErrorCount = 0;
var eventCount = 0;
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
window.onerror = handleError; window.onerror = handleError;
function handleError(message, url, line, column, error) { function handleError(message, url, line, column, error) {
hadSyntaxError = error instanceof SyntaxError; errorCount++;
if (error instanceof SyntaxError) {
syntaxErrorCount++;
}
} }
function testError() { function testError() {
ok(!wasRun, 'Check script was not run'); ok(!wasRun, 'Check script was not run');
ok(hadSyntaxError, 'Check that a SyntaxError was thrown'); ok(errorCount == 1, 'Check that an error was reported');
ok(syntaxErrorCount == 1, 'Check that a syntax error was reported');
ok(eventCount == 0, 'Check that no error event was fired');
SimpleTest.finish(); SimpleTest.finish();
} }
</script> </script>
<script type="module"> <script type="module" onerror="eventCount++">
// Module with a syntax error. // Module with a syntax error.
some invalid js syntax; some invalid js syntax;
wasRun = true; wasRun = true;

View File

@ -4,22 +4,29 @@
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script> <script>
var wasRun = false; var wasRun = false;
var hadSyntaxError = false; var errorCount = 0;
var syntaxErrorCount = 0;
var eventCount = 0;
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
window.onerror = handleError; window.onerror = handleError;
function handleError(message, url, line, column, error) { function handleError(message, url, line, column, error) {
hadSyntaxError = error instanceof SyntaxError; errorCount++;
if (error instanceof SyntaxError) {
syntaxErrorCount++;
}
} }
function testError() { function testError() {
ok(!wasRun, 'Check script was not run'); ok(!wasRun, 'Check script was not run');
ok(hadSyntaxError, 'Check that a SyntaxError was thrown'); ok(errorCount == 1, 'Check that an error was reported');
ok(syntaxErrorCount == 1, 'Check that a syntax error was reported');
ok(eventCount == 0, 'Check that no error event was fired');
SimpleTest.finish(); SimpleTest.finish();
} }
</script> </script>
<script type="module" async> <script type="module" async onerror="eventCount++">
// Module with a syntax error. // Module with a syntax error.
some invalid js syntax; some invalid js syntax;
wasRun = true; wasRun = true;

View File

@ -43,15 +43,29 @@ void ModuleLoadRequest::Cancel()
ScriptLoadRequest::Cancel(); ScriptLoadRequest::Cancel();
mModuleScript = nullptr; mModuleScript = nullptr;
mProgress = ScriptLoadRequest::Progress::Ready; mProgress = ScriptLoadRequest::Progress::Ready;
CancelImports();
mReady.RejectIfExists(NS_ERROR_DOM_ABORT_ERR, __func__);
}
void
ModuleLoadRequest::CancelImports()
{
for (size_t i = 0; i < mImports.Length(); i++) { for (size_t i = 0; i < mImports.Length(); i++) {
mImports[i]->Cancel(); mImports[i]->Cancel();
} }
mReady.RejectIfExists(NS_ERROR_FAILURE, __func__);
} }
void void
ModuleLoadRequest::SetReady() ModuleLoadRequest::SetReady()
{ {
// Mark a module as ready to execute. This means that this module and all it
// dependencies have had their source loaded, parsed as a module and the
// modules instantiated.
//
// The mReady promise is used to ensure that when all dependencies of a module
// have become ready, DependenciesLoaded is called on that module
// request. This is set up in StartFetchingModuleDependencies.
#ifdef DEBUG #ifdef DEBUG
for (size_t i = 0; i < mImports.Length(); i++) { for (size_t i = 0; i < mImports.Length(); i++) {
MOZ_ASSERT(mImports[i]->IsReadyToRun()); MOZ_ASSERT(mImports[i]->IsReadyToRun());
@ -69,30 +83,53 @@ ModuleLoadRequest::ModuleLoaded()
// been loaded. // been loaded.
mModuleScript = mLoader->GetFetchedModule(mURI); mModuleScript = mLoader->GetFetchedModule(mURI);
if (!mModuleScript || mModuleScript->IsErrored()) {
ModuleErrored();
return;
}
mLoader->StartFetchingModuleDependencies(this); mLoader->StartFetchingModuleDependencies(this);
} }
void
ModuleLoadRequest::ModuleErrored()
{
mLoader->CheckModuleDependenciesLoaded(this);
MOZ_ASSERT(!mModuleScript || mModuleScript->IsErrored());
CancelImports();
SetReady();
LoadFinished();
}
void void
ModuleLoadRequest::DependenciesLoaded() ModuleLoadRequest::DependenciesLoaded()
{ {
// The module and all of its dependencies have been successfully fetched and // The module and all of its dependencies have been successfully fetched and
// compiled. // compiled.
if (!mLoader->InstantiateModuleTree(this)) { MOZ_ASSERT(mModuleScript);
LoadFailed();
return;
}
mLoader->CheckModuleDependenciesLoaded(this);
SetReady(); SetReady();
mLoader->ProcessLoadedModuleTree(this); LoadFinished();
mLoader = nullptr;
mParent = nullptr;
} }
void void
ModuleLoadRequest::LoadFailed() ModuleLoadRequest::LoadFailed()
{ {
// We failed to load the source text or an error occurred unrelated to the
// content of the module (e.g. OOM).
MOZ_ASSERT(!mModuleScript);
Cancel(); Cancel();
LoadFinished();
}
void
ModuleLoadRequest::LoadFinished()
{
mLoader->ProcessLoadedModuleTree(this); mLoader->ProcessLoadedModuleTree(this);
mLoader = nullptr; mLoader = nullptr;
mParent = nullptr; mParent = nullptr;

View File

@ -45,9 +45,15 @@ public:
void Cancel() override; void Cancel() override;
void ModuleLoaded(); void ModuleLoaded();
void ModuleErrored();
void DependenciesLoaded(); void DependenciesLoaded();
void LoadFailed(); void LoadFailed();
private:
void LoadFinished();
void CancelImports();
public:
// Is this a request for a top level module script or an import? // Is this a request for a top level module script or an import?
bool mIsTopLevel; bool mIsTopLevel;

View File

@ -26,6 +26,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ModuleScript)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoader) NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoader)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL) NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL)
tmp->UnlinkModuleRecord(); tmp->UnlinkModuleRecord();
tmp->mError.setUndefined();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ModuleScript) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ModuleScript)
@ -34,28 +35,20 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ModuleScript) NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ModuleScript)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mModuleRecord) NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mModuleRecord)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mException) NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mError)
NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleScript) NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleScript)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ModuleScript) NS_IMPL_CYCLE_COLLECTING_RELEASE(ModuleScript)
ModuleScript::ModuleScript(ScriptLoader *aLoader, nsIURI* aBaseURL, ModuleScript::ModuleScript(ScriptLoader *aLoader, nsIURI* aBaseURL)
JS::Handle<JSObject*> aModuleRecord)
: mLoader(aLoader), : mLoader(aLoader),
mBaseURL(aBaseURL), mBaseURL(aBaseURL)
mModuleRecord(aModuleRecord),
mInstantiationState(Uninstantiated)
{ {
MOZ_ASSERT(mLoader); MOZ_ASSERT(mLoader);
MOZ_ASSERT(mBaseURL); MOZ_ASSERT(mBaseURL);
MOZ_ASSERT(mModuleRecord); MOZ_ASSERT(!mModuleRecord);
MOZ_ASSERT(mException.isUndefined()); MOZ_ASSERT(mError.isUndefined());
// Make module's host defined field point to this module script object.
// This is cleared in the UnlinkModuleRecord().
JS::SetModuleHostDefinedField(mModuleRecord, JS::PrivateValue(this));
HoldJSObjects(this);
} }
void void
@ -66,34 +59,63 @@ ModuleScript::UnlinkModuleRecord()
MOZ_ASSERT(JS::GetModuleHostDefinedField(mModuleRecord).toPrivate() == MOZ_ASSERT(JS::GetModuleHostDefinedField(mModuleRecord).toPrivate() ==
this); this);
JS::SetModuleHostDefinedField(mModuleRecord, JS::UndefinedValue()); JS::SetModuleHostDefinedField(mModuleRecord, JS::UndefinedValue());
mModuleRecord = nullptr;
} }
mModuleRecord = nullptr;
mException.setUndefined();
} }
ModuleScript::~ModuleScript() ModuleScript::~ModuleScript()
{ {
if (mModuleRecord) { // The object may be destroyed without being unlinked first.
// The object may be destroyed without being unlinked first. UnlinkModuleRecord();
UnlinkModuleRecord();
}
DropJSObjects(this); DropJSObjects(this);
} }
void void
ModuleScript::SetInstantiationResult(JS::Handle<JS::Value> aMaybeException) ModuleScript::SetModuleRecord(JS::Handle<JSObject*> aModuleRecord)
{ {
MOZ_ASSERT(mInstantiationState == Uninstantiated); MOZ_ASSERT(!mModuleRecord);
MOZ_ASSERT(mModuleRecord); MOZ_ASSERT(mError.isUndefined());
MOZ_ASSERT(mException.isUndefined());
if (aMaybeException.isUndefined()) { mModuleRecord = aModuleRecord;
mInstantiationState = Instantiated;
} else { // Make module's host defined field point to this module script object.
mModuleRecord = nullptr; // This is cleared in the UnlinkModuleRecord().
mException = aMaybeException; JS::SetModuleHostDefinedField(mModuleRecord, JS::PrivateValue(this));
mInstantiationState = Errored; HoldJSObjects(this);
}
void
ModuleScript::SetPreInstantiationError(const JS::Value& aError)
{
MOZ_ASSERT(!aError.isUndefined());
UnlinkModuleRecord();
mError = aError;
HoldJSObjects(this);
}
bool
ModuleScript::IsErrored() const
{
if (!mModuleRecord) {
MOZ_ASSERT(!mError.isUndefined());
return true;
} }
return JS::IsModuleErrored(mModuleRecord);
}
JS::Value
ModuleScript::Error() const
{
MOZ_ASSERT(IsErrored());
if (!mModuleRecord) {
return mError;
}
return JS::GetModuleError(mModuleRecord);
} }
} // dom namespace } // dom namespace

View File

@ -20,17 +20,10 @@ class ScriptLoader;
class ModuleScript final : public nsISupports class ModuleScript final : public nsISupports
{ {
enum InstantiationState {
Uninstantiated,
Instantiated,
Errored
};
RefPtr<ScriptLoader> mLoader; RefPtr<ScriptLoader> mLoader;
nsCOMPtr<nsIURI> mBaseURL; nsCOMPtr<nsIURI> mBaseURL;
JS::Heap<JSObject*> mModuleRecord; JS::Heap<JSObject*> mModuleRecord;
JS::Heap<JS::Value> mException; JS::Heap<JS::Value> mError;
InstantiationState mInstantiationState;
~ModuleScript(); ~ModuleScript();
@ -39,24 +32,17 @@ public:
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ModuleScript) NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ModuleScript)
ModuleScript(ScriptLoader* aLoader, ModuleScript(ScriptLoader* aLoader,
nsIURI* aBaseURL, nsIURI* aBaseURL);
JS::Handle<JSObject*> aModuleRecord);
void SetModuleRecord(JS::Handle<JSObject*> aModuleRecord);
void SetPreInstantiationError(const JS::Value& aError);
ScriptLoader* Loader() const { return mLoader; } ScriptLoader* Loader() const { return mLoader; }
JSObject* ModuleRecord() const { return mModuleRecord; } JSObject* ModuleRecord() const { return mModuleRecord; }
JS::Value Exception() const { return mException; }
nsIURI* BaseURL() const { return mBaseURL; } nsIURI* BaseURL() const { return mBaseURL; }
void SetInstantiationResult(JS::Handle<JS::Value> aMaybeException); bool IsErrored() const;
bool IsUninstantiated() const { JS::Value Error() const;
return mInstantiationState == Uninstantiated;
}
bool IsInstantiated() const {
return mInstantiationState == Instantiated;
}
bool InstantiationFailed() const {
return mInstantiationState == Errored;
}
void UnlinkModuleRecord(); void UnlinkModuleRecord();
}; };

View File

@ -418,21 +418,23 @@ void
ScriptLoader::SetModuleFetchFinishedAndResumeWaitingRequests(ModuleLoadRequest *aRequest, ScriptLoader::SetModuleFetchFinishedAndResumeWaitingRequests(ModuleLoadRequest *aRequest,
nsresult aResult) nsresult aResult)
{ {
// Update module map with the result of fetching a single module script. The // Update module map with the result of fetching a single module script.
// module script pointer is nullptr on error. //
// If any requests for the same URL are waiting on this one to complete, they
MOZ_ASSERT(!aRequest->IsReadyToRun()); // will have ModuleLoaded or LoadFailed on them when the promise is
// resolved/rejected. This is set up in StartLoad.
RefPtr<GenericPromise::Private> promise; RefPtr<GenericPromise::Private> promise;
MOZ_ALWAYS_TRUE(mFetchingModules.Get(aRequest->mURI, getter_AddRefs(promise))); MOZ_ALWAYS_TRUE(mFetchingModules.Get(aRequest->mURI, getter_AddRefs(promise)));
mFetchingModules.Remove(aRequest->mURI); mFetchingModules.Remove(aRequest->mURI);
RefPtr<ModuleScript> ms(aRequest->mModuleScript); RefPtr<ModuleScript> moduleScript(aRequest->mModuleScript);
MOZ_ASSERT(NS_SUCCEEDED(aResult) == (ms != nullptr)); MOZ_ASSERT(NS_FAILED(aResult) == !moduleScript);
mFetchedModules.Put(aRequest->mURI, ms);
mFetchedModules.Put(aRequest->mURI, moduleScript);
if (promise) { if (promise) {
if (ms) { if (moduleScript) {
promise->Resolve(true, __func__); promise->Resolve(true, __func__);
} else { } else {
promise->Reject(aResult, __func__); promise->Reject(aResult, __func__);
@ -478,19 +480,29 @@ ScriptLoader::ProcessFetchedModuleSource(ModuleLoadRequest* aRequest)
MOZ_ASSERT(!aRequest->mModuleScript); MOZ_ASSERT(!aRequest->mModuleScript);
nsresult rv = CreateModuleScript(aRequest); nsresult rv = CreateModuleScript(aRequest);
MOZ_ASSERT(NS_FAILED(rv) == !aRequest->mModuleScript);
SetModuleFetchFinishedAndResumeWaitingRequests(aRequest, rv); SetModuleFetchFinishedAndResumeWaitingRequests(aRequest, rv);
free(aRequest->mScriptTextBuf); free(aRequest->mScriptTextBuf);
aRequest->mScriptTextBuf = nullptr; aRequest->mScriptTextBuf = nullptr;
aRequest->mScriptTextLength = 0; aRequest->mScriptTextLength = 0;
if (NS_SUCCEEDED(rv)) { if (NS_FAILED(rv)) {
aRequest->LoadFailed();
return rv;
}
if (!aRequest->mModuleScript->IsErrored()) {
StartFetchingModuleDependencies(aRequest); StartFetchingModuleDependencies(aRequest);
} }
return rv; return NS_OK;
} }
static nsresult
ResolveRequestedModules(ModuleLoadRequest* aRequest, nsCOMArray<nsIURI>& aUrls);
nsresult nsresult
ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest) ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest)
{ {
@ -544,9 +556,34 @@ ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest)
} }
} }
MOZ_ASSERT(NS_SUCCEEDED(rv) == (module != nullptr)); MOZ_ASSERT(NS_SUCCEEDED(rv) == (module != nullptr));
if (module) { RefPtr<ModuleScript> moduleScript = new ModuleScript(this, aRequest->mBaseURL);
aRequest->mModuleScript = aRequest->mModuleScript = moduleScript;
new ModuleScript(this, aRequest->mBaseURL, module);
if (!module) {
// Compilation failed
MOZ_ASSERT(aes.HasException());
JS::Rooted<JS::Value> error(cx);
if (!aes.StealException(&error)) {
aRequest->mModuleScript = nullptr;
return NS_ERROR_FAILURE;
}
moduleScript->SetPreInstantiationError(error);
aRequest->ModuleErrored();
return NS_OK;
}
moduleScript->SetModuleRecord(module);
// Validate requested modules and treat failure to resolve module specifiers
// the same as a parse error.
nsCOMArray<nsIURI> urls;
rv = ResolveRequestedModules(aRequest, urls);
if (NS_FAILED(rv)) {
// ResolveRequestedModules sets pre-instanitation error on failure.
MOZ_ASSERT(moduleScript->IsErrored());
aRequest->ModuleErrored();
return NS_OK;
} }
} }
@ -556,8 +593,8 @@ ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest)
} }
static bool static bool
ThrowTypeError(JSContext* aCx, ModuleScript* aScript, CreateTypeError(JSContext* aCx, ModuleScript* aScript, const nsString& aMessage,
const nsString& aMessage) JS::MutableHandle<JS::Value> aError)
{ {
JS::Rooted<JSObject*> module(aCx, aScript->ModuleRecord()); JS::Rooted<JSObject*> module(aCx, aScript->ModuleRecord());
JS::Rooted<JSScript*> script(aCx, JS::GetModuleScript(aCx, module)); JS::Rooted<JSScript*> script(aCx, JS::GetModuleScript(aCx, module));
@ -572,17 +609,11 @@ ThrowTypeError(JSContext* aCx, ModuleScript* aScript,
return false; return false;
} }
JS::Rooted<JS::Value> error(aCx); return JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, 0, 0, nullptr,
if (!JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, 0, 0, nullptr, message, aError);
message, &error)) {
return false;
}
JS_SetPendingException(aCx, error);
return false;
} }
static bool static nsresult
HandleResolveFailure(JSContext* aCx, ModuleScript* aScript, HandleResolveFailure(JSContext* aCx, ModuleScript* aScript,
const nsAString& aSpecifier) const nsAString& aSpecifier)
{ {
@ -591,19 +622,13 @@ HandleResolveFailure(JSContext* aCx, ModuleScript* aScript,
nsAutoString message(NS_LITERAL_STRING("Error resolving module specifier: ")); nsAutoString message(NS_LITERAL_STRING("Error resolving module specifier: "));
message.Append(aSpecifier); message.Append(aSpecifier);
return ThrowTypeError(aCx, aScript, message); JS::Rooted<JS::Value> error(aCx);
} if (!CreateTypeError(aCx, aScript, message, &error)) {
return NS_ERROR_OUT_OF_MEMORY;
}
static bool aScript->SetPreInstantiationError(error);
HandleModuleNotFound(JSContext* aCx, ModuleScript* aScript, return NS_OK;
const nsAString& aSpecifier)
{
// TODO: How can we get the line number of the failed import?
nsAutoString message(NS_LITERAL_STRING("Resolved module not found in map: "));
message.Append(aSpecifier);
return ThrowTypeError(aCx, aScript, message);
} }
static already_AddRefed<nsIURI> static already_AddRefed<nsIURI>
@ -701,7 +726,8 @@ ResolveRequestedModules(ModuleLoadRequest* aRequest, nsCOMArray<nsIURI> &aUrls)
ModuleScript* ms = aRequest->mModuleScript; ModuleScript* ms = aRequest->mModuleScript;
nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(ms, specifier); nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(ms, specifier);
if (!uri) { if (!uri) {
HandleResolveFailure(cx, ms, specifier); nsresult rv = HandleResolveFailure(cx, ms, specifier);
NS_ENSURE_SUCCESS(rv, rv);
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -720,13 +746,15 @@ void
ScriptLoader::StartFetchingModuleDependencies(ModuleLoadRequest* aRequest) ScriptLoader::StartFetchingModuleDependencies(ModuleLoadRequest* aRequest)
{ {
MOZ_ASSERT(aRequest->mModuleScript); MOZ_ASSERT(aRequest->mModuleScript);
MOZ_ASSERT(!aRequest->mModuleScript->IsErrored());
MOZ_ASSERT(!aRequest->IsReadyToRun()); MOZ_ASSERT(!aRequest->IsReadyToRun());
aRequest->mProgress = ModuleLoadRequest::Progress::FetchingImports; aRequest->mProgress = ModuleLoadRequest::Progress::FetchingImports;
nsCOMArray<nsIURI> urls; nsCOMArray<nsIURI> urls;
nsresult rv = ResolveRequestedModules(aRequest, urls); nsresult rv = ResolveRequestedModules(aRequest, urls);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
aRequest->LoadFailed(); aRequest->ModuleErrored();
return; return;
} }
@ -750,7 +778,7 @@ ScriptLoader::StartFetchingModuleDependencies(ModuleLoadRequest* aRequest)
GenericPromise::All(AbstractThread::GetCurrent(), importsReady); GenericPromise::All(AbstractThread::GetCurrent(), importsReady);
allReady->Then(AbstractThread::GetCurrent(), __func__, aRequest, allReady->Then(AbstractThread::GetCurrent(), __func__, aRequest,
&ModuleLoadRequest::DependenciesLoaded, &ModuleLoadRequest::DependenciesLoaded,
&ModuleLoadRequest::LoadFailed); &ModuleLoadRequest::ModuleErrored);
} }
RefPtr<GenericPromise> RefPtr<GenericPromise>
@ -773,6 +801,7 @@ ScriptLoader::StartFetchingModuleAndDependencies(ModuleLoadRequest* aRequest,
nsresult rv = StartLoad(childRequest, NS_LITERAL_STRING("module"), false); nsresult rv = StartLoad(childRequest, NS_LITERAL_STRING("module"), false);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
MOZ_ASSERT(!childRequest->mModuleScript);
childRequest->mReady.Reject(rv, __func__); childRequest->mReady.Reject(rv, __func__);
return ready; return ready;
} }
@ -781,6 +810,7 @@ ScriptLoader::StartFetchingModuleAndDependencies(ModuleLoadRequest* aRequest,
return ready; return ready;
} }
// 8.1.3.8.1 HostResolveImportedModule(referencingModule, specifier)
bool bool
HostResolveImportedModule(JSContext* aCx, unsigned argc, JS::Value* vp) HostResolveImportedModule(JSContext* aCx, unsigned argc, JS::Value* vp)
{ {
@ -795,31 +825,25 @@ HostResolveImportedModule(JSContext* aCx, unsigned argc, JS::Value* vp)
MOZ_ASSERT(script->ModuleRecord() == module); MOZ_ASSERT(script->ModuleRecord() == module);
// Let url be the result of resolving a module specifier given referencing // Let url be the result of resolving a module specifier given referencing
// module script and specifier. If the result is failure, throw a TypeError // module script and specifier.
// exception and abort these steps.
nsAutoJSString string; nsAutoJSString string;
if (!string.init(aCx, specifier)) { if (!string.init(aCx, specifier)) {
return false; return false;
} }
nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(script, string); nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(script, string);
if (!uri) {
return HandleResolveFailure(aCx, script, string);
}
// Let resolved module script be the value of the entry in module map whose // This cannot fail because resolving a module specifier must have been
// key is url. If no such entry exists, throw a TypeError exception and abort // previously successful with these same two arguments.
// these steps. MOZ_ASSERT(uri, "Failed to resolve previously-resolved module specifier");
// Let resolved module script be moduleMap[url]. (This entry must exist for us
// to have gotten to this point.)
ModuleScript* ms = script->Loader()->GetFetchedModule(uri); ModuleScript* ms = script->Loader()->GetFetchedModule(uri);
if (!ms) { MOZ_ASSERT(ms, "Resolved module not found in module map");
return HandleModuleNotFound(aCx, script, string);
}
if (ms->InstantiationFailed()) { MOZ_ASSERT(!ms->IsErrored());
JS::Rooted<JS::Value> exception(aCx, ms->Exception());
JS_SetPendingException(aCx, exception);
return false;
}
*vp = JS::ObjectValue(*ms->ModuleRecord()); *vp = JS::ObjectValue(*ms->ModuleRecord());
return true; return true;
@ -843,10 +867,36 @@ EnsureModuleResolveHook(JSContext* aCx)
return NS_OK; return NS_OK;
} }
void
ScriptLoader::CheckModuleDependenciesLoaded(ModuleLoadRequest* aRequest)
{
RefPtr<ModuleScript> moduleScript = aRequest->mModuleScript;
if (moduleScript && !moduleScript->IsErrored()) {
for (auto childRequest : aRequest->mImports) {
ModuleScript* childScript = childRequest->mModuleScript;
if (!childScript) {
// Load error
aRequest->mModuleScript = nullptr;
return;
} else if (childScript->IsErrored()) {
// Script error
moduleScript->SetPreInstantiationError(childScript->Error());
return;
}
}
}
}
void void
ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest) ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest)
{ {
if (aRequest->IsTopLevel()) { if (aRequest->IsTopLevel()) {
ModuleScript* moduleScript = aRequest->mModuleScript;
if (moduleScript && !moduleScript->IsErrored()) {
if (!InstantiateModuleTree(aRequest)) {
aRequest->mModuleScript = nullptr;
}
}
MaybeMoveToLoadedList(aRequest); MaybeMoveToLoadedList(aRequest);
ProcessPendingRequests(); ProcessPendingRequests();
} }
@ -859,60 +909,35 @@ ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest)
bool bool
ScriptLoader::InstantiateModuleTree(ModuleLoadRequest* aRequest) ScriptLoader::InstantiateModuleTree(ModuleLoadRequest* aRequest)
{ {
// Perform eager instantiation of the loaded module tree. // Instantiate a top-level module and record any error.
MOZ_ASSERT(aRequest); MOZ_ASSERT(aRequest);
MOZ_ASSERT(aRequest->IsTopLevel());
ModuleScript* ms = aRequest->mModuleScript; ModuleScript* moduleScript = aRequest->mModuleScript;
MOZ_ASSERT(ms); MOZ_ASSERT(moduleScript);
if (!ms || !ms->ModuleRecord()) { MOZ_ASSERT(moduleScript->ModuleRecord());
return false;
}
nsAutoMicroTask mt;
AutoJSAPI jsapi; AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(ms->ModuleRecord()))) { if (NS_WARN_IF(!jsapi.Init(moduleScript->ModuleRecord()))) {
return false; return false;
} }
nsresult rv = EnsureModuleResolveHook(jsapi.cx()); nsresult rv = EnsureModuleResolveHook(jsapi.cx());
NS_ENSURE_SUCCESS(rv, false); NS_ENSURE_SUCCESS(rv, false);
JS::Rooted<JSObject*> module(jsapi.cx(), ms->ModuleRecord()); JS::Rooted<JSObject*> module(jsapi.cx(), moduleScript->ModuleRecord());
bool ok = NS_SUCCEEDED(nsJSUtils::ModuleDeclarationInstantiation(jsapi.cx(), module)); bool ok = NS_SUCCEEDED(nsJSUtils::ModuleInstantiate(jsapi.cx(), module));
JS::RootedValue exception(jsapi.cx());
if (!ok) { if (!ok) {
MOZ_ASSERT(jsapi.HasException()); MOZ_ASSERT(jsapi.HasException());
JS::RootedValue exception(jsapi.cx());
if (!jsapi.StealException(&exception)) { if (!jsapi.StealException(&exception)) {
return false; return false;
} }
MOZ_ASSERT(!exception.isUndefined()); MOZ_ASSERT(!exception.isUndefined());
} // Ignore the exception. It will be recorded in the module record.
// Mark this module and any uninstantiated dependencies found via depth-first
// search as instantiated and record any error.
mozilla::Vector<ModuleLoadRequest*, 1> requests;
if (!requests.append(aRequest)) {
return false;
}
while (!requests.empty()) {
ModuleLoadRequest* request = requests.popCopy();
ModuleScript* ms = request->mModuleScript;
if (!ms->IsUninstantiated()) {
continue;
}
ms->SetInstantiationResult(exception);
for (auto import : request->mImports) {
if (import->mModuleScript->IsUninstantiated() &&
!requests.append(import))
{
return false;
}
}
} }
return true; return true;
@ -1427,13 +1452,19 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
ModuleLoadRequest* modReq = request->AsModuleRequest(); ModuleLoadRequest* modReq = request->AsModuleRequest();
modReq->mBaseURL = mDocument->GetDocBaseURI(); modReq->mBaseURL = mDocument->GetDocBaseURI();
rv = CreateModuleScript(modReq); rv = CreateModuleScript(modReq);
NS_ENSURE_SUCCESS(rv, false); MOZ_ASSERT(NS_FAILED(rv) == !modReq->mModuleScript);
StartFetchingModuleDependencies(modReq); if (NS_FAILED(rv)) {
modReq->LoadFailed();
return false;
}
if (aElement->GetScriptAsync()) { if (aElement->GetScriptAsync()) {
mLoadingAsyncRequests.AppendElement(request); mLoadingAsyncRequests.AppendElement(request);
} else { } else {
AddDeferRequest(request); AddDeferRequest(request);
} }
if (!modReq->mModuleScript->IsErrored()) {
StartFetchingModuleDependencies(modReq);
}
return false; return false;
} }
request->mProgress = ScriptLoadRequest::Progress::Ready; request->mProgress = ScriptLoadRequest::Progress::Ready;
@ -1511,11 +1542,7 @@ ScriptLoader::ProcessOffThreadRequest(ScriptLoadRequest* aRequest)
if (aRequest->IsModuleRequest()) { if (aRequest->IsModuleRequest()) {
MOZ_ASSERT(aRequest->mOffThreadToken); MOZ_ASSERT(aRequest->mOffThreadToken);
ModuleLoadRequest* request = aRequest->AsModuleRequest(); ModuleLoadRequest* request = aRequest->AsModuleRequest();
nsresult rv = ProcessFetchedModuleSource(request); return ProcessFetchedModuleSource(request);
if (NS_FAILED(rv)) {
request->LoadFailed();
}
return rv;
} }
aRequest->SetReady(); aRequest->SetReady();
@ -1687,7 +1714,7 @@ ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest)
if (aRequest->IsModuleRequest() && if (aRequest->IsModuleRequest() &&
!aRequest->AsModuleRequest()->mModuleScript) !aRequest->AsModuleRequest()->mModuleScript)
{ {
// There was an error parsing a module script. Nothing to do here. // There was an error fetching a module script. Nothing to do here.
FireScriptAvailable(NS_ERROR_FAILURE, aRequest); FireScriptAvailable(NS_ERROR_FAILURE, aRequest);
return NS_OK; return NS_OK;
} }
@ -1909,8 +1936,8 @@ ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest)
// http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block // http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block
nsAutoMicroTask mt; nsAutoMicroTask mt;
AutoEntryScript aes(globalObject, "<script> element", true); AutoEntryScript aes(globalObject, "<script> element", true);
JS::Rooted<JSObject*> global(aes.cx(), JSContext* cx = aes.cx();
globalObject->GetGlobalJSObject()); JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());
bool oldProcessingScriptTag = context->GetProcessingScriptTag(); bool oldProcessingScriptTag = context->GetProcessingScriptTag();
context->SetProcessingScriptTag(true); context->SetProcessingScriptTag(true);
@ -1931,28 +1958,38 @@ ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest)
} }
if (aRequest->IsModuleRequest()) { if (aRequest->IsModuleRequest()) {
rv = EnsureModuleResolveHook(cx);
NS_ENSURE_SUCCESS(rv, rv);
ModuleLoadRequest* request = aRequest->AsModuleRequest(); ModuleLoadRequest* request = aRequest->AsModuleRequest();
MOZ_ASSERT(request->mModuleScript); MOZ_ASSERT(request->mModuleScript);
MOZ_ASSERT(!request->mOffThreadToken); MOZ_ASSERT(!request->mOffThreadToken);
ModuleScript* ms = request->mModuleScript;
MOZ_ASSERT(!ms->IsUninstantiated()); ModuleScript* moduleScript = request->mModuleScript;
if (ms->InstantiationFailed()) { if (moduleScript->IsErrored()) {
JS::Rooted<JS::Value> exception(aes.cx(), ms->Exception()); // Module has an error status
JS_SetPendingException(aes.cx(), exception); JS::Rooted<JS::Value> error(cx, moduleScript->Error());
rv = NS_ERROR_FAILURE; JS_SetPendingException(cx, error);
} else { return NS_OK; // An error is reported by AutoEntryScript.
JS::Rooted<JSObject*> module(aes.cx(), ms->ModuleRecord()); }
MOZ_ASSERT(module);
rv = nsJSUtils::ModuleEvaluation(aes.cx(), module); JS::Rooted<JSObject*> module(cx, moduleScript->ModuleRecord());
MOZ_ASSERT(module);
rv = nsJSUtils::ModuleEvaluate(cx, module);
MOZ_ASSERT(NS_FAILED(rv) == aes.HasException());
if (NS_FAILED(rv)) {
// Evaluation failed
rv = NS_OK; // An error is reported by AutoEntryScript.
} }
} else { } else {
JS::CompileOptions options(aes.cx()); JS::CompileOptions options(cx);
rv = FillCompileOptionsForRequest(aes, aRequest, global, &options); rv = FillCompileOptionsForRequest(aes, aRequest, global, &options);
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
nsAutoString inlineData; nsAutoString inlineData;
SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData); SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData);
rv = nsJSUtils::EvaluateString(aes.cx(), srcBuf, global, options, rv = nsJSUtils::EvaluateString(cx, srcBuf, global, options,
aRequest->OffThreadTokenPtr()); aRequest->OffThreadTokenPtr());
} }
} }

View File

@ -580,6 +580,7 @@ private:
nsresult CreateModuleScript(ModuleLoadRequest* aRequest); nsresult CreateModuleScript(ModuleLoadRequest* aRequest);
nsresult ProcessFetchedModuleSource(ModuleLoadRequest* aRequest); nsresult ProcessFetchedModuleSource(ModuleLoadRequest* aRequest);
void CheckModuleDependenciesLoaded(ModuleLoadRequest* aRequest);
void ProcessLoadedModuleTree(ModuleLoadRequest* aRequest); void ProcessLoadedModuleTree(ModuleLoadRequest* aRequest);
bool InstantiateModuleTree(ModuleLoadRequest* aRequest); bool InstantiateModuleTree(ModuleLoadRequest* aRequest);
void StartFetchingModuleDependencies(ModuleLoadRequest* aRequest); void StartFetchingModuleDependencies(ModuleLoadRequest* aRequest);

View File

@ -2,11 +2,11 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function CallModuleResolveHook(module, specifier, expectedMinimumState) function CallModuleResolveHook(module, specifier, expectedMinimumStatus)
{ {
let requestedModule = HostResolveImportedModule(module, specifier); let requestedModule = HostResolveImportedModule(module, specifier);
if (requestedModule.state < expectedMinimumState) if (requestedModule.state < expectedMinimumStatus)
ThrowInternalError(JSMSG_BAD_MODULE_STATE); ThrowInternalError(JSMSG_BAD_MODULE_STATUS);
return requestedModule; return requestedModule;
} }
@ -65,7 +65,36 @@ function ModuleGetExportedNames(exportStarSet = [])
return exportedNames; return exportedNames;
} }
function ModuleSetStatus(module, newStatus)
{
assert(newStatus >= MODULE_STATUS_ERRORED && newStatus <= MODULE_STATUS_EVALUATED,
"Bad new module status in ModuleSetStatus");
if (newStatus !== MODULE_STATUS_ERRORED)
assert(newStatus > module.status, "New module status inconsistent with current status");
UnsafeSetReservedSlot(module, MODULE_OBJECT_STATUS_SLOT, newStatus);
}
// 15.2.1.16.3 ResolveExport(exportName, resolveSet) // 15.2.1.16.3 ResolveExport(exportName, resolveSet)
//
// Returns an object describing the location of the resolved export or
// indicating a failure.
//
// On success this returns: { resolved: true, module, bindingName }
//
// There are three failure cases:
//
// - The resolution failure can be blamed on a particular module.
// Returns: { resolved: false, module, ambiguous: false }
//
// - 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 }
//
function ModuleResolveExport(exportName, resolveSet = []) function ModuleResolveExport(exportName, resolveSet = [])
{ {
if (!IsObject(this) || !IsModule(this)) { if (!IsObject(this) || !IsModule(this)) {
@ -77,88 +106,104 @@ function ModuleResolveExport(exportName, resolveSet = [])
let module = this; let module = this;
// Step 2 // Step 2
assert(module.status !== MODULE_STATUS_ERRORED, "Bad module status in ResolveExport");
// Step 3
for (let i = 0; i < resolveSet.length; i++) { for (let i = 0; i < resolveSet.length; i++) {
let r = resolveSet[i]; let r = resolveSet[i];
if (r.module === module && r.exportName === exportName) if (r.module === module && r.exportName === exportName) {
return null; // This is a circular import request.
return {resolved: false, module: null, ambiguous: false};
}
} }
// Step 3 // Step 4
_DefineDataProperty(resolveSet, resolveSet.length, {module: module, exportName: exportName}); _DefineDataProperty(resolveSet, resolveSet.length, {module: module, exportName: exportName});
// Step 4 // Step 5
let localExportEntries = module.localExportEntries; let localExportEntries = module.localExportEntries;
for (let i = 0; i < localExportEntries.length; i++) { for (let i = 0; i < localExportEntries.length; i++) {
let e = localExportEntries[i]; let e = localExportEntries[i];
if (exportName === e.exportName) if (exportName === e.exportName)
return {module: module, bindingName: e.localName}; return {resolved: true, module, bindingName: e.localName};
} }
// Step 5 // Step 6
let indirectExportEntries = module.indirectExportEntries; let indirectExportEntries = module.indirectExportEntries;
for (let i = 0; i < indirectExportEntries.length; i++) { for (let i = 0; i < indirectExportEntries.length; i++) {
let e = indirectExportEntries[i]; let e = indirectExportEntries[i];
if (exportName === e.exportName) { if (exportName === e.exportName) {
let importedModule = CallModuleResolveHook(module, e.moduleRequest, let importedModule = CallModuleResolveHook(module, e.moduleRequest,
MODULE_STATE_PARSED); MODULE_STATUS_UNINSTANTIATED);
return callFunction(importedModule.resolveExport, importedModule, e.importName, let resolution = callFunction(importedModule.resolveExport, importedModule, e.importName,
resolveSet); resolveSet);
if (!resolution.resolved && !resolution.module)
resolution.module = module;
return resolution;
} }
} }
// Step 6 // Step 7
if (exportName === "default") { if (exportName === "default") {
// A default export cannot be provided by an export *. // A default export cannot be provided by an export *.
return null; return {resolved: false, module: null, ambiguous: false};
} }
// Step 7 // Step 8
let starResolution = null; let starResolution = null;
// Step 8 // Step 9
let starExportEntries = module.starExportEntries; let starExportEntries = module.starExportEntries;
for (let i = 0; i < starExportEntries.length; i++) { for (let i = 0; i < starExportEntries.length; i++) {
let e = starExportEntries[i]; let e = starExportEntries[i];
let importedModule = CallModuleResolveHook(module, e.moduleRequest, let importedModule = CallModuleResolveHook(module, e.moduleRequest,
MODULE_STATE_PARSED); MODULE_STATUS_UNINSTANTIATED);
let resolution = callFunction(importedModule.resolveExport, importedModule, let resolution = callFunction(importedModule.resolveExport, importedModule, exportName,
exportName, resolveSet); resolveSet);
if (resolution === "ambiguous") if (!resolution.resolved && (resolution.module || resolution.ambiguous))
return resolution; return resolution;
if (resolution !== null) { if (resolution.resolved) {
if (starResolution === null) { if (starResolution === null) {
starResolution = resolution; starResolution = resolution;
} else { } else {
if (resolution.module !== starResolution.module || if (resolution.module !== starResolution.module ||
resolution.exportName !== starResolution.exportName) resolution.bindingName !== starResolution.bindingName)
{ {
return "ambiguous"; return {resolved: false, module: null, ambiguous: true};
} }
} }
} }
} }
// Step 9 // Step 10
return starResolution; if (starResolution !== null)
return starResolution;
return {resolved: false, module: null, ambiguous: false};
} }
// 15.2.1.18 GetModuleNamespace(module) // 15.2.1.18 GetModuleNamespace(module)
function GetModuleNamespace(module) function GetModuleNamespace(module)
{ {
// Step 1
assert(IsModule(module), "GetModuleNamespace called with non-module");
// Step 2 // Step 2
let namespace = module.namespace; assert(module.status !== MODULE_STATUS_UNINSTANTIATED &&
module.status !== MODULE_STATUS_ERRORED,
"Bad module status in GetModuleNamespace");
// Step 3 // Step 3
let namespace = module.namespace;
if (typeof namespace === "undefined") { if (typeof namespace === "undefined") {
let exportedNames = callFunction(module.getExportedNames, module); let exportedNames = callFunction(module.getExportedNames, module);
let unambiguousNames = []; let unambiguousNames = [];
for (let i = 0; i < exportedNames.length; i++) { for (let i = 0; i < exportedNames.length; i++) {
let name = exportedNames[i]; let name = exportedNames[i];
let resolution = callFunction(module.resolveExport, module, name); let resolution = callFunction(module.resolveExport, module, name);
if (resolution === null) if (resolution.resolved)
ThrowSyntaxError(JSMSG_MISSING_NAMESPACE_EXPORT);
if (resolution !== "ambiguous")
_DefineDataProperty(unambiguousNames, unambiguousNames.length, name); _DefineDataProperty(unambiguousNames, unambiguousNames.length, name);
} }
namespace = ModuleNamespaceCreate(module, unambiguousNames); namespace = ModuleNamespaceCreate(module, unambiguousNames);
@ -180,7 +225,7 @@ function ModuleNamespaceCreate(module, exports)
for (let i = 0; i < exports.length; i++) { for (let i = 0; i < exports.length; i++) {
let name = exports[i]; let name = exports[i];
let binding = callFunction(module.resolveExport, module, name); let binding = callFunction(module.resolveExport, module, name);
assert(binding !== null && binding !== "ambiguous", "Failed to resolve binding"); assert(binding.resolved, "Failed to resolve binding");
AddModuleNamespaceBinding(ns, name, binding.module, binding.bindingName); AddModuleNamespaceBinding(ns, name, binding.module, binding.bindingName);
} }
@ -193,8 +238,8 @@ function GetModuleEnvironment(module)
// Check for a previous failed attempt to instantiate this module. This can // Check for a previous failed attempt to instantiate this module. This can
// only happen due to a bug in the module loader. // only happen due to a bug in the module loader.
if (module.state == MODULE_STATE_FAILED) if (module.status === MODULE_STATUS_ERRORED)
ThrowInternalError(JSMSG_MODULE_INSTANTIATE_FAILED); ThrowInternalError(JSMSG_MODULE_INSTANTIATE_FAILED, module.status);
let env = UnsafeGetReservedSlot(module, MODULE_OBJECT_ENVIRONMENT_SLOT); let env = UnsafeGetReservedSlot(module, MODULE_OBJECT_ENVIRONMENT_SLOT);
assert(env === undefined || IsModuleEnvironment(env), assert(env === undefined || IsModuleEnvironment(env),
@ -203,112 +248,401 @@ function GetModuleEnvironment(module)
return env; return env;
} }
function RecordInstantationFailure(module) function RecordModuleError(module, error)
{ {
// Set the module's state to 'failed' to indicate a failed module // Set the module's status to 'errored' to indicate a failed module
// instantiation and reset the environment slot to 'undefined'. // instantiation and record the exception. The environment slot is also
assert(IsModule(module), "Non-module passed to RecordInstantationFailure"); // reset to 'undefined'.
SetModuleState(module, MODULE_STATE_FAILED);
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); UnsafeSetReservedSlot(module, MODULE_OBJECT_ENVIRONMENT_SLOT, undefined);
} }
// 15.2.1.16.4 ModuleDeclarationInstantiation() function CountArrayValues(array, value)
function ModuleDeclarationInstantiation()
{ {
if (!IsObject(this) || !IsModule(this)) let count = 0;
return callFunction(CallModuleMethodIfWrapped, this, "ModuleDeclarationInstantiation"); for (let i = 0; i < array.length; i++) {
if (array[i] === value)
// Step 1 count++;
let module = this;
// Step 5
if (GetModuleEnvironment(module) !== undefined)
return;
// Step 7
CreateModuleEnvironment(module);
let env = GetModuleEnvironment(module);
SetModuleState(this, MODULE_STATE_INSTANTIATED);
try {
// Step 8
let requestedModules = module.requestedModules;
for (let i = 0; i < requestedModules.length; i++) {
let required = requestedModules[i];
let requiredModule = CallModuleResolveHook(module, required, MODULE_STATE_PARSED);
callFunction(requiredModule.declarationInstantiation, requiredModule);
}
// Step 9
let indirectExportEntries = module.indirectExportEntries;
for (let i = 0; i < indirectExportEntries.length; i++) {
let e = indirectExportEntries[i];
let resolution = callFunction(module.resolveExport, module, e.exportName);
if (resolution === null)
ThrowSyntaxError(JSMSG_MISSING_INDIRECT_EXPORT, e.exportName);
if (resolution === "ambiguous")
ThrowSyntaxError(JSMSG_AMBIGUOUS_INDIRECT_EXPORT, e.exportName);
}
// Step 12
let importEntries = module.importEntries;
for (let i = 0; i < importEntries.length; i++) {
let imp = importEntries[i];
let importedModule = CallModuleResolveHook(module, imp.moduleRequest,
MODULE_STATE_INSTANTIATED);
if (imp.importName === "*") {
let namespace = GetModuleNamespace(importedModule);
CreateNamespaceBinding(env, imp.localName, namespace);
} else {
let resolution = callFunction(importedModule.resolveExport, importedModule,
imp.importName);
if (resolution === null)
ThrowSyntaxError(JSMSG_MISSING_IMPORT, imp.importName);
if (resolution === "ambiguous")
ThrowSyntaxError(JSMSG_AMBIGUOUS_IMPORT, imp.importName);
if (resolution.module.state < MODULE_STATE_INSTANTIATED)
ThrowInternalError(JSMSG_BAD_MODULE_STATE);
CreateImportBinding(env, imp.localName, resolution.module, resolution.bindingName);
}
}
// Step 17.a.iii
InstantiateModuleFunctionDeclarations(module);
} catch (e) {
RecordInstantationFailure(module);
throw e;
} }
return count;
} }
_SetCanonicalName(ModuleDeclarationInstantiation, "ModuleDeclarationInstantiation");
// 15.2.1.16.5 ModuleEvaluation() function ArrayContains(array, value)
function ModuleEvaluation() {
for (let i = 0; i < array.length; i++) {
if (array[i] === value)
return true;
}
return false;
}
// 15.2.1.16.4 ModuleInstantiate()
function ModuleInstantiate()
{ {
if (!IsObject(this) || !IsModule(this)) if (!IsObject(this) || !IsModule(this))
return callFunction(CallModuleMethodIfWrapped, this, "ModuleEvaluation"); return callFunction(CallModuleMethodIfWrapped, this, "ModuleInstantiate");
// Step 1 // Step 1
let module = this; let module = this;
if (module.state < MODULE_STATE_INSTANTIATED) // Step 2
ThrowInternalError(JSMSG_BAD_MODULE_STATE); if (module.status === MODULE_STATUS_INSTANTIATING ||
module.status === MODULE_STATUS_EVALUATING)
{
ThrowInternalError(JSMSG_BAD_MODULE_STATUS);
}
// Step 4 // Step 3
if (module.state == MODULE_STATE_EVALUATED) let stack = [];
return undefined;
// Step 5 // Steps 4-5
SetModuleState(this, MODULE_STATE_EVALUATED); try {
InnerModuleDeclarationInstantiation(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);
}
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);
}
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");
throw error;
}
// Step 6 // Step 6
assert(module.status == MODULE_STATUS_INSTANTIATED ||
module.status == MODULE_STATUS_EVALUATED,
"Bad module status after successful instantiation");
// Step 7
assert(stack.length === 0,
"Stack should be empty after successful instantiation");
// Step 8
return undefined;
}
_SetCanonicalName(ModuleInstantiate, "ModuleInstantiate");
// 15.2.1.16.4.1 InnerModuleDeclarationInstantiation(module, stack, index)
function InnerModuleDeclarationInstantiation(module, stack, index)
{
// Step 1
// TODO: Support module records other than source text module records.
// Step 2
if (module.status === MODULE_STATUS_INSTANTIATING ||
module.status === MODULE_STATUS_INSTANTIATED ||
module.status === MODULE_STATUS_EVALUATED)
{
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
ModuleSetStatus(module, MODULE_STATUS_INSTANTIATING);
// Step 6-8
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, index);
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, index);
index++;
// Step 9
_DefineDataProperty(stack, stack.length, module);
// Step 10
let requestedModules = module.requestedModules; let requestedModules = module.requestedModules;
for (let i = 0; i < requestedModules.length; i++) { for (let i = 0; i < requestedModules.length; i++) {
let required = requestedModules[i]; let required = requestedModules[i];
let requiredModule = CallModuleResolveHook(module, required, MODULE_STATE_INSTANTIATED); let requiredModule = CallModuleResolveHook(module, required, MODULE_STATUS_ERRORED);
callFunction(requiredModule.evaluation, requiredModule);
index = InnerModuleDeclarationInstantiation(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");
assert((requiredModule.status === MODULE_STATUS_INSTANTIATING) ===
ArrayContains(stack, requiredModule),
"Required module should be in the stack iff it is currently being instantiated");
assert(typeof requiredModule.dfsIndex === "number", "Bad dfsIndex");
assert(typeof requiredModule.dfsAncestorIndex === "number", "Bad dfsAncestorIndex");
if (requiredModule.status === MODULE_STATUS_INSTANTIATING) {
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT,
std_Math_min(module.dfsAncestorIndex,
requiredModule.dfsAncestorIndex));
}
} }
return EvaluateModule(module); // Step 11
ModuleDeclarationEnvironmentSetup(module);
// Steps 12-13
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
if (module.dfsAncestorIndex === module.dfsIndex) {
let requiredModule;
do {
requiredModule = callFunction(std_Array_pop, stack);
ModuleSetStatus(requiredModule, MODULE_STATUS_INSTANTIATED);
} while (requiredModule !== module);
}
// Step 15
return index;
}
// 15.2.1.16.4.2 ModuleDeclarationEnvironmentSetup(module)
function ModuleDeclarationEnvironmentSetup(module)
{
// Step 1
let indirectExportEntries = module.indirectExportEntries;
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)
}
}
// Steps 5-6
CreateModuleEnvironment(module);
let env = GetModuleEnvironment(module);
// Step 8
let importEntries = module.importEntries;
for (let i = 0; i < importEntries.length; i++) {
let imp = importEntries[i];
let importedModule = CallModuleResolveHook(module, imp.moduleRequest,
MODULE_STATUS_INSTANTIATING);
if (imp.importName === "*") {
let namespace = GetModuleNamespace(importedModule);
CreateNamespaceBinding(env, imp.localName, namespace);
} 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);
}
CreateImportBinding(env, imp.localName, resolution.module, resolution.bindingName);
}
}
InstantiateModuleFunctionDeclarations(module);
}
// 15.2.1.16.4.3 ResolutionError(module)
function ResolutionError(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(kind === "import" || kind === "indirectExport",
"Unexpected kind in ResolutionError");
assert(line > 0,
"Line number should be present for all imports and indirect exports");
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;
}
let message = GetErrorMessage(errorNumber) + ": " + name;
let error = CreateModuleSyntaxError(module, line, column, message);
RecordModuleError(module, error);
throw error;
}
// 15.2.1.16.5 ModuleEvaluate()
function ModuleEvaluate()
{
if (!IsObject(this) || !IsModule(this))
return callFunction(CallModuleMethodIfWrapped, this, "ModuleEvaluate");
// Step 1
let module = this;
// Step 2
if (module.status !== MODULE_STATUS_ERRORED &&
module.status !== MODULE_STATUS_INSTANTIATED &&
module.status !== MODULE_STATUS_EVALUATED)
{
ThrowInternalError(JSMSG_BAD_MODULE_STATUS);
}
// Step 3
let stack = [];
// Steps 4-5
try {
InnerModuleEvaluation(module, stack, 0);
} catch (error) {
for (let i = 0; i < stack.length; i++) {
let m = stack[i];
assert(m.status === MODULE_STATUS_EVALUATING,
"Bad module status after failed evaluation");
RecordModuleError(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);
}
assert(module.status === MODULE_STATUS_ERRORED,
"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,
"Bad module status after successful evaluation");
assert(stack.length === 0,
"Stack should be empty after successful evaluation");
return undefined;
}
_SetCanonicalName(ModuleEvaluate, "ModuleEvaluate");
// 15.2.1.16.5.1 InnerModuleEvaluation(module, stack, index)
function InnerModuleEvaluation(module, stack, index)
{
// Step 1
// TODO: Support module records other than source text module records.
// Step 2
if (module.status === MODULE_STATUS_EVALUATING ||
module.status === MODULE_STATUS_EVALUATED)
{
return index;
}
// Step 3
if (module.status === MODULE_STATUS_ERRORED)
throw module.error;
// Step 4
assert(module.status === MODULE_STATUS_INSTANTIATED,
"Bad module status in ModuleEvaluation");
// Step 5
ModuleSetStatus(module, MODULE_STATUS_EVALUATING);
// Steps 6-8
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_INDEX_SLOT, index);
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT, index);
index++;
// Step 9
_DefineDataProperty(stack, stack.length, module);
// Step 10
let requestedModules = module.requestedModules;
for (let i = 0; i < requestedModules.length; i++) {
let required = requestedModules[i];
let requiredModule =
CallModuleResolveHook(module, required, MODULE_STATUS_INSTANTIATED);
index = InnerModuleEvaluation(requiredModule, stack, index);
assert(requiredModule.status == MODULE_STATUS_EVALUATING ||
requiredModule.status == MODULE_STATUS_EVALUATED,
"Bad module status after InnerModuleEvaluation");
assert((requiredModule.status === MODULE_STATUS_EVALUATING) ===
ArrayContains(stack, requiredModule),
"Required module should be in the stack iff it is currently being evaluated");
assert(typeof requiredModule.dfsIndex === "number", "Bad dfsIndex");
assert(typeof requiredModule.dfsAncestorIndex === "number", "Bad dfsAncestorIndex");
if (requiredModule.status === MODULE_STATUS_EVALUATING) {
UnsafeSetReservedSlot(module, MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT,
std_Math_min(module.dfsAncestorIndex,
requiredModule.dfsAncestorIndex));
}
}
// Step 11
ExecuteModule(module);
// Step 12
assert(CountArrayValues(stack, module) === 1,
"Current module should appear exactly once in the stack");
// Step 13
assert(module.dfsAncestorIndex <= module.dfsIndex,
"Bad DFS ancestor index");
// Step 14
if (module.dfsAncestorIndex === module.dfsIndex) {
let requiredModule;
do {
requiredModule = callFunction(std_Array_pop, stack);
ModuleSetStatus(requiredModule, MODULE_STATUS_EVALUATED);
} while (requiredModule !== module);
}
// Step 15
return index;
} }
_SetCanonicalName(ModuleEvaluation, "ModuleEvaluation");

View File

@ -18,10 +18,11 @@
using namespace js; using namespace js;
using namespace js::frontend; using namespace js::frontend;
static_assert(MODULE_STATE_FAILED < MODULE_STATE_PARSED && static_assert(MODULE_STATUS_ERRORED < MODULE_STATUS_UNINSTANTIATED &&
MODULE_STATE_PARSED < MODULE_STATE_INSTANTIATED && MODULE_STATUS_UNINSTANTIATED < MODULE_STATUS_INSTANTIATING &&
MODULE_STATE_INSTANTIATED < MODULE_STATE_EVALUATED, MODULE_STATUS_INSTANTIATING < MODULE_STATUS_INSTANTIATED &&
"Module states are ordered incorrectly"); MODULE_STATUS_INSTANTIATED < MODULE_STATUS_EVALUATED,
"Module statuses are ordered incorrectly");
template<typename T, Value ValueGetter(const T* obj)> template<typename T, Value ValueGetter(const T* obj)>
static bool static bool
@ -42,7 +43,7 @@ ModuleValueGetter(JSContext* cx, unsigned argc, Value* vp)
#define DEFINE_GETTER_FUNCTIONS(cls, name, slot) \ #define DEFINE_GETTER_FUNCTIONS(cls, name, slot) \
static Value \ static Value \
cls##_##name##Value(const cls* obj) { \ cls##_##name##Value(const cls* obj) { \
return obj->getFixedSlot(cls::slot); \ return obj->getReservedSlot(cls::slot); \
} \ } \
\ \
static bool \ static bool \
@ -69,6 +70,15 @@ ModuleValueGetter(JSContext* cx, unsigned argc, Value* vp)
return &value.toString()->asAtom(); \ return &value.toString()->asAtom(); \
} }
#define DEFINE_UINT32_ACCESSOR_METHOD(cls, name) \
uint32_t \
cls::name() const \
{ \
Value value = cls##_##name##Value(this); \
MOZ_ASSERT(value.toInt32() >= 0); \
return value.toInt32(); \
}
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// ImportEntryObject // ImportEntryObject
@ -82,10 +92,14 @@ ImportEntryObject::class_ = {
DEFINE_GETTER_FUNCTIONS(ImportEntryObject, moduleRequest, ModuleRequestSlot) DEFINE_GETTER_FUNCTIONS(ImportEntryObject, moduleRequest, ModuleRequestSlot)
DEFINE_GETTER_FUNCTIONS(ImportEntryObject, importName, ImportNameSlot) DEFINE_GETTER_FUNCTIONS(ImportEntryObject, importName, ImportNameSlot)
DEFINE_GETTER_FUNCTIONS(ImportEntryObject, localName, LocalNameSlot) DEFINE_GETTER_FUNCTIONS(ImportEntryObject, localName, LocalNameSlot)
DEFINE_GETTER_FUNCTIONS(ImportEntryObject, lineNumber, LineNumberSlot)
DEFINE_GETTER_FUNCTIONS(ImportEntryObject, columnNumber, ColumnNumberSlot)
DEFINE_ATOM_ACCESSOR_METHOD(ImportEntryObject, moduleRequest) DEFINE_ATOM_ACCESSOR_METHOD(ImportEntryObject, moduleRequest)
DEFINE_ATOM_ACCESSOR_METHOD(ImportEntryObject, importName) DEFINE_ATOM_ACCESSOR_METHOD(ImportEntryObject, importName)
DEFINE_ATOM_ACCESSOR_METHOD(ImportEntryObject, localName) DEFINE_ATOM_ACCESSOR_METHOD(ImportEntryObject, localName)
DEFINE_UINT32_ACCESSOR_METHOD(ImportEntryObject, lineNumber)
DEFINE_UINT32_ACCESSOR_METHOD(ImportEntryObject, columnNumber)
/* static */ bool /* static */ bool
ImportEntryObject::isInstance(HandleValue value) ImportEntryObject::isInstance(HandleValue value)
@ -100,6 +114,8 @@ GlobalObject::initImportEntryProto(JSContext* cx, Handle<GlobalObject*> global)
JS_PSG("moduleRequest", ImportEntryObject_moduleRequestGetter, 0), JS_PSG("moduleRequest", ImportEntryObject_moduleRequestGetter, 0),
JS_PSG("importName", ImportEntryObject_importNameGetter, 0), JS_PSG("importName", ImportEntryObject_importNameGetter, 0),
JS_PSG("localName", ImportEntryObject_localNameGetter, 0), JS_PSG("localName", ImportEntryObject_localNameGetter, 0),
JS_PSG("lineNumber", ImportEntryObject_lineNumberGetter, 0),
JS_PSG("columnNumber", ImportEntryObject_columnNumberGetter, 0),
JS_PS_END JS_PS_END
}; };
@ -118,8 +134,12 @@ GlobalObject::initImportEntryProto(JSContext* cx, Handle<GlobalObject*> global)
ImportEntryObject::create(ExclusiveContext* cx, ImportEntryObject::create(ExclusiveContext* cx,
HandleAtom moduleRequest, HandleAtom moduleRequest,
HandleAtom importName, HandleAtom importName,
HandleAtom localName) HandleAtom localName,
uint32_t lineNumber,
uint32_t columnNumber)
{ {
MOZ_ASSERT(lineNumber > 0);
RootedObject proto(cx, cx->global()->getImportEntryPrototype()); RootedObject proto(cx, cx->global()->getImportEntryPrototype());
RootedObject obj(cx, NewObjectWithGivenProto(cx, &class_, proto)); RootedObject obj(cx, NewObjectWithGivenProto(cx, &class_, proto));
if (!obj) if (!obj)
@ -129,6 +149,8 @@ ImportEntryObject::create(ExclusiveContext* cx,
self->initReservedSlot(ModuleRequestSlot, StringValue(moduleRequest)); self->initReservedSlot(ModuleRequestSlot, StringValue(moduleRequest));
self->initReservedSlot(ImportNameSlot, StringValue(importName)); self->initReservedSlot(ImportNameSlot, StringValue(importName));
self->initReservedSlot(LocalNameSlot, StringValue(localName)); self->initReservedSlot(LocalNameSlot, StringValue(localName));
self->initReservedSlot(LineNumberSlot, Int32Value(lineNumber));
self->initReservedSlot(ColumnNumberSlot, Int32Value(columnNumber));
return self; return self;
} }
@ -146,11 +168,15 @@ DEFINE_GETTER_FUNCTIONS(ExportEntryObject, exportName, ExportNameSlot)
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, moduleRequest, ModuleRequestSlot) DEFINE_GETTER_FUNCTIONS(ExportEntryObject, moduleRequest, ModuleRequestSlot)
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, importName, ImportNameSlot) DEFINE_GETTER_FUNCTIONS(ExportEntryObject, importName, ImportNameSlot)
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, localName, LocalNameSlot) DEFINE_GETTER_FUNCTIONS(ExportEntryObject, localName, LocalNameSlot)
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, lineNumber, LineNumberSlot)
DEFINE_GETTER_FUNCTIONS(ExportEntryObject, columnNumber, ColumnNumberSlot)
DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ExportEntryObject, exportName) DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ExportEntryObject, exportName)
DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ExportEntryObject, moduleRequest) DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ExportEntryObject, moduleRequest)
DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ExportEntryObject, importName) DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ExportEntryObject, importName)
DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ExportEntryObject, localName) DEFINE_ATOM_OR_NULL_ACCESSOR_METHOD(ExportEntryObject, localName)
DEFINE_UINT32_ACCESSOR_METHOD(ExportEntryObject, lineNumber)
DEFINE_UINT32_ACCESSOR_METHOD(ExportEntryObject, columnNumber)
/* static */ bool /* static */ bool
ExportEntryObject::isInstance(HandleValue value) ExportEntryObject::isInstance(HandleValue value)
@ -166,6 +192,8 @@ GlobalObject::initExportEntryProto(JSContext* cx, Handle<GlobalObject*> global)
JS_PSG("moduleRequest", ExportEntryObject_moduleRequestGetter, 0), JS_PSG("moduleRequest", ExportEntryObject_moduleRequestGetter, 0),
JS_PSG("importName", ExportEntryObject_importNameGetter, 0), JS_PSG("importName", ExportEntryObject_importNameGetter, 0),
JS_PSG("localName", ExportEntryObject_localNameGetter, 0), JS_PSG("localName", ExportEntryObject_localNameGetter, 0),
JS_PSG("lineNumber", ExportEntryObject_lineNumberGetter, 0),
JS_PSG("columnNumber", ExportEntryObject_columnNumberGetter, 0),
JS_PS_END JS_PS_END
}; };
@ -191,8 +219,13 @@ ExportEntryObject::create(ExclusiveContext* cx,
HandleAtom maybeExportName, HandleAtom maybeExportName,
HandleAtom maybeModuleRequest, HandleAtom maybeModuleRequest,
HandleAtom maybeImportName, HandleAtom maybeImportName,
HandleAtom maybeLocalName) HandleAtom maybeLocalName,
uint32_t lineNumber,
uint32_t columnNumber)
{ {
// Line and column numbers are optional for export entries since direct
// entries are checked at parse time.
RootedObject proto(cx, cx->global()->getExportEntryPrototype()); RootedObject proto(cx, cx->global()->getExportEntryPrototype());
RootedObject obj(cx, NewObjectWithGivenProto(cx, &class_, proto)); RootedObject obj(cx, NewObjectWithGivenProto(cx, &class_, proto));
if (!obj) if (!obj)
@ -203,6 +236,8 @@ ExportEntryObject::create(ExclusiveContext* cx,
self->initReservedSlot(ModuleRequestSlot, StringOrNullValue(maybeModuleRequest)); self->initReservedSlot(ModuleRequestSlot, StringOrNullValue(maybeModuleRequest));
self->initReservedSlot(ImportNameSlot, StringOrNullValue(maybeImportName)); self->initReservedSlot(ImportNameSlot, StringOrNullValue(maybeImportName));
self->initReservedSlot(LocalNameSlot, StringOrNullValue(maybeLocalName)); self->initReservedSlot(LocalNameSlot, StringOrNullValue(maybeLocalName));
self->initReservedSlot(LineNumberSlot, Int32Value(lineNumber));
self->initReservedSlot(ColumnNumberSlot, Int32Value(columnNumber));
return self; return self;
} }
@ -564,7 +599,7 @@ ModuleObject::class_ = {
ArrayObject& \ ArrayObject& \
cls::name() const \ cls::name() const \
{ \ { \
return getFixedSlot(cls::slot).toObject().as<ArrayObject>(); \ return getReservedSlot(cls::slot).toObject().as<ArrayObject>(); \
} }
DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, requestedModules, RequestedModulesSlot) DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, requestedModules, RequestedModulesSlot)
@ -688,7 +723,7 @@ void
ModuleObject::init(HandleScript script) ModuleObject::init(HandleScript script)
{ {
initReservedSlot(ScriptSlot, PrivateValue(script)); initReservedSlot(ScriptSlot, PrivateValue(script));
initReservedSlot(StateSlot, Int32Value(MODULE_STATE_FAILED)); initReservedSlot(StatusSlot, Int32Value(MODULE_STATUS_ERRORED));
} }
void void
@ -709,7 +744,7 @@ ModuleObject::initImportExportData(HandleArrayObject requestedModules,
initReservedSlot(LocalExportEntriesSlot, ObjectValue(*localExportEntries)); initReservedSlot(LocalExportEntriesSlot, ObjectValue(*localExportEntries));
initReservedSlot(IndirectExportEntriesSlot, ObjectValue(*indirectExportEntries)); initReservedSlot(IndirectExportEntriesSlot, ObjectValue(*indirectExportEntries));
initReservedSlot(StarExportEntriesSlot, ObjectValue(*starExportEntries)); initReservedSlot(StarExportEntriesSlot, ObjectValue(*starExportEntries));
setReservedSlot(StateSlot, Int32Value(MODULE_STATE_PARSED)); setReservedSlot(StatusSlot, Int32Value(MODULE_STATUS_UNINSTANTIATED));
} }
static bool static bool
@ -790,17 +825,24 @@ ModuleObject::script() const
} }
static inline void static inline void
AssertValidModuleState(ModuleState state) AssertValidModuleStatus(ModuleStatus status)
{ {
MOZ_ASSERT(state >= MODULE_STATE_FAILED && state <= MODULE_STATE_EVALUATED); MOZ_ASSERT(status >= MODULE_STATUS_ERRORED && status <= MODULE_STATUS_EVALUATED);
} }
ModuleState ModuleStatus
ModuleObject::state() const ModuleObject::status() const
{ {
ModuleState state = getReservedSlot(StateSlot).toInt32(); ModuleStatus status = getReservedSlot(StatusSlot).toInt32();
AssertValidModuleState(state); AssertValidModuleStatus(status);
return state; return status;
}
Value
ModuleObject::error() const
{
MOZ_ASSERT(status() == MODULE_STATUS_ERRORED);
return getReservedSlot(ErrorSlot);
} }
Value Value
@ -899,17 +941,8 @@ ModuleObject::instantiateFunctionDeclarations(JSContext* cx, HandleModuleObject
return true; return true;
} }
void
ModuleObject::setState(int32_t newState)
{
AssertValidModuleState(newState);
MOZ_ASSERT(state() != MODULE_STATE_FAILED);
MOZ_ASSERT(newState == MODULE_STATE_FAILED || newState > state());
setReservedSlot(StateSlot, Int32Value(newState));
}
/* static */ bool /* static */ bool
ModuleObject::evaluate(JSContext* cx, HandleModuleObject self, MutableHandleValue rval) ModuleObject::execute(JSContext* cx, HandleModuleObject self, MutableHandleValue rval)
{ {
MOZ_ASSERT(IsFrozen(cx, self)); MOZ_ASSERT(IsFrozen(cx, self));
@ -959,44 +992,50 @@ InvokeSelfHostedMethod(JSContext* cx, HandleModuleObject self, HandlePropertyNam
} }
/* static */ bool /* static */ bool
ModuleObject::DeclarationInstantiation(JSContext* cx, HandleModuleObject self) ModuleObject::Instantiate(JSContext* cx, HandleModuleObject self)
{ {
return InvokeSelfHostedMethod(cx, self, cx->names().ModuleDeclarationInstantiation); return InvokeSelfHostedMethod(cx, self, cx->names().ModuleInstantiate);
} }
/* static */ bool /* static */ bool
ModuleObject::Evaluation(JSContext* cx, HandleModuleObject self) ModuleObject::Evaluate(JSContext* cx, HandleModuleObject self)
{ {
return InvokeSelfHostedMethod(cx, self, cx->names().ModuleEvaluation); return InvokeSelfHostedMethod(cx, self, cx->names().ModuleEvaluate);
} }
DEFINE_GETTER_FUNCTIONS(ModuleObject, namespace_, NamespaceSlot) DEFINE_GETTER_FUNCTIONS(ModuleObject, namespace_, NamespaceSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, state, StateSlot) DEFINE_GETTER_FUNCTIONS(ModuleObject, status, StatusSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, error, ErrorSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, requestedModules, RequestedModulesSlot) DEFINE_GETTER_FUNCTIONS(ModuleObject, requestedModules, RequestedModulesSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, importEntries, ImportEntriesSlot) DEFINE_GETTER_FUNCTIONS(ModuleObject, importEntries, ImportEntriesSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, localExportEntries, LocalExportEntriesSlot) DEFINE_GETTER_FUNCTIONS(ModuleObject, localExportEntries, LocalExportEntriesSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, indirectExportEntries, IndirectExportEntriesSlot) DEFINE_GETTER_FUNCTIONS(ModuleObject, indirectExportEntries, IndirectExportEntriesSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, starExportEntries, StarExportEntriesSlot) DEFINE_GETTER_FUNCTIONS(ModuleObject, starExportEntries, StarExportEntriesSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, dfsIndex, DFSIndexSlot)
DEFINE_GETTER_FUNCTIONS(ModuleObject, dfsAncestorIndex, DFSAncestorIndexSlot)
/* static */ bool /* static */ bool
GlobalObject::initModuleProto(JSContext* cx, Handle<GlobalObject*> global) GlobalObject::initModuleProto(JSContext* cx, Handle<GlobalObject*> global)
{ {
static const JSPropertySpec protoAccessors[] = { static const JSPropertySpec protoAccessors[] = {
JS_PSG("namespace", ModuleObject_namespace_Getter, 0), JS_PSG("namespace", ModuleObject_namespace_Getter, 0),
JS_PSG("state", ModuleObject_stateGetter, 0), JS_PSG("status", ModuleObject_statusGetter, 0),
JS_PSG("error", ModuleObject_errorGetter, 0),
JS_PSG("requestedModules", ModuleObject_requestedModulesGetter, 0), JS_PSG("requestedModules", ModuleObject_requestedModulesGetter, 0),
JS_PSG("importEntries", ModuleObject_importEntriesGetter, 0), JS_PSG("importEntries", ModuleObject_importEntriesGetter, 0),
JS_PSG("localExportEntries", ModuleObject_localExportEntriesGetter, 0), JS_PSG("localExportEntries", ModuleObject_localExportEntriesGetter, 0),
JS_PSG("indirectExportEntries", ModuleObject_indirectExportEntriesGetter, 0), JS_PSG("indirectExportEntries", ModuleObject_indirectExportEntriesGetter, 0),
JS_PSG("starExportEntries", ModuleObject_starExportEntriesGetter, 0), JS_PSG("starExportEntries", ModuleObject_starExportEntriesGetter, 0),
JS_PSG("dfsIndex", ModuleObject_dfsIndexGetter, 0),
JS_PSG("dfsAncestorIndex", ModuleObject_dfsAncestorIndexGetter, 0),
JS_PS_END JS_PS_END
}; };
static const JSFunctionSpec protoFunctions[] = { static const JSFunctionSpec protoFunctions[] = {
JS_SELF_HOSTED_FN("getExportedNames", "ModuleGetExportedNames", 1, 0), JS_SELF_HOSTED_FN("getExportedNames", "ModuleGetExportedNames", 1, 0),
JS_SELF_HOSTED_FN("resolveExport", "ModuleResolveExport", 2, 0), JS_SELF_HOSTED_FN("resolveExport", "ModuleResolveExport", 2, 0),
JS_SELF_HOSTED_FN("declarationInstantiation", "ModuleDeclarationInstantiation", 0, 0), JS_SELF_HOSTED_FN("declarationInstantiation", "ModuleInstantiate", 0, 0),
JS_SELF_HOSTED_FN("evaluation", "ModuleEvaluation", 0, 0), JS_SELF_HOSTED_FN("evaluation", "ModuleEvaluate", 0, 0),
JS_FS_END JS_FS_END
}; };
@ -1018,9 +1057,11 @@ GlobalObject::initModuleProto(JSContext* cx, Handle<GlobalObject*> global)
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// ModuleBuilder // ModuleBuilder
ModuleBuilder::ModuleBuilder(ExclusiveContext* cx, HandleModuleObject module) ModuleBuilder::ModuleBuilder(ExclusiveContext* cx, HandleModuleObject module,
const frontend::TokenStream& tokenStream)
: cx_(cx), : cx_(cx),
module_(cx, module), module_(cx, module),
tokenStream_(tokenStream),
requestedModules_(cx, AtomVector(cx)), requestedModules_(cx, AtomVector(cx)),
importedBoundNames_(cx, AtomVector(cx)), importedBoundNames_(cx, AtomVector(cx)),
importEntries_(cx, ImportEntryVector(cx)), importEntries_(cx, ImportEntryVector(cx)),
@ -1045,6 +1086,7 @@ ModuleBuilder::buildTables()
if (!localExportEntries_.append(exp)) if (!localExportEntries_.append(exp))
return false; return false;
} else { } else {
MOZ_ASSERT(exp->lineNumber());
RootedAtom exportName(cx_, exp->exportName()); RootedAtom exportName(cx_, exp->exportName());
RootedAtom moduleRequest(cx_, importEntry->moduleRequest()); RootedAtom moduleRequest(cx_, importEntry->moduleRequest());
RootedAtom importName(cx_, importEntry->importName()); RootedAtom importName(cx_, importEntry->importName());
@ -1053,7 +1095,9 @@ ModuleBuilder::buildTables()
exportName, exportName,
moduleRequest, moduleRequest,
importName, importName,
nullptr); nullptr,
exp->lineNumber(),
exp->columnNumber());
if (!exportEntry || !indirectExportEntries_.append(exportEntry)) if (!exportEntry || !indirectExportEntries_.append(exportEntry))
return false; return false;
} }
@ -1062,6 +1106,7 @@ ModuleBuilder::buildTables()
if (!starExportEntries_.append(exp)) if (!starExportEntries_.append(exp))
return false; return false;
} else { } else {
MOZ_ASSERT(exp->lineNumber());
if (!indirectExportEntries_.append(exp)) if (!indirectExportEntries_.append(exp))
return false; return false;
} }
@ -1126,8 +1171,12 @@ ModuleBuilder::processImport(frontend::ParseNode* pn)
if (!importedBoundNames_.append(localName)) if (!importedBoundNames_.append(localName))
return false; return false;
uint32_t line;
uint32_t column;
tokenStream_.srcCoords.lineNumAndColumnIndex(spec->pn_left->pn_pos.begin, &line, &column);
RootedImportEntryObject importEntry(cx_); RootedImportEntryObject importEntry(cx_);
importEntry = ImportEntryObject::create(cx_, module, importName, localName); importEntry = ImportEntryObject::create(cx_, module, importName, localName, line, column);
if (!importEntry || !importEntries_.append(importEntry)) if (!importEntry || !importEntries_.append(importEntry))
return false; return false;
} }
@ -1158,7 +1207,7 @@ ModuleBuilder::processExport(frontend::ParseNode* pn)
MOZ_ASSERT(spec->isKind(PNK_EXPORT_SPEC)); MOZ_ASSERT(spec->isKind(PNK_EXPORT_SPEC));
RootedAtom localName(cx_, spec->pn_left->pn_atom); RootedAtom localName(cx_, spec->pn_left->pn_atom);
RootedAtom exportName(cx_, spec->pn_right->pn_atom); RootedAtom exportName(cx_, spec->pn_right->pn_atom);
if (!appendExportEntry(exportName, localName)) if (!appendExportEntry(exportName, localName, spec))
return false; return false;
} }
break; break;
@ -1223,12 +1272,12 @@ ModuleBuilder::processExportFrom(frontend::ParseNode* pn)
if (spec->isKind(PNK_EXPORT_SPEC)) { if (spec->isKind(PNK_EXPORT_SPEC)) {
RootedAtom bindingName(cx_, spec->pn_left->pn_atom); RootedAtom bindingName(cx_, spec->pn_left->pn_atom);
RootedAtom exportName(cx_, spec->pn_right->pn_atom); RootedAtom exportName(cx_, spec->pn_right->pn_atom);
if (!appendExportFromEntry(exportName, module, bindingName)) if (!appendExportFromEntry(exportName, module, bindingName, spec->pn_left))
return false; return false;
} else { } else {
MOZ_ASSERT(spec->isKind(PNK_EXPORT_BATCH_SPEC)); MOZ_ASSERT(spec->isKind(PNK_EXPORT_BATCH_SPEC));
RootedAtom importName(cx_, cx_->names().star); RootedAtom importName(cx_, cx_->names().star);
if (!appendExportFromEntry(nullptr, module, importName)) if (!appendExportFromEntry(nullptr, module, importName, spec))
return false; return false;
} }
} }
@ -1257,19 +1306,30 @@ ModuleBuilder::hasExportedName(JSAtom* name) const
} }
bool bool
ModuleBuilder::appendExportEntry(HandleAtom exportName, HandleAtom localName) ModuleBuilder::appendExportEntry(HandleAtom exportName, HandleAtom localName, ParseNode* node)
{ {
uint32_t line = 0;
uint32_t column = 0;
if (node)
tokenStream_.srcCoords.lineNumAndColumnIndex(node->pn_pos.begin, &line, &column);
Rooted<ExportEntryObject*> exportEntry(cx_); Rooted<ExportEntryObject*> exportEntry(cx_);
exportEntry = ExportEntryObject::create(cx_, exportName, nullptr, nullptr, localName); exportEntry = ExportEntryObject::create(cx_, exportName, nullptr, nullptr, localName,
line, column);
return exportEntry && exportEntries_.append(exportEntry); return exportEntry && exportEntries_.append(exportEntry);
} }
bool bool
ModuleBuilder::appendExportFromEntry(HandleAtom exportName, HandleAtom moduleRequest, ModuleBuilder::appendExportFromEntry(HandleAtom exportName, HandleAtom moduleRequest,
HandleAtom importName) HandleAtom importName, ParseNode* node)
{ {
uint32_t line;
uint32_t column;
tokenStream_.srcCoords.lineNumAndColumnIndex(node->pn_pos.begin, &line, &column);
Rooted<ExportEntryObject*> exportEntry(cx_); Rooted<ExportEntryObject*> exportEntry(cx_);
exportEntry = ExportEntryObject::create(cx_, exportName, moduleRequest, importName, nullptr); exportEntry = ExportEntryObject::create(cx_, exportName, moduleRequest, importName, nullptr,
line, column);
return exportEntry && exportEntries_.append(exportEntry); return exportEntry && exportEntries_.append(exportEntry);
} }

View File

@ -24,6 +24,7 @@ class ModuleObject;
namespace frontend { namespace frontend {
class ParseNode; class ParseNode;
class TokenStream;
} /* namespace frontend */ } /* namespace frontend */
typedef Rooted<ModuleObject*> RootedModuleObject; typedef Rooted<ModuleObject*> RootedModuleObject;
@ -39,6 +40,8 @@ class ImportEntryObject : public NativeObject
ModuleRequestSlot = 0, ModuleRequestSlot = 0,
ImportNameSlot, ImportNameSlot,
LocalNameSlot, LocalNameSlot,
LineNumberSlot,
ColumnNumberSlot,
SlotCount SlotCount
}; };
@ -48,10 +51,14 @@ class ImportEntryObject : public NativeObject
static ImportEntryObject* create(ExclusiveContext* cx, static ImportEntryObject* create(ExclusiveContext* cx,
HandleAtom moduleRequest, HandleAtom moduleRequest,
HandleAtom importName, HandleAtom importName,
HandleAtom localName); HandleAtom localName,
uint32_t lineNumber,
uint32_t columnNumber);
JSAtom* moduleRequest() const; JSAtom* moduleRequest() const;
JSAtom* importName() const; JSAtom* importName() const;
JSAtom* localName() const; JSAtom* localName() const;
uint32_t lineNumber() const;
uint32_t columnNumber() const;
}; };
typedef Rooted<ImportEntryObject*> RootedImportEntryObject; typedef Rooted<ImportEntryObject*> RootedImportEntryObject;
@ -66,6 +73,8 @@ class ExportEntryObject : public NativeObject
ModuleRequestSlot, ModuleRequestSlot,
ImportNameSlot, ImportNameSlot,
LocalNameSlot, LocalNameSlot,
LineNumberSlot,
ColumnNumberSlot,
SlotCount SlotCount
}; };
@ -76,11 +85,15 @@ class ExportEntryObject : public NativeObject
HandleAtom maybeExportName, HandleAtom maybeExportName,
HandleAtom maybeModuleRequest, HandleAtom maybeModuleRequest,
HandleAtom maybeImportName, HandleAtom maybeImportName,
HandleAtom maybeLocalName); HandleAtom maybeLocalName,
uint32_t lineNumber,
uint32_t columnNumber);
JSAtom* exportName() const; JSAtom* exportName() const;
JSAtom* moduleRequest() const; JSAtom* moduleRequest() const;
JSAtom* importName() const; JSAtom* importName() const;
JSAtom* localName() const; JSAtom* localName() const;
uint32_t lineNumber() const;
uint32_t columnNumber() const;
}; };
typedef Rooted<ExportEntryObject*> RootedExportEntryObject; typedef Rooted<ExportEntryObject*> RootedExportEntryObject;
@ -192,8 +205,8 @@ struct FunctionDeclaration
using FunctionDeclarationVector = GCVector<FunctionDeclaration, 0, ZoneAllocPolicy>; using FunctionDeclarationVector = GCVector<FunctionDeclaration, 0, ZoneAllocPolicy>;
// Possible values for ModuleState are defined in SelfHostingDefines.h. // Possible values for ModuleStatus are defined in SelfHostingDefines.h.
using ModuleState = int32_t; using ModuleStatus = int32_t;
class ModuleObject : public NativeObject class ModuleObject : public NativeObject
{ {
@ -204,7 +217,8 @@ class ModuleObject : public NativeObject
InitialEnvironmentSlot, InitialEnvironmentSlot,
EnvironmentSlot, EnvironmentSlot,
NamespaceSlot, NamespaceSlot,
StateSlot, StatusSlot,
ErrorSlot,
HostDefinedSlot, HostDefinedSlot,
RequestedModulesSlot, RequestedModulesSlot,
ImportEntriesSlot, ImportEntriesSlot,
@ -215,11 +229,21 @@ class ModuleObject : public NativeObject
NamespaceExportsSlot, NamespaceExportsSlot,
NamespaceBindingsSlot, NamespaceBindingsSlot,
FunctionDeclarationsSlot, FunctionDeclarationsSlot,
DFSIndexSlot,
DFSAncestorIndexSlot,
SlotCount SlotCount
}; };
static_assert(EnvironmentSlot == MODULE_OBJECT_ENVIRONMENT_SLOT, static_assert(EnvironmentSlot == MODULE_OBJECT_ENVIRONMENT_SLOT,
"EnvironmentSlot must match self-hosting define"); "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(DFSIndexSlot == MODULE_OBJECT_DFS_INDEX_SLOT,
"DFSIndexSlot must match self-hosting define");
static_assert(DFSAncestorIndexSlot == MODULE_OBJECT_DFS_ANCESTOR_INDEX_SLOT,
"DFSAncestorIndexSlot must match self-hosting define");
static const Class class_; static const Class class_;
@ -244,7 +268,8 @@ class ModuleObject : public NativeObject
ModuleEnvironmentObject& initialEnvironment() const; ModuleEnvironmentObject& initialEnvironment() const;
ModuleEnvironmentObject* environment() const; ModuleEnvironmentObject* environment() const;
ModuleNamespaceObject* namespace_(); ModuleNamespaceObject* namespace_();
ModuleState state() const; ModuleStatus status() const;
Value error() const;
Value hostDefinedField() const; Value hostDefinedField() const;
ArrayObject& requestedModules() const; ArrayObject& requestedModules() const;
ArrayObject& importEntries() const; ArrayObject& importEntries() const;
@ -255,8 +280,8 @@ class ModuleObject : public NativeObject
JSObject* namespaceExports(); JSObject* namespaceExports();
IndirectBindingMap* namespaceBindings(); IndirectBindingMap* namespaceBindings();
static bool DeclarationInstantiation(JSContext* cx, HandleModuleObject self); static bool Instantiate(JSContext* cx, HandleModuleObject self);
static bool Evaluation(JSContext* cx, HandleModuleObject self); static bool Evaluate(JSContext* cx, HandleModuleObject self);
void setHostDefinedField(const JS::Value& value); void setHostDefinedField(const JS::Value& value);
@ -269,10 +294,8 @@ class ModuleObject : public NativeObject
// For intrinsic_InstantiateModuleFunctionDeclarations. // For intrinsic_InstantiateModuleFunctionDeclarations.
static bool instantiateFunctionDeclarations(JSContext* cx, HandleModuleObject self); static bool instantiateFunctionDeclarations(JSContext* cx, HandleModuleObject self);
void setState(ModuleState newState); // For intrinsic_ExecuteModule.
static bool execute(JSContext* cx, HandleModuleObject self, MutableHandleValue rval);
// For intrinsic_EvaluateModule.
static bool evaluate(JSContext* cx, HandleModuleObject self, MutableHandleValue rval);
// For intrinsic_NewModuleNamespace. // For intrinsic_NewModuleNamespace.
static ModuleNamespaceObject* createNamespace(JSContext* cx, HandleModuleObject self, static ModuleNamespaceObject* createNamespace(JSContext* cx, HandleModuleObject self,
@ -294,7 +317,8 @@ class ModuleObject : public NativeObject
class MOZ_STACK_CLASS ModuleBuilder class MOZ_STACK_CLASS ModuleBuilder
{ {
public: public:
explicit ModuleBuilder(ExclusiveContext* cx, HandleModuleObject module); explicit ModuleBuilder(ExclusiveContext* cx, HandleModuleObject module,
const frontend::TokenStream& tokenStream);
bool processImport(frontend::ParseNode* pn); bool processImport(frontend::ParseNode* pn);
bool processExport(frontend::ParseNode* pn); bool processExport(frontend::ParseNode* pn);
@ -319,6 +343,7 @@ class MOZ_STACK_CLASS ModuleBuilder
ExclusiveContext* cx_; ExclusiveContext* cx_;
RootedModuleObject module_; RootedModuleObject module_;
const frontend::TokenStream& tokenStream_;
RootedAtomVector requestedModules_; RootedAtomVector requestedModules_;
RootedAtomVector importedBoundNames_; RootedAtomVector importedBoundNames_;
RootedImportEntryVector importEntries_; RootedImportEntryVector importEntries_;
@ -329,9 +354,10 @@ class MOZ_STACK_CLASS ModuleBuilder
ImportEntryObject* importEntryFor(JSAtom* localName) const; ImportEntryObject* importEntryFor(JSAtom* localName) const;
bool appendExportEntry(HandleAtom exportName, HandleAtom localName); bool appendExportEntry(HandleAtom exportName, HandleAtom localName,
frontend::ParseNode* node = nullptr);
bool appendExportFromEntry(HandleAtom exportName, HandleAtom moduleRequest, bool appendExportFromEntry(HandleAtom exportName, HandleAtom moduleRequest,
HandleAtom importName); HandleAtom importName, frontend::ParseNode* node);
bool maybeAppendRequestedModule(HandleAtom module); bool maybeAppendRequestedModule(HandleAtom module);

View File

@ -3741,7 +3741,7 @@ reflect_parse(JSContext* cx, uint32_t argc, Value* vp)
if (!module) if (!module)
return false; return false;
ModuleBuilder builder(cx, module); ModuleBuilder builder(cx, module, parser.tokenStream);
ModuleSharedContext modulesc(cx, module, &cx->global()->emptyGlobalScope(), builder); ModuleSharedContext modulesc(cx, module, &cx->global()->emptyGlobalScope(), builder);
pn = parser.moduleBody(&modulesc); pn = parser.moduleBody(&modulesc);
if (!pn) if (!pn)

View File

@ -97,11 +97,17 @@
#define REGEXP_STRING_ITERATOR_FLAGS_SLOT 2 #define REGEXP_STRING_ITERATOR_FLAGS_SLOT 2
#define REGEXP_STRING_ITERATOR_DONE_SLOT 3 #define REGEXP_STRING_ITERATOR_DONE_SLOT 3
#define MODULE_OBJECT_ENVIRONMENT_SLOT 2 #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_STATE_FAILED 0 #define MODULE_STATUS_ERRORED 0
#define MODULE_STATE_PARSED 1 #define MODULE_STATUS_UNINSTANTIATED 1
#define MODULE_STATE_INSTANTIATED 2 #define MODULE_STATUS_INSTANTIATING 2
#define MODULE_STATE_EVALUATED 3 #define MODULE_STATUS_INSTANTIATED 3
#define MODULE_STATUS_EVALUATING 4
#define MODULE_STATUS_EVALUATED 5
#endif #endif

View File

@ -27,13 +27,20 @@
// Assertions and debug printing, defined here instead of in the header above // Assertions and debug printing, defined here instead of in the header above
// to make `assert` invisible to C++. // to make `assert` invisible to C++.
#ifdef DEBUG #ifdef DEBUG
#define assert(b, info) if (!(b)) AssertionFailed(__FILE__ + ":" + __LINE__ + ": " + info) #define assert(b, info) \
#define dbg(msg) DumpMessage(callFunction(std_Array_pop, \ do { \
StringSplitString(__FILE__, '/')) \ if (!(b)) \
+ '#' + __LINE__ + ': ' + msg) AssertionFailed(__FILE__ + ":" + __LINE__ + ": " + info) \
} while (false)
#define dbg(msg) \
do { \
DumpMessage(callFunction(std_Array_pop, \
StringSplitString(__FILE__, '/')) + \
'#' + __LINE__ + ': ' + msg) \
} while (false)
#else #else
#define assert(b, info) // Elided assertion. #define assert(b, info) do {} while (false) // Elided assertion.
#define dbg(msg) // Elided debugging output. #define dbg(msg) do {} while (false) // Elided debugging output.
#endif #endif
// All C++-implemented standard builtins library functions used in self-hosted // All C++-implemented standard builtins library functions used in self-hosted

View File

@ -405,7 +405,7 @@ BytecodeCompiler::compileModule()
module->init(script); module->init(script);
ModuleBuilder builder(cx, module); ModuleBuilder builder(cx, module, parser->tokenStream);
ModuleSharedContext modulesc(cx, module, enclosingScope, builder); ModuleSharedContext modulesc(cx, module, enclosingScope, builder);
ParseNode* pn = parser->moduleBody(&modulesc); ParseNode* pn = parser->moduleBody(&modulesc);
if (!pn) if (!pn)

View File

@ -8261,7 +8261,7 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
if (topLevelFunction) { if (topLevelFunction) {
if (sc->isModuleContext()) { if (sc->isModuleContext()) {
// For modules, we record the function and instantiate the binding // For modules, we record the function and instantiate the binding
// during ModuleDeclarationInstantiation(), before the script is run. // during ModuleInstantiate(), before the script is run.
RootedModuleObject module(cx, sc->asModuleContext()->module()); RootedModuleObject module(cx, sc->asModuleContext()->module());
if (!module->noteFunctionDeclaration(cx, name, fun)) if (!module->noteFunctionDeclaration(cx, name, fun))

View File

@ -5038,7 +5038,7 @@ Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node impor
// Namespace imports are are not indirect bindings but lexical // Namespace imports are are not indirect bindings but lexical
// definitions that hold a module namespace object. They are treated // definitions that hold a module namespace object. They are treated
// as const variables which are initialized during the // as const variables which are initialized during the
// ModuleDeclarationInstantiation step. // ModuleInstantiate step.
RootedPropertyName bindingName(context, importedBinding()); RootedPropertyName bindingName(context, importedBinding());
if (!bindingName) if (!bindingName)
return false; return false;

View File

@ -5,10 +5,11 @@
load(libdir + "asserts.js"); load(libdir + "asserts.js");
load(libdir + "dummyModuleResolveHook.js"); load(libdir + "dummyModuleResolveHook.js");
function checkModuleEval(source, result) { function checkModuleEval(source) {
let m = parseModule(source); let m = parseModule(source);
m.declarationInstantiation(); m.declarationInstantiation();
assertEq(m.evaluation(), result); m.evaluation();
return m;
} }
function checkModuleSyntaxError(source) { function checkModuleSyntaxError(source) {
@ -23,17 +24,19 @@ c.declarationInstantiation();
c.evaluation(); c.evaluation();
// Check importing/exporting non-ambiguous name works. // Check importing/exporting non-ambiguous name works.
checkModuleEval("import { a } from 'c'; a;", 1); let d = checkModuleEval("import { a } from 'c';");
checkModuleEval("export { a } from 'c';", undefined); assertEq(getModuleEnvironmentValue(d, "a"), 1);
checkModuleEval("export { a } from 'c';");
// Check importing/exporting ambiguous name is a syntax error. // Check importing/exporting ambiguous name is a syntax error.
checkModuleSyntaxError("import { b } from 'c';"); checkModuleSyntaxError("import { b } from 'c';");
checkModuleSyntaxError("export { b } from 'c';"); checkModuleSyntaxError("export { b } from 'c';");
// Check that namespace objects include only non-ambiguous names. // Check that namespace objects include only non-ambiguous names.
let m = parseModule("import * as ns from 'c'; ns;"); let m = parseModule("import * as ns from 'c';");
m.declarationInstantiation(); m.declarationInstantiation();
let ns = m.evaluation(); m.evaluation();
let ns = c.namespace;
let names = Object.keys(ns); let names = Object.keys(ns);
assertEq(names.length, 2); assertEq(names.length, 2);
assertEq('a' in ns, true); assertEq('a' in ns, true);

View File

@ -0,0 +1,17 @@
// Prior to https://github.com/tc39/ecma262/pull/916 it was possible for a
// module namespace object to be successfully created that was later found to be
// erroneous. Test that this is no longer the case.
"use strict";
load(libdir + "asserts.js");
load(libdir + "dummyModuleResolveHook.js");
moduleRepo['A'] = parseModule('import "B"; export {x} from "C"');
moduleRepo['B'] = parseModule('import * as a from "A"');
moduleRepo['C'] = parseModule('export * from "D"; export * from "E"');
moduleRepo['D'] = parseModule('export let x');
moduleRepo['E'] = parseModule('export let x');
let m = moduleRepo['A'];
assertThrowsInstanceOf(() => m.declarationInstantiation(), SyntaxError);

View File

@ -1,23 +1,36 @@
// |jit-test| error: InternalError
// This tests that attempting to perform ModuleDeclarationInstantation a second // This tests that attempting to perform ModuleDeclarationInstantation a second
// time after a failure throws an error. Doing this would be a bug in the module // time after a failure re-throws the same error.
// loader, which is expected to throw away modules if there is an error
// instantiating them.
// //
// The first attempt fails becuase module 'a' is not available. The second // The first attempt fails becuase module 'a' is not available. The second
// attempt fails because of the previous failure (it would otherwise succeed as // attempt fails because of the previous failure (it would otherwise succeed as
// 'a' is now available). // 'a' is now available).
let moduleRepo = {}; load(libdir + "dummyModuleResolveHook.js");
setModuleResolveHook(function(module, specifier) {
return moduleRepo[specifier]; let b = moduleRepo['b'] = parseModule("export var b = 3; export var c = 4;");
}); let c = moduleRepo['c'] = parseModule("export * from 'a'; export * from 'b';");
let e1;
let threw = false;
try { try {
let b = moduleRepo['b'] = parseModule("export var b = 3; export var c = 4;");
let c = moduleRepo['c'] = parseModule("export * from 'a'; export * from 'b';");
c.declarationInstantiation(); c.declarationInstantiation();
} catch (exc) {} } catch (exc) {
threw = true;
e1 = exc;
}
assertEq(threw, true);
assertEq(typeof e1 === "undefined", false);
let a = moduleRepo['a'] = parseModule("export var a = 1; export var b = 2;"); let a = moduleRepo['a'] = parseModule("export var a = 1; export var b = 2;");
let d = moduleRepo['d'] = parseModule("import { a } from 'c'; a;"); let d = moduleRepo['d'] = parseModule("import { a } from 'c'; a;");
d.declarationInstantiation();
threw = false;
let e2;
try {
d.declarationInstantiation();
} catch (exc) {
threw = true;
e2 = exc;
}
assertEq(threw, true);
assertEq(e1, e2);

View File

@ -20,3 +20,5 @@ let d = moduleRepo['d'] = parseModule("import { a } from 'c'; a;");
// Attempting to instantiate 'd' throws an error because depdency 'a' of // Attempting to instantiate 'd' throws an error because depdency 'a' of
// instantiated module 'c' is not instantiated. // instantiated module 'c' is not instantiated.
d.declarationInstantiation(); d.declarationInstantiation();
d.evaluation();

View File

@ -0,0 +1,6 @@
// |jit-test| error: NaN
let m = parseModule(`
throw i => { return 5; }, m-1;
`);
m.declarationInstantiation();
m.evaluation();

View File

@ -1,32 +1,34 @@
// Test interaction with global object and global lexical scope. // Test interaction with global object and global lexical scope.
function parseAndEvaluate(source) { function evalModuleAndCheck(source, expected) {
let m = parseModule(source); let m = parseModule(source);
m.declarationInstantiation(); m.declarationInstantiation();
return m.evaluation(); m.evaluation();
assertEq(getModuleEnvironmentValue(m, "r"), expected);
} }
var x = 1; var x = 1;
assertEq(parseAndEvaluate("let r = x; x = 2; r"), 1); evalModuleAndCheck("export let r = x; x = 2;", 1);
assertEq(x, 2); assertEq(x, 2);
let y = 3; let y = 3;
assertEq(parseAndEvaluate("let r = y; y = 4; r"), 3); evalModuleAndCheck("export let r = y; y = 4;", 3);
assertEq(y, 4); assertEq(y, 4);
if (helperThreadCount() == 0) if (helperThreadCount() == 0)
quit(); quit();
function offThreadParseAndEvaluate(source) { function offThreadEvalModuleAndCheck(source, expected) {
offThreadCompileModule(source); offThreadCompileModule(source);
let m = finishOffThreadModule(); let m = finishOffThreadModule();
print("compiled"); print("compiled");
m.declarationInstantiation(); m.declarationInstantiation();
return m.evaluation(); m.evaluation();
assertEq(getModuleEnvironmentValue(m, "r"), expected);
} }
assertEq(offThreadParseAndEvaluate("let r = x; x = 5; r"), 2); offThreadEvalModuleAndCheck("export let r = x; x = 5;", 2);
assertEq(x, 5); assertEq(x, 5);
assertEq(offThreadParseAndEvaluate("let r = y; y = 6; r"), 4); offThreadEvalModuleAndCheck("export let r = y; y = 6;", 4);
assertEq(y, 6); assertEq(y, 6);

View File

@ -6,16 +6,17 @@ load(libdir + "dummyModuleResolveHook.js");
function parseAndEvaluate(source) { function parseAndEvaluate(source) {
let m = parseModule(source); let m = parseModule(source);
m.declarationInstantiation(); m.declarationInstantiation();
return m.evaluation(); m.evaluation();
return m;
} }
// Check the evaluation of an empty module succeeds. // Check the evaluation of an empty module succeeds.
assertEq(typeof parseAndEvaluate(""), "undefined"); parseAndEvaluate("");
// Check evaluation returns evaluation result the first time, then undefined. // Check evaluation returns evaluation result the first time, then undefined.
let m = parseModule("1"); let m = parseModule("1");
m.declarationInstantiation(); m.declarationInstantiation();
assertEq(m.evaluation(), 1); assertEq(m.evaluation(), undefined);
assertEq(typeof m.evaluation(), "undefined"); assertEq(typeof m.evaluation(), "undefined");
// Check top level variables are initialized by evaluation. // Check top level variables are initialized by evaluation.
@ -60,31 +61,35 @@ parseAndEvaluate("export default class { constructor() {} };");
parseAndEvaluate("export default class foo { constructor() {} };"); parseAndEvaluate("export default class foo { constructor() {} };");
// Test default import // Test default import
m = parseModule("import a from 'a'; a;") m = parseModule("import a from 'a'; export { a };")
m.declarationInstantiation(); m.declarationInstantiation();
assertEq(m.evaluation(), 2); m.evaluation();
assertEq(getModuleEnvironmentValue(m, "a"), 2);
// Test named import // Test named import
m = parseModule("import { x as y } from 'a'; y;") m = parseModule("import { x as y } from 'a'; export { y };")
m.declarationInstantiation(); m.declarationInstantiation();
assertEq(m.evaluation(), 1); m.evaluation();
assertEq(getModuleEnvironmentValue(m, "y"), 1);
// Call exported function // Call exported function
m = parseModule("import { f } from 'a'; f(3);") m = parseModule("import { f } from 'a'; export let x = f(3);")
m.declarationInstantiation(); m.declarationInstantiation();
assertEq(m.evaluation(), 4); m.evaluation();
assertEq(getModuleEnvironmentValue(m, "x"), 4);
// Test importing an indirect export // Test importing an indirect export
moduleRepo['b'] = parseModule("export { x as z } from 'a';"); moduleRepo['b'] = parseModule("export { x as z } from 'a';");
assertEq(parseAndEvaluate("import { z } from 'b'; z"), 1); m = parseAndEvaluate("import { z } from 'b'; export { z }");
assertEq(getModuleEnvironmentValue(m, "z"), 1);
// Test cyclic dependencies // Test cyclic dependencies
moduleRepo['c1'] = parseModule("export var x = 1; export {y} from 'c2'"); moduleRepo['c1'] = parseModule("export var x = 1; export {y} from 'c2'");
moduleRepo['c2'] = parseModule("export var y = 2; export {x} from 'c1'"); moduleRepo['c2'] = parseModule("export var y = 2; export {x} from 'c1'");
assertDeepEq(parseAndEvaluate(`import { x as x1, y as y1 } from 'c1'; m = parseAndEvaluate(`import { x as x1, y as y1 } from 'c1';
import { x as x2, y as y2 } from 'c2'; import { x as x2, y as y2 } from 'c2';
[x1, y1, x2, y2]`), export let z = [x1, y1, x2, y2]`),
[1, 2, 1, 2]); assertDeepEq(getModuleEnvironmentValue(m, "z"), [1, 2, 1, 2]);
// Import access in functions // Import access in functions
m = parseModule("import { x } from 'a'; function f() { return x; }") m = parseModule("import { x } from 'a'; function f() { return x; }")

View File

@ -573,14 +573,14 @@ MSG_DEF(JSMSG_REINIT_THIS, 0, JSEXN_REFERENCEERR, "super() called twice in
// Modules // Modules
MSG_DEF(JSMSG_BAD_DEFAULT_EXPORT, 0, JSEXN_SYNTAXERR, "default export cannot be provided by export *") MSG_DEF(JSMSG_BAD_DEFAULT_EXPORT, 0, JSEXN_SYNTAXERR, "default export cannot be provided by export *")
MSG_DEF(JSMSG_MISSING_INDIRECT_EXPORT, 1, JSEXN_SYNTAXERR, "indirect export '{0}' not found") MSG_DEF(JSMSG_MISSING_INDIRECT_EXPORT, 0, JSEXN_SYNTAXERR, "indirect export not found")
MSG_DEF(JSMSG_AMBIGUOUS_INDIRECT_EXPORT, 1, JSEXN_SYNTAXERR, "ambiguous indirect export '{0}'") MSG_DEF(JSMSG_AMBIGUOUS_INDIRECT_EXPORT, 0, JSEXN_SYNTAXERR, "ambiguous indirect export")
MSG_DEF(JSMSG_MISSING_IMPORT, 1, JSEXN_SYNTAXERR, "import '{0}' not found") MSG_DEF(JSMSG_MISSING_IMPORT, 0, JSEXN_SYNTAXERR, "import not found")
MSG_DEF(JSMSG_AMBIGUOUS_IMPORT, 1, JSEXN_SYNTAXERR, "ambiguous import '{0}'") 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_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_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_MODULE_INSTANTIATE_FAILED, 0, JSEXN_INTERNALERR, "attempt to re-instantiate module after failure")
MSG_DEF(JSMSG_BAD_MODULE_STATE, 0, JSEXN_INTERNALERR, "module record in unexpected state") MSG_DEF(JSMSG_BAD_MODULE_STATUS, 0, JSEXN_INTERNALERR, "module record has unexpected status")
// Promise // Promise
MSG_DEF(JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF, 0, JSEXN_TYPEERR, "A promise cannot be resolved with itself.") MSG_DEF(JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF, 0, JSEXN_TYPEERR, "A promise cannot be resolved with itself.")

View File

@ -4704,21 +4704,21 @@ JS::GetModuleHostDefinedField(JSObject* module)
} }
JS_PUBLIC_API(bool) JS_PUBLIC_API(bool)
JS::ModuleDeclarationInstantiation(JSContext* cx, JS::HandleObject moduleArg) JS::ModuleInstantiate(JSContext* cx, JS::HandleObject moduleArg)
{ {
AssertHeapIsIdle(cx); AssertHeapIsIdle(cx);
CHECK_REQUEST(cx); CHECK_REQUEST(cx);
assertSameCompartment(cx, moduleArg); assertSameCompartment(cx, moduleArg);
return ModuleObject::DeclarationInstantiation(cx, moduleArg.as<ModuleObject>()); return ModuleObject::Instantiate(cx, moduleArg.as<ModuleObject>());
} }
JS_PUBLIC_API(bool) JS_PUBLIC_API(bool)
JS::ModuleEvaluation(JSContext* cx, JS::HandleObject moduleArg) JS::ModuleEvaluate(JSContext* cx, JS::HandleObject moduleArg)
{ {
AssertHeapIsIdle(cx); AssertHeapIsIdle(cx);
CHECK_REQUEST(cx); CHECK_REQUEST(cx);
assertSameCompartment(cx, moduleArg); assertSameCompartment(cx, moduleArg);
return ModuleObject::Evaluation(cx, moduleArg.as<ModuleObject>()); return ModuleObject::Evaluate(cx, moduleArg.as<ModuleObject>());
} }
JS_PUBLIC_API(JSObject*) JS_PUBLIC_API(JSObject*)
@ -4739,6 +4739,18 @@ JS::GetModuleScript(JSContext* cx, JS::HandleObject moduleArg)
return moduleArg->as<ModuleObject>().script(); 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_PUBLIC_API(JSObject*)
JS_New(JSContext* cx, HandleObject ctor, const JS::HandleValueArray& inputArgs) JS_New(JSContext* cx, HandleObject ctor, const JS::HandleValueArray& inputArgs)
{ {

View File

@ -4356,28 +4356,27 @@ extern JS_PUBLIC_API(JS::Value)
GetModuleHostDefinedField(JSObject* module); GetModuleHostDefinedField(JSObject* module);
/* /*
* Perform the ModuleDeclarationInstantiation operation on on the give source * Perform the ModuleInstantiate operation on the given source text module
* text module record. * record.
* *
* This transitively resolves all module dependencies (calling the * This transitively resolves all module dependencies (calling the
* HostResolveImportedModule hook) and initializes the environment record for * HostResolveImportedModule hook) and initializes the environment record for
* the module. * the module.
*/ */
extern JS_PUBLIC_API(bool) extern JS_PUBLIC_API(bool)
ModuleDeclarationInstantiation(JSContext* cx, JS::HandleObject moduleRecord); ModuleInstantiate(JSContext* cx, JS::HandleObject moduleRecord);
/* /*
* Perform the ModuleEvaluation operation on on the give source text module * Perform the ModuleEvaluate operation on the given source text module record.
* record.
* *
* This does nothing if this module has already been evaluated. Otherwise, it * This does nothing if this module has already been evaluated. Otherwise, it
* transitively evaluates all dependences of this module and then evaluates this * transitively evaluates all dependences of this module and then evaluates this
* module. * module.
* *
* ModuleDeclarationInstantiation must have completed prior to calling this. * ModuleInstantiate must have completed prior to calling this.
*/ */
extern JS_PUBLIC_API(bool) extern JS_PUBLIC_API(bool)
ModuleEvaluation(JSContext* cx, JS::HandleObject moduleRecord); ModuleEvaluate(JSContext* cx, JS::HandleObject moduleRecord);
/* /*
* Get a list of the module specifiers used by a source text module * Get a list of the module specifiers used by a source text module
@ -4396,6 +4395,12 @@ GetRequestedModules(JSContext* cx, JS::HandleObject moduleRecord);
extern JS_PUBLIC_API(JSScript*) extern JS_PUBLIC_API(JSScript*)
GetModuleScript(JSContext* cx, JS::HandleObject moduleRecord); 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 */ } /* namespace JS */
extern JS_PUBLIC_API(bool) extern JS_PUBLIC_API(bool)

View File

@ -227,8 +227,8 @@
macro(missingArguments, missingArguments, "missingArguments") \ macro(missingArguments, missingArguments, "missingArguments") \
macro(module, module, "module") \ macro(module, module, "module") \
macro(Module, Module, "Module") \ macro(Module, Module, "Module") \
macro(ModuleDeclarationInstantiation, ModuleDeclarationInstantiation, "ModuleDeclarationInstantiation") \ macro(ModuleInstantiate, ModuleInstantiate, "ModuleInstantiate") \
macro(ModuleEvaluation, ModuleEvaluation, "ModuleEvaluation") \ macro(ModuleEvaluate, ModuleEvaluate, "ModuleEvaluate") \
macro(month, month, "month") \ macro(month, month, "month") \
macro(multiline, multiline, "multiline") \ macro(multiline, multiline, "multiline") \
macro(name, name, "name") \ macro(name, name, "name") \

View File

@ -345,6 +345,50 @@ intrinsic_ThrowInternalError(JSContext* cx, unsigned argc, Value* vp)
return false; return false;
} }
static bool
intrinsic_GetErrorMessage(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isInt32());
const JSErrorFormatString* errorString = GetErrorMessage(nullptr, args[0].toInt32());
MOZ_ASSERT(errorString);
MOZ_ASSERT(errorString->argCount == 0);
RootedString message(cx, JS_NewStringCopyZ(cx, errorString->format));
if (!message)
return false;
args.rval().setString(message);
return true;
}
static bool
intrinsic_CreateModuleSyntaxError(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 4);
MOZ_ASSERT(args[0].isObject());
MOZ_ASSERT(args[1].isInt32());
MOZ_ASSERT(args[2].isInt32());
MOZ_ASSERT(args[3].isString());
RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
RootedString filename(cx, JS_NewStringCopyZ(cx, module->script()->filename()));
RootedString message(cx, args[3].toString());
RootedValue error(cx);
if (!JS::CreateError(cx, JSEXN_SYNTAXERR, nullptr, filename, args[1].toInt32(),
args[2].toInt32(), nullptr, message, &error))
{
return false;
}
args.rval().set(error);
return true;
}
/** /**
* Handles an assertion failure in self-hosted code just like an assertion * Handles an assertion failure in self-hosted code just like an assertion
* failure in C++ code. Information about the failure can be provided in args[0]. * failure in C++ code. Information about the failure can be provided in args[0].
@ -2060,24 +2104,12 @@ intrinsic_InstantiateModuleFunctionDeclarations(JSContext* cx, unsigned argc, Va
} }
static bool static bool
intrinsic_SetModuleState(JSContext* cx, unsigned argc, Value* vp) intrinsic_ExecuteModule(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
ModuleState newState = args[1].toInt32();
module->setState(newState);
args.rval().setUndefined();
return true;
}
static bool
intrinsic_EvaluateModule(JSContext* cx, unsigned argc, Value* vp)
{ {
CallArgs args = CallArgsFromVp(argc, vp); CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1); MOZ_ASSERT(args.length() == 1);
RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>()); RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
return ModuleObject::evaluate(cx, module, args.rval()); return ModuleObject::execute(cx, module, args.rval());
} }
static bool static bool
@ -2351,6 +2383,8 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("ThrowTypeError", intrinsic_ThrowTypeError, 4,0), JS_FN("ThrowTypeError", intrinsic_ThrowTypeError, 4,0),
JS_FN("ThrowSyntaxError", intrinsic_ThrowSyntaxError, 4,0), JS_FN("ThrowSyntaxError", intrinsic_ThrowSyntaxError, 4,0),
JS_FN("ThrowInternalError", intrinsic_ThrowInternalError, 4,0), JS_FN("ThrowInternalError", intrinsic_ThrowInternalError, 4,0),
JS_FN("GetErrorMessage", intrinsic_GetErrorMessage, 1,0),
JS_FN("CreateModuleSyntaxError", intrinsic_CreateModuleSyntaxError, 4,0),
JS_FN("AssertionFailed", intrinsic_AssertionFailed, 1,0), JS_FN("AssertionFailed", intrinsic_AssertionFailed, 1,0),
JS_FN("DumpMessage", intrinsic_DumpMessage, 1,0), JS_FN("DumpMessage", intrinsic_DumpMessage, 1,0),
JS_FN("OwnPropertyKeys", intrinsic_OwnPropertyKeys, 1,0), JS_FN("OwnPropertyKeys", intrinsic_OwnPropertyKeys, 1,0),
@ -2630,8 +2664,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("CreateNamespaceBinding", intrinsic_CreateNamespaceBinding, 3, 0), JS_FN("CreateNamespaceBinding", intrinsic_CreateNamespaceBinding, 3, 0),
JS_FN("InstantiateModuleFunctionDeclarations", JS_FN("InstantiateModuleFunctionDeclarations",
intrinsic_InstantiateModuleFunctionDeclarations, 1, 0), intrinsic_InstantiateModuleFunctionDeclarations, 1, 0),
JS_FN("SetModuleState", intrinsic_SetModuleState, 1, 0), JS_FN("ExecuteModule", intrinsic_ExecuteModule, 1, 0),
JS_FN("EvaluateModule", intrinsic_EvaluateModule, 1, 0),
JS_FN("NewModuleNamespace", intrinsic_NewModuleNamespace, 2, 0), JS_FN("NewModuleNamespace", intrinsic_NewModuleNamespace, 2, 0),
JS_FN("AddModuleNamespaceBinding", intrinsic_AddModuleNamespaceBinding, 4, 0), JS_FN("AddModuleNamespaceBinding", intrinsic_AddModuleNamespaceBinding, 4, 0),
JS_FN("ModuleNamespaceExports", intrinsic_ModuleNamespaceExports, 1, 0), JS_FN("ModuleNamespaceExports", intrinsic_ModuleNamespaceExports, 1, 0),