1#include "jsi.h"
2#include "jslex.h"
3#include "jsvalue.h"
4#include "jsbuiltin.h"
5
6static void reprvalue(js_State *J, js_Buffer **sb);
7
8static 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
17static 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
54static 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
69static 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
100static 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
127static 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
141static 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
228void 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
254const 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
261const 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