objj-runtime
Version:
JavaScript (ECMAScript) and Objective-J runtime
279 lines (241 loc) • 8.3 kB
JavaScript
/*
* 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);
};