From ee9daa6438f67ef466ae52ad263b8083448149cf Mon Sep 17 00:00:00 2001 From: rhuanjl Date: Mon, 19 Feb 2018 22:23:12 +0000 Subject: [PATCH] Implement JsGetModuleNamespace API in Jsrt and ch --- bin/ChakraCore/ChakraCore.def | 1 + bin/ch/ChakraRtInterface.cpp | 1 + bin/ch/ChakraRtInterface.h | 3 + bin/ch/WScriptJsrt.cpp | 62 +++++++++++++++++++++ bin/ch/WScriptJsrt.h | 1 + lib/Jsrt/ChakraCommon.h | 4 ++ lib/Jsrt/ChakraCore.h | 16 ++++++ lib/Jsrt/Core/JsrtCore.cpp | 17 ++++++ lib/Jsrt/Jsrt.cpp | 8 +-- test/es6module/GetModuleNamespace.js | 82 ++++++++++++++++++++++++++++ test/es6module/rlexe.xml | 7 +++ 11 files changed, 198 insertions(+), 4 deletions(-) create mode 100644 test/es6module/GetModuleNamespace.js diff --git a/bin/ChakraCore/ChakraCore.def b/bin/ChakraCore/ChakraCore.def index 3010e888d9b..df529b8bb0b 100644 --- a/bin/ChakraCore/ChakraCore.def +++ b/bin/ChakraCore/ChakraCore.def @@ -52,6 +52,7 @@ JsParseModuleSource JsModuleEvaluation JsSetModuleHostInfo JsGetModuleHostInfo +JsGetModuleNamespace JsInitializeJITServer JsCreateSharedArrayBufferWithSharedContent diff --git a/bin/ch/ChakraRtInterface.cpp b/bin/ch/ChakraRtInterface.cpp index 503d6b8c025..9dad4935714 100644 --- a/bin/ch/ChakraRtInterface.cpp +++ b/bin/ch/ChakraRtInterface.cpp @@ -127,6 +127,7 @@ bool ChakraRTInterface::LoadChakraDll(ArgInfo* argInfo, HINSTANCE *outLibrary) m_jsApiHooks.pfJsrtSetModuleHostInfo = (JsAPIHooks::JsSetModuleHostInfoPtr)GetChakraCoreSymbol(library, "JsSetModuleHostInfo"); m_jsApiHooks.pfJsrtGetModuleHostInfo = (JsAPIHooks::JsGetModuleHostInfoPtr)GetChakraCoreSymbol(library, "JsGetModuleHostInfo"); m_jsApiHooks.pfJsrtModuleEvaluation = (JsAPIHooks::JsModuleEvaluationPtr)GetChakraCoreSymbol(library, "JsModuleEvaluation"); + m_jsApiHooks.pfJsrtGetModuleNamespace = (JsAPIHooks::JsGetModuleNamespacePtr)GetChakraCoreSymbol(library, "JsGetModuleNamespace"); m_jsApiHooks.pfJsrtDiagStartDebugging = (JsAPIHooks::JsrtDiagStartDebugging)GetChakraCoreSymbol(library, "JsDiagStartDebugging"); m_jsApiHooks.pfJsrtDiagStopDebugging = (JsAPIHooks::JsrtDiagStopDebugging)GetChakraCoreSymbol(library, "JsDiagStopDebugging"); m_jsApiHooks.pfJsrtDiagGetSource = (JsAPIHooks::JsrtDiagGetSource)GetChakraCoreSymbol(library, "JsDiagGetSource"); diff --git a/bin/ch/ChakraRtInterface.h b/bin/ch/ChakraRtInterface.h index 5bd00024930..6ca8862aa70 100644 --- a/bin/ch/ChakraRtInterface.h +++ b/bin/ch/ChakraRtInterface.h @@ -30,6 +30,7 @@ struct JsAPIHooks typedef JsErrorCode (WINAPI *JsInitializeModuleRecordPtr)(JsModuleRecord referencingModule, JsValueRef normalizedSpecifier, JsModuleRecord* moduleRecord); typedef JsErrorCode (WINAPI *JsParseModuleSourcePtr)(JsModuleRecord requestModule, JsSourceContext sourceContext, byte* sourceText, unsigned int sourceLength, JsParseModuleSourceFlags sourceFlag, JsValueRef* exceptionValueRef); typedef JsErrorCode (WINAPI *JsModuleEvaluationPtr)(JsModuleRecord requestModule, JsValueRef* result); + typedef JsErrorCode (WINAPI *JsGetModuleNamespacePtr)(JsModuleRecord requestModule, JsValueRef *moduleNamespace); typedef JsErrorCode (WINAPI *JsSetModuleHostInfoPtr)(JsModuleRecord requestModule, JsModuleHostInfoKind moduleHostInfo, void* hostInfo); typedef JsErrorCode (WINAPI *JsGetModuleHostInfoPtr)(JsModuleRecord requestModule, JsModuleHostInfoKind moduleHostInfo, void** hostInfo); typedef JsErrorCode (WINAPI *JsrtCallFunctionPtr)(JsValueRef function, JsValueRef* arguments, unsigned short argumentCount, JsValueRef *result); @@ -129,6 +130,7 @@ struct JsAPIHooks JsParseModuleSourcePtr pfJsrtParseModuleSource; JsInitializeModuleRecordPtr pfJsrtInitializeModuleRecord; JsModuleEvaluationPtr pfJsrtModuleEvaluation; + JsGetModuleNamespacePtr pfJsrtGetModuleNamespace; JsSetModuleHostInfoPtr pfJsrtSetModuleHostInfo; JsGetModuleHostInfoPtr pfJsrtGetModuleHostInfo; JsrtCallFunctionPtr pfJsrtCallFunction; @@ -381,6 +383,7 @@ class ChakraRTInterface return HOOK_JS_API(ParseModuleSource(requestModule, sourceContext, sourceText, sourceLength, sourceFlag, exceptionValueRef)); } static JsErrorCode WINAPI JsModuleEvaluation(JsModuleRecord requestModule, JsValueRef* result) { return HOOK_JS_API(ModuleEvaluation(requestModule, result)); } + static JsErrorCode WINAPI JsGetModuleNamespace(JsModuleRecord requestModule, JsValueRef *moduleNamespace) { return HOOK_JS_API(GetModuleNamespace(requestModule, moduleNamespace)); } static JsErrorCode WINAPI JsInitializeModuleRecord(JsModuleRecord referencingModule, JsValueRef normalizedSpecifier, JsModuleRecord* moduleRecord) { return HOOK_JS_API(InitializeModuleRecord(referencingModule, normalizedSpecifier, moduleRecord)); } diff --git a/bin/ch/WScriptJsrt.cpp b/bin/ch/WScriptJsrt.cpp index 935b2728cd6..d2e884219c3 100644 --- a/bin/ch/WScriptJsrt.cpp +++ b/bin/ch/WScriptJsrt.cpp @@ -230,6 +230,67 @@ JsValueRef WScriptJsrt::LoadScriptFileHelper(JsValueRef callee, JsValueRef *argu return returnValue; } +JsValueRef __stdcall WScriptJsrt::GetModuleNamespace(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) +{ + JsErrorCode errorCode = JsNoError; + JsValueRef returnValue = JS_INVALID_REFERENCE; + LPCWSTR errorMessage = _u(""); + char fullPath[_MAX_PATH]; + + if (argumentCount < 2) + { + errorCode = JsErrorInvalidArgument; + errorMessage = _u("Need an argument for WScript.GetModuleNamespace"); + } + else + { + AutoString specifierStr(arguments[1]); + errorCode = specifierStr.GetError(); + + if (errorCode == JsNoError) + { + if (_fullpath(fullPath, specifierStr.GetString(), _MAX_PATH) == nullptr) + { + errorCode = JsErrorInvalidArgument; + } + else + { + auto moduleEntry = moduleRecordMap.find(fullPath); + if (moduleEntry == moduleRecordMap.end()) + { + errorCode = JsErrorInvalidArgument; + errorMessage = _u("Need to supply a path for an already loaded module for WScript.GetModuleNamespace"); + } + else + { + errorCode = ChakraRTInterface::JsGetModuleNamespace(moduleEntry->second, &returnValue); + if (errorCode == JsErrorModuleNotEvaluated) + { + errorMessage = _u("GetModuleNamespace called with un-evaluated module"); + } + } + } + } + } + + if (errorCode != JsNoError) + { + JsValueRef errorObject; + JsValueRef errorMessageString; + + if (wcscmp(errorMessage, _u("")) == 0) + { + errorMessage = ConvertErrorCodeToMessage(errorCode); + } + + ERROR_MESSAGE_TO_STRING(errCode, errorMessage, errorMessageString); + + ChakraRTInterface::JsCreateError(errorMessageString, &errorObject); + ChakraRTInterface::JsSetException(errorObject); + } + return returnValue; +} + JsValueRef __stdcall WScriptJsrt::LoadScriptCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { return LoadScriptHelper(callee, isConstructCall, arguments, argumentCount, callbackState, false); @@ -859,6 +920,7 @@ bool WScriptJsrt::Initialize() IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "LoadTextFile", LoadTextFileCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Flag", FlagCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "RegisterModuleSource", RegisterModuleSourceCallback)); + IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "GetModuleNamespace", GetModuleNamespace)); // ToDo Remove IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Edit", EmptyCallback)); diff --git a/bin/ch/WScriptJsrt.h b/bin/ch/WScriptJsrt.h index 580baaca933..0c24cf2aed8 100644 --- a/bin/ch/WScriptJsrt.h +++ b/bin/ch/WScriptJsrt.h @@ -107,6 +107,7 @@ class WScriptJsrt static JsValueRef CALLBACK LoadScriptFileCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState); static JsValueRef CALLBACK LoadScriptCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState); static JsValueRef CALLBACK LoadModuleCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState); + static JsValueRef CALLBACK GetModuleNamespace(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState); static JsValueRef CALLBACK SetTimeoutCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState); static JsValueRef CALLBACK ClearTimeoutCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState); static JsValueRef CALLBACK AttachCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState); diff --git a/lib/Jsrt/ChakraCommon.h b/lib/Jsrt/ChakraCommon.h index c01049e236d..e1eb00a8bb7 100644 --- a/lib/Jsrt/ChakraCommon.h +++ b/lib/Jsrt/ChakraCommon.h @@ -230,6 +230,10 @@ typedef unsigned short uint16_t; /// JsNoWeakRefRequired, /// + /// Module was not yet evaluated when JsGetModuleNamespace was called. + /// + JsErrorModuleNotEvaluated, + /// /// Category of errors that relates to errors occurring within the engine itself. /// JsErrorCategoryEngine = 0x20000, diff --git a/lib/Jsrt/ChakraCore.h b/lib/Jsrt/ChakraCore.h index 2aefcfd9aff..c69567f29ec 100644 --- a/lib/Jsrt/ChakraCore.h +++ b/lib/Jsrt/ChakraCore.h @@ -1036,5 +1036,21 @@ CHAKRA_API JsSetHostPromiseRejectionTracker( _In_ JsHostPromiseRejectionTrackerCallback promiseRejectionTrackerCallback, _In_opt_ void *callbackState); + +/// +/// Provides the namespace object for a module. +/// +/// +/// Requires an active script context and that the module has already been evaluated. +/// +/// The JsModuleRecord for which the namespace is being requested. +/// A JsValueRef - the requested namespace object. +/// +/// The code JsNoError if the operation succeeded, a failure code otherwise. +/// +CHAKRA_API + JsGetModuleNamespace( + _In_ JsModuleRecord requestModule, + _Outptr_result_maybenull_ JsValueRef *moduleNamespace); #endif // _CHAKRACOREBUILD #endif // _CHAKRACORE_H_ diff --git a/lib/Jsrt/Core/JsrtCore.cpp b/lib/Jsrt/Core/JsrtCore.cpp index 10b76876133..77dd538139b 100644 --- a/lib/Jsrt/Core/JsrtCore.cpp +++ b/lib/Jsrt/Core/JsrtCore.cpp @@ -227,3 +227,20 @@ JsGetModuleHostInfo( }); return errorCode; } + +CHAKRA_API JsGetModuleNamespace(_In_ JsModuleRecord requestModule, _Outptr_result_maybenull_ JsValueRef *moduleNamespace) +{ + PARAM_NOT_NULL(moduleNamespace); + *moduleNamespace = nullptr; + if (!Js::SourceTextModuleRecord::Is(requestModule)) + { + return JsErrorInvalidArgument; + } + Js::SourceTextModuleRecord* moduleRecord = Js::SourceTextModuleRecord::FromHost(requestModule); + if (!moduleRecord->WasEvaluated()) + { + return JsErrorModuleNotEvaluated; + } + *moduleNamespace = static_cast(moduleRecord->GetNamespace()); + return JsNoError; +} diff --git a/lib/Jsrt/Jsrt.cpp b/lib/Jsrt/Jsrt.cpp index 994d2f59cd7..61fce0696ba 100644 --- a/lib/Jsrt/Jsrt.cpp +++ b/lib/Jsrt/Jsrt.cpp @@ -5336,10 +5336,10 @@ CHAKRA_API JsGetDataViewInfo( CHAKRA_API JsSetHostPromiseRejectionTracker(_In_ JsHostPromiseRejectionTrackerCallback promiseRejectionTrackerCallback, _In_opt_ void *callbackState) { - return ContextAPINoScriptWrapper_NoRecord([&](Js::ScriptContext *scriptContext) -> JsErrorCode { - scriptContext->GetLibrary()->SetNativeHostPromiseRejectionTrackerCallback((Js::JavascriptLibrary::HostPromiseRejectionTrackerCallback) promiseRejectionTrackerCallback, callbackState); - return JsNoError; - }, + return ContextAPINoScriptWrapper_NoRecord([&](Js::ScriptContext *scriptContext) -> JsErrorCode { + scriptContext->GetLibrary()->SetNativeHostPromiseRejectionTrackerCallback((Js::JavascriptLibrary::HostPromiseRejectionTrackerCallback) promiseRejectionTrackerCallback, callbackState); + return JsNoError; + }, /*allowInObjectBeforeCollectCallback*/true); } diff --git a/test/es6module/GetModuleNamespace.js b/test/es6module/GetModuleNamespace.js new file mode 100644 index 00000000000..79bd0c34585 --- /dev/null +++ b/test/es6module/GetModuleNamespace.js @@ -0,0 +1,82 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + +WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js"); + +WScript.RegisterModuleSource("mod0.js", ` + export const export1 = 5; + export default function export2 () + { + return true; + } + export class export3 + { + + } + export async function export4 () + { + return await null; + } + export let export5 = "exporting"; + const notExported1 = "foo"; + let notExported2 = "bar"; + var notExported3 = 5; +`); + +WScript.RegisterModuleSource("mod1.js",` + export let export1 = 10; + export default function export2 () + { + return false; + } + export class export3 + { + + } + export async function export4 () + { + return await null; + } + export const export5 = "exported"; + export {export6} from "mod2.js"; +`); + +WScript.RegisterModuleSource("mod2.js",` + export function export6 () + { + return true; + } +`); + +let test1 = import("mod0.js").then(namespaceFromImport => { + let mod0Namespace = WScript.GetModuleNamespace("mod0.js"); + assert.areEqual(mod0Namespace.export1, 5, "mod0: export1 should equal 5"); + assert.isTrue(mod0Namespace.default(), "mod0: export2 should return true"); + assert.areEqual(typeof mod0Namespace.export3.constructor, "function", "mod0: export3 should have constructor function"); + assert.areEqual(mod0Namespace.export4.constructor.name, "AsyncFunction", "mod0: export4 should be an async function"); + assert.areEqual(mod0Namespace.export5, "exporting", "mod0: export5 should be a string, exporting"); + assert.isUndefined(mod0Namespace.notExported1, "Not exported module const should not be property of namespace"); + assert.isUndefined(mod0Namespace.notExported2, "Not exported module let should not be property of namespace"); + assert.isUndefined(mod0Namespace.notExported3, "Not exported module var should not be property of namespace"); + assert.areEqual(namespaceFromImport, mod0Namespace, "ModuleNamespace object should match resolved value of import"); +}); + +let test2 = import("mod1.js").then(namespaceFromImport => { + let mod1Namespace = WScript.GetModuleNamespace("mod1.js"); + assert.areEqual(mod1Namespace.export1, 10); + assert.isFalse(mod1Namespace.default()); + assert.isUndefined(mod1Namespace.export2, "mod1: Name of default export should be default.") + assert.areEqual(typeof mod1Namespace.export3.constructor, "function"); + assert.areEqual(mod1Namespace.export4.constructor.name, "AsyncFunction"); + assert.areEqual(mod1Namespace.export5, "exported"); + assert.areEqual(mod1Namespace.export6(), true); + assert.areEqual(namespaceFromImport, mod1Namespace,"ModuleNamespace object should match resolved value of import"); +}); + +assert.throws(()=>WScript.GetModuleNamespace("mod0.js"), Error, "Expected error for un-evaluated module", "GetModuleNamespace called with un-evaluated module"); +assert.throws(()=>WScript.GetModuleNamespace("mod3.js", Error, "Expected error for un-loaded module", "Need to supply a path for an already loaded module for WScript.GetModuleNamespace")); +assert.throws(()=>WScript.GetModuleNamespace(), Error, "Expected error for no-argument", "Need an argument for WScript.GetModuleNamespace"); + +Promise.all([test1,test2]).then(()=>print("pass")).catch((e)=>print("fail: " + e)); diff --git a/test/es6module/rlexe.xml b/test/es6module/rlexe.xml index d99fb2a3925..7d84cbf84f2 100644 --- a/test/es6module/rlexe.xml +++ b/test/es6module/rlexe.xml @@ -141,6 +141,13 @@ BugFix,exclude_sanitize_address + + + GetModuleNamespace.js + -ESDynamicImport + exclude_jshost,exclude_sanitize_address + + module-load-twice.js