Skip to content

Commit

Permalink
Implement Top Level Await
Browse files Browse the repository at this point in the history
  • Loading branch information
rhuanjl committed Dec 6, 2020
1 parent 7d4bdd8 commit 8c3a82b
Show file tree
Hide file tree
Showing 23 changed files with 761 additions and 175 deletions.
90 changes: 28 additions & 62 deletions bin/ch/WScriptJsrt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -600,30 +600,6 @@ JsValueRef WScriptJsrt::LoadScriptHelper(JsValueRef callee, bool isConstructCall
return returnValue;
}

JsErrorCode WScriptJsrt::InitializeModuleInfo(JsModuleRecord moduleRecord)
{
JsErrorCode errorCode = JsNoError;
errorCode = ChakraRTInterface::JsSetModuleHostInfo(moduleRecord, JsModuleHostInfo_FetchImportedModuleCallback, (void*)WScriptJsrt::FetchImportedModule);

if (errorCode == JsNoError)
{
errorCode = ChakraRTInterface::JsSetModuleHostInfo(moduleRecord, JsModuleHostInfo_FetchImportedModuleFromScriptCallback, (void*)WScriptJsrt::FetchImportedModuleFromScript);

if (errorCode == JsNoError)
{
errorCode = ChakraRTInterface::JsSetModuleHostInfo(moduleRecord, JsModuleHostInfo_NotifyModuleReadyCallback, (void*)WScriptJsrt::NotifyModuleReadyCallback);

if (errorCode == JsNoError)
{
errorCode = ChakraRTInterface::JsSetModuleHostInfo(moduleRecord, JsModuleHostInfo_InitializeImportMetaCallback, (void*)WScriptJsrt::InitializeImportMetaCallback);
}
}
}

IfJsrtErrorFailLogAndRetErrorCode(errorCode);
return JsNoError;
}

void WScriptJsrt::GetDir(LPCSTR fullPathNarrow, std::string *fullDirNarrow)
{
char fileDrive[_MAX_DRIVE];
Expand Down Expand Up @@ -668,10 +644,6 @@ JsErrorCode WScriptJsrt::LoadModuleFromString(LPCSTR fileName, LPCSTR fileConten
nullptr, specifier, &requestModule);
}
if (errorCode == JsNoError)
{
errorCode = InitializeModuleInfo(requestModule);
}
if (errorCode == JsNoError)
{
if (fullName)
{
Expand Down Expand Up @@ -1220,7 +1192,11 @@ bool WScriptJsrt::Initialize()
IfJsrtErrorFail(CreatePropertyIdFromString("console", &consoleName), false);
IfJsrtErrorFail(ChakraRTInterface::JsSetProperty(global, consoleName, console, true), false);

IfJsrtErrorFail(InitializeModuleInfo(nullptr), false);
IfJsrtErrorFail(ChakraRTInterface::JsSetModuleHostInfo(nullptr, JsModuleHostInfo_FetchImportedModuleCallback, (void*)WScriptJsrt::FetchImportedModule), false);
IfJsrtErrorFail(ChakraRTInterface::JsSetModuleHostInfo(nullptr, JsModuleHostInfo_FetchImportedModuleFromScriptCallback, (void*)WScriptJsrt::FetchImportedModuleFromScript), false);
IfJsrtErrorFail(ChakraRTInterface::JsSetModuleHostInfo(nullptr, JsModuleHostInfo_NotifyModuleReadyCallback, (void*)WScriptJsrt::NotifyModuleReadyCallback), false);
IfJsrtErrorFail(ChakraRTInterface::JsSetModuleHostInfo(nullptr, JsModuleHostInfo_InitializeImportMetaCallback, (void*)WScriptJsrt::InitializeImportMetaCallback), false);
IfJsrtErrorFail(ChakraRTInterface::JsSetModuleHostInfo(nullptr, JsModuleHostInfo_ReportModuleCompletionCallback, (void*)WScriptJsrt::ReportModuleCompletionCallback), false);

// When the command-line argument `-Test262` is set,
// WScript will have the extra support API below and $262 will be
Expand Down Expand Up @@ -1787,11 +1763,15 @@ JsValueRef __stdcall WScriptJsrt::GetProxyPropertiesCallback(JsValueRef callee,
return returnValue;
}

bool WScriptJsrt::PrintException(LPCSTR fileName, JsErrorCode jsErrorCode)
bool WScriptJsrt::PrintException(LPCSTR fileName, JsErrorCode jsErrorCode, JsValueRef exception)
{
LPCWSTR errorTypeString = ConvertErrorCodeToMessage(jsErrorCode);
JsValueRef exception;
ChakraRTInterface::JsGetAndClearException(&exception);

if (exception == nullptr)
{
ChakraRTInterface::JsGetAndClearException(&exception);
}

if (HostConfigFlags::flags.MuteHostErrorMsgIsEnabled)
{
return false;
Expand Down Expand Up @@ -1992,21 +1972,7 @@ HRESULT WScriptJsrt::ModuleMessage::Call(LPCSTR fileName)
errorCode = ChakraRTInterface::JsModuleEvaluation(moduleRecord, &result);
if (errorCode != JsNoError)
{
if (moduleErrMap[moduleRecord] == RootModule)
{
PrintException(fileName, errorCode);
}
else
{
bool hasException = false;
ChakraRTInterface::JsHasException(&hasException);
if (hasException)
{
JsValueRef exception;
ChakraRTInterface::JsGetAndClearException(&exception);
exception; //unusued
}
}
PrintException(fileName, errorCode); // this should not be called
}
}
}
Expand Down Expand Up @@ -2039,6 +2005,17 @@ HRESULT WScriptJsrt::ModuleMessage::Call(LPCSTR fileName)
return errorCode;
}

JsErrorCode WScriptJsrt::ReportModuleCompletionCallback(JsModuleRecord module, JsValueRef exception)
{
if (exception != nullptr)
{
JsValueRef specifier = JS_INVALID_REFERENCE;
ChakraRTInterface::JsGetModuleHostInfo(module, JsModuleHostInfo_Url, &specifier);
PrintException(AutoString(specifier).GetString(), JsErrorCode::JsErrorScriptException, exception);
}
return JsNoError;
}

JsErrorCode WScriptJsrt::FetchImportedModuleHelper(JsModuleRecord referencingModule,
JsValueRef specifier, __out JsModuleRecord* dependentModuleRecord, LPCSTR refdir)
{
Expand Down Expand Up @@ -2070,7 +2047,6 @@ JsErrorCode WScriptJsrt::FetchImportedModuleHelper(JsModuleRecord referencingMod
if (errorCode == JsNoError)
{
GetDir(fullPath, &moduleDirMap[moduleRecord]);
InitializeModuleInfo(moduleRecord);
std::string pathKey = std::string(fullPath);
moduleRecordMap[pathKey] = moduleRecord;
moduleErrMap[moduleRecord] = ImportedModule;
Expand Down Expand Up @@ -2112,32 +2088,22 @@ JsErrorCode WScriptJsrt::FetchImportedModuleFromScript(_In_ JsSourceContext dwRe
return FetchImportedModuleHelper(nullptr, specifier, dependentModuleRecord);
}

// Callback from chakraCore when the module resolution is finished, either successfuly or unsuccessfully.
// Callback from chakraCore when the module resolution is finished, either successfully or unsuccessfully.
JsErrorCode WScriptJsrt::NotifyModuleReadyCallback(_In_opt_ JsModuleRecord referencingModule, _In_opt_ JsValueRef exceptionVar)
{
if (exceptionVar != nullptr)
if (exceptionVar != nullptr && HostConfigFlags::flags.TraceHostCallbackIsEnabled)
{
ChakraRTInterface::JsSetException(exceptionVar);
JsValueRef specifier = JS_INVALID_REFERENCE;
ChakraRTInterface::JsGetModuleHostInfo(referencingModule, JsModuleHostInfo_Url, &specifier);
AutoString fileName;
if (specifier != JS_INVALID_REFERENCE)
{
fileName.Initialize(specifier);
}

if (HostConfigFlags::flags.TraceHostCallbackIsEnabled)
{
wprintf(_u("NotifyModuleReadyCallback(exception) %S\n"), fileName.GetString());
}

// No need to print - just consume the exception
JsValueRef exception;
ChakraRTInterface::JsGetAndClearException(&exception);
exception; // unused
wprintf(_u("NotifyModuleReadyCallback(exception) %S\n"), fileName.GetString());
}

if (exceptionVar != nullptr || moduleErrMap[referencingModule] != ErroredModule)
if (moduleErrMap[referencingModule] != ErroredModule)
{
WScriptJsrt::ModuleMessage* moduleMessage =
WScriptJsrt::ModuleMessage::Create(referencingModule, nullptr);
Expand Down
4 changes: 2 additions & 2 deletions bin/ch/WScriptJsrt.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class WScriptJsrt
static JsErrorCode FetchImportedModule(_In_ JsModuleRecord referencingModule, _In_ JsValueRef specifier, _Outptr_result_maybenull_ JsModuleRecord* dependentModuleRecord);
static JsErrorCode FetchImportedModuleFromScript(_In_ DWORD_PTR dwReferencingSourceContext, _In_ JsValueRef specifier, _Outptr_result_maybenull_ JsModuleRecord* dependentModuleRecord);
static JsErrorCode NotifyModuleReadyCallback(_In_opt_ JsModuleRecord referencingModule, _In_opt_ JsValueRef exceptionVar);
static JsErrorCode ReportModuleCompletionCallback(JsModuleRecord module, JsValueRef exception);
static JsErrorCode CALLBACK InitializeImportMetaCallback(_In_opt_ JsModuleRecord referencingModule, _In_opt_ JsValueRef importMetaVar);
static void CALLBACK PromiseContinuationCallback(JsValueRef task, void *callbackState);
static void CALLBACK PromiseRejectionTrackerCallback(JsValueRef promise, JsValueRef reason, bool handled, void *callbackState);
Expand Down Expand Up @@ -101,7 +102,7 @@ class WScriptJsrt
static void CALLBACK JsContextBeforeCollectCallback(JsRef contextRef, void *data);
#endif

static bool PrintException(LPCSTR fileName, JsErrorCode jsErrorCode);
static bool PrintException(LPCSTR fileName, JsErrorCode jsErrorCode, JsValueRef exception = nullptr);
static JsValueRef LoadScript(JsValueRef callee, LPCSTR fileName, LPCSTR fileContent, LPCSTR scriptInjectType, bool isSourceModule, JsFinalizeCallback finalizeCallback, bool isFile);
static DWORD_PTR GetNextSourceContext();
static JsValueRef LoadScriptFileHelper(JsValueRef callee, JsValueRef *arguments, unsigned short argumentCount, bool isSourceModule);
Expand Down Expand Up @@ -129,7 +130,6 @@ class WScriptJsrt

static JsValueRef CALLBACK EmptyCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
static JsErrorCode CALLBACK LoadModuleFromString(LPCSTR fileName, LPCSTR fileContent, LPCSTR fullName = nullptr, bool isFile = false);
static JsErrorCode CALLBACK InitializeModuleInfo(JsModuleRecord moduleRecord);

static JsValueRef CALLBACK LoadBinaryFileCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
static JsValueRef CALLBACK LoadTextFileCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
Expand Down
2 changes: 2 additions & 0 deletions lib/Common/ConfigFlagsList.h
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,7 @@ PHASE(All)
#define DEFAULT_CONFIG_ESImportMeta (true)
#define DEFAULT_CONFIG_ESExportNsAs (true)
#define DEFAULT_CONFIG_ES2018AsyncIteration (true)
#define DEFAULT_CONFIG_ESTopLevelAwait (true)

#define DEFAULT_CONFIG_ESSharedArrayBuffer (false)

Expand Down Expand Up @@ -1159,6 +1160,7 @@ FLAGPR (Boolean, ES6, ES6RegExSticky , "Enable ES6 RegEx stick
FLAGPR (Boolean, ES6, ES2018RegExDotAll , "Enable ES2018 RegEx dotAll flag" , DEFAULT_CONFIG_ES2018RegExDotAll)
FLAGPR (Boolean, ES6, ESExportNsAs , "Enable ES experimental export * as name" , DEFAULT_CONFIG_ESExportNsAs)
FLAGPR (Boolean, ES6, ES2018AsyncIteration , "Enable ES2018 Async Iteration" , DEFAULT_CONFIG_ES2018AsyncIteration)
FLAGPR (Boolean, ES6, ESTopLevelAwait , "Enable Top Level Await in modules" , DEFAULT_CONFIG_ESTopLevelAwait)

#ifndef COMPILE_DISABLE_ES6RegExPrototypeProperties
#define COMPILE_DISABLE_ES6RegExPrototypeProperties 0
Expand Down
27 changes: 26 additions & 1 deletion lib/Jsrt/ChakraCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ typedef enum JsModuleHostInfoKind
/// <summary>
/// Callback to allow host to initialize import.meta object properties.
/// </summary>
JsModuleHostInfo_InitializeImportMetaCallback = 0x7
JsModuleHostInfo_InitializeImportMetaCallback = 0x7,
/// <summary>
/// Callback to report module completion or exception thrown when evaluating a module.
/// </summary>
JsModuleHostInfo_ReportModuleCompletionCallback = 0x8
} JsModuleHostInfoKind;

/// <summary>
Expand Down Expand Up @@ -207,6 +211,27 @@ typedef JsErrorCode(CHAKRA_CALLBACK * NotifyModuleReadyCallback)(_In_opt_ JsModu
/// </returns>
typedef JsErrorCode(CHAKRA_CALLBACK * InitializeImportMetaCallback)(_In_opt_ JsModuleRecord referencingModule, _In_opt_ JsValueRef importMetaVar);

/// <summary>
/// User implemented callback to report completion of module execution.
/// </summary>
/// <remarks>
/// This callback is used to report the completion of module execution and to report any runtime exceptions.
/// Note it is not used for dynamicly imported modules import() as the reuslt from those are handled with a
/// promise.
/// If this callback is not set and a module produces an exception:
/// a) a purely synchronous module tree with an exception will set the exception on the runtime
/// (this is not done if this callback is set)
/// b) an exception in an asynchronous module tree will not be reported directly.
///
/// However in all cases the exception will be set on the JsModuleRecord.
/// </remarks>
/// <param name="module">The root module that has completed either with an exception or normally.</param>
/// <param name="exception">The exception object which was thrown or nullptr if the module had a normal completion.</param>
/// <returns>
/// Returns a JsErrorCode: JsNoError if successful.
/// </returns>
typedef JsErrorCode(CHAKRA_CALLBACK * ReportModuleCompletionCallback)(_In_ JsModuleRecord module, _In_opt_ JsValueRef exception);

/// <summary>
/// A structure containing information about a native function callback.
/// </summary>
Expand Down
17 changes: 17 additions & 0 deletions lib/Jsrt/Core/JsrtContextCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,23 @@ HRESULT ChakraCoreHostScriptContext::InitializeImportMeta(Js::ModuleRecordBase*
return E_INVALIDARG;
}

bool ChakraCoreHostScriptContext::ReportModuleCompletion(Js::ModuleRecordBase* module, Js::Var exception)
{
if (reportModuleCompletionCallback == nullptr)
{
return false;
}
{
AUTO_NO_EXCEPTION_REGION;
JsErrorCode errorCode = reportModuleCompletionCallback(module, exception);
if (errorCode == JsNoError)
{
return true;
}
}
return false;
}

ChakraCoreStreamWriter::~ChakraCoreStreamWriter()
{
HeapDelete(m_serializerCore);
Expand Down
10 changes: 8 additions & 2 deletions lib/Jsrt/Core/JsrtContextCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ class ChakraCoreHostScriptContext sealed : public HostScriptContext
fetchImportedModuleCallback(nullptr),
fetchImportedModuleFromScriptCallback(nullptr),
notifyModuleReadyCallback(nullptr),
initializeImportMetaCallback(nullptr)
initializeImportMetaCallback(nullptr),
reportModuleCompletionCallback(nullptr)
{
}
~ChakraCoreHostScriptContext()
Expand Down Expand Up @@ -253,6 +254,7 @@ class ChakraCoreHostScriptContext sealed : public HostScriptContext
HRESULT NotifyHostAboutModuleReady(Js::ModuleRecordBase* referencingModule, Js::Var exceptionVar) override;

HRESULT InitializeImportMeta(Js::ModuleRecordBase* referencingModule, Js::Var importMetaObject) override;
bool ReportModuleCompletion(Js::ModuleRecordBase* module, Js::Var exception) override;

void SetNotifyModuleReadyCallback(NotifyModuleReadyCallback notifyCallback) { this->notifyModuleReadyCallback = notifyCallback; }
NotifyModuleReadyCallback GetNotifyModuleReadyCallback() const { return this->notifyModuleReadyCallback; }
Expand All @@ -263,9 +265,12 @@ class ChakraCoreHostScriptContext sealed : public HostScriptContext
void SetFetchImportedModuleFromScriptCallback(FetchImportedModuleFromScriptCallBack fetchCallback) { this->fetchImportedModuleFromScriptCallback = fetchCallback; }
FetchImportedModuleFromScriptCallBack GetFetchImportedModuleFromScriptCallback() const { return this->fetchImportedModuleFromScriptCallback; }

void SetInitializeImportMetaCallback(InitializeImportMetaCallback finalizeCallback) { this->initializeImportMetaCallback = finalizeCallback; }
void SetInitializeImportMetaCallback(InitializeImportMetaCallback initializeCallback) { this->initializeImportMetaCallback = initializeCallback; }
InitializeImportMetaCallback GetInitializeImportMetaCallback() const { return this->initializeImportMetaCallback; }

void SetReportModuleCompletionCallback(ReportModuleCompletionCallback processCallback) { this->reportModuleCompletionCallback = processCallback; }
ReportModuleCompletionCallback GetReportModuleCompletionCallback() const { return this->reportModuleCompletionCallback; }

#if DBG_DUMP || defined(PROFILE_EXEC) || defined(PROFILE_MEM)
void EnsureParentInfo(Js::ScriptContext* scriptContext = NULL) override
{
Expand All @@ -281,4 +286,5 @@ class ChakraCoreHostScriptContext sealed : public HostScriptContext
FetchImportedModuleFromScriptCallBack fetchImportedModuleFromScriptCallback;
NotifyModuleReadyCallback notifyModuleReadyCallback;
InitializeImportMetaCallback initializeImportMetaCallback;
ReportModuleCompletionCallback reportModuleCompletionCallback;
};
13 changes: 9 additions & 4 deletions lib/Jsrt/Core/JsrtCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,9 @@ JsSetModuleHostInfo(
Js::SourceTextModuleRecord* moduleRecord;
if (!Js::SourceTextModuleRecord::Is(requestModule))
{
if (moduleHostInfo != JsModuleHostInfo_FetchImportedModuleCallback &&
moduleHostInfo != JsModuleHostInfo_FetchImportedModuleFromScriptCallback &&
moduleHostInfo != JsModuleHostInfo_NotifyModuleReadyCallback &&
moduleHostInfo != JsModuleHostInfo_InitializeImportMetaCallback)
if (moduleHostInfo == JsModuleHostInfo_Exception ||
moduleHostInfo == JsModuleHostInfo_HostDefined ||
moduleHostInfo == JsModuleHostInfo_Url)
{
return JsErrorInvalidArgument;
}
Expand Down Expand Up @@ -188,6 +187,9 @@ JsSetModuleHostInfo(
case JsModuleHostInfo_InitializeImportMetaCallback:
currentContext->GetHostScriptContext()->SetInitializeImportMetaCallback(reinterpret_cast<InitializeImportMetaCallback>(hostInfo));
break;
case JsModuleHostInfo_ReportModuleCompletionCallback:
currentContext->GetHostScriptContext()->SetReportModuleCompletionCallback(reinterpret_cast<ReportModuleCompletionCallback>(hostInfo));
break;
case JsModuleHostInfo_Url:
moduleRecord->SetSpecifier(hostInfo);
break;
Expand Down Expand Up @@ -238,6 +240,9 @@ JsGetModuleHostInfo(
case JsModuleHostInfo_InitializeImportMetaCallback:
*hostInfo = reinterpret_cast<void*>(currentContext->GetHostScriptContext()->GetInitializeImportMetaCallback());
break;
case JsModuleHostInfo_ReportModuleCompletionCallback:
*hostInfo = reinterpret_cast<void*>(currentContext->GetHostScriptContext()->GetReportModuleCompletionCallback());
break;
case JsModuleHostInfo_Url:
*hostInfo = reinterpret_cast<void*>(moduleRecord->GetSpecifier());
break;
Expand Down
29 changes: 27 additions & 2 deletions lib/Parser/Parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2727,6 +2727,17 @@ bool Parser::IsTopLevelModuleFunc()
return curFunc->nop == knopFncDecl && curFunc->IsModule();
}

void Parser::MakeModuleAsync()
{
Assert(IsTopLevelModuleFunc());
if (!m_scriptContext->GetConfig()->IsESTopLevelAwaitEnabled())
{
Error(ERRExperimental);
}
ParseNodeFnc * curFunc = GetCurrentFunctionNode();
curFunc->SetIsAsync();
}

template<bool buildAST> ParseNodePtr Parser::ParseImportCall()
{
this->GetScanner()->Scan();
Expand Down Expand Up @@ -8873,7 +8884,14 @@ ParseNodePtr Parser::ParseExpr(int oplMin,
// is not a grammar production outside of async functions.
//
// Further, await expressions are disallowed within parameter scopes.
Error(ERRBadAwait);
if (IsTopLevelModuleFunc())
{
MakeModuleAsync();
}
else
{
Error(ERRBadAwait);
}
}
}

Expand Down Expand Up @@ -10282,7 +10300,14 @@ ParseNodePtr Parser::ParseStatement()
{
if (!this->GetScanner()->AwaitIsKeywordRegion())
{
Error(ERRBadAwait); // for await () in a non-async function
if (IsTopLevelModuleFunc())
{
MakeModuleAsync();
}
else
{
Error(ERRBadAwait); // for await () in a non-async function
}
}
if (!m_scriptContext->GetConfig()->IsES2018AsyncIterationEnabled())
{
Expand Down
Loading

0 comments on commit 8c3a82b

Please sign in to comment.