From 676ed097e9b2bdeb38d147da581e959c7a8a1051 Mon Sep 17 00:00:00 2001 From: Jens Ayton Date: Sat, 15 Jan 2011 14:25:49 +0000 Subject: [PATCH] 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 --- Oolite.xcodeproj/project.pbxproj | 4 + src/Cocoa/exports-debug-32.exp | 3 + src/Cocoa/exports-debug-64.exp | 3 + .../Scripting/OOJSEngineDebuggerHelpers.m | 237 ++++++++++++++++++ src/Core/Scripting/OOJavaScriptEngine.h | 8 - src/Core/Scripting/OOJavaScriptEngine.m | 98 -------- tools/gdb-macros.txt | 87 +++++++ 7 files changed, 334 insertions(+), 106 deletions(-) create mode 100644 src/Core/Scripting/OOJSEngineDebuggerHelpers.m create mode 100644 tools/gdb-macros.txt diff --git a/Oolite.xcodeproj/project.pbxproj b/Oolite.xcodeproj/project.pbxproj index a0a9ab69..c9a56f45 100644 --- a/Oolite.xcodeproj/project.pbxproj +++ b/Oolite.xcodeproj/project.pbxproj @@ -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 = ""; }; 1AC0E94A0B974DC200C46994 /* PORTING.TXT */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = PORTING.TXT; sourceTree = ""; }; 1AC0E94B0B974DC200C46994 /* README_LINUX.TXT */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README_LINUX.TXT; sourceTree = ""; }; + 1AC0F29D12E1DADC00ECBBB0 /* OOJSEngineDebuggerHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OOJSEngineDebuggerHelpers.m; sourceTree = ""; }; 1AC27A0D0EA7E9940054E5F0 /* OOJSEquipmentInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OOJSEquipmentInfo.h; sourceTree = ""; }; 1AC27A0E0EA7E9940054E5F0 /* OOJSEquipmentInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OOJSEquipmentInfo.m; sourceTree = ""; }; 1AC544F90D4D217900C90E5B /* oolite-font.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "oolite-font.png"; sourceTree = ""; }; @@ -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; }; diff --git a/src/Cocoa/exports-debug-32.exp b/src/Cocoa/exports-debug-32.exp index 3f0919db..ea26d2e3 100644 --- a/src/Cocoa/exports-debug-32.exp +++ b/src/Cocoa/exports-debug-32.exp @@ -67,3 +67,6 @@ _JSObjectToStrDbg _JSStringToStrDbg _JSValueTypeDbg _JSValueToStrSafeDbg +_JSObjectToStrSafeDbg +_JSStringToStrSafeDbg +_JSIDToStrSafeDbg diff --git a/src/Cocoa/exports-debug-64.exp b/src/Cocoa/exports-debug-64.exp index 30b270e0..fa53a60c 100644 --- a/src/Cocoa/exports-debug-64.exp +++ b/src/Cocoa/exports-debug-64.exp @@ -72,3 +72,6 @@ _JSObjectToStrDbg _JSStringToStrDbg _JSValueTypeDbg _JSValueToStrSafeDbg +_JSObjectToStrSafeDbg +_JSStringToStrSafeDbg +_JSIDToStrSafeDbg diff --git a/src/Core/Scripting/OOJSEngineDebuggerHelpers.m b/src/Core/Scripting/OOJSEngineDebuggerHelpers.m new file mode 100644 index 00000000..ee2ee4c5 --- /dev/null +++ b/src/Core/Scripting/OOJSEngineDebuggerHelpers.m @@ -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 diff --git a/src/Core/Scripting/OOJavaScriptEngine.h b/src/Core/Scripting/OOJavaScriptEngine.h index d7c75817..ca35917a 100644 --- a/src/Core/Scripting/OOJavaScriptEngine.h +++ b/src/Core/Scripting/OOJavaScriptEngine.h @@ -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 diff --git a/src/Core/Scripting/OOJavaScriptEngine.m b/src/Core/Scripting/OOJavaScriptEngine.m index 039bf4db..4af47c5f 100644 --- a/src/Core/Scripting/OOJavaScriptEngine.m +++ b/src/Core/Scripting/OOJavaScriptEngine.m @@ -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 diff --git a/tools/gdb-macros.txt b/tools/gdb-macros.txt new file mode 100644 index 00000000..2469c7bf --- /dev/null +++ b/tools/gdb-macros.txt @@ -0,0 +1,87 @@ +# Oolite gdb conveniences +# To use, copy into ~/.gdbinit. + +define pjsv + call (char *)JSValueToStrSafeDbg($arg0) +end +document psjv +pjsv +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 +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 +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 +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 +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 +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 +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