UNPKG

objj-runtime

Version:

JavaScript (ECMAScript) and Objective-J runtime

279 lines (241 loc) 8.3 kB
/* * Debug.js * Objective-J * * Created by Thomas Robinson. * Copyright 2008-2010, 280 North, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef BROWSER CPLogRegister(CPLogDefault); #endif // formatting helpers function objj_debug_object_format(aReceiver) { return (aReceiver && aReceiver.isa) ? exports.sprintf("<%s %#08x>", GETMETA(aReceiver).name, aReceiver._UID) : String(aReceiver); } function objj_debug_message_format(aReceiver, aSelector) { return exports.sprintf("[%s %s]", objj_debug_object_format(aReceiver), aSelector); } // save the original msgSend implementations so we can restore them later var objj_msgSend_original = objj_msgSend, objj_msgSendSuper_original = objj_msgSendSuper, objj_msgSendFast_original = objj_msgSendFast, objj_msgSendFast0_original = objj_msgSendFast0, objj_msgSendFast1_original = objj_msgSendFast1, objj_msgSendFast2_original = objj_msgSendFast2, objj_msgSendFast3_original = objj_msgSendFast3; function objj_msgSend_reset_all_classes() { objj_enumerateClassesUsingBlock(function(aClass) { if (aClass.hasOwnProperty("objj_msgSend")) { aClass.objj_msgSend = objj_msgSendFast; aClass.objj_msgSend0 = objj_msgSendFast0; aClass.objj_msgSend1 = objj_msgSendFast1; aClass.objj_msgSend2 = objj_msgSendFast2; aClass.objj_msgSend3 = objj_msgSendFast3; } }); } // decorator management functions // reset to default objj_msgSend* implementations GLOBAL(objj_msgSend_reset) = function() { objj_msgSend = objj_msgSend_original; objj_msgSendSuper = objj_msgSendSuper_original; objj_msgSendFast = objj_msgSendFast_original; objj_msgSendFast0 = objj_msgSendFast0_original; objj_msgSendFast1 = objj_msgSendFast1_original; objj_msgSendFast2 = objj_msgSendFast2_original; objj_msgSendFast3 = objj_msgSendFast3_original; objj_msgSend_reset_all_classes(); } // decorate both objj_msgSend and objj_msgSendSuper GLOBAL(objj_msgSend_decorate) = function() { var index = 0, count = arguments.length; for (; index < count; ++index) { objj_msgSend = arguments[index](objj_msgSend); objj_msgSendSuper = arguments[index](objj_msgSendSuper); objj_msgSendFast = arguments[index](objj_msgSendFast); objj_msgSendFast0 = arguments[index](objj_msgSendFast0); objj_msgSendFast1 = arguments[index](objj_msgSendFast1); objj_msgSendFast2 = arguments[index](objj_msgSendFast2); objj_msgSendFast3 = arguments[index](objj_msgSendFast3); } if (count) objj_msgSend_reset_all_classes(); } // reset then decorate both objj_msgSend and objj_msgSendSuper GLOBAL(objj_msgSend_set_decorators) = function() { objj_msgSend_reset(); objj_msgSend_decorate.apply(NULL, arguments); } // backtrace decorator var objj_backtrace = []; GLOBAL(objj_backtrace_print) = function(/*Callable*/ aStream) { var index = 0, count = objj_backtrace.length; for (; index < count; ++index) { var frame = objj_backtrace[index]; aStream(objj_debug_message_format(frame.receiver, frame.selector)); } } GLOBAL(objj_backtrace_decorator) = function(msgSend) { return function(aReceiverOrSuper, aSelector) { var aReceiver = aReceiverOrSuper && (aReceiverOrSuper.receiver || aReceiverOrSuper); // push the receiver and selector onto the backtrace stack objj_backtrace.push({ receiver: aReceiver, selector : aSelector }); try { return msgSend.apply(this, arguments); } catch (anException) { if (objj_backtrace.length) { // print the exception and backtrace CPLog.warn("Exception " + anException + " in " + objj_debug_message_format(aReceiver, aSelector)); objj_backtrace_print(CPLog.warn); objj_backtrace = []; } // re-throw the exception throw anException; } finally { // make sure to always pop objj_backtrace.pop(); } }; } GLOBAL(objj_supress_exceptions_decorator) = function(msgSend) { return function(aReceiverOrSuper, aSelector) { var aReceiver = aReceiverOrSuper && (aReceiverOrSuper.receiver || aReceiverOrSuper); try { return msgSend.apply(this, arguments); } catch (anException) { // print the exception and backtrace CPLog.warn("Exception " + anException + " in " + objj_debug_message_format(aReceiver, aSelector)); } }; } // type checking decorator var objj_typechecks_reported = {}, objj_typecheck_prints_backtrace = NO; GLOBAL(objj_typecheck_decorator) = function(msgSend) { return function(aReceiverOrSuper, aSelector) { var aReceiver = aReceiverOrSuper && (aReceiverOrSuper.receiver || aReceiverOrSuper); if (!aReceiver) return msgSend.apply(this, arguments); var types = aReceiver.isa.method_dtable[aSelector].method_types; for (var i = 2; i < arguments.length; i++) { try { objj_debug_typecheck(types[i-1], arguments[i]); } catch (e) { var key = [GETMETA(aReceiver).name, aSelector, i, e].join(";"); if (!objj_typechecks_reported[key]) { objj_typechecks_reported[key] = YES; CPLog.warn("Type check failed on argument " + (i-2) + " of " + objj_debug_message_format(aReceiver, aSelector) + ": " + e); if (objj_typecheck_prints_backtrace) objj_backtrace_print(CPLog.warn); } } } var result = msgSend.apply(NULL, arguments); try { objj_debug_typecheck(types[0], result); } catch (e) { var key = [GETMETA(aReceiver).name, aSelector, "ret", e].join(";"); if (!objj_typechecks_reported[key]) { objj_typechecks_reported[key] = YES; CPLog.warn("Type check failed on return val of " + objj_debug_message_format(aReceiver, aSelector) + ": " + e); if (objj_typecheck_prints_backtrace) objj_backtrace_print(CPLog.warn); } } return result; }; } // type checking logic: GLOBAL(objj_debug_typecheck) = function(expectedType, object) { var objjClass; if (!expectedType) { return; } else if (expectedType === "id") { if (object !== undefined) return; } else if (expectedType === "void") { if (object === undefined) return; } else if (objjClass = objj_getClass(expectedType)) { if (object === nil) { return; } else if (object !== undefined && object.isa) { var theClass = object.isa; for (; theClass; theClass = theClass.super_class) if (theClass === objjClass) return; } } else { return; } var actualType; if (object === NULL) actualType = "null"; else if (object === undefined) actualType = "void"; else if (object.isa) actualType = GETMETA(object).name; else actualType = typeof object; throw ("expected=" + expectedType + ", actual=" + actualType); };