| 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 |  | 
|---|