Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic Module Import #2913

Merged
merged 1 commit into from
May 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions bin/NativeTests/JsRTApiTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1739,6 +1739,7 @@ namespace JsRTApiTest
REQUIRE(JsInitializeModuleRecord(nullptr, specifier, &requestModule) == JsNoError);
successTest.mainModule = requestModule;
REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_FetchImportedModuleCallback, Success_FIMC) == JsNoError);
REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_FetchImportedModuleFromScriptCallback, Success_FIMC) == JsNoError);
REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_NotifyModuleReadyCallback, Succes_NMRC) == JsNoError);

JsValueRef errorObject = JS_INVALID_REFERENCE;
Expand Down Expand Up @@ -1834,6 +1835,7 @@ namespace JsRTApiTest
REQUIRE(JsInitializeModuleRecord(nullptr, specifier, &requestModule) == JsNoError);
reentrantParseData.mainModule = requestModule;
REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_FetchImportedModuleCallback, ReentrantParse_FIMC) == JsNoError);
REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_FetchImportedModuleFromScriptCallback, ReentrantParse_FIMC) == JsNoError);
REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_NotifyModuleReadyCallback, ReentrantParse_NMRC) == JsNoError);

JsValueRef errorObject = JS_INVALID_REFERENCE;
Expand Down Expand Up @@ -1913,6 +1915,7 @@ namespace JsRTApiTest
REQUIRE(JsInitializeModuleRecord(nullptr, specifier, &requestModule) == JsNoError);
reentrantNoErrorParseData.mainModule = requestModule;
REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_FetchImportedModuleCallback, reentrantNoErrorParse_FIMC) == JsNoError);
REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_FetchImportedModuleFromScriptCallback, reentrantNoErrorParse_FIMC) == JsNoError);
REQUIRE(JsSetModuleHostInfo(requestModule, JsModuleHostInfo_NotifyModuleReadyCallback, reentrantNoErrorParse_NMRC) == JsNoError);

JsValueRef errorObject = JS_INVALID_REFERENCE;
Expand Down
38 changes: 21 additions & 17 deletions bin/ch/Helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,26 +139,30 @@ HRESULT Helpers::LoadScriptFromFile(LPCSTR filename, LPCSTR& contents, UINT* len
//
if (fopen_s(&file, filename, "rb") != 0)
{
#ifdef _WIN32
DWORD lastError = GetLastError();
char16 wszBuff[512];
fprintf(stderr, "Error in opening file '%s' ", filename);
wszBuff[0] = 0;
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
nullptr,
lastError,
0,
wszBuff,
_countof(wszBuff),
nullptr))
if (!HostConfigFlags::flags.AsyncModuleLoadIsEnabled)
{
fwprintf(stderr, _u(": %s"), wszBuff);
}
fwprintf(stderr, _u("\n"));
#ifdef _WIN32
DWORD lastError = GetLastError();
char16 wszBuff[512];
fprintf(stderr, "Error in opening file '%s' ", filename);
wszBuff[0] = 0;
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
nullptr,
lastError,
0,
wszBuff,
_countof(wszBuff),
nullptr))
{
fwprintf(stderr, _u(": %s"), wszBuff);
}
fwprintf(stderr, _u("\n"));
#elif defined(_POSIX_VERSION)
fprintf(stderr, "Error in opening file: ");
perror(filename);
fprintf(stderr, "Error in opening file: ");
perror(filename);
#endif
}

IfFailGo(E_FAIL);
}

Expand Down
1 change: 1 addition & 0 deletions bin/ch/HostConfigFlagsList.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ FLAG(int, InspectMaxStringLength, "Max string length to dump in locals
FLAG(BSTR, Serialized, "If source is UTF8, deserializes from bytecode file", NULL)
FLAG(bool, OOPJIT, "Run JIT in a separate process", false)
FLAG(bool, EnsureCloseJITServer, "JIT process will be force closed when ch is terminated", true)
FLAG(bool, AsyncModuleLoad, "Silence host error output for module load failures to enable promise testing", false)
#undef FLAG
#endif
73 changes: 59 additions & 14 deletions bin/ch/WScriptJsrt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -304,14 +304,22 @@ JsErrorCode WScriptJsrt::InitializeModuleInfo(JsValueRef specifier, JsModuleReco
{
JsErrorCode errorCode = JsNoError;
errorCode = ChakraRTInterface::JsSetModuleHostInfo(moduleRecord, JsModuleHostInfo_FetchImportedModuleCallback, (void*)WScriptJsrt::FetchImportedModule);

if (errorCode == JsNoError)
{
errorCode = ChakraRTInterface::JsSetModuleHostInfo(moduleRecord, JsModuleHostInfo_NotifyModuleReadyCallback, (void*)WScriptJsrt::NotifyModuleReadyCallback);
}
if (errorCode == JsNoError)
{
errorCode = ChakraRTInterface::JsSetModuleHostInfo(moduleRecord, JsModuleHostInfo_HostDefined, specifier);
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_HostDefined, specifier);
}
}
}

IfJsrtErrorFailLogAndRetErrorCode(errorCode);
return JsNoError;
}
Expand Down Expand Up @@ -351,9 +359,10 @@ JsErrorCode WScriptJsrt::LoadModuleFromString(LPCSTR fileName, LPCSTR fileConten
JsValueRef errorObject = JS_INVALID_REFERENCE;

// ParseModuleSource is sync, while additional fetch & evaluation are async.
unsigned int fileContentLength = (fileContent == nullptr) ? 0 : (unsigned int)strlen(fileContent);
errorCode = ChakraRTInterface::JsParseModuleSource(requestModule, dwSourceCookie, (LPBYTE)fileContent,
(unsigned int)strlen(fileContent), JsParseModuleSourceFlags_DataIsUTF8, &errorObject);
if ((errorCode != JsNoError) && errorObject != JS_INVALID_REFERENCE)
fileContentLength, JsParseModuleSourceFlags_DataIsUTF8, &errorObject);
if ((errorCode != JsNoError) && errorObject != JS_INVALID_REFERENCE && fileContent != nullptr)
{
ChakraRTInterface::JsSetException(errorObject);
return errorCode;
Expand Down Expand Up @@ -875,10 +884,24 @@ bool WScriptJsrt::Initialize()
IfJsrtErrorFail(CreatePropertyIdFromString("console", &consoleName), false);
IfJsrtErrorFail(ChakraRTInterface::JsSetProperty(global, consoleName, console, true), false);

IfJsrtErrorFail(InitializeModuleCallbacks(), false);

Error:
return hr == S_OK;
}

JsErrorCode WScriptJsrt::InitializeModuleCallbacks()
{
JsModuleRecord moduleRecord = JS_INVALID_REFERENCE;
JsErrorCode errorCode = ChakraRTInterface::JsInitializeModuleRecord(nullptr, nullptr, &moduleRecord);
if (errorCode == JsNoError)
{
errorCode = InitializeModuleInfo(nullptr, moduleRecord);
}

return errorCode;
}

bool WScriptJsrt::Uninitialize()
{
// moduleRecordMap is a global std::map, its destructor may access overrided
Expand Down Expand Up @@ -1217,7 +1240,12 @@ HRESULT WScriptJsrt::ModuleMessage::Call(LPCSTR fileName)
hr = Helpers::LoadScriptFromFile(specifierStr.GetString(), fileContent);
if (FAILED(hr))
{
fprintf(stderr, "Couldn't load file.\n");
if (!HostConfigFlags::flags.AsyncModuleLoadIsEnabled)
{
fprintf(stderr, "Couldn't load file.\n");
}

LoadScript(nullptr, specifierStr.GetString(), nullptr, "module", true, WScriptJsrt::FinalizeFree);
}
else
{
Expand All @@ -1228,12 +1256,8 @@ HRESULT WScriptJsrt::ModuleMessage::Call(LPCSTR fileName)
return errorCode;
}

// Callback from chakracore to fetch dependent module. In the test harness,
// we are not doing any translation, just treat the specifier as fileName.
// While this call will come back directly from ParseModuleSource, the additional
// task are treated as Promise that will be executed later.
JsErrorCode WScriptJsrt::FetchImportedModule(_In_ JsModuleRecord referencingModule,
_In_ JsValueRef specifier, _Outptr_result_maybenull_ JsModuleRecord* dependentModuleRecord)
JsErrorCode WScriptJsrt::FetchImportedModuleHelper(JsModuleRecord referencingModule,
JsValueRef specifier, __out JsModuleRecord* dependentModuleRecord)
{
JsModuleRecord moduleRecord = JS_INVALID_REFERENCE;
AutoString specifierStr;
Expand Down Expand Up @@ -1267,6 +1291,27 @@ JsErrorCode WScriptJsrt::FetchImportedModule(_In_ JsModuleRecord referencingModu
return errorCode;
}

// Callback from chakracore to fetch dependent module. In the test harness,
// we are not doing any translation, just treat the specifier as fileName.
// While this call will come back directly from ParseModuleSource, the additional
// task are treated as Promise that will be executed later.
JsErrorCode WScriptJsrt::FetchImportedModule(_In_ JsModuleRecord referencingModule,
_In_ JsValueRef specifier, _Outptr_result_maybenull_ JsModuleRecord* dependentModuleRecord)
{
return FetchImportedModuleHelper(referencingModule, specifier, dependentModuleRecord);
}

// Callback from chakracore to fetch module dynamically during runtime. In the test harness,
// we are not doing any translation, just treat the specifier as fileName.
// While this call will come back directly from runtime script or module code, the additional
// task can be scheduled asynchronously that executed later.
JsErrorCode WScriptJsrt::FetchImportedModuleFromScript(_In_ JsSourceContext dwReferencingSourceContext,
Copy link
Contributor

@akroshg akroshg May 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FetchImportedModuleFromScript [](start = 25, length = 29)

wondering if this function can be refactored with above function. It seems most of the code is shared. #Resolved

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated in latest iteration


In reply to: 115327127 [](ancestors = 115327127)

_In_ JsValueRef specifier, _Outptr_result_maybenull_ JsModuleRecord* dependentModuleRecord)
{
// ch.exe assumes all imported source files are located at .
return FetchImportedModuleHelper(nullptr, specifier, dependentModuleRecord);
}

// Callback from chakraCore when the module resolution is finished, either successfuly or unsuccessfully.
JsErrorCode WScriptJsrt::NotifyModuleReadyCallback(_In_opt_ JsModuleRecord referencingModule, _In_opt_ JsValueRef exceptionVar)
{
Expand Down
4 changes: 4 additions & 0 deletions bin/ch/WScriptJsrt.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ class WScriptJsrt
static void PushMessage(MessageBase *message) { messageQueue->InsertSorted(message); }

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 InitializeModuleCallbacks();
static void CALLBACK PromiseContinuationCallback(JsValueRef task, void *callbackState);

static LPCWSTR ConvertErrorCodeToMessage(JsErrorCode errorCode)
Expand Down Expand Up @@ -116,6 +118,8 @@ class WScriptJsrt
static JsValueRef CALLBACK LoadTextFileCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
static JsValueRef CALLBACK FlagCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);

static JsErrorCode FetchImportedModuleHelper(JsModuleRecord referencingModule, JsValueRef specifier, __out JsModuleRecord* dependentModuleRecord);

static MessageQueue *messageQueue;
static DWORD_PTR sourceContext;
static std::map<std::string, JsModuleRecord> moduleRecordMap;
Expand Down
2 changes: 2 additions & 0 deletions lib/Backend/JnHelperMethodList.h
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,8 @@ HELPERCALL(SetHomeObj, Js::JavascriptOperators::OP_SetHomeObj,
HELPERCALL(LdHomeObjProto, Js::JavascriptOperators::OP_LdHomeObjProto, 0)
HELPERCALL(LdFuncObjProto, Js::JavascriptOperators::OP_LdFuncObjProto, 0)

HELPERCALL(ImportCall, Js::JavascriptOperators::OP_ImportCall, 0)

HELPERCALL(ResumeYield, Js::JavascriptOperators::OP_ResumeYield, AttrCanThrow)

#include "ExternalHelperMethodList.h"
Expand Down
14 changes: 14 additions & 0 deletions lib/Backend/Lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2844,6 +2844,20 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa
break;
}

case Js::OpCode::ImportCall:
{
IR::Opnd *src1Opnd = instr->UnlinkSrc1();
IR::Opnd *functionObjOpnd = nullptr;
m_lowererMD.LoadFunctionObjectOpnd(instr, functionObjOpnd);

LoadScriptContext(instr);
m_lowererMD.LoadHelperArgument(instr, src1Opnd);
m_lowererMD.LoadHelperArgument(instr, functionObjOpnd);
m_lowererMD.ChangeToHelperCall(instr, IR::HelperImportCall);

break;
}

case Js::OpCode::SetComputedNameVar:
{
IR::Opnd *src2Opnd = instr->UnlinkSrc2();
Expand Down
7 changes: 2 additions & 5 deletions lib/Common/ConfigFlagsList.h
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ PHASE(All)
// If ES6Module needs to be disabled by compile flag, DEFAULT_CONFIG_ES6Module should be false
#define DEFAULT_CONFIG_ES6Module (false)
#else
#define DEFAULT_CONFIG_ES6Module (false)
#define DEFAULT_CONFIG_ES6Module (true)
#endif
#define DEFAULT_CONFIG_ES6Object (true)
#define DEFAULT_CONFIG_ES6Number (true)
Expand Down Expand Up @@ -998,10 +998,7 @@ FLAGPR (Boolean, ES6, ES7TrailingComma , "Enable ES7 trailing co
FLAGPR (Boolean, ES6, ES6IsConcatSpreadable , "Enable ES6 isConcatSpreadable Symbol" , DEFAULT_CONFIG_ES6IsConcatSpreadable)
FLAGPR (Boolean, ES6, ES6Math , "Enable ES6 Math extensions" , DEFAULT_CONFIG_ES6Math)

#ifndef COMPILE_DISABLE_ES6Module
#define COMPILE_DISABLE_ES6Module 0
#endif
FLAGPR_REGOVR_EXP(Boolean, ES6, ES6Module , "Enable ES6 Modules" , DEFAULT_CONFIG_ES6Module)
FLAGPR (Boolean, ES6, ES6Module , "Enable ES6 Modules" , DEFAULT_CONFIG_ES6Module)
FLAGPR (Boolean, ES6, ES6Object , "Enable ES6 Object extensions" , DEFAULT_CONFIG_ES6Object)
FLAGPR (Boolean, ES6, ES6Number , "Enable ES6 Number extensions" , DEFAULT_CONFIG_ES6Number)
FLAGPR (Boolean, ES6, ES6ObjectLiterals , "Enable ES6 Object literal extensions" , DEFAULT_CONFIG_ES6ObjectLiterals)
Expand Down
6 changes: 6 additions & 0 deletions lib/Common/Memory/ArenaAllocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ namespace Memory
#define Adelete(alloc, obj) AllocatorDelete(ArenaAllocator, alloc, obj)
#define AdeletePlus(alloc, size, obj) AllocatorDeletePlus(ArenaAllocator, alloc, size, obj)
#define AdeleteArray(alloc, count, obj) AllocatorDeleteArray(ArenaAllocator, alloc, count, obj)
#define AdeleteUnlessNull(alloc, obj) \
if (obj != nullptr) \
{ \
Adelete(alloc, obj); \
obj = nullptr; \
}


#define AnewNoThrow(alloc,T,...) AllocatorNewNoThrow(ArenaAllocator, alloc, T, __VA_ARGS__)
Expand Down
18 changes: 17 additions & 1 deletion lib/Jsrt/ChakraCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ typedef enum JsModuleHostInfoKind
JsModuleHostInfo_Exception = 0x01,
JsModuleHostInfo_HostDefined = 0x02,
JsModuleHostInfo_NotifyModuleReadyCallback = 0x3,
JsModuleHostInfo_FetchImportedModuleCallback = 0x4
JsModuleHostInfo_FetchImportedModuleCallback = 0x4,
JsModuleHostInfo_FetchImportedModuleFromScriptCallback = 0x5
} JsModuleHostInfoKind;

/// <summary>
Expand Down Expand Up @@ -70,6 +71,21 @@ typedef JsErrorCode(CHAKRA_CALLBACK * FetchImportedModuleCallBack)(_In_ JsModule
/// <returns>
/// true if the operation succeeded, false otherwise.
/// </returns>
typedef JsErrorCode(CHAKRA_CALLBACK * FetchImportedModuleFromScriptCallBack)(_In_ JsSourceContext dwReferencingSourceContext, _In_ JsValueRef specifier, _Outptr_result_maybenull_ JsModuleRecord* dependentModuleRecord);

/// <summary>
/// User implemented callback to get notification when the module is ready.
/// </summary>
/// <remarks>
/// Notify the host after ModuleDeclarationInstantiation step (15.2.1.1.6.4) is finished. If there was error in the process, exceptionVar
/// holds the exception. Otherwise the referencingModule is ready and the host should schedule execution afterwards.
/// </remarks>
/// <param name="dwReferencingSourceContext">The referencing script that calls import()</param>
/// <param name="exceptionVar">If nullptr, the module is successfully initialized and host should queue the execution job
/// otherwise it's the exception object.</param>
/// <returns>
/// true if the operation succeeded, false otherwise.
/// </returns>
typedef JsErrorCode(CHAKRA_CALLBACK * NotifyModuleReadyCallback)(_In_opt_ JsModuleRecord referencingModule, _In_opt_ JsValueRef exceptionVar);

/// <summary>
Expand Down
21 changes: 20 additions & 1 deletion lib/Jsrt/Core/JsrtContextCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,25 @@ void JsrtContextCore::OnScriptLoad(Js::JavascriptFunction * scriptFunction, Js::
}

HRESULT ChakraCoreHostScriptContext::FetchImportedModule(Js::ModuleRecordBase* referencingModule, LPCOLESTR specifier, Js::ModuleRecordBase** dependentModuleRecord)
{
return FetchImportedModuleHelper(
[=](Js::JavascriptString *specifierVar, JsModuleRecord *dependentRecord) -> JsErrorCode
{
return fetchImportedModuleCallback(referencingModule, specifierVar, dependentRecord);
}, specifier, dependentModuleRecord);
}

HRESULT ChakraCoreHostScriptContext::FetchImportedModuleFromScript(JsSourceContext dwReferencingSourceContext, LPCOLESTR specifier, Js::ModuleRecordBase** dependentModuleRecord)
{
return FetchImportedModuleHelper(
[=](Js::JavascriptString *specifierVar, JsModuleRecord *dependentRecord) -> JsErrorCode
{
return fetchImportedModuleFromScriptCallback(dwReferencingSourceContext, specifierVar, dependentRecord);
}, specifier, dependentModuleRecord);
}

template<typename Fn>
HRESULT ChakraCoreHostScriptContext::FetchImportedModuleHelper(Fn fetch, LPCOLESTR specifier, Js::ModuleRecordBase** dependentModuleRecord)
{
if (fetchImportedModuleCallback == nullptr)
{
Expand All @@ -110,7 +129,7 @@ HRESULT ChakraCoreHostScriptContext::FetchImportedModule(Js::ModuleRecordBase* r
JsModuleRecord dependentRecord = JS_INVALID_REFERENCE;
{
AUTO_NO_EXCEPTION_REGION;
JsErrorCode errorCode = fetchImportedModuleCallback(referencingModule, specifierVar, &dependentRecord);
JsErrorCode errorCode = fetch(specifierVar, &dependentRecord);
if (errorCode == JsNoError)
{
*dependentModuleRecord = static_cast<Js::ModuleRecordBase*>(dependentRecord);
Expand Down
Loading