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