]>
Commit | Line | Data |
---|---|---|
1e59de90 TL |
1 | /* |
2 | * Object built-ins | |
3 | */ | |
4 | ||
5 | #include "duk_internal.h" | |
6 | ||
7 | DUK_INTERNAL duk_ret_t duk_bi_object_constructor(duk_context *ctx) { | |
8 | if (!duk_is_constructor_call(ctx) && | |
9 | !duk_is_null_or_undefined(ctx, 0)) { | |
10 | duk_to_object(ctx, 0); | |
11 | return 1; | |
12 | } | |
13 | ||
14 | if (duk_is_object(ctx, 0)) { | |
15 | return 1; | |
16 | } | |
17 | ||
18 | /* Pointer and buffer primitive values are treated like other | |
19 | * primitives values which have a fully fledged object counterpart: | |
20 | * promote to an object value. Lightfuncs are coerced with | |
21 | * ToObject() even they could also be returned as is. | |
22 | */ | |
23 | if (duk_check_type_mask(ctx, 0, DUK_TYPE_MASK_STRING | | |
24 | DUK_TYPE_MASK_BOOLEAN | | |
25 | DUK_TYPE_MASK_NUMBER | | |
26 | DUK_TYPE_MASK_POINTER | | |
27 | DUK_TYPE_MASK_BUFFER | | |
28 | DUK_TYPE_MASK_LIGHTFUNC)) { | |
29 | duk_to_object(ctx, 0); | |
30 | return 1; | |
31 | } | |
32 | ||
33 | duk_push_object_helper(ctx, | |
34 | DUK_HOBJECT_FLAG_EXTENSIBLE | | |
35 | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT), | |
36 | DUK_BIDX_OBJECT_PROTOTYPE); | |
37 | return 1; | |
38 | } | |
39 | ||
40 | /* Shared helper to implement Object.getPrototypeOf and the ES6 | |
41 | * Object.prototype.__proto__ getter. | |
42 | * | |
43 | * http://www.ecma-international.org/ecma-262/6.0/index.html#sec-get-object.prototype.__proto__ | |
44 | */ | |
45 | DUK_INTERNAL duk_ret_t duk_bi_object_getprototype_shared(duk_context *ctx) { | |
46 | duk_hthread *thr = (duk_hthread *) ctx; | |
47 | duk_hobject *h; | |
48 | duk_hobject *proto; | |
49 | ||
50 | DUK_UNREF(thr); | |
51 | ||
52 | /* magic: 0=getter call, 1=Object.getPrototypeOf */ | |
53 | if (duk_get_current_magic(ctx) == 0) { | |
54 | duk_push_this_coercible_to_object(ctx); | |
55 | duk_insert(ctx, 0); | |
56 | } | |
57 | ||
58 | h = duk_require_hobject_or_lfunc(ctx, 0); | |
59 | /* h is NULL for lightfunc */ | |
60 | ||
61 | /* XXX: should the API call handle this directly, i.e. attempt | |
62 | * to duk_push_hobject(ctx, null) would push a null instead? | |
63 | * (On the other hand 'undefined' would be just as logical, but | |
64 | * not wanted here.) | |
65 | */ | |
66 | ||
67 | if (h == NULL) { | |
68 | duk_push_hobject_bidx(ctx, DUK_BIDX_FUNCTION_PROTOTYPE); | |
69 | } else { | |
70 | proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h); | |
71 | if (proto) { | |
72 | duk_push_hobject(ctx, proto); | |
73 | } else { | |
74 | duk_push_null(ctx); | |
75 | } | |
76 | } | |
77 | return 1; | |
78 | } | |
79 | ||
80 | /* Shared helper to implement ES6 Object.setPrototypeOf and | |
81 | * Object.prototype.__proto__ setter. | |
82 | * | |
83 | * http://www.ecma-international.org/ecma-262/6.0/index.html#sec-get-object.prototype.__proto__ | |
84 | * http://www.ecma-international.org/ecma-262/6.0/index.html#sec-object.setprototypeof | |
85 | */ | |
86 | DUK_INTERNAL duk_ret_t duk_bi_object_setprototype_shared(duk_context *ctx) { | |
87 | duk_hthread *thr = (duk_hthread *) ctx; | |
88 | duk_hobject *h_obj; | |
89 | duk_hobject *h_new_proto; | |
90 | duk_hobject *h_curr; | |
91 | duk_ret_t ret_success = 1; /* retval for success path */ | |
92 | ||
93 | /* Preliminaries for __proto__ and setPrototypeOf (E6 19.1.2.18 steps 1-4); | |
94 | * magic: 0=setter call, 1=Object.setPrototypeOf | |
95 | */ | |
96 | if (duk_get_current_magic(ctx) == 0) { | |
97 | duk_push_this_check_object_coercible(ctx); | |
98 | duk_insert(ctx, 0); | |
99 | if (!duk_check_type_mask(ctx, 1, DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_OBJECT)) { | |
100 | return 0; | |
101 | } | |
102 | ||
103 | /* __proto__ setter returns 'undefined' on success unlike the | |
104 | * setPrototypeOf() call which returns the target object. | |
105 | */ | |
106 | ret_success = 0; | |
107 | } else { | |
108 | duk_require_object_coercible(ctx, 0); | |
109 | duk_require_type_mask(ctx, 1, DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_OBJECT); | |
110 | } | |
111 | ||
112 | h_new_proto = duk_get_hobject(ctx, 1); | |
113 | /* h_new_proto may be NULL */ | |
114 | if (duk_is_lightfunc(ctx, 0)) { | |
115 | if (h_new_proto == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]) { | |
116 | goto skip; | |
117 | } | |
118 | goto fail_nonextensible; | |
119 | } | |
120 | h_obj = duk_get_hobject(ctx, 0); | |
121 | if (!h_obj) { | |
122 | goto skip; | |
123 | } | |
124 | DUK_ASSERT(h_obj != NULL); | |
125 | ||
126 | /* [[SetPrototypeOf]] standard behavior, E6 9.1.2 */ | |
127 | /* TODO: implement Proxy object support here */ | |
128 | ||
129 | if (h_new_proto == DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_obj)) { | |
130 | goto skip; | |
131 | } | |
132 | if (!DUK_HOBJECT_HAS_EXTENSIBLE(h_obj)) { | |
133 | goto fail_nonextensible; | |
134 | } | |
135 | for (h_curr = h_new_proto; h_curr != NULL; h_curr = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_curr)) { | |
136 | /* Loop prevention */ | |
137 | if (h_curr == h_obj) { | |
138 | goto fail_loop; | |
139 | } | |
140 | } | |
141 | DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h_obj, h_new_proto); | |
142 | /* fall thru */ | |
143 | ||
144 | skip: | |
145 | duk_set_top(ctx, 1); | |
146 | return ret_success; | |
147 | ||
148 | fail_nonextensible: | |
149 | fail_loop: | |
150 | return DUK_RET_TYPE_ERROR; | |
151 | } | |
152 | ||
153 | DUK_INTERNAL duk_ret_t duk_bi_object_constructor_get_own_property_descriptor(duk_context *ctx) { | |
154 | /* XXX: no need for indirect call */ | |
155 | return duk_hobject_object_get_own_property_descriptor(ctx); | |
156 | } | |
157 | ||
158 | DUK_INTERNAL duk_ret_t duk_bi_object_constructor_create(duk_context *ctx) { | |
159 | duk_tval *tv; | |
160 | duk_hobject *proto = NULL; | |
161 | ||
162 | DUK_ASSERT_TOP(ctx, 2); | |
163 | ||
164 | tv = duk_get_tval(ctx, 0); | |
165 | DUK_ASSERT(tv != NULL); | |
166 | if (DUK_TVAL_IS_NULL(tv)) { | |
167 | ; | |
168 | } else if (DUK_TVAL_IS_OBJECT(tv)) { | |
169 | proto = DUK_TVAL_GET_OBJECT(tv); | |
170 | DUK_ASSERT(proto != NULL); | |
171 | } else { | |
172 | return DUK_RET_TYPE_ERROR; | |
173 | } | |
174 | ||
175 | (void) duk_push_object_helper_proto(ctx, | |
176 | DUK_HOBJECT_FLAG_EXTENSIBLE | | |
177 | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT), | |
178 | proto); | |
179 | ||
180 | if (!duk_is_undefined(ctx, 1)) { | |
181 | /* [ O Properties obj ] */ | |
182 | ||
183 | duk_replace(ctx, 0); | |
184 | ||
185 | /* [ obj Properties ] */ | |
186 | ||
187 | /* Just call the "original" Object.defineProperties() to | |
188 | * finish up. | |
189 | */ | |
190 | ||
191 | return duk_bi_object_constructor_define_properties(ctx); | |
192 | } | |
193 | ||
194 | /* [ O Properties obj ] */ | |
195 | ||
196 | return 1; | |
197 | } | |
198 | ||
199 | DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_property(duk_context *ctx) { | |
200 | duk_hobject *obj; | |
201 | duk_hstring *key; | |
202 | duk_hobject *get; | |
203 | duk_hobject *set; | |
204 | duk_idx_t idx_value; | |
205 | duk_uint_t defprop_flags; | |
206 | ||
207 | DUK_ASSERT(ctx != NULL); | |
208 | ||
209 | DUK_DDD(DUK_DDDPRINT("Object.defineProperty(): ctx=%p obj=%!T key=%!T desc=%!T", | |
210 | (void *) ctx, | |
211 | (duk_tval *) duk_get_tval(ctx, 0), | |
212 | (duk_tval *) duk_get_tval(ctx, 1), | |
213 | (duk_tval *) duk_get_tval(ctx, 2))); | |
214 | ||
215 | /* [ obj key desc ] */ | |
216 | ||
217 | /* Lightfuncs are currently supported by coercing to a temporary | |
218 | * Function object; changes will be allowed (the coerced value is | |
219 | * extensible) but will be lost. | |
220 | */ | |
221 | obj = duk_require_hobject_or_lfunc_coerce(ctx, 0); | |
222 | (void) duk_to_string(ctx, 1); | |
223 | key = duk_require_hstring(ctx, 1); | |
224 | (void) duk_require_hobject(ctx, 2); | |
225 | ||
226 | DUK_ASSERT(obj != NULL); | |
227 | DUK_ASSERT(key != NULL); | |
228 | DUK_ASSERT(duk_get_hobject(ctx, 2) != NULL); | |
229 | ||
230 | /* | |
231 | * Validate and convert argument property descriptor (an Ecmascript | |
232 | * object) into a set of defprop_flags and possibly property value, | |
233 | * getter, and/or setter values on the value stack. | |
234 | * | |
235 | * Lightfunc set/get values are coerced to full Functions. | |
236 | */ | |
237 | ||
238 | duk_hobject_prepare_property_descriptor(ctx, | |
239 | 2 /*idx_desc*/, | |
240 | &defprop_flags, | |
241 | &idx_value, | |
242 | &get, | |
243 | &set); | |
244 | ||
245 | /* | |
246 | * Use Object.defineProperty() helper for the actual operation. | |
247 | */ | |
248 | ||
249 | duk_hobject_define_property_helper(ctx, | |
250 | defprop_flags, | |
251 | obj, | |
252 | key, | |
253 | idx_value, | |
254 | get, | |
255 | set); | |
256 | ||
257 | /* Ignore the normalize/validate helper outputs on the value stack, | |
258 | * they're popped automatically. | |
259 | */ | |
260 | ||
261 | /* | |
262 | * Return target object. | |
263 | */ | |
264 | ||
265 | duk_push_hobject(ctx, obj); | |
266 | return 1; | |
267 | } | |
268 | ||
269 | DUK_INTERNAL duk_ret_t duk_bi_object_constructor_define_properties(duk_context *ctx) { | |
270 | duk_small_uint_t pass; | |
271 | duk_uint_t defprop_flags; | |
272 | duk_hobject *obj; | |
273 | duk_idx_t idx_value; | |
274 | duk_hobject *get; | |
275 | duk_hobject *set; | |
276 | ||
277 | /* Lightfunc handling by ToObject() coercion. */ | |
278 | obj = duk_require_hobject_or_lfunc_coerce(ctx, 0); /* target */ | |
279 | DUK_ASSERT(obj != NULL); | |
280 | ||
281 | duk_to_object(ctx, 1); /* properties object */ | |
282 | ||
283 | DUK_DDD(DUK_DDDPRINT("target=%!iT, properties=%!iT", | |
284 | (duk_tval *) duk_get_tval(ctx, 0), | |
285 | (duk_tval *) duk_get_tval(ctx, 1))); | |
286 | ||
287 | /* | |
288 | * Two pass approach to processing the property descriptors. | |
289 | * On first pass validate and normalize all descriptors before | |
290 | * any changes are made to the target object. On second pass | |
291 | * make the actual modifications to the target object. | |
292 | * | |
293 | * Right now we'll just use the same normalize/validate helper | |
294 | * on both passes, ignoring its outputs on the first pass. | |
295 | */ | |
296 | ||
297 | for (pass = 0; pass < 2; pass++) { | |
298 | duk_set_top(ctx, 2); /* -> [ hobject props ] */ | |
299 | duk_enum(ctx, 1, DUK_ENUM_OWN_PROPERTIES_ONLY /*enum_flags*/); | |
300 | ||
301 | for (;;) { | |
302 | duk_hstring *key; | |
303 | ||
304 | /* [ hobject props enum(props) ] */ | |
305 | ||
306 | duk_set_top(ctx, 3); | |
307 | ||
308 | if (!duk_next(ctx, 2, 1 /*get_value*/)) { | |
309 | break; | |
310 | } | |
311 | ||
312 | DUK_DDD(DUK_DDDPRINT("-> key=%!iT, desc=%!iT", | |
313 | (duk_tval *) duk_get_tval(ctx, -2), | |
314 | (duk_tval *) duk_get_tval(ctx, -1))); | |
315 | ||
316 | /* [ hobject props enum(props) key desc ] */ | |
317 | ||
318 | duk_hobject_prepare_property_descriptor(ctx, | |
319 | 4 /*idx_desc*/, | |
320 | &defprop_flags, | |
321 | &idx_value, | |
322 | &get, | |
323 | &set); | |
324 | ||
325 | /* [ hobject props enum(props) key desc value? getter? setter? ] */ | |
326 | ||
327 | if (pass == 0) { | |
328 | continue; | |
329 | } | |
330 | ||
331 | key = duk_get_hstring(ctx, 3); | |
332 | DUK_ASSERT(key != NULL); | |
333 | ||
334 | duk_hobject_define_property_helper(ctx, | |
335 | defprop_flags, | |
336 | obj, | |
337 | key, | |
338 | idx_value, | |
339 | get, | |
340 | set); | |
341 | } | |
342 | } | |
343 | ||
344 | /* | |
345 | * Return target object | |
346 | */ | |
347 | ||
348 | duk_dup(ctx, 0); | |
349 | return 1; | |
350 | } | |
351 | ||
352 | DUK_INTERNAL duk_ret_t duk_bi_object_constructor_seal_freeze_shared(duk_context *ctx) { | |
353 | duk_hthread *thr = (duk_hthread *) ctx; | |
354 | duk_hobject *h; | |
355 | duk_bool_t is_freeze; | |
356 | ||
357 | h = duk_require_hobject_or_lfunc(ctx, 0); | |
358 | if (!h) { | |
359 | /* Lightfunc, always success. */ | |
360 | return 1; | |
361 | } | |
362 | ||
363 | is_freeze = (duk_bool_t) duk_get_current_magic(ctx); | |
364 | duk_hobject_object_seal_freeze_helper(thr, h, is_freeze); | |
365 | ||
366 | /* Sealed and frozen objects cannot gain any more properties, | |
367 | * so this is a good time to compact them. | |
368 | */ | |
369 | duk_hobject_compact_props(thr, h); | |
370 | ||
371 | return 1; | |
372 | } | |
373 | ||
374 | DUK_INTERNAL duk_ret_t duk_bi_object_constructor_prevent_extensions(duk_context *ctx) { | |
375 | duk_hthread *thr = (duk_hthread *) ctx; | |
376 | duk_hobject *h; | |
377 | ||
378 | h = duk_require_hobject_or_lfunc(ctx, 0); | |
379 | if (!h) { | |
380 | /* Lightfunc, always success. */ | |
381 | return 1; | |
382 | } | |
383 | DUK_ASSERT(h != NULL); | |
384 | ||
385 | DUK_HOBJECT_CLEAR_EXTENSIBLE(h); | |
386 | ||
387 | /* A non-extensible object cannot gain any more properties, | |
388 | * so this is a good time to compact. | |
389 | */ | |
390 | duk_hobject_compact_props(thr, h); | |
391 | ||
392 | return 1; | |
393 | } | |
394 | ||
395 | DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is_sealed_frozen_shared(duk_context *ctx) { | |
396 | duk_hobject *h; | |
397 | duk_bool_t is_frozen; | |
398 | duk_bool_t rc; | |
399 | ||
400 | h = duk_require_hobject_or_lfunc(ctx, 0); | |
401 | if (!h) { | |
402 | duk_push_true(ctx); /* frozen and sealed */ | |
403 | } else { | |
404 | is_frozen = duk_get_current_magic(ctx); | |
405 | rc = duk_hobject_object_is_sealed_frozen_helper((duk_hthread *) ctx, h, is_frozen /*is_frozen*/); | |
406 | duk_push_boolean(ctx, rc); | |
407 | } | |
408 | return 1; | |
409 | } | |
410 | ||
411 | DUK_INTERNAL duk_ret_t duk_bi_object_constructor_is_extensible(duk_context *ctx) { | |
412 | duk_hobject *h; | |
413 | ||
414 | h = duk_require_hobject_or_lfunc(ctx, 0); | |
415 | if (!h) { | |
416 | duk_push_false(ctx); | |
417 | } else { | |
418 | duk_push_boolean(ctx, DUK_HOBJECT_HAS_EXTENSIBLE(h)); | |
419 | } | |
420 | return 1; | |
421 | } | |
422 | ||
423 | /* Shared helper for Object.getOwnPropertyNames() and Object.keys(). | |
424 | * Magic: 0=getOwnPropertyNames, 1=Object.keys. | |
425 | */ | |
426 | DUK_INTERNAL duk_ret_t duk_bi_object_constructor_keys_shared(duk_context *ctx) { | |
427 | duk_hthread *thr = (duk_hthread *) ctx; | |
428 | duk_hobject *obj; | |
429 | #if defined(DUK_USE_ES6_PROXY) | |
430 | duk_hobject *h_proxy_target; | |
431 | duk_hobject *h_proxy_handler; | |
432 | duk_hobject *h_trap_result; | |
433 | duk_uarridx_t i, len, idx; | |
434 | #endif | |
435 | duk_small_uint_t enum_flags; | |
436 | ||
437 | DUK_ASSERT_TOP(ctx, 1); | |
438 | DUK_UNREF(thr); | |
439 | ||
440 | obj = duk_require_hobject_or_lfunc_coerce(ctx, 0); | |
441 | DUK_ASSERT(obj != NULL); | |
442 | DUK_UNREF(obj); | |
443 | ||
444 | #if defined(DUK_USE_ES6_PROXY) | |
445 | if (DUK_LIKELY(!duk_hobject_proxy_check(thr, | |
446 | obj, | |
447 | &h_proxy_target, | |
448 | &h_proxy_handler))) { | |
449 | goto skip_proxy; | |
450 | } | |
451 | ||
452 | duk_push_hobject(ctx, h_proxy_handler); | |
453 | if (!duk_get_prop_stridx(ctx, -1, DUK_STRIDX_OWN_KEYS)) { | |
454 | /* Careful with reachability here: don't pop 'obj' before pushing | |
455 | * proxy target. | |
456 | */ | |
457 | DUK_DDD(DUK_DDDPRINT("no ownKeys trap, get keys of target instead")); | |
458 | duk_pop_2(ctx); | |
459 | duk_push_hobject(ctx, h_proxy_target); | |
460 | duk_replace(ctx, 0); | |
461 | DUK_ASSERT_TOP(ctx, 1); | |
462 | goto skip_proxy; | |
463 | } | |
464 | ||
465 | /* [ obj handler trap ] */ | |
466 | duk_insert(ctx, -2); | |
467 | duk_push_hobject(ctx, h_proxy_target); /* -> [ obj trap handler target ] */ | |
468 | duk_call_method(ctx, 1 /*nargs*/); /* -> [ obj trap_result ] */ | |
469 | h_trap_result = duk_require_hobject(ctx, -1); | |
470 | DUK_UNREF(h_trap_result); | |
471 | ||
472 | len = (duk_uarridx_t) duk_get_length(ctx, -1); | |
473 | idx = 0; | |
474 | duk_push_array(ctx); | |
475 | for (i = 0; i < len; i++) { | |
476 | /* [ obj trap_result res_arr ] */ | |
477 | if (duk_get_prop_index(ctx, -2, i) && duk_is_string(ctx, -1)) { | |
478 | /* XXX: for Object.keys() we should check enumerability of key */ | |
479 | /* [ obj trap_result res_arr propname ] */ | |
480 | duk_put_prop_index(ctx, -2, idx); | |
481 | idx++; | |
482 | } else { | |
483 | duk_pop(ctx); | |
484 | } | |
485 | } | |
486 | ||
487 | /* XXX: missing trap result validation for non-configurable target keys | |
488 | * (must be present), for non-extensible target all target keys must be | |
489 | * present and no extra keys can be present. | |
490 | * http://www.ecma-international.org/ecma-262/6.0/#sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys | |
491 | */ | |
492 | ||
493 | /* XXX: for Object.keys() the [[OwnPropertyKeys]] result (trap result) | |
494 | * should be filtered so that only enumerable keys remain. Enumerability | |
495 | * should be checked with [[GetOwnProperty]] on the original object | |
496 | * (i.e., the proxy in this case). If the proxy has a getOwnPropertyDescriptor | |
497 | * trap, it should be triggered for every property. If the proxy doesn't have | |
498 | * the trap, enumerability should be checked against the target object instead. | |
499 | * We don't do any of this now, so Object.keys() and Object.getOwnPropertyNames() | |
500 | * return the same result now for proxy traps. We still do clean up the trap | |
501 | * result, so that Object.keys() and Object.getOwnPropertyNames() will return a | |
502 | * clean array of strings without gaps. | |
503 | */ | |
504 | return 1; | |
505 | ||
506 | skip_proxy: | |
507 | #endif /* DUK_USE_ES6_PROXY */ | |
508 | ||
509 | DUK_ASSERT_TOP(ctx, 1); | |
510 | ||
511 | if (duk_get_current_magic(ctx)) { | |
512 | /* Object.keys */ | |
513 | enum_flags = DUK_ENUM_OWN_PROPERTIES_ONLY | | |
514 | DUK_ENUM_NO_PROXY_BEHAVIOR; | |
515 | } else { | |
516 | /* Object.getOwnPropertyNames */ | |
517 | enum_flags = DUK_ENUM_INCLUDE_NONENUMERABLE | | |
518 | DUK_ENUM_OWN_PROPERTIES_ONLY | | |
519 | DUK_ENUM_NO_PROXY_BEHAVIOR; | |
520 | } | |
521 | ||
522 | return duk_hobject_get_enumerated_keys(ctx, enum_flags); | |
523 | } | |
524 | ||
525 | DUK_INTERNAL duk_ret_t duk_bi_object_prototype_to_string(duk_context *ctx) { | |
526 | duk_push_this(ctx); | |
527 | duk_to_object_class_string_top(ctx); | |
528 | return 1; | |
529 | } | |
530 | ||
531 | DUK_INTERNAL duk_ret_t duk_bi_object_prototype_to_locale_string(duk_context *ctx) { | |
532 | DUK_ASSERT_TOP(ctx, 0); | |
533 | (void) duk_push_this_coercible_to_object(ctx); | |
534 | duk_get_prop_stridx(ctx, 0, DUK_STRIDX_TO_STRING); | |
535 | if (!duk_is_callable(ctx, 1)) { | |
536 | return DUK_RET_TYPE_ERROR; | |
537 | } | |
538 | duk_dup(ctx, 0); /* -> [ O toString O ] */ | |
539 | duk_call_method(ctx, 0); /* XXX: call method tail call? */ | |
540 | return 1; | |
541 | } | |
542 | ||
543 | DUK_INTERNAL duk_ret_t duk_bi_object_prototype_value_of(duk_context *ctx) { | |
544 | (void) duk_push_this_coercible_to_object(ctx); | |
545 | return 1; | |
546 | } | |
547 | ||
548 | DUK_INTERNAL duk_ret_t duk_bi_object_prototype_is_prototype_of(duk_context *ctx) { | |
549 | duk_hthread *thr = (duk_hthread *) ctx; | |
550 | duk_hobject *h_v; | |
551 | duk_hobject *h_obj; | |
552 | ||
553 | DUK_ASSERT_TOP(ctx, 1); | |
554 | ||
555 | h_v = duk_get_hobject(ctx, 0); | |
556 | if (!h_v) { | |
557 | duk_push_false(ctx); /* XXX: tail call: return duk_push_false(ctx) */ | |
558 | return 1; | |
559 | } | |
560 | ||
561 | h_obj = duk_push_this_coercible_to_object(ctx); | |
562 | DUK_ASSERT(h_obj != NULL); | |
563 | ||
564 | /* E5.1 Section 15.2.4.6, step 3.a, lookup proto once before compare. | |
565 | * Prototype loops should cause an error to be thrown. | |
566 | */ | |
567 | duk_push_boolean(ctx, duk_hobject_prototype_chain_contains(thr, DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h_v), h_obj, 0 /*ignore_loop*/)); | |
568 | return 1; | |
569 | } | |
570 | ||
571 | DUK_INTERNAL duk_ret_t duk_bi_object_prototype_has_own_property(duk_context *ctx) { | |
572 | return duk_hobject_object_ownprop_helper(ctx, 0 /*required_desc_flags*/); | |
573 | } | |
574 | ||
575 | DUK_INTERNAL duk_ret_t duk_bi_object_prototype_property_is_enumerable(duk_context *ctx) { | |
576 | return duk_hobject_object_ownprop_helper(ctx, DUK_PROPDESC_FLAG_ENUMERABLE /*required_desc_flags*/); | |
577 | } |