mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 04:57:03 +00:00
246 lines
6.8 KiB
JavaScript
246 lines
6.8 KiB
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;
|
|
|
|
// decorator management functions
|
|
|
|
// reset to default objj_msgSend* implementations
|
|
GLOBAL(objj_msgSend_reset) = function()
|
|
{
|
|
objj_msgSend = objj_msgSend_original;
|
|
objj_msgSendSuper = objj_msgSendSuper_original;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
// 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(NULL, 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(NULL, 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(NULL, arguments);
|
|
|
|
var types = aReceiver.isa.method_dtable[aSelector].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);
|
|
}
|