Cleaned up JS debug helpers, made them easier to use and added some basic documentation.

git-svn-id: http://svn.berlios.de/svnroot/repos/oolite-linux/trunk@4071 127b21dd-08f5-0310-b4b7-95ae10353056
master
Jens Ayton 2011-01-15 14:25:49 +00:00
parent 5bea193153
commit 676ed097e9
7 changed files with 334 additions and 106 deletions

View File

@ -599,6 +599,7 @@
1ABDBA3B0EB365D90086BC3D /* OOIsNumberLiteral.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ABDBA390EB365D90086BC3D /* OOIsNumberLiteral.m */; };
1ABDBA3C0EB365D90086BC3D /* OOIsNumberLiteral.h in Headers */ = {isa = PBXBuildFile; fileRef = 1ABDBA3A0EB365D90086BC3D /* OOIsNumberLiteral.h */; };
1ABF219B12D691B300075821 /* Oolite.docktileplugin in Copy Plug-ins */ = {isa = PBXBuildFile; fileRef = 1A01BCE711C569DF0011197F /* Oolite.docktileplugin */; };
1AC0F29E12E1DADC00ECBBB0 /* OOJSEngineDebuggerHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 1AC0F29D12E1DADC00ECBBB0 /* OOJSEngineDebuggerHelpers.m */; };
1AC27A0F0EA7E9940054E5F0 /* OOJSEquipmentInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 1AC27A0D0EA7E9940054E5F0 /* OOJSEquipmentInfo.h */; };
1AC27A100EA7E9940054E5F0 /* OOJSEquipmentInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 1AC27A0E0EA7E9940054E5F0 /* OOJSEquipmentInfo.m */; };
1AC544FA0D4D217900C90E5B /* oolite-font.png in Copy Textures */ = {isa = PBXBuildFile; fileRef = 1AC544F90D4D217900C90E5B /* oolite-font.png */; };
@ -1830,6 +1831,7 @@
1AC0E9490B974DC200C46994 /* LICENSE.TXT */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE.TXT; sourceTree = "<group>"; };
1AC0E94A0B974DC200C46994 /* PORTING.TXT */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = PORTING.TXT; sourceTree = "<group>"; };
1AC0E94B0B974DC200C46994 /* README_LINUX.TXT */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README_LINUX.TXT; sourceTree = "<group>"; };
1AC0F29D12E1DADC00ECBBB0 /* OOJSEngineDebuggerHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OOJSEngineDebuggerHelpers.m; sourceTree = "<group>"; };
1AC27A0D0EA7E9940054E5F0 /* OOJSEquipmentInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OOJSEquipmentInfo.h; sourceTree = "<group>"; };
1AC27A0E0EA7E9940054E5F0 /* OOJSEquipmentInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OOJSEquipmentInfo.m; sourceTree = "<group>"; };
1AC544F90D4D217900C90E5B /* oolite-font.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "oolite-font.png"; sourceTree = "<group>"; };
@ -2563,6 +2565,7 @@
2B4CDFEB107B3D8400526C98 /* OOJSManifest.m */,
1A5DBA9A0BC000DC00D57389 /* OOJavaScriptEngine.h */,
1A5DBA9B0BC000DC00D57389 /* OOJavaScriptEngine.m */,
1AC0F29D12E1DADC00ECBBB0 /* OOJSEngineDebuggerHelpers.m */,
1A11C2AF11CFC35000F3EE77 /* OOJSEngineTimeManagement.h */,
1A11C2B011CFC35000F3EE77 /* OOJSEngineTimeManagement.m */,
1ABC47FC0F155F0500B977AD /* OOJSFunction.h */,
@ -3857,6 +3860,7 @@
1A09EF4412BD0BCA00BF7F48 /* PlayerEntityStickMapper.m in Sources */,
1AB5E1F012BD628500C334DD /* OOJoystickManager.m in Sources */,
1A0942CE12D7D5B9003B6273 /* OOJSFrameCallbacks.m in Sources */,
1AC0F29E12E1DADC00ECBBB0 /* OOJSEngineDebuggerHelpers.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -67,3 +67,6 @@ _JSObjectToStrDbg
_JSStringToStrDbg
_JSValueTypeDbg
_JSValueToStrSafeDbg
_JSObjectToStrSafeDbg
_JSStringToStrSafeDbg
_JSIDToStrSafeDbg

View File

@ -72,3 +72,6 @@ _JSObjectToStrDbg
_JSStringToStrDbg
_JSValueTypeDbg
_JSValueToStrSafeDbg
_JSObjectToStrSafeDbg
_JSStringToStrSafeDbg
_JSIDToStrSafeDbg

View File

@ -0,0 +1,237 @@
/*
OOJSEngineDebuggerHelpers.m
JavaScript support for Oolite
Copyright (C) 2007-2011 David Taylor and Jens Ayton.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
/*
These functions exist to help debugging JavaScript code. They can be called
directly from gdb, for example:
call (char *)JSValueToStrDbg(someValue)
The functions are:
const char *JSValueToStrDbg(jsval)
const char *JSObjectToStrDbg(JSObject *)
const char *JSStringToStrDbg(JSString *)
Converts any JS value/object/JSString to a string, using the complete
process and potentially calling into SpiderMonkey with a secondory
contenxt and invoking JS toString() methods. This might mess up
SpiderMonkey internal state in some cases.
const char *JSValueToStrSafeDbg(jsval)
const char *JSObjectToStrSafeDbg(JSObject *)
const char *JSStringToStrSafeDbg(JSString *)
As above, but without calling into SpiderMonkey functions that require
a context. In particular, as of the FF4b9 version of SpiderMonkey only
interned strings can be converted, and for objects only the class name
is provided.
const char *JSIDToStrSafeDbg(jsid)
Like JSValueToStrSafeDbg() for jsids. (String jsids must always be
interned, so this is generally sufficient.)
const char *JSValueTypeDbg(jsval)
Returns the type of the jsval, or the class name if it's an object.
All dynamic strings are autorelease.
Another useful function is OOJSDumpStack (results are found in the log):
call OOJSDumpStack(context)
A set of macros can be found in tools/gdb-macros.txt.
In addition to calling them from the debug console, Xcode users might want
to use them in data formatters (by double-clicking the "Summary" field for
a variable of the appropriate type). I recommend the following:
jsval: {JSValueToStrSafeDbg($VAR)}:s
jsval*: {JSValueToStrSafeDbg(*$VAR)}:s
jsid: {JSIDToStrSafeDbg($VAR)}:s
JSObject*: {JSObjectToStrSafeDbg($VAR)}:s
JSString*: {JSStringToStrSafeDbg($VAR)}:s
These, and a variety of Oolite type formatters, can be set up using
Mac-specific/DataFormatters.
*/
#ifndef NDEBUG
#import "OOJavaScriptEngine.h"
#pragma GCC visibility push (default)
const char *JSValueToStrDbg(jsval val)
{
OOJavaScriptEngine *jsEngine = [OOJavaScriptEngine sharedEngine];
JSContext *context = [jsEngine acquireContext];
JS_BeginRequest(context);
const char *result = [[NSString stringWithJavaScriptValue:val inContext:context] UTF8String];
JS_EndRequest(context);
[jsEngine releaseContext:context];
return result;
}
const char *JSObjectToStrDbg(JSObject *obj)
{
if (obj == NULL) return "null";
return JSValueToStrDbg(OBJECT_TO_JSVAL(obj));
}
const char *JSStringToStrDbg(JSString *str)
{
if (str == NULL) return "null";
return JSValueToStrDbg(STRING_TO_JSVAL(str));
}
const char *JSValueTypeDbg(jsval val)
{
if (JSVAL_IS_INT(val)) return "integer";
if (JSVAL_IS_DOUBLE(val)) return "double";
if (JSVAL_IS_STRING(val)) return "string";
if (JSVAL_IS_BOOLEAN(val)) return "boolean";
if (JSVAL_IS_NULL(val)) return "null";
if (JSVAL_IS_VOID(val)) return "void";
#if OO_NEW_JS && defined(JS_USE_JSVAL_JSID_STRUCT_TYPES)
if (JSVAL_IS_MAGIC_IMPL(val))
{
switch(val.s.payload.why)
{
case JS_ARRAY_HOLE: return "magic (array hole)";
case JS_ARGS_HOLE: return "magic (args hole)";
case JS_NATIVE_ENUMERATE: return "magic (native enumerate)";
case JS_NO_ITER_VALUE: return "magic (no iter value)";
case JS_GENERATOR_CLOSING: return "magic (generator closing)";
case JS_NO_CONSTANT: return "magic (no constant)";
case JS_THIS_POISON: return "magic (this poison)";
case JS_ARG_POISON: return "magic (arg poison)";
case JS_SERIALIZE_NO_NODE: return "magic (serialize no node)";
case JS_GENERIC_MAGIC: return "magic (generic)";
};
return "magic";
}
#endif
if (JSVAL_IS_OBJECT(val)) return OOJSGetClass(NULL, JSVAL_TO_OBJECT(val))->name; // Fun fact: although a context is required if JS_THREADSAFE is defined, it isn't actually used.
return "unknown";
}
// Doesn't follow pointers, mess with requests or otherwise poke the SpiderMonkey.
const char *JSValueToStrSafeDbg(jsval val)
{
NSString *formatted = nil;
if (JSVAL_IS_INT(val)) formatted = [NSString stringWithFormat:@"%i", (long)JSVAL_TO_INT(val)];
else if (JSVAL_IS_DOUBLE(val)) formatted = [NSString stringWithFormat:@"%g", JSVAL_TO_DOUBLE(val)];
else if (JSVAL_IS_BOOLEAN(val)) formatted = (JSVAL_TO_BOOLEAN(val)) ? @"true" : @"false";
else if (JSVAL_IS_STRING(val))
{
JSString *string = JSVAL_TO_STRING(val);
const jschar *chars = NULL;
size_t length = JS_GetStringLength(string);
#if OO_NEW_JS && OOJS_FF4B9
if (JS_StringHasBeenInterned(string))
{
chars = JS_GetInternedStringChars(string);
}
// Flat strings can be extracted without a context, but cannot be detected.
#else
chars = JS_GetStringChars(string);
#endif
if (chars == NULL) formatted = [NSString stringWithFormat:@"string [%zu chars]", length];
else formatted = [NSString stringWithCharacters:chars length:length];
}
else if (JSVAL_IS_VOID(val)) return "undefined";
else return JSValueTypeDbg(val);
return [formatted UTF8String];
}
const char *JSObjectToStrSafeDbg(JSObject *obj)
{
if (obj == NULL) return "null";
return JSValueToStrSafeDbg(OBJECT_TO_JSVAL(obj));
}
const char *JSStringToStrSafeDbg(JSString *str)
{
if (str == NULL) return "null";
return JSValueToStrSafeDbg(STRING_TO_JSVAL(str));
}
const char *JSIDToStrSafeDbg(jsid anID)
{
NSString *formatted = nil;
if (JSID_IS_INT(anID)) formatted = [NSString stringWithFormat:@"%i", (long)JSID_TO_INT(anID)];
else if (JSID_IS_VOID(anID)) return "void";
else if (JSID_IS_EMPTY(anID)) return "empty";
else if (JSID_IS_ZERO(anID)) return "0";
else if (JSID_IS_OBJECT(anID)) return OOJSGetClass(NULL, JSID_TO_OBJECT(anID))->name;
else if (JSID_IS_DEFAULT_XML_NAMESPACE(anID)) return "default XML namespace";
else if (JSID_IS_STRING(anID))
{
JSString *string = JSID_TO_STRING(anID);
const jschar *chars = NULL;
size_t length = JS_GetStringLength(string);
#if OO_NEW_JS && OOJS_FF4B9
if (JS_StringHasBeenInterned(string))
{
chars = JS_GetInternedStringChars(string);
}
else
{
// Bug; jsid strings must be interned.
return "*** uninterned string in jsid! ***";
}
#else
chars = JS_GetStringChars(string);
#endif
formatted = [NSString stringWithFormat:@"\"%@\"", [NSString stringWithCharacters:chars length:length]];
}
else
{
formatted = [NSString stringWithFormat:@"unknown <0x%llX>", (long long)JSID_BITS(anID)];
}
return [formatted UTF8String];
}
#endif

View File

@ -769,11 +769,6 @@ OOINLINE const jschar *OOJSGetStringCharsAndLength(JSContext *context, JSString
}
#if !OOJS_FF4B9
OOINLINE const jschar *JS_GetInternedStringChars(JSString *string) { return NULL; }
#endif
#define OOJSVAL_TO_DOUBLE JSVAL_TO_DOUBLE
#else
// In old API, jsvals could be pointers to doubles; in new, they're actual doubles.
@ -789,9 +784,6 @@ OOINLINE const jschar *OOJSGetStringCharsAndLength(JSContext *context, JSString
*length = JS_GetStringLength(string);
return JS_GetStringChars(string);
}
OOINLINE const jschar *JS_GetInternedStringChars(JSString *string) { return NULL; }
#endif

View File

@ -1611,104 +1611,6 @@ NSString *OOJSDebugDescribe(JSContext *context, jsval value)
@end
#ifndef NDEBUG
// For use in debugger
const char *JSValueToStrDbg(jsval val)
{
OOJavaScriptEngine *jsEngine = [OOJavaScriptEngine sharedEngine];
JSContext *context = [jsEngine acquireContext];
JS_BeginRequest(context);
const char *result = [[NSString stringWithJavaScriptValue:val inContext:context] UTF8String];
JS_EndRequest(context);
[jsEngine releaseContext:context];
return result;
}
const char *JSObjectToStrDbg(JSObject *obj)
{
if (obj == NULL) return "null";
return JSValueToStrDbg(OBJECT_TO_JSVAL(obj));
}
const char *JSValueTypeDbg(jsval val)
{
if (JSVAL_IS_INT(val)) return "integer";
if (JSVAL_IS_DOUBLE(val)) return "double";
if (JSVAL_IS_STRING(val)) return "string";
if (JSVAL_IS_BOOLEAN(val)) return "boolean";
if (JSVAL_IS_NULL(val)) return "null";
if (JSVAL_IS_VOID(val)) return "void";
#if OO_NEW_JS && defined(JS_USE_JSVAL_JSID_STRUCT_TYPES)
if (JSVAL_IS_MAGIC_IMPL(val))
{
switch(val.s.payload.why)
{
case JS_ARRAY_HOLE: return "magic (array hole)";
case JS_ARGS_HOLE: return "magic (args hole)";
case JS_NATIVE_ENUMERATE: return "magic (native enumerate)";
case JS_NO_ITER_VALUE: return "magic (no iter value)";
case JS_GENERATOR_CLOSING: return "magic (generator closing)";
case JS_NO_CONSTANT: return "magic (no constant)";
case JS_THIS_POISON: return "magic (this poison)";
case JS_ARG_POISON: return "magic (arg poison)";
case JS_SERIALIZE_NO_NODE: return "magic (serialize no node)";
case JS_GENERIC_MAGIC: return "magic (generic)";
};
return "magic";
}
#endif
if (JSVAL_IS_OBJECT(val)) return OOJSGetClass(NULL, JSVAL_TO_OBJECT(val))->name; // Fun fact: although a context is required if JS_THREADSAFE is defined, it isn't actually used.
return "unknown";
}
// Doesn't follow pointers, mess with requests or otherwise poke the SpiderMonkey.
const char *JSValueToStrSafeDbg(jsval val)
{
NSString *formatted = nil;
if (JSVAL_IS_INT(val)) formatted = [NSString stringWithFormat:@"%i", (long)JSVAL_TO_INT(val)];
else if (JSVAL_IS_DOUBLE(val)) formatted = [NSString stringWithFormat:@"%g", JSVAL_TO_DOUBLE(val)];
else if (JSVAL_IS_BOOLEAN(val)) formatted = (JSVAL_TO_BOOLEAN(val)) ? @"true" : @"false";
else if (JSVAL_IS_STRING(val))
{
JSString *string = JSVAL_TO_STRING(val);
const jschar *chars = NULL;
size_t length = JS_GetStringLength(string);
#if OO_NEW_JS
if (JS_StringHasBeenInterned(string))
{
chars = JS_GetInternedStringChars(string);
}
// Flat strings can be extracted without a context, but cannot be detected.
#endif
if (chars == NULL) formatted = [NSString stringWithFormat:@"string [%zu chars]", length];
else formatted = [NSString stringWithCharacters:chars length:length];
}
else if (JSVAL_IS_VOID(val)) return "undefined";
else return JSValueTypeDbg(val);
return [formatted UTF8String];
}
const char *JSStringToStrDbg(JSString *str)
{
if (str == NULL) return "null";
return JSValueToStrSafeDbg(STRING_TO_JSVAL(str));
}
#endif
@implementation NSArray (OOJavaScriptConversion)
- (jsval)oo_jsValueInContext:(JSContext *)context

87
tools/gdb-macros.txt Normal file
View File

@ -0,0 +1,87 @@
# Oolite gdb conveniences
# To use, copy into ~/.gdbinit.
define pjsv
call (char *)JSValueToStrSafeDbg($arg0)
end
document psjv
pjsv <value>
Oolite
Print a description of the specified JavaScript value (jsval).
See also: pjso, pjss, pjsid, pjsvfull
end
define pjso
call (char *)JSObjectToStrSafeDbg($arg0)
end
document pjso
pjso <value>
Oolite
Print a description of the specified JavaScript object (JSObject *).
See also: pjsv, pjss, pjsid, pjsofull
end
define pjss
call (char *)JSStringToStrSafeDbg($arg0)
end
document pjss
pjss <value>
Oolite
Print a description of the specified JavaScript string (JSString *).
See also: pjsv, pjso, pjsid, pjssfull
end
define pjsvfull
call (char *)JSValueToStrDbg($arg0)
end
document pjsvfull
pjsvfull <value>
Oolite
Print a description of the specified JavaScript value (jsval), even if this
involves calling into SpiderMonkey and possibly invoking JS toString() methods.
See also: pjsofull, pjssfull, pjsv
end
define pjso
call (char *)JSObjectToStrSafeDbg($arg0)
end
document pjsofull
pjsofull <value>
Oolite
Print a description of the specified JavaScript object (JSObject *), even if
this involves calling into SpiderMonkey and possibly invoking JS toString()
methods.
See also: pjsvfull, pjssfull, pjso
end
define pjss
call (char *)JSStringToStrSafeDbg($arg0)
end
document pjssfull
pjssfull <value>
Oolite
Print a description of the specified JavaScript string (JSString *), even if
this involves calling into SpiderMonkey and possibly invoking JS toString()
methods.
See also: pjsvfull, pjsofull, pjss
end
define pjsid
call (char *)JSIDToStrSafeDbg($arg0)
end
document
pjsid <value>
Oolite
Print a description of the specified JavaScript identifier (jsid).
See also: pjsv, pjso, pjss
end
define jsstack
call OOJSDumpStack(context)
end
document jsstack
jsstack
Oolite
Dump the current JavaScript call stack to the log.
end