| //------------------------------------------------------------------------------------------------------- |
| // 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 <vector> |
| |
| #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<ArrayBufferTransferInfo> transferableArrays; |
| }; |
| |
| MessageQueue* WScriptJsrt::messageQueue = nullptr; |
| std::map<std::string, JsModuleRecord> WScriptJsrt::moduleRecordMap; |
| std::map<JsModuleRecord, std::string> WScriptJsrt::moduleDirMap; |
| std::map<JsModuleRecord, ModuleState> WScriptJsrt::moduleErrMap; |
| std::map<DWORD_PTR, std::string> 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 && 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<int>(tmp); |
| msg = new CallbackMessage(time, function); |
| messageQueue->InsertSorted(msg); |
| |
| IfJsrtErrorSetGo(ChakraRTInterface::JsDoubleToNumber(static_cast<double>(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<int>(tmp); |
| messageQueue->RemoveById(timerId); |
| } |
| |
| IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&undef)); |
| return undef; |
| |
| Error: |
| SetExceptionIf(errorCode, errorMessage); |
| return JS_INVALID_REFERENCE; |
| } |
| |
| template <class DebugOperationFunc> |
| 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<HANDLE> 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<char*>(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<char*>(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; |
| } |
| |
| 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); |
| } |
| |