diff --git a/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js index 7f1b276b3..6891f509f 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js +++ b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js @@ -11,41 +11,43 @@ */ 'use strict'; -let exceptionID = 0; - /** * Handles the developer-visible aspect of errors and exceptions */ +let exceptionID = 0; function reportException(e: Error, isFatal: bool) { - const parseErrorStack = require('parseErrorStack'); - const symbolicateStackTrace = require('symbolicateStackTrace'); - const RCTExceptionsManager = require('NativeModules').ExceptionsManager; - - const currentExceptionID = ++exceptionID; - if (RCTExceptionsManager) { + const {ExceptionsManager} = require('NativeModules'); + if (ExceptionsManager) { + const parseErrorStack = require('parseErrorStack'); const stack = parseErrorStack(e); + const currentExceptionID = ++exceptionID; if (isFatal) { - RCTExceptionsManager.reportFatalException(e.message, stack, currentExceptionID); + ExceptionsManager.reportFatalException(e.message, stack, currentExceptionID); } else { - RCTExceptionsManager.reportSoftException(e.message, stack, currentExceptionID); + ExceptionsManager.reportSoftException(e.message, stack, currentExceptionID); } if (__DEV__) { + const symbolicateStackTrace = require('symbolicateStackTrace'); symbolicateStackTrace(stack).then( (prettyStack) => { if (prettyStack) { - RCTExceptionsManager.updateExceptionMessage(e.message, prettyStack, currentExceptionID); + ExceptionsManager.updateExceptionMessage(e.message, prettyStack, currentExceptionID); } else { throw new Error('The stack is null'); } } ).catch( - (error) => - console.warn('Unable to symbolicate stack trace: ' + error.message) + (error) => console.warn('Unable to symbolicate stack trace: ' + error.message) ); } } } +declare var console: typeof console & { + _errorOriginal: Function, + reportErrorsAsExceptions: boolean, +}; + /** * Logs exceptions to the (native) console and displays them */ @@ -57,8 +59,7 @@ function handleException(e: Error, isFatal: boolean) { if (!e.message) { e = new Error(e); } - - if (typeof console._errorOriginal === 'function') { + if (console._errorOriginal) { console._errorOriginal(e.message); } else { console.error(e.message); @@ -66,6 +67,29 @@ function handleException(e: Error, isFatal: boolean) { reportException(e, isFatal); } +function reactConsoleErrorHandler() { + console._errorOriginal.apply(console, arguments); + if (!console.reportErrorsAsExceptions) { + return; + } + + if (arguments[0] && arguments[0].stack) { + reportException(arguments[0], /* isFatal */ false); + } else { + const stringifySafe = require('stringifySafe'); + const str = Array.prototype.map.call(arguments, stringifySafe).join(', '); + if (str.slice(0, 10) === '"Warning: ') { + // React warnings use console.error so that a stack trace is shown, but + // we don't (currently) want these to show a redbox + // (Note: Logic duplicated in polyfills/console.js.) + return; + } + const error : any = new Error('console.error: ' + str); + error.framesToPop = 1; + reportException(error, /* isFatal */ false); + } +} + /** * Shows a redbox with stacktrace for all console.error messages. Disable by * setting `console.reportErrorsAsExceptions = false;` in your app. @@ -76,33 +100,12 @@ function installConsoleErrorReporter() { return; // already installed } // Flow doesn't like it when you set arbitrary values on a global object - (console: any)._errorOriginal = console.error.bind(console); - console.error = function reactConsoleError() { - // Flow doesn't like it when you set arbitrary values on a global object - (console: any)._errorOriginal.apply(null, arguments); - if (!console.reportErrorsAsExceptions) { - return; - } - - if (arguments[0] && arguments[0].stack) { - reportException(arguments[0], /* isFatal */ false); - } else { - const stringifySafe = require('stringifySafe'); - const str = Array.prototype.map.call(arguments, stringifySafe).join(', '); - if (str.slice(0, 10) === '"Warning: ') { - // React warnings use console.error so that a stack trace is shown, but - // we don't (currently) want these to show a redbox - // (Note: Logic duplicated in polyfills/console.js.) - return; - } - const error : any = new Error('console.error: ' + str); - error.framesToPop = 1; - reportException(error, /* isFatal */ false); - } - }; + console._errorOriginal = console.error.bind(console); + console.error = reactConsoleErrorHandler; if (console.reportErrorsAsExceptions === undefined) { + // Individual apps can disable this // Flow doesn't like it when you set arbitrary values on a global object - (console: any).reportErrorsAsExceptions = true; // Individual apps can disable this + console.reportErrorsAsExceptions = true; } } diff --git a/packager/react-packager/src/Resolver/polyfills/console.js b/packager/react-packager/src/Resolver/polyfills/console.js index 33b853b2e..ed603997c 100644 --- a/packager/react-packager/src/Resolver/polyfills/console.js +++ b/packager/react-packager/src/Resolver/polyfills/console.js @@ -16,7 +16,7 @@ /* eslint-disable */ -var inspect = (function() { +const inspect = (function() { // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -356,8 +356,8 @@ var inspect = (function() { })(); -var OBJECT_COLUMN_NAME = '(index)'; -var LOG_LEVELS = { +const OBJECT_COLUMN_NAME = '(index)'; +const LOG_LEVELS = { trace: 0, info: 1, warn: 2, @@ -371,7 +371,7 @@ function setupConsole(global) { function getNativeLogFunction(level) { return function() { - var str; + let str; if (arguments.length === 1 && typeof arguments[0] === 'string') { str = arguments[0]; } else { @@ -380,7 +380,7 @@ function setupConsole(global) { }).join(', '); } - var logLevel = level; + let logLevel = level; if (str.slice(0, 9) === 'Warning: ' && logLevel >= LOG_LEVELS.error) { // React warnings use console.error so that a stack trace is shown, // but we don't (currently) want these to show a redbox @@ -391,7 +391,7 @@ function setupConsole(global) { }; } - var repeat = function(element, n) { + function repeat(element, n) { return Array.apply(null, Array(n)).map(function() { return element; }); }; @@ -431,7 +431,7 @@ function setupConsole(global) { // Join all elements in the row into a single string with | separators // (appends extra spaces to each cell to make separators | alligned) - var joinRow = function(row, space) { + function joinRow(row, space) { var cells = row.map(function(cell, i) { var extraSpaces = repeat(' ', columnWidths[i] - cell.length).join(''); return cell + extraSpaces; @@ -465,7 +465,7 @@ function setupConsole(global) { Object.defineProperty(global, 'originalConsole', descriptor); } - var console = { + global.console = { error: getNativeLogFunction(LOG_LEVELS.error), info: getNativeLogFunction(LOG_LEVELS.info), log: getNativeLogFunction(LOG_LEVELS.info), @@ -475,14 +475,6 @@ function setupConsole(global) { table: consoleTablePolyfill }; - // don't reassign to the original descriptor. breaks on ios7 - Object.defineProperty(global, 'console', { - value: console, - configurable: descriptor ? descriptor.configurable : true, - enumerable: descriptor ? descriptor.enumerable : true, - writable: descriptor ? descriptor.writable : true, - }); - // If available, also call the original `console` method since that is // sometimes useful. Ex: on OS X, this will let you see rich output in // the Safari Web Inspector console. diff --git a/packager/react-packager/src/Resolver/polyfills/error-guard.js b/packager/react-packager/src/Resolver/polyfills/error-guard.js index eaeea1f38..8b9697b0a 100644 --- a/packager/react-packager/src/Resolver/polyfills/error-guard.js +++ b/packager/react-packager/src/Resolver/polyfills/error-guard.js @@ -13,30 +13,40 @@ * before any of the modules, this ErrorUtils must be defined (and the handler * set) globally before requiring anything. */ + /* eslint strict:0 */ -var ErrorUtils = { - _inGuard: 0, - _globalHandler: null, +const _inGuard = 0; + +/** + * This is the error handler that is called when we encounter an exception + * when loading a module. This will report any errors encountered before + * ExceptionsManager is configured. + */ +const _globalHandler = function onError(e) { + throw e; +}; + +const ErrorUtils = { setGlobalHandler: function(fun) { - ErrorUtils._globalHandler = fun; + _globalHandler = fun; }, getGlobalHandler: function() { - return ErrorUtils._globalHandler; + return _globalHandler; }, reportError: function(error) { - ErrorUtils._globalHandler && ErrorUtils._globalHandler(error); + _globalHandler && _globalHandler(error); }, reportFatalError: function(error) { - ErrorUtils._globalHandler && ErrorUtils._globalHandler(error, true); + _globalHandler && _globalHandler(error, true); }, applyWithGuard: function(fun, context, args) { try { - ErrorUtils._inGuard++; + _inGuard++; return fun.apply(context, args); } catch (e) { ErrorUtils.reportError(e); } finally { - ErrorUtils._inGuard--; + _inGuard--; } }, applyWithGuardIfNeeded: function(fun, context, args) { @@ -47,7 +57,7 @@ var ErrorUtils = { } }, inGuard: function() { - return ErrorUtils._inGuard; + return _inGuard; }, guard: function(fun, name, context) { if (typeof fun !== 'function') { @@ -70,18 +80,5 @@ var ErrorUtils = { return guarded; } }; + global.ErrorUtils = ErrorUtils; - -/** - * This is the error handler that is called when we encounter an exception - * when loading a module. This will report any errors encountered before - * ExceptionsManager is configured. - */ -function setupErrorGuard() { - var onError = function(e) { - throw e; - }; - global.ErrorUtils.setGlobalHandler(onError); -} - -setupErrorGuard();