1 /*
2 * Identifier access and function closure handling.
3 *
4 * Provides the primitives for slow path identifier accesses: GETVAR,
5 * PUTVAR, DELVAR, etc. The fast path, direct register accesses, should
6 * be used for most identifier accesses. Consequently, these slow path
7 * primitives should be optimized for maximum compactness.
8 *
9 * ECMAScript environment records (declarative and object) are represented
10 * as internal objects with control keys. Environment records have a
11 * parent record ("outer environment reference") which is represented by
12 * the implicit prototype for technical reasons (in other words, it is a
13 * convenient field). The prototype chain is not followed in the ordinary
14 * sense for variable lookups.
15 *
16 * See identifier-handling.rst for more details on the identifier algorithms
17 * and the internal representation. See function-objects.rst for details on
18 * what function templates and instances are expected to look like.
19 *
20 * Care must be taken to avoid duk_tval pointer invalidation caused by
21 * e.g. value stack or object resizing.
22 *
23 * TODO: properties for function instances could be initialized much more
24 * efficiently by creating a property allocation for a certain size and
25 * filling in keys and values directly (and INCREFing both with "bulk incref"
26 * primitives.
27 *
28 * XXX: duk_hobject_getprop() and duk_hobject_putprop() calls are a bit
29 * awkward (especially because they follow the prototype chain); rework
30 * if "raw" own property helpers are added.
31 */
32
33 #include "duk_internal.h"
34
35 /*
36 * Local result type for duk__get_identifier_reference() lookup.
37 */
38
39 typedef struct {
40 duk_hobject *env;
41 duk_hobject *holder; /* for object-bound identifiers */
42 duk_tval *value; /* for register-bound and declarative env identifiers */
43 duk_uint_t attrs; /* property attributes for identifier (relevant if value != NULL) */
44 duk_bool_t has_this; /* for object-bound identifiers: provide 'this' binding */
45 } duk__id_lookup_result;
46
47 /*
48 * Create a new function object based on a "template function" which contains
49 * compiled bytecode, constants, etc, but lacks a lexical environment.
50 *
51 * ECMAScript requires that each created closure is a separate object, with
52 * its own set of editable properties. However, structured property values
53 * (such as the formal arguments list and the variable map) are shared.
54 * Also the bytecode, constants, and inner functions are shared.
55 *
56 * See E5 Section 13.2 for detailed requirements on the function objects;
57 * there are no similar requirements for function "templates" which are an
58 * implementation dependent internal feature. Also see function-objects.rst
59 * for a discussion on the function instance properties provided by this
60 * implementation.
61 *
62 * Notes:
63 *
64 * * Order of internal properties should match frequency of use, since the
65 * properties will be linearly scanned on lookup (functions usually don't
66 * have enough properties to warrant a hash part).
67 *
68 * * The created closure is independent of its template; they do share the
69 * same 'data' buffer object, but the template object itself can be freed
70 * even if the closure object remains reachable.
71 */
72
duk__inc_data_inner_refcounts(duk_hthread * thr,duk_hcompfunc * f)73 DUK_LOCAL void duk__inc_data_inner_refcounts(duk_hthread *thr, duk_hcompfunc *f) {
74 duk_tval *tv, *tv_end;
75 duk_hobject **funcs, **funcs_end;
76
77 DUK_UNREF(thr);
78
79 /* If function creation fails due to out-of-memory, the data buffer
80 * pointer may be NULL in some cases. That's actually possible for
81 * GC code, but shouldn't be possible here because the incomplete
82 * function will be unwound from the value stack and never instantiated.
83 */
84 DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, f) != NULL);
85
86 tv = DUK_HCOMPFUNC_GET_CONSTS_BASE(thr->heap, f);
87 tv_end = DUK_HCOMPFUNC_GET_CONSTS_END(thr->heap, f);
88 while (tv < tv_end) {
89 DUK_TVAL_INCREF(thr, tv);
90 tv++;
91 }
92
93 funcs = DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, f);
94 funcs_end = DUK_HCOMPFUNC_GET_FUNCS_END(thr->heap, f);
95 while (funcs < funcs_end) {
96 DUK_HEAPHDR_INCREF(thr, (duk_heaphdr *) *funcs);
97 funcs++;
98 }
99 }
100
101 /* Push a new closure on the stack.
102 *
103 * Note: if fun_temp has NEWENV, i.e. a new lexical and variable declaration
104 * is created when the function is called, only outer_lex_env matters
105 * (outer_var_env is ignored and may or may not be same as outer_lex_env).
106 */
107
108 DUK_LOCAL const duk_uint16_t duk__closure_copy_proplist[] = {
109 /* order: most frequent to least frequent */
110 DUK_STRIDX_INT_VARMAP,
111 DUK_STRIDX_INT_FORMALS,
112 #if defined(DUK_USE_PC2LINE)
113 DUK_STRIDX_INT_PC2LINE,
114 #endif
115 #if defined(DUK_USE_FUNC_FILENAME_PROPERTY)
116 DUK_STRIDX_FILE_NAME,
117 #endif
118 #if defined(DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY)
119 DUK_STRIDX_INT_SOURCE
120 #endif
121 };
122
123 DUK_INTERNAL
duk_js_push_closure(duk_hthread * thr,duk_hcompfunc * fun_temp,duk_hobject * outer_var_env,duk_hobject * outer_lex_env,duk_bool_t add_auto_proto)124 void duk_js_push_closure(duk_hthread *thr,
125 duk_hcompfunc *fun_temp,
126 duk_hobject *outer_var_env,
127 duk_hobject *outer_lex_env,
128 duk_bool_t add_auto_proto) {
129 duk_hcompfunc *fun_clos;
130 duk_harray *formals;
131 duk_small_uint_t i;
132 duk_uint_t len_value;
133
134 DUK_ASSERT(fun_temp != NULL);
135 DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, fun_temp) != NULL);
136 DUK_ASSERT(DUK_HCOMPFUNC_GET_FUNCS(thr->heap, fun_temp) != NULL);
137 DUK_ASSERT(DUK_HCOMPFUNC_GET_BYTECODE(thr->heap, fun_temp) != NULL);
138 DUK_ASSERT(outer_var_env != NULL);
139 DUK_ASSERT(outer_lex_env != NULL);
140 DUK_UNREF(len_value);
141
142 DUK_STATS_INC(thr->heap, stats_envrec_pushclosure);
143
144 fun_clos = duk_push_hcompfunc(thr);
145 DUK_ASSERT(fun_clos != NULL);
146 DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) fun_clos) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
147
148 duk_push_hobject(thr, &fun_temp->obj); /* -> [ ... closure template ] */
149
150 DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) fun_clos));
151 DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, fun_clos) == NULL);
152 DUK_ASSERT(DUK_HCOMPFUNC_GET_FUNCS(thr->heap, fun_clos) == NULL);
153 DUK_ASSERT(DUK_HCOMPFUNC_GET_BYTECODE(thr->heap, fun_clos) == NULL);
154
155 DUK_HCOMPFUNC_SET_DATA(thr->heap, fun_clos, DUK_HCOMPFUNC_GET_DATA(thr->heap, fun_temp));
156 DUK_HCOMPFUNC_SET_FUNCS(thr->heap, fun_clos, DUK_HCOMPFUNC_GET_FUNCS(thr->heap, fun_temp));
157 DUK_HCOMPFUNC_SET_BYTECODE(thr->heap, fun_clos, DUK_HCOMPFUNC_GET_BYTECODE(thr->heap, fun_temp));
158
159 /* Note: all references inside 'data' need to get their refcounts
160 * upped too. This is the case because refcounts are decreased
161 * through every function referencing 'data' independently.
162 */
163
164 DUK_HBUFFER_INCREF(thr, DUK_HCOMPFUNC_GET_DATA(thr->heap, fun_clos));
165 duk__inc_data_inner_refcounts(thr, fun_temp);
166
167 fun_clos->nregs = fun_temp->nregs;
168 fun_clos->nargs = fun_temp->nargs;
169 #if defined(DUK_USE_DEBUGGER_SUPPORT)
170 fun_clos->start_line = fun_temp->start_line;
171 fun_clos->end_line = fun_temp->end_line;
172 #endif
173
174 DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, fun_clos) != NULL);
175 DUK_ASSERT(DUK_HCOMPFUNC_GET_FUNCS(thr->heap, fun_clos) != NULL);
176 DUK_ASSERT(DUK_HCOMPFUNC_GET_BYTECODE(thr->heap, fun_clos) != NULL);
177
178 /* XXX: Could also copy from template, but there's no way to have any
179 * other value here now (used code has no access to the template).
180 * Prototype is set by duk_push_hcompfunc().
181 */
182 DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, &fun_clos->obj) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
183 #if 0
184 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, &fun_clos->obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
185 #endif
186
187 /* Copy duk_hobject flags as is from the template using a mask.
188 * Leave out duk_heaphdr owned flags just in case (e.g. if there's
189 * some GC flag or similar). Some flags can then be adjusted
190 * separately if necessary.
191 */
192
193 /* DUK_HEAPHDR_SET_FLAGS() masks changes to non-duk_heaphdr flags only. */
194 DUK_HEAPHDR_SET_FLAGS((duk_heaphdr *) fun_clos, DUK_HEAPHDR_GET_FLAGS_RAW((duk_heaphdr *) fun_temp));
195 DUK_DD(DUK_DDPRINT("fun_temp heaphdr flags: 0x%08lx, fun_clos heaphdr flags: 0x%08lx",
196 (unsigned long) DUK_HEAPHDR_GET_FLAGS_RAW((duk_heaphdr *) fun_temp),
197 (unsigned long) DUK_HEAPHDR_GET_FLAGS_RAW((duk_heaphdr *) fun_clos)));
198
199 DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(&fun_clos->obj));
200 DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(&fun_clos->obj));
201 DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(&fun_clos->obj));
202 DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(&fun_clos->obj));
203 DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(&fun_clos->obj));
204 /* DUK_HOBJECT_FLAG_ARRAY_PART: don't care */
205 /* DUK_HOBJECT_FLAG_NEWENV: handled below */
206 DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(&fun_clos->obj));
207 DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(&fun_clos->obj));
208 DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(&fun_clos->obj));
209
210 if (!DUK_HOBJECT_HAS_CONSTRUCTABLE(&fun_clos->obj)) {
211 /* If the template is not constructable don't add an automatic
212 * .prototype property. This is the case for e.g. ES2015 object
213 * literal getters/setters and method definitions.
214 */
215 add_auto_proto = 0;
216 }
217
218 /*
219 * Setup environment record properties based on the template and
220 * its flags.
221 *
222 * If DUK_HOBJECT_HAS_NEWENV(fun_temp) is true, the environment
223 * records represent identifiers "outside" the function; the
224 * "inner" environment records are created on demand. Otherwise,
225 * the environment records are those that will be directly used
226 * (e.g. for declarations).
227 *
228 * _Lexenv is always set; _Varenv defaults to _Lexenv if missing,
229 * so _Varenv is only set if _Lexenv != _Varenv.
230 *
231 * This is relatively complex, see doc/identifier-handling.rst.
232 */
233
234 if (DUK_HOBJECT_HAS_NEWENV(&fun_clos->obj)) {
235 #if defined(DUK_USE_FUNC_NAME_PROPERTY)
236 if (DUK_HOBJECT_HAS_NAMEBINDING(&fun_clos->obj)) {
237 duk_hobject *proto;
238 duk_hdecenv *new_env;
239
240 /*
241 * Named function expression, name needs to be bound
242 * in an intermediate environment record. The "outer"
243 * lexical/variable environment will thus be:
244 *
245 * a) { funcname: <func>, __prototype: outer_lex_env }
246 * b) { funcname: <func>, __prototype: <globalenv> } (if outer_lex_env missing)
247 */
248
249 if (outer_lex_env) {
250 proto = outer_lex_env;
251 } else {
252 proto = thr->builtins[DUK_BIDX_GLOBAL_ENV];
253 }
254
255 /* -> [ ... closure template env ] */
256 new_env = duk_hdecenv_alloc(thr,
257 DUK_HOBJECT_FLAG_EXTENSIBLE |
258 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV));
259 DUK_ASSERT(new_env != NULL);
260 duk_push_hobject(thr, (duk_hobject *) new_env);
261
262 DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL);
263 DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) new_env, proto);
264 DUK_HOBJECT_INCREF_ALLOWNULL(thr, proto);
265
266 DUK_ASSERT(new_env->thread == NULL); /* Closed. */
267 DUK_ASSERT(new_env->varmap == NULL);
268
269 /* It's important that duk_xdef_prop() is a 'raw define' so that any
270 * properties in an ancestor are never an issue (they should never be
271 * e.g. non-writable, but just in case).
272 *
273 * Because template objects are not visible to user code, the case
274 * where .name is missing shouldn't happen in practice. It it does,
275 * the name 'undefined' gets bound and maps to the closure (which is
276 * a bit odd, but safe).
277 */
278 (void) duk_get_prop_stridx_short(thr, -2, DUK_STRIDX_NAME);
279 /* -> [ ... closure template env funcname ] */
280 duk_dup_m4(thr); /* -> [ ... closure template env funcname closure ] */
281 duk_xdef_prop(thr, -3, DUK_PROPDESC_FLAGS_NONE); /* -> [ ... closure template env ] */
282 /* env[funcname] = closure */
283
284 /* [ ... closure template env ] */
285
286 DUK_HCOMPFUNC_SET_LEXENV(thr->heap, fun_clos, (duk_hobject *) new_env);
287 DUK_HCOMPFUNC_SET_VARENV(thr->heap, fun_clos, (duk_hobject *) new_env);
288 DUK_HOBJECT_INCREF(thr, (duk_hobject *) new_env);
289 DUK_HOBJECT_INCREF(thr, (duk_hobject *) new_env);
290 duk_pop_unsafe(thr);
291
292 /* [ ... closure template ] */
293 }
294 else
295 #endif /* DUK_USE_FUNC_NAME_PROPERTY */
296 {
297 /*
298 * Other cases (function declaration, anonymous function expression,
299 * strict direct eval code). The "outer" environment will be whatever
300 * the caller gave us.
301 */
302
303 DUK_HCOMPFUNC_SET_LEXENV(thr->heap, fun_clos, outer_lex_env);
304 DUK_HCOMPFUNC_SET_VARENV(thr->heap, fun_clos, outer_lex_env);
305 DUK_HOBJECT_INCREF(thr, outer_lex_env);
306 DUK_HOBJECT_INCREF(thr, outer_lex_env);
307
308 /* [ ... closure template ] */
309 }
310 } else {
311 /*
312 * Function gets no new environment when called. This is the
313 * case for global code, indirect eval code, and non-strict
314 * direct eval code. There is no direct correspondence to the
315 * E5 specification, as global/eval code is not exposed as a
316 * function.
317 */
318
319 DUK_ASSERT(!DUK_HOBJECT_HAS_NAMEBINDING(&fun_temp->obj));
320
321 DUK_HCOMPFUNC_SET_LEXENV(thr->heap, fun_clos, outer_lex_env);
322 DUK_HCOMPFUNC_SET_VARENV(thr->heap, fun_clos, outer_var_env);
323 DUK_HOBJECT_INCREF(thr, outer_lex_env); /* NULLs not allowed; asserted on entry */
324 DUK_HOBJECT_INCREF(thr, outer_var_env);
325 }
326 DUK_DDD(DUK_DDDPRINT("closure varenv -> %!ipO, lexenv -> %!ipO",
327 (duk_heaphdr *) fun_clos->var_env,
328 (duk_heaphdr *) fun_clos->lex_env));
329
330 /* Call handling assumes this for all callable closures. */
331 DUK_ASSERT(DUK_HCOMPFUNC_GET_LEXENV(thr->heap, fun_clos) != NULL);
332 DUK_ASSERT(DUK_HCOMPFUNC_GET_VARENV(thr->heap, fun_clos) != NULL);
333
334 /*
335 * Copy some internal properties directly
336 *
337 * The properties will be non-writable and non-enumerable, but
338 * configurable.
339 *
340 * Function templates are bare objects, so inheritance of internal
341 * Symbols is not an issue here even when using ordinary property
342 * reads. The function instance created is not bare, so internal
343 * Symbols must be defined without inheritance checks.
344 */
345
346 /* [ ... closure template ] */
347
348 DUK_DDD(DUK_DDDPRINT("copying properties: closure=%!iT, template=%!iT",
349 (duk_tval *) duk_get_tval(thr, -2),
350 (duk_tval *) duk_get_tval(thr, -1)));
351
352 for (i = 0; i < (duk_small_uint_t) (sizeof(duk__closure_copy_proplist) / sizeof(duk_uint16_t)); i++) {
353 duk_small_int_t stridx = (duk_small_int_t) duk__closure_copy_proplist[i];
354 if (duk_xget_owndataprop_stridx_short(thr, -1, stridx)) {
355 /* [ ... closure template val ] */
356 DUK_DDD(DUK_DDDPRINT("copying property, stridx=%ld -> found", (long) stridx));
357 duk_xdef_prop_stridx_short(thr, -3, stridx, DUK_PROPDESC_FLAGS_C);
358 } else {
359 DUK_DDD(DUK_DDDPRINT("copying property, stridx=%ld -> not found", (long) stridx));
360 duk_pop_unsafe(thr);
361 }
362 }
363
364 /*
365 * "length" maps to number of formals (E5 Section 13.2) for function
366 * declarations/expressions (non-bound functions). Note that 'nargs'
367 * is NOT necessarily equal to the number of arguments. Use length
368 * of _Formals; if missing, assume nargs matches .length.
369 */
370
371 /* [ ... closure template ] */
372
373 formals = duk_hobject_get_formals(thr, (duk_hobject *) fun_temp);
374 if (formals) {
375 len_value = (duk_uint_t) formals->length;
376 DUK_DD(DUK_DDPRINT("closure length from _Formals -> %ld", (long) len_value));
377 } else {
378 len_value = fun_temp->nargs;
379 DUK_DD(DUK_DDPRINT("closure length defaulted from nargs -> %ld", (long) len_value));
380 }
381
382 duk_push_uint(thr, len_value); /* [ ... closure template len_value ] */
383 duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C);
384
385 /*
386 * "prototype" is, by default, a fresh object with the "constructor"
387 * property.
388 *
389 * Note that this creates a circular reference for every function
390 * instance (closure) which prevents refcount-based collection of
391 * function instances.
392 *
393 * XXX: Try to avoid creating the default prototype object, because
394 * many functions are not used as constructors and the default
395 * prototype is unnecessary. Perhaps it could be created on-demand
396 * when it is first accessed?
397 */
398
399 /* [ ... closure template ] */
400
401 if (add_auto_proto) {
402 duk_push_object(thr); /* -> [ ... closure template newobj ] */
403 duk_dup_m3(thr); /* -> [ ... closure template newobj closure ] */
404 duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_CONSTRUCTOR, DUK_PROPDESC_FLAGS_WC); /* -> [ ... closure template newobj ] */
405 duk_compact(thr, -1); /* compact the prototype */
406 duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_PROTOTYPE, DUK_PROPDESC_FLAGS_W); /* -> [ ... closure template ] */
407 }
408
409 /*
410 * "arguments" and "caller" must be mapped to throwers for strict
411 * mode and bound functions (E5 Section 15.3.5).
412 *
413 * XXX: This is expensive to have for every strict function instance.
414 * Try to implement as virtual properties or on-demand created properties.
415 */
416
417 /* [ ... closure template ] */
418
419 if (DUK_HOBJECT_HAS_STRICT(&fun_clos->obj)) {
420 duk_xdef_prop_stridx_thrower(thr, -2, DUK_STRIDX_CALLER);
421 duk_xdef_prop_stridx_thrower(thr, -2, DUK_STRIDX_LC_ARGUMENTS);
422 } else {
423 #if defined(DUK_USE_NONSTD_FUNC_CALLER_PROPERTY)
424 DUK_DDD(DUK_DDDPRINT("function is non-strict and non-standard 'caller' property in use, add initial 'null' value"));
425 duk_push_null(thr);
426 duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_CALLER, DUK_PROPDESC_FLAGS_NONE);
427 #else
428 DUK_DDD(DUK_DDDPRINT("function is non-strict and non-standard 'caller' property not used"));
429 #endif
430 }
431
432 /*
433 * "name" used to be non-standard but is now defined by ES2015.
434 * In ES2015/ES2016 the .name property is configurable.
435 */
436
437 /* [ ... closure template ] */
438
439 #if defined(DUK_USE_FUNC_NAME_PROPERTY)
440 /* XXX: Look for own property only; doesn't matter much because
441 * templates are bare objects.
442 */
443 if (duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_NAME)) {
444 /* [ ... closure template name ] */
445 DUK_ASSERT(duk_is_string(thr, -1));
446 DUK_DD(DUK_DDPRINT("setting function instance name to %!T", duk_get_tval(thr, -1)));
447 duk_xdef_prop_stridx_short(thr, -3, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C); /* -> [ ... closure template ] */
448 } else {
449 /* Anonymous functions don't have a .name in ES2015, so don't set
450 * it on the instance either. The instance will then inherit
451 * it from Function.prototype.name.
452 */
453 DUK_DD(DUK_DDPRINT("not setting function instance .name"));
454 duk_pop_unsafe(thr);
455 }
456 #endif
457
458 /*
459 * Compact the closure, in most cases no properties will be added later.
460 * Also, without this the closures end up having unused property slots
461 * (e.g. in Duktape 0.9.0, 8 slots would be allocated and only 7 used).
462 * A better future solution would be to allocate the closure directly
463 * to correct size (and setup the properties directly without going
464 * through the API).
465 */
466
467 duk_compact(thr, -2);
468
469 /*
470 * Some assertions (E5 Section 13.2).
471 */
472
473 DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(&fun_clos->obj) == DUK_HOBJECT_CLASS_FUNCTION);
474 DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, &fun_clos->obj) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
475 DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(&fun_clos->obj));
476 DUK_ASSERT(duk_has_prop_stridx(thr, -2, DUK_STRIDX_LENGTH) != 0);
477 DUK_ASSERT(add_auto_proto == 0 || duk_has_prop_stridx(thr, -2, DUK_STRIDX_PROTOTYPE) != 0);
478 /* May be missing .name */
479 DUK_ASSERT(!DUK_HOBJECT_HAS_STRICT(&fun_clos->obj) ||
480 duk_has_prop_stridx(thr, -2, DUK_STRIDX_CALLER) != 0);
481 DUK_ASSERT(!DUK_HOBJECT_HAS_STRICT(&fun_clos->obj) ||
482 duk_has_prop_stridx(thr, -2, DUK_STRIDX_LC_ARGUMENTS) != 0);
483
484 /*
485 * Finish
486 */
487
488 /* [ ... closure template ] */
489
490 DUK_DDD(DUK_DDDPRINT("created function instance: template=%!iT -> closure=%!iT",
491 (duk_tval *) duk_get_tval(thr, -1),
492 (duk_tval *) duk_get_tval(thr, -2)));
493
494 duk_pop_unsafe(thr);
495
496 /* [ ... closure ] */
497 }
498
499 /*
500 * Delayed activation environment record initialization (for functions
501 * with NEWENV).
502 *
503 * The non-delayed initialization is handled by duk_handle_call().
504 */
505
duk__preallocate_env_entries(duk_hthread * thr,duk_hobject * varmap,duk_hobject * env)506 DUK_LOCAL void duk__preallocate_env_entries(duk_hthread *thr, duk_hobject *varmap, duk_hobject *env) {
507 duk_uint_fast32_t i;
508
509 for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(varmap); i++) {
510 duk_hstring *key;
511
512 key = DUK_HOBJECT_E_GET_KEY(thr->heap, varmap, i);
513 DUK_ASSERT(key != NULL); /* assume keys are compact in _Varmap */
514 DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, varmap, i)); /* assume plain values */
515
516 /* Predefine as 'undefined' to reserve a property slot.
517 * This makes the unwind process (where register values
518 * are copied to the env object) safe against throwing.
519 *
520 * XXX: This could be made much faster by creating the
521 * property table directly.
522 */
523 duk_push_undefined(thr);
524 DUK_DDD(DUK_DDDPRINT("preallocate env entry for key %!O", key));
525 duk_hobject_define_property_internal(thr, env, key, DUK_PROPDESC_FLAGS_WE);
526 }
527 }
528
529 /* shared helper */
530 DUK_INTERNAL
duk_create_activation_environment_record(duk_hthread * thr,duk_hobject * func,duk_size_t bottom_byteoff)531 duk_hobject *duk_create_activation_environment_record(duk_hthread *thr,
532 duk_hobject *func,
533 duk_size_t bottom_byteoff) {
534 duk_hdecenv *env;
535 duk_hobject *parent;
536 duk_hcompfunc *f;
537
538 DUK_ASSERT(thr != NULL);
539 DUK_ASSERT(func != NULL);
540
541 DUK_STATS_INC(thr->heap, stats_envrec_create);
542
543 f = (duk_hcompfunc *) func;
544 parent = DUK_HCOMPFUNC_GET_LEXENV(thr->heap, f);
545 if (!parent) {
546 parent = thr->builtins[DUK_BIDX_GLOBAL_ENV];
547 }
548
549 env = duk_hdecenv_alloc(thr,
550 DUK_HOBJECT_FLAG_EXTENSIBLE |
551 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV));
552 DUK_ASSERT(env != NULL);
553 duk_push_hobject(thr, (duk_hobject *) env);
554
555 DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) env) == NULL);
556 DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) env, parent);
557 DUK_HOBJECT_INCREF_ALLOWNULL(thr, parent); /* parent env is the prototype */
558
559 /* open scope information, for compiled functions only */
560
561 DUK_ASSERT(env->thread == NULL);
562 DUK_ASSERT(env->varmap == NULL);
563 DUK_ASSERT(env->regbase_byteoff == 0);
564 if (DUK_HOBJECT_IS_COMPFUNC(func)) {
565 duk_hobject *varmap;
566
567 varmap = duk_hobject_get_varmap(thr, func);
568 if (varmap != NULL) {
569 env->varmap = varmap;
570 DUK_HOBJECT_INCREF(thr, varmap);
571 env->thread = thr;
572 DUK_HTHREAD_INCREF(thr, thr);
573 env->regbase_byteoff = bottom_byteoff;
574
575 /* Preallocate env property table to avoid potential
576 * for out-of-memory on unwind when the env is closed.
577 */
578 duk__preallocate_env_entries(thr, varmap, (duk_hobject *) env);
579 } else {
580 /* If function has no _Varmap, leave the environment closed. */
581 DUK_ASSERT(env->thread == NULL);
582 DUK_ASSERT(env->varmap == NULL);
583 DUK_ASSERT(env->regbase_byteoff == 0);
584 }
585 }
586
587 return (duk_hobject *) env;
588 }
589
590 DUK_INTERNAL
duk_js_init_activation_environment_records_delayed(duk_hthread * thr,duk_activation * act)591 void duk_js_init_activation_environment_records_delayed(duk_hthread *thr,
592 duk_activation *act) {
593 duk_hobject *func;
594 duk_hobject *env;
595
596 DUK_ASSERT(thr != NULL);
597 func = DUK_ACT_GET_FUNC(act);
598 DUK_ASSERT(func != NULL);
599 DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func)); /* bound functions are never in act 'func' */
600
601 /*
602 * Delayed initialization only occurs for 'NEWENV' functions.
603 */
604
605 DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func));
606 DUK_ASSERT(act->lex_env == NULL);
607 DUK_ASSERT(act->var_env == NULL);
608
609 DUK_STATS_INC(thr->heap, stats_envrec_delayedcreate);
610
611 env = duk_create_activation_environment_record(thr, func, act->bottom_byteoff);
612 DUK_ASSERT(env != NULL);
613 /* 'act' is a stable pointer, so still OK. */
614
615 DUK_DDD(DUK_DDDPRINT("created delayed fresh env: %!ipO", (duk_heaphdr *) env));
616 #if defined(DUK_USE_DEBUG_LEVEL) && (DUK_USE_DEBUG_LEVEL >= 2)
617 {
618 duk_hobject *p = env;
619 while (p) {
620 DUK_DDD(DUK_DDDPRINT(" -> %!ipO", (duk_heaphdr *) p));
621 p = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, p);
622 }
623 }
624 #endif
625
626 act->lex_env = env;
627 act->var_env = env;
628 DUK_HOBJECT_INCREF(thr, env); /* XXX: incref by count (here 2 times) */
629 DUK_HOBJECT_INCREF(thr, env);
630
631 duk_pop_unsafe(thr);
632 }
633
634 /*
635 * Closing environment records.
636 *
637 * The environment record MUST be closed with the thread where its activation
638 * is; i.e. if 'env' is open, 'thr' must match env->thread, and the regbase
639 * and varmap must still be valid. On entry, 'env' must be reachable.
640 */
641
duk_js_close_environment_record(duk_hthread * thr,duk_hobject * env)642 DUK_INTERNAL void duk_js_close_environment_record(duk_hthread *thr, duk_hobject *env) {
643 duk_uint_fast32_t i;
644 duk_hobject *varmap;
645 duk_hstring *key;
646 duk_tval *tv;
647 duk_uint_t regnum;
648
649 DUK_ASSERT(thr != NULL);
650 DUK_ASSERT(env != NULL);
651
652 if (DUK_UNLIKELY(!DUK_HOBJECT_IS_DECENV(env))) {
653 DUK_DDD(DUK_DDDPRINT("env not a declarative record: %!iO", (duk_heaphdr *) env));
654 return;
655 }
656
657 varmap = ((duk_hdecenv *) env)->varmap;
658 if (varmap == NULL) {
659 DUK_DDD(DUK_DDDPRINT("env already closed: %!iO", (duk_heaphdr *) env));
660
661 return;
662 }
663 DUK_ASSERT(((duk_hdecenv *) env)->thread != NULL);
664 DUK_HDECENV_ASSERT_VALID((duk_hdecenv *) env);
665
666 DUK_DDD(DUK_DDDPRINT("closing env: %!iO", (duk_heaphdr *) env));
667 DUK_DDD(DUK_DDDPRINT("varmap: %!O", (duk_heaphdr *) varmap));
668
669 /* Env must be closed in the same thread as where it runs. */
670 DUK_ASSERT(((duk_hdecenv *) env)->thread == thr);
671
672 /* XXX: additional conditions when to close variables? we don't want to do it
673 * unless the environment may have "escaped" (referenced in a function closure).
674 * With delayed environments, the existence is probably good enough of a check.
675 */
676
677 /* Note: we rely on the _Varmap having a bunch of nice properties, like:
678 * - being compacted and unmodified during this process
679 * - not containing an array part
680 * - having correct value types
681 */
682
683 DUK_DDD(DUK_DDDPRINT("copying bound register values, %ld bound regs", (long) DUK_HOBJECT_GET_ENEXT(varmap)));
684
685 /* Copy over current variable values from value stack to the
686 * environment record. The scope object is empty but may
687 * inherit from another scope which has conflicting names.
688 */
689
690 /* XXX: Do this using a once allocated entry area, no side effects.
691 * Hash part would need special treatment however (maybe copy, and
692 * then realloc with hash part if large enough).
693 */
694 for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(varmap); i++) {
695 duk_size_t regbase_byteoff;
696
697 key = DUK_HOBJECT_E_GET_KEY(thr->heap, varmap, i);
698 DUK_ASSERT(key != NULL); /* assume keys are compact in _Varmap */
699 DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, varmap, i)); /* assume plain values */
700
701 tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, varmap, i);
702 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
703 DUK_ASSERT(DUK_TVAL_GET_NUMBER(tv) <= (duk_double_t) DUK_UINT32_MAX); /* limits */
704 #if defined(DUK_USE_FASTINT)
705 DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv));
706 regnum = (duk_uint_t) DUK_TVAL_GET_FASTINT_U32(tv);
707 #else
708 regnum = (duk_uint_t) DUK_TVAL_GET_NUMBER(tv);
709 #endif
710
711 regbase_byteoff = ((duk_hdecenv *) env)->regbase_byteoff;
712 DUK_ASSERT((duk_uint8_t *) thr->valstack + regbase_byteoff + sizeof(duk_tval) * regnum >= (duk_uint8_t *) thr->valstack);
713 DUK_ASSERT((duk_uint8_t *) thr->valstack + regbase_byteoff + sizeof(duk_tval) * regnum < (duk_uint8_t *) thr->valstack_top);
714
715 /* Write register value into env as named properties.
716 * If property already exists, overwrites silently.
717 * Property is writable, but not deletable (not configurable
718 * in terms of property attributes).
719 *
720 * This property write must not throw because we're unwinding
721 * and unwind code is not allowed to throw at present. The
722 * call itself has no such guarantees, but we've preallocated
723 * entries for each property when the env was created, so no
724 * out-of-memory error should be possible. If this guarantee
725 * is not provided, problems like GH-476 may happen.
726 */
727 duk_push_tval(thr, (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + regbase_byteoff + sizeof(duk_tval) * regnum));
728 DUK_DDD(DUK_DDDPRINT("closing identifier %!O -> reg %ld, value %!T",
729 (duk_heaphdr *) key,
730 (long) regnum,
731 (duk_tval *) duk_get_tval(thr, -1)));
732 duk_hobject_define_property_internal(thr, env, key, DUK_PROPDESC_FLAGS_WE);
733 }
734
735 /* NULL atomically to avoid inconsistent state + side effects. */
736 DUK_HOBJECT_DECREF_NORZ(thr, ((duk_hdecenv *) env)->thread);
737 DUK_HOBJECT_DECREF_NORZ(thr, ((duk_hdecenv *) env)->varmap);
738 ((duk_hdecenv *) env)->thread = NULL;
739 ((duk_hdecenv *) env)->varmap = NULL;
740
741 DUK_DDD(DUK_DDDPRINT("env after closing: %!O", (duk_heaphdr *) env));
742 }
743
744 /*
745 * GETIDREF: a GetIdentifierReference-like helper.
746 *
747 * Provides a parent traversing lookup and a single level lookup
748 * (for HasBinding).
749 *
750 * Instead of returning the value, returns a bunch of values allowing
751 * the caller to read, write, or delete the binding. Value pointers
752 * are duk_tval pointers which can be mutated directly as long as
753 * refcounts are properly updated. Note that any operation which may
754 * reallocate valstacks or compact objects may invalidate the returned
755 * duk_tval (but not object) pointers, so caller must be very careful.
756 *
757 * If starting environment record 'env' is given, 'act' is ignored.
758 * However, if 'env' is NULL, the caller may identify, in 'act', an
759 * activation which hasn't had its declarative environment initialized
760 * yet. The activation registers are then looked up, and its parent
761 * traversed normally.
762 *
763 * The 'out' structure values are only valid if the function returns
764 * success (non-zero).
765 */
766
767 /* lookup name from an open declarative record's registers */
768 DUK_LOCAL
duk__getid_open_decl_env_regs(duk_hthread * thr,duk_hstring * name,duk_hdecenv * env,duk__id_lookup_result * out)769 duk_bool_t duk__getid_open_decl_env_regs(duk_hthread *thr,
770 duk_hstring *name,
771 duk_hdecenv *env,
772 duk__id_lookup_result *out) {
773 duk_tval *tv;
774 duk_size_t reg_rel;
775
776 DUK_ASSERT(thr != NULL);
777 DUK_ASSERT(name != NULL);
778 DUK_ASSERT(env != NULL);
779 DUK_ASSERT(out != NULL);
780
781 DUK_ASSERT(DUK_HOBJECT_IS_DECENV((duk_hobject *) env));
782 DUK_HDECENV_ASSERT_VALID(env);
783
784 if (env->thread == NULL) {
785 /* already closed */
786 return 0;
787 }
788 DUK_ASSERT(env->varmap != NULL);
789
790 tv = duk_hobject_find_entry_tval_ptr(thr->heap, env->varmap, name);
791 if (DUK_UNLIKELY(tv == NULL)) {
792 return 0;
793 }
794
795 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
796 DUK_ASSERT(DUK_TVAL_GET_NUMBER(tv) <= (duk_double_t) DUK_UINT32_MAX); /* limits */
797 #if defined(DUK_USE_FASTINT)
798 DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv));
799 reg_rel = (duk_size_t) DUK_TVAL_GET_FASTINT_U32(tv);
800 #else
801 reg_rel = (duk_size_t) DUK_TVAL_GET_NUMBER(tv);
802 #endif
803 DUK_ASSERT_DISABLE(reg_rel >= 0); /* unsigned */
804
805 tv = (duk_tval *) (void *) ((duk_uint8_t *) env->thread->valstack + env->regbase_byteoff + sizeof(duk_tval) * reg_rel);
806 DUK_ASSERT(tv >= env->thread->valstack && tv < env->thread->valstack_end); /* XXX: more accurate? */
807
808 out->value = tv;
809 out->attrs = DUK_PROPDESC_FLAGS_W; /* registers are mutable, non-deletable */
810 out->env = (duk_hobject *) env;
811 out->holder = NULL;
812 out->has_this = 0;
813 return 1;
814 }
815
816 /* lookup name from current activation record's functions' registers */
817 DUK_LOCAL
duk__getid_activation_regs(duk_hthread * thr,duk_hstring * name,duk_activation * act,duk__id_lookup_result * out)818 duk_bool_t duk__getid_activation_regs(duk_hthread *thr,
819 duk_hstring *name,
820 duk_activation *act,
821 duk__id_lookup_result *out) {
822 duk_tval *tv;
823 duk_hobject *func;
824 duk_hobject *varmap;
825 duk_size_t reg_rel;
826
827 DUK_ASSERT(thr != NULL);
828 DUK_ASSERT(name != NULL);
829 DUK_ASSERT(act != NULL);
830 DUK_ASSERT(out != NULL);
831
832 func = DUK_ACT_GET_FUNC(act);
833 DUK_ASSERT(func != NULL);
834 DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func));
835
836 if (!DUK_HOBJECT_IS_COMPFUNC(func)) {
837 return 0;
838 }
839
840 /* XXX: move varmap to duk_hcompfunc struct field? */
841 varmap = duk_hobject_get_varmap(thr, func);
842 if (!varmap) {
843 return 0;
844 }
845
846 tv = duk_hobject_find_entry_tval_ptr(thr->heap, varmap, name);
847 if (!tv) {
848 return 0;
849 }
850 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
851 reg_rel = (duk_size_t) DUK_TVAL_GET_NUMBER(tv);
852 DUK_ASSERT_DISABLE(reg_rel >= 0);
853 DUK_ASSERT(reg_rel < ((duk_hcompfunc *) func)->nregs);
854
855 tv = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + act->bottom_byteoff);
856 tv += reg_rel;
857
858 out->value = tv;
859 out->attrs = DUK_PROPDESC_FLAGS_W; /* registers are mutable, non-deletable */
860 out->env = NULL;
861 out->holder = NULL;
862 out->has_this = 0;
863 return 1;
864 }
865
866 DUK_LOCAL
duk__get_identifier_reference(duk_hthread * thr,duk_hobject * env,duk_hstring * name,duk_activation * act,duk_bool_t parents,duk__id_lookup_result * out)867 duk_bool_t duk__get_identifier_reference(duk_hthread *thr,
868 duk_hobject *env,
869 duk_hstring *name,
870 duk_activation *act,
871 duk_bool_t parents,
872 duk__id_lookup_result *out) {
873 duk_tval *tv;
874 duk_uint_t sanity;
875
876 DUK_ASSERT(thr != NULL);
877 DUK_ASSERT(env != NULL || act != NULL);
878 DUK_ASSERT(name != NULL);
879 DUK_ASSERT(out != NULL);
880
881 DUK_ASSERT(!env || DUK_HOBJECT_IS_ENV(env));
882 DUK_ASSERT(!env || !DUK_HOBJECT_HAS_ARRAY_PART(env));
883
884 /*
885 * Conceptually, we look for the identifier binding by starting from
886 * 'env' and following to chain of environment records (represented
887 * by the prototype chain).
888 *
889 * If 'env' is NULL, the current activation does not yet have an
890 * allocated declarative environment record; this should be treated
891 * exactly as if the environment record existed but had no bindings
892 * other than register bindings.
893 *
894 * Note: we assume that with the DUK_HOBJECT_FLAG_NEWENV cleared
895 * the environment will always be initialized immediately; hence
896 * a NULL 'env' should only happen with the flag set. This is the
897 * case for: (1) function calls, and (2) strict, direct eval calls.
898 */
899
900 if (env == NULL && act != NULL) {
901 duk_hobject *func;
902 duk_hcompfunc *f;
903
904 DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference: env is NULL, activation is non-NULL -> "
905 "delayed env case, look up activation regs first"));
906
907 /*
908 * Try registers
909 */
910
911 if (duk__getid_activation_regs(thr, name, act, out)) {
912 DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
913 "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O "
914 "(found from register bindings when env=NULL)",
915 (duk_heaphdr *) name, (duk_tval *) out->value,
916 (long) out->attrs, (long) out->has_this,
917 (duk_heaphdr *) out->env, (duk_heaphdr *) out->holder));
918 return 1;
919 }
920
921 DUK_DDD(DUK_DDDPRINT("not found in current activation regs"));
922
923 /*
924 * Not found in registers, proceed to the parent record.
925 * Here we need to determine what the parent would be,
926 * if 'env' was not NULL (i.e. same logic as when initializing
927 * the record).
928 *
929 * Note that environment initialization is only deferred when
930 * DUK_HOBJECT_HAS_NEWENV is set, and this only happens for:
931 * - Function code
932 * - Strict eval code
933 *
934 * We only need to check _Lexenv here; _Varenv exists only if it
935 * differs from _Lexenv (and thus _Lexenv will also be present).
936 */
937
938 if (!parents) {
939 DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference failed, no parent traversal "
940 "(not found from register bindings when env=NULL)"));
941 goto fail_not_found;
942 }
943
944 func = DUK_ACT_GET_FUNC(act);
945 DUK_ASSERT(func != NULL);
946 DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func));
947 f = (duk_hcompfunc *) func;
948
949 env = DUK_HCOMPFUNC_GET_LEXENV(thr->heap, f);
950 if (!env) {
951 env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
952 }
953
954 DUK_DDD(DUK_DDDPRINT("continue lookup from env: %!iO",
955 (duk_heaphdr *) env));
956 }
957
958 /*
959 * Prototype walking starting from 'env'.
960 *
961 * ('act' is not needed anywhere here.)
962 */
963
964 sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
965 while (env != NULL) {
966 duk_small_uint_t cl;
967 duk_uint_t attrs;
968
969 DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference, name=%!O, considering env=%p -> %!iO",
970 (duk_heaphdr *) name,
971 (void *) env,
972 (duk_heaphdr *) env));
973
974 DUK_ASSERT(env != NULL);
975 DUK_ASSERT(DUK_HOBJECT_IS_ENV(env));
976 DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(env));
977
978 cl = DUK_HOBJECT_GET_CLASS_NUMBER(env);
979 DUK_ASSERT(cl == DUK_HOBJECT_CLASS_OBJENV || cl == DUK_HOBJECT_CLASS_DECENV);
980 if (cl == DUK_HOBJECT_CLASS_DECENV) {
981 /*
982 * Declarative environment record.
983 *
984 * Identifiers can never be stored in ancestors and are
985 * always plain values, so we can use an internal helper
986 * and access the value directly with an duk_tval ptr.
987 *
988 * A closed environment is only indicated by it missing
989 * the "book-keeping" properties required for accessing
990 * register-bound variables.
991 */
992
993 DUK_HDECENV_ASSERT_VALID((duk_hdecenv *) env);
994 if (duk__getid_open_decl_env_regs(thr, name, (duk_hdecenv *) env, out)) {
995 DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
996 "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O "
997 "(declarative environment record, scope open, found in regs)",
998 (duk_heaphdr *) name, (duk_tval *) out->value,
999 (long) out->attrs, (long) out->has_this,
1000 (duk_heaphdr *) out->env, (duk_heaphdr *) out->holder));
1001 return 1;
1002 }
1003
1004 tv = duk_hobject_find_entry_tval_ptr_and_attrs(thr->heap, env, name, &attrs);
1005 if (tv) {
1006 out->value = tv;
1007 out->attrs = attrs;
1008 out->env = env;
1009 out->holder = env;
1010 out->has_this = 0;
1011
1012 DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
1013 "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O "
1014 "(declarative environment record, found in properties)",
1015 (duk_heaphdr *) name, (duk_tval *) out->value,
1016 (long) out->attrs, (long) out->has_this,
1017 (duk_heaphdr *) out->env, (duk_heaphdr *) out->holder));
1018 return 1;
1019 }
1020 } else {
1021 /*
1022 * Object environment record.
1023 *
1024 * Binding (target) object is an external, uncontrolled object.
1025 * Identifier may be bound in an ancestor property, and may be
1026 * an accessor. Target can also be a Proxy which we must support
1027 * here.
1028 */
1029
1030 /* XXX: we could save space by using _Target OR _This. If _Target, assume
1031 * this binding is undefined. If _This, assumes this binding is _This, and
1032 * target is also _This. One property would then be enough.
1033 */
1034
1035 duk_hobject *target;
1036 duk_bool_t found;
1037
1038 DUK_ASSERT(cl == DUK_HOBJECT_CLASS_OBJENV);
1039 DUK_HOBJENV_ASSERT_VALID((duk_hobjenv *) env);
1040
1041 target = ((duk_hobjenv *) env)->target;
1042 DUK_ASSERT(target != NULL);
1043
1044 /* Target may be a Proxy or property may be an accessor, so we must
1045 * use an actual, Proxy-aware hasprop check here.
1046 *
1047 * out->holder is NOT set to the actual duk_hobject where the
1048 * property is found, but rather the object binding target object.
1049 */
1050
1051 #if defined(DUK_USE_ES6_PROXY)
1052 if (DUK_UNLIKELY(DUK_HOBJECT_IS_PROXY(target))) {
1053 duk_tval tv_name;
1054 duk_tval tv_target_tmp;
1055
1056 DUK_ASSERT(name != NULL);
1057 DUK_TVAL_SET_STRING(&tv_name, name);
1058 DUK_TVAL_SET_OBJECT(&tv_target_tmp, target);
1059
1060 found = duk_hobject_hasprop(thr, &tv_target_tmp, &tv_name);
1061 } else
1062 #endif /* DUK_USE_ES6_PROXY */
1063 {
1064 /* XXX: duk_hobject_hasprop() would be correct for
1065 * non-Proxy objects too, but it is about ~20-25%
1066 * slower at present so separate code paths for
1067 * Proxy and non-Proxy now.
1068 */
1069 found = duk_hobject_hasprop_raw(thr, target, name);
1070 }
1071
1072 if (found) {
1073 out->value = NULL; /* can't get value, may be accessor */
1074 out->attrs = 0; /* irrelevant when out->value == NULL */
1075 out->env = env;
1076 out->holder = target;
1077 out->has_this = ((duk_hobjenv *) env)->has_this;
1078
1079 DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
1080 "name=%!O -> value=%!T, attrs=%ld, has_this=%ld, env=%!O, holder=%!O "
1081 "(object environment record)",
1082 (duk_heaphdr *) name, (duk_tval *) out->value,
1083 (long) out->attrs, (long) out->has_this,
1084 (duk_heaphdr *) out->env, (duk_heaphdr *) out->holder));
1085 return 1;
1086 }
1087 }
1088
1089 if (!parents) {
1090 DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference failed, no parent traversal "
1091 "(not found from first traversed env)"));
1092 goto fail_not_found;
1093 }
1094
1095 if (DUK_UNLIKELY(sanity-- == 0)) {
1096 DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT);
1097 DUK_WO_NORETURN(return 0;);
1098 }
1099 env = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, env);
1100 }
1101
1102 /*
1103 * Not found (even in global object)
1104 */
1105
1106 fail_not_found:
1107 return 0;
1108 }
1109
1110 /*
1111 * HASVAR: check identifier binding from a given environment record
1112 * without traversing its parents.
1113 *
1114 * This primitive is not exposed to user code as such, but is used
1115 * internally for e.g. declaration binding instantiation.
1116 *
1117 * See E5 Sections:
1118 * 10.2.1.1.1 HasBinding(N)
1119 * 10.2.1.2.1 HasBinding(N)
1120 *
1121 * Note: strictness has no bearing on this check. Hence we don't take
1122 * a 'strict' parameter.
1123 */
1124
1125 #if 0 /*unused*/
1126 DUK_INTERNAL
1127 duk_bool_t duk_js_hasvar_envrec(duk_hthread *thr,
1128 duk_hobject *env,
1129 duk_hstring *name) {
1130 duk__id_lookup_result ref;
1131 duk_bool_t parents;
1132
1133 DUK_DDD(DUK_DDDPRINT("hasvar: thr=%p, env=%p, name=%!O "
1134 "(env -> %!dO)",
1135 (void *) thr, (void *) env, (duk_heaphdr *) name,
1136 (duk_heaphdr *) env));
1137
1138 DUK_ASSERT(thr != NULL);
1139 DUK_ASSERT(env != NULL);
1140 DUK_ASSERT(name != NULL);
1141
1142 DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(env);
1143 DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name);
1144
1145 DUK_ASSERT(DUK_HOBJECT_IS_ENV(env));
1146 DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(env));
1147
1148 /* lookup results is ignored */
1149 parents = 0;
1150 return duk__get_identifier_reference(thr, env, name, NULL, parents, &ref);
1151 }
1152 #endif
1153
1154 /*
1155 * GETVAR
1156 *
1157 * See E5 Sections:
1158 * 11.1.2 Identifier Reference
1159 * 10.3.1 Identifier Resolution
1160 * 11.13.1 Simple Assignment [example of where the Reference is GetValue'd]
1161 * 8.7.1 GetValue (V)
1162 * 8.12.1 [[GetOwnProperty]] (P)
1163 * 8.12.2 [[GetProperty]] (P)
1164 * 8.12.3 [[Get]] (P)
1165 *
1166 * If 'throw' is true, always leaves two values on top of stack: [val this].
1167 *
1168 * If 'throw' is false, returns 0 if identifier cannot be resolved, and the
1169 * stack will be unaffected in this case. If identifier is resolved, returns
1170 * 1 and leaves [val this] on top of stack.
1171 *
1172 * Note: the 'strict' flag of a reference returned by GetIdentifierReference
1173 * is ignored by GetValue. Hence we don't take a 'strict' parameter.
1174 *
1175 * The 'throw' flag is needed for implementing 'typeof' for an unreferenced
1176 * identifier. An unreference identifier in other contexts generates a
1177 * ReferenceError.
1178 */
1179
1180 DUK_LOCAL
duk__getvar_helper(duk_hthread * thr,duk_hobject * env,duk_activation * act,duk_hstring * name,duk_bool_t throw_flag)1181 duk_bool_t duk__getvar_helper(duk_hthread *thr,
1182 duk_hobject *env,
1183 duk_activation *act,
1184 duk_hstring *name,
1185 duk_bool_t throw_flag) {
1186 duk__id_lookup_result ref;
1187 duk_tval tv_tmp_obj;
1188 duk_tval tv_tmp_key;
1189 duk_bool_t parents;
1190
1191 DUK_DDD(DUK_DDDPRINT("getvar: thr=%p, env=%p, act=%p, name=%!O "
1192 "(env -> %!dO)",
1193 (void *) thr, (void *) env, (void *) act,
1194 (duk_heaphdr *) name, (duk_heaphdr *) env));
1195
1196 DUK_ASSERT(thr != NULL);
1197 DUK_ASSERT(name != NULL);
1198 /* env and act may be NULL */
1199
1200 DUK_STATS_INC(thr->heap, stats_getvar_all);
1201
1202 DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(env);
1203 DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name);
1204
1205 parents = 1; /* follow parent chain */
1206 if (duk__get_identifier_reference(thr, env, name, act, parents, &ref)) {
1207 if (ref.value) {
1208 duk_push_tval(thr, ref.value);
1209 duk_push_undefined(thr);
1210 } else {
1211 DUK_ASSERT(ref.holder != NULL);
1212
1213 /* ref.holder is safe across the getprop call (even
1214 * with side effects) because 'env' is reachable and
1215 * ref.holder is a direct heap pointer.
1216 */
1217
1218 DUK_TVAL_SET_OBJECT(&tv_tmp_obj, ref.holder);
1219 DUK_TVAL_SET_STRING(&tv_tmp_key, name);
1220 (void) duk_hobject_getprop(thr, &tv_tmp_obj, &tv_tmp_key); /* [value] */
1221
1222 if (ref.has_this) {
1223 duk_push_hobject(thr, ref.holder);
1224 } else {
1225 duk_push_undefined(thr);
1226 }
1227
1228 /* [value this] */
1229 }
1230
1231 return 1;
1232 } else {
1233 if (throw_flag) {
1234 DUK_ERROR_FMT1(thr, DUK_ERR_REFERENCE_ERROR,
1235 "identifier '%s' undefined",
1236 (const char *) DUK_HSTRING_GET_DATA(name));
1237 DUK_WO_NORETURN(return 0;);
1238 }
1239
1240 return 0;
1241 }
1242 }
1243
1244 DUK_INTERNAL
duk_js_getvar_envrec(duk_hthread * thr,duk_hobject * env,duk_hstring * name,duk_bool_t throw_flag)1245 duk_bool_t duk_js_getvar_envrec(duk_hthread *thr,
1246 duk_hobject *env,
1247 duk_hstring *name,
1248 duk_bool_t throw_flag) {
1249 return duk__getvar_helper(thr, env, NULL, name, throw_flag);
1250 }
1251
1252 DUK_INTERNAL
duk_js_getvar_activation(duk_hthread * thr,duk_activation * act,duk_hstring * name,duk_bool_t throw_flag)1253 duk_bool_t duk_js_getvar_activation(duk_hthread *thr,
1254 duk_activation *act,
1255 duk_hstring *name,
1256 duk_bool_t throw_flag) {
1257 DUK_ASSERT(act != NULL);
1258 return duk__getvar_helper(thr, act->lex_env, act, name, throw_flag);
1259 }
1260
1261 /*
1262 * PUTVAR
1263 *
1264 * See E5 Sections:
1265 * 11.1.2 Identifier Reference
1266 * 10.3.1 Identifier Resolution
1267 * 11.13.1 Simple Assignment [example of where the Reference is PutValue'd]
1268 * 8.7.2 PutValue (V,W) [see especially step 3.b, undefined -> automatic global in non-strict mode]
1269 * 8.12.4 [[CanPut]] (P)
1270 * 8.12.5 [[Put]] (P)
1271 *
1272 * Note: may invalidate any valstack (or object) duk_tval pointers because
1273 * putting a value may reallocate any object or any valstack. Caller beware.
1274 */
1275
1276 DUK_LOCAL
duk__putvar_helper(duk_hthread * thr,duk_hobject * env,duk_activation * act,duk_hstring * name,duk_tval * val,duk_bool_t strict)1277 void duk__putvar_helper(duk_hthread *thr,
1278 duk_hobject *env,
1279 duk_activation *act,
1280 duk_hstring *name,
1281 duk_tval *val,
1282 duk_bool_t strict) {
1283 duk__id_lookup_result ref;
1284 duk_tval tv_tmp_val;
1285 duk_tval tv_tmp_obj;
1286 duk_tval tv_tmp_key;
1287 duk_bool_t parents;
1288
1289 DUK_STATS_INC(thr->heap, stats_putvar_all);
1290
1291 DUK_DDD(DUK_DDDPRINT("putvar: thr=%p, env=%p, act=%p, name=%!O, val=%p, strict=%ld "
1292 "(env -> %!dO, val -> %!T)",
1293 (void *) thr, (void *) env, (void *) act,
1294 (duk_heaphdr *) name, (void *) val, (long) strict,
1295 (duk_heaphdr *) env, (duk_tval *) val));
1296
1297 DUK_ASSERT(thr != NULL);
1298 DUK_ASSERT(name != NULL);
1299 DUK_ASSERT(val != NULL);
1300 /* env and act may be NULL */
1301
1302 DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(env);
1303 DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name);
1304 DUK_ASSERT_REFCOUNT_NONZERO_TVAL(val);
1305
1306 DUK_TVAL_SET_TVAL(&tv_tmp_val, val); /* Stabilize. */
1307 val = NULL;
1308
1309 /*
1310 * In strict mode E5 protects 'eval' and 'arguments' from being
1311 * assigned to (or even declared anywhere). Attempt to do so
1312 * should result in a compile time SyntaxError. See the internal
1313 * design documentation for details.
1314 *
1315 * Thus, we should never come here, run-time, for strict code,
1316 * and name 'eval' or 'arguments'.
1317 */
1318
1319 DUK_ASSERT(!strict ||
1320 (name != DUK_HTHREAD_STRING_EVAL(thr) &&
1321 name != DUK_HTHREAD_STRING_LC_ARGUMENTS(thr)));
1322
1323 /*
1324 * Lookup variable and update in-place if found.
1325 */
1326
1327 parents = 1; /* follow parent chain */
1328
1329 if (duk__get_identifier_reference(thr, env, name, act, parents, &ref)) {
1330 if (ref.value && (ref.attrs & DUK_PROPDESC_FLAG_WRITABLE)) {
1331 /* Update duk_tval in-place if pointer provided and the
1332 * property is writable. If the property is not writable
1333 * (immutable binding), use duk_hobject_putprop() which
1334 * will respect mutability.
1335 */
1336 duk_tval *tv_val;
1337
1338 tv_val = ref.value;
1339 DUK_ASSERT(tv_val != NULL);
1340 DUK_TVAL_SET_TVAL_UPDREF(thr, tv_val, &tv_tmp_val); /* side effects */
1341
1342 /* ref.value invalidated here */
1343 } else {
1344 DUK_ASSERT(ref.holder != NULL);
1345
1346 DUK_TVAL_SET_OBJECT(&tv_tmp_obj, ref.holder);
1347 DUK_TVAL_SET_STRING(&tv_tmp_key, name);
1348 (void) duk_hobject_putprop(thr, &tv_tmp_obj, &tv_tmp_key, &tv_tmp_val, strict);
1349
1350 /* ref.value invalidated here */
1351 }
1352
1353 return;
1354 }
1355
1356 /*
1357 * Not found: write to global object (non-strict) or ReferenceError
1358 * (strict); see E5 Section 8.7.2, step 3.
1359 */
1360
1361 if (strict) {
1362 DUK_DDD(DUK_DDDPRINT("identifier binding not found, strict => reference error"));
1363 DUK_ERROR_FMT1(thr, DUK_ERR_REFERENCE_ERROR,
1364 "identifier '%s' undefined",
1365 (const char *) DUK_HSTRING_GET_DATA(name));
1366 DUK_WO_NORETURN(return;);
1367 }
1368
1369 DUK_DDD(DUK_DDDPRINT("identifier binding not found, not strict => set to global"));
1370
1371 DUK_TVAL_SET_OBJECT(&tv_tmp_obj, thr->builtins[DUK_BIDX_GLOBAL]);
1372 DUK_TVAL_SET_STRING(&tv_tmp_key, name);
1373 (void) duk_hobject_putprop(thr, &tv_tmp_obj, &tv_tmp_key, &tv_tmp_val, 0); /* 0 = no throw */
1374
1375 /* NB: 'val' may be invalidated here because put_value may realloc valstack,
1376 * caller beware.
1377 */
1378 }
1379
1380 DUK_INTERNAL
duk_js_putvar_envrec(duk_hthread * thr,duk_hobject * env,duk_hstring * name,duk_tval * val,duk_bool_t strict)1381 void duk_js_putvar_envrec(duk_hthread *thr,
1382 duk_hobject *env,
1383 duk_hstring *name,
1384 duk_tval *val,
1385 duk_bool_t strict) {
1386 duk__putvar_helper(thr, env, NULL, name, val, strict);
1387 }
1388
1389 DUK_INTERNAL
duk_js_putvar_activation(duk_hthread * thr,duk_activation * act,duk_hstring * name,duk_tval * val,duk_bool_t strict)1390 void duk_js_putvar_activation(duk_hthread *thr,
1391 duk_activation *act,
1392 duk_hstring *name,
1393 duk_tval *val,
1394 duk_bool_t strict) {
1395 DUK_ASSERT(act != NULL);
1396 duk__putvar_helper(thr, act->lex_env, act, name, val, strict);
1397 }
1398
1399 /*
1400 * DELVAR
1401 *
1402 * See E5 Sections:
1403 * 11.4.1 The delete operator
1404 * 10.2.1.1.5 DeleteBinding (N) [declarative environment record]
1405 * 10.2.1.2.5 DeleteBinding (N) [object environment record]
1406 *
1407 * Variable bindings established inside eval() are deletable (configurable),
1408 * other bindings are not, including variables declared in global level.
1409 * Registers are always non-deletable, and the deletion of other bindings
1410 * is controlled by the configurable flag.
1411 *
1412 * For strict mode code, the 'delete' operator should fail with a compile
1413 * time SyntaxError if applied to identifiers. Hence, no strict mode
1414 * run-time deletion of identifiers should ever happen. This function
1415 * should never be called from strict mode code!
1416 */
1417
1418 DUK_LOCAL
duk__delvar_helper(duk_hthread * thr,duk_hobject * env,duk_activation * act,duk_hstring * name)1419 duk_bool_t duk__delvar_helper(duk_hthread *thr,
1420 duk_hobject *env,
1421 duk_activation *act,
1422 duk_hstring *name) {
1423 duk__id_lookup_result ref;
1424 duk_bool_t parents;
1425
1426 DUK_DDD(DUK_DDDPRINT("delvar: thr=%p, env=%p, act=%p, name=%!O "
1427 "(env -> %!dO)",
1428 (void *) thr, (void *) env, (void *) act,
1429 (duk_heaphdr *) name, (duk_heaphdr *) env));
1430
1431 DUK_ASSERT(thr != NULL);
1432 DUK_ASSERT(name != NULL);
1433 /* env and act may be NULL */
1434
1435 DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name);
1436
1437 parents = 1; /* follow parent chain */
1438
1439 if (duk__get_identifier_reference(thr, env, name, act, parents, &ref)) {
1440 if (ref.value && !(ref.attrs & DUK_PROPDESC_FLAG_CONFIGURABLE)) {
1441 /* Identifier found in registers (always non-deletable)
1442 * or declarative environment record and non-configurable.
1443 */
1444 return 0;
1445 }
1446 DUK_ASSERT(ref.holder != NULL);
1447
1448 return duk_hobject_delprop_raw(thr, ref.holder, name, 0);
1449 }
1450
1451 /*
1452 * Not found (even in global object).
1453 *
1454 * In non-strict mode this is a silent SUCCESS (!), see E5 Section 11.4.1,
1455 * step 3.b. In strict mode this case is a compile time SyntaxError so
1456 * we should not come here.
1457 */
1458
1459 DUK_DDD(DUK_DDDPRINT("identifier to be deleted not found: name=%!O "
1460 "(treated as silent success)",
1461 (duk_heaphdr *) name));
1462 return 1;
1463 }
1464
1465 #if 0 /*unused*/
1466 DUK_INTERNAL
1467 duk_bool_t duk_js_delvar_envrec(duk_hthread *thr,
1468 duk_hobject *env,
1469 duk_hstring *name) {
1470 return duk__delvar_helper(thr, env, NULL, name);
1471 }
1472 #endif
1473
1474 DUK_INTERNAL
duk_js_delvar_activation(duk_hthread * thr,duk_activation * act,duk_hstring * name)1475 duk_bool_t duk_js_delvar_activation(duk_hthread *thr,
1476 duk_activation *act,
1477 duk_hstring *name) {
1478 DUK_ASSERT(act != NULL);
1479 return duk__delvar_helper(thr, act->lex_env, act, name);
1480 }
1481
1482 /*
1483 * DECLVAR
1484 *
1485 * See E5 Sections:
1486 * 10.4.3 Entering Function Code
1487 * 10.5 Declaration Binding Instantion
1488 * 12.2 Variable Statement
1489 * 11.1.2 Identifier Reference
1490 * 10.3.1 Identifier Resolution
1491 *
1492 * Variable declaration behavior is mainly discussed in Section 10.5,
1493 * and is not discussed in the execution semantics (Sections 11-13).
1494 *
1495 * Conceptually declarations happen when code (global, eval, function)
1496 * is entered, before any user code is executed. In practice, register-
1497 * bound identifiers are 'declared' automatically (by virtue of being
1498 * allocated to registers with the initial value 'undefined'). Other
1499 * identifiers are declared in the function prologue with this primitive.
1500 *
1501 * Since non-register bindings eventually back to an internal object's
1502 * properties, the 'prop_flags' argument is used to specify binding
1503 * type:
1504 *
1505 * - Immutable binding: set DUK_PROPDESC_FLAG_WRITABLE to false
1506 * - Non-deletable binding: set DUK_PROPDESC_FLAG_CONFIGURABLE to false
1507 * - The flag DUK_PROPDESC_FLAG_ENUMERABLE should be set, although it
1508 * doesn't really matter for internal objects
1509 *
1510 * All bindings are non-deletable mutable bindings except:
1511 *
1512 * - Declarations in eval code (mutable, deletable)
1513 * - 'arguments' binding in strict function code (immutable)
1514 * - Function name binding of a function expression (immutable)
1515 *
1516 * Declarations may go to declarative environment records (always
1517 * so for functions), but may also go to object environment records
1518 * (e.g. global code). The global object environment has special
1519 * behavior when re-declaring a function (but not a variable); see
1520 * E5.1 specification, Section 10.5, step 5.e.
1521 *
1522 * Declarations always go to the 'top-most' environment record, i.e.
1523 * we never check the record chain. It's not an error even if a
1524 * property (even an immutable or non-deletable one) of the same name
1525 * already exists.
1526 *
1527 * If a declared variable already exists, its value needs to be updated
1528 * (if possible). Returns 1 if a PUTVAR needs to be done by the caller;
1529 * otherwise returns 0.
1530 */
1531
1532 DUK_LOCAL
duk__declvar_helper(duk_hthread * thr,duk_hobject * env,duk_hstring * name,duk_tval * val,duk_small_uint_t prop_flags,duk_bool_t is_func_decl)1533 duk_bool_t duk__declvar_helper(duk_hthread *thr,
1534 duk_hobject *env,
1535 duk_hstring *name,
1536 duk_tval *val,
1537 duk_small_uint_t prop_flags,
1538 duk_bool_t is_func_decl) {
1539 duk_hobject *holder;
1540 duk_bool_t parents;
1541 duk__id_lookup_result ref;
1542 duk_tval *tv;
1543
1544 DUK_DDD(DUK_DDDPRINT("declvar: thr=%p, env=%p, name=%!O, val=%!T, prop_flags=0x%08lx, is_func_decl=%ld "
1545 "(env -> %!iO)",
1546 (void *) thr, (void *) env, (duk_heaphdr *) name,
1547 (duk_tval *) val, (unsigned long) prop_flags,
1548 (unsigned int) is_func_decl, (duk_heaphdr *) env));
1549
1550 DUK_ASSERT(thr != NULL);
1551 DUK_ASSERT(env != NULL);
1552 DUK_ASSERT(name != NULL);
1553 DUK_ASSERT(val != NULL);
1554
1555 /* Note: in strict mode the compiler should reject explicit
1556 * declaration of 'eval' or 'arguments'. However, internal
1557 * bytecode may declare 'arguments' in the function prologue.
1558 * We don't bother checking (or asserting) for these now.
1559 */
1560
1561 /* Note: val is a stable duk_tval pointer. The caller makes
1562 * a value copy into its stack frame, so 'tv_val' is not subject
1563 * to side effects here.
1564 */
1565
1566 /*
1567 * Check whether already declared.
1568 *
1569 * We need to check whether the binding exists in the environment
1570 * without walking its parents. However, we still need to check
1571 * register-bound identifiers and the prototype chain of an object
1572 * environment target object.
1573 */
1574
1575 parents = 0; /* just check 'env' */
1576 if (duk__get_identifier_reference(thr, env, name, NULL, parents, &ref)) {
1577 duk_int_t e_idx;
1578 duk_int_t h_idx;
1579 duk_small_uint_t flags;
1580
1581 /*
1582 * Variable already declared, ignore re-declaration.
1583 * The only exception is the updated behavior of E5.1 for
1584 * global function declarations, E5.1 Section 10.5, step 5.e.
1585 * This behavior does not apply to global variable declarations.
1586 */
1587
1588 if (!(is_func_decl && env == thr->builtins[DUK_BIDX_GLOBAL_ENV])) {
1589 DUK_DDD(DUK_DDDPRINT("re-declare a binding, ignoring"));
1590 return 1; /* 1 -> needs a PUTVAR */
1591 }
1592
1593 /*
1594 * Special behavior in E5.1.
1595 *
1596 * Note that even though parents == 0, the conflicting property
1597 * may be an inherited property (currently our global object's
1598 * prototype is Object.prototype). Step 5.e first operates on
1599 * the existing property (which is potentially in an ancestor)
1600 * and then defines a new property in the global object (and
1601 * never modifies the ancestor).
1602 *
1603 * Also note that this logic would become even more complicated
1604 * if the conflicting property might be a virtual one. Object
1605 * prototype has no virtual properties, though.
1606 *
1607 * XXX: this is now very awkward, rework.
1608 */
1609
1610 DUK_DDD(DUK_DDDPRINT("re-declare a function binding in global object, "
1611 "updated E5.1 processing"));
1612
1613 DUK_ASSERT(ref.holder != NULL);
1614 holder = ref.holder;
1615
1616 /* holder will be set to the target object, not the actual object
1617 * where the property was found (see duk__get_identifier_reference()).
1618 */
1619 DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(holder) == DUK_HOBJECT_CLASS_GLOBAL);
1620 DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(holder)); /* global object doesn't have array part */
1621
1622 /* XXX: use a helper for prototype traversal; no loop check here */
1623 /* must be found: was found earlier, and cannot be inherited */
1624 for (;;) {
1625 DUK_ASSERT(holder != NULL);
1626 if (duk_hobject_find_entry(thr->heap, holder, name, &e_idx, &h_idx)) {
1627 DUK_ASSERT(e_idx >= 0);
1628 break;
1629 }
1630 /* SCANBUILD: NULL pointer dereference, doesn't actually trigger,
1631 * asserted above.
1632 */
1633 holder = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, holder);
1634 }
1635 DUK_ASSERT(holder != NULL);
1636 DUK_ASSERT(e_idx >= 0);
1637 /* SCANBUILD: scan-build produces a NULL pointer dereference warning
1638 * below; it never actually triggers because holder is actually never
1639 * NULL.
1640 */
1641
1642 /* ref.holder is global object, holder is the object with the
1643 * conflicting property.
1644 */
1645
1646 flags = DUK_HOBJECT_E_GET_FLAGS(thr->heap, holder, e_idx);
1647 if (!(flags & DUK_PROPDESC_FLAG_CONFIGURABLE)) {
1648 if (flags & DUK_PROPDESC_FLAG_ACCESSOR) {
1649 DUK_DDD(DUK_DDDPRINT("existing property is a non-configurable "
1650 "accessor -> reject"));
1651 goto fail_existing_attributes;
1652 }
1653 if (!((flags & DUK_PROPDESC_FLAG_WRITABLE) &&
1654 (flags & DUK_PROPDESC_FLAG_ENUMERABLE))) {
1655 DUK_DDD(DUK_DDDPRINT("existing property is a non-configurable "
1656 "plain property which is not writable and "
1657 "enumerable -> reject"));
1658 goto fail_existing_attributes;
1659 }
1660
1661 DUK_DDD(DUK_DDDPRINT("existing property is not configurable but "
1662 "is plain, enumerable, and writable -> "
1663 "allow redeclaration"));
1664 }
1665
1666 if (holder == ref.holder) {
1667 /* XXX: if duk_hobject_define_property_internal() was updated
1668 * to handle a pre-existing accessor property, this would be
1669 * a simple call (like for the ancestor case).
1670 */
1671 DUK_DDD(DUK_DDDPRINT("redefine, offending property in global object itself"));
1672
1673 if (flags & DUK_PROPDESC_FLAG_ACCESSOR) {
1674 duk_hobject *tmp;
1675
1676 tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, holder, e_idx);
1677 DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, holder, e_idx, NULL);
1678 DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp);
1679 DUK_UNREF(tmp);
1680 tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, holder, e_idx);
1681 DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, holder, e_idx, NULL);
1682 DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp);
1683 DUK_UNREF(tmp);
1684 } else {
1685 tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, holder, e_idx);
1686 DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv);
1687 }
1688
1689 /* Here val would be potentially invalid if we didn't make
1690 * a value copy at the caller.
1691 */
1692
1693 tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, holder, e_idx);
1694 DUK_TVAL_SET_TVAL(tv, val);
1695 DUK_TVAL_INCREF(thr, tv);
1696 DUK_HOBJECT_E_SET_FLAGS(thr->heap, holder, e_idx, prop_flags);
1697
1698 DUK_DDD(DUK_DDDPRINT("updated global binding, final result: "
1699 "value -> %!T, prop_flags=0x%08lx",
1700 (duk_tval *) DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, holder, e_idx),
1701 (unsigned long) prop_flags));
1702 } else {
1703 DUK_DDD(DUK_DDDPRINT("redefine, offending property in ancestor"));
1704
1705 DUK_ASSERT(ref.holder == thr->builtins[DUK_BIDX_GLOBAL]);
1706 duk_push_tval(thr, val);
1707 duk_hobject_define_property_internal(thr, ref.holder, name, prop_flags);
1708 }
1709
1710 return 0;
1711 }
1712
1713 /*
1714 * Not found (in registers or record objects). Declare
1715 * to current variable environment.
1716 */
1717
1718 /*
1719 * Get holder object
1720 */
1721
1722 if (DUK_HOBJECT_IS_DECENV(env)) {
1723 DUK_HDECENV_ASSERT_VALID((duk_hdecenv *) env);
1724 holder = env;
1725 } else {
1726 DUK_HOBJENV_ASSERT_VALID((duk_hobjenv *) env);
1727 holder = ((duk_hobjenv *) env)->target;
1728 DUK_ASSERT(holder != NULL);
1729 }
1730
1731 /*
1732 * Define new property
1733 *
1734 * Note: this may fail if the holder is not extensible.
1735 */
1736
1737 /* XXX: this is awkward as we use an internal method which doesn't handle
1738 * extensibility etc correctly. Basically we'd want to do a [[DefineOwnProperty]]
1739 * or Object.defineProperty() here.
1740 */
1741
1742 if (!DUK_HOBJECT_HAS_EXTENSIBLE(holder)) {
1743 goto fail_not_extensible;
1744 }
1745
1746 duk_push_hobject(thr, holder);
1747 duk_push_hstring(thr, name);
1748 duk_push_tval(thr, val);
1749 duk_xdef_prop(thr, -3, prop_flags); /* [holder name val] -> [holder] */
1750 duk_pop_unsafe(thr);
1751
1752 return 0;
1753
1754 fail_existing_attributes:
1755 fail_not_extensible:
1756 DUK_ERROR_TYPE(thr, "declaration failed");
1757 DUK_WO_NORETURN(return 0;);
1758 }
1759
1760 DUK_INTERNAL
duk_js_declvar_activation(duk_hthread * thr,duk_activation * act,duk_hstring * name,duk_tval * val,duk_small_uint_t prop_flags,duk_bool_t is_func_decl)1761 duk_bool_t duk_js_declvar_activation(duk_hthread *thr,
1762 duk_activation *act,
1763 duk_hstring *name,
1764 duk_tval *val,
1765 duk_small_uint_t prop_flags,
1766 duk_bool_t is_func_decl) {
1767 duk_hobject *env;
1768 duk_tval tv_val_copy;
1769
1770 DUK_ASSERT(act != NULL);
1771
1772 /*
1773 * Make a value copy of the input val. This ensures that
1774 * side effects cannot invalidate the pointer.
1775 */
1776
1777 DUK_TVAL_SET_TVAL(&tv_val_copy, val);
1778 val = &tv_val_copy;
1779
1780 /*
1781 * Delayed env creation check
1782 */
1783
1784 if (!act->var_env) {
1785 DUK_ASSERT(act->lex_env == NULL);
1786 duk_js_init_activation_environment_records_delayed(thr, act);
1787 /* 'act' is a stable pointer, so still OK. */
1788 }
1789 DUK_ASSERT(act->lex_env != NULL);
1790 DUK_ASSERT(act->var_env != NULL);
1791
1792 env = act->var_env;
1793 DUK_ASSERT(env != NULL);
1794 DUK_ASSERT(DUK_HOBJECT_IS_ENV(env));
1795
1796 return duk__declvar_helper(thr, env, name, val, prop_flags, is_func_decl);
1797 }
1798