From 4bea2b243f2935aaf804845c4e0eab909a727cdf Mon Sep 17 00:00:00 2001 From: rhuanjl Date: Thu, 21 Jan 2021 23:26:52 +0000 Subject: [PATCH] 1. Add JsGetExceptionWithMetaData to ch 2. Add handler in WScript::PrintException for Error.toString failure 3. Add test case --- bin/ch/ChakraRtInterface.cpp | 2 + bin/ch/ChakraRtInterface.h | 4 ++ bin/ch/WScriptJsrt.cpp | 65 +++++++++++++++++++- bin/ch/ch.cpp | 21 ++++--- bin/ch/stdafx.h | 2 + test/Error/exceptionInErrorToString.baseline | 4 ++ test/Error/exceptionInErrorToString.js | 12 ++++ test/Error/rlexe.xml | 6 ++ 8 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 test/Error/exceptionInErrorToString.baseline create mode 100644 test/Error/exceptionInErrorToString.js diff --git a/bin/ch/ChakraRtInterface.cpp b/bin/ch/ChakraRtInterface.cpp index 9c32df85a29..bab1e7bc18e 100644 --- a/bin/ch/ChakraRtInterface.cpp +++ b/bin/ch/ChakraRtInterface.cpp @@ -1,5 +1,6 @@ //------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. +// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #include "stdafx.h" @@ -122,6 +123,7 @@ bool ChakraRTInterface::LoadChakraDll(ArgInfo* argInfo, HINSTANCE *outLibrary) m_jsApiHooks.pfJsrtGetArrayBufferStorage = (JsAPIHooks::JsrtGetArrayBufferStoragePtr)GetChakraCoreSymbol(library, "JsGetArrayBufferStorage"); m_jsApiHooks.pfJsrtHasException = (JsAPIHooks::JsrtHasExceptionPtr)GetChakraCoreSymbol(library, "JsHasException"); m_jsApiHooks.pfJsrtSetException = (JsAPIHooks::JsrtSetExceptionPtr)GetChakraCoreSymbol(library, "JsSetException"); + m_jsApiHooks.pfJsrtGetAndClearExceptionWithMetadata = (JsAPIHooks::JsrtGetAndClearExceptiopnWithMetadataPtr)GetChakraCoreSymbol(library, "JsGetAndClearExceptionWithMetadata"); m_jsApiHooks.pfJsrtGetAndClearException = (JsAPIHooks::JsrtGetAndClearExceptiopnPtr)GetChakraCoreSymbol(library, "JsGetAndClearException"); m_jsApiHooks.pfJsrtCreateError = (JsAPIHooks::JsrtCreateErrorPtr)GetChakraCoreSymbol(library, "JsCreateError"); m_jsApiHooks.pfJsrtGetRuntime = (JsAPIHooks::JsrtGetRuntimePtr)GetChakraCoreSymbol(library, "JsGetRuntime"); diff --git a/bin/ch/ChakraRtInterface.h b/bin/ch/ChakraRtInterface.h index eadc02e6866..7eb0cef3da6 100644 --- a/bin/ch/ChakraRtInterface.h +++ b/bin/ch/ChakraRtInterface.h @@ -1,5 +1,6 @@ //------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. +// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #pragma once @@ -58,6 +59,7 @@ struct JsAPIHooks typedef JsErrorCode (WINAPI *JsrtCreateErrorPtr)(JsValueRef message, JsValueRef *error); typedef JsErrorCode (WINAPI *JsrtHasExceptionPtr)(bool *hasException); typedef JsErrorCode (WINAPI *JsrtSetExceptionPtr)(JsValueRef exception); + typedef JsErrorCode (WINAPI *JsrtGetAndClearExceptiopnWithMetadataPtr)(JsValueRef* metadata); typedef JsErrorCode (WINAPI *JsrtGetAndClearExceptiopnPtr)(JsValueRef* exception); typedef JsErrorCode (WINAPI *JsrtGetRuntimePtr)(JsContextRef context, JsRuntimeHandle *runtime); typedef JsErrorCode (WINAPI *JsrtReleasePtr)(JsRef ref, unsigned int* count); @@ -197,6 +199,7 @@ struct JsAPIHooks JsrtCreateErrorPtr pfJsrtCreateError; JsrtHasExceptionPtr pfJsrtHasException; JsrtSetExceptionPtr pfJsrtSetException; + JsrtGetAndClearExceptiopnWithMetadataPtr pfJsrtGetAndClearExceptionWithMetadata; JsrtGetAndClearExceptiopnPtr pfJsrtGetAndClearException; JsrtGetRuntimePtr pfJsrtGetRuntime; JsrtReleasePtr pfJsrtRelease; @@ -428,6 +431,7 @@ class ChakraRTInterface static JsErrorCode WINAPI JsHasException(bool *hasException) { return HOOK_JS_API(HasException(hasException)); } static JsErrorCode WINAPI JsSetException(JsValueRef exception) { return HOOK_JS_API(SetException(exception)); } static JsErrorCode WINAPI JsGetAndClearException(JsValueRef *exception) { return HOOK_JS_API(GetAndClearException(exception)); } + static JsErrorCode WINAPI JsGetAndClearExceptionWithMetadata(JsValueRef * metadata) { return HOOK_JS_API(GetAndClearExceptionWithMetadata(metadata)); } static JsErrorCode WINAPI JsGetRuntime(JsContextRef context, JsRuntimeHandle *runtime) { return HOOK_JS_API(GetRuntime(context, runtime)); } static JsErrorCode WINAPI JsRelease(JsRef ref, unsigned int* count) { return HOOK_JS_API(Release(ref, count)); } static JsErrorCode WINAPI JsAddRef(JsRef ref, unsigned int* count) { return HOOK_JS_API(AddRef(ref, count)); } diff --git a/bin/ch/WScriptJsrt.cpp b/bin/ch/WScriptJsrt.cpp index 1eff3ab27b3..275006dd152 100644 --- a/bin/ch/WScriptJsrt.cpp +++ b/bin/ch/WScriptJsrt.cpp @@ -1,5 +1,6 @@ //------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft Corporation and contributors. All rights reserved. +// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #include "stdafx.h" @@ -1766,10 +1767,21 @@ JsValueRef __stdcall WScriptJsrt::GetProxyPropertiesCallback(JsValueRef callee, bool WScriptJsrt::PrintException(LPCSTR fileName, JsErrorCode jsErrorCode, JsValueRef exception) { LPCWSTR errorTypeString = ConvertErrorCodeToMessage(jsErrorCode); + JsValueRef metaData = JS_INVALID_REFERENCE; if (exception == nullptr) { - ChakraRTInterface::JsGetAndClearException(&exception); + if (ChakraRTInterface::JsGetAndClearExceptionWithMetadata(&metaData) == JsNoError) + { + JsPropertyIdRef exceptionId = JS_INVALID_REFERENCE; + IfJsrtErrorFail(CreatePropertyIdFromString("exception", &exceptionId), false); + IfJsrtErrorFail(ChakraRTInterface::JsGetProperty(metaData, exceptionId, &exception), false); + } + else + { + IfJsrtErrorFail(ChakraRTInterface::JsGetAndClearException(&exception), false); + } + } if (HostConfigFlags::flags.MuteHostErrorMsgIsEnabled) @@ -1783,7 +1795,56 @@ bool WScriptJsrt::PrintException(LPCSTR fileName, JsErrorCode jsErrorCode, JsVal { AutoString errorMessage; - IfJsrtErrorFail(errorMessage.Initialize(exception), false); + if (errorMessage.Initialize(exception) != JsNoError) + { + fwprintf(stderr, _u("ERROR attempting to coerce error to string, using alternate handler\n")); + bool hasException = false; + ChakraRTInterface::JsHasException(&hasException); + if (hasException) + { + JsValueRef throwAway = JS_INVALID_REFERENCE; + ChakraRTInterface::JsGetAndClearException(&throwAway); + } + JsPropertyIdRef messagePropertyId = JS_INVALID_REFERENCE; + IfJsrtErrorFail(CreatePropertyIdFromString("message", &messagePropertyId), false); + JsValueRef message = JS_INVALID_REFERENCE; + IfJsrtErrorFail(ChakraRTInterface::JsGetProperty(exception, messagePropertyId, &message), false); + IfJsrtErrorFail(errorMessage.Initialize(message), false); + + if (jsErrorCode != JsErrorCode::JsErrorScriptCompile) + { + CHAR shortFileName[_MAX_PATH]; + CHAR ext[_MAX_EXT]; + _splitpath_s(fileName, nullptr, 0, nullptr, 0, shortFileName, _countof(shortFileName), ext, _countof(ext)); + + if (metaData != JS_INVALID_REFERENCE) + { + 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(metaData, linePropertyId, &lineProperty), false); + IfJsrtErrorFail(ChakraRTInterface::JsNumberToInt(lineProperty, &line), false); + + IfJsrtErrorFail(CreatePropertyIdFromString("column", &columnPropertyId), false); + IfJsrtErrorFail(ChakraRTInterface::JsGetProperty(metaData, columnPropertyId, &columnProperty), false); + IfJsrtErrorFail(ChakraRTInterface::JsNumberToInt(columnProperty, &column), false); + fwprintf(stderr, _u("%ls\n at code (%S%S:%d:%d)\n"), + errorMessage.GetWideString(), shortFileName, ext, line + 1, column + 1); + } + else + { + fwprintf(stderr, _u("%ls\n\tat code (%S%S:\?\?:\?\?)\n"), errorMessage.GetWideString(), shortFileName, ext); + } + return true; + } + } if (jsErrorCode == JsErrorCode::JsErrorScriptCompile) { diff --git a/bin/ch/ch.cpp b/bin/ch/ch.cpp index 5b8905b3ca9..a048b09df3a 100644 --- a/bin/ch/ch.cpp +++ b/bin/ch/ch.cpp @@ -1,5 +1,6 @@ //------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. +// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #include "stdafx.h" @@ -398,6 +399,10 @@ HRESULT RunScript(const char* fileName, LPCSTR fileContents, size_t fileLength, IfJsErrorFailLogLabel(ChakraRTInterface::JsCreateString(fullPath, strlen(fullPath), &fname), ErrorRunFinalize); + // memory management for serialized script case - need to define these here + SerializedCallbackInfo serializedCallbackInfo; + serializedCallbackInfo.freeingHandled = true; + if (bufferValue != nullptr) { if (fileContents == nullptr) @@ -414,7 +419,6 @@ HRESULT RunScript(const char* fileName, LPCSTR fileContents, size_t fileLength, else // fileContents != nullptr { // Memory management is a little more complex here - SerializedCallbackInfo serializedCallbackInfo; serializedCallbackInfo.scriptBody = (void*)fileContents; serializedCallbackInfo.scriptBodyFinalizeCallback = fileContentsFinalizeCallback; serializedCallbackInfo.freeingHandled = false; @@ -427,15 +431,6 @@ HRESULT RunScript(const char* fileName, LPCSTR fileContents, size_t fileLength, // Use source ptr as sourceContext fname, nullptr /*result*/); - // Now that we're down here, we can free the fileContents if they weren't sent into - // a GC-managed object. - if (!serializedCallbackInfo.freeingHandled) - { - if (fileContentsFinalizeCallback != nullptr) - { - fileContentsFinalizeCallback((void*)fileContents); - } - } } } else if (parserStateCache != nullptr) @@ -520,6 +515,12 @@ HRESULT RunScript(const char* fileName, LPCSTR fileContents, size_t fileLength, IfFailGo(messageQueue->ProcessAll(fileName)); } while(!messageQueue->IsEmpty()); } + + // free the source for the serialized script case if it's not been handed to a managed object + if (!serializedCallbackInfo.freeingHandled && fileContentsFinalizeCallback != nullptr) + { + fileContentsFinalizeCallback((void*)fileContents); + } } if(false) diff --git a/bin/ch/stdafx.h b/bin/ch/stdafx.h index 2262d7bcc45..450a33d7253 100644 --- a/bin/ch/stdafx.h +++ b/bin/ch/stdafx.h @@ -1,5 +1,6 @@ //------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. +// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #pragma once @@ -197,6 +198,7 @@ class AutoString JsErrorCode Initialize(JsValueRef value) { + errorCode = JsNoError; JsValueRef strValue; JsValueType type; ChakraRTInterface::JsGetValueType(value, &type); diff --git a/test/Error/exceptionInErrorToString.baseline b/test/Error/exceptionInErrorToString.baseline new file mode 100644 index 00000000000..e060e1e4442 --- /dev/null +++ b/test/Error/exceptionInErrorToString.baseline @@ -0,0 +1,4 @@ +CALLED toString +ERROR attempting to coerce error to string, using alternate handler +'a' is not defined + at code (exceptionInErrorToString.js:12:1) diff --git a/test/Error/exceptionInErrorToString.js b/test/Error/exceptionInErrorToString.js new file mode 100644 index 00000000000..e75ed5d1911 --- /dev/null +++ b/test/Error/exceptionInErrorToString.js @@ -0,0 +1,12 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft. All rights reserved. +// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + + +// Print sensible output for crashing when Error.toString is overwritten +// See https://github.com/chakra-core/ChakraCore/issues/6567 for more detail + +ReferenceError.prototype.toString = function ( ) { print("CALLED toString"); return { toString () { return "hi"}};} +a.b(); diff --git a/test/Error/rlexe.xml b/test/Error/rlexe.xml index 52aa111bfe5..aaa022a15d0 100644 --- a/test/Error/rlexe.xml +++ b/test/Error/rlexe.xml @@ -179,4 +179,10 @@ errorPrototypetoString.js + + + exceptionInErrorToString.js + exceptionInErrorToString.baseline + +