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