Skip to content

Commit 2c33faf

Browse files
committed
Add parser support for ES6 module feature
Initial support for handling import and export statements in the Chakra parser. It is gated behind a config flag (-ES6Module). We are treating the import and export keywords like a tag for declarations or identifier references which should be physically stored in a special slot array which we need to build up at module link time (or, at least before bytecode generation time). The array is not built up as part of this change but we are collecting all the exports, imports, and referenced modules such that building that array should be possible. Once we build this array, accesses to identifiers which are created via an import keyword will load from that slot array and accesses to identifiers declared via an export keyword will load and store from the same slot array. Currently, we are not hooking up export declarations to their imported usages. If you import a binding from another module and access it, we will only guarantee that the binding exists. The value of the binding will always be undefined. We are keeping track of exports, imports, and referenced modules in the top-level ParseNode of the parser (which is subsequently handed-off to the ByteCodeGenerator). When parsing a module we set a flag (fscrIsModuleCode) which also causes us to create the root ParseNode as a knopModule type node. That is in turn used to allocate the ParseNode itself as a PnModule node, which derives from PnProg but adds the lists for imports, exports, and referenced modules. We are still treating the top-level ParseNode as a knopProg (which is to say, we reset the nop to knopProg after allocating it) because we handle the case of knopProg everywhere and we don't need to complicate things by also needing to handle knopModule. Instead we just use the PnModule extensions when we are in a module-specific parsing path and otherwise treat the node as if it was a PnProg ParseNode anyway. Note that import and export statements do not themselves have special ParseNode types. This change adds an experimental method to Jsrt (JsExperimentalRunModule) and a ch.exe hook (WScript.LoadModuleFile) for loading a module from a file. Loading a module via this hook acts similarly to including a module in a script tag like <script type="module">. The experimental method is subject to change as the feature evolves. A couple of points about module code. Module code is always strict code. Import and export statements may only occur as statements at global scope - they may not be located inside functions or inside eval'd code. For more information about ES6 modules, please see the spec http://tc39.github.io/ecma262/#sec-modules.
1 parent dd8c63b commit 2c33faf

28 files changed

Lines changed: 1140 additions & 27 deletions

bin/ch/WScriptJsrt.cpp

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,17 @@ JsValueRef __stdcall WScriptJsrt::QuitCallback(JsValueRef callee, bool isConstru
9191
ExitProcess(exitCode);
9292
}
9393

94+
JsValueRef __stdcall WScriptJsrt::LoadModuleFileCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
95+
{
96+
return LoadScriptFileHelper(callee, arguments, argumentCount, true);
97+
}
98+
9499
JsValueRef __stdcall WScriptJsrt::LoadScriptFileCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
100+
{
101+
return LoadScriptFileHelper(callee, arguments, argumentCount, false);
102+
}
103+
104+
JsValueRef WScriptJsrt::LoadScriptFileHelper(JsValueRef callee, JsValueRef *arguments, unsigned short argumentCount, bool isSourceModule)
95105
{
96106
HRESULT hr = E_FAIL;
97107
JsValueRef returnValue = JS_INVALID_REFERENCE;
@@ -127,7 +137,7 @@ JsValueRef __stdcall WScriptJsrt::LoadScriptFileCallback(JsValueRef callee, bool
127137
}
128138
else
129139
{
130-
returnValue = LoadScript(callee, fileName, fileNameLength, fileContent, scriptInjectType);
140+
returnValue = LoadScript(callee, fileName, fileNameLength, fileContent, scriptInjectType, isSourceModule);
131141
}
132142
}
133143
}
@@ -151,6 +161,16 @@ JsValueRef __stdcall WScriptJsrt::LoadScriptFileCallback(JsValueRef callee, bool
151161
}
152162

153163
JsValueRef __stdcall WScriptJsrt::LoadScriptCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
164+
{
165+
return LoadScriptHelper(callee, isConstructCall, arguments, argumentCount, callbackState, false);
166+
}
167+
168+
JsValueRef __stdcall WScriptJsrt::LoadModuleCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
169+
{
170+
return LoadScriptHelper(callee, isConstructCall, arguments, argumentCount, callbackState, true);
171+
}
172+
173+
JsValueRef WScriptJsrt::LoadScriptHelper(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState, bool isSourceModule)
154174
{
155175
HRESULT hr = E_FAIL;
156176
JsErrorCode errorCode = JsNoError;
@@ -184,7 +204,7 @@ JsValueRef __stdcall WScriptJsrt::LoadScriptCallback(JsValueRef callee, bool isC
184204
{
185205
IfJsrtErrorSetGo(ChakraRTInterface::JsStringToPointer(arguments[3], &fileName, &fileNameLength));
186206
}
187-
returnValue = LoadScript(callee, fileName, fileNameLength, fileContent, scriptInjectType);
207+
returnValue = LoadScript(callee, fileName, fileNameLength, fileContent, scriptInjectType, isSourceModule);
188208
}
189209

190210
Error:
@@ -205,7 +225,7 @@ JsValueRef __stdcall WScriptJsrt::LoadScriptCallback(JsValueRef callee, bool isC
205225
return returnValue;
206226
}
207227

208-
JsValueRef WScriptJsrt::LoadScript(JsValueRef callee, LPCWSTR fileName, size_t fileNameLength, LPCWSTR fileContent, LPCWSTR scriptInjectType)
228+
JsValueRef WScriptJsrt::LoadScript(JsValueRef callee, LPCWSTR fileName, size_t fileNameLength, LPCWSTR fileContent, LPCWSTR scriptInjectType, bool isSourceModule)
209229
{
210230
HRESULT hr = E_FAIL;
211231
JsErrorCode errorCode = JsNoError;
@@ -237,7 +257,15 @@ JsValueRef WScriptJsrt::LoadScript(JsValueRef callee, LPCWSTR fileName, size_t f
237257
IfJsrtErrorSetGo(ChakraRTInterface::JsGetContextOfObject(callee, &calleeContext));
238258

239259
IfJsrtErrorSetGo(ChakraRTInterface::JsSetCurrentContext(calleeContext));
240-
errorCode = ChakraRTInterface::JsRunScript(fileContent, GetNextSourceContext(), fullPath, &returnValue);
260+
261+
if (isSourceModule)
262+
{
263+
errorCode = ChakraRTInterface::JsRunModule(fileContent, GetNextSourceContext(), fullPath, &returnValue);
264+
}
265+
else
266+
{
267+
errorCode = ChakraRTInterface::JsRunScript(fileContent, GetNextSourceContext(), fullPath, &returnValue);
268+
}
241269

242270
if (errorCode == JsNoError)
243271
{
@@ -257,7 +285,15 @@ JsValueRef WScriptJsrt::LoadScript(JsValueRef callee, LPCWSTR fileName, size_t f
257285
// Initialize the host objects
258286
Initialize();
259287

260-
errorCode = ChakraRTInterface::JsRunScript(fileContent, GetNextSourceContext(), fullPath, &returnValue);
288+
289+
if (isSourceModule)
290+
{
291+
errorCode = ChakraRTInterface::JsRunModule(fileContent, GetNextSourceContext(), fullPath, &returnValue);
292+
}
293+
else
294+
{
295+
errorCode = ChakraRTInterface::JsRunScript(fileContent, GetNextSourceContext(), fullPath, &returnValue);
296+
}
261297

262298
if (errorCode == JsNoError)
263299
{
@@ -435,13 +471,27 @@ bool WScriptJsrt::Initialize()
435471
CreateNamedFunction(loadScriptFileString, LoadScriptFileCallback, &loadScriptFile);
436472
IfJsrtErrorFail(ChakraRTInterface::JsSetProperty(wscript, loadScriptFilePropertyId, loadScriptFile, true), false);
437473

474+
JsValueRef loadModuleFile;
475+
const wchar_t* loadModuleFileString = L"LoadModuleFile";
476+
JsPropertyIdRef loadModuleFilePropertyId;
477+
IfJsrtErrorFail(ChakraRTInterface::JsGetPropertyIdFromName(loadModuleFileString, &loadModuleFilePropertyId), false);
478+
CreateNamedFunction(loadModuleFileString, LoadModuleFileCallback, &loadModuleFile);
479+
IfJsrtErrorFail(ChakraRTInterface::JsSetProperty(wscript, loadModuleFilePropertyId, loadModuleFile, true), false);
480+
438481
JsValueRef loadScript;
439482
JsPropertyIdRef loadScriptName;
440483
const wchar_t* loadScriptString = L"LoadScript";
441484
IfJsrtErrorFail(ChakraRTInterface::JsGetPropertyIdFromName(loadScriptString, &loadScriptName), false);
442485
CreateNamedFunction(loadScriptString, LoadScriptCallback, &loadScript);
443486
IfJsrtErrorFail(ChakraRTInterface::JsSetProperty(wscript, loadScriptName, loadScript, true), false);
444487

488+
JsValueRef loadModule;
489+
JsPropertyIdRef loadModuleName;
490+
const wchar_t* loadModuleString = L"LoadModule";
491+
IfJsrtErrorFail(ChakraRTInterface::JsGetPropertyIdFromName(loadModuleString, &loadModuleName), false);
492+
CreateNamedFunction(loadModuleString, LoadModuleCallback, &loadModule);
493+
IfJsrtErrorFail(ChakraRTInterface::JsSetProperty(wscript, loadModuleName, loadModule, true), false);
494+
445495
JsValueRef setTimeout;
446496
JsPropertyIdRef setTimeoutName;
447497
const wchar_t* setTimeoutString = L"SetTimeout";

bin/ch/WScriptJsrt.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,20 @@ class WScriptJsrt
4949
}
5050

5151
static bool PrintException(LPCWSTR fileName, JsErrorCode jsErrorCode);
52-
static JsValueRef LoadScript(JsValueRef callee, LPCWSTR fileName, size_t fileNameLength, LPCWSTR fileContent, LPCWSTR scriptInjectType);
52+
static JsValueRef LoadScript(JsValueRef callee, LPCWSTR fileName, size_t fileNameLength, LPCWSTR fileContent, LPCWSTR scriptInjectType, bool isSourceModule);
5353
static DWORD_PTR GetNextSourceContext();
54+
static JsValueRef LoadScriptFileHelper(JsValueRef callee, JsValueRef *arguments, unsigned short argumentCount, bool isSourceModule);
55+
static JsValueRef LoadScriptHelper(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState, bool isSourceModule);
5456

5557
private:
5658
static bool CreateArgumentsObject(JsValueRef *argsObject);
5759
static bool CreateNamedFunction(const wchar_t*, JsNativeFunction callback, JsValueRef* functionVar);
5860
static JsValueRef __stdcall EchoCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
5961
static JsValueRef __stdcall QuitCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
62+
static JsValueRef __stdcall LoadModuleFileCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
6063
static JsValueRef __stdcall LoadScriptFileCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
6164
static JsValueRef __stdcall LoadScriptCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
65+
static JsValueRef __stdcall LoadModuleCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
6266
static JsValueRef __stdcall SetTimeoutCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
6367
static JsValueRef __stdcall ClearTimeoutCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
6468
static MessageQueue *messageQueue;

bin/ch/chakrartinterface.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ HINSTANCE ChakraRTInterface::LoadChakraDll(ArgInfo& argInfo)
6767
m_jsApiHooks.pfJsrtGetProperty = (JsAPIHooks::JsrtGetPropertyPtr)GetProcAddress(library, "JsGetProperty");
6868
m_jsApiHooks.pfJsrtHasProperty = (JsAPIHooks::JsrtHasPropertyPtr)GetProcAddress(library, "JsHasProperty");
6969
m_jsApiHooks.pfJsrtRunScript = (JsAPIHooks::JsrtRunScriptPtr)GetProcAddress(library, "JsRunScript");
70+
m_jsApiHooks.pfJsrtRunModule = (JsAPIHooks::JsrtRunModulePtr)GetProcAddress(library, "JsExperimentalApiRunModule");
7071
m_jsApiHooks.pfJsrtCallFunction = (JsAPIHooks::JsrtCallFunctionPtr)GetProcAddress(library, "JsCallFunction");
7172
m_jsApiHooks.pfJsrtNumbertoDouble = (JsAPIHooks::JsrtNumberToDoublePtr)GetProcAddress(library, "JsNumberToDouble");
7273
m_jsApiHooks.pfJsrtNumbertoInt = (JsAPIHooks::JsrtNumberToIntPtr)GetProcAddress(library, "JsNumberToInt");

bin/ch/chakrartinterface.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ struct JsAPIHooks
2828
typedef JsErrorCode (WINAPI *JsrtGetPropertyPtr)(JsValueRef object, JsPropertyIdRef property, JsValueRef* value);
2929
typedef JsErrorCode (WINAPI *JsrtHasPropertyPtr)(JsValueRef object, JsPropertyIdRef property, bool *hasProperty);
3030
typedef JsErrorCode (WINAPI *JsrtRunScriptPtr)(const wchar_t *script, DWORD_PTR sourceContext, const wchar_t *sourceUrl, JsValueRef* result);
31+
typedef JsErrorCode (WINAPI *JsrtRunModulePtr)(const wchar_t *script, DWORD_PTR sourceContext, const wchar_t *sourceUrl, JsValueRef* result);
3132
typedef JsErrorCode (WINAPI *JsrtCallFunctionPtr)(JsValueRef function, JsValueRef* arguments, unsigned short argumentCount, JsValueRef *result);
3233
typedef JsErrorCode (WINAPI *JsrtNumberToDoublePtr)(JsValueRef value, double *doubleValue);
3334
typedef JsErrorCode (WINAPI *JsrtNumberToIntPtr)(JsValueRef value, int *intValue);
@@ -71,6 +72,7 @@ struct JsAPIHooks
7172
JsrtGetPropertyPtr pfJsrtGetProperty;
7273
JsrtHasPropertyPtr pfJsrtHasProperty;
7374
JsrtRunScriptPtr pfJsrtRunScript;
75+
JsrtRunModulePtr pfJsrtRunModule;
7476
JsrtCallFunctionPtr pfJsrtCallFunction;
7577
JsrtNumberToDoublePtr pfJsrtNumbertoDouble;
7678
JsrtNumberToIntPtr pfJsrtNumbertoInt;
@@ -176,6 +178,7 @@ class ChakraRTInterface
176178
static JsErrorCode WINAPI JsGetProperty(JsValueRef object, JsPropertyIdRef property, JsValueRef* value) { return m_jsApiHooks.pfJsrtGetProperty(object, property, value); }
177179
static JsErrorCode WINAPI JsHasProperty(JsValueRef object, JsPropertyIdRef property, bool *hasProperty) { return m_jsApiHooks.pfJsrtHasProperty(object, property, hasProperty); }
178180
static JsErrorCode WINAPI JsRunScript(const wchar_t *script, DWORD_PTR sourceContext, const wchar_t *sourceUrl, JsValueRef* result) { return m_jsApiHooks.pfJsrtRunScript(script, sourceContext, sourceUrl, result); }
181+
static JsErrorCode WINAPI JsRunModule(const wchar_t *script, DWORD_PTR sourceContext, const wchar_t *sourceUrl, JsValueRef* result) { return m_jsApiHooks.pfJsrtRunModule(script, sourceContext, sourceUrl, result); }
179182
static JsErrorCode WINAPI JsCallFunction(JsValueRef function, JsValueRef* arguments, unsigned short argumentCount, JsValueRef *result) { return m_jsApiHooks.pfJsrtCallFunction(function, arguments, argumentCount, result); }
180183
static JsErrorCode WINAPI JsNumberToDouble(JsValueRef value, double* doubleValue) { return m_jsApiHooks.pfJsrtNumbertoDouble(value, doubleValue); }
181184
static JsErrorCode WINAPI JsNumberToInt(JsValueRef value, int* intValue) { return m_jsApiHooks.pfJsrtNumbertoInt(value, intValue); }

lib/Parser/ParseFlags.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ enum
5050
fscrConsoleScopeEval = 1 << 24, // The eval string is console eval or debugEval, used to have top level
5151
// let/const in global scope instead of eval scope so that they can be preserved across console inputs
5252
fscrNoAsmJs = 1 << 25, // Disable generation of asm.js code
53-
fscrAll = (1 << 26) - 1
53+
fscrIsModuleCode = 1 << 26, // Current code should be parsed as a module body
54+
fscrAll = (1 << 27) - 1
5455
};
5556

lib/Parser/ParserCommon.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,30 @@ enum ErrorTypeEnum
3434
struct ParseNode;
3535
typedef ParseNode *ParseNodePtr;
3636

37+
struct Ident;
38+
typedef Ident *IdentPtr;
39+
40+
struct ModuleImportEntry
41+
{
42+
IdentPtr moduleRequest;
43+
IdentPtr importName;
44+
IdentPtr localName;
45+
46+
ParseNodePtr varDecl;
47+
};
48+
49+
struct ModuleExportEntry
50+
{
51+
IdentPtr moduleRequest;
52+
IdentPtr importName;
53+
IdentPtr localName;
54+
IdentPtr exportName;
55+
};
56+
57+
typedef SList<ModuleImportEntry, ArenaAllocator> ModuleImportEntryList;
58+
typedef SList<ModuleExportEntry, ArenaAllocator> ModuleExportEntryList;
59+
typedef SList<IdentPtr, ArenaAllocator> IdentPtrList;
60+
3761
//
3862
// Below was moved from scrutil.h to share with chakradiag.
3963
//

0 commit comments

Comments
 (0)