1 | #include "jsi.h" |
2 | #include "jsvalue.h" |
3 | #include "jsbuiltin.h" |
4 | |
5 | static void jsB_new_Object(js_State *J) |
6 | { |
7 | if (js_isundefined(J, 1) || js_isnull(J, 1)) |
8 | js_newobject(J); |
9 | else |
10 | js_pushobject(J, js_toobject(J, 1)); |
11 | } |
12 | |
13 | static void jsB_Object(js_State *J) |
14 | { |
15 | if (js_isundefined(J, 1) || js_isnull(J, 1)) |
16 | js_newobject(J); |
17 | else |
18 | js_pushobject(J, js_toobject(J, 1)); |
19 | } |
20 | |
21 | static void Op_toString(js_State *J) |
22 | { |
23 | if (js_isundefined(J, 0)) |
24 | js_pushliteral(J, "[object Undefined]" ); |
25 | else if (js_isnull(J, 0)) |
26 | js_pushliteral(J, "[object Null]" ); |
27 | else { |
28 | js_Object *self = js_toobject(J, 0); |
29 | switch (self->type) { |
30 | case JS_COBJECT: js_pushliteral(J, "[object Object]" ); break; |
31 | case JS_CARRAY: js_pushliteral(J, "[object Array]" ); break; |
32 | case JS_CFUNCTION: js_pushliteral(J, "[object Function]" ); break; |
33 | case JS_CSCRIPT: js_pushliteral(J, "[object Function]" ); break; |
34 | case JS_CCFUNCTION: js_pushliteral(J, "[object Function]" ); break; |
35 | case JS_CERROR: js_pushliteral(J, "[object Error]" ); break; |
36 | case JS_CBOOLEAN: js_pushliteral(J, "[object Boolean]" ); break; |
37 | case JS_CNUMBER: js_pushliteral(J, "[object Number]" ); break; |
38 | case JS_CSTRING: js_pushliteral(J, "[object String]" ); break; |
39 | case JS_CREGEXP: js_pushliteral(J, "[object RegExp]" ); break; |
40 | case JS_CDATE: js_pushliteral(J, "[object Date]" ); break; |
41 | case JS_CMATH: js_pushliteral(J, "[object Math]" ); break; |
42 | case JS_CJSON: js_pushliteral(J, "[object JSON]" ); break; |
43 | case JS_CARGUMENTS: js_pushliteral(J, "[object Arguments]" ); break; |
44 | case JS_CITERATOR: js_pushliteral(J, "[Iterator]" ); break; |
45 | case JS_CUSERDATA: |
46 | js_pushliteral(J, "[object " ); |
47 | js_pushliteral(J, self->u.user.tag); |
48 | js_concat(J); |
49 | js_pushliteral(J, "]" ); |
50 | js_concat(J); |
51 | break; |
52 | } |
53 | } |
54 | } |
55 | |
56 | static void Op_valueOf(js_State *J) |
57 | { |
58 | js_copy(J, 0); |
59 | } |
60 | |
61 | static void Op_hasOwnProperty(js_State *J) |
62 | { |
63 | js_Object *self = js_toobject(J, 0); |
64 | const char *name = js_tostring(J, 1); |
65 | js_Property *ref = jsV_getownproperty(J, self, name); |
66 | js_pushboolean(J, ref != NULL); |
67 | } |
68 | |
69 | static void Op_isPrototypeOf(js_State *J) |
70 | { |
71 | js_Object *self = js_toobject(J, 0); |
72 | if (js_isobject(J, 1)) { |
73 | js_Object *V = js_toobject(J, 1); |
74 | do { |
75 | V = V->prototype; |
76 | if (V == self) { |
77 | js_pushboolean(J, 1); |
78 | return; |
79 | } |
80 | } while (V); |
81 | } |
82 | js_pushboolean(J, 0); |
83 | } |
84 | |
85 | static void Op_propertyIsEnumerable(js_State *J) |
86 | { |
87 | js_Object *self = js_toobject(J, 0); |
88 | const char *name = js_tostring(J, 1); |
89 | js_Property *ref = jsV_getownproperty(J, self, name); |
90 | js_pushboolean(J, ref && !(ref->atts & JS_DONTENUM)); |
91 | } |
92 | |
93 | static void O_getPrototypeOf(js_State *J) |
94 | { |
95 | js_Object *obj; |
96 | if (!js_isobject(J, 1)) |
97 | js_typeerror(J, "not an object" ); |
98 | obj = js_toobject(J, 1); |
99 | if (obj->prototype) |
100 | js_pushobject(J, obj->prototype); |
101 | else |
102 | js_pushnull(J); |
103 | } |
104 | |
105 | static void O_getOwnPropertyDescriptor(js_State *J) |
106 | { |
107 | js_Object *obj; |
108 | js_Property *ref; |
109 | if (!js_isobject(J, 1)) |
110 | js_typeerror(J, "not an object" ); |
111 | obj = js_toobject(J, 1); |
112 | ref = jsV_getproperty(J, obj, js_tostring(J, 2)); |
113 | if (!ref) |
114 | js_pushundefined(J); |
115 | else { |
116 | js_newobject(J); |
117 | if (!ref->getter && !ref->setter) { |
118 | js_pushvalue(J, ref->value); |
119 | js_setproperty(J, -2, "value" ); |
120 | js_pushboolean(J, !(ref->atts & JS_READONLY)); |
121 | js_setproperty(J, -2, "writable" ); |
122 | } else { |
123 | if (ref->getter) |
124 | js_pushobject(J, ref->getter); |
125 | else |
126 | js_pushundefined(J); |
127 | js_setproperty(J, -2, "get" ); |
128 | if (ref->setter) |
129 | js_pushobject(J, ref->setter); |
130 | else |
131 | js_pushundefined(J); |
132 | js_setproperty(J, -2, "set" ); |
133 | } |
134 | js_pushboolean(J, !(ref->atts & JS_DONTENUM)); |
135 | js_setproperty(J, -2, "enumerable" ); |
136 | js_pushboolean(J, !(ref->atts & JS_DONTCONF)); |
137 | js_setproperty(J, -2, "configurable" ); |
138 | } |
139 | } |
140 | |
141 | static int O_getOwnPropertyNames_walk(js_State *J, js_Property *ref, int i) |
142 | { |
143 | if (ref->left->level) |
144 | i = O_getOwnPropertyNames_walk(J, ref->left, i); |
145 | js_pushliteral(J, ref->name); |
146 | js_setindex(J, -2, i++); |
147 | if (ref->right->level) |
148 | i = O_getOwnPropertyNames_walk(J, ref->right, i); |
149 | return i; |
150 | } |
151 | |
152 | static void O_getOwnPropertyNames(js_State *J) |
153 | { |
154 | js_Object *obj; |
155 | int k; |
156 | int i; |
157 | |
158 | if (!js_isobject(J, 1)) |
159 | js_typeerror(J, "not an object" ); |
160 | obj = js_toobject(J, 1); |
161 | |
162 | js_newarray(J); |
163 | |
164 | if (obj->properties->level) |
165 | i = O_getOwnPropertyNames_walk(J, obj->properties, 0); |
166 | else |
167 | i = 0; |
168 | |
169 | if (obj->type == JS_CARRAY) { |
170 | js_pushliteral(J, "length" ); |
171 | js_setindex(J, -2, i++); |
172 | } |
173 | |
174 | if (obj->type == JS_CSTRING) { |
175 | js_pushliteral(J, "length" ); |
176 | js_setindex(J, -2, i++); |
177 | for (k = 0; k < obj->u.s.length; ++k) { |
178 | js_pushnumber(J, k); |
179 | js_setindex(J, -2, i++); |
180 | } |
181 | } |
182 | |
183 | if (obj->type == JS_CREGEXP) { |
184 | js_pushliteral(J, "source" ); |
185 | js_setindex(J, -2, i++); |
186 | js_pushliteral(J, "global" ); |
187 | js_setindex(J, -2, i++); |
188 | js_pushliteral(J, "ignoreCase" ); |
189 | js_setindex(J, -2, i++); |
190 | js_pushliteral(J, "multiline" ); |
191 | js_setindex(J, -2, i++); |
192 | js_pushliteral(J, "lastIndex" ); |
193 | js_setindex(J, -2, i++); |
194 | } |
195 | } |
196 | |
197 | static void ToPropertyDescriptor(js_State *J, js_Object *obj, const char *name, js_Object *desc) |
198 | { |
199 | int haswritable = 0; |
200 | int hasvalue = 0; |
201 | int enumerable = 0; |
202 | int configurable = 0; |
203 | int writable = 0; |
204 | int atts = 0; |
205 | |
206 | js_pushobject(J, obj); |
207 | js_pushobject(J, desc); |
208 | |
209 | if (js_hasproperty(J, -1, "writable" )) { |
210 | haswritable = 1; |
211 | writable = js_toboolean(J, -1); |
212 | js_pop(J, 1); |
213 | } |
214 | if (js_hasproperty(J, -1, "enumerable" )) { |
215 | enumerable = js_toboolean(J, -1); |
216 | js_pop(J, 1); |
217 | } |
218 | if (js_hasproperty(J, -1, "configurable" )) { |
219 | configurable = js_toboolean(J, -1); |
220 | js_pop(J, 1); |
221 | } |
222 | if (js_hasproperty(J, -1, "value" )) { |
223 | hasvalue = 1; |
224 | js_setproperty(J, -3, name); |
225 | } |
226 | |
227 | if (!writable) atts |= JS_READONLY; |
228 | if (!enumerable) atts |= JS_DONTENUM; |
229 | if (!configurable) atts |= JS_DONTCONF; |
230 | |
231 | if (js_hasproperty(J, -1, "get" )) { |
232 | if (haswritable || hasvalue) |
233 | js_typeerror(J, "value/writable and get/set attributes are exclusive" ); |
234 | } else { |
235 | js_pushundefined(J); |
236 | } |
237 | |
238 | if (js_hasproperty(J, -2, "set" )) { |
239 | if (haswritable || hasvalue) |
240 | js_typeerror(J, "value/writable and get/set attributes are exclusive" ); |
241 | } else { |
242 | js_pushundefined(J); |
243 | } |
244 | |
245 | js_defaccessor(J, -4, name, atts); |
246 | |
247 | js_pop(J, 2); |
248 | } |
249 | |
250 | static void O_defineProperty(js_State *J) |
251 | { |
252 | if (!js_isobject(J, 1)) js_typeerror(J, "not an object" ); |
253 | if (!js_isobject(J, 3)) js_typeerror(J, "not an object" ); |
254 | ToPropertyDescriptor(J, js_toobject(J, 1), js_tostring(J, 2), js_toobject(J, 3)); |
255 | js_copy(J, 1); |
256 | } |
257 | |
258 | static void O_defineProperties_walk(js_State *J, js_Property *ref) |
259 | { |
260 | if (ref->left->level) |
261 | O_defineProperties_walk(J, ref->left); |
262 | if (!(ref->atts & JS_DONTENUM)) { |
263 | js_pushvalue(J, ref->value); |
264 | ToPropertyDescriptor(J, js_toobject(J, 1), ref->name, js_toobject(J, -1)); |
265 | js_pop(J, 1); |
266 | } |
267 | if (ref->right->level) |
268 | O_defineProperties_walk(J, ref->right); |
269 | } |
270 | |
271 | static void O_defineProperties(js_State *J) |
272 | { |
273 | js_Object *props; |
274 | |
275 | if (!js_isobject(J, 1)) js_typeerror(J, "not an object" ); |
276 | if (!js_isobject(J, 2)) js_typeerror(J, "not an object" ); |
277 | |
278 | props = js_toobject(J, 2); |
279 | if (props->properties->level) |
280 | O_defineProperties_walk(J, props->properties); |
281 | |
282 | js_copy(J, 1); |
283 | } |
284 | |
285 | static void O_create_walk(js_State *J, js_Object *obj, js_Property *ref) |
286 | { |
287 | if (ref->left->level) |
288 | O_create_walk(J, obj, ref->left); |
289 | if (!(ref->atts & JS_DONTENUM)) { |
290 | if (ref->value.type != JS_TOBJECT) |
291 | js_typeerror(J, "not an object" ); |
292 | ToPropertyDescriptor(J, obj, ref->name, ref->value.u.object); |
293 | } |
294 | if (ref->right->level) |
295 | O_create_walk(J, obj, ref->right); |
296 | } |
297 | |
298 | static void O_create(js_State *J) |
299 | { |
300 | js_Object *obj; |
301 | js_Object *proto; |
302 | js_Object *props; |
303 | |
304 | if (js_isobject(J, 1)) |
305 | proto = js_toobject(J, 1); |
306 | else if (js_isnull(J, 1)) |
307 | proto = NULL; |
308 | else |
309 | js_typeerror(J, "not an object or null" ); |
310 | |
311 | obj = jsV_newobject(J, JS_COBJECT, proto); |
312 | js_pushobject(J, obj); |
313 | |
314 | if (js_isdefined(J, 2)) { |
315 | if (!js_isobject(J, 2)) |
316 | js_typeerror(J, "not an object" ); |
317 | props = js_toobject(J, 2); |
318 | if (props->properties->level) |
319 | O_create_walk(J, obj, props->properties); |
320 | } |
321 | } |
322 | |
323 | static int O_keys_walk(js_State *J, js_Property *ref, int i) |
324 | { |
325 | if (ref->left->level) |
326 | i = O_keys_walk(J, ref->left, i); |
327 | if (!(ref->atts & JS_DONTENUM)) { |
328 | js_pushliteral(J, ref->name); |
329 | js_setindex(J, -2, i++); |
330 | } |
331 | if (ref->right->level) |
332 | i = O_keys_walk(J, ref->right, i); |
333 | return i; |
334 | } |
335 | |
336 | static void O_keys(js_State *J) |
337 | { |
338 | js_Object *obj; |
339 | int i, k; |
340 | |
341 | if (!js_isobject(J, 1)) |
342 | js_typeerror(J, "not an object" ); |
343 | obj = js_toobject(J, 1); |
344 | |
345 | js_newarray(J); |
346 | |
347 | if (obj->properties->level) |
348 | i = O_keys_walk(J, obj->properties, 0); |
349 | else |
350 | i = 0; |
351 | |
352 | if (obj->type == JS_CSTRING) { |
353 | for (k = 0; k < obj->u.s.length; ++k) { |
354 | js_pushnumber(J, k); |
355 | js_setindex(J, -2, i++); |
356 | } |
357 | } |
358 | } |
359 | |
360 | static void O_preventExtensions(js_State *J) |
361 | { |
362 | if (!js_isobject(J, 1)) |
363 | js_typeerror(J, "not an object" ); |
364 | js_toobject(J, 1)->extensible = 0; |
365 | js_copy(J, 1); |
366 | } |
367 | |
368 | static void O_isExtensible(js_State *J) |
369 | { |
370 | if (!js_isobject(J, 1)) |
371 | js_typeerror(J, "not an object" ); |
372 | js_pushboolean(J, js_toobject(J, 1)->extensible); |
373 | } |
374 | |
375 | static void O_seal_walk(js_State *J, js_Property *ref) |
376 | { |
377 | if (ref->left->level) |
378 | O_seal_walk(J, ref->left); |
379 | ref->atts |= JS_DONTCONF; |
380 | if (ref->right->level) |
381 | O_seal_walk(J, ref->right); |
382 | } |
383 | |
384 | static void O_seal(js_State *J) |
385 | { |
386 | js_Object *obj; |
387 | |
388 | if (!js_isobject(J, 1)) |
389 | js_typeerror(J, "not an object" ); |
390 | |
391 | obj = js_toobject(J, 1); |
392 | obj->extensible = 0; |
393 | |
394 | if (obj->properties->level) |
395 | O_seal_walk(J, obj->properties); |
396 | |
397 | js_copy(J, 1); |
398 | } |
399 | |
400 | static int O_isSealed_walk(js_State *J, js_Property *ref) |
401 | { |
402 | if (ref->left->level) |
403 | if (!O_isSealed_walk(J, ref->left)) |
404 | return 0; |
405 | if (!(ref->atts & JS_DONTCONF)) |
406 | return 0; |
407 | if (ref->right->level) |
408 | if (!O_isSealed_walk(J, ref->right)) |
409 | return 0; |
410 | return 1; |
411 | } |
412 | |
413 | static void O_isSealed(js_State *J) |
414 | { |
415 | js_Object *obj; |
416 | |
417 | if (!js_isobject(J, 1)) |
418 | js_typeerror(J, "not an object" ); |
419 | |
420 | obj = js_toobject(J, 1); |
421 | if (obj->extensible) { |
422 | js_pushboolean(J, 0); |
423 | return; |
424 | } |
425 | |
426 | if (obj->properties->level) |
427 | js_pushboolean(J, O_isSealed_walk(J, obj->properties)); |
428 | else |
429 | js_pushboolean(J, 1); |
430 | } |
431 | |
432 | static void O_freeze_walk(js_State *J, js_Property *ref) |
433 | { |
434 | if (ref->left->level) |
435 | O_freeze_walk(J, ref->left); |
436 | ref->atts |= JS_READONLY | JS_DONTCONF; |
437 | if (ref->right->level) |
438 | O_freeze_walk(J, ref->right); |
439 | } |
440 | |
441 | static void O_freeze(js_State *J) |
442 | { |
443 | js_Object *obj; |
444 | |
445 | if (!js_isobject(J, 1)) |
446 | js_typeerror(J, "not an object" ); |
447 | |
448 | obj = js_toobject(J, 1); |
449 | obj->extensible = 0; |
450 | |
451 | if (obj->properties->level) |
452 | O_freeze_walk(J, obj->properties); |
453 | |
454 | js_copy(J, 1); |
455 | } |
456 | |
457 | static int O_isFrozen_walk(js_State *J, js_Property *ref) |
458 | { |
459 | if (ref->left->level) |
460 | if (!O_isFrozen_walk(J, ref->left)) |
461 | return 0; |
462 | if (!(ref->atts & JS_READONLY)) |
463 | return 0; |
464 | if (!(ref->atts & JS_DONTCONF)) |
465 | return 0; |
466 | if (ref->right->level) |
467 | if (!O_isFrozen_walk(J, ref->right)) |
468 | return 0; |
469 | return 1; |
470 | } |
471 | |
472 | static void O_isFrozen(js_State *J) |
473 | { |
474 | js_Object *obj; |
475 | |
476 | if (!js_isobject(J, 1)) |
477 | js_typeerror(J, "not an object" ); |
478 | |
479 | obj = js_toobject(J, 1); |
480 | |
481 | if (obj->properties->level) { |
482 | if (!O_isFrozen_walk(J, obj->properties)) { |
483 | js_pushboolean(J, 0); |
484 | return; |
485 | } |
486 | } |
487 | |
488 | js_pushboolean(J, !obj->extensible); |
489 | } |
490 | |
491 | void jsB_initobject(js_State *J) |
492 | { |
493 | js_pushobject(J, J->Object_prototype); |
494 | { |
495 | jsB_propf(J, "Object.prototype.toString" , Op_toString, 0); |
496 | jsB_propf(J, "Object.prototype.toLocaleString" , Op_toString, 0); |
497 | jsB_propf(J, "Object.prototype.valueOf" , Op_valueOf, 0); |
498 | jsB_propf(J, "Object.prototype.hasOwnProperty" , Op_hasOwnProperty, 1); |
499 | jsB_propf(J, "Object.prototype.isPrototypeOf" , Op_isPrototypeOf, 1); |
500 | jsB_propf(J, "Object.prototype.propertyIsEnumerable" , Op_propertyIsEnumerable, 1); |
501 | } |
502 | js_newcconstructor(J, jsB_Object, jsB_new_Object, "Object" , 1); |
503 | { |
504 | /* ES5 */ |
505 | jsB_propf(J, "Object.getPrototypeOf" , O_getPrototypeOf, 1); |
506 | jsB_propf(J, "Object.getOwnPropertyDescriptor" , O_getOwnPropertyDescriptor, 2); |
507 | jsB_propf(J, "Object.getOwnPropertyNames" , O_getOwnPropertyNames, 1); |
508 | jsB_propf(J, "Object.create" , O_create, 2); |
509 | jsB_propf(J, "Object.defineProperty" , O_defineProperty, 3); |
510 | jsB_propf(J, "Object.defineProperties" , O_defineProperties, 2); |
511 | jsB_propf(J, "Object.seal" , O_seal, 1); |
512 | jsB_propf(J, "Object.freeze" , O_freeze, 1); |
513 | jsB_propf(J, "Object.preventExtensions" , O_preventExtensions, 1); |
514 | jsB_propf(J, "Object.isSealed" , O_isSealed, 1); |
515 | jsB_propf(J, "Object.isFrozen" , O_isFrozen, 1); |
516 | jsB_propf(J, "Object.isExtensible" , O_isExtensible, 1); |
517 | jsB_propf(J, "Object.keys" , O_keys, 1); |
518 | } |
519 | js_defglobal(J, "Object" , JS_DONTENUM); |
520 | } |
521 | |