//------------------------------------------------------------------------------------------------------- // 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 #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 ? MessageQueue* WScriptJsrt::messageQueue = nullptr; std::map WScriptJsrt::moduleRecordMap; 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++; } 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(" ")); } wprintf(_u("%ls"), str.GetWideString()); } } 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)) { fwprintf(stderr, _u("Couldn't load file.\n")); } else { returnValue = LoadScript(callee, *fileName, fileContent, *scriptInjectType ? *scriptInjectType : "self", isSourceModule, WScriptJsrt::FinalizeFree); } } } Error: 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); } 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]; 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 && 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); } } Error: 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; } 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_HostDefined, specifier); } } } IfJsrtErrorFailLogAndRetErrorCode(errorCode); return JsNoError; } JsErrorCode WScriptJsrt::LoadModuleFromString(LPCSTR fileName, LPCSTR fileContent) { DWORD_PTR dwSourceCookie = WScriptJsrt::GetNextSourceContext(); JsModuleRecord requestModule = JS_INVALID_REFERENCE; auto moduleRecordEntry = moduleRecordMap.find(std::string(fileName)); 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) { moduleRecordMap[std::string(fileName)] = requestModule; } } 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); errorCode = ChakraRTInterface::JsParseModuleSource(requestModule, dwSourceCookie, (LPBYTE)fileContent, fileContentLength, JsParseModuleSourceFlags_DataIsUTF8, &errorObject); if ((errorCode != JsNoError) && errorObject != JS_INVALID_REFERENCE && fileContent != nullptr && !HostConfigFlags::flags.IgnoreScriptErrorCode) { ChakraRTInterface::JsSetException(errorObject); return errorCode; } return JsNoError; } JsValueRef WScriptJsrt::LoadScript(JsValueRef callee, LPCSTR fileName, LPCSTR fileContent, LPCSTR scriptInjectType, bool isSourceModule, JsFinalizeCallback finalizeCallback) { HRESULT hr = E_FAIL; JsErrorCode errorCode = JsNoError; LPCWSTR errorMessage = _u("Internal error."); JsValueRef returnValue = JS_INVALID_REFERENCE; JsErrorCode innerErrorCode = JsNoError; JsContextRef currentContext = JS_INVALID_REFERENCE; JsRuntimeHandle runtime = JS_INVALID_RUNTIME_HANDLE; void *callbackArg = (finalizeCallback != nullptr ? (void*)fileContent : nullptr); char fullPathNarrow[_MAX_PATH]; size_t len = 0; IfJsrtErrorSetGo(ChakraRTInterface::JsGetCurrentContext(¤tContext)); IfJsrtErrorSetGo(ChakraRTInterface::JsGetRuntime(currentContext, &runtime)); if (fileName) { if (_fullpath(fullPathNarrow, fileName, _MAX_PATH) == nullptr) { IfFailGo(E_FAIL); } // canonicalize that path name to lower case for the profile storage // REVIEW: This doesn't work for UTF8... len = strlen(fullPathNarrow); for (size_t i = 0; i < len; i++) { fullPathNarrow[i] = (char)tolower(fullPathNarrow[i]); } } else { // No fileName provided (WScript.LoadScript()), use dummy "script.js" strcpy_s(fullPathNarrow, "script.js"); } // 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); } 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(fullPathNarrow, strlen(fullPathNarrow), &fname)); errorCode = ChakraRTInterface::JsRun(scriptSource, GetNextSourceContext(), 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(fullPathNarrow, strlen(fullPathNarrow), &fname)); errorCode = ChakraRTInterface::JsRun(scriptSource, GetNextSourceContext(), 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) { if (innerErrorCode != JsNoError) { // Failed to retrieve the inner error message, so set a custom error string errorMessage = ConvertErrorCodeToMessage(errorCode); } JsValueRef error = JS_INVALID_REFERENCE; JsValueRef messageProperty = JS_INVALID_REFERENCE; ERROR_MESSAGE_TO_STRING(errCode, errorMessage, messageProperty); if (errCode == JsNoError) { errCode = ChakraRTInterface::JsCreateError(messageProperty, &error); if (errCode == JsNoError) { errCode = ChakraRTInterface::JsSetException(error); } } 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"); JsValueRef function; JsValueRef timerId; unsigned int time; double tmp; CallbackMessage *msg = nullptr; if (argumentCount != 3) { goto Error; } function = arguments[1]; IfJsrtError(ChakraRTInterface::JsNumberToDouble(arguments[2], &tmp)); time = static_cast(tmp); msg = new CallbackMessage(time, function); messageQueue->InsertSorted(msg); IfJsrtError(ChakraRTInterface::JsDoubleToNumber(static_cast(msg->GetId()), &timerId)); return timerId; Error: JsValueRef errorObject; JsValueRef errorMessageString; ERROR_MESSAGE_TO_STRING(errorCode, errorMessage, errorMessageString); if (errorCode != JsNoError) { errorCode = ChakraRTInterface::JsCreateError(errorMessageString, &errorObject); if (errorCode != JsNoError) { ChakraRTInterface::JsSetException(errorObject); } } 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"); if (argumentCount != 2) { goto Error; } unsigned int timerId; double tmp; JsValueRef undef; JsValueRef global; IfJsrtError(ChakraRTInterface::JsNumberToDouble(arguments[1], &tmp)); timerId = static_cast(tmp); messageQueue->RemoveById(timerId); IfJsrtError(ChakraRTInterface::JsGetGlobalObject(&global)); IfJsrtError(ChakraRTInterface::JsGetUndefinedValue(&undef)); return undef; Error: JsValueRef errorObject; JsValueRef errorMessageString; ERROR_MESSAGE_TO_STRING(errorCode, errorMessage, errorMessageString); if (errorCode != JsNoError) { errorCode = ChakraRTInterface::JsCreateError(errorMessageString, &errorObject); if (errorCode != JsNoError) { ChakraRTInterface::JsSetException(errorObject); } } 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);"); JsValueType argumentType = JsUndefined; if (argumentCount != 2) { goto Error; } IfJsrtError(ChakraRTInterface::JsGetValueType(arguments[1], &argumentType)); if (argumentType != JsFunction) { 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: JsValueRef errorObject; JsValueRef errorMessageString; ERROR_MESSAGE_TO_STRING(errorCode, errorMessage, errorMessageString); if (errorCode != JsNoError) { errorCode = ChakraRTInterface::JsCreateError(errorMessageString, &errorObject); if (errorCode != JsNoError) { ChakraRTInterface::JsSetException(errorObject); } } 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);"); JsValueType argumentType = JsUndefined; if (argumentCount != 2) { goto Error; } IfJsrtError(ChakraRTInterface::JsGetValueType(arguments[1], &argumentType)); if (argumentType != JsFunction) { 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: JsValueRef errorObject; JsValueRef errorMessageString; ERROR_MESSAGE_TO_STRING(errorCode, errorMessage, errorMessageString); if (errorCode != JsNoError) { errorCode = ChakraRTInterface::JsCreateError(errorMessageString, &errorObject); if (errorCode != JsNoError) { ChakraRTInterface::JsSetException(errorObject); } } 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); CreateNamedFunction(name, nativeFunction, &propertyValueRef); 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 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)); // 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; 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); 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)); 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); if (HostConfigFlags::flags.$262) { 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)); // OSX does build does not support $262 as filename const wchar_t $262[] = #include "262.js" ; JsValueRef $262ScriptRef; IfJsrtErrorFailLogAndRetFalse(ChakraRTInterface::JsCreateStringUtf16((uint16_t*)$262, _countof($262), &$262ScriptRef)); JsValueRef fname; IfJsrtErrorFailLogAndRetFalse(ChakraRTInterface::JsCreateString("$262", strlen("$262"), &fname)); IfJsrtErrorFailLogAndRetFalse(ChakraRTInterface::JsRun($262ScriptRef, WScriptJsrt::GetNextSourceContext(), fname, JsParseScriptAttributeNone, nullptr)); } Error: return hr == S_OK; } JsErrorCode WScriptJsrt::InitializeModuleCallbacks() { JsModuleRecord moduleRecord = JS_INVALID_REFERENCE; JsErrorCode errorCode = ChakraRTInterface::JsInitializeModuleRecord(nullptr, nullptr, &moduleRecord); if (errorCode == JsNoError) { errorCode = InitializeModuleInfo(nullptr, moduleRecord); } return errorCode; } bool WScriptJsrt::Uninitialize() { // moduleRecordMap is a global std::map, its destructor may access overrided // "operator delete" / global HeapAllocator::Instance. Clear it manually here // to avoid worrying about global destructor order. moduleRecordMap.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 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; 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::LoadScriptFromFile(*fileName, fileContent, &lengthBytes); if (FAILED(hr)) { fwprintf(stderr, _u("Couldn't load file.\n")); IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&returnValue)); } else { JsValueRef stringObject; IfJsrtErrorSetGo(ChakraRTInterface::JsCreateString( fileContent, lengthBytes, &stringObject)); free((void*)fileContent); return stringObject; } } } Error: 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; 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)) { fwprintf(stderr, _u("Couldn't load file.\n")); } else { 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: 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; } 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)"; // do not mix char/wchar. print them separately fprintf(stderr, "thrown at %s:\n^\n", fName); 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) : MessageBase(0), moduleRecord(module), specifier(specifier) { ChakraRTInterface::JsAddRef(module, nullptr); if (specifier != nullptr) { // 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) { ChakraRTInterface::JsRelease(specifier, nullptr); } } HRESULT WScriptJsrt::ModuleMessage::Call(LPCSTR fileName) { JsErrorCode errorCode; JsValueRef result = JS_INVALID_REFERENCE; HRESULT hr; if (specifier == nullptr) { errorCode = ChakraRTInterface::JsModuleEvaluation(moduleRecord, &result); if (errorCode != JsNoError) { PrintException(fileName, errorCode); } } else { LPCSTR fileContent = nullptr; AutoString specifierStr(specifier); errorCode = specifierStr.GetError(); if (errorCode == JsNoError) { hr = Helpers::LoadScriptFromFile(specifierStr.GetString(), fileContent); if (FAILED(hr)) { if (!HostConfigFlags::flags.MuteHostErrorMsgIsEnabled) { fprintf(stderr, "Couldn't load file.\n"); } LoadScript(nullptr, specifierStr.GetString(), nullptr, "module", true, WScriptJsrt::FinalizeFree); } else { LoadScript(nullptr, specifierStr.GetString(), fileContent, "module", true, WScriptJsrt::FinalizeFree); } } } return errorCode; } JsErrorCode WScriptJsrt::FetchImportedModuleHelper(JsModuleRecord referencingModule, JsValueRef specifier, __out JsModuleRecord* dependentModuleRecord) { JsModuleRecord moduleRecord = JS_INVALID_REFERENCE; AutoString specifierStr; *dependentModuleRecord = nullptr; if (specifierStr.Initialize(specifier) != JsNoError) { return specifierStr.GetError(); } auto moduleEntry = moduleRecordMap.find(std::string(*specifierStr)); if (moduleEntry != moduleRecordMap.end()) { *dependentModuleRecord = moduleEntry->second; return JsNoError; } JsErrorCode errorCode = ChakraRTInterface::JsInitializeModuleRecord(referencingModule, specifier, &moduleRecord); if (errorCode == JsNoError) { InitializeModuleInfo(specifier, moduleRecord); moduleRecordMap[std::string(*specifierStr)] = moduleRecord; ModuleMessage* moduleMessage = WScriptJsrt::ModuleMessage::Create(referencingModule, specifier); 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) { 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) { // ch.exe assumes all imported source files are located at . return FetchImportedModuleHelper(nullptr, specifier, dependentModuleRecord); } // Callback from chakraCore when the module resolution is finished, either successfuly or unsuccessfully. JsErrorCode WScriptJsrt::NotifyModuleReadyCallback(_In_opt_ JsModuleRecord referencingModule, _In_opt_ JsValueRef exceptionVar) { 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) { printf("NotifyModuleReadyCallback(exception) %s\n", fileName.GetString()); } PrintException(*fileName, JsErrorScriptException); } else { WScriptJsrt::ModuleMessage* moduleMessage = WScriptJsrt::ModuleMessage::Create(referencingModule, nullptr); if (moduleMessage == nullptr) { return JsErrorOutOfMemory; } WScriptJsrt::PushMessage(moduleMessage); } 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); }