| 1 | #include "jsi.h" |
| 2 | #include "jslex.h" |
| 3 | #include "jsvalue.h" |
| 4 | #include "jsbuiltin.h" |
| 5 | |
| 6 | static void reprvalue(js_State *J, js_Buffer **sb); |
| 7 | |
| 8 | static void reprnum(js_State *J, js_Buffer **sb, double n) |
| 9 | { |
| 10 | char buf[40]; |
| 11 | if (n == 0 && signbit(n)) |
| 12 | js_puts(J, sb, "-0" ); |
| 13 | else |
| 14 | js_puts(J, sb, jsV_numbertostring(J, buf, n)); |
| 15 | } |
| 16 | |
| 17 | static void reprstr(js_State *J, js_Buffer **sb, const char *s) |
| 18 | { |
| 19 | static const char *HEX = "0123456789ABCDEF" ; |
| 20 | Rune c; |
| 21 | js_putc(J, sb, '"'); |
| 22 | while (*s) { |
| 23 | s += chartorune(&c, s); |
| 24 | switch (c) { |
| 25 | case '"': js_puts(J, sb, "\\\"" ); break; |
| 26 | case '\\': js_puts(J, sb, "\\\\" ); break; |
| 27 | case '\b': js_puts(J, sb, "\\b" ); break; |
| 28 | case '\f': js_puts(J, sb, "\\f" ); break; |
| 29 | case '\n': js_puts(J, sb, "\\n" ); break; |
| 30 | case '\r': js_puts(J, sb, "\\r" ); break; |
| 31 | case '\t': js_puts(J, sb, "\\t" ); break; |
| 32 | default: |
| 33 | if (c < ' ' || c > 127) { |
| 34 | js_puts(J, sb, "\\u" ); |
| 35 | js_putc(J, sb, HEX[(c>>12)&15]); |
| 36 | js_putc(J, sb, HEX[(c>>8)&15]); |
| 37 | js_putc(J, sb, HEX[(c>>4)&15]); |
| 38 | js_putc(J, sb, HEX[c&15]); |
| 39 | } else { |
| 40 | js_putc(J, sb, c); break; |
| 41 | } |
| 42 | } |
| 43 | } |
| 44 | js_putc(J, sb, '"'); |
| 45 | } |
| 46 | |
| 47 | #ifndef isalpha |
| 48 | #define isalpha(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) |
| 49 | #endif |
| 50 | #ifndef isdigit |
| 51 | #define isdigit(c) (c >= '0' && c <= '9') |
| 52 | #endif |
| 53 | |
| 54 | static void reprident(js_State *J, js_Buffer **sb, const char *name) |
| 55 | { |
| 56 | const char *p = name; |
| 57 | if (isdigit(*p)) |
| 58 | while (isdigit(*p)) |
| 59 | ++p; |
| 60 | else if (isalpha(*p) || *p == '_') |
| 61 | while (isdigit(*p) || isalpha(*p) || *p == '_') |
| 62 | ++p; |
| 63 | if (p > name && *p == 0) |
| 64 | js_puts(J, sb, name); |
| 65 | else |
| 66 | reprstr(J, sb, name); |
| 67 | } |
| 68 | |
| 69 | static void reprobject(js_State *J, js_Buffer **sb) |
| 70 | { |
| 71 | const char *key; |
| 72 | int i, n; |
| 73 | |
| 74 | n = js_gettop(J) - 1; |
| 75 | for (i = 0; i < n; ++i) { |
| 76 | if (js_isobject(J, i)) { |
| 77 | if (js_toobject(J, i) == js_toobject(J, -1)) { |
| 78 | js_puts(J, sb, "{}" ); |
| 79 | return; |
| 80 | } |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | n = 0; |
| 85 | js_putc(J, sb, '{'); |
| 86 | js_pushiterator(J, -1, 1); |
| 87 | while ((key = js_nextiterator(J, -1))) { |
| 88 | if (n++ > 0) |
| 89 | js_puts(J, sb, ", " ); |
| 90 | reprident(J, sb, key); |
| 91 | js_puts(J, sb, ": " ); |
| 92 | js_getproperty(J, -2, key); |
| 93 | reprvalue(J, sb); |
| 94 | js_pop(J, 1); |
| 95 | } |
| 96 | js_pop(J, 1); |
| 97 | js_putc(J, sb, '}'); |
| 98 | } |
| 99 | |
| 100 | static void reprarray(js_State *J, js_Buffer **sb) |
| 101 | { |
| 102 | int n, i; |
| 103 | |
| 104 | n = js_gettop(J) - 1; |
| 105 | for (i = 0; i < n; ++i) { |
| 106 | if (js_isobject(J, i)) { |
| 107 | if (js_toobject(J, i) == js_toobject(J, -1)) { |
| 108 | js_puts(J, sb, "[]" ); |
| 109 | return; |
| 110 | } |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | js_putc(J, sb, '['); |
| 115 | n = js_getlength(J, -1); |
| 116 | for (i = 0; i < n; ++i) { |
| 117 | if (i > 0) |
| 118 | js_puts(J, sb, ", " ); |
| 119 | if (js_hasindex(J, -1, i)) { |
| 120 | reprvalue(J, sb); |
| 121 | js_pop(J, 1); |
| 122 | } |
| 123 | } |
| 124 | js_putc(J, sb, ']'); |
| 125 | } |
| 126 | |
| 127 | static void reprfun(js_State *J, js_Buffer **sb, js_Function *fun) |
| 128 | { |
| 129 | int i; |
| 130 | js_puts(J, sb, "function " ); |
| 131 | js_puts(J, sb, fun->name); |
| 132 | js_putc(J, sb, '('); |
| 133 | for (i = 0; i < fun->numparams; ++i) { |
| 134 | if (i > 0) |
| 135 | js_puts(J, sb, ", " ); |
| 136 | js_puts(J, sb, fun->vartab[i]); |
| 137 | } |
| 138 | js_puts(J, sb, ") { [byte code] }" ); |
| 139 | } |
| 140 | |
| 141 | static void reprvalue(js_State *J, js_Buffer **sb) |
| 142 | { |
| 143 | if (js_isundefined(J, -1)) |
| 144 | js_puts(J, sb, "undefined" ); |
| 145 | else if (js_isnull(J, -1)) |
| 146 | js_puts(J, sb, "null" ); |
| 147 | else if (js_isboolean(J, -1)) |
| 148 | js_puts(J, sb, js_toboolean(J, -1) ? "true" : "false" ); |
| 149 | else if (js_isnumber(J, -1)) |
| 150 | reprnum(J, sb, js_tonumber(J, -1)); |
| 151 | else if (js_isstring(J, -1)) |
| 152 | reprstr(J, sb, js_tostring(J, -1)); |
| 153 | else if (js_isobject(J, -1)) { |
| 154 | js_Object *obj = js_toobject(J, -1); |
| 155 | switch (obj->type) { |
| 156 | default: |
| 157 | reprobject(J, sb); |
| 158 | break; |
| 159 | case JS_CARRAY: |
| 160 | reprarray(J, sb); |
| 161 | break; |
| 162 | case JS_CFUNCTION: |
| 163 | case JS_CSCRIPT: |
| 164 | reprfun(J, sb, obj->u.f.function); |
| 165 | break; |
| 166 | case JS_CCFUNCTION: |
| 167 | js_puts(J, sb, "function " ); |
| 168 | js_puts(J, sb, obj->u.c.name); |
| 169 | js_puts(J, sb, "() { [native code] }" ); |
| 170 | break; |
| 171 | case JS_CBOOLEAN: |
| 172 | js_puts(J, sb, "(new Boolean(" ); |
| 173 | js_puts(J, sb, obj->u.boolean ? "true" : "false" ); |
| 174 | js_puts(J, sb, "))" ); |
| 175 | break; |
| 176 | case JS_CNUMBER: |
| 177 | js_puts(J, sb, "(new Number(" ); |
| 178 | reprnum(J, sb, obj->u.number); |
| 179 | js_puts(J, sb, "))" ); |
| 180 | break; |
| 181 | case JS_CSTRING: |
| 182 | js_puts(J, sb, "(new String(" ); |
| 183 | reprstr(J, sb, obj->u.s.string); |
| 184 | js_puts(J, sb, "))" ); |
| 185 | break; |
| 186 | case JS_CREGEXP: |
| 187 | js_putc(J, sb, '/'); |
| 188 | js_puts(J, sb, obj->u.r.source); |
| 189 | js_putc(J, sb, '/'); |
| 190 | if (obj->u.r.flags & JS_REGEXP_G) js_putc(J, sb, 'g'); |
| 191 | if (obj->u.r.flags & JS_REGEXP_I) js_putc(J, sb, 'i'); |
| 192 | if (obj->u.r.flags & JS_REGEXP_M) js_putc(J, sb, 'm'); |
| 193 | break; |
| 194 | case JS_CDATE: |
| 195 | js_puts(J, sb, "(new Date(" ); |
| 196 | fmtnum(J, sb, obj->u.number); |
| 197 | js_puts(J, sb, "))" ); |
| 198 | break; |
| 199 | case JS_CERROR: |
| 200 | js_puts(J, sb, "(new " ); |
| 201 | js_getproperty(J, -1, "name" ); |
| 202 | js_puts(J, sb, js_tostring(J, -1)); |
| 203 | js_pop(J, 1); |
| 204 | js_putc(J, sb, '('); |
| 205 | js_getproperty(J, -1, "message" ); |
| 206 | reprstr(J, sb, js_tostring(J, -1)); |
| 207 | js_pop(J, 1); |
| 208 | js_puts(J, sb, "))" ); |
| 209 | break; |
| 210 | case JS_CMATH: |
| 211 | js_puts(J, sb, "Math" ); |
| 212 | break; |
| 213 | case JS_CJSON: |
| 214 | js_puts(J, sb, "JSON" ); |
| 215 | break; |
| 216 | case JS_CITERATOR: |
| 217 | js_puts(J, sb, "[iterator " ); |
| 218 | break; |
| 219 | case JS_CUSERDATA: |
| 220 | js_puts(J, sb, "[userdata " ); |
| 221 | js_puts(J, sb, obj->u.user.tag); |
| 222 | js_putc(J, sb, ']'); |
| 223 | break; |
| 224 | } |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | void js_repr(js_State *J, int idx) |
| 229 | { |
| 230 | js_Buffer *sb = NULL; |
| 231 | int savebot; |
| 232 | |
| 233 | if (js_try(J)) { |
| 234 | js_free(J, sb); |
| 235 | js_throw(J); |
| 236 | } |
| 237 | |
| 238 | js_copy(J, idx); |
| 239 | |
| 240 | savebot = J->bot; |
| 241 | J->bot = J->top - 1; |
| 242 | reprvalue(J, &sb); |
| 243 | J->bot = savebot; |
| 244 | |
| 245 | js_pop(J, 1); |
| 246 | |
| 247 | js_putc(J, &sb, 0); |
| 248 | js_pushstring(J, sb ? sb->s : "undefined" ); |
| 249 | |
| 250 | js_endtry(J); |
| 251 | js_free(J, sb); |
| 252 | } |
| 253 | |
| 254 | const char *js_torepr(js_State *J, int idx) |
| 255 | { |
| 256 | js_repr(J, idx); |
| 257 | js_replace(J, idx < 0 ? idx-1 : idx); |
| 258 | return js_tostring(J, idx); |
| 259 | } |
| 260 | |
| 261 | const char *js_tryrepr(js_State *J, int idx, const char *error) |
| 262 | { |
| 263 | const char *s; |
| 264 | if (js_try(J)) { |
| 265 | js_pop(J, 1); |
| 266 | return error; |
| 267 | } |
| 268 | s = js_torepr(J, idx); |
| 269 | js_endtry(J); |
| 270 | return s; |
| 271 | } |
| 272 | |