Line data Source code
1 0 : /* 2 : 3 : OOJSEngineDebuggerHelpers.m 4 : 5 : JavaScript support for Oolite 6 : Copyright (C) 2007-2013 David Taylor and Jens Ayton. 7 : 8 : This program is free software; you can redistribute it and/or 9 : modify it under the terms of the GNU General Public License 10 : as published by the Free Software Foundation; either version 2 11 : of the License, or (at your option) any later version. 12 : 13 : This program is distributed in the hope that it will be useful, 14 : but WITHOUT ANY WARRANTY; without even the implied warranty of 15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 : GNU General Public License for more details. 17 : 18 : You should have received a copy of the GNU General Public License 19 : along with this program; if not, write to the Free Software 20 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 21 : MA 02110-1301, USA. 22 : 23 : */ 24 : 25 : 26 : /* 27 : These functions exist to help debugging JavaScript code. They can be called 28 : directly from gdb, for example: 29 : 30 : call (char *)JSValueToStrDbg(someValue) 31 : 32 : The functions are: 33 : 34 : const char *JSValueToStrDbg(jsval) 35 : const char *JSObjectToStrDbg(JSObject *) 36 : const char *JSStringToStrDbg(JSString *) 37 : Converts any JS value/object/JSString to a string, using the complete 38 : process and potentially calling into SpiderMonkey with a secondory 39 : context and invoking JS toString() methods. This might mess up 40 : SpiderMonkey internal state in some cases. 41 : 42 : const char *JSValueToStrSafeDbg(jsval) 43 : const char *JSObjectToStrSafeDbg(JSObject *) 44 : const char *JSStringToStrSafeDbg(JSString *) 45 : As above, but without calling into SpiderMonkey functions that require 46 : a context. In particular, as of the FF4b9 version of SpiderMonkey only 47 : interned strings can be converted, and for objects only the class name 48 : is provided. 49 : 50 : const char *JSIDToStrSafeDbg(jsid) 51 : Like JSValueToStrSafeDbg() for jsids. (String jsids must always be 52 : interned, so this is generally sufficient.) 53 : 54 : const char *JSValueTypeDbg(jsval) 55 : Returns the type of the jsval, or the class name if it's an object. 56 : 57 : All dynamic strings are autoreleased. 58 : 59 : Another useful function is OOJSDumpStack (results are found in the log): 60 : 61 : call OOJSDumpStack(context) 62 : 63 : A set of macros can be found in tools/gdb-macros.txt. 64 : In almost all Oolite functions that deal with JavaScript, there is a single 65 : JSContext called "context". In SpiderMonkey functions, it's called "cx". 66 : 67 : 68 : In addition to calling them from the debug console, Xcode users might want 69 : to use them in data formatters (by double-clicking the "Summary" field for 70 : a variable of the appropriate type). I recommend the following: 71 : 72 : jsval: {JSValueToStrSafeDbg($VAR)}:s 73 : jsval*: {JSValueToStrSafeDbg(*$VAR)}:s 74 : jsid: {JSIDToStrSafeDbg($VAR)}:s 75 : JSObject*: {JSObjectToStrSafeDbg($VAR)}:s 76 : JSString*: {JSStringToStrSafeDbg($VAR)}:s 77 : 78 : These, and a variety of Oolite type formatters, can be set up using 79 : Mac-specific/DataFormatters. 80 : */ 81 : 82 : 83 : #ifndef NDEBUG 84 : 85 : #import "OOJavaScriptEngine.h" 86 : 87 : 88 0 : const char *JSValueToStrDbg(jsval val) 89 : { 90 : JSContext *context = OOJSAcquireContext(); 91 : const char *result = [OOStringFromJSValueEvenIfNull(context, val) UTF8String]; 92 : OOJSRelinquishContext(context); 93 : 94 : return result; 95 : } 96 : 97 : 98 0 : const char *JSObjectToStrDbg(JSObject *obj) 99 : { 100 : if (obj == NULL) return "null"; 101 : return JSValueToStrDbg(OBJECT_TO_JSVAL(obj)); 102 : } 103 : 104 : 105 0 : const char *JSStringToStrDbg(JSString *str) 106 : { 107 : if (str == NULL) return "null"; 108 : return JSValueToStrDbg(STRING_TO_JSVAL(str)); 109 : } 110 : 111 : 112 0 : const char *JSValueTypeDbg(jsval val) 113 : { 114 : if (JSVAL_IS_INT(val)) return "integer"; 115 : if (JSVAL_IS_DOUBLE(val)) return "double"; 116 : if (JSVAL_IS_STRING(val)) return "string"; 117 : if (JSVAL_IS_BOOLEAN(val)) return "boolean"; 118 : if (JSVAL_IS_NULL(val)) return "null"; 119 : if (JSVAL_IS_VOID(val)) return "void"; 120 : #ifdef JS_USE_JSVAL_JSID_STRUCT_TYPES 121 : if (JSVAL_IS_MAGIC_IMPL(val)) 122 : { 123 : switch(val.s.payload.why) 124 : { 125 : case JS_ARRAY_HOLE: return "magic (array hole)"; 126 : case JS_ARGS_HOLE: return "magic (args hole)"; 127 : case JS_NATIVE_ENUMERATE: return "magic (native enumerate)"; 128 : case JS_NO_ITER_VALUE: return "magic (no iter value)"; 129 : case JS_GENERATOR_CLOSING: return "magic (generator closing)"; 130 : case JS_NO_CONSTANT: return "magic (no constant)"; 131 : case JS_THIS_POISON: return "magic (this poison)"; 132 : case JS_ARG_POISON: return "magic (arg poison)"; 133 : case JS_SERIALIZE_NO_NODE: return "magic (serialize no node)"; 134 : case JS_GENERIC_MAGIC: return "magic (generic)"; 135 : }; 136 : return "magic"; 137 : } 138 : #endif 139 : 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. 140 : return "unknown"; 141 : } 142 : 143 : 144 : // Doesn't follow pointers, mess with requests or otherwise poke the SpiderMonkey. 145 0 : const char *JSValueToStrSafeDbg(jsval val) 146 : { 147 : NSString *formatted = nil; 148 : 149 : if (JSVAL_IS_INT(val)) formatted = [NSString stringWithFormat:@"%i", JSVAL_TO_INT(val)]; 150 : else if (JSVAL_IS_DOUBLE(val)) formatted = [NSString stringWithFormat:@"%g", JSVAL_TO_DOUBLE(val)]; 151 : else if (JSVAL_IS_BOOLEAN(val)) formatted = (JSVAL_TO_BOOLEAN(val)) ? @"true" : @"false"; 152 : else if (JSVAL_IS_STRING(val)) 153 : { 154 : JSString *string = JSVAL_TO_STRING(val); 155 : const jschar *chars = NULL; 156 : size_t length = JS_GetStringLength(string); 157 : 158 : if (JS_StringHasBeenInterned(string)) 159 : { 160 : chars = JS_GetInternedStringChars(string); 161 : } 162 : // Flat strings can be extracted without a context, but cannot be detected. 163 : 164 : if (chars == NULL) formatted = [NSString stringWithFormat:@"string [%zu chars]", length]; 165 : else formatted = [NSString stringWithCharacters:chars length:length]; 166 : } 167 : else if (JSVAL_IS_VOID(val)) return "undefined"; 168 : else return JSValueTypeDbg(val); 169 : 170 : return [formatted UTF8String]; 171 : } 172 : 173 : 174 0 : const char *JSObjectToStrSafeDbg(JSObject *obj) 175 : { 176 : if (obj == NULL) return "null"; 177 : return JSValueToStrSafeDbg(OBJECT_TO_JSVAL(obj)); 178 : } 179 : 180 : 181 0 : const char *JSStringToStrSafeDbg(JSString *str) 182 : { 183 : if (str == NULL) return "null"; 184 : return JSValueToStrSafeDbg(STRING_TO_JSVAL(str)); 185 : } 186 : 187 : 188 0 : const char *JSIDToStrSafeDbg(jsid anID) 189 : { 190 : NSString *formatted = nil; 191 : 192 : if (JSID_IS_INT(anID)) formatted = [NSString stringWithFormat:@"%i", JSID_TO_INT(anID)]; 193 : else if (JSID_IS_VOID(anID)) return "void"; 194 : else if (JSID_IS_EMPTY(anID)) return "empty"; 195 : else if (JSID_IS_ZERO(anID)) return "0"; 196 : else if (JSID_IS_OBJECT(anID)) return OOJSGetClass(NULL, JSID_TO_OBJECT(anID))->name; 197 : else if (JSID_IS_DEFAULT_XML_NAMESPACE(anID)) return "default XML namespace"; 198 : else if (JSID_IS_STRING(anID)) 199 : { 200 : JSString *string = JSID_TO_STRING(anID); 201 : const jschar *chars = NULL; 202 : size_t length = JS_GetStringLength(string); 203 : 204 : if (JS_StringHasBeenInterned(string)) 205 : { 206 : chars = JS_GetInternedStringChars(string); 207 : } 208 : else 209 : { 210 : // Bug; jsid strings must be interned. 211 : return "*** uninterned string in jsid! ***"; 212 : } 213 : formatted = [NSString stringWithCharacters:chars length:length]; 214 : } 215 : else 216 : { 217 : formatted = [NSString stringWithFormat:@"unknown <0x%llX>", (long long)JSID_BITS(anID)]; 218 : } 219 : 220 : return [formatted UTF8String]; 221 : } 222 : #endif