//------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft Corporation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #include "stdafx.h" #include "PlatformAgnostic/ChakraICU.h" #include #if defined(_X86_) || defined(_M_IX86) #define CPU_ARCH_TEXT "x86" #elif defined(_AMD64_) || defined(_IA64_) || defined(_M_AMD64) || defined(_M_IA64) #define CPU_ARCH_TEXT "x86_64" #elif defined(_ARM_) || defined(_M_ARM) #define CPU_ARCH_TEXT "ARM" #elif defined(_ARM64_) || defined(_M_ARM64) #define CPU_ARCH_TEXT "ARM64" #endif // do not change the order below // otherwise, i.e. android system can be marked as posix? etc.. #ifdef _WIN32 #define DEST_PLATFORM_TEXT "win32" #else // ! _WIN32 #if defined(__APPLE__) #ifdef __IOS__ #define DEST_PLATFORM_TEXT "ios" #else // ! iOS #define DEST_PLATFORM_TEXT "darwin" #endif // iOS ? #elif defined(__ANDROID__) #define DEST_PLATFORM_TEXT "android" #elif defined(__linux__) #define DEST_PLATFORM_TEXT "posix" #elif defined(__FreeBSD__) || defined(__unix__) #define DEST_PLATFORM_TEXT "bsd" #endif // FreeBSD or unix ? #endif // _WIN32 ? #pragma prefast(disable:26444, "This warning unfortunately raises false positives when auto is used for declaring the type of an iterator in a loop.") #ifdef HAS_ICU #define INTL_LIBRARY_TEXT "icu" #elif defined(_WIN32) #define INTL_LIBRARY_TEXT "winglob" #else #define INTL_LIBRARY_TEXT "" #endif struct ArrayBufferTransferInfo { byte* buffer; uint length; ArrayBufferFreeFn freeFn; }; struct SerializerBlob { void *data; size_t dataLength; std::vector transferableArrays; }; MessageQueue* WScriptJsrt::messageQueue = nullptr; std::map WScriptJsrt::moduleRecordMap; std::map WScriptJsrt::moduleDirMap; std::map WScriptJsrt::moduleErrMap; std::map WScriptJsrt::scriptDirMap; DWORD_PTR WScriptJsrt::sourceContext = 0; #define ERROR_MESSAGE_TO_STRING(errorCode, errorMessage, errorMessageString) \ JsErrorCode errorCode = JsNoError; \ do \ { \ const char *outOfMemoryString = \ "Failed to convert wide string. Out of memory?";\ \ char *errorMessageNarrow; \ if (FAILED(WideStringToNarrowDynamic(errorMessage, &errorMessageNarrow))) \ { \ errorCode = ChakraRTInterface::JsCreateString(outOfMemoryString, \ strlen(outOfMemoryString), &errorMessageString); \ } \ else \ { \ errorCode = ChakraRTInterface::JsCreateString(errorMessageNarrow, \ strlen(errorMessageNarrow), &errorMessageString); \ free(errorMessageNarrow); \ } \ } \ while(0) DWORD_PTR WScriptJsrt::GetNextSourceContext() { return sourceContext++; } void WScriptJsrt::RegisterScriptDir(DWORD_PTR sourceContext, LPCSTR fullDirNarrow) { GetDir(fullDirNarrow, &scriptDirMap[sourceContext]); } bool WScriptJsrt::CreateArgumentsObject(JsValueRef *argsObject) { LPWSTR *argv = HostConfigFlags::argsVal; JsValueRef retArr; Assert(argsObject); *argsObject = nullptr; IfJsrtErrorFail(ChakraRTInterface::JsCreateArray(HostConfigFlags::argsCount, &retArr), false); for (int i = 0; i < HostConfigFlags::argsCount; i++) { JsValueRef value; JsValueRef index; char *argNarrow; if (FAILED(WideStringToNarrowDynamic(argv[i], &argNarrow))) { return false; } JsErrorCode errCode = ChakraRTInterface::JsCreateString( argNarrow, strlen(argNarrow), &value); free(argNarrow); IfJsrtErrorFail(errCode, false); IfJsrtErrorFail(ChakraRTInterface::JsDoubleToNumber(i, &index), false); IfJsrtErrorFail(ChakraRTInterface::JsSetIndexedProperty(retArr, index, value), false); } *argsObject = retArr; return true; } JsValueRef __stdcall WScriptJsrt::EchoCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { for (unsigned int i = 1; i < argumentCount; i++) { JsValueRef strValue; JsErrorCode error = ChakraRTInterface::JsConvertValueToString(arguments[i], &strValue); if (error == JsNoError) { AutoString str(strValue); if (str.GetError() == JsNoError) { if (i > 1) { wprintf(_u(" ")); } charcount_t len; LPWSTR ws = str.GetWideString(&len); LPWSTR wsNoNull = new WCHAR[((size_t)len) + 1]; charcount_t newIndex = 0; for (charcount_t j = 0; j < len; j++) { if (ws[j] != _u('\0')) { wsNoNull[newIndex++] = ws[j]; } } wsNoNull[newIndex] = _u('\0'); wprintf(_u("%s"), wsNoNull); delete[] wsNoNull; } } if (error == JsErrorScriptException) { return nullptr; } } wprintf(_u("\n")); fflush(stdout); JsValueRef undefinedValue; if (ChakraRTInterface::JsGetUndefinedValue(&undefinedValue) == JsNoError) { return undefinedValue; } else { return nullptr; } } JsValueRef __stdcall WScriptJsrt::QuitCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { int exitCode = 0; if (argumentCount > 1) { double exitCodeDouble; IfJsrtErrorFail(ChakraRTInterface::JsNumberToDouble(arguments[1], &exitCodeDouble), JS_INVALID_REFERENCE); exitCode = (int)exitCodeDouble; } ExitProcess(exitCode); } JsValueRef __stdcall WScriptJsrt::LoadScriptFileCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { return LoadScriptFileHelper(callee, arguments, argumentCount, false); } // needed because of calling convention differences between _stdcall and _cdecl void CHAKRA_CALLBACK WScriptJsrt::FinalizeFree(void* addr) { free(addr); } JsValueRef WScriptJsrt::LoadScriptFileHelper(JsValueRef callee, JsValueRef *arguments, unsigned short argumentCount, bool isSourceModule) { HRESULT hr = E_FAIL; JsValueRef returnValue = JS_INVALID_REFERENCE; JsErrorCode errorCode = JsNoError; LPCWSTR errorMessage = _u(""); if (argumentCount < 2 || argumentCount > 4) { errorCode = JsErrorInvalidArgument; errorMessage = _u("Need more or fewer arguments for WScript.LoadScript"); } else { LPCSTR fileContent; AutoString fileName(arguments[1]); IfJsrtErrorSetGo(fileName.GetError()); AutoString scriptInjectType; if (argumentCount > 2) { IfJsrtErrorSetGo(scriptInjectType.Initialize(arguments[2])); } if (errorCode == JsNoError) { hr = Helpers::LoadScriptFromFile(*fileName, fileContent); if (FAILED(hr)) { fprintf(stderr, "Couldn't load file '%s'\n", fileName.GetString()); IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&returnValue)); return returnValue; } returnValue = LoadScript(callee, *fileName, fileContent, *scriptInjectType ? *scriptInjectType : "self", isSourceModule, WScriptJsrt::FinalizeFree, true); } } Error: SetExceptionIf(errorCode, errorMessage); return returnValue; } void WScriptJsrt::SetExceptionIf(JsErrorCode errorCode, LPCWSTR errorMessage) { if (errorCode != JsNoError) { // If the exception is already is set - no need to create a new exception. bool hasException = false; if (!(ChakraRTInterface::JsHasException(&hasException) == JsNoError && hasException)) { 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); } } } byte * CHAKRA_CALLBACK ReallocateBufferMemory(void * state, byte *oldBuffer, size_t newSize, size_t *allocatedSize) { void* data = realloc((void*)oldBuffer, newSize); if (allocatedSize) { *allocatedSize = newSize; } return (byte*)data; } bool CHAKRA_CALLBACK WriteHostObject(void * state, JsValueRef data) { // Not implemented return true; } JsValueRef __stdcall WScriptJsrt::SerializeObject(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { JsErrorCode errorCode = JsNoError; LPCWSTR errorMessage = _u(""); JsValueRef returnValue = JS_INVALID_REFERENCE; HRESULT hr = S_OK; JsValueRef *transferVarsArray = nullptr; int transferVarsCount = 0; if (argumentCount < 2) { errorCode = JsErrorInvalidArgument; errorMessage = _u("Need an argument for WScript.Serialize"); } else { JsValueRef rootObject = arguments[1]; JsValueRef transferArray = nullptr; if (argumentCount > 2) { JsValueType argumentType = JsUndefined; transferArray = arguments[2]; IfJsrtErrorSetGo(ChakraRTInterface::JsGetValueType(transferArray, &argumentType)); if (argumentType != JsUndefined) { if (argumentType != JsArray) { errorCode = JsErrorInvalidArgument; goto Error; } JsPropertyIdRef lengthPropId; JsValueRef arrayLengthObj = JS_INVALID_REFERENCE; int arrayLength = 0; IfJsrtErrorSetGo(CreatePropertyIdFromString("length", &lengthPropId)); IfJsrtErrorSetGo(ChakraRTInterface::JsGetProperty(transferArray, lengthPropId, &arrayLengthObj)); IfJsrtErrorSetGo(ChakraRTInterface::JsNumberToInt(arrayLengthObj, &arrayLength)); if (arrayLength > 0) { transferVarsArray = new JsValueRef[arrayLength]; if (transferVarsArray == nullptr) { errorCode = JsErrorOutOfMemory; goto Error; } for (int i = 0; i < arrayLength; i++) { JsValueRef index; JsValueRef value = JS_INVALID_REFERENCE; JsValueType jsType = JsUndefined; IfJsrtErrorSetGo(ChakraRTInterface::JsIntToNumber(i, &index)); IfJsrtErrorSetGo(ChakraRTInterface::JsGetIndexedProperty(transferArray, index, &value)); IfJsrtErrorSetGo(ChakraRTInterface::JsGetValueType(value, &jsType)); if (jsType == JsArrayBuffer) { *(transferVarsArray + transferVarsCount) = value; transferVarsCount++; } } } } } JsVarSerializerHandle serializerHandle = nullptr; // This memory will be claimed at WScriptJsrt::Deserialize. SerializerBlob *blob = new SerializerBlob(); IfJsrtErrorSetGo(ChakraRTInterface::JsVarSerializer(ReallocateBufferMemory, WriteHostObject, nullptr, &serializerHandle)); IfJsrtErrorSetGo(ChakraRTInterface::JsVarSerializerSetTransferableVars(serializerHandle, transferVarsArray, transferVarsCount)); IfJsrtErrorSetGo(ChakraRTInterface::JsVarSerializerWriteValue(serializerHandle, rootObject)); IfJsrtErrorSetGo(ChakraRTInterface::JsVarSerializerReleaseData(serializerHandle, (byte**)&blob->data, &blob->dataLength)); for (int i = 0; i < transferVarsCount; i++) { JsValueRef arrayBuffer = transferVarsArray[i]; ArrayBufferTransferInfo bufferInfo; IfJsrtErrorSetGo(ChakraRTInterface::JsGetArrayBufferStorage(arrayBuffer, &bufferInfo.buffer, &bufferInfo.length)); IfJsrtErrorSetGo(ChakraRTInterface::JsExternalizeArrayBuffer(arrayBuffer)); IfJsrtErrorSetGo(ChakraRTInterface::JsGetArrayBufferFreeFunction(arrayBuffer, &bufferInfo.freeFn)); blob->transferableArrays.push_back(bufferInfo); IfJsrtErrorSetGo(ChakraRTInterface::JsDetachArrayBuffer(arrayBuffer)); } errorCode = ChakraRTInterface::JsCreateExternalArrayBuffer((void*)blob, sizeof(SerializerBlob), nullptr, nullptr, &returnValue); IfJsrtErrorSetGo(ChakraRTInterface::JsVarSerializerFree(serializerHandle)); } Error: SetExceptionIf(errorCode, errorMessage); if (transferVarsArray) { delete[] transferVarsArray; } return returnValue; } JsValueRef CHAKRA_CALLBACK ReadHostObject(void * state) { Assert(false); // TBD return nullptr; } JsValueRef CHAKRA_CALLBACK GetSharedArrayBufferFromId(void * state, uint32_t id) { Assert(false); // TBD return nullptr; } JsValueRef CHAKRA_CALLBACK GetWasmModuleFromId(void * state, uint32_t transfer_id) { Assert(false); // TBD return nullptr; } struct BufferFreeFunctionState { ArrayBufferFreeFn freeFn; void* buffer; }; void CHAKRA_CALLBACK BufferFreeFunction(void * state) { BufferFreeFunctionState* bufferState = (BufferFreeFunctionState*)state; if (!bufferState) { return; } if (bufferState->freeFn) { bufferState->freeFn(bufferState->buffer); } delete bufferState; } JsValueRef __stdcall WScriptJsrt::Deserialize(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { JsErrorCode errorCode = JsNoError; LPCWSTR errorMessage = _u(""); JsValueRef returnValue = JS_INVALID_REFERENCE; JsValueRef * transferables = nullptr; HRESULT hr = S_OK; if (argumentCount < 2) { errorCode = JsErrorInvalidArgument; errorMessage = _u("Need an argument for WScript.Deserialize"); } else { JsValueRef dataObject = arguments[1]; uint32 dataLength = 0; BYTE *data = nullptr; IfJsrtErrorSetGo(ChakraRTInterface::JsGetArrayBufferStorage(dataObject, &data, &dataLength)); SerializerBlob *blob = (SerializerBlob*)data; JsVarDeserializerHandle deserializerHandle = nullptr; IfJsrtErrorSetGo(ChakraRTInterface::JsVarDeserializer(blob->data, blob->dataLength, ReadHostObject, GetSharedArrayBufferFromId, nullptr, &deserializerHandle)); size_t arraySize = blob->transferableArrays.size(); if (arraySize > 0) { transferables = new JsValueRef[arraySize]; for (size_t i = 0; i < arraySize; ++i) { JsValueRef result = nullptr; BufferFreeFunctionState* bufferFreeState = new BufferFreeFunctionState(); bufferFreeState->buffer = blob->transferableArrays[i].buffer; bufferFreeState->freeFn = blob->transferableArrays[i].freeFn; IfJsrtErrorSetGo(ChakraRTInterface::JsCreateExternalArrayBuffer(blob->transferableArrays[i].buffer, blob->transferableArrays[i].length, BufferFreeFunction, bufferFreeState, &result)); transferables[i] = result; } IfJsrtErrorSetGo(ChakraRTInterface::JsVarDeserializerSetTransferableVars(deserializerHandle, transferables, arraySize)); } IfJsrtErrorSetGo(ChakraRTInterface::JsVarDeserializerReadValue(deserializerHandle, &returnValue)); IfJsrtErrorSetGo(ChakraRTInterface::JsVarDeserializerFree(deserializerHandle)); delete blob; } Error: SetExceptionIf(errorCode, errorMessage); if (transferables) { delete[] transferables; } 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"); } } } } } SetExceptionIf(errorCode, errorMessage); 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); } JsValueRef __stdcall WScriptJsrt::LoadModuleCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { return LoadScriptHelper(callee, isConstructCall, arguments, argumentCount, callbackState, true); } JsValueRef WScriptJsrt::LoadScriptHelper(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState, bool isSourceModule) { HRESULT hr = E_FAIL; JsErrorCode errorCode = JsNoError; LPCWSTR errorMessage = _u(""); JsValueRef returnValue = JS_INVALID_REFERENCE; if (argumentCount < 2 || argumentCount > 4) { errorCode = JsErrorInvalidArgument; errorMessage = _u("Need more or fewer arguments for WScript.LoadScript"); } else { AutoString fileContent; char *fileNameNarrow = nullptr; AutoString fileName; AutoString scriptInjectType; char fileNameBuffer[MAX_PATH]; bool isFile = true; IfJsrtErrorSetGo(fileContent.Initialize(arguments[1])); // ExternalArrayBuffer Finalize will clean this up // but only if we actually register a finalizecallback for this fileContent.MakePersistent(); if (argumentCount > 2) { IfJsrtErrorSetGo(scriptInjectType.Initialize(arguments[2])); if (argumentCount > 3) { IfJsrtErrorSetGo(fileName.Initialize(arguments[3])); fileNameNarrow = *fileName; } } if (!fileNameNarrow) { isFile = false; if (isSourceModule) { sprintf_s(fileNameBuffer, MAX_PATH, "moduleScript%i.js", (int)sourceContext); fileNameNarrow = fileNameBuffer; } } if (*fileContent) { // TODO: This is CESU-8. How to tell the engine? // TODO: How to handle this source (script) life time? returnValue = LoadScript(callee, fileNameNarrow, *fileContent, *scriptInjectType ? *scriptInjectType : "self", isSourceModule, WScriptJsrt::FinalizeFree, isFile); } } Error: SetExceptionIf(errorCode, errorMessage); return returnValue; } JsErrorCode WScriptJsrt::InitializeModuleInfo(JsValueRef specifier, 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); if (errorCode == JsNoError && moduleRecord != nullptr) { errorCode = ChakraRTInterface::JsSetModuleHostInfo(moduleRecord, JsModuleHostInfo_HostDefined, specifier); } } } } IfJsrtErrorFailLogAndRetErrorCode(errorCode); return JsNoError; } void WScriptJsrt::GetDir(LPCSTR fullPathNarrow, std::string *fullDirNarrow) { char fileDrive[_MAX_DRIVE]; char fileDir[_MAX_DIR]; std::string result; if (_splitpath_s(fullPathNarrow, fileDrive, _countof(fileDrive), fileDir, _countof(fileDir), nullptr, 0, nullptr, 0) == 0) { result += fileDrive; result += fileDir; } *fullDirNarrow = result; } JsErrorCode WScriptJsrt::ModuleEntryPoint(LPCSTR fileName, LPCSTR fileContent, LPCSTR fullName) { return LoadModuleFromString(fileName, fileContent, fullName, true); } JsErrorCode WScriptJsrt::LoadModuleFromString(LPCSTR fileName, LPCSTR fileContent, LPCSTR fullName, bool isFile) { DWORD_PTR dwSourceCookie = WScriptJsrt::GetNextSourceContext(); JsModuleRecord requestModule = JS_INVALID_REFERENCE; LPCSTR moduleRecordKey = fullName ? fullName : fileName; auto moduleRecordEntry = moduleRecordMap.find(std::string(moduleRecordKey)); JsErrorCode errorCode = JsNoError; // we need to create a new moduleRecord if the specifier (fileName) is not found; // otherwise we'll use the old one. if (moduleRecordEntry == moduleRecordMap.end()) { JsValueRef specifier; errorCode = ChakraRTInterface::JsCreateString( fileName, strlen(fileName), &specifier); if (errorCode == JsNoError) { errorCode = ChakraRTInterface::JsInitializeModuleRecord( nullptr, specifier, &requestModule); } if (errorCode == JsNoError) { errorCode = InitializeModuleInfo(specifier, requestModule); } if (errorCode == JsNoError) { if (fullName) { GetDir(fullName, &moduleDirMap[requestModule]); } moduleRecordMap[std::string(moduleRecordKey)] = requestModule; moduleErrMap[requestModule] = RootModule; } } else { requestModule = moduleRecordEntry->second; } IfJsrtErrorFailLogAndRetErrorCode(errorCode); JsValueRef errorObject = JS_INVALID_REFERENCE; // ParseModuleSource is sync, while additional fetch & evaluation are async. unsigned int fileContentLength = (fileContent == nullptr) ? 0 : (unsigned int)strlen(fileContent); if (isFile && fullName) { JsValueRef moduleUrl; ChakraRTInterface::JsCreateString(fullName, strlen(fullName), &moduleUrl); errorCode = ChakraRTInterface::JsSetModuleHostInfo(requestModule, JsModuleHostInfo_Url, moduleUrl); IfJsrtErrorFail(errorCode, errorCode); } errorCode = ChakraRTInterface::JsParseModuleSource(requestModule, dwSourceCookie, (LPBYTE)fileContent, fileContentLength, JsParseModuleSourceFlags_DataIsUTF8, &errorObject); if ((errorCode != JsNoError) && errorObject != JS_INVALID_REFERENCE && fileContent != nullptr && !HostConfigFlags::flags.IgnoreScriptErrorCode && moduleErrMap[requestModule] == RootModule) { ChakraRTInterface::JsSetException(errorObject); moduleErrMap[requestModule] = ErroredModule; return errorCode; } return JsNoError; } JsValueRef WScriptJsrt::LoadScript(JsValueRef callee, LPCSTR fileName, LPCSTR fileContent, LPCSTR scriptInjectType, bool isSourceModule, JsFinalizeCallback finalizeCallback, bool isFile) { HRESULT hr = E_FAIL; JsErrorCode errorCode = JsNoError; LPCWSTR errorMessage = _u("Internal error."); JsValueRef returnValue = JS_INVALID_REFERENCE; JsContextRef currentContext = JS_INVALID_REFERENCE; JsRuntimeHandle runtime = JS_INVALID_RUNTIME_HANDLE; void *callbackArg = (finalizeCallback != nullptr ? (void*)fileContent : nullptr); char fullPath[_MAX_PATH]; IfJsrtErrorSetGo(ChakraRTInterface::JsGetCurrentContext(¤tContext)); IfJsrtErrorSetGo(ChakraRTInterface::JsGetRuntime(currentContext, &runtime)); if (fileName == nullptr) { fileName = "script.js"; } if (_fullpath(fullPath, fileName, _MAX_PATH) == nullptr) { goto Error; } // this is called with LoadModuleCallback method as well where caller pass in a string that should be // treated as a module source text instead of opening a new file. if (isSourceModule || (strcmp(scriptInjectType, "module") == 0)) { errorCode = LoadModuleFromString(fileName, fileContent, fullPath, isFile); } else if (strcmp(scriptInjectType, "self") == 0) { JsContextRef calleeContext; IfJsrtErrorSetGo(ChakraRTInterface::JsGetContextOfObject(callee, &calleeContext)); IfJsrtErrorSetGo(ChakraRTInterface::JsSetCurrentContext(calleeContext)); JsValueRef scriptSource; IfJsrtErrorSetGo(ChakraRTInterface::JsCreateExternalArrayBuffer((void*)fileContent, (unsigned int)strlen(fileContent), finalizeCallback, callbackArg, &scriptSource)); JsValueRef fname; IfJsrtErrorSetGo(ChakraRTInterface::JsCreateString(fullPath, strlen(fullPath), &fname)); JsSourceContext sourceContext = GetNextSourceContext(); RegisterScriptDir(sourceContext, fullPath); if (HostConfigFlags::flags.UseParserStateCacheIsEnabled) { JsValueRef parserState; IfJsrtErrorSetGo(ChakraRTInterface::JsSerializeParserState(scriptSource, &parserState, JsParseScriptAttributeNone)); errorCode = ChakraRTInterface::JsRunScriptWithParserState(scriptSource, sourceContext, fname, JsParseScriptAttributeNone, parserState, &returnValue); } else { errorCode = ChakraRTInterface::JsRun(scriptSource, sourceContext, fname, JsParseScriptAttributeNone, &returnValue); } if(errorCode == JsNoError) { errorCode = ChakraRTInterface::JsGetGlobalObject(&returnValue); } IfJsrtErrorSetGo(ChakraRTInterface::JsSetCurrentContext(currentContext)); } else if (strcmp(scriptInjectType, "samethread") == 0) { JsValueRef newContext = JS_INVALID_REFERENCE; // Create a new context and set it as the current context IfJsrtErrorSetGo(ChakraRTInterface::JsCreateContext(runtime, &newContext)); #if ENABLE_TTD //We need this here since this context is created in record IfJsrtErrorSetGo(ChakraRTInterface::JsSetObjectBeforeCollectCallback(newContext, nullptr, WScriptJsrt::JsContextBeforeCollectCallback)); #endif IfJsrtErrorSetGo(ChakraRTInterface::JsSetCurrentContext(newContext)); IfJsErrorFailLog(ChakraRTInterface::JsSetPromiseContinuationCallback(PromiseContinuationCallback, (void*)messageQueue)); // Initialize the host objects Initialize(); JsValueRef scriptSource; IfJsrtErrorSetGo(ChakraRTInterface::JsCreateExternalArrayBuffer((void*)fileContent, (unsigned int)strlen(fileContent), finalizeCallback, callbackArg, &scriptSource)); JsValueRef fname; IfJsrtErrorSetGo(ChakraRTInterface::JsCreateString(fullPath, strlen(fullPath), &fname)); JsSourceContext sourceContext = GetNextSourceContext(); RegisterScriptDir(sourceContext, fullPath); if (HostConfigFlags::flags.UseParserStateCacheIsEnabled) { JsValueRef parserState; IfJsrtErrorSetGo(ChakraRTInterface::JsSerializeParserState(scriptSource, &parserState, JsParseScriptAttributeNone)); errorCode = ChakraRTInterface::JsRunScriptWithParserState(scriptSource, sourceContext, fname, JsParseScriptAttributeNone, parserState, &returnValue); } else { errorCode = ChakraRTInterface::JsRun(scriptSource, sourceContext, fname, JsParseScriptAttributeNone, &returnValue); } if (errorCode == JsNoError) { errorCode = ChakraRTInterface::JsGetGlobalObject(&returnValue); } // Set the context back to the old one ChakraRTInterface::JsSetCurrentContext(currentContext); } else if (strcmp(scriptInjectType, "crossthread") == 0) { auto& threadData = GetRuntimeThreadLocalData().threadData; if (threadData == nullptr) { threadData = new RuntimeThreadData(); } RuntimeThreadData* child = new RuntimeThreadData(); child->initialSource = fileContent; threadData->children.push_back(child); child->parent = threadData; // TODO: need to add a switch in case we don't need to wait for // child initial script completion ResetEvent(threadData->hevntInitialScriptCompleted); child->hThread = ::CreateThread(NULL, NULL, [](void* param) -> DWORD { return ((RuntimeThreadData*)param)->ThreadProc(); }, (void*)child, NULL, NULL); WaitForSingleObject(threadData->hevntInitialScriptCompleted, INFINITE); } else { errorCode = JsErrorInvalidArgument; errorMessage = _u("Unsupported argument type inject type."); } Error: JsValueRef value = returnValue; if (errorCode != JsNoError) { SetExceptionIf(errorCode, errorMessage); ChakraRTInterface::JsDoubleToNumber(errorCode, &value); } _flushall(); return value; } JsValueRef WScriptJsrt::SetTimeoutCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { LPCWSTR errorMessage = _u("invalid call to WScript.SetTimeout"); JsErrorCode errorCode = JsNoError; HRESULT hr = S_OK; JsValueRef function; JsValueRef timerId; unsigned int time; double tmp; CallbackMessage *msg = nullptr; if (argumentCount != 3) { errorCode = JsErrorInvalidArgument; goto Error; } function = arguments[1]; IfJsrtErrorSetGo(ChakraRTInterface::JsNumberToDouble(arguments[2], &tmp)); time = static_cast(tmp); msg = new CallbackMessage(time, function); messageQueue->InsertSorted(msg); IfJsrtErrorSetGo(ChakraRTInterface::JsDoubleToNumber(static_cast(msg->GetId()), &timerId)); return timerId; Error: SetExceptionIf(errorCode, errorMessage); return JS_INVALID_REFERENCE; } JsValueRef WScriptJsrt::ClearTimeoutCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { LPCWSTR errorMessage = _u("invalid call to WScript.ClearTimeout"); JsErrorCode errorCode = JsNoError; HRESULT hr = S_OK; if (argumentCount != 2) { errorCode = JsErrorInvalidArgument; goto Error; } unsigned int timerId; double tmp; JsValueRef undef; if (ChakraRTInterface::JsNumberToDouble(arguments[1], &tmp) == JsNoError) { timerId = static_cast(tmp); messageQueue->RemoveById(timerId); } IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&undef)); return undef; Error: SetExceptionIf(errorCode, errorMessage); return JS_INVALID_REFERENCE; } template void QueueDebugOperation(JsValueRef function, const DebugOperationFunc& operation) { WScriptJsrt::PushMessage(WScriptJsrt::CallbackMessage::Create(function, operation)); } JsValueRef WScriptJsrt::AttachCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { LPCWSTR errorMessage = _u("WScript.Attach requires a function, like WScript.Attach(foo);"); JsErrorCode errorCode = JsNoError; HRESULT hr = S_OK; JsValueType argumentType = JsUndefined; if (argumentCount != 2) { errorCode = JsErrorInvalidArgument; goto Error; } IfJsrtErrorSetGo(ChakraRTInterface::JsGetValueType(arguments[1], &argumentType)); if (argumentType != JsFunction) { errorCode = JsErrorInvalidArgument; goto Error; } QueueDebugOperation(arguments[1], [](WScriptJsrt::CallbackMessage& msg) { JsContextRef currentContext = JS_INVALID_REFERENCE; ChakraRTInterface::JsGetCurrentContext(¤tContext); JsRuntimeHandle currentRuntime = JS_INVALID_RUNTIME_HANDLE; ChakraRTInterface::JsGetRuntime(currentContext, ¤tRuntime); Debugger* debugger = Debugger::GetDebugger(currentRuntime); debugger->StartDebugging(currentRuntime); debugger->SourceRunDown(); return msg.CallFunction(""); }); Error: SetExceptionIf(errorCode, errorMessage); return JS_INVALID_REFERENCE; } JsValueRef WScriptJsrt::DetachCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { LPCWSTR errorMessage = _u("WScript.Detach requires a function, like WScript.Detach(foo);"); JsErrorCode errorCode = JsNoError; HRESULT hr = S_OK; JsValueType argumentType = JsUndefined; if (argumentCount != 2) { errorCode = JsErrorInvalidArgument; goto Error; } IfJsrtErrorSetGo(ChakraRTInterface::JsGetValueType(arguments[1], &argumentType)); if (argumentType != JsFunction) { errorCode = JsErrorInvalidArgument; goto Error; } QueueDebugOperation(arguments[1], [](WScriptJsrt::CallbackMessage& msg) { JsContextRef currentContext = JS_INVALID_REFERENCE; ChakraRTInterface::JsGetCurrentContext(¤tContext); JsRuntimeHandle currentRuntime = JS_INVALID_RUNTIME_HANDLE; ChakraRTInterface::JsGetRuntime(currentContext, ¤tRuntime); if (Debugger::debugger != nullptr) { Debugger* debugger = Debugger::GetDebugger(currentRuntime); debugger->StopDebugging(currentRuntime); } return msg.CallFunction(""); }); Error: SetExceptionIf(errorCode, errorMessage); return JS_INVALID_REFERENCE; } JsValueRef WScriptJsrt::DumpFunctionPositionCallback(JsValueRef callee, bool isConstructCall, JsValueRef * arguments, unsigned short argumentCount, void * callbackState) { JsValueRef functionPosition = JS_INVALID_REFERENCE; if (argumentCount > 1) { if (ChakraRTInterface::JsDiagGetFunctionPosition(arguments[1], &functionPosition) != JsNoError) { // If we can't get the functionPosition pass undefined IfJsErrorFailLogAndRet(ChakraRTInterface::JsGetUndefinedValue(&functionPosition)); } if (Debugger::debugger != nullptr) { Debugger::debugger->DumpFunctionPosition(functionPosition); } } return JS_INVALID_REFERENCE; } JsValueRef WScriptJsrt::RequestAsyncBreakCallback(JsValueRef callee, bool isConstructCall, JsValueRef * arguments, unsigned short argumentCount, void * callbackState) { if (Debugger::debugger != nullptr && !Debugger::debugger->IsDetached()) { IfJsErrorFailLogAndRet(ChakraRTInterface::JsDiagRequestAsyncBreak(Debugger::GetRuntime())); } else { Helpers::LogError(_u("RequestAsyncBreak can only be called when debugger is attached")); } return JS_INVALID_REFERENCE; } JsValueRef WScriptJsrt::EmptyCallback(JsValueRef callee, bool isConstructCall, JsValueRef * arguments, unsigned short argumentCount, void * callbackState) { return JS_INVALID_REFERENCE; } bool WScriptJsrt::CreateNamedFunction(const char* nameString, JsNativeFunction callback, JsValueRef* functionVar) { JsValueRef nameVar; IfJsrtErrorFail(ChakraRTInterface::JsCreateString( nameString, strlen(nameString), &nameVar), false); IfJsrtErrorFail(ChakraRTInterface::JsCreateNamedFunction(nameVar, callback, nullptr, functionVar), false); return true; } bool WScriptJsrt::InstallObjectsOnObject(JsValueRef object, const char* name, JsNativeFunction nativeFunction) { JsValueRef propertyValueRef; JsPropertyIdRef propertyId; IfJsrtErrorFail(CreatePropertyIdFromString(name, &propertyId), false); if (!CreateNamedFunction(name, nativeFunction, &propertyValueRef)) { return false; } IfJsrtErrorFail(ChakraRTInterface::JsSetProperty(object, propertyId, propertyValueRef, true), false); return true; } bool WScriptJsrt::Initialize() { HRESULT hr = S_OK; char CH_BINARY_LOCATION[2048]; #ifdef CHAKRA_STATIC_LIBRARY const char* LINK_TYPE = "static"; #else const char* LINK_TYPE = "shared"; #endif #ifdef HAS_ICU int icuVersion = PlatformAgnostic::ICUHelpers::GetICUMajorVersion(); #else int icuVersion = -1; #endif JsValueRef wscript; IfJsrtErrorFail(ChakraRTInterface::JsCreateObject(&wscript), false); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Echo", EchoCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Quit", QuitCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "LoadScriptFile", LoadScriptFileCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "LoadScript", LoadScriptCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "LoadModule", LoadModuleCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "SetTimeout", SetTimeoutCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "ClearTimeout", ClearTimeoutCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Attach", AttachCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Detach", DetachCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "DumpFunctionPosition", DumpFunctionPositionCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "RequestAsyncBreak", RequestAsyncBreakCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "LoadBinaryFile", LoadBinaryFileCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "LoadTextFile", LoadTextFileCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Flag", FlagCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "RegisterModuleSource", RegisterModuleSourceCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "GetModuleNamespace", GetModuleNamespace)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "GetProxyProperties", GetProxyPropertiesCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "SerializeObject", SerializeObject)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Deserialize", Deserialize)); // ToDo Remove IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Edit", EmptyCallback)); // Platform JsValueRef platformObject; IfJsrtErrorFail(ChakraRTInterface::JsCreateObject(&platformObject), false); JsPropertyIdRef platformProperty; IfJsrtErrorFail(CreatePropertyIdFromString("Platform", &platformProperty), false); // Set CPU arch JsPropertyIdRef archProperty; IfJsrtErrorFail(CreatePropertyIdFromString("ARCH", &archProperty), false); JsValueRef archValue; IfJsrtErrorFail(ChakraRTInterface::JsCreateString( CPU_ARCH_TEXT, strlen(CPU_ARCH_TEXT), &archValue), false); IfJsrtErrorFail(ChakraRTInterface::JsSetProperty(platformObject, archProperty, archValue, true), false); // Set Build Type JsPropertyIdRef buildProperty; IfJsrtErrorFail(CreatePropertyIdFromString("BUILD_TYPE", &buildProperty), false); JsValueRef buildValue; #ifdef _DEBUG #define BUILD_TYPE_STRING_CH "Debug" // (O0) #elif defined(ENABLE_DEBUG_CONFIG_OPTIONS) #define BUILD_TYPE_STRING_CH "Test" // (O3 with debug config options) #else #define BUILD_TYPE_STRING_CH "Release" // (O3) #endif IfJsrtErrorFail(ChakraRTInterface::JsCreateString( BUILD_TYPE_STRING_CH, strlen(BUILD_TYPE_STRING_CH), &buildValue), false); IfJsrtErrorFail(ChakraRTInterface::JsSetProperty(platformObject, buildProperty, buildValue, true), false); #undef BUILD_TYPE_STRING_CH // Set Link Type [static / shared] JsPropertyIdRef linkProperty; IfJsrtErrorFail(CreatePropertyIdFromString("LINK_TYPE", &linkProperty), false); JsValueRef linkValue; IfJsrtErrorFail(ChakraRTInterface::JsCreateString( LINK_TYPE, strlen(LINK_TYPE), &linkValue), false); IfJsrtErrorFail(ChakraRTInterface::JsSetProperty(platformObject, linkProperty, linkValue, true), false); // Set Binary Location JsValueRef binaryPathValue; PlatformAgnostic::SystemInfo::GetBinaryLocation(CH_BINARY_LOCATION, sizeof(CH_BINARY_LOCATION)); JsPropertyIdRef binaryPathProperty; IfJsrtErrorFail(CreatePropertyIdFromString("BINARY_PATH", &binaryPathProperty), false); IfJsrtErrorFail(ChakraRTInterface::JsCreateString( CH_BINARY_LOCATION, strlen(CH_BINARY_LOCATION), &binaryPathValue), false); IfJsrtErrorFail(ChakraRTInterface::JsSetProperty( platformObject, binaryPathProperty, binaryPathValue, true), false); // Set destination OS JsPropertyIdRef osProperty; IfJsrtErrorFail(CreatePropertyIdFromString("OS", &osProperty), false); JsValueRef osValue; IfJsrtErrorFail(ChakraRTInterface::JsCreateString( DEST_PLATFORM_TEXT, strlen(DEST_PLATFORM_TEXT), &osValue), false); IfJsrtErrorFail(ChakraRTInterface::JsSetProperty(platformObject, osProperty, osValue, true), false); // set Internationalization library JsPropertyIdRef intlLibraryProp; IfJsrtErrorFail(CreatePropertyIdFromString("INTL_LIBRARY", &intlLibraryProp), false); JsValueRef intlLibraryStr; IfJsrtErrorFail(ChakraRTInterface::JsCreateString(INTL_LIBRARY_TEXT, strlen(INTL_LIBRARY_TEXT), &intlLibraryStr), false); IfJsrtErrorFail(ChakraRTInterface::JsSetProperty(platformObject, intlLibraryProp, intlLibraryStr, true), false); JsPropertyIdRef icuVersionProp; IfJsrtErrorFail(CreatePropertyIdFromString("ICU_VERSION", &icuVersionProp), false); JsValueRef icuVersionNum; IfJsrtErrorFail(ChakraRTInterface::JsIntToNumber(icuVersion, &icuVersionNum), false); IfJsrtErrorFail(ChakraRTInterface::JsSetProperty(platformObject, icuVersionProp, icuVersionNum, true), false); IfJsrtErrorFail(ChakraRTInterface::JsSetProperty(wscript, platformProperty, platformObject, true), false); JsValueRef argsObject; if (!CreateArgumentsObject(&argsObject)) { return false; } JsPropertyIdRef argsName; IfJsrtErrorFail(CreatePropertyIdFromString("Arguments", &argsName), false); IfJsrtErrorFail(ChakraRTInterface::JsSetProperty(wscript, argsName, argsObject, true), false); JsPropertyIdRef wscriptName; IfJsrtErrorFail(CreatePropertyIdFromString("WScript", &wscriptName), false); JsValueRef global; IfJsrtErrorFail(ChakraRTInterface::JsGetGlobalObject(&global), false); IfJsrtErrorFail(ChakraRTInterface::JsSetProperty(global, wscriptName, wscript, true), false); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(global, "print", EchoCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(global, "read", LoadTextFileCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(global, "readbuffer", LoadBinaryFileCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(global, "readline", ReadLineStdinCallback)); JsValueRef console; IfJsrtErrorFail(ChakraRTInterface::JsCreateObject(&console), false); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(console, "log", EchoCallback)); JsPropertyIdRef consoleName; IfJsrtErrorFail(CreatePropertyIdFromString("console", &consoleName), false); IfJsrtErrorFail(ChakraRTInterface::JsSetProperty(global, consoleName, console, true), false); IfJsrtErrorFail(InitializeModuleCallbacks(), false); // When the command-line argument `-Test262` is set, // WScript will have the extra support API below and $262 will be // added to global scope if (HostConfigFlags::flags.Test262) { IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Broadcast", BroadcastCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "ReceiveBroadcast", ReceiveBroadcastCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Report", ReportCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "GetReport", GetReportCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Leaving", LeavingCallback)); IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Sleep", SleepCallback)); // $262 const char Test262[] = #include "262.js" ; JsValueRef Test262ScriptRef; IfJsrtErrorFailLogAndRetFalse(ChakraRTInterface::JsCreateString(Test262, strlen(Test262), &Test262ScriptRef)); JsValueRef fname; IfJsrtErrorFailLogAndRetFalse(ChakraRTInterface::JsCreateString("262", strlen("262"), &fname)); IfJsrtErrorFailLogAndRetFalse(ChakraRTInterface::JsRun(Test262ScriptRef, WScriptJsrt::GetNextSourceContext(), fname, JsParseScriptAttributeNone, nullptr)); } Error: return hr == S_OK; } JsErrorCode WScriptJsrt::InitializeModuleCallbacks() { return InitializeModuleInfo(nullptr, nullptr); } bool WScriptJsrt::Uninitialize() { // moduleRecordMap is a global std::map, its destructor may access overridden // "operator delete" / global HeapAllocator::Instance. Clear it manually here // to avoid worrying about global destructor order. moduleRecordMap.clear(); moduleDirMap.clear(); moduleErrMap.clear(); scriptDirMap.clear(); auto& threadData = GetRuntimeThreadLocalData().threadData; if (threadData && !threadData->children.empty()) { LONG count = (LONG)threadData->children.size(); std::vector childrenHandles; //Clang does not support "for each" yet for(auto i = threadData->children.begin(); i!= threadData->children.end(); i++) { auto child = *i; childrenHandles.push_back(child->hThread); SetEvent(child->hevntShutdown); } DWORD waitRet = WaitForMultipleObjects(count, &childrenHandles[0], TRUE, INFINITE); Assert(waitRet == WAIT_OBJECT_0); for (auto i = threadData->children.begin(); i != threadData->children.end(); i++) { delete *i; } threadData->children.clear(); } return true; } #if ENABLE_TTD void CALLBACK WScriptJsrt::JsContextBeforeCollectCallback(JsRef contextRef, void *data) { ChakraRTInterface::JsTTDNotifyContextDestroy(contextRef); } #endif FileNode * SourceMap::root = nullptr; JsValueRef __stdcall WScriptJsrt::RegisterModuleSourceCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { HRESULT hr = E_FAIL; JsValueRef returnValue = JS_INVALID_REFERENCE; JsErrorCode errorCode = JsNoError; if (argumentCount < 3) { IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&returnValue)); } else { AutoString fileName; AutoString data; IfJsrtErrorSetGo(fileName.Initialize(arguments[1])); IfJsrtErrorSetGo(data.Initialize(arguments[2])); SourceMap::Add(fileName, data); } Error: return returnValue; } JsValueRef __stdcall WScriptJsrt::LoadTextFileCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { HRESULT hr = E_FAIL; JsValueRef returnValue = JS_INVALID_REFERENCE; JsErrorCode errorCode = JsNoError; const char* fileContent = nullptr; if (argumentCount < 2) { IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&returnValue)); } else { AutoString fileName; IfJsrtErrorSetGo(fileName.Initialize(arguments[1])); if (errorCode == JsNoError) { UINT lengthBytes = 0; hr = Helpers::LoadScriptFromFile(*fileName, fileContent, &lengthBytes); if (FAILED(hr)) { fprintf(stderr, "Couldn't load file '%s'\n", fileName.GetString()); IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&returnValue)); return returnValue; } IfJsrtErrorSetGo(ChakraRTInterface::JsCreateString( fileContent, lengthBytes, &returnValue)); } } Error: if (fileContent) { free((void*)fileContent); } return returnValue; } int JsFgets(char* buf, int size, FILE* file) { int n = size - 1; if (n < 0) return -1; bool crflag = false; int c, i = 0; for (i = 0; i < n && (c = getc(file)) != EOF; i++) { buf[i] = (char)c; if (c == '\n') { // any \n ends a line i++; // keep the \n; we know there is room for \0 break; } if (crflag) { // \r not followed by \n ends line at the \r ungetc(c, file); break; // and overwrite c in buf with \0 } crflag = (c == '\r'); } buf[i] = '\0'; return i; } JsValueRef __stdcall WScriptJsrt::ReadLineStdinCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { HRESULT hr = E_FAIL; JsValueRef returnValue = JS_INVALID_REFERENCE; JsErrorCode errorCode = JsNoError; const int BUFSIZE = 256; FILE* from = stdin; int buflength = 0; int bufsize = BUFSIZE; char* buf = static_cast(malloc(bufsize)); char* tmp; int gotlength = 0; if (!buf) { goto Error; } while ((gotlength = JsFgets(buf + buflength, bufsize - buflength, from)) > 0) { buflength += gotlength; // are we done? if (buf[buflength - 2] == '\r' && buf[buflength - 1] == '\n') { buf[buflength - 1] = '\0'; buf[buflength - 2] = '\0'; buflength -= 2; break; } else if (buf[buflength - 1] == '\n') { buf[buflength - 1] = '\0'; buflength -= 1; break; } else if (buflength < bufsize - 1) { break; } // Else, grow our buffer for another pass. bufsize *= 2; if (bufsize > buflength) { tmp = static_cast(realloc(buf, bufsize)); } else { goto Error; } if (!tmp) { goto Error; } buf = tmp; } //Treat the empty string specially. if (buflength == 0) { if (feof(from)) { goto Error; } else { JsValueRef emptyStringObject; IfJsrtErrorSetGo(ChakraRTInterface::JsCreateString(buf, buflength, &emptyStringObject)); free(buf); return emptyStringObject; } } // Turn buf into a JSString. Note that buflength includes the trailing null character. JsValueRef stringObject; IfJsrtErrorSetGo(ChakraRTInterface::JsCreateString(buf, buflength, &stringObject)); free(buf); return stringObject; Error: if (buf) { free(buf); } return returnValue; } JsValueRef __stdcall WScriptJsrt::LoadBinaryFileCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { HRESULT hr = E_FAIL; JsValueRef returnValue = JS_INVALID_REFERENCE; JsErrorCode errorCode = JsNoError; bool isHeapAlloc = true; if (argumentCount < 2) { IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&returnValue)); } else { const char *fileContent; AutoString fileName; IfJsrtErrorSetGo(fileName.Initialize(arguments[1])); if (errorCode == JsNoError) { UINT lengthBytes = 0; hr = Helpers::LoadBinaryFile(*fileName, fileContent, lengthBytes); if (FAILED(hr)) { fprintf(stderr, "Couldn't load file '%s'\n", fileName.GetString()); IfJsrtErrorSetGoLabel(ChakraRTInterface::JsGetUndefinedValue(&returnValue), Error); return returnValue; } JsValueRef arrayBuffer; IfJsrtErrorSetGoLabel(ChakraRTInterface::JsCreateArrayBuffer(lengthBytes, &arrayBuffer), ErrorStillFree); BYTE* buffer; unsigned int bufferLength; IfJsrtErrorSetGoLabel(ChakraRTInterface::JsGetArrayBufferStorage(arrayBuffer, &buffer, &bufferLength), ErrorStillFree); if (bufferLength < lengthBytes) { fwprintf(stderr, _u("Array buffer size is insufficient to store the binary file.\n")); } else { if (memcpy_s(buffer, bufferLength, (BYTE*)fileContent, lengthBytes) == 0) { returnValue = arrayBuffer; } } ErrorStillFree: if (isHeapAlloc) { HeapFree(GetProcessHeap(), 0, (void*)fileContent); } } } Error: return returnValue; } JsValueRef __stdcall WScriptJsrt::FlagCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { HRESULT hr = E_FAIL; JsValueRef returnValue = JS_INVALID_REFERENCE; JsErrorCode errorCode = JsNoError; IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&returnValue)); #if ENABLE_DEBUG_CONFIG_OPTIONS if (argumentCount > 1) { AutoString cmd; IfJsrtErrorSetGo(cmd.Initialize(arguments[1])); char16* argv[] = { nullptr, cmd.GetWideString() }; ChakraRTInterface::SetConfigFlags(2, argv, nullptr); } #endif Error: return returnValue; } JsValueRef __stdcall WScriptJsrt::BroadcastCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { HRESULT hr = E_FAIL; JsValueRef returnValue = JS_INVALID_REFERENCE; JsErrorCode errorCode = JsNoError; IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&returnValue)); if (argumentCount > 1) { auto& threadData = GetRuntimeThreadLocalData().threadData; if (threadData) { ChakraRTInterface::JsGetSharedArrayBufferContent(arguments[1], &threadData->sharedContent); LONG count = (LONG)threadData->children.size(); threadData->hSemaphore = CreateSemaphore(NULL, 0, count, NULL); if (threadData->hSemaphore) { //Clang does not support "for each" yet for (auto i = threadData->children.begin(); i != threadData->children.end(); i++) { auto child = *i; SetEvent(child->hevntReceivedBroadcast); } WaitForSingleObject(threadData->hSemaphore, INFINITE); CloseHandle(threadData->hSemaphore); threadData->hSemaphore = INVALID_HANDLE_VALUE; } else { fwprintf(stderr, _u("Couldn't create semaphore.\n")); fflush(stderr); } ChakraRTInterface::JsReleaseSharedArrayBufferContentHandle(threadData->sharedContent); } } Error: return returnValue; } JsValueRef __stdcall WScriptJsrt::ReceiveBroadcastCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { HRESULT hr = E_FAIL; JsValueRef returnValue = JS_INVALID_REFERENCE; JsErrorCode errorCode = JsNoError; IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&returnValue)); if (argumentCount > 1) { auto& threadData = GetRuntimeThreadLocalData().threadData; if (threadData) { if (threadData->receiveBroadcastCallbackFunc) { ChakraRTInterface::JsRelease(threadData->receiveBroadcastCallbackFunc, nullptr); } threadData->receiveBroadcastCallbackFunc = arguments[1]; ChakraRTInterface::JsAddRef(threadData->receiveBroadcastCallbackFunc, nullptr); } } Error: return returnValue; } JsValueRef __stdcall WScriptJsrt::ReportCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { HRESULT hr = E_FAIL; JsValueRef returnValue = JS_INVALID_REFERENCE; JsErrorCode errorCode = JsNoError; IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&returnValue)); if (argumentCount > 1) { JsValueRef stringRef; ChakraRTInterface::JsConvertValueToString(arguments[1], &stringRef); AutoString autoStr(stringRef); if (autoStr.GetError() == JsNoError) { std::string str(autoStr.GetString()); auto& threadData = GetRuntimeThreadLocalData().threadData; if (threadData && threadData->parent) { EnterCriticalSection(&threadData->parent->csReportQ); threadData->parent->reportQ.push_back(str); LeaveCriticalSection(&threadData->parent->csReportQ); } } } Error: return returnValue; } JsValueRef __stdcall WScriptJsrt::GetReportCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { HRESULT hr = E_FAIL; JsValueRef returnValue = JS_INVALID_REFERENCE; JsErrorCode errorCode = JsNoError; IfJsrtErrorSetGo(ChakraRTInterface::JsGetNullValue(&returnValue)); if (argumentCount > 0) { auto& threadData = GetRuntimeThreadLocalData().threadData; if (threadData) { EnterCriticalSection(&threadData->csReportQ); if (threadData->reportQ.size() > 0) { auto str = threadData->reportQ.front(); threadData->reportQ.pop_front(); ChakraRTInterface::JsCreateString(str.c_str(), str.size(), &returnValue); } LeaveCriticalSection(&threadData->csReportQ); } } Error: return returnValue; } JsValueRef __stdcall WScriptJsrt::LeavingCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { HRESULT hr = E_FAIL; JsValueRef returnValue = JS_INVALID_REFERENCE; JsErrorCode errorCode = JsNoError; IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&returnValue)); if (argumentCount > 0) { auto& threadData = GetRuntimeThreadLocalData().threadData; if (threadData) { threadData->leaving = true; } } Error: return returnValue; } JsValueRef __stdcall WScriptJsrt::SleepCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { HRESULT hr = E_FAIL; JsValueRef returnValue = JS_INVALID_REFERENCE; JsErrorCode errorCode = JsNoError; IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&returnValue)); if (argumentCount > 1) { double timeout = 0.0; ChakraRTInterface::JsNumberToDouble(arguments[1], &timeout); Sleep((DWORD)timeout); } Error: return returnValue; } JsValueRef __stdcall WScriptJsrt::GetProxyPropertiesCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState) { HRESULT hr = E_FAIL; JsValueRef returnValue = JS_INVALID_REFERENCE; JsValueRef undefined = JS_INVALID_REFERENCE; JsErrorCode errorCode = JsNoError; IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&undefined)); returnValue = undefined; if (argumentCount > 1) { bool isProxy = false; JsValueRef target; JsValueRef handler; IfJsrtErrorSetGo(ChakraRTInterface::JsGetProxyProperties(arguments[1], &isProxy, &target, &handler)); if (isProxy) { JsPropertyIdRef targetProperty; JsPropertyIdRef handlerProperty; JsPropertyIdRef revokedProperty; IfJsrtErrorSetGo(CreatePropertyIdFromString("target", &targetProperty)); IfJsrtErrorSetGo(CreatePropertyIdFromString("handler", &handlerProperty)); IfJsrtErrorSetGo(CreatePropertyIdFromString("revoked", &revokedProperty)); IfJsrtErrorSetGo(ChakraRTInterface::JsCreateObject(&returnValue)); JsValueRef revoked = JS_INVALID_REFERENCE; if (target == JS_INVALID_REFERENCE) { IfJsrtErrorSetGo(ChakraRTInterface::JsGetTrueValue(&revoked)); target = undefined; handler = undefined; } else { IfJsrtErrorSetGo(ChakraRTInterface::JsGetFalseValue(&revoked)); } IfJsrtErrorSetGo(ChakraRTInterface::JsSetProperty(returnValue, handlerProperty, handler, true)); IfJsrtErrorSetGo(ChakraRTInterface::JsSetProperty(returnValue, targetProperty, target, true)); IfJsrtErrorSetGo(ChakraRTInterface::JsSetProperty(returnValue, revokedProperty, revoked, true)); } } Error: return returnValue; } bool WScriptJsrt::PrintException(LPCSTR fileName, JsErrorCode jsErrorCode) { LPCWSTR errorTypeString = ConvertErrorCodeToMessage(jsErrorCode); JsValueRef exception; ChakraRTInterface::JsGetAndClearException(&exception); if (HostConfigFlags::flags.MuteHostErrorMsgIsEnabled) { return false; } if (exception != nullptr) { if (jsErrorCode == JsErrorCode::JsErrorScriptCompile || jsErrorCode == JsErrorCode::JsErrorScriptException) { AutoString errorMessage; IfJsrtErrorFail(errorMessage.Initialize(exception), false); if (jsErrorCode == JsErrorCode::JsErrorScriptCompile) { JsPropertyIdRef linePropertyId = JS_INVALID_REFERENCE; JsValueRef lineProperty = JS_INVALID_REFERENCE; JsPropertyIdRef columnPropertyId = JS_INVALID_REFERENCE; JsValueRef columnProperty = JS_INVALID_REFERENCE; int line; int column; IfJsrtErrorFail(CreatePropertyIdFromString("line", &linePropertyId), false); IfJsrtErrorFail(ChakraRTInterface::JsGetProperty(exception, linePropertyId, &lineProperty), false); IfJsrtErrorFail(ChakraRTInterface::JsNumberToInt(lineProperty, &line), false); IfJsrtErrorFail(CreatePropertyIdFromString("column", &columnPropertyId), false); IfJsrtErrorFail(ChakraRTInterface::JsGetProperty(exception, columnPropertyId, &columnProperty), false); IfJsrtErrorFail(ChakraRTInterface::JsNumberToInt(columnProperty, &column), false); CHAR shortFileName[_MAX_PATH]; CHAR ext[_MAX_EXT]; _splitpath_s(fileName, nullptr, 0, nullptr, 0, shortFileName, _countof(shortFileName), ext, _countof(ext)); fwprintf(stderr, _u("%ls\n\tat code (%S%S:%d:%d)\n"), errorMessage.GetWideString(), shortFileName, ext, (int)line + 1, (int)column + 1); } else { JsValueType propertyType = JsUndefined; JsPropertyIdRef stackPropertyId = JS_INVALID_REFERENCE; JsValueRef stackProperty = JS_INVALID_REFERENCE; AutoString errorStack; JsErrorCode errorCode = CreatePropertyIdFromString("stack", &stackPropertyId); if (errorCode == JsErrorCode::JsNoError) { errorCode = ChakraRTInterface::JsGetProperty(exception, stackPropertyId, &stackProperty); if (errorCode == JsErrorCode::JsNoError) { errorCode = ChakraRTInterface::JsGetValueType(stackProperty, &propertyType); } } if (errorCode != JsErrorCode::JsNoError || propertyType == JsUndefined) { const char *fName = fileName != nullptr ? fileName : "(unknown)"; CHAR shortFileName[_MAX_PATH]; CHAR ext[_MAX_EXT]; _splitpath_s(fName, nullptr, 0, nullptr, 0, shortFileName, _countof(shortFileName), ext, _countof(ext)); // do not mix char/wchar. print them separately fprintf(stderr, "thrown at %s%s:\n^\n", shortFileName, ext); fwprintf(stderr, _u("%ls\n"), errorMessage.GetWideString()); } else { IfJsrtErrorFail(errorStack.Initialize(stackProperty), false); fwprintf(stderr, _u("%ls\n"), errorStack.GetWideString()); } } } else { fwprintf(stderr, _u("Error : %ls\n"), errorTypeString); } return true; } else { fwprintf(stderr, _u("Error : %ls\n"), errorTypeString); } return false; } void WScriptJsrt::AddMessageQueue(MessageQueue *_messageQueue) { Assert(messageQueue == nullptr); messageQueue = _messageQueue; } WScriptJsrt::CallbackMessage::CallbackMessage(unsigned int time, JsValueRef function) : MessageBase(time), m_function(function) { JsErrorCode error = ChakraRTInterface::JsAddRef(m_function, nullptr); if (error != JsNoError) { // Simply report a fatal error and exit because continuing from this point would result in inconsistent state // and FailFast telemetry would not be useful. wprintf(_u("FATAL ERROR: ChakraRTInterface::JsAddRef failed in WScriptJsrt::CallbackMessage::`ctor`. error=0x%x\n"), error); exit(1); } } WScriptJsrt::CallbackMessage::~CallbackMessage() { bool hasException = false; ChakraRTInterface::JsHasException(&hasException); if (hasException) { WScriptJsrt::PrintException("", JsErrorScriptException); } JsErrorCode errorCode = ChakraRTInterface::JsRelease(m_function, nullptr); Assert(errorCode == JsNoError); m_function = JS_INVALID_REFERENCE; } HRESULT WScriptJsrt::CallbackMessage::Call(LPCSTR fileName) { return CallFunction(fileName); } HRESULT WScriptJsrt::CallbackMessage::CallFunction(LPCSTR fileName) { HRESULT hr = S_OK; JsValueRef global; JsValueRef result; JsValueRef stringValue; JsValueType type; JsErrorCode errorCode = JsNoError; IfJsrtErrorHR(ChakraRTInterface::JsGetGlobalObject(&global)); IfJsrtErrorHR(ChakraRTInterface::JsGetValueType(m_function, &type)); if (type == JsString) { IfJsrtErrorHR(ChakraRTInterface::JsConvertValueToString(m_function, &stringValue)); JsValueRef fname; ChakraRTInterface::JsCreateString("", strlen(""), &fname); // Run the code errorCode = ChakraRTInterface::JsRun(stringValue, JS_SOURCE_CONTEXT_NONE, fname, JsParseScriptAttributeArrayBufferIsUtf16Encoded, nullptr /*no result needed*/); } else { errorCode = ChakraRTInterface::JsCallFunction(m_function, &global, 1, &result); } if (errorCode != JsNoError) { hr = E_FAIL; PrintException(fileName, errorCode); } Error: return hr; } WScriptJsrt::ModuleMessage::ModuleMessage(JsModuleRecord module, JsValueRef specifier, std::string* fullPathPtr) : MessageBase(0), moduleRecord(module), specifier(specifier) { fullPath = nullptr; ChakraRTInterface::JsAddRef(module, nullptr); if (specifier != nullptr) { fullPath = new std::string (*fullPathPtr); // nullptr specifier means a Promise to execute; non-nullptr means a "fetch" operation. ChakraRTInterface::JsAddRef(specifier, nullptr); } } WScriptJsrt::ModuleMessage::~ModuleMessage() { ChakraRTInterface::JsRelease(moduleRecord, nullptr); if (specifier != nullptr) { delete fullPath; ChakraRTInterface::JsRelease(specifier, nullptr); } } HRESULT WScriptJsrt::ModuleMessage::Call(LPCSTR fileName) { JsErrorCode errorCode = JsNoError; JsValueRef result = JS_INVALID_REFERENCE; HRESULT hr; if (specifier == nullptr) { if (moduleErrMap[moduleRecord] != ErroredModule) { 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 } } } } } else { LPCSTR fileContent = nullptr; AutoString specifierStr(specifier); errorCode = specifierStr.GetError(); if (errorCode == JsNoError) { hr = Helpers::LoadScriptFromFile(*specifierStr, fileContent, nullptr, fullPath, true); if (FAILED(hr)) { if (!HostConfigFlags::flags.MuteHostErrorMsgIsEnabled) { auto actualModuleRecord = moduleRecordMap.find(*fullPath); if (actualModuleRecord == moduleRecordMap.end() || moduleErrMap[actualModuleRecord->second] == RootModule) { fprintf(stderr, "Couldn't load file '%s'\n", specifierStr.GetString()); } } LoadScript(nullptr, fullPath == nullptr ? *specifierStr : fullPath->c_str(), nullptr, "module", true, WScriptJsrt::FinalizeFree, false); goto Error; } LoadScript(nullptr, fullPath == nullptr ? *specifierStr : fullPath->c_str(), fileContent, "module", true, WScriptJsrt::FinalizeFree, true); } } Error: return errorCode; } JsErrorCode WScriptJsrt::FetchImportedModuleHelper(JsModuleRecord referencingModule, JsValueRef specifier, __out JsModuleRecord* dependentModuleRecord, LPCSTR refdir) { JsModuleRecord moduleRecord = JS_INVALID_REFERENCE; AutoString specifierStr; *dependentModuleRecord = nullptr; if (specifierStr.Initialize(specifier) != JsNoError) { return specifierStr.GetError(); } char fullPath[_MAX_PATH]; std::string specifierFullPath = refdir ? refdir : ""; specifierFullPath += *specifierStr; if (_fullpath(fullPath, specifierFullPath.c_str(), _MAX_PATH) == nullptr) { return JsErrorInvalidArgument; } auto moduleEntry = moduleRecordMap.find(std::string(fullPath)); if (moduleEntry != moduleRecordMap.end()) { *dependentModuleRecord = moduleEntry->second; return JsNoError; } JsErrorCode errorCode = ChakraRTInterface::JsInitializeModuleRecord(referencingModule, specifier, &moduleRecord); if (errorCode == JsNoError) { GetDir(fullPath, &moduleDirMap[moduleRecord]); InitializeModuleInfo(specifier, moduleRecord); std::string pathKey = std::string(fullPath); moduleRecordMap[pathKey] = moduleRecord; moduleErrMap[moduleRecord] = ImportedModule; ModuleMessage* moduleMessage = WScriptJsrt::ModuleMessage::Create(referencingModule, specifier, &pathKey); if (moduleMessage == nullptr) { return JsErrorOutOfMemory; } WScriptJsrt::PushMessage(moduleMessage); *dependentModuleRecord = moduleRecord; } 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) { auto moduleDirEntry = moduleDirMap.find(referencingModule); if (moduleDirEntry != moduleDirMap.end()) { std::string dir = moduleDirEntry->second; return FetchImportedModuleHelper(referencingModule, specifier, dependentModuleRecord, dir.c_str()); } 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, _In_ JsValueRef specifier, _Outptr_result_maybenull_ JsModuleRecord* dependentModuleRecord) { 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) { if (exceptionVar != nullptr) { ChakraRTInterface::JsSetException(exceptionVar); JsValueRef specifier = JS_INVALID_REFERENCE; ChakraRTInterface::JsGetModuleHostInfo(referencingModule, JsModuleHostInfo_HostDefined, &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 } if (exceptionVar != nullptr || moduleErrMap[referencingModule] != ErroredModule) { WScriptJsrt::ModuleMessage* moduleMessage = WScriptJsrt::ModuleMessage::Create(referencingModule, nullptr); if (moduleMessage == nullptr) { return JsErrorOutOfMemory; } WScriptJsrt::PushMessage(moduleMessage); } return JsNoError; } JsErrorCode __stdcall WScriptJsrt::InitializeImportMetaCallback(_In_opt_ JsModuleRecord referencingModule, _In_opt_ JsValueRef importMetaVar) { if (importMetaVar != nullptr) { JsValueRef specifier = JS_INVALID_REFERENCE; ChakraRTInterface::JsGetModuleHostInfo(referencingModule, JsModuleHostInfo_HostDefined, &specifier); JsPropertyIdRef urlPropId; if (JsNoError == CreatePropertyIdFromString("url", &urlPropId)) { ChakraRTInterface::JsSetProperty(importMetaVar, urlPropId, specifier, false); } } return JsNoError; } void WScriptJsrt::PromiseContinuationCallback(JsValueRef task, void *callbackState) { Assert(task != JS_INVALID_REFERENCE); Assert(callbackState != JS_INVALID_REFERENCE); MessageQueue * messageQueue = (MessageQueue *)callbackState; WScriptJsrt::CallbackMessage *msg = new WScriptJsrt::CallbackMessage(0, task); messageQueue->InsertSorted(msg); } void WScriptJsrt::PromiseRejectionTrackerCallback(JsValueRef promise, JsValueRef reason, bool handled, void *callbackState) { Assert(promise != JS_INVALID_REFERENCE); Assert(reason != JS_INVALID_REFERENCE); if (!handled) { wprintf(_u("Uncaught promise rejection\n")); } else { wprintf(_u("Promise rejection handled\n")); } JsPropertyIdRef stackPropertyID; JsErrorCode error = ChakraRTInterface::JsCreatePropertyId("stack", strlen("stack"), &stackPropertyID); if (error == JsNoError) { JsValueRef stack; error = ChakraRTInterface::JsGetProperty(reason, stackPropertyID, &stack); if (error == JsNoError) { JsValueRef stackStrValue; error = ChakraRTInterface::JsConvertValueToString(stack, &stackStrValue); if (error == JsNoError) { AutoString str(stackStrValue); wprintf(_u("%ls\n"), str.GetWideString()); } } } if (error != JsNoError) { // weren't able to print stack, so just convert reason to a string JsValueRef strValue; error = ChakraRTInterface::JsConvertValueToString(reason, &strValue); if (error == JsNoError) { AutoString str(strValue); wprintf(_u("%ls\n"), str.GetWideString()); } } fflush(stdout); }