1 /*
2  *  Object built-ins
3  */
4 
5 #include "duk_internal.h"
6 
7 /* Needed even when Object built-in disabled. */
duk_bi_object_prototype_to_string(duk_hthread * thr)8 DUK_INTERNAL duk_ret_t duk_bi_object_prototype_to_string(duk_hthread *thr) {
9 	duk_tval *tv;
10 
11 	tv = DUK_HTHREAD_THIS_PTR(thr);
12 	duk_push_class_string_tval(thr, tv, 0 /*avoid_side_effects*/);
13 	return 1;
14 }
15 
16 #if defined(DUK_USE_OBJECT_BUILTIN)
duk_bi_object_constructor(duk_hthread * thr)17 DUK_INTERNAL duk_ret_t duk_bi_object_constructor(duk_hthread *thr) {
18 	duk_uint_t arg_mask;
19 
20 	arg_mask = duk_get_type_mask(thr, 0);
21 
22 	if (!duk_is_constructor_call(thr) &&  /* not a constructor call */
23 	    ((arg_mask & (DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_UNDEFINED)) == 0)) {  /* and argument not null or undefined */
24 		duk_to_object(thr, 0);
25 		return 1;
26 	}
27 
28 	/* Pointer and buffer primitive values are treated like other
29 	 * primitives values which have a fully fledged object counterpart:
30 	 * promote to an object value.  Lightfuncs and plain buffers are
31 	 * coerced with ToObject() even they could also be returned as is.
32 	 */
33 	if (arg_mask & (DUK_TYPE_MASK_OBJECT |
34 	                DUK_TYPE_MASK_STRING |
35 	                DUK_TYPE_MASK_BOOLEAN |
36 	                DUK_TYPE_MASK_NUMBER |
37 	                DUK_TYPE_MASK_POINTER |
38 	                DUK_TYPE_MASK_BUFFER |
39 	                DUK_TYPE_MASK_LIGHTFUNC)) {
40 		/* For DUK_TYPE_OBJECT the coercion is a no-op and could
41 		 * be checked for explicitly, but Object(obj) calls are
42 		 * not very common so opt for minimal footprint.
43 		 */
44 		duk_to_object(thr, 0);
45 		return 1;
46 	}
47 
48 	(void) duk_push_object_helper(thr,
49 	                              DUK_HOBJECT_FLAG_EXTENSIBLE |
50 	                              DUK_HOBJECT_FLAG_FASTREFS |
51 	                              DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
52 	                              DUK_BIDX_OBJECT_PROTOTYPE);
53 	return 1;
54 }
55 #endif  /* DUK_USE_OBJECT_BUILTIN */
56 
57 #if defined(DUK_USE_OBJECT_BUILTIN) && defined(DUK_USE_ES6)
duk_bi_object_constructor_assign(duk_hthread * thr)58 DUK_INTERNAL duk_ret_t duk_bi_object_constructor_assign(duk_hthread *thr) {
59 	duk_idx_t nargs;
60 	duk_int_t idx;
61 
62 	nargs = duk_get_top_require_min(thr, 1 /*min_top*/);
63 
64 	duk_to_object(thr, 0);
65 	for (idx = 1; idx < nargs; idx++) {
66 		/* E7 19.1.2.1 (step 4a) */
67 		if (duk_is_null_or_undefined(thr, idx)) {
68 			continue;
69 		}
70 
71 		/* duk_enum() respects ES2015+ [[OwnPropertyKeys]] ordering, which is
72 		 * convenient here.
73 		 */
74 		duk_to_object(thr, idx);
75 		duk_enum(thr, idx, DUK_ENUM_OWN_PROPERTIES_ONLY);
76 		while (duk_next(thr, -1, 1 /*get_value*/)) {
77 			/* [ target ... enum key value ] */
78 			duk_put_prop(thr, 0);
79 			/* [ target ... enum ] */
80 		}
81 		/* Could pop enumerator, but unnecessary because of duk_set_top()
82 		 * below.
83 		 */
84 	}
85 
86 	duk_set_top(thr, 1);
87 	return 1;
88 }
89 #endif
90 
91 #if defined(DUK_USE_OBJECT_BUILTIN) && defined(DUK_USE_ES6)
duk_bi_object_constructor_is(duk_hthread * thr)92 DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is(duk_hthread *thr) {
93 	DUK_ASSERT_TOP(thr, 2);
94 	duk_push_boolean(thr, duk_samevalue(thr, 0, 1));
95 	return 1;
96 }
97 #endif
98 
99 #if defined(DUK_USE_OBJECT_BUILTIN)
duk_bi_object_constructor_create(duk_hthread * thr)100 DUK_INTERNAL duk_ret_t duk_bi_object_constructor_create(duk_hthread *thr) {
101 	duk_hobject *proto;
102 
103 	DUK_ASSERT_TOP(thr, 2);
104 
105 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
106 	duk_hbufobj_promote_plain(thr, 0);
107 #endif
108 	proto = duk_require_hobject_accept_mask(thr, 0, DUK_TYPE_MASK_NULL);
109 	DUK_ASSERT(proto != NULL || duk_is_null(thr, 0));
110 
111 	(void) duk_push_object_helper_proto(thr,
112 	                                    DUK_HOBJECT_FLAG_EXTENSIBLE |
113 	                                    DUK_HOBJECT_FLAG_FASTREFS |
114 	                                    DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
115 	                                    proto);
116 
117 	if (!duk_is_undefined(thr, 1)) {
118 		/* [ O Properties obj ] */
119 
120 		duk_replace(thr, 0);
121 
122 		/* [ obj Properties ] */
123 
124 		/* Just call the "original" Object.defineProperties() to
125 		 * finish up.
126 		 */
127 
128 		return duk_bi_object_constructor_define_properties(thr);
129 	}
130 
131 	/* [ O Properties obj ] */
132 
133 	return 1;
134 }
135 #endif  /* DUK_USE_OBJECT_BUILTIN */
136 
137 #if defined(DUK_USE_OBJECT_BUILTIN)
duk_bi_object_constructor_define_properties(duk_hthread * thr)138 DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_properties(duk_hthread *thr) {
139 	duk_small_uint_t pass;
140 	duk_uint_t defprop_flags;
141 	duk_hobject *obj;
142 	duk_idx_t idx_value;
143 	duk_hobject *get;
144 	duk_hobject *set;
145 
146 	/* Lightfunc and plain buffer handling by ToObject() coercion. */
147 	obj = duk_require_hobject_promote_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
148 	DUK_ASSERT(obj != NULL);
149 
150 	duk_to_object(thr, 1);        /* properties object */
151 
152 	DUK_DDD(DUK_DDDPRINT("target=%!iT, properties=%!iT",
153 	                     (duk_tval *) duk_get_tval(thr, 0),
154 	                     (duk_tval *) duk_get_tval(thr, 1)));
155 
156 	/*
157 	 *  Two pass approach to processing the property descriptors.
158 	 *  On first pass validate and normalize all descriptors before
159 	 *  any changes are made to the target object.  On second pass
160 	 *  make the actual modifications to the target object.
161 	 *
162 	 *  Right now we'll just use the same normalize/validate helper
163 	 *  on both passes, ignoring its outputs on the first pass.
164 	 */
165 
166 	for (pass = 0; pass < 2; pass++) {
167 		duk_set_top(thr, 2);  /* -> [ hobject props ] */
168 		duk_enum(thr, 1, DUK_ENUM_OWN_PROPERTIES_ONLY | DUK_ENUM_INCLUDE_SYMBOLS /*enum_flags*/);
169 
170 		for (;;) {
171 			duk_hstring *key;
172 
173 			/* [ hobject props enum(props) ] */
174 
175 			duk_set_top(thr, 3);
176 
177 			if (!duk_next(thr, 2, 1 /*get_value*/)) {
178 				break;
179 			}
180 
181 			DUK_DDD(DUK_DDDPRINT("-> key=%!iT, desc=%!iT",
182 			                     (duk_tval *) duk_get_tval(thr, -2),
183 			                     (duk_tval *) duk_get_tval(thr, -1)));
184 
185 			/* [ hobject props enum(props) key desc ] */
186 
187 			duk_hobject_prepare_property_descriptor(thr,
188 			                                        4 /*idx_desc*/,
189 			                                        &defprop_flags,
190 			                                        &idx_value,
191 			                                        &get,
192 			                                        &set);
193 
194 			/* [ hobject props enum(props) key desc [multiple values] ] */
195 
196 			if (pass == 0) {
197 				continue;
198 			}
199 
200 			/* This allows symbols on purpose. */
201 			key = duk_known_hstring(thr, 3);
202 			DUK_ASSERT(key != NULL);
203 
204 			duk_hobject_define_property_helper(thr,
205 			                                   defprop_flags,
206 			                                   obj,
207 			                                   key,
208 			                                   idx_value,
209 			                                   get,
210 			                                   set,
211 			                                   1 /*throw_flag*/);
212 		}
213 	}
214 
215 	/*
216 	 *  Return target object
217 	 */
218 
219 	duk_dup_0(thr);
220 	return 1;
221 }
222 #endif  /* DUK_USE_OBJECT_BUILTIN */
223 
224 #if defined(DUK_USE_OBJECT_BUILTIN)
duk_bi_object_constructor_seal_freeze_shared(duk_hthread * thr)225 DUK_INTERNAL duk_ret_t duk_bi_object_constructor_seal_freeze_shared(duk_hthread *thr) {
226 	DUK_ASSERT_TOP(thr, 1);
227 
228 	duk_seal_freeze_raw(thr, 0, (duk_bool_t) duk_get_current_magic(thr) /*is_freeze*/);
229 	return 1;
230 }
231 #endif  /* DUK_USE_OBJECT_BUILTIN */
232 
233 #if defined(DUK_USE_OBJECT_BUILTIN)
duk_bi_object_constructor_is_sealed_frozen_shared(duk_hthread * thr)234 DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is_sealed_frozen_shared(duk_hthread *thr) {
235 	duk_hobject *h;
236 	duk_bool_t is_frozen;
237 	duk_uint_t mask;
238 
239 	is_frozen = (duk_bool_t) duk_get_current_magic(thr);
240 	mask = duk_get_type_mask(thr, 0);
241 	if (mask & (DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER)) {
242 		DUK_ASSERT(is_frozen == 0 || is_frozen == 1);
243 		duk_push_boolean(thr, (mask & DUK_TYPE_MASK_LIGHTFUNC) ?
244 		                          1 :               /* lightfunc always frozen and sealed */
245 		                          (is_frozen ^ 1)); /* buffer sealed but not frozen (index props writable) */
246 	} else {
247 		/* ES2015 Sections 19.1.2.12, 19.1.2.13: anything other than an object
248 		 * is considered to be already sealed and frozen.
249 		 */
250 		h = duk_get_hobject(thr, 0);
251 		duk_push_boolean(thr, (h == NULL) ||
252 		                      duk_hobject_object_is_sealed_frozen_helper(thr, h, is_frozen /*is_frozen*/));
253 	}
254 	return 1;
255 }
256 #endif  /* DUK_USE_OBJECT_BUILTIN */
257 
258 #if defined(DUK_USE_OBJECT_BUILTIN)
duk_bi_object_prototype_to_locale_string(duk_hthread * thr)259 DUK_INTERNAL duk_ret_t duk_bi_object_prototype_to_locale_string(duk_hthread *thr) {
260 	DUK_ASSERT_TOP(thr, 0);
261 	(void) duk_push_this_coercible_to_object(thr);
262 	duk_get_prop_stridx_short(thr, 0, DUK_STRIDX_TO_STRING);
263 #if 0  /* This is mentioned explicitly in the E5.1 spec, but duk_call_method() checks for it in practice. */
264 	duk_require_callable(thr, 1);
265 #endif
266 	duk_dup_0(thr);  /* -> [ O toString O ] */
267 	duk_call_method(thr, 0);  /* XXX: call method tail call? */
268 	return 1;
269 }
270 #endif  /* DUK_USE_OBJECT_BUILTIN */
271 
272 #if defined(DUK_USE_OBJECT_BUILTIN)
duk_bi_object_prototype_value_of(duk_hthread * thr)273 DUK_INTERNAL duk_ret_t duk_bi_object_prototype_value_of(duk_hthread *thr) {
274 	/* For lightfuncs and plain buffers, returns Object() coerced. */
275 	(void) duk_push_this_coercible_to_object(thr);
276 	return 1;
277 }
278 #endif  /* DUK_USE_OBJECT_BUILTIN */
279 
280 #if defined(DUK_USE_OBJECT_BUILTIN)
duk_bi_object_prototype_is_prototype_of(duk_hthread * thr)281 DUK_INTERNAL duk_ret_t duk_bi_object_prototype_is_prototype_of(duk_hthread *thr) {
282 	duk_hobject *h_v;
283 	duk_hobject *h_obj;
284 
285 	DUK_ASSERT_TOP(thr, 1);
286 
287 	h_v = duk_get_hobject(thr, 0);
288 	if (!h_v) {
289 		duk_push_false(thr);  /* XXX: tail call: return duk_push_false(thr) */
290 		return 1;
291 	}
292 
293 	h_obj = duk_push_this_coercible_to_object(thr);
294 	DUK_ASSERT(h_obj != NULL);
295 
296 	/* E5.1 Section 15.2.4.6, step 3.a, lookup proto once before compare.
297 	 * Prototype loops should cause an error to be thrown.
298 	 */
299 	duk_push_boolean(thr, duk_hobject_prototype_chain_contains(thr, DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_v), h_obj, 0 /*ignore_loop*/));
300 	return 1;
301 }
302 #endif  /* DUK_USE_OBJECT_BUILTIN */
303 
304 #if defined(DUK_USE_OBJECT_BUILTIN)
duk_bi_object_prototype_has_own_property(duk_hthread * thr)305 DUK_INTERNAL duk_ret_t duk_bi_object_prototype_has_own_property(duk_hthread *thr) {
306 	return (duk_ret_t) duk_hobject_object_ownprop_helper(thr, 0 /*required_desc_flags*/);
307 }
308 #endif  /* DUK_USE_OBJECT_BUILTIN */
309 
310 #if defined(DUK_USE_OBJECT_BUILTIN)
duk_bi_object_prototype_property_is_enumerable(duk_hthread * thr)311 DUK_INTERNAL duk_ret_t duk_bi_object_prototype_property_is_enumerable(duk_hthread *thr) {
312 	return (duk_ret_t) duk_hobject_object_ownprop_helper(thr, DUK_PROPDESC_FLAG_ENUMERABLE /*required_desc_flags*/);
313 }
314 #endif  /* DUK_USE_OBJECT_BUILTIN */
315 
316 #if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
317 /* Shared helper to implement Object.getPrototypeOf,
318  * Object.prototype.__proto__ getter, and Reflect.getPrototypeOf.
319  *
320  * http://www.ecma-international.org/ecma-262/6.0/index.html#sec-get-object.prototype.__proto__
321  */
duk_bi_object_getprototype_shared(duk_hthread * thr)322 DUK_INTERNAL duk_ret_t duk_bi_object_getprototype_shared(duk_hthread *thr) {
323 	/*
324 	 *  magic = 0: __proto__ getter
325 	 *  magic = 1: Object.getPrototypeOf()
326 	 *  magic = 2: Reflect.getPrototypeOf()
327 	 */
328 
329 	duk_hobject *h;
330 	duk_hobject *proto;
331 	duk_tval *tv;
332 	duk_int_t magic;
333 
334 	magic = duk_get_current_magic(thr);
335 
336 	if (magic == 0) {
337 		DUK_ASSERT_TOP(thr, 0);
338 		duk_push_this_coercible_to_object(thr);
339 	}
340 	DUK_ASSERT(duk_get_top(thr) >= 1);
341 	if (magic < 2) {
342 		/* ES2015 Section 19.1.2.9, step 1 */
343 		duk_to_object(thr, 0);
344 	}
345 	tv = DUK_GET_TVAL_POSIDX(thr, 0);
346 
347 	switch (DUK_TVAL_GET_TAG(tv)) {
348 	case DUK_TAG_BUFFER:
349 		proto = thr->builtins[DUK_BIDX_UINT8ARRAY_PROTOTYPE];
350 		break;
351 	case DUK_TAG_LIGHTFUNC:
352 		proto = thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE];
353 		break;
354 	case DUK_TAG_OBJECT:
355 		h = DUK_TVAL_GET_OBJECT(tv);
356 		proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h);
357 		break;
358 	default:
359 		/* This implicitly handles CheckObjectCoercible() caused
360 		 * TypeError.
361 		 */
362 		DUK_DCERROR_TYPE_INVALID_ARGS(thr);
363 	}
364 	if (proto != NULL) {
365 		duk_push_hobject(thr, proto);
366 	} else {
367 		duk_push_null(thr);
368 	}
369 	return 1;
370 }
371 #endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
372 
373 #if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
374 /* Shared helper to implement ES2015 Object.setPrototypeOf,
375  * Object.prototype.__proto__ setter, and Reflect.setPrototypeOf.
376  *
377  * http://www.ecma-international.org/ecma-262/6.0/index.html#sec-get-object.prototype.__proto__
378  * http://www.ecma-international.org/ecma-262/6.0/index.html#sec-object.setprototypeof
379  */
duk_bi_object_setprototype_shared(duk_hthread * thr)380 DUK_INTERNAL duk_ret_t duk_bi_object_setprototype_shared(duk_hthread *thr) {
381 	/*
382 	 *  magic = 0: __proto__ setter
383 	 *  magic = 1: Object.setPrototypeOf()
384 	 *  magic = 2: Reflect.setPrototypeOf()
385 	 */
386 
387 	duk_hobject *h_obj;
388 	duk_hobject *h_new_proto;
389 	duk_hobject *h_curr;
390 	duk_ret_t ret_success = 1;  /* retval for success path */
391 	duk_uint_t mask;
392 	duk_int_t magic;
393 
394 	/* Preliminaries for __proto__ and setPrototypeOf (E6 19.1.2.18 steps 1-4). */
395 	magic = duk_get_current_magic(thr);
396 	if (magic == 0) {
397 		duk_push_this_check_object_coercible(thr);
398 		duk_insert(thr, 0);
399 		if (!duk_check_type_mask(thr, 1, DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_OBJECT)) {
400 			return 0;
401 		}
402 
403 		/* __proto__ setter returns 'undefined' on success unlike the
404 		 * setPrototypeOf() call which returns the target object.
405 		 */
406 		ret_success = 0;
407 	} else {
408 		if (magic == 1) {
409 			duk_require_object_coercible(thr, 0);
410 		} else {
411 			duk_require_hobject_accept_mask(thr, 0,
412 			                                DUK_TYPE_MASK_LIGHTFUNC |
413 			                                DUK_TYPE_MASK_BUFFER);
414 		}
415 		duk_require_type_mask(thr, 1, DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_OBJECT);
416 	}
417 
418 	h_new_proto = duk_get_hobject(thr, 1);
419 	/* h_new_proto may be NULL */
420 
421 	mask = duk_get_type_mask(thr, 0);
422 	if (mask & (DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER)) {
423 		duk_hobject *curr_proto;
424 		curr_proto = thr->builtins[(mask & DUK_TYPE_MASK_LIGHTFUNC) ?
425 		                               DUK_BIDX_FUNCTION_PROTOTYPE :
426 		                               DUK_BIDX_UINT8ARRAY_PROTOTYPE];
427 		if (h_new_proto == curr_proto) {
428 			goto skip;
429 		}
430 		goto fail_nonextensible;
431 	}
432 	h_obj = duk_get_hobject(thr, 0);
433 	if (h_obj == NULL) {
434 		goto skip;
435 	}
436 	DUK_ASSERT(h_obj != NULL);
437 
438 	/* [[SetPrototypeOf]] standard behavior, E6 9.1.2. */
439 	/* TODO: implement Proxy object support here */
440 
441 	if (h_new_proto == DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_obj)) {
442 		goto skip;
443 	}
444 	if (!DUK_HOBJECT_HAS_EXTENSIBLE(h_obj)) {
445 		goto fail_nonextensible;
446 	}
447 	for (h_curr = h_new_proto; h_curr != NULL; h_curr = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_curr)) {
448 		/* Loop prevention. */
449 		if (h_curr == h_obj) {
450 			goto fail_loop;
451 		}
452 	}
453 	DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h_obj, h_new_proto);
454 	/* fall thru */
455 
456  skip:
457 	duk_set_top(thr, 1);
458 	if (magic == 2) {
459 		duk_push_true(thr);
460 	}
461 	return ret_success;
462 
463  fail_nonextensible:
464  fail_loop:
465 	if (magic != 2) {
466 		DUK_DCERROR_TYPE_INVALID_ARGS(thr);
467 	} else {
468 		duk_push_false(thr);
469 		return 1;
470 	}
471 }
472 #endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
473 
474 #if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
duk_bi_object_constructor_define_property(duk_hthread * thr)475 DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_property(duk_hthread *thr) {
476 	/*
477 	 *  magic = 0: Object.defineProperty()
478 	 *  magic = 1: Reflect.defineProperty()
479 	 */
480 
481 	duk_hobject *obj;
482 	duk_hstring *key;
483 	duk_hobject *get;
484 	duk_hobject *set;
485 	duk_idx_t idx_value;
486 	duk_uint_t defprop_flags;
487 	duk_small_uint_t magic;
488 	duk_bool_t throw_flag;
489 	duk_bool_t ret;
490 
491 	DUK_ASSERT(thr != NULL);
492 
493 	DUK_DDD(DUK_DDDPRINT("Object.defineProperty(): ctx=%p obj=%!T key=%!T desc=%!T",
494 	                     (void *) thr,
495 	                     (duk_tval *) duk_get_tval(thr, 0),
496 	                     (duk_tval *) duk_get_tval(thr, 1),
497 	                     (duk_tval *) duk_get_tval(thr, 2)));
498 
499 	/* [ obj key desc ] */
500 
501 	magic = (duk_small_uint_t) duk_get_current_magic(thr);
502 
503 	/* Lightfuncs are currently supported by coercing to a temporary
504 	 * Function object; changes will be allowed (the coerced value is
505 	 * extensible) but will be lost.  Same for plain buffers.
506 	 */
507 	obj = duk_require_hobject_promote_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
508 	DUK_ASSERT(obj != NULL);
509 	key = duk_to_property_key_hstring(thr, 1);
510 	(void) duk_require_hobject(thr, 2);
511 
512 	DUK_ASSERT(obj != NULL);
513 	DUK_ASSERT(key != NULL);
514 	DUK_ASSERT(duk_get_hobject(thr, 2) != NULL);
515 
516 	/*
517 	 *  Validate and convert argument property descriptor (an ECMAScript
518 	 *  object) into a set of defprop_flags and possibly property value,
519 	 *  getter, and/or setter values on the value stack.
520 	 *
521 	 *  Lightfunc set/get values are coerced to full Functions.
522 	 */
523 
524 	duk_hobject_prepare_property_descriptor(thr,
525 	                                        2 /*idx_desc*/,
526 	                                        &defprop_flags,
527 	                                        &idx_value,
528 	                                        &get,
529 	                                        &set);
530 
531 	/*
532 	 *  Use Object.defineProperty() helper for the actual operation.
533 	 */
534 
535 	DUK_ASSERT(magic == 0U || magic == 1U);
536 	throw_flag = magic ^ 1U;
537 	ret = duk_hobject_define_property_helper(thr,
538 	                                         defprop_flags,
539 	                                         obj,
540 	                                         key,
541 	                                         idx_value,
542 	                                         get,
543 	                                         set,
544 	                                         throw_flag);
545 
546 	/* Ignore the normalize/validate helper outputs on the value stack,
547 	 * they're popped automatically.
548 	 */
549 
550 	if (magic == 0U) {
551 		/* Object.defineProperty(): return target object. */
552 		duk_push_hobject(thr, obj);
553 	} else {
554 		/* Reflect.defineProperty(): return success/fail. */
555 		duk_push_boolean(thr, ret);
556 	}
557 	return 1;
558 }
559 #endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
560 
561 #if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
duk_bi_object_constructor_get_own_property_descriptor(duk_hthread * thr)562 DUK_INTERNAL duk_ret_t duk_bi_object_constructor_get_own_property_descriptor(duk_hthread *thr) {
563 	DUK_ASSERT_TOP(thr, 2);
564 
565 	/* ES2015 Section 19.1.2.6, step 1 */
566 	if (duk_get_current_magic(thr) == 0) {
567 		duk_to_object(thr, 0);
568 	}
569 
570 	/* [ obj key ] */
571 
572 	duk_hobject_object_get_own_property_descriptor(thr, -2);
573 	return 1;
574 }
575 #endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
576 
577 #if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
duk_bi_object_constructor_is_extensible(duk_hthread * thr)578 DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is_extensible(duk_hthread *thr) {
579 	/*
580 	 *  magic = 0: Object.isExtensible()
581 	 *  magic = 1: Reflect.isExtensible()
582 	 */
583 
584 	duk_hobject *h;
585 
586 	if (duk_get_current_magic(thr) == 0) {
587 		h = duk_get_hobject(thr, 0);
588 	} else {
589 		/* Reflect.isExtensible(): throw if non-object, but we accept lightfuncs
590 		 * and plain buffers here because they pretend to be objects.
591 		 */
592 		h = duk_require_hobject_accept_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
593 	}
594 
595 	duk_push_boolean(thr, (h != NULL) && DUK_HOBJECT_HAS_EXTENSIBLE(h));
596 	return 1;
597 }
598 #endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
599 
600 #if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
601 /* Shared helper for various key/symbol listings, magic:
602  * 0=Object.keys()
603  * 1=Object.getOwnPropertyNames(),
604  * 2=Object.getOwnPropertySymbols(),
605  * 3=Reflect.ownKeys()
606  */
607 DUK_LOCAL const duk_small_uint_t duk__object_keys_enum_flags[4] = {
608 	/* Object.keys() */
609 	DUK_ENUM_OWN_PROPERTIES_ONLY |
610 	    DUK_ENUM_NO_PROXY_BEHAVIOR,
611 
612 	/* Object.getOwnPropertyNames() */
613 	DUK_ENUM_INCLUDE_NONENUMERABLE |
614 	    DUK_ENUM_OWN_PROPERTIES_ONLY |
615 	    DUK_ENUM_NO_PROXY_BEHAVIOR,
616 
617 	/* Object.getOwnPropertySymbols() */
618 	DUK_ENUM_INCLUDE_SYMBOLS |
619 	    DUK_ENUM_OWN_PROPERTIES_ONLY |
620 	    DUK_ENUM_EXCLUDE_STRINGS |
621 	    DUK_ENUM_INCLUDE_NONENUMERABLE |
622 	    DUK_ENUM_NO_PROXY_BEHAVIOR,
623 
624 	/* Reflect.ownKeys() */
625 	DUK_ENUM_INCLUDE_SYMBOLS |
626 	    DUK_ENUM_OWN_PROPERTIES_ONLY |
627 	    DUK_ENUM_INCLUDE_NONENUMERABLE |
628 	    DUK_ENUM_NO_PROXY_BEHAVIOR
629 };
630 
duk_bi_object_constructor_keys_shared(duk_hthread * thr)631 DUK_INTERNAL duk_ret_t duk_bi_object_constructor_keys_shared(duk_hthread *thr) {
632 	duk_hobject *obj;
633 #if defined(DUK_USE_ES6_PROXY)
634 	duk_hobject *h_proxy_target;
635 	duk_hobject *h_proxy_handler;
636 	duk_hobject *h_trap_result;
637 #endif
638 	duk_small_uint_t enum_flags;
639 	duk_int_t magic;
640 
641 	DUK_ASSERT_TOP(thr, 1);
642 
643 	magic = duk_get_current_magic(thr);
644 	if (magic == 3) {
645 		/* ES2015 Section 26.1.11 requires a TypeError for non-objects.  Lightfuncs
646 		 * and plain buffers pretend to be objects, so accept those too.
647 		 */
648 		obj = duk_require_hobject_promote_mask(thr, 0, DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
649 	} else {
650 		/* ES2015: ToObject coerce. */
651 		obj = duk_to_hobject(thr, 0);
652 	}
653 	DUK_ASSERT(obj != NULL);
654 	DUK_UNREF(obj);
655 
656 	/* XXX: proxy chains */
657 
658 #if defined(DUK_USE_ES6_PROXY)
659 	/* XXX: better sharing of code between proxy target call sites */
660 	if (DUK_LIKELY(!duk_hobject_proxy_check(obj,
661 	                                        &h_proxy_target,
662 	                                        &h_proxy_handler))) {
663 		goto skip_proxy;
664 	}
665 
666 	duk_push_hobject(thr, h_proxy_handler);
667 	if (!duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_OWN_KEYS)) {
668 		/* Careful with reachability here: don't pop 'obj' before pushing
669 		 * proxy target.
670 		 */
671 		DUK_DDD(DUK_DDDPRINT("no ownKeys trap, get keys of target instead"));
672 		duk_pop_2(thr);
673 		duk_push_hobject(thr, h_proxy_target);
674 		duk_replace(thr, 0);
675 		DUK_ASSERT_TOP(thr, 1);
676 		goto skip_proxy;
677 	}
678 
679 	/* [ obj handler trap ] */
680 	duk_insert(thr, -2);
681 	duk_push_hobject(thr, h_proxy_target);  /* -> [ obj trap handler target ] */
682 	duk_call_method(thr, 1 /*nargs*/);      /* -> [ obj trap_result ] */
683 	h_trap_result = duk_require_hobject(thr, -1);
684 	DUK_UNREF(h_trap_result);
685 
686 	magic = duk_get_current_magic(thr);
687 	DUK_ASSERT(magic >= 0 && magic < (duk_int_t) (sizeof(duk__object_keys_enum_flags) / sizeof(duk_small_uint_t)));
688 	enum_flags = duk__object_keys_enum_flags[magic];
689 
690 	duk_proxy_ownkeys_postprocess(thr, h_proxy_target, enum_flags);
691 	return 1;
692 
693  skip_proxy:
694 #endif  /* DUK_USE_ES6_PROXY */
695 
696 	DUK_ASSERT_TOP(thr, 1);
697 	magic = duk_get_current_magic(thr);
698 	DUK_ASSERT(magic >= 0 && magic < (duk_int_t) (sizeof(duk__object_keys_enum_flags) / sizeof(duk_small_uint_t)));
699 	enum_flags = duk__object_keys_enum_flags[magic];
700 	return duk_hobject_get_enumerated_keys(thr, enum_flags);
701 }
702 #endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
703 
704 #if defined(DUK_USE_OBJECT_BUILTIN) || defined(DUK_USE_REFLECT_BUILTIN)
duk_bi_object_constructor_prevent_extensions(duk_hthread * thr)705 DUK_INTERNAL duk_ret_t duk_bi_object_constructor_prevent_extensions(duk_hthread *thr) {
706 	/*
707 	 *  magic = 0: Object.preventExtensions()
708 	 *  magic = 1: Reflect.preventExtensions()
709 	 */
710 
711 	duk_hobject *h;
712 	duk_uint_t mask;
713 	duk_int_t magic;
714 
715 	magic = duk_get_current_magic(thr);
716 
717 	/* Silent success for lightfuncs and plain buffers always. */
718 	mask = DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER;
719 
720 	/* Object.preventExtensions() silent success for non-object. */
721 	if (magic == 0) {
722 		mask |= DUK_TYPE_MASK_UNDEFINED |
723 		        DUK_TYPE_MASK_NULL |
724 		        DUK_TYPE_MASK_BOOLEAN |
725 		        DUK_TYPE_MASK_NUMBER |
726 		        DUK_TYPE_MASK_STRING |
727 		        DUK_TYPE_MASK_POINTER;
728 	}
729 
730 	if (duk_check_type_mask(thr, 0, mask)) {
731 		/* Not an object, already non-extensible so always success. */
732 		goto done;
733 	}
734 	h = duk_require_hobject(thr, 0);
735 	DUK_ASSERT(h != NULL);
736 
737 	DUK_HOBJECT_CLEAR_EXTENSIBLE(h);
738 
739 	/* A non-extensible object cannot gain any more properties,
740 	 * so this is a good time to compact.
741 	 */
742 	duk_hobject_compact_props(thr, h);
743 
744  done:
745 	if (magic == 1) {
746 		duk_push_true(thr);
747 	}
748 	return 1;
749 }
750 #endif  /* DUK_USE_OBJECT_BUILTIN || DUK_USE_REFLECT_BUILTIN */
751 
752 /*
753  *  __defineGetter__, __defineSetter__, __lookupGetter__, __lookupSetter__
754  */
755 
756 #if defined(DUK_USE_ES8)
duk_bi_object_prototype_defineaccessor(duk_hthread * thr)757 DUK_INTERNAL duk_ret_t duk_bi_object_prototype_defineaccessor(duk_hthread *thr) {
758 	duk_push_this(thr);
759 	duk_insert(thr, 0);
760 	duk_to_object(thr, 0);
761 	duk_require_callable(thr, 2);
762 
763 	/* [ ToObject(this) key getter/setter ] */
764 
765 	/* ToPropertyKey() coercion is not needed, duk_def_prop() does it. */
766 	duk_def_prop(thr, 0, DUK_DEFPROP_SET_ENUMERABLE |
767 	                     DUK_DEFPROP_SET_CONFIGURABLE |
768 	                     (duk_get_current_magic(thr) ? DUK_DEFPROP_HAVE_SETTER : DUK_DEFPROP_HAVE_GETTER));
769 	return 0;
770 }
duk_bi_object_prototype_lookupaccessor(duk_hthread * thr)771 DUK_INTERNAL duk_ret_t duk_bi_object_prototype_lookupaccessor(duk_hthread *thr) {
772 	duk_uint_t sanity;
773 
774 	duk_push_this(thr);
775 	duk_to_object(thr, -1);
776 
777 	/* XXX: Prototype walk (with sanity) should be a core property
778 	 * operation, could add a flag to e.g. duk_get_prop_desc().
779 	 */
780 
781 	/* ToPropertyKey() coercion is not needed, duk_get_prop_desc() does it. */
782 	sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
783 	while (!duk_is_undefined(thr, -1)) {
784 		/* [ key obj ] */
785 		duk_dup(thr, 0);
786 		duk_get_prop_desc(thr, 1, 0 /*flags*/);
787 		if (!duk_is_undefined(thr, -1)) {
788 			duk_get_prop_stridx(thr, -1, (duk_get_current_magic(thr) != 0 ? DUK_STRIDX_SET : DUK_STRIDX_GET));
789 			return 1;
790 		}
791 		duk_pop(thr);
792 
793 		if (DUK_UNLIKELY(sanity-- == 0)) {
794 			DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT);
795 			DUK_WO_NORETURN(return 0;);
796 		}
797 
798 		duk_get_prototype(thr, -1);
799 		duk_remove(thr, -2);
800 	}
801 	return 1;
802 }
803 #endif  /* DUK_USE_ES8 */
804