diff --git a/vnext/Chakra/ChakraCoreDebugger.h b/vnext/Chakra/ChakraCoreDebugger.h index 9665b5806f2..cb6461ed49c 100644 --- a/vnext/Chakra/ChakraCoreDebugger.h +++ b/vnext/Chakra/ChakraCoreDebugger.h @@ -91,6 +91,13 @@ class DebugProtocolHandler return result; } + + JsErrorCode GetConsoleObject(JsValueRef* consoleObject) + { + JsErrorCode result = JsDebugProtocolHandlerCreateConsoleObject(m_protocolHandler, consoleObject); + + return result; + } }; diff --git a/vnext/Chakra/ChakraExecutor.cpp b/vnext/Chakra/ChakraExecutor.cpp index 6f607014884..32feba56edf 100644 --- a/vnext/Chakra/ChakraExecutor.cpp +++ b/vnext/Chakra/ChakraExecutor.cpp @@ -203,6 +203,75 @@ struct JsRuntimeTracker #endif }; +#if !defined(USE_EDGEMODE_JSRT) + +#define IfFailRet(v) \ + { \ + JsErrorCode error = (v); \ + if (error != JsNoError) \ + { \ + return error; \ + } \ + } + + +JsErrorCode ChakraExecutor::RedirectConsoleToDebugger(JsValueRef debuggerConsoleObject) { + + JsValueRef globalObject = JS_INVALID_REFERENCE; + IfFailRet(JsGetGlobalObject(&globalObject)); + + JsPropertyIdRef consolePropertyId = JS_INVALID_REFERENCE; + IfFailRet(JsGetPropertyIdFromName(L"console", &consolePropertyId)); + + JsValueRef consoleObject = JS_INVALID_REFERENCE; + IfFailRet(JsGetProperty(globalObject, consolePropertyId, &consoleObject)); + + JsValueRef undefinedValue = JS_INVALID_REFERENCE; + IfFailRet(JsGetUndefinedValue(&undefinedValue)); + + if (consoleObject == JS_INVALID_REFERENCE || consoleObject == undefinedValue) + { + return JsErrorNotImplemented; + } + +const char* script = + "function patchConsoleObject$$1(global, console, debugConsole) {\n" + "var obj = {};\n" + "for (var fn in console) {\n" + "if (typeof console[fn] === \"function\") {\n" + "(function(name) {\n" + "obj[name] = function(...rest) {\n" + "console[name](rest);\n" + "if (name in debugConsole && typeof debugConsole[name] === \"function\") {\n" + "debugConsole[name](rest);\n" + "}\n" + "}\n" + "})(fn);\n" + "}\n" + "}\n" + "global.console = obj;\n" + "}\n" + "patchConsoleObject$$1;\n"; + + + + JsValueRef patchFunction = JS_INVALID_REFERENCE; + + JsValueRef scriptUrl = JS_INVALID_REFERENCE; + IfFailRet(JsCreateString("", 0, &scriptUrl)); + + JsValueRef scriptContentValue = JS_INVALID_REFERENCE; + IfFailRet(JsCreateString(script, strlen(script), &scriptContentValue)); + IfFailRet(JsRun(scriptContentValue, JS_SOURCE_CONTEXT_NONE, scriptUrl, JsParseScriptAttributeLibraryCode, &patchFunction)); + + JsValueRef args[4] = { undefinedValue, globalObject, consoleObject, debuggerConsoleObject }; + IfFailRet(JsCallFunction(patchFunction, args, _countof(args), nullptr /*no return value*/)); + + return JsNoError; +} + +#endif + static thread_local JsRuntimeTracker tls_runtimeTracker; void ChakraExecutor::initOnJSVMThread() @@ -299,6 +368,20 @@ void ChakraExecutor::initOnJSVMThread() // Add a pointer to ourselves so we can retrieve it later in our hooks JsSetContextData(m_context, this); +#if !defined(USE_EDGEMODE_JSRT) + if (enableDebugging && m_instanceArgs.DebuggerConsoleRedirection) { + JsValueRef debuggerConsoleObject; + tls_runtimeTracker.DebugProtocolHandler->GetConsoleObject(&debuggerConsoleObject); + + JsValueRef undefinedValue = JS_INVALID_REFERENCE; + JsGetUndefinedValue(&undefinedValue); + + if (debuggerConsoleObject != JS_INVALID_REFERENCE && debuggerConsoleObject != undefinedValue) { + needToRedirectConsoleToDebugger = true; + } + } +#endif + installNativeHook<&ChakraExecutor::nativeFlushQueueImmediate>("nativeFlushQueueImmediate"); installNativeHook<&ChakraExecutor::nativeCallSyncHook>("nativeCallSyncHook"); installNativeHook<&ChakraExecutor::nativeLoggingHook>("nativeLoggingHook"); @@ -480,6 +563,18 @@ void ChakraExecutor::loadApplicationScript(std::unique_ptr sc } #endif +#if !defined(USE_EDGEMODE_JSRT) + if (needToRedirectConsoleToDebugger) { + JsValueRef debuggerConsoleObject; + tls_runtimeTracker.DebugProtocolHandler->GetConsoleObject(&debuggerConsoleObject); + + JsErrorCode result = RedirectConsoleToDebugger(debuggerConsoleObject); + if (result == JsNoError) { + needToRedirectConsoleToDebugger = false; + } + } +#endif + // TODO(luk): t13903306 Remove this check once we make native modules working for java2js if (m_delegate) { diff --git a/vnext/Chakra/ChakraExecutor.h b/vnext/Chakra/ChakraExecutor.h index 86b41371736..67c8ba0f18b 100644 --- a/vnext/Chakra/ChakraExecutor.h +++ b/vnext/Chakra/ChakraExecutor.h @@ -158,6 +158,8 @@ class ChakraExecutor : public JSExecutor std::unique_ptr& debugProtocolHandler, std::unique_ptr& debugService); static void CHAKRA_CALLBACK ProcessDebuggerCommandQueueCallback(void* callbackState); void ProcessDebuggerCommandQueue(); + bool needToRedirectConsoleToDebugger = false; + JsErrorCode RedirectConsoleToDebugger(JsValueRef debuggerConsoleObject); #endif void flush(); void flushQueueImmediate(ChakraValue&&); diff --git a/vnext/Chakra/ChakraInstanceArgs.h b/vnext/Chakra/ChakraInstanceArgs.h index 53ec997008b..2f598e6675b 100644 --- a/vnext/Chakra/ChakraInstanceArgs.h +++ b/vnext/Chakra/ChakraInstanceArgs.h @@ -51,6 +51,11 @@ struct ChakraInstanceArgs */ bool EnableNativePerformanceNow { true }; + /** + * @brief Whether to enable ChakraCore console redirection to debugger. + */ + bool DebuggerConsoleRedirection { false }; + /** * @brief Port number to use when debugging. */ diff --git a/vnext/ReactWindowsCore/DevSettings.h b/vnext/ReactWindowsCore/DevSettings.h index 16e9a811e95..1bcedf1bee8 100644 --- a/vnext/ReactWindowsCore/DevSettings.h +++ b/vnext/ReactWindowsCore/DevSettings.h @@ -66,6 +66,9 @@ struct DevSettings /// Debugging will start as soon as the react native instance is loaded. bool useWebDebugger{ false }; + // Enables ChakraCore console redirection to debugger + bool debuggerConsoleRedirection{ false }; + /// Dispatcher for notifications about JS engine memory consumption. std::shared_ptr memoryTracker; diff --git a/vnext/Shared/OInstance.cpp b/vnext/Shared/OInstance.cpp index 0160e68b3d4..560b6a05dc3 100644 --- a/vnext/Shared/OInstance.cpp +++ b/vnext/Shared/OInstance.cpp @@ -387,7 +387,8 @@ InstanceImpl::InstanceImpl(std::string&& jsBundleBasePath, instanceArgs.LoggingCallback = m_devSettings->loggingCallback; instanceArgs.EnableNativePerformanceNow = m_devSettings->enableNativePerformanceNow; - + instanceArgs.DebuggerConsoleRedirection = m_devSettings->debuggerConsoleRedirection; + if (!m_devSettings->useJITCompilation) { #if (defined(_MSC_VER) && !defined(WINRT))