]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* |
2 | * Object handling: property access and other support functions. | |
3 | */ | |
4 | ||
5 | #include "duk_internal.h" | |
6 | ||
7 | /* | |
8 | * Property handling | |
9 | * | |
10 | * The API exposes only the most common property handling functions. | |
11 | * The caller can invoke Ecmascript built-ins for full control (e.g. | |
12 | * defineProperty, getOwnPropertyDescriptor). | |
13 | */ | |
14 | ||
15 | DUK_EXTERNAL duk_bool_t duk_get_prop(duk_context *ctx, duk_idx_t obj_index) { | |
16 | duk_hthread *thr = (duk_hthread *) ctx; | |
17 | duk_tval *tv_obj; | |
18 | duk_tval *tv_key; | |
19 | duk_bool_t rc; | |
20 | ||
21 | DUK_ASSERT_CTX_VALID(ctx); | |
22 | ||
23 | /* Note: copying tv_obj and tv_key to locals to shield against a valstack | |
24 | * resize is not necessary for a property get right now. | |
25 | */ | |
26 | ||
27 | tv_obj = duk_require_tval(ctx, obj_index); | |
28 | tv_key = duk_require_tval(ctx, -1); | |
29 | ||
30 | rc = duk_hobject_getprop(thr, tv_obj, tv_key); | |
31 | DUK_ASSERT(rc == 0 || rc == 1); | |
32 | /* a value is left on stack regardless of rc */ | |
33 | ||
34 | duk_remove(ctx, -2); /* remove key */ | |
35 | return rc; /* 1 if property found, 0 otherwise */ | |
36 | } | |
37 | ||
38 | DUK_EXTERNAL duk_bool_t duk_get_prop_string(duk_context *ctx, duk_idx_t obj_index, const char *key) { | |
39 | DUK_ASSERT_CTX_VALID(ctx); | |
40 | DUK_ASSERT(key != NULL); | |
41 | ||
42 | obj_index = duk_require_normalize_index(ctx, obj_index); | |
43 | duk_push_string(ctx, key); | |
44 | return duk_get_prop(ctx, obj_index); | |
45 | } | |
46 | ||
47 | DUK_EXTERNAL duk_bool_t duk_get_prop_index(duk_context *ctx, duk_idx_t obj_index, duk_uarridx_t arr_index) { | |
48 | DUK_ASSERT_CTX_VALID(ctx); | |
49 | ||
50 | obj_index = duk_require_normalize_index(ctx, obj_index); | |
51 | duk_push_uarridx(ctx, arr_index); | |
52 | return duk_get_prop(ctx, obj_index); | |
53 | } | |
54 | ||
55 | DUK_INTERNAL duk_bool_t duk_get_prop_stridx(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx) { | |
56 | duk_hthread *thr = (duk_hthread *) ctx; | |
57 | ||
58 | DUK_ASSERT_CTX_VALID(ctx); | |
59 | DUK_ASSERT_DISABLE(stridx >= 0); | |
60 | DUK_ASSERT(stridx < DUK_HEAP_NUM_STRINGS); | |
11fdf7f2 | 61 | DUK_UNREF(thr); |
7c673cae FG |
62 | |
63 | obj_index = duk_require_normalize_index(ctx, obj_index); | |
64 | duk_push_hstring(ctx, DUK_HTHREAD_GET_STRING(thr, stridx)); | |
65 | return duk_get_prop(ctx, obj_index); | |
66 | } | |
67 | ||
68 | DUK_INTERNAL duk_bool_t duk_get_prop_stridx_boolean(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx, duk_bool_t *out_has_prop) { | |
69 | duk_bool_t rc; | |
70 | ||
71 | DUK_ASSERT_CTX_VALID(ctx); | |
72 | DUK_ASSERT_DISABLE(stridx >= 0); | |
73 | DUK_ASSERT(stridx < DUK_HEAP_NUM_STRINGS); | |
74 | ||
75 | rc = duk_get_prop_stridx(ctx, obj_index, stridx); | |
76 | if (out_has_prop) { | |
77 | *out_has_prop = rc; | |
78 | } | |
79 | rc = duk_to_boolean(ctx, -1); | |
80 | DUK_ASSERT(rc == 0 || rc == 1); | |
81 | duk_pop(ctx); | |
82 | return rc; | |
83 | } | |
84 | ||
11fdf7f2 | 85 | DUK_LOCAL duk_bool_t duk__put_prop_shared(duk_context *ctx, duk_idx_t obj_idx, duk_idx_t idx_key) { |
7c673cae FG |
86 | duk_hthread *thr = (duk_hthread *) ctx; |
87 | duk_tval *tv_obj; | |
88 | duk_tval *tv_key; | |
89 | duk_tval *tv_val; | |
90 | duk_small_int_t throw_flag; | |
91 | duk_bool_t rc; | |
92 | ||
7c673cae FG |
93 | /* Note: copying tv_obj and tv_key to locals to shield against a valstack |
94 | * resize is not necessary for a property put right now (putprop protects | |
95 | * against it internally). | |
96 | */ | |
97 | ||
11fdf7f2 TL |
98 | /* Key and value indices are either (-2, -1) or (-1, -2). Given idx_key, |
99 | * idx_val is always (idx_key ^ 0x01). | |
100 | */ | |
101 | DUK_ASSERT((idx_key == -2 && (idx_key ^ 1) == -1) || | |
102 | (idx_key == -1 && (idx_key ^ 1) == -2)); | |
103 | tv_obj = duk_require_tval(ctx, obj_idx); | |
104 | tv_key = duk_require_tval(ctx, idx_key); | |
105 | tv_val = duk_require_tval(ctx, idx_key ^ 1); | |
7c673cae FG |
106 | throw_flag = duk_is_strict_call(ctx); |
107 | ||
108 | rc = duk_hobject_putprop(thr, tv_obj, tv_key, tv_val, throw_flag); | |
109 | DUK_ASSERT(rc == 0 || rc == 1); | |
110 | ||
111 | duk_pop_2(ctx); /* remove key and value */ | |
112 | return rc; /* 1 if property found, 0 otherwise */ | |
113 | } | |
114 | ||
11fdf7f2 TL |
115 | DUK_EXTERNAL duk_bool_t duk_put_prop(duk_context *ctx, duk_idx_t obj_idx) { |
116 | DUK_ASSERT_CTX_VALID(ctx); | |
117 | return duk__put_prop_shared(ctx, obj_idx, -2); | |
118 | } | |
119 | ||
120 | DUK_EXTERNAL duk_bool_t duk_put_prop_string(duk_context *ctx, duk_idx_t obj_idx, const char *key) { | |
7c673cae FG |
121 | DUK_ASSERT_CTX_VALID(ctx); |
122 | DUK_ASSERT(key != NULL); | |
123 | ||
11fdf7f2 TL |
124 | /* Careful here and with other duk_put_prop_xxx() helpers: the |
125 | * target object and the property value may be in the same value | |
126 | * stack slot (unusual, but still conceptually clear). | |
127 | */ | |
128 | obj_idx = duk_normalize_index(ctx, obj_idx); | |
129 | (void) duk_push_string(ctx, key); | |
130 | return duk__put_prop_shared(ctx, obj_idx, -1); | |
7c673cae FG |
131 | } |
132 | ||
11fdf7f2 | 133 | DUK_EXTERNAL duk_bool_t duk_put_prop_index(duk_context *ctx, duk_idx_t obj_idx, duk_uarridx_t arr_idx) { |
7c673cae FG |
134 | DUK_ASSERT_CTX_VALID(ctx); |
135 | ||
11fdf7f2 TL |
136 | obj_idx = duk_require_normalize_index(ctx, obj_idx); |
137 | duk_push_uarridx(ctx, arr_idx); | |
138 | return duk__put_prop_shared(ctx, obj_idx, -1); | |
7c673cae FG |
139 | } |
140 | ||
11fdf7f2 | 141 | DUK_INTERNAL duk_bool_t duk_put_prop_stridx(duk_context *ctx, duk_idx_t obj_idx, duk_small_int_t stridx) { |
7c673cae FG |
142 | duk_hthread *thr = (duk_hthread *) ctx; |
143 | ||
144 | DUK_ASSERT_CTX_VALID(ctx); | |
145 | DUK_ASSERT_DISABLE(stridx >= 0); | |
146 | DUK_ASSERT(stridx < DUK_HEAP_NUM_STRINGS); | |
11fdf7f2 | 147 | DUK_UNREF(thr); |
7c673cae | 148 | |
11fdf7f2 | 149 | obj_idx = duk_require_normalize_index(ctx, obj_idx); |
7c673cae | 150 | duk_push_hstring(ctx, DUK_HTHREAD_GET_STRING(thr, stridx)); |
11fdf7f2 | 151 | return duk__put_prop_shared(ctx, obj_idx, -1); |
7c673cae FG |
152 | } |
153 | ||
154 | DUK_EXTERNAL duk_bool_t duk_del_prop(duk_context *ctx, duk_idx_t obj_index) { | |
155 | duk_hthread *thr = (duk_hthread *) ctx; | |
156 | duk_tval *tv_obj; | |
157 | duk_tval *tv_key; | |
158 | duk_small_int_t throw_flag; | |
159 | duk_bool_t rc; | |
160 | ||
161 | DUK_ASSERT_CTX_VALID(ctx); | |
162 | ||
163 | /* Note: copying tv_obj and tv_key to locals to shield against a valstack | |
164 | * resize is not necessary for a property delete right now. | |
165 | */ | |
166 | ||
167 | tv_obj = duk_require_tval(ctx, obj_index); | |
168 | tv_key = duk_require_tval(ctx, -1); | |
169 | throw_flag = duk_is_strict_call(ctx); | |
170 | ||
171 | rc = duk_hobject_delprop(thr, tv_obj, tv_key, throw_flag); | |
172 | DUK_ASSERT(rc == 0 || rc == 1); | |
173 | ||
174 | duk_pop(ctx); /* remove key */ | |
175 | return rc; | |
176 | } | |
177 | ||
178 | DUK_EXTERNAL duk_bool_t duk_del_prop_string(duk_context *ctx, duk_idx_t obj_index, const char *key) { | |
179 | DUK_ASSERT_CTX_VALID(ctx); | |
180 | DUK_ASSERT(key != NULL); | |
181 | ||
182 | obj_index = duk_require_normalize_index(ctx, obj_index); | |
183 | duk_push_string(ctx, key); | |
184 | return duk_del_prop(ctx, obj_index); | |
185 | } | |
186 | ||
187 | DUK_EXTERNAL duk_bool_t duk_del_prop_index(duk_context *ctx, duk_idx_t obj_index, duk_uarridx_t arr_index) { | |
188 | DUK_ASSERT_CTX_VALID(ctx); | |
189 | ||
190 | obj_index = duk_require_normalize_index(ctx, obj_index); | |
191 | duk_push_uarridx(ctx, arr_index); | |
192 | return duk_del_prop(ctx, obj_index); | |
193 | } | |
194 | ||
195 | DUK_INTERNAL duk_bool_t duk_del_prop_stridx(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx) { | |
196 | duk_hthread *thr = (duk_hthread *) ctx; | |
197 | ||
198 | DUK_ASSERT_CTX_VALID(ctx); | |
199 | DUK_ASSERT_DISABLE(stridx >= 0); | |
200 | DUK_ASSERT(stridx < DUK_HEAP_NUM_STRINGS); | |
11fdf7f2 | 201 | DUK_UNREF(thr); |
7c673cae FG |
202 | |
203 | obj_index = duk_require_normalize_index(ctx, obj_index); | |
204 | duk_push_hstring(ctx, DUK_HTHREAD_GET_STRING(thr, stridx)); | |
205 | return duk_del_prop(ctx, obj_index); | |
206 | } | |
207 | ||
208 | DUK_EXTERNAL duk_bool_t duk_has_prop(duk_context *ctx, duk_idx_t obj_index) { | |
209 | duk_hthread *thr = (duk_hthread *) ctx; | |
210 | duk_tval *tv_obj; | |
211 | duk_tval *tv_key; | |
212 | duk_bool_t rc; | |
213 | ||
214 | DUK_ASSERT_CTX_VALID(ctx); | |
215 | ||
216 | /* Note: copying tv_obj and tv_key to locals to shield against a valstack | |
217 | * resize is not necessary for a property existence check right now. | |
218 | */ | |
219 | ||
220 | tv_obj = duk_require_tval(ctx, obj_index); | |
221 | tv_key = duk_require_tval(ctx, -1); | |
222 | ||
223 | rc = duk_hobject_hasprop(thr, tv_obj, tv_key); | |
224 | DUK_ASSERT(rc == 0 || rc == 1); | |
225 | ||
226 | duk_pop(ctx); /* remove key */ | |
227 | return rc; /* 1 if property found, 0 otherwise */ | |
228 | } | |
229 | ||
230 | DUK_EXTERNAL duk_bool_t duk_has_prop_string(duk_context *ctx, duk_idx_t obj_index, const char *key) { | |
231 | DUK_ASSERT_CTX_VALID(ctx); | |
232 | DUK_ASSERT(key != NULL); | |
233 | ||
234 | obj_index = duk_require_normalize_index(ctx, obj_index); | |
235 | duk_push_string(ctx, key); | |
236 | return duk_has_prop(ctx, obj_index); | |
237 | } | |
238 | ||
239 | DUK_EXTERNAL duk_bool_t duk_has_prop_index(duk_context *ctx, duk_idx_t obj_index, duk_uarridx_t arr_index) { | |
240 | DUK_ASSERT_CTX_VALID(ctx); | |
241 | ||
242 | obj_index = duk_require_normalize_index(ctx, obj_index); | |
243 | duk_push_uarridx(ctx, arr_index); | |
244 | return duk_has_prop(ctx, obj_index); | |
245 | } | |
246 | ||
247 | DUK_INTERNAL duk_bool_t duk_has_prop_stridx(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx) { | |
248 | duk_hthread *thr = (duk_hthread *) ctx; | |
249 | ||
250 | DUK_ASSERT_CTX_VALID(ctx); | |
251 | DUK_ASSERT_DISABLE(stridx >= 0); | |
252 | DUK_ASSERT(stridx < DUK_HEAP_NUM_STRINGS); | |
11fdf7f2 | 253 | DUK_UNREF(thr); |
7c673cae FG |
254 | |
255 | obj_index = duk_require_normalize_index(ctx, obj_index); | |
256 | duk_push_hstring(ctx, DUK_HTHREAD_GET_STRING(thr, stridx)); | |
257 | return duk_has_prop(ctx, obj_index); | |
258 | } | |
259 | ||
260 | /* Define own property without inheritance looks and such. This differs from | |
261 | * [[DefineOwnProperty]] because special behaviors (like Array 'length') are | |
262 | * not invoked by this method. The caller must be careful to invoke any such | |
263 | * behaviors if necessary. | |
264 | */ | |
265 | DUK_INTERNAL void duk_xdef_prop(duk_context *ctx, duk_idx_t obj_index, duk_small_uint_t desc_flags) { | |
266 | duk_hthread *thr = (duk_hthread *) ctx; | |
267 | duk_hobject *obj; | |
268 | duk_hstring *key; | |
269 | ||
270 | DUK_ASSERT_CTX_VALID(ctx); | |
271 | ||
272 | obj = duk_require_hobject(ctx, obj_index); | |
273 | DUK_ASSERT(obj != NULL); | |
274 | key = duk_to_hstring(ctx, -2); | |
275 | DUK_ASSERT(key != NULL); | |
276 | DUK_ASSERT(duk_require_tval(ctx, -1) != NULL); | |
277 | ||
278 | duk_hobject_define_property_internal(thr, obj, key, desc_flags); | |
279 | ||
280 | duk_pop(ctx); /* pop key */ | |
281 | } | |
282 | ||
283 | DUK_INTERNAL void duk_xdef_prop_index(duk_context *ctx, duk_idx_t obj_index, duk_uarridx_t arr_index, duk_small_uint_t desc_flags) { | |
284 | duk_hthread *thr = (duk_hthread *) ctx; | |
285 | duk_hobject *obj; | |
286 | ||
287 | DUK_ASSERT_CTX_VALID(ctx); | |
288 | ||
289 | obj = duk_require_hobject(ctx, obj_index); | |
290 | DUK_ASSERT(obj != NULL); | |
291 | ||
292 | duk_hobject_define_property_internal_arridx(thr, obj, arr_index, desc_flags); | |
293 | /* value popped by call */ | |
294 | } | |
295 | ||
296 | DUK_INTERNAL void duk_xdef_prop_stridx(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx, duk_small_uint_t desc_flags) { | |
297 | duk_hthread *thr = (duk_hthread *) ctx; | |
298 | duk_hobject *obj; | |
299 | duk_hstring *key; | |
300 | ||
301 | DUK_ASSERT_CTX_VALID(ctx); | |
302 | DUK_ASSERT_DISABLE(stridx >= 0); | |
303 | DUK_ASSERT(stridx < DUK_HEAP_NUM_STRINGS); | |
304 | ||
305 | obj = duk_require_hobject(ctx, obj_index); | |
306 | DUK_ASSERT(obj != NULL); | |
307 | key = DUK_HTHREAD_GET_STRING(thr, stridx); | |
308 | DUK_ASSERT(key != NULL); | |
309 | DUK_ASSERT(duk_require_tval(ctx, -1) != NULL); | |
310 | ||
311 | duk_hobject_define_property_internal(thr, obj, key, desc_flags); | |
312 | /* value popped by call */ | |
313 | } | |
314 | ||
315 | DUK_INTERNAL void duk_xdef_prop_stridx_builtin(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx, duk_small_int_t builtin_idx, duk_small_uint_t desc_flags) { | |
316 | duk_hthread *thr = (duk_hthread *) ctx; | |
317 | duk_hobject *obj; | |
318 | duk_hstring *key; | |
319 | ||
320 | DUK_ASSERT_CTX_VALID(ctx); | |
321 | DUK_ASSERT_DISABLE(stridx >= 0); | |
322 | DUK_ASSERT(stridx < DUK_HEAP_NUM_STRINGS); | |
323 | DUK_ASSERT_DISABLE(builtin_idx >= 0); | |
324 | DUK_ASSERT(builtin_idx < DUK_NUM_BUILTINS); | |
325 | ||
326 | obj = duk_require_hobject(ctx, obj_index); | |
327 | DUK_ASSERT(obj != NULL); | |
328 | key = DUK_HTHREAD_GET_STRING(thr, stridx); | |
329 | DUK_ASSERT(key != NULL); | |
330 | ||
331 | duk_push_hobject(ctx, thr->builtins[builtin_idx]); | |
332 | duk_hobject_define_property_internal(thr, obj, key, desc_flags); | |
333 | /* value popped by call */ | |
334 | } | |
335 | ||
336 | /* This is a rare property helper; it sets the global thrower (E5 Section 13.2.3) | |
337 | * setter/getter into an object property. This is needed by the 'arguments' | |
338 | * object creation code, function instance creation code, and Function.prototype.bind(). | |
339 | */ | |
340 | ||
341 | DUK_INTERNAL void duk_xdef_prop_stridx_thrower(duk_context *ctx, duk_idx_t obj_index, duk_small_int_t stridx, duk_small_uint_t desc_flags) { | |
342 | duk_hthread *thr = (duk_hthread *) ctx; | |
343 | duk_hobject *obj = duk_require_hobject(ctx, obj_index); | |
344 | duk_hobject *thrower = thr->builtins[DUK_BIDX_TYPE_ERROR_THROWER]; | |
345 | duk_hobject_define_accessor_internal(thr, obj, DUK_HTHREAD_GET_STRING(thr, stridx), thrower, thrower, desc_flags); | |
346 | } | |
347 | ||
348 | /* Object.defineProperty() equivalent C binding. */ | |
349 | DUK_EXTERNAL void duk_def_prop(duk_context *ctx, duk_idx_t obj_index, duk_uint_t flags) { | |
350 | duk_hthread *thr = (duk_hthread *) ctx; | |
351 | duk_idx_t idx_base; | |
352 | duk_hobject *obj; | |
353 | duk_hstring *key; | |
354 | duk_idx_t idx_value; | |
355 | duk_hobject *get; | |
356 | duk_hobject *set; | |
357 | duk_uint_t is_data_desc; | |
358 | duk_uint_t is_acc_desc; | |
359 | ||
360 | DUK_ASSERT_CTX_VALID(ctx); | |
361 | ||
362 | obj = duk_require_hobject(ctx, obj_index); | |
363 | ||
364 | is_data_desc = flags & (DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_HAVE_WRITABLE); | |
365 | is_acc_desc = flags & (DUK_DEFPROP_HAVE_GETTER | DUK_DEFPROP_HAVE_SETTER); | |
366 | if (is_data_desc && is_acc_desc) { | |
367 | /* "Have" flags must not be conflicting so that they would | |
368 | * apply to both a plain property and an accessor at the same | |
369 | * time. | |
370 | */ | |
371 | goto fail_invalid_desc; | |
372 | } | |
373 | ||
374 | idx_base = duk_get_top_index(ctx); | |
375 | if (flags & DUK_DEFPROP_HAVE_SETTER) { | |
376 | duk_require_type_mask(ctx, idx_base, DUK_TYPE_MASK_UNDEFINED | | |
377 | DUK_TYPE_MASK_OBJECT | | |
378 | DUK_TYPE_MASK_LIGHTFUNC); | |
379 | set = duk_get_hobject_or_lfunc_coerce(ctx, idx_base); | |
380 | if (set != NULL && !DUK_HOBJECT_IS_CALLABLE(set)) { | |
381 | goto fail_not_callable; | |
382 | } | |
383 | idx_base--; | |
384 | } else { | |
385 | set = NULL; | |
386 | } | |
387 | if (flags & DUK_DEFPROP_HAVE_GETTER) { | |
388 | duk_require_type_mask(ctx, idx_base, DUK_TYPE_MASK_UNDEFINED | | |
389 | DUK_TYPE_MASK_OBJECT | | |
390 | DUK_TYPE_MASK_LIGHTFUNC); | |
391 | get = duk_get_hobject_or_lfunc_coerce(ctx, idx_base); | |
392 | if (get != NULL && !DUK_HOBJECT_IS_CALLABLE(get)) { | |
393 | goto fail_not_callable; | |
394 | } | |
395 | idx_base--; | |
396 | } else { | |
397 | get = NULL; | |
398 | } | |
399 | if (flags & DUK_DEFPROP_HAVE_VALUE) { | |
400 | idx_value = idx_base; | |
401 | idx_base--; | |
402 | } else { | |
403 | idx_value = (duk_idx_t) -1; | |
404 | } | |
405 | key = duk_require_hstring(ctx, idx_base); | |
406 | ||
407 | duk_require_valid_index(ctx, idx_base); | |
408 | ||
409 | duk_hobject_define_property_helper(ctx, | |
410 | flags /*defprop_flags*/, | |
411 | obj, | |
412 | key, | |
413 | idx_value, | |
414 | get, | |
415 | set); | |
416 | ||
417 | /* Clean up stack */ | |
418 | ||
419 | duk_set_top(ctx, idx_base); | |
420 | ||
421 | /* [ ... obj ... ] */ | |
422 | ||
423 | return; | |
424 | ||
425 | fail_invalid_desc: | |
11fdf7f2 | 426 | DUK_ERROR_TYPE(thr, DUK_STR_INVALID_DESCRIPTOR); |
7c673cae FG |
427 | return; |
428 | ||
429 | fail_not_callable: | |
11fdf7f2 | 430 | DUK_ERROR_TYPE(thr, DUK_STR_NOT_CALLABLE); |
7c673cae FG |
431 | return; |
432 | } | |
433 | ||
434 | /* | |
435 | * Object related | |
436 | * | |
437 | * Note: seal() and freeze() are accessible through Ecmascript bindings, | |
438 | * and are not exposed through the API. | |
439 | */ | |
440 | ||
441 | DUK_EXTERNAL void duk_compact(duk_context *ctx, duk_idx_t obj_index) { | |
442 | duk_hthread *thr = (duk_hthread *) ctx; | |
443 | duk_hobject *obj; | |
444 | ||
445 | DUK_ASSERT_CTX_VALID(ctx); | |
446 | ||
447 | obj = duk_get_hobject(ctx, obj_index); | |
448 | if (obj) { | |
449 | /* Note: this may fail, caller should protect the call if necessary */ | |
450 | duk_hobject_compact_props(thr, obj); | |
451 | } | |
452 | } | |
453 | ||
454 | /* XXX: the duk_hobject_enum.c stack APIs should be reworked */ | |
455 | ||
456 | DUK_EXTERNAL void duk_enum(duk_context *ctx, duk_idx_t obj_index, duk_uint_t enum_flags) { | |
457 | DUK_ASSERT_CTX_VALID(ctx); | |
458 | ||
459 | duk_dup(ctx, obj_index); | |
460 | duk_require_hobject_or_lfunc_coerce(ctx, -1); | |
461 | duk_hobject_enumerator_create(ctx, enum_flags); /* [target] -> [enum] */ | |
462 | } | |
463 | ||
464 | DUK_EXTERNAL duk_bool_t duk_next(duk_context *ctx, duk_idx_t enum_index, duk_bool_t get_value) { | |
465 | DUK_ASSERT_CTX_VALID(ctx); | |
466 | ||
467 | duk_require_hobject(ctx, enum_index); | |
468 | duk_dup(ctx, enum_index); | |
469 | return duk_hobject_enumerator_next(ctx, get_value); | |
470 | } | |
471 | ||
472 | /* | |
473 | * Helpers for writing multiple properties | |
474 | */ | |
475 | ||
476 | DUK_EXTERNAL void duk_put_function_list(duk_context *ctx, duk_idx_t obj_index, const duk_function_list_entry *funcs) { | |
477 | const duk_function_list_entry *ent = funcs; | |
478 | ||
479 | DUK_ASSERT_CTX_VALID(ctx); | |
480 | ||
481 | obj_index = duk_require_normalize_index(ctx, obj_index); | |
482 | if (ent != NULL) { | |
483 | while (ent->key != NULL) { | |
484 | duk_push_c_function(ctx, ent->value, ent->nargs); | |
485 | duk_put_prop_string(ctx, obj_index, ent->key); | |
486 | ent++; | |
487 | } | |
488 | } | |
489 | } | |
490 | ||
491 | DUK_EXTERNAL void duk_put_number_list(duk_context *ctx, duk_idx_t obj_index, const duk_number_list_entry *numbers) { | |
492 | const duk_number_list_entry *ent = numbers; | |
493 | ||
494 | DUK_ASSERT_CTX_VALID(ctx); | |
495 | ||
496 | obj_index = duk_require_normalize_index(ctx, obj_index); | |
497 | if (ent != NULL) { | |
498 | while (ent->key != NULL) { | |
499 | duk_push_number(ctx, ent->value); | |
500 | duk_put_prop_string(ctx, obj_index, ent->key); | |
501 | ent++; | |
502 | } | |
503 | } | |
504 | } | |
505 | ||
506 | /* | |
507 | * Shortcut for accessing global object properties | |
508 | */ | |
509 | ||
510 | DUK_EXTERNAL duk_bool_t duk_get_global_string(duk_context *ctx, const char *key) { | |
511 | duk_hthread *thr = (duk_hthread *) ctx; | |
512 | duk_bool_t ret; | |
513 | ||
514 | DUK_ASSERT_CTX_VALID(ctx); | |
515 | DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); | |
516 | ||
517 | /* XXX: direct implementation */ | |
518 | ||
519 | duk_push_hobject(ctx, thr->builtins[DUK_BIDX_GLOBAL]); | |
520 | ret = duk_get_prop_string(ctx, -1, key); | |
521 | duk_remove(ctx, -2); | |
522 | return ret; | |
523 | } | |
524 | ||
525 | DUK_EXTERNAL duk_bool_t duk_put_global_string(duk_context *ctx, const char *key) { | |
526 | duk_hthread *thr = (duk_hthread *) ctx; | |
527 | duk_bool_t ret; | |
528 | ||
529 | DUK_ASSERT_CTX_VALID(ctx); | |
530 | DUK_ASSERT(thr->builtins[DUK_BIDX_GLOBAL] != NULL); | |
531 | ||
532 | /* XXX: direct implementation */ | |
533 | ||
534 | duk_push_hobject(ctx, thr->builtins[DUK_BIDX_GLOBAL]); | |
535 | duk_insert(ctx, -2); | |
536 | ret = duk_put_prop_string(ctx, -2, key); /* [ ... global val ] -> [ ... global ] */ | |
537 | duk_pop(ctx); | |
538 | return ret; | |
539 | } | |
540 | ||
541 | /* | |
542 | * Object prototype | |
543 | */ | |
544 | ||
545 | DUK_EXTERNAL void duk_get_prototype(duk_context *ctx, duk_idx_t index) { | |
546 | duk_hthread *thr = (duk_hthread *) ctx; | |
547 | duk_hobject *obj; | |
548 | duk_hobject *proto; | |
549 | ||
550 | DUK_ASSERT_CTX_VALID(ctx); | |
551 | DUK_UNREF(thr); | |
552 | ||
553 | obj = duk_require_hobject(ctx, index); | |
554 | DUK_ASSERT(obj != NULL); | |
555 | ||
556 | /* XXX: shared helper for duk_push_hobject_or_undefined()? */ | |
557 | proto = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, obj); | |
558 | if (proto) { | |
559 | duk_push_hobject(ctx, proto); | |
560 | } else { | |
561 | duk_push_undefined(ctx); | |
562 | } | |
563 | } | |
564 | ||
565 | DUK_EXTERNAL void duk_set_prototype(duk_context *ctx, duk_idx_t index) { | |
566 | duk_hthread *thr = (duk_hthread *) ctx; | |
567 | duk_hobject *obj; | |
568 | duk_hobject *proto; | |
569 | ||
570 | DUK_ASSERT_CTX_VALID(ctx); | |
571 | ||
572 | obj = duk_require_hobject(ctx, index); | |
573 | DUK_ASSERT(obj != NULL); | |
574 | duk_require_type_mask(ctx, -1, DUK_TYPE_MASK_UNDEFINED | | |
575 | DUK_TYPE_MASK_OBJECT); | |
576 | proto = duk_get_hobject(ctx, -1); | |
577 | /* proto can also be NULL here (allowed explicitly) */ | |
578 | ||
11fdf7f2 TL |
579 | #if defined(DUK_USE_ROM_OBJECTS) |
580 | if (DUK_HEAPHDR_HAS_READONLY((duk_heaphdr *) obj)) { | |
581 | DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONFIGURABLE); /* XXX: "read only object"? */ | |
582 | return; | |
583 | } | |
584 | #endif | |
585 | ||
7c673cae FG |
586 | DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, obj, proto); |
587 | ||
588 | duk_pop(ctx); | |
589 | } | |
590 | ||
591 | /* | |
592 | * Object finalizer | |
593 | */ | |
594 | ||
595 | /* XXX: these could be implemented as macros calling an internal function | |
596 | * directly. | |
597 | * XXX: same issue as with Duktape.fin: there's no way to delete the property | |
598 | * now (just set it to undefined). | |
599 | */ | |
600 | DUK_EXTERNAL void duk_get_finalizer(duk_context *ctx, duk_idx_t index) { | |
601 | DUK_ASSERT_CTX_VALID(ctx); | |
602 | ||
603 | duk_get_prop_stridx(ctx, index, DUK_STRIDX_INT_FINALIZER); | |
604 | } | |
605 | ||
606 | DUK_EXTERNAL void duk_set_finalizer(duk_context *ctx, duk_idx_t index) { | |
607 | DUK_ASSERT_CTX_VALID(ctx); | |
608 | ||
609 | duk_put_prop_stridx(ctx, index, DUK_STRIDX_INT_FINALIZER); | |
610 | } |