1 #include "jsi.h"
2 #include "jsvalue.h"
3 #include "jsbuiltin.h"
4 
jsB_new_Object(js_State * J)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 
jsB_Object(js_State * J)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 
Op_toString(js_State * J)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, "[object 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 
Op_valueOf(js_State * J)56 static void Op_valueOf(js_State *J)
57 {
58 	js_copy(J, 0);
59 }
60 
Op_hasOwnProperty(js_State * J)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 
Op_isPrototypeOf(js_State * J)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 
Op_propertyIsEnumerable(js_State * J)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 
O_getPrototypeOf(js_State * J)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 
O_getOwnPropertyDescriptor(js_State * J)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 
O_getOwnPropertyNames_walk(js_State * J,js_Property * ref,int i)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 
O_getOwnPropertyNames(js_State * J)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 
ToPropertyDescriptor(js_State * J,js_Object * obj,const char * name,js_Object * desc)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 
O_defineProperty(js_State * J)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 
O_defineProperties_walk(js_State * J,js_Property * ref)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 
O_defineProperties(js_State * J)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 
O_create_walk(js_State * J,js_Object * obj,js_Property * ref)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 
O_create(js_State * J)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 
O_keys_walk(js_State * J,js_Property * ref,int i)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 
O_keys(js_State * J)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 
O_preventExtensions(js_State * J)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 
O_isExtensible(js_State * J)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 
O_seal_walk(js_State * J,js_Property * ref)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 
O_seal(js_State * J)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 
O_isSealed_walk(js_State * J,js_Property * ref)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 
O_isSealed(js_State * J)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 
O_freeze_walk(js_State * J,js_Property * ref)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 
O_freeze(js_State * J)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 
O_isFrozen_walk(js_State * J,js_Property * ref)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 
O_isFrozen(js_State * J)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 
jsB_initobject(js_State * J)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