|
|
@@ -7,6 +7,7 @@
|
|
|
#include "Types/SimpleDictionaryPropertyDescriptor.h"
|
|
|
#include "Types/SimpleDictionaryTypeHandler.h"
|
|
|
#include "ModuleNamespace.h"
|
|
|
+#include "Library/JavascriptPromise.h"
|
|
|
|
|
|
namespace Js
|
|
|
{
|
|
|
@@ -36,15 +37,14 @@ namespace Js
|
|
|
resolvedExportMap(nullptr),
|
|
|
wasParsed(false),
|
|
|
wasDeclarationInitialized(false),
|
|
|
-#if DBG
|
|
|
parentsNotified(false),
|
|
|
-#endif
|
|
|
isRootModule(false),
|
|
|
hadNotifyHostReady(false),
|
|
|
localExportSlots(nullptr),
|
|
|
numPendingChildrenModule(0),
|
|
|
moduleId(InvalidModuleIndex),
|
|
|
localSlotCount(InvalidSlotCount),
|
|
|
+ promise(nullptr),
|
|
|
localExportCount(0)
|
|
|
{
|
|
|
namespaceRecord.module = this;
|
|
|
@@ -74,15 +74,11 @@ namespace Js
|
|
|
localExportRecordList = nullptr;
|
|
|
indirectExportRecordList = nullptr;
|
|
|
starExportRecordList = nullptr;
|
|
|
- childrenModuleSet = nullptr;
|
|
|
parentModuleList = nullptr;
|
|
|
if (!isShutdown)
|
|
|
{
|
|
|
- if (parser != nullptr)
|
|
|
- {
|
|
|
- AllocatorDelete(ArenaAllocator, scriptContext->GeneralAllocator(), parser);
|
|
|
- parser = nullptr;
|
|
|
- }
|
|
|
+ AdeleteUnlessNull(scriptContext->GeneralAllocator(), parser);
|
|
|
+ AdeleteUnlessNull(scriptContext->GeneralAllocator(), childrenModuleSet);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -91,6 +87,7 @@ namespace Js
|
|
|
OUTPUT_TRACE_DEBUGONLY(Js::ModulePhase, _u("ParseSource(%s)\n"), this->GetSpecifierSz());
|
|
|
Assert(!wasParsed);
|
|
|
Assert(parser == nullptr);
|
|
|
+ Assert(exceptionVar != nullptr);
|
|
|
HRESULT hr = NOERROR;
|
|
|
ScriptContext* scriptContext = GetScriptContext();
|
|
|
CompileScriptException se;
|
|
|
@@ -98,6 +95,9 @@ namespace Js
|
|
|
*exceptionVar = nullptr;
|
|
|
if (!scriptContext->GetConfig()->IsES6ModuleEnabled())
|
|
|
{
|
|
|
+ JavascriptError *pError = scriptContext->GetLibrary()->CreateError();
|
|
|
+ JavascriptError::SetErrorMessageProperties(pError, hr, _u("ES6Module not supported"), scriptContext);
|
|
|
+ *exceptionVar = pError;
|
|
|
return E_NOTIMPL;
|
|
|
}
|
|
|
// Host indicates that the current module failed to load.
|
|
|
@@ -105,8 +105,8 @@ namespace Js
|
|
|
{
|
|
|
Assert(sourceLength == 0);
|
|
|
hr = E_FAIL;
|
|
|
- JavascriptError *pError = scriptContext->GetLibrary()->CreateError();
|
|
|
- JavascriptError::SetErrorMessageProperties(pError, hr, _u("host failed to download module"), scriptContext);
|
|
|
+ JavascriptError *pError = scriptContext->GetLibrary()->CreateURIError();
|
|
|
+ JavascriptError::SetErrorMessageProperties(pError, hr, this->GetSpecifierSz(), scriptContext);
|
|
|
*exceptionVar = pError;
|
|
|
}
|
|
|
else
|
|
|
@@ -114,7 +114,7 @@ namespace Js
|
|
|
try
|
|
|
{
|
|
|
AUTO_NESTED_HANDLED_EXCEPTION_TYPE((ExceptionType)(ExceptionType_OutOfMemory | ExceptionType_StackOverflow));
|
|
|
- this->parser = (Parser*)AllocatorNew(ArenaAllocator, allocator, Parser, scriptContext);
|
|
|
+ this->parser = Anew(allocator, Parser, scriptContext);
|
|
|
srcInfo->moduleID = moduleId;
|
|
|
|
|
|
LoadScriptFlag loadScriptFlag = (LoadScriptFlag)(LoadScriptFlag_Expression | LoadScriptFlag_Module |
|
|
|
@@ -166,7 +166,7 @@ namespace Js
|
|
|
if (this->parser)
|
|
|
{
|
|
|
this->parseTree = nullptr;
|
|
|
- AllocatorDelete(ArenaAllocator, allocator, this->parser);
|
|
|
+ Adelete(allocator, this->parser);
|
|
|
this->parser = nullptr;
|
|
|
}
|
|
|
if (this->errorObject == nullptr)
|
|
|
@@ -175,6 +175,11 @@ namespace Js
|
|
|
}
|
|
|
|
|
|
OUTPUT_TRACE_DEBUGONLY(Js::ModulePhase, _u("\t>NotifyParentAsNeeded\n"));
|
|
|
+ if (this->promise != nullptr)
|
|
|
+ {
|
|
|
+ SourceTextModuleRecord::ResolveOrRejectDynamicImportPromise(false, this->errorObject, this->scriptContext, this);
|
|
|
+ }
|
|
|
+
|
|
|
NotifyParentsAsNeeded();
|
|
|
}
|
|
|
return hr;
|
|
|
@@ -190,9 +195,7 @@ namespace Js
|
|
|
parentModule->OnChildModuleReady(this, this->errorObject);
|
|
|
});
|
|
|
}
|
|
|
-#if DBG
|
|
|
SetParentsNotified();
|
|
|
-#endif
|
|
|
}
|
|
|
|
|
|
void SourceTextModuleRecord::ImportModuleListsFromParser()
|
|
|
@@ -220,6 +223,55 @@ namespace Js
|
|
|
return hr;
|
|
|
}
|
|
|
|
|
|
+ Var SourceTextModuleRecord::PostProcessDynamicModuleImport()
|
|
|
+ {
|
|
|
+ JavascriptPromise *promise = this->GetPromise();
|
|
|
+ ScriptContext* scriptContext = GetScriptContext();
|
|
|
+ AnalysisAssert(scriptContext != nullptr);
|
|
|
+ if (promise == nullptr)
|
|
|
+ {
|
|
|
+ promise = JavascriptPromise::CreateEnginePromise(scriptContext);
|
|
|
+ this->SetPromise(promise);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this->ParentsNotified())
|
|
|
+ {
|
|
|
+ HRESULT hr = NOERROR;
|
|
|
+ if (!WasDeclarationInitialized())
|
|
|
+ {
|
|
|
+ ModuleDeclarationInstantiation();
|
|
|
+
|
|
|
+ if (this->errorObject != nullptr)
|
|
|
+ {
|
|
|
+ SourceTextModuleRecord::ResolveOrRejectDynamicImportPromise(false, this->errorObject, scriptContext, this);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (!hadNotifyHostReady && !WasEvaluated())
|
|
|
+ {
|
|
|
+ bool isScriptActive = scriptContext->GetThreadContext()->IsScriptActive();
|
|
|
+
|
|
|
+ LEAVE_SCRIPT_IF(scriptContext, isScriptActive,
|
|
|
+ {
|
|
|
+ hr = scriptContext->GetHostScriptContext()->NotifyHostAboutModuleReady(this, this->errorObject);
|
|
|
+ });
|
|
|
+
|
|
|
+ hadNotifyHostReady = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (FAILED(hr))
|
|
|
+ {
|
|
|
+ Js::JavascriptError * error = scriptContext->GetLibrary()->CreateURIError();
|
|
|
+ JavascriptError::SetErrorMessageProperties(error, hr, this->GetSpecifierSz(), scriptContext);
|
|
|
+ return SourceTextModuleRecord::ResolveOrRejectDynamicImportPromise(false, error, scriptContext, this);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return this->promise;
|
|
|
+ }
|
|
|
+
|
|
|
HRESULT SourceTextModuleRecord::PrepareForModuleDeclarationInitialization()
|
|
|
{
|
|
|
OUTPUT_TRACE_DEBUGONLY(Js::ModulePhase, _u("PrepareForModuleDeclarationInitialization(%s)\n"), this->GetSpecifierSz());
|
|
|
@@ -230,17 +282,22 @@ namespace Js
|
|
|
OUTPUT_TRACE_DEBUGONLY(Js::ModulePhase, _u("\t>NotifyParentsAsNeeded\n"));
|
|
|
NotifyParentsAsNeeded();
|
|
|
|
|
|
- if (!WasDeclarationInitialized() && isRootModule)
|
|
|
+ if (this->promise != nullptr || (!WasDeclarationInitialized() && isRootModule))
|
|
|
{
|
|
|
// TODO: move this as a promise call? if parser is called from a different thread
|
|
|
// We'll need to call the bytecode gen in the main thread as we are accessing GC.
|
|
|
ScriptContext* scriptContext = GetScriptContext();
|
|
|
- Assert(!scriptContext->GetThreadContext()->IsScriptActive());
|
|
|
+ bool isScriptActive = scriptContext->GetThreadContext()->IsScriptActive();
|
|
|
+ Assert(!isScriptActive || this->promise != nullptr);
|
|
|
|
|
|
ModuleDeclarationInstantiation();
|
|
|
- if (!hadNotifyHostReady)
|
|
|
+ if (!hadNotifyHostReady && !WasEvaluated())
|
|
|
{
|
|
|
- hr = scriptContext->GetHostScriptContext()->NotifyHostAboutModuleReady(this, this->errorObject);
|
|
|
+ LEAVE_SCRIPT_IF(scriptContext, isScriptActive,
|
|
|
+ {
|
|
|
+ hr = scriptContext->GetHostScriptContext()->NotifyHostAboutModuleReady(this, this->errorObject);
|
|
|
+ });
|
|
|
+
|
|
|
hadNotifyHostReady = true;
|
|
|
}
|
|
|
}
|
|
|
@@ -268,9 +325,21 @@ namespace Js
|
|
|
|
|
|
OUTPUT_TRACE_DEBUGONLY(Js::ModulePhase, _u("\t>NotifyParentAsNeeded (childException)\n"), this->GetSpecifierSz());
|
|
|
NotifyParentsAsNeeded();
|
|
|
- if (isRootModule && !hadNotifyHostReady)
|
|
|
+
|
|
|
+ bool isScriptActive = scriptContext->GetThreadContext()->IsScriptActive();
|
|
|
+
|
|
|
+ if (this->promise != nullptr)
|
|
|
+ {
|
|
|
+ SourceTextModuleRecord::ResolveOrRejectDynamicImportPromise(false, this->errorObject, this->scriptContext, this);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this->promise != nullptr || (isRootModule && !hadNotifyHostReady))
|
|
|
{
|
|
|
- hr = scriptContext->GetHostScriptContext()->NotifyHostAboutModuleReady(this, this->errorObject);
|
|
|
+ LEAVE_SCRIPT_IF(scriptContext, isScriptActive,
|
|
|
+ {
|
|
|
+ hr = scriptContext->GetHostScriptContext()->NotifyHostAboutModuleReady(this, this->errorObject);
|
|
|
+ });
|
|
|
+
|
|
|
hadNotifyHostReady = true;
|
|
|
}
|
|
|
}
|
|
|
@@ -311,7 +380,7 @@ namespace Js
|
|
|
ArenaAllocator* allocator = scriptContext->GeneralAllocator();
|
|
|
if (exportStarSet == nullptr)
|
|
|
{
|
|
|
- exportStarSet = (ExportModuleRecordList*)AllocatorNew(ArenaAllocator, allocator, ExportModuleRecordList, allocator);
|
|
|
+ exportStarSet = Anew(allocator, ExportModuleRecordList, allocator);
|
|
|
}
|
|
|
if (exportStarSet->Has(this))
|
|
|
{
|
|
|
@@ -321,7 +390,7 @@ namespace Js
|
|
|
ExportedNames* tempExportedNames = nullptr;
|
|
|
if (this->localExportRecordList != nullptr)
|
|
|
{
|
|
|
- tempExportedNames = (ExportedNames*)AllocatorNew(ArenaAllocator, allocator, ExportedNames, allocator);
|
|
|
+ tempExportedNames = Anew(allocator, ExportedNames, allocator);
|
|
|
this->localExportRecordList->Map([=](ModuleImportOrExportEntry exportEntry) {
|
|
|
PropertyId exportNameId = EnsurePropertyIdForIdentifier(exportEntry.exportName);
|
|
|
tempExportedNames->Prepend(exportNameId);
|
|
|
@@ -331,7 +400,7 @@ namespace Js
|
|
|
{
|
|
|
if (tempExportedNames == nullptr)
|
|
|
{
|
|
|
- tempExportedNames = (ExportedNames*)AllocatorNew(ArenaAllocator, allocator, ExportedNames, allocator);
|
|
|
+ tempExportedNames = Anew(allocator, ExportedNames, allocator);
|
|
|
}
|
|
|
this->indirectExportRecordList->Map([=](ModuleImportOrExportEntry exportEntry) {
|
|
|
PropertyId exportedNameId = EnsurePropertyIdForIdentifier(exportEntry.exportName);
|
|
|
@@ -342,7 +411,7 @@ namespace Js
|
|
|
{
|
|
|
if (tempExportedNames == nullptr)
|
|
|
{
|
|
|
- tempExportedNames = (ExportedNames*)AllocatorNew(ArenaAllocator, allocator, ExportedNames, allocator);
|
|
|
+ tempExportedNames = Anew(allocator, ExportedNames, allocator);
|
|
|
}
|
|
|
this->starExportRecordList->Map([=](ModuleImportOrExportEntry exportEntry) {
|
|
|
Assert(exportEntry.moduleRequest != nullptr);
|
|
|
@@ -409,7 +478,7 @@ namespace Js
|
|
|
ArenaAllocator* allocator = scriptContext->GeneralAllocator();
|
|
|
if (resolvedExportMap == nullptr)
|
|
|
{
|
|
|
- resolvedExportMap = AllocatorNew(ArenaAllocator, allocator, ResolvedExportMap, allocator);
|
|
|
+ resolvedExportMap = Anew(allocator, ResolvedExportMap, allocator);
|
|
|
}
|
|
|
if (resolvedExportMap->TryGetReference(exportName, exportRecord))
|
|
|
{
|
|
|
@@ -418,11 +487,11 @@ namespace Js
|
|
|
// TODO: use per-call/loop allocator?
|
|
|
if (exportStarSet == nullptr)
|
|
|
{
|
|
|
- exportStarSet = (ExportModuleRecordList*)AllocatorNew(ArenaAllocator, allocator, ExportModuleRecordList, allocator);
|
|
|
+ exportStarSet = Anew(allocator, ExportModuleRecordList, allocator);
|
|
|
}
|
|
|
if (resolveSet == nullptr)
|
|
|
{
|
|
|
- resolveSet = (ResolveSet*)AllocatorNew(ArenaAllocator, allocator, ResolveSet, allocator);
|
|
|
+ resolveSet = Anew(allocator, ResolveSet, allocator);
|
|
|
}
|
|
|
|
|
|
*exportRecord = nullptr;
|
|
|
@@ -586,7 +655,7 @@ namespace Js
|
|
|
void SourceTextModuleRecord::SetParent(SourceTextModuleRecord* parentRecord, LPCOLESTR moduleName)
|
|
|
{
|
|
|
Assert(parentRecord != nullptr);
|
|
|
- Assert(parentRecord->childrenModuleSet != nullptr);
|
|
|
+ parentRecord->EnsureChildModuleSet(GetScriptContext());
|
|
|
if (!parentRecord->childrenModuleSet->ContainsKey(moduleName))
|
|
|
{
|
|
|
parentRecord->childrenModuleSet->AddNew(moduleName, this);
|
|
|
@@ -609,6 +678,15 @@ namespace Js
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ void SourceTextModuleRecord::EnsureChildModuleSet(ScriptContext *scriptContext)
|
|
|
+ {
|
|
|
+ if (nullptr == this->childrenModuleSet)
|
|
|
+ {
|
|
|
+ ArenaAllocator* allocator = scriptContext->GeneralAllocator();
|
|
|
+ this->childrenModuleSet = Anew(allocator, ChildModuleRecordSet, allocator);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
HRESULT SourceTextModuleRecord::ResolveExternalModuleDependencies()
|
|
|
{
|
|
|
OUTPUT_TRACE_DEBUGONLY(Js::ModulePhase, _u("ResolveExternalModuleDependencies(%s)\n"), this->GetSpecifierSz());
|
|
|
@@ -618,11 +696,7 @@ namespace Js
|
|
|
HRESULT hr = NOERROR;
|
|
|
if (requestedModuleList != nullptr)
|
|
|
{
|
|
|
- if (nullptr == childrenModuleSet)
|
|
|
- {
|
|
|
- ArenaAllocator* allocator = scriptContext->GeneralAllocator();
|
|
|
- childrenModuleSet = (ChildModuleRecordSet*)AllocatorNew(ArenaAllocator, allocator, ChildModuleRecordSet, allocator);
|
|
|
- }
|
|
|
+ EnsureChildModuleSet(scriptContext);
|
|
|
requestedModuleList->MapUntil([&](IdentPtr specifier) {
|
|
|
ModuleRecordBase* moduleRecordBase = nullptr;
|
|
|
SourceTextModuleRecord* moduleRecord = nullptr;
|
|
|
@@ -739,19 +813,23 @@ namespace Js
|
|
|
{
|
|
|
return nullptr;
|
|
|
}
|
|
|
- Assert(this->errorObject == nullptr);
|
|
|
+
|
|
|
if (this->errorObject != nullptr)
|
|
|
{
|
|
|
- JavascriptExceptionOperators::Throw(errorObject, scriptContext);
|
|
|
+ if (this->promise != nullptr)
|
|
|
+ {
|
|
|
+ SourceTextModuleRecord::ResolveOrRejectDynamicImportPromise(false, this->errorObject, this->scriptContext, this);
|
|
|
+ return scriptContext->GetLibrary()->GetUndefined();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ JavascriptExceptionOperators::Throw(errorObject, this->scriptContext);
|
|
|
+ }
|
|
|
}
|
|
|
- Assert(!WasEvaluated());
|
|
|
+
|
|
|
+ Assert(this->errorObject == nullptr);
|
|
|
SetWasEvaluated();
|
|
|
- // we shouldn't evaluate if there are existing failure. This is defense in depth as the host shouldn't
|
|
|
- // call into evaluation if there was previous failure on the module.
|
|
|
- if (this->errorObject)
|
|
|
- {
|
|
|
- return this->errorObject;
|
|
|
- }
|
|
|
+
|
|
|
if (childrenModuleSet != nullptr)
|
|
|
{
|
|
|
childrenModuleSet->EachValue([=](SourceTextModuleRecord* childModuleRecord)
|
|
|
@@ -765,7 +843,41 @@ namespace Js
|
|
|
CleanupBeforeExecution();
|
|
|
|
|
|
Arguments outArgs(CallInfo(CallFlags_Value, 0), nullptr);
|
|
|
- return rootFunction->CallRootFunction(outArgs, scriptContext, true);
|
|
|
+
|
|
|
+ Var ret = nullptr;
|
|
|
+ JavascriptExceptionObject *exception = nullptr;
|
|
|
+ try
|
|
|
+ {
|
|
|
+ AUTO_NESTED_HANDLED_EXCEPTION_TYPE((ExceptionType)(ExceptionType_OutOfMemory | ExceptionType_JavascriptException));
|
|
|
+ ENTER_SCRIPT_IF(scriptContext, true, false, false, !scriptContext->GetThreadContext()->IsScriptActive(),
|
|
|
+ {
|
|
|
+ ret = rootFunction->CallRootFunction(outArgs, scriptContext, true);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ catch (const Js::JavascriptException &err)
|
|
|
+ {
|
|
|
+ exception = err.GetAndClear();
|
|
|
+ Var errorObject = exception->GetThrownObject(scriptContext);
|
|
|
+ AssertOrFailFastMsg(errorObject != nullptr, "ModuleEvaluation: null error object thrown from root function");
|
|
|
+ this->errorObject = errorObject;
|
|
|
+ if (this->promise != nullptr)
|
|
|
+ {
|
|
|
+ ResolveOrRejectDynamicImportPromise(false, errorObject, scriptContext, this);
|
|
|
+ return scriptContext->GetLibrary()->GetUndefined();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (exception != nullptr)
|
|
|
+ {
|
|
|
+ JavascriptExceptionOperators::DoThrowCheckClone(exception, scriptContext);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this->promise != nullptr)
|
|
|
+ {
|
|
|
+ SourceTextModuleRecord::ResolveOrRejectDynamicImportPromise(true, this->GetNamespace(), this->GetScriptContext(), this);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
HRESULT SourceTextModuleRecord::OnHostException(void* errorVar)
|
|
|
@@ -836,9 +948,9 @@ namespace Js
|
|
|
if (localExportRecordList != nullptr)
|
|
|
{
|
|
|
ArenaAllocator* allocator = scriptContext->GeneralAllocator();
|
|
|
- localExportMapByExportName = AllocatorNew(ArenaAllocator, allocator, LocalExportMap, allocator);
|
|
|
- localExportMapByLocalName = AllocatorNew(ArenaAllocator, allocator, LocalExportMap, allocator);
|
|
|
- localExportIndexList = AllocatorNew(ArenaAllocator, allocator, LocalExportIndexList, allocator);
|
|
|
+ localExportMapByExportName = Anew(allocator, LocalExportMap, allocator);
|
|
|
+ localExportMapByLocalName = Anew(allocator, LocalExportMap, allocator);
|
|
|
+ localExportIndexList = Anew(allocator, LocalExportIndexList, allocator);
|
|
|
localExportRecordList->Map([&](ModuleImportOrExportEntry exportEntry)
|
|
|
{
|
|
|
Assert(exportEntry.moduleRequest == nullptr);
|
|
|
@@ -983,4 +1095,39 @@ namespace Js
|
|
|
}
|
|
|
return slotIndex;
|
|
|
}
|
|
|
+
|
|
|
+ // static
|
|
|
+ Var SourceTextModuleRecord::ResolveOrRejectDynamicImportPromise(bool isResolve, Var value, ScriptContext *scriptContext, SourceTextModuleRecord *moduleRecord)
|
|
|
+ {
|
|
|
+ bool isScriptActive = scriptContext->GetThreadContext()->IsScriptActive();
|
|
|
+ JavascriptPromise *promise = nullptr;
|
|
|
+ if (moduleRecord != nullptr)
|
|
|
+ {
|
|
|
+ promise = moduleRecord->GetPromise();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (promise == nullptr)
|
|
|
+ {
|
|
|
+ promise = JavascriptPromise::CreateEnginePromise(scriptContext);
|
|
|
+ }
|
|
|
+
|
|
|
+ ENTER_SCRIPT_IF(scriptContext, true, false, false, !isScriptActive,
|
|
|
+ {
|
|
|
+ if (isResolve)
|
|
|
+ {
|
|
|
+ promise->Resolve(value, scriptContext);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ promise->Reject(value, scriptContext);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (moduleRecord != nullptr)
|
|
|
+ {
|
|
|
+ moduleRecord->SetPromise(nullptr);
|
|
|
+ }
|
|
|
+
|
|
|
+ return promise;
|
|
|
+ }
|
|
|
}
|