2 * Identifier access and function closure handling.
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.
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.
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.
20 * Care must be taken to avoid duk_tval pointer invalidation caused by
21 * e.g. value stack or object resizing.
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"
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.
33 #include "duk_internal.h"
36 * Local result type for duk__get_identifier_reference() lookup.
40 duk_hobject
*holder
; /* for object-bound identifiers */
41 duk_tval
*value
; /* for register-bound and declarative env identifiers */
42 duk_int_t attrs
; /* property attributes for identifier (relevant if value != NULL) */
43 duk_tval
*this_binding
;
45 } duk__id_lookup_result
;
48 * Create a new function object based on a "template function" which contains
49 * compiled bytecode, constants, etc, but lacks a lexical environment.
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.
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
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).
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.
73 DUK_LOCAL
void duk__inc_data_inner_refcounts(duk_hthread
*thr
, duk_hcompiledfunction
*f
) {
74 duk_tval
*tv
, *tv_end
;
75 duk_hobject
**funcs
, **funcs_end
;
77 /* If function creation fails due to out-of-memory, the data buffer
78 * pointer may be NULL in some cases. That's actually possible for
79 * GC code, but shouldn't be possible here because the incomplete
80 * function will be unwound from the value stack and never instantiated.
82 DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_DATA(thr
->heap
, f
) != NULL
);
85 tv
= DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr
->heap
, f
);
86 tv_end
= DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(thr
->heap
, f
);
88 DUK_TVAL_INCREF(thr
, tv
);
92 funcs
= DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(thr
->heap
, f
);
93 funcs_end
= DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(thr
->heap
, f
);
94 while (funcs
< funcs_end
) {
95 DUK_HEAPHDR_INCREF(thr
, (duk_heaphdr
*) *funcs
);
100 /* Push a new closure on the stack.
102 * Note: if fun_temp has NEWENV, i.e. a new lexical and variable declaration
103 * is created when the function is called, only outer_lex_env matters
104 * (outer_var_env is ignored and may or may not be same as outer_lex_env).
107 DUK_LOCAL
const duk_uint16_t duk__closure_copy_proplist
[] = {
108 /* order: most frequent to least frequent */
109 DUK_STRIDX_INT_VARMAP
,
110 DUK_STRIDX_INT_FORMALS
,
112 DUK_STRIDX_INT_PC2LINE
,
113 DUK_STRIDX_FILE_NAME
,
114 DUK_STRIDX_INT_SOURCE
118 void duk_js_push_closure(duk_hthread
*thr
,
119 duk_hcompiledfunction
*fun_temp
,
120 duk_hobject
*outer_var_env
,
121 duk_hobject
*outer_lex_env
,
122 duk_bool_t add_auto_proto
) {
123 duk_context
*ctx
= (duk_context
*) thr
;
124 duk_hcompiledfunction
*fun_clos
;
126 duk_uint_t len_value
;
128 DUK_ASSERT(fun_temp
!= NULL
);
129 DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_DATA(thr
->heap
, fun_temp
) != NULL
);
130 DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_FUNCS(thr
->heap
, fun_temp
) != NULL
);
131 DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_BYTECODE(thr
->heap
, fun_temp
) != NULL
);
132 DUK_ASSERT(outer_var_env
!= NULL
);
133 DUK_ASSERT(outer_lex_env
!= NULL
);
134 DUK_UNREF(len_value
);
136 duk_push_compiledfunction(ctx
);
137 duk_push_hobject(ctx
, &fun_temp
->obj
); /* -> [ ... closure template ] */
139 fun_clos
= (duk_hcompiledfunction
*) duk_get_hcompiledfunction(ctx
, -2);
140 DUK_ASSERT(fun_clos
!= NULL
);
141 DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject
*) fun_clos
));
142 DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_DATA(thr
->heap
, fun_clos
) == NULL
);
143 DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_FUNCS(thr
->heap
, fun_clos
) == NULL
);
144 DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_BYTECODE(thr
->heap
, fun_clos
) == NULL
);
146 DUK_HCOMPILEDFUNCTION_SET_DATA(thr
->heap
, fun_clos
, DUK_HCOMPILEDFUNCTION_GET_DATA(thr
->heap
, fun_temp
));
147 DUK_HCOMPILEDFUNCTION_SET_FUNCS(thr
->heap
, fun_clos
, DUK_HCOMPILEDFUNCTION_GET_FUNCS(thr
->heap
, fun_temp
));
148 DUK_HCOMPILEDFUNCTION_SET_BYTECODE(thr
->heap
, fun_clos
, DUK_HCOMPILEDFUNCTION_GET_BYTECODE(thr
->heap
, fun_temp
));
150 /* Note: all references inside 'data' need to get their refcounts
151 * upped too. This is the case because refcounts are decreased
152 * through every function referencing 'data' independently.
155 DUK_HBUFFER_INCREF(thr
, DUK_HCOMPILEDFUNCTION_GET_DATA(thr
->heap
, fun_clos
));
156 duk__inc_data_inner_refcounts(thr
, fun_temp
);
158 fun_clos
->nregs
= fun_temp
->nregs
;
159 fun_clos
->nargs
= fun_temp
->nargs
;
160 #if defined(DUK_USE_DEBUGGER_SUPPORT)
161 fun_clos
->start_line
= fun_temp
->start_line
;
162 fun_clos
->end_line
= fun_temp
->end_line
;
165 DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_DATA(thr
->heap
, fun_clos
) != NULL
);
166 DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_FUNCS(thr
->heap
, fun_clos
) != NULL
);
167 DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_BYTECODE(thr
->heap
, fun_clos
) != NULL
);
169 /* XXX: could also copy from template, but there's no way to have any
170 * other value here now (used code has no access to the template).
172 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr
, &fun_clos
->obj
, thr
->builtins
[DUK_BIDX_FUNCTION_PROTOTYPE
]);
175 * Init/assert flags, copying them where appropriate. Some flags
176 * (like NEWENV) are processed separately below.
179 /* XXX: copy flags using a mask */
181 DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(&fun_clos
->obj
));
182 DUK_HOBJECT_SET_CONSTRUCTABLE(&fun_clos
->obj
); /* Note: not set in template (has no "prototype") */
183 DUK_ASSERT(DUK_HOBJECT_HAS_CONSTRUCTABLE(&fun_clos
->obj
));
184 DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(&fun_clos
->obj
));
185 DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(&fun_clos
->obj
));
186 DUK_ASSERT(!DUK_HOBJECT_HAS_NATIVEFUNCTION(&fun_clos
->obj
));
187 DUK_ASSERT(!DUK_HOBJECT_HAS_THREAD(&fun_clos
->obj
));
188 /* DUK_HOBJECT_FLAG_ARRAY_PART: don't care */
189 if (DUK_HOBJECT_HAS_STRICT(&fun_temp
->obj
)) {
190 DUK_HOBJECT_SET_STRICT(&fun_clos
->obj
);
192 if (DUK_HOBJECT_HAS_NOTAIL(&fun_temp
->obj
)) {
193 DUK_HOBJECT_SET_NOTAIL(&fun_clos
->obj
);
195 /* DUK_HOBJECT_FLAG_NEWENV: handled below */
196 if (DUK_HOBJECT_HAS_NAMEBINDING(&fun_temp
->obj
)) {
197 /* Although NAMEBINDING is not directly needed for using
198 * function instances, it's needed by bytecode dump/load
201 DUK_HOBJECT_SET_NAMEBINDING(&fun_clos
->obj
);
203 if (DUK_HOBJECT_HAS_CREATEARGS(&fun_temp
->obj
)) {
204 DUK_HOBJECT_SET_CREATEARGS(&fun_clos
->obj
);
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
));
211 * Setup environment record properties based on the template and
214 * If DUK_HOBJECT_HAS_NEWENV(fun_temp) is true, the environment
215 * records represent identifiers "outside" the function; the
216 * "inner" environment records are created on demand. Otherwise,
217 * the environment records are those that will be directly used
218 * (e.g. for declarations).
220 * _Lexenv is always set; _Varenv defaults to _Lexenv if missing,
221 * so _Varenv is only set if _Lexenv != _Varenv.
223 * This is relatively complex, see doc/identifier-handling.rst.
226 if (DUK_HOBJECT_HAS_NEWENV(&fun_temp
->obj
)) {
227 DUK_HOBJECT_SET_NEWENV(&fun_clos
->obj
);
229 if (DUK_HOBJECT_HAS_NAMEBINDING(&fun_temp
->obj
)) {
233 * Named function expression, name needs to be bound
234 * in an intermediate environment record. The "outer"
235 * lexical/variable environment will thus be:
237 * a) { funcname: <func>, __prototype: outer_lex_env }
238 * b) { funcname: <func>, __prototype: <globalenv> } (if outer_lex_env missing)
241 DUK_ASSERT(duk_has_prop_stridx(ctx
, -1, DUK_STRIDX_NAME
)); /* required if NAMEBINDING set */
244 proto
= outer_lex_env
;
246 proto
= thr
->builtins
[DUK_BIDX_GLOBAL_ENV
];
249 /* -> [ ... closure template env ] */
250 (void) duk_push_object_helper_proto(ctx
,
251 DUK_HOBJECT_FLAG_EXTENSIBLE
|
252 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV
),
255 /* It's important that duk_xdef_prop() is a 'raw define' so that any
256 * properties in an ancestor are never an issue (they should never be
257 * e.g. non-writable, but just in case).
259 duk_get_prop_stridx(ctx
, -2, DUK_STRIDX_NAME
); /* -> [ ... closure template env funcname ] */
260 duk_dup(ctx
, -4); /* -> [ ... closure template env funcname closure ] */
261 duk_xdef_prop(ctx
, -3, DUK_PROPDESC_FLAGS_NONE
); /* -> [ ... closure template env ] */
262 /* env[funcname] = closure */
264 /* [ ... closure template env ] */
266 duk_xdef_prop_stridx(ctx
, -3, DUK_STRIDX_INT_LEXENV
, DUK_PROPDESC_FLAGS_WC
);
267 /* since closure has NEWENV, never define DUK_STRIDX_INT_VARENV, as it
268 * will be ignored anyway
271 /* [ ... closure template ] */
274 * Other cases (function declaration, anonymous function expression,
275 * strict direct eval code). The "outer" environment will be whatever
276 * the caller gave us.
279 duk_push_hobject(ctx
, outer_lex_env
); /* -> [ ... closure template env ] */
280 duk_xdef_prop_stridx(ctx
, -3, DUK_STRIDX_INT_LEXENV
, DUK_PROPDESC_FLAGS_WC
);
281 /* since closure has NEWENV, never define DUK_STRIDX_INT_VARENV, as it
282 * will be ignored anyway
285 /* [ ... closure template ] */
289 * Function gets no new environment when called. This is the
290 * case for global code, indirect eval code, and non-strict
291 * direct eval code. There is no direct correspondence to the
292 * E5 specification, as global/eval code is not exposed as a
296 DUK_ASSERT(!DUK_HOBJECT_HAS_NAMEBINDING(&fun_temp
->obj
));
298 duk_push_hobject(ctx
, outer_lex_env
); /* -> [ ... closure template env ] */
299 duk_xdef_prop_stridx(ctx
, -3, DUK_STRIDX_INT_LEXENV
, DUK_PROPDESC_FLAGS_WC
);
301 if (outer_var_env
!= outer_lex_env
) {
302 duk_push_hobject(ctx
, outer_var_env
); /* -> [ ... closure template env ] */
303 duk_xdef_prop_stridx(ctx
, -3, DUK_STRIDX_INT_VARENV
, DUK_PROPDESC_FLAGS_WC
);
306 #ifdef DUK_USE_DDDPRINT
307 duk_get_prop_stridx(ctx
, -2, DUK_STRIDX_INT_VARENV
);
308 duk_get_prop_stridx(ctx
, -3, DUK_STRIDX_INT_LEXENV
);
309 DUK_DDD(DUK_DDDPRINT("closure varenv -> %!ipT, lexenv -> %!ipT",
310 (duk_tval
*) duk_get_tval(ctx
, -2),
311 (duk_tval
*) duk_get_tval(ctx
, -1)));
316 * Copy some internal properties directly
318 * The properties will be writable and configurable, but not enumerable.
321 /* [ ... closure template ] */
323 DUK_DDD(DUK_DDDPRINT("copying properties: closure=%!iT, template=%!iT",
324 (duk_tval
*) duk_get_tval(ctx
, -2),
325 (duk_tval
*) duk_get_tval(ctx
, -1)));
327 for (i
= 0; i
< (duk_small_uint_t
) (sizeof(duk__closure_copy_proplist
) / sizeof(duk_uint16_t
)); i
++) {
328 duk_small_int_t stridx
= (duk_small_int_t
) duk__closure_copy_proplist
[i
];
329 if (duk_get_prop_stridx(ctx
, -1, stridx
)) {
330 /* [ ... closure template val ] */
331 DUK_DDD(DUK_DDDPRINT("copying property, stridx=%ld -> found", (long) stridx
));
332 duk_xdef_prop_stridx(ctx
, -3, stridx
, DUK_PROPDESC_FLAGS_WC
);
334 DUK_DDD(DUK_DDDPRINT("copying property, stridx=%ld -> not found", (long) stridx
));
340 * "length" maps to number of formals (E5 Section 13.2) for function
341 * declarations/expressions (non-bound functions). Note that 'nargs'
342 * is NOT necessarily equal to the number of arguments.
345 /* [ ... closure template ] */
349 /* XXX: use helper for size optimization */
350 if (duk_get_prop_stridx(ctx
, -2, DUK_STRIDX_INT_FORMALS
)) {
351 /* [ ... closure template formals ] */
352 DUK_ASSERT(duk_has_prop_stridx(ctx
, -1, DUK_STRIDX_LENGTH
));
353 DUK_ASSERT(duk_get_length(ctx
, -1) <= DUK_UINT_MAX
); /* formal arg limits */
354 len_value
= (duk_uint_t
) duk_get_length(ctx
, -1);
356 /* XXX: what to do if _Formals is not empty but compiler has
357 * optimized it away -- read length from an explicit property
363 duk_push_uint(ctx
, len_value
); /* [ ... closure template len_value ] */
364 duk_xdef_prop_stridx(ctx
, -3, DUK_STRIDX_LENGTH
, DUK_PROPDESC_FLAGS_NONE
);
367 * "prototype" is, by default, a fresh object with the "constructor"
370 * Note that this creates a circular reference for every function
371 * instance (closure) which prevents refcount-based collection of
372 * function instances.
374 * XXX: Try to avoid creating the default prototype object, because
375 * many functions are not used as constructors and the default
376 * prototype is unnecessary. Perhaps it could be created on-demand
377 * when it is first accessed?
380 /* [ ... closure template ] */
382 if (add_auto_proto
) {
383 duk_push_object(ctx
); /* -> [ ... closure template newobj ] */
384 duk_dup(ctx
, -3); /* -> [ ... closure template newobj closure ] */
385 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_CONSTRUCTOR
, DUK_PROPDESC_FLAGS_WC
); /* -> [ ... closure template newobj ] */
386 duk_compact(ctx
, -1); /* compact the prototype */
387 duk_xdef_prop_stridx(ctx
, -3, DUK_STRIDX_PROTOTYPE
, DUK_PROPDESC_FLAGS_W
); /* -> [ ... closure template ] */
391 * "arguments" and "caller" must be mapped to throwers for strict
392 * mode and bound functions (E5 Section 15.3.5).
394 * XXX: This is expensive to have for every strict function instance.
395 * Try to implement as virtual properties or on-demand created properties.
398 /* [ ... closure template ] */
400 if (DUK_HOBJECT_HAS_STRICT(&fun_clos
->obj
)) {
401 duk_xdef_prop_stridx_thrower(ctx
, -2, DUK_STRIDX_CALLER
, DUK_PROPDESC_FLAGS_NONE
);
402 duk_xdef_prop_stridx_thrower(ctx
, -2, DUK_STRIDX_LC_ARGUMENTS
, DUK_PROPDESC_FLAGS_NONE
);
404 #ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY
405 DUK_DDD(DUK_DDDPRINT("function is non-strict and non-standard 'caller' property in use, add initial 'null' value"));
407 duk_xdef_prop_stridx(ctx
, -3, DUK_STRIDX_CALLER
, DUK_PROPDESC_FLAGS_NONE
);
409 DUK_DDD(DUK_DDDPRINT("function is non-strict and non-standard 'caller' property not used"));
414 * "name" is a non-standard property found in at least V8, Rhino, smjs.
415 * For Rhino and smjs it is non-writable, non-enumerable, and non-configurable;
416 * for V8 it is writable, non-enumerable, non-configurable. It is also defined
417 * for an anonymous function expression in which case the value is an empty string.
418 * We could also leave name 'undefined' for anonymous functions but that would
419 * differ from behavior of other engines, so use an empty string.
421 * XXX: make optional? costs something per function.
424 /* [ ... closure template ] */
426 if (duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_NAME
)) {
427 /* [ ... closure template name ] */
428 DUK_ASSERT(duk_is_string(ctx
, -1));
430 /* [ ... closure template undefined ] */
432 duk_push_hstring_stridx(ctx
, DUK_STRIDX_EMPTY_STRING
);
434 duk_xdef_prop_stridx(ctx
, -3, DUK_STRIDX_NAME
, DUK_PROPDESC_FLAGS_NONE
); /* -> [ ... closure template ] */
437 * Compact the closure, in most cases no properties will be added later.
438 * Also, without this the closures end up having unused property slots
439 * (e.g. in Duktape 0.9.0, 8 slots would be allocated and only 7 used).
440 * A better future solution would be to allocate the closure directly
441 * to correct size (and setup the properties directly without going
445 duk_compact(ctx
, -2);
448 * Some assertions (E5 Section 13.2).
451 DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(&fun_clos
->obj
) == DUK_HOBJECT_CLASS_FUNCTION
);
452 DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr
->heap
, &fun_clos
->obj
) == thr
->builtins
[DUK_BIDX_FUNCTION_PROTOTYPE
]);
453 DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE(&fun_clos
->obj
));
454 DUK_ASSERT(duk_has_prop_stridx(ctx
, -2, DUK_STRIDX_LENGTH
) != 0);
455 DUK_ASSERT(add_auto_proto
== 0 || duk_has_prop_stridx(ctx
, -2, DUK_STRIDX_PROTOTYPE
) != 0);
456 DUK_ASSERT(duk_has_prop_stridx(ctx
, -2, DUK_STRIDX_NAME
) != 0); /* non-standard */
457 DUK_ASSERT(!DUK_HOBJECT_HAS_STRICT(&fun_clos
->obj
) ||
458 duk_has_prop_stridx(ctx
, -2, DUK_STRIDX_CALLER
) != 0);
459 DUK_ASSERT(!DUK_HOBJECT_HAS_STRICT(&fun_clos
->obj
) ||
460 duk_has_prop_stridx(ctx
, -2, DUK_STRIDX_LC_ARGUMENTS
) != 0);
466 /* [ ... closure template ] */
468 DUK_DDD(DUK_DDDPRINT("created function instance: template=%!iT -> closure=%!iT",
469 (duk_tval
*) duk_get_tval(ctx
, -1),
470 (duk_tval
*) duk_get_tval(ctx
, -2)));
474 /* [ ... closure ] */
478 * Delayed activation environment record initialization (for functions
481 * The non-delayed initialization is handled by duk_handle_call().
486 duk_hobject
*duk_create_activation_environment_record(duk_hthread
*thr
,
488 duk_size_t idx_bottom
) {
489 duk_context
*ctx
= (duk_context
*) thr
;
494 DUK_ASSERT(thr
!= NULL
);
495 DUK_ASSERT(func
!= NULL
);
497 tv
= duk_hobject_find_existing_entry_tval_ptr(thr
->heap
, func
, DUK_HTHREAD_STRING_INT_LEXENV(thr
));
499 DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv
));
500 DUK_ASSERT(DUK_HOBJECT_IS_ENV(DUK_TVAL_GET_OBJECT(tv
)));
501 parent
= DUK_TVAL_GET_OBJECT(tv
);
503 parent
= thr
->builtins
[DUK_BIDX_GLOBAL_ENV
];
506 (void) duk_push_object_helper(ctx
,
507 DUK_HOBJECT_FLAG_EXTENSIBLE
|
508 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV
),
509 -1); /* no prototype, updated below */
510 env
= duk_require_hobject(ctx
, -1);
511 DUK_ASSERT(env
!= NULL
);
512 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr
, env
, parent
); /* parent env is the prototype */
514 /* open scope information, for compiled functions only */
516 if (DUK_HOBJECT_IS_COMPILEDFUNCTION(func
)) {
517 duk_push_hthread(ctx
, thr
);
518 duk_xdef_prop_stridx_wec(ctx
, -2, DUK_STRIDX_INT_THREAD
);
519 duk_push_hobject(ctx
, func
);
520 duk_xdef_prop_stridx_wec(ctx
, -2, DUK_STRIDX_INT_CALLEE
);
521 duk_push_size_t(ctx
, idx_bottom
);
522 duk_xdef_prop_stridx_wec(ctx
, -2, DUK_STRIDX_INT_REGBASE
);
529 void duk_js_init_activation_environment_records_delayed(duk_hthread
*thr
,
530 duk_activation
*act
) {
531 duk_context
*ctx
= (duk_context
*) thr
;
535 func
= DUK_ACT_GET_FUNC(act
);
536 DUK_ASSERT(func
!= NULL
);
537 DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func
)); /* bound functions are never in act 'func' */
540 * Delayed initialization only occurs for 'NEWENV' functions.
543 DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func
));
544 DUK_ASSERT(act
->lex_env
== NULL
);
545 DUK_ASSERT(act
->var_env
== NULL
);
547 env
= duk_create_activation_environment_record(thr
, func
, act
->idx_bottom
);
548 DUK_ASSERT(env
!= NULL
);
550 DUK_DDD(DUK_DDDPRINT("created delayed fresh env: %!ipO", (duk_heaphdr
*) env
));
551 #ifdef DUK_USE_DDDPRINT
553 duk_hobject
*p
= env
;
555 DUK_DDD(DUK_DDDPRINT(" -> %!ipO", (duk_heaphdr
*) p
));
556 p
= DUK_HOBJECT_GET_PROTOTYPE(thr
->heap
, p
);
563 DUK_HOBJECT_INCREF(thr
, env
); /* XXX: incref by count (here 2 times) */
564 DUK_HOBJECT_INCREF(thr
, env
);
570 * Closing environment records.
572 * The environment record MUST be closed with the thread where its activation
573 * is. In other words (if 'env' is open):
575 * - 'thr' must match _env.thread
576 * - 'func' must match _env.callee
577 * - 'regbase' must match _env.regbase
579 * These are not looked up from the env to minimize code size.
581 * XXX: should access the own properties directly instead of using the API
584 DUK_INTERNAL
void duk_js_close_environment_record(duk_hthread
*thr
, duk_hobject
*env
, duk_hobject
*func
, duk_size_t regbase
) {
585 duk_context
*ctx
= (duk_context
*) thr
;
588 DUK_ASSERT(thr
!= NULL
);
589 DUK_ASSERT(env
!= NULL
);
590 /* func is NULL for lightfuncs */
592 if (!DUK_HOBJECT_IS_DECENV(env
) || DUK_HOBJECT_HAS_ENVRECCLOSED(env
)) {
593 DUK_DDD(DUK_DDDPRINT("environment record not a declarative record, "
594 "or already closed: %!iO",
595 (duk_heaphdr
*) env
));
599 DUK_DDD(DUK_DDDPRINT("closing environment record: %!iO, func: %!iO, regbase: %ld",
600 (duk_heaphdr
*) env
, (duk_heaphdr
*) func
, (long) regbase
));
602 duk_push_hobject(ctx
, env
);
604 /* assertions: env must be closed in the same thread as where it runs */
605 #ifdef DUK_USE_ASSERTIONS
609 if (duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_INT_CALLEE
)) {
610 DUK_ASSERT(duk_is_object(ctx
, -1));
611 DUK_ASSERT(duk_get_hobject(ctx
, -1) == (duk_hobject
*) func
);
615 if (duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_INT_THREAD
)) {
616 DUK_ASSERT(duk_is_object(ctx
, -1));
617 DUK_ASSERT(duk_get_hobject(ctx
, -1) == (duk_hobject
*) thr
);
621 if (duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_INT_REGBASE
)) {
622 DUK_ASSERT(duk_is_number(ctx
, -1));
623 DUK_ASSERT(duk_get_number(ctx
, -1) == (double) regbase
);
631 if (func
!= NULL
&& DUK_HOBJECT_IS_COMPILEDFUNCTION(func
)) {
637 /* XXX: additional conditions when to close variables? we don't want to do it
638 * unless the environment may have "escaped" (referenced in a function closure).
639 * With delayed environments, the existence is probably good enough of a check.
642 /* XXX: any way to detect faster whether something needs to be closed?
643 * We now look up _Callee and then skip the rest.
646 /* Note: we rely on the _Varmap having a bunch of nice properties, like:
647 * - being compacted and unmodified during this process
648 * - not containing an array part
649 * - having correct value types
654 if (!duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_INT_CALLEE
)) {
655 DUK_DDD(DUK_DDDPRINT("env has no callee property, nothing to close; re-delete the control properties just in case"));
660 /* [... env callee] */
662 if (!duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_INT_VARMAP
)) {
663 DUK_DDD(DUK_DDDPRINT("callee has no varmap property, nothing to close; delete the control properties"));
667 varmap
= duk_require_hobject(ctx
, -1);
668 DUK_ASSERT(varmap
!= NULL
);
670 DUK_DDD(DUK_DDDPRINT("varmap: %!O", (duk_heaphdr
*) varmap
));
672 /* [... env callee varmap] */
674 DUK_DDD(DUK_DDDPRINT("copying bound register values, %ld bound regs", (long) DUK_HOBJECT_GET_ENEXT(varmap
)));
676 for (i
= 0; i
< (duk_uint_fast32_t
) DUK_HOBJECT_GET_ENEXT(varmap
); i
++) {
677 key
= DUK_HOBJECT_E_GET_KEY(thr
->heap
, varmap
, i
);
678 DUK_ASSERT(key
!= NULL
); /* assume keys are compacted */
680 DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr
->heap
, varmap
, i
)); /* assume plain values */
682 tv
= DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr
->heap
, varmap
, i
);
683 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv
)); /* assume value is a number */
684 regnum
= (duk_uint_t
) DUK_TVAL_GET_NUMBER(tv
);
685 DUK_ASSERT_DISABLE(regnum
>= 0); /* unsigned */
686 DUK_ASSERT(regnum
< ((duk_hcompiledfunction
*) func
)->nregs
); /* regnum is sane */
687 DUK_ASSERT(thr
->valstack
+ regbase
+ regnum
>= thr
->valstack
);
688 DUK_ASSERT(thr
->valstack
+ regbase
+ regnum
< thr
->valstack_top
);
690 /* XXX: slightly awkward */
691 duk_push_hstring(ctx
, key
);
692 duk_push_tval(ctx
, thr
->valstack
+ regbase
+ regnum
);
693 DUK_DDD(DUK_DDDPRINT("closing identifier '%s' -> reg %ld, value %!T",
694 (const char *) duk_require_string(ctx
, -2),
696 (duk_tval
*) duk_get_tval(ctx
, -1)));
698 /* [... env callee varmap key val] */
700 /* if property already exists, overwrites silently */
701 duk_xdef_prop(ctx
, -5, DUK_PROPDESC_FLAGS_WE
); /* writable but not deletable */
713 duk_del_prop_stridx(ctx
, -1, DUK_STRIDX_INT_CALLEE
);
714 duk_del_prop_stridx(ctx
, -1, DUK_STRIDX_INT_THREAD
);
715 duk_del_prop_stridx(ctx
, -1, DUK_STRIDX_INT_REGBASE
);
719 DUK_HOBJECT_SET_ENVRECCLOSED(env
);
721 DUK_DDD(DUK_DDDPRINT("environment record after being closed: %!O",
722 (duk_heaphdr
*) env
));
726 * GETIDREF: a GetIdentifierReference-like helper.
728 * Provides a parent traversing lookup and a single level lookup
731 * Instead of returning the value, returns a bunch of values allowing
732 * the caller to read, write, or delete the binding. Value pointers
733 * are duk_tval pointers which can be mutated directly as long as
734 * refcounts are properly updated. Note that any operation which may
735 * reallocate valstacks or compact objects may invalidate the returned
736 * duk_tval (but not object) pointers, so caller must be very careful.
738 * If starting environment record 'env' is given, 'act' is ignored.
739 * However, if 'env' is NULL, the caller may identify, in 'act', an
740 * activation which hasn't had its declarative environment initialized
741 * yet. The activation registers are then looked up, and its parent
742 * traversed normally.
744 * The 'out' structure values are only valid if the function returns
745 * success (non-zero).
748 /* lookup name from an open declarative record's registers */
750 duk_bool_t
duk__getid_open_decl_env_regs(duk_hthread
*thr
,
753 duk__id_lookup_result
*out
) {
754 duk_hthread
*env_thr
;
755 duk_hobject
*env_func
;
756 duk_size_t env_regbase
;
762 DUK_ASSERT(thr
!= NULL
);
763 DUK_ASSERT(name
!= NULL
);
764 DUK_ASSERT(env
!= NULL
);
765 DUK_ASSERT(out
!= NULL
);
767 DUK_ASSERT(DUK_HOBJECT_IS_DECENV(env
));
769 tv
= duk_hobject_find_existing_entry_tval_ptr(thr
->heap
, env
, DUK_HTHREAD_STRING_INT_CALLEE(thr
));
771 /* env is closed, should be missing _Callee, _Thread, _Regbase */
772 DUK_ASSERT(duk_hobject_find_existing_entry_tval_ptr(thr
->heap
, env
, DUK_HTHREAD_STRING_INT_CALLEE(thr
)) == NULL
);
773 DUK_ASSERT(duk_hobject_find_existing_entry_tval_ptr(thr
->heap
, env
, DUK_HTHREAD_STRING_INT_THREAD(thr
)) == NULL
);
774 DUK_ASSERT(duk_hobject_find_existing_entry_tval_ptr(thr
->heap
, env
, DUK_HTHREAD_STRING_INT_REGBASE(thr
)) == NULL
);
778 DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv
));
779 DUK_ASSERT(DUK_TVAL_GET_OBJECT(tv
) != NULL
);
780 DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(DUK_TVAL_GET_OBJECT(tv
)));
781 env_func
= DUK_TVAL_GET_OBJECT(tv
);
782 DUK_ASSERT(env_func
!= NULL
);
784 tv
= duk_hobject_find_existing_entry_tval_ptr(thr
->heap
, env_func
, DUK_HTHREAD_STRING_INT_VARMAP(thr
));
788 DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv
));
789 varmap
= DUK_TVAL_GET_OBJECT(tv
);
790 DUK_ASSERT(varmap
!= NULL
);
792 tv
= duk_hobject_find_existing_entry_tval_ptr(thr
->heap
, varmap
, name
);
796 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv
));
797 reg_rel
= (duk_size_t
) DUK_TVAL_GET_NUMBER(tv
);
798 DUK_ASSERT_DISABLE(reg_rel
>= 0); /* unsigned */
799 DUK_ASSERT(reg_rel
< ((duk_hcompiledfunction
*) env_func
)->nregs
);
801 tv
= duk_hobject_find_existing_entry_tval_ptr(thr
->heap
, env
, DUK_HTHREAD_STRING_INT_THREAD(thr
));
802 DUK_ASSERT(tv
!= NULL
);
803 DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv
));
804 DUK_ASSERT(DUK_TVAL_GET_OBJECT(tv
) != NULL
);
805 DUK_ASSERT(DUK_HOBJECT_IS_THREAD(DUK_TVAL_GET_OBJECT(tv
)));
806 env_thr
= (duk_hthread
*) DUK_TVAL_GET_OBJECT(tv
);
807 DUK_ASSERT(env_thr
!= NULL
);
809 /* Note: env_thr != thr is quite possible and normal, so careful
810 * with what thread is used for valstack lookup.
813 tv
= duk_hobject_find_existing_entry_tval_ptr(thr
->heap
, env
, DUK_HTHREAD_STRING_INT_REGBASE(thr
));
814 DUK_ASSERT(tv
!= NULL
);
815 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv
));
816 env_regbase
= (duk_size_t
) DUK_TVAL_GET_NUMBER(tv
);
818 idx
= env_regbase
+ reg_rel
;
819 tv
= env_thr
->valstack
+ idx
;
820 DUK_ASSERT(tv
>= env_thr
->valstack
&& tv
< env_thr
->valstack_end
); /* XXX: more accurate? */
823 out
->attrs
= DUK_PROPDESC_FLAGS_W
; /* registers are mutable, non-deletable */
824 out
->this_binding
= NULL
; /* implicit this value always undefined for
825 * declarative environment records.
833 /* lookup name from current activation record's functions' registers */
835 duk_bool_t
duk__getid_activation_regs(duk_hthread
*thr
,
838 duk__id_lookup_result
*out
) {
845 DUK_ASSERT(thr
!= NULL
);
846 DUK_ASSERT(name
!= NULL
);
847 DUK_ASSERT(act
!= NULL
);
848 DUK_ASSERT(out
!= NULL
);
850 func
= DUK_ACT_GET_FUNC(act
);
851 DUK_ASSERT(func
!= NULL
);
852 DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func
));
854 if (!DUK_HOBJECT_IS_COMPILEDFUNCTION(func
)) {
858 tv
= duk_hobject_find_existing_entry_tval_ptr(thr
->heap
, func
, DUK_HTHREAD_STRING_INT_VARMAP(thr
));
862 DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv
));
863 varmap
= DUK_TVAL_GET_OBJECT(tv
);
864 DUK_ASSERT(varmap
!= NULL
);
866 tv
= duk_hobject_find_existing_entry_tval_ptr(thr
->heap
, varmap
, name
);
870 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv
));
871 reg_rel
= (duk_size_t
) DUK_TVAL_GET_NUMBER(tv
);
872 DUK_ASSERT_DISABLE(reg_rel
>= 0);
873 DUK_ASSERT(reg_rel
< ((duk_hcompiledfunction
*) func
)->nregs
);
875 idx
= act
->idx_bottom
+ reg_rel
;
876 DUK_ASSERT(idx
>= act
->idx_bottom
);
877 tv
= thr
->valstack
+ idx
;
880 out
->attrs
= DUK_PROPDESC_FLAGS_W
; /* registers are mutable, non-deletable */
881 out
->this_binding
= NULL
; /* implicit this value always undefined for
882 * declarative environment records.
891 duk_bool_t
duk__get_identifier_reference(duk_hthread
*thr
,
896 duk__id_lookup_result
*out
) {
902 DUK_ASSERT(thr
!= NULL
);
903 DUK_ASSERT(env
!= NULL
|| act
!= NULL
);
904 DUK_ASSERT(name
!= NULL
);
905 DUK_ASSERT(out
!= NULL
);
907 DUK_ASSERT(!env
|| DUK_HOBJECT_IS_ENV(env
));
908 DUK_ASSERT(!env
|| !DUK_HOBJECT_HAS_ARRAY_PART(env
));
911 * Conceptually, we look for the identifier binding by starting from
912 * 'env' and following to chain of environment records (represented
913 * by the prototype chain).
915 * If 'env' is NULL, the current activation does not yet have an
916 * allocated declarative environment record; this should be treated
917 * exactly as if the environment record existed but had no bindings
918 * other than register bindings.
920 * Note: we assume that with the DUK_HOBJECT_FLAG_NEWENV cleared
921 * the environment will always be initialized immediately; hence
922 * a NULL 'env' should only happen with the flag set. This is the
923 * case for: (1) function calls, and (2) strict, direct eval calls.
926 if (env
== NULL
&& act
!= NULL
) {
929 DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference: env is NULL, activation is non-NULL -> "
930 "delayed env case, look up activation regs first"));
936 if (duk__getid_activation_regs(thr
, name
, act
, out
)) {
937 DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
938 "name=%!O -> value=%!T, attrs=%ld, this=%!T, env=%!O, holder=%!O "
939 "(found from register bindings when env=NULL)",
940 (duk_heaphdr
*) name
, (duk_tval
*) out
->value
,
941 (long) out
->attrs
, (duk_tval
*) out
->this_binding
,
942 (duk_heaphdr
*) out
->env
, (duk_heaphdr
*) out
->holder
));
946 DUK_DDD(DUK_DDDPRINT("not found in current activation regs"));
949 * Not found in registers, proceed to the parent record.
950 * Here we need to determine what the parent would be,
951 * if 'env' was not NULL (i.e. same logic as when initializing
954 * Note that environment initialization is only deferred when
955 * DUK_HOBJECT_HAS_NEWENV is set, and this only happens for:
959 * We only need to check _Lexenv here; _Varenv exists only if it
960 * differs from _Lexenv (and thus _Lexenv will also be present).
964 DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference failed, no parent traversal "
965 "(not found from register bindings when env=NULL)"));
969 func
= DUK_ACT_GET_FUNC(act
);
970 DUK_ASSERT(func
!= NULL
);
971 DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func
));
973 tv
= duk_hobject_find_existing_entry_tval_ptr(thr
->heap
, func
, DUK_HTHREAD_STRING_INT_LEXENV(thr
));
975 DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv
));
976 env
= DUK_TVAL_GET_OBJECT(tv
);
978 DUK_ASSERT(duk_hobject_find_existing_entry_tval_ptr(thr
->heap
, func
, DUK_HTHREAD_STRING_INT_VARENV(thr
)) == NULL
);
979 env
= thr
->builtins
[DUK_BIDX_GLOBAL_ENV
];
982 DUK_DDD(DUK_DDDPRINT("continue lookup from env: %!iO",
983 (duk_heaphdr
*) env
));
987 * Prototype walking starting from 'env'.
989 * ('act' is not needed anywhere here.)
992 sanity
= DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY
;
993 while (env
!= NULL
) {
997 DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference, name=%!O, considering env=%p -> %!iO",
998 (duk_heaphdr
*) name
,
1000 (duk_heaphdr
*) env
));
1002 DUK_ASSERT(env
!= NULL
);
1003 DUK_ASSERT(DUK_HOBJECT_IS_ENV(env
));
1004 DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(env
));
1006 cl
= DUK_HOBJECT_GET_CLASS_NUMBER(env
);
1007 DUK_ASSERT(cl
== DUK_HOBJECT_CLASS_OBJENV
|| cl
== DUK_HOBJECT_CLASS_DECENV
);
1008 if (cl
== DUK_HOBJECT_CLASS_DECENV
) {
1010 * Declarative environment record.
1012 * Identifiers can never be stored in ancestors and are
1013 * always plain values, so we can use an internal helper
1014 * and access the value directly with an duk_tval ptr.
1016 * A closed environment is only indicated by it missing
1017 * the "book-keeping" properties required for accessing
1018 * register-bound variables.
1021 if (DUK_HOBJECT_HAS_ENVRECCLOSED(env
)) {
1022 /* already closed */
1026 if (duk__getid_open_decl_env_regs(thr
, name
, env
, out
)) {
1027 DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
1028 "name=%!O -> value=%!T, attrs=%ld, this=%!T, env=%!O, holder=%!O "
1029 "(declarative environment record, scope open, found in regs)",
1030 (duk_heaphdr
*) name
, (duk_tval
*) out
->value
,
1031 (long) out
->attrs
, (duk_tval
*) out
->this_binding
,
1032 (duk_heaphdr
*) out
->env
, (duk_heaphdr
*) out
->holder
));
1037 tv
= duk_hobject_find_existing_entry_tval_ptr_and_attrs(thr
->heap
, env
, name
, &attrs
);
1041 out
->this_binding
= NULL
; /* implicit this value always undefined for
1042 * declarative environment records.
1047 DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
1048 "name=%!O -> value=%!T, attrs=%ld, this=%!T, env=%!O, holder=%!O "
1049 "(declarative environment record, found in properties)",
1050 (duk_heaphdr
*) name
, (duk_tval
*) out
->value
,
1051 (long) out
->attrs
, (duk_tval
*) out
->this_binding
,
1052 (duk_heaphdr
*) out
->env
, (duk_heaphdr
*) out
->holder
));
1057 * Object environment record.
1059 * Binding (target) object is an external, uncontrolled object.
1060 * Identifier may be bound in an ancestor property, and may be
1061 * an accessor. Target can also be a Proxy which we must support
1065 /* XXX: we could save space by using _Target OR _This. If _Target, assume
1066 * this binding is undefined. If _This, assumes this binding is _This, and
1067 * target is also _This. One property would then be enough.
1070 duk_hobject
*target
;
1073 DUK_ASSERT(cl
== DUK_HOBJECT_CLASS_OBJENV
);
1075 tv_target
= duk_hobject_find_existing_entry_tval_ptr(thr
->heap
, env
, DUK_HTHREAD_STRING_INT_TARGET(thr
));
1076 DUK_ASSERT(tv_target
!= NULL
);
1077 DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_target
));
1078 target
= DUK_TVAL_GET_OBJECT(tv_target
);
1079 DUK_ASSERT(target
!= NULL
);
1081 /* Target may be a Proxy or property may be an accessor, so we must
1082 * use an actual, Proxy-aware hasprop check here.
1084 * out->holder is NOT set to the actual duk_hobject where the
1085 * property is found, but rather the object binding target object.
1088 if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(target
)) {
1089 DUK_ASSERT(name
!= NULL
);
1090 DUK_TVAL_SET_STRING(&tv_name
, name
);
1092 found
= duk_hobject_hasprop(thr
, tv_target
, &tv_name
);
1094 /* XXX: duk_hobject_hasprop() would be correct for
1095 * non-Proxy objects too, but it is about ~20-25%
1096 * slower at present so separate code paths for
1097 * Proxy and non-Proxy now.
1099 found
= duk_hobject_hasprop_raw(thr
, target
, name
);
1103 out
->value
= NULL
; /* can't get value, may be accessor */
1104 out
->attrs
= 0; /* irrelevant when out->value == NULL */
1105 tv
= duk_hobject_find_existing_entry_tval_ptr(thr
->heap
, env
, DUK_HTHREAD_STRING_INT_THIS(thr
));
1106 out
->this_binding
= tv
; /* may be NULL */
1108 out
->holder
= target
;
1110 DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
1111 "name=%!O -> value=%!T, attrs=%ld, this=%!T, env=%!O, holder=%!O "
1112 "(object environment record)",
1113 (duk_heaphdr
*) name
, (duk_tval
*) out
->value
,
1114 (long) out
->attrs
, (duk_tval
*) out
->this_binding
,
1115 (duk_heaphdr
*) out
->env
, (duk_heaphdr
*) out
->holder
));
1121 DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference failed, no parent traversal "
1122 "(not found from first traversed env)"));
1123 goto fail_not_found
;
1126 if (sanity
-- == 0) {
1127 DUK_ERROR_RANGE(thr
, DUK_STR_PROTOTYPE_CHAIN_LIMIT
);
1129 env
= DUK_HOBJECT_GET_PROTOTYPE(thr
->heap
, env
);
1133 * Not found (even in global object)
1141 * HASVAR: check identifier binding from a given environment record
1142 * without traversing its parents.
1144 * This primitive is not exposed to user code as such, but is used
1145 * internally for e.g. declaration binding instantiation.
1148 * 10.2.1.1.1 HasBinding(N)
1149 * 10.2.1.2.1 HasBinding(N)
1151 * Note: strictness has no bearing on this check. Hence we don't take
1152 * a 'strict' parameter.
1157 duk_bool_t
duk_js_hasvar_envrec(duk_hthread
*thr
,
1159 duk_hstring
*name
) {
1160 duk__id_lookup_result ref
;
1163 DUK_DDD(DUK_DDDPRINT("hasvar: thr=%p, env=%p, name=%!O "
1165 (void *) thr
, (void *) env
, (duk_heaphdr
*) name
,
1166 (duk_heaphdr
*) env
));
1168 DUK_ASSERT(thr
!= NULL
);
1169 DUK_ASSERT(env
!= NULL
);
1170 DUK_ASSERT(name
!= NULL
);
1172 DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(env
);
1173 DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name
);
1175 DUK_ASSERT(DUK_HOBJECT_IS_ENV(env
));
1176 DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(env
));
1178 /* lookup results is ignored */
1180 return duk__get_identifier_reference(thr
, env
, name
, NULL
, parents
, &ref
);
1188 * 11.1.2 Identifier Reference
1189 * 10.3.1 Identifier Resolution
1190 * 11.13.1 Simple Assignment [example of where the Reference is GetValue'd]
1191 * 8.7.1 GetValue (V)
1192 * 8.12.1 [[GetOwnProperty]] (P)
1193 * 8.12.2 [[GetProperty]] (P)
1194 * 8.12.3 [[Get]] (P)
1196 * If 'throw' is true, always leaves two values on top of stack: [val this].
1198 * If 'throw' is false, returns 0 if identifier cannot be resolved, and the
1199 * stack will be unaffected in this case. If identifier is resolved, returns
1200 * 1 and leaves [val this] on top of stack.
1202 * Note: the 'strict' flag of a reference returned by GetIdentifierReference
1203 * is ignored by GetValue. Hence we don't take a 'strict' parameter.
1205 * The 'throw' flag is needed for implementing 'typeof' for an unreferenced
1206 * identifier. An unreference identifier in other contexts generates a
1211 duk_bool_t
duk__getvar_helper(duk_hthread
*thr
,
1213 duk_activation
*act
,
1215 duk_bool_t throw_flag
) {
1216 duk_context
*ctx
= (duk_context
*) thr
;
1217 duk__id_lookup_result ref
;
1218 duk_tval tv_tmp_obj
;
1219 duk_tval tv_tmp_key
;
1222 DUK_DDD(DUK_DDDPRINT("getvar: thr=%p, env=%p, act=%p, name=%!O "
1224 (void *) thr
, (void *) env
, (void *) act
,
1225 (duk_heaphdr
*) name
, (duk_heaphdr
*) env
));
1227 DUK_ASSERT(thr
!= NULL
);
1228 DUK_ASSERT(name
!= NULL
);
1229 /* env and act may be NULL */
1231 DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(env
);
1232 DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name
);
1234 parents
= 1; /* follow parent chain */
1235 if (duk__get_identifier_reference(thr
, env
, name
, act
, parents
, &ref
)) {
1237 DUK_ASSERT(ref
.this_binding
== NULL
); /* always for register bindings */
1238 duk_push_tval(ctx
, ref
.value
);
1239 duk_push_undefined(ctx
);
1241 DUK_ASSERT(ref
.holder
!= NULL
);
1243 /* Note: getprop may invoke any getter and invalidate any
1244 * duk_tval pointers, so this must be done first.
1247 if (ref
.this_binding
) {
1248 duk_push_tval(ctx
, ref
.this_binding
);
1250 duk_push_undefined(ctx
);
1253 DUK_TVAL_SET_OBJECT(&tv_tmp_obj
, ref
.holder
);
1254 DUK_TVAL_SET_STRING(&tv_tmp_key
, name
);
1255 (void) duk_hobject_getprop(thr
, &tv_tmp_obj
, &tv_tmp_key
); /* [this value] */
1257 /* ref.value, ref.this.binding invalidated here by getprop call */
1259 duk_insert(ctx
, -2); /* [this value] -> [value this] */
1265 DUK_ERROR_FMT1(thr
, DUK_ERR_REFERENCE_ERROR
,
1266 "identifier '%s' undefined",
1267 (const char *) DUK_HSTRING_GET_DATA(name
));
1275 duk_bool_t
duk_js_getvar_envrec(duk_hthread
*thr
,
1278 duk_bool_t throw_flag
) {
1279 return duk__getvar_helper(thr
, env
, NULL
, name
, throw_flag
);
1283 duk_bool_t
duk_js_getvar_activation(duk_hthread
*thr
,
1284 duk_activation
*act
,
1286 duk_bool_t throw_flag
) {
1287 DUK_ASSERT(act
!= NULL
);
1288 return duk__getvar_helper(thr
, act
->lex_env
, act
, name
, throw_flag
);
1295 * 11.1.2 Identifier Reference
1296 * 10.3.1 Identifier Resolution
1297 * 11.13.1 Simple Assignment [example of where the Reference is PutValue'd]
1298 * 8.7.2 PutValue (V,W) [see especially step 3.b, undefined -> automatic global in non-strict mode]
1299 * 8.12.4 [[CanPut]] (P)
1300 * 8.12.5 [[Put]] (P)
1302 * Note: may invalidate any valstack (or object) duk_tval pointers because
1303 * putting a value may reallocate any object or any valstack. Caller beware.
1307 void duk__putvar_helper(duk_hthread
*thr
,
1309 duk_activation
*act
,
1312 duk_bool_t strict
) {
1313 duk__id_lookup_result ref
;
1314 duk_tval tv_tmp_obj
;
1315 duk_tval tv_tmp_key
;
1318 DUK_DDD(DUK_DDDPRINT("putvar: thr=%p, env=%p, act=%p, name=%!O, val=%p, strict=%ld "
1319 "(env -> %!dO, val -> %!T)",
1320 (void *) thr
, (void *) env
, (void *) act
,
1321 (duk_heaphdr
*) name
, (void *) val
, (long) strict
,
1322 (duk_heaphdr
*) env
, (duk_tval
*) val
));
1324 DUK_ASSERT(thr
!= NULL
);
1325 DUK_ASSERT(name
!= NULL
);
1326 DUK_ASSERT(val
!= NULL
);
1327 /* env and act may be NULL */
1329 DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(env
);
1330 DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name
);
1331 DUK_ASSERT_REFCOUNT_NONZERO_TVAL(val
);
1334 * In strict mode E5 protects 'eval' and 'arguments' from being
1335 * assigned to (or even declared anywhere). Attempt to do so
1336 * should result in a compile time SyntaxError. See the internal
1337 * design documentation for details.
1339 * Thus, we should never come here, run-time, for strict code,
1340 * and name 'eval' or 'arguments'.
1343 DUK_ASSERT(!strict
||
1344 (name
!= DUK_HTHREAD_STRING_EVAL(thr
) &&
1345 name
!= DUK_HTHREAD_STRING_LC_ARGUMENTS(thr
)));
1348 * Lookup variable and update in-place if found.
1351 parents
= 1; /* follow parent chain */
1353 if (duk__get_identifier_reference(thr
, env
, name
, act
, parents
, &ref
)) {
1354 if (ref
.value
&& (ref
.attrs
& DUK_PROPDESC_FLAG_WRITABLE
)) {
1355 /* Update duk_tval in-place if pointer provided and the
1356 * property is writable. If the property is not writable
1357 * (immutable binding), use duk_hobject_putprop() which
1358 * will respect mutability.
1362 DUK_ASSERT(ref
.this_binding
== NULL
); /* always for register bindings */
1365 DUK_ASSERT(tv_val
!= NULL
);
1366 DUK_TVAL_SET_TVAL_UPDREF(thr
, tv_val
, val
); /* side effects */
1368 /* ref.value and ref.this_binding invalidated here */
1370 DUK_ASSERT(ref
.holder
!= NULL
);
1372 DUK_TVAL_SET_OBJECT(&tv_tmp_obj
, ref
.holder
);
1373 DUK_TVAL_SET_STRING(&tv_tmp_key
, name
);
1374 (void) duk_hobject_putprop(thr
, &tv_tmp_obj
, &tv_tmp_key
, val
, strict
);
1376 /* ref.value and ref.this_binding invalidated here */
1383 * Not found: write to global object (non-strict) or ReferenceError
1384 * (strict); see E5 Section 8.7.2, step 3.
1388 DUK_DDD(DUK_DDDPRINT("identifier binding not found, strict => reference error"));
1389 DUK_ERROR(thr
, DUK_ERR_REFERENCE_ERROR
, "identifier not defined");
1392 DUK_DDD(DUK_DDDPRINT("identifier binding not found, not strict => set to global"));
1394 DUK_TVAL_SET_OBJECT(&tv_tmp_obj
, thr
->builtins
[DUK_BIDX_GLOBAL
]);
1395 DUK_TVAL_SET_STRING(&tv_tmp_key
, name
);
1396 (void) duk_hobject_putprop(thr
, &tv_tmp_obj
, &tv_tmp_key
, val
, 0); /* 0 = no throw */
1398 /* NB: 'val' may be invalidated here because put_value may realloc valstack,
1404 void duk_js_putvar_envrec(duk_hthread
*thr
,
1408 duk_bool_t strict
) {
1409 duk__putvar_helper(thr
, env
, NULL
, name
, val
, strict
);
1413 void duk_js_putvar_activation(duk_hthread
*thr
,
1414 duk_activation
*act
,
1417 duk_bool_t strict
) {
1418 DUK_ASSERT(act
!= NULL
);
1419 duk__putvar_helper(thr
, act
->lex_env
, act
, name
, val
, strict
);
1426 * 11.4.1 The delete operator
1427 * 10.2.1.1.5 DeleteBinding (N) [declarative environment record]
1428 * 10.2.1.2.5 DeleteBinding (N) [object environment record]
1430 * Variable bindings established inside eval() are deletable (configurable),
1431 * other bindings are not, including variables declared in global level.
1432 * Registers are always non-deletable, and the deletion of other bindings
1433 * is controlled by the configurable flag.
1435 * For strict mode code, the 'delete' operator should fail with a compile
1436 * time SyntaxError if applied to identifiers. Hence, no strict mode
1437 * run-time deletion of identifiers should ever happen. This function
1438 * should never be called from strict mode code!
1442 duk_bool_t
duk__delvar_helper(duk_hthread
*thr
,
1444 duk_activation
*act
,
1445 duk_hstring
*name
) {
1446 duk__id_lookup_result ref
;
1449 DUK_DDD(DUK_DDDPRINT("delvar: thr=%p, env=%p, act=%p, name=%!O "
1451 (void *) thr
, (void *) env
, (void *) act
,
1452 (duk_heaphdr
*) name
, (duk_heaphdr
*) env
));
1454 DUK_ASSERT(thr
!= NULL
);
1455 DUK_ASSERT(name
!= NULL
);
1456 /* env and act may be NULL */
1458 DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name
);
1460 parents
= 1; /* follow parent chain */
1462 if (duk__get_identifier_reference(thr
, env
, name
, act
, parents
, &ref
)) {
1463 if (ref
.value
&& !(ref
.attrs
& DUK_PROPDESC_FLAG_CONFIGURABLE
)) {
1464 /* Identifier found in registers (always non-deletable)
1465 * or declarative environment record and non-configurable.
1469 DUK_ASSERT(ref
.holder
!= NULL
);
1471 return duk_hobject_delprop_raw(thr
, ref
.holder
, name
, 0);
1475 * Not found (even in global object).
1477 * In non-strict mode this is a silent SUCCESS (!), see E5 Section 11.4.1,
1478 * step 3.b. In strict mode this case is a compile time SyntaxError so
1479 * we should not come here.
1482 DUK_DDD(DUK_DDDPRINT("identifier to be deleted not found: name=%!O "
1483 "(treated as silent success)",
1484 (duk_heaphdr
*) name
));
1490 duk_bool_t
duk_js_delvar_envrec(duk_hthread
*thr
,
1492 duk_hstring
*name
) {
1493 return duk__delvar_helper(thr
, env
, NULL
, name
);
1498 duk_bool_t
duk_js_delvar_activation(duk_hthread
*thr
,
1499 duk_activation
*act
,
1500 duk_hstring
*name
) {
1501 DUK_ASSERT(act
!= NULL
);
1502 return duk__delvar_helper(thr
, act
->lex_env
, act
, name
);
1509 * 10.4.3 Entering Function Code
1510 * 10.5 Declaration Binding Instantion
1511 * 12.2 Variable Statement
1512 * 11.1.2 Identifier Reference
1513 * 10.3.1 Identifier Resolution
1515 * Variable declaration behavior is mainly discussed in Section 10.5,
1516 * and is not discussed in the execution semantics (Sections 11-13).
1518 * Conceptually declarations happen when code (global, eval, function)
1519 * is entered, before any user code is executed. In practice, register-
1520 * bound identifiers are 'declared' automatically (by virtue of being
1521 * allocated to registers with the initial value 'undefined'). Other
1522 * identifiers are declared in the function prologue with this primitive.
1524 * Since non-register bindings eventually back to an internal object's
1525 * properties, the 'prop_flags' argument is used to specify binding
1528 * - Immutable binding: set DUK_PROPDESC_FLAG_WRITABLE to false
1529 * - Non-deletable binding: set DUK_PROPDESC_FLAG_CONFIGURABLE to false
1530 * - The flag DUK_PROPDESC_FLAG_ENUMERABLE should be set, although it
1531 * doesn't really matter for internal objects
1533 * All bindings are non-deletable mutable bindings except:
1535 * - Declarations in eval code (mutable, deletable)
1536 * - 'arguments' binding in strict function code (immutable)
1537 * - Function name binding of a function expression (immutable)
1539 * Declarations may go to declarative environment records (always
1540 * so for functions), but may also go to object environment records
1541 * (e.g. global code). The global object environment has special
1542 * behavior when re-declaring a function (but not a variable); see
1543 * E5.1 specification, Section 10.5, step 5.e.
1545 * Declarations always go to the 'top-most' environment record, i.e.
1546 * we never check the record chain. It's not an error even if a
1547 * property (even an immutable or non-deletable one) of the same name
1550 * If a declared variable already exists, its value needs to be updated
1551 * (if possible). Returns 1 if a PUTVAR needs to be done by the caller;
1552 * otherwise returns 0.
1556 duk_bool_t
duk__declvar_helper(duk_hthread
*thr
,
1560 duk_small_int_t prop_flags
,
1561 duk_bool_t is_func_decl
) {
1562 duk_context
*ctx
= (duk_context
*) thr
;
1563 duk_hobject
*holder
;
1565 duk__id_lookup_result ref
;
1568 DUK_DDD(DUK_DDDPRINT("declvar: thr=%p, env=%p, name=%!O, val=%!T, prop_flags=0x%08lx, is_func_decl=%ld "
1570 (void *) thr
, (void *) env
, (duk_heaphdr
*) name
,
1571 (duk_tval
*) val
, (unsigned long) prop_flags
,
1572 (unsigned int) is_func_decl
, (duk_heaphdr
*) env
));
1574 DUK_ASSERT(thr
!= NULL
);
1575 DUK_ASSERT(env
!= NULL
);
1576 DUK_ASSERT(name
!= NULL
);
1577 DUK_ASSERT(val
!= NULL
);
1579 /* Note: in strict mode the compiler should reject explicit
1580 * declaration of 'eval' or 'arguments'. However, internal
1581 * bytecode may declare 'arguments' in the function prologue.
1582 * We don't bother checking (or asserting) for these now.
1585 /* Note: val is a stable duk_tval pointer. The caller makes
1586 * a value copy into its stack frame, so 'tv_val' is not subject
1587 * to side effects here.
1591 * Check whether already declared.
1593 * We need to check whether the binding exists in the environment
1594 * without walking its parents. However, we still need to check
1595 * register-bound identifiers and the prototype chain of an object
1596 * environment target object.
1599 parents
= 0; /* just check 'env' */
1600 if (duk__get_identifier_reference(thr
, env
, name
, NULL
, parents
, &ref
)) {
1603 duk_small_int_t flags
;
1606 * Variable already declared, ignore re-declaration.
1607 * The only exception is the updated behavior of E5.1 for
1608 * global function declarations, E5.1 Section 10.5, step 5.e.
1609 * This behavior does not apply to global variable declarations.
1612 if (!(is_func_decl
&& env
== thr
->builtins
[DUK_BIDX_GLOBAL_ENV
])) {
1613 DUK_DDD(DUK_DDDPRINT("re-declare a binding, ignoring"));
1614 return 1; /* 1 -> needs a PUTVAR */
1618 * Special behavior in E5.1.
1620 * Note that even though parents == 0, the conflicting property
1621 * may be an inherited property (currently our global object's
1622 * prototype is Object.prototype). Step 5.e first operates on
1623 * the existing property (which is potentially in an ancestor)
1624 * and then defines a new property in the global object (and
1625 * never modifies the ancestor).
1627 * Also note that this logic would become even more complicated
1628 * if the conflicting property might be a virtual one. Object
1629 * prototype has no virtual properties, though.
1631 * XXX: this is now very awkward, rework.
1634 DUK_DDD(DUK_DDDPRINT("re-declare a function binding in global object, "
1635 "updated E5.1 processing"));
1637 DUK_ASSERT(ref
.holder
!= NULL
);
1638 holder
= ref
.holder
;
1640 /* holder will be set to the target object, not the actual object
1641 * where the property was found (see duk__get_identifier_reference()).
1643 DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(holder
) == DUK_HOBJECT_CLASS_GLOBAL
);
1644 DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(holder
)); /* global object doesn't have array part */
1646 /* XXX: use a helper for prototype traversal; no loop check here */
1647 /* must be found: was found earlier, and cannot be inherited */
1649 DUK_ASSERT(holder
!= NULL
);
1650 duk_hobject_find_existing_entry(thr
->heap
, holder
, name
, &e_idx
, &h_idx
);
1654 /* SCANBUILD: NULL pointer dereference, doesn't actually trigger,
1657 holder
= DUK_HOBJECT_GET_PROTOTYPE(thr
->heap
, holder
);
1659 DUK_ASSERT(holder
!= NULL
);
1660 DUK_ASSERT(e_idx
>= 0);
1661 /* SCANBUILD: scan-build produces a NULL pointer dereference warning
1662 * below; it never actually triggers because holder is actually never
1666 /* ref.holder is global object, holder is the object with the
1667 * conflicting property.
1670 flags
= DUK_HOBJECT_E_GET_FLAGS(thr
->heap
, holder
, e_idx
);
1671 if (!(flags
& DUK_PROPDESC_FLAG_CONFIGURABLE
)) {
1672 if (flags
& DUK_PROPDESC_FLAG_ACCESSOR
) {
1673 DUK_DDD(DUK_DDDPRINT("existing property is a non-configurable "
1674 "accessor -> reject"));
1675 goto fail_existing_attributes
;
1677 if (!((flags
& DUK_PROPDESC_FLAG_WRITABLE
) &&
1678 (flags
& DUK_PROPDESC_FLAG_ENUMERABLE
))) {
1679 DUK_DDD(DUK_DDDPRINT("existing property is a non-configurable "
1680 "plain property which is not writable and "
1681 "enumerable -> reject"));
1682 goto fail_existing_attributes
;
1685 DUK_DDD(DUK_DDDPRINT("existing property is not configurable but "
1686 "is plain, enumerable, and writable -> "
1687 "allow redeclaration"));
1690 if (holder
== ref
.holder
) {
1691 /* XXX: if duk_hobject_define_property_internal() was updated
1692 * to handle a pre-existing accessor property, this would be
1693 * a simple call (like for the ancestor case).
1695 DUK_DDD(DUK_DDDPRINT("redefine, offending property in global object itself"));
1697 if (flags
& DUK_PROPDESC_FLAG_ACCESSOR
) {
1700 tmp
= DUK_HOBJECT_E_GET_VALUE_GETTER(thr
->heap
, holder
, e_idx
);
1701 DUK_HOBJECT_E_SET_VALUE_GETTER(thr
->heap
, holder
, e_idx
, NULL
);
1702 DUK_HOBJECT_DECREF_ALLOWNULL(thr
, tmp
);
1704 tmp
= DUK_HOBJECT_E_GET_VALUE_SETTER(thr
->heap
, holder
, e_idx
);
1705 DUK_HOBJECT_E_SET_VALUE_SETTER(thr
->heap
, holder
, e_idx
, NULL
);
1706 DUK_HOBJECT_DECREF_ALLOWNULL(thr
, tmp
);
1709 tv
= DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr
->heap
, holder
, e_idx
);
1710 DUK_TVAL_SET_UNDEFINED_UPDREF(thr
, tv
);
1713 /* Here val would be potentially invalid if we didn't make
1714 * a value copy at the caller.
1717 tv
= DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr
->heap
, holder
, e_idx
);
1718 DUK_TVAL_SET_TVAL(tv
, val
);
1719 DUK_TVAL_INCREF(thr
, tv
);
1720 DUK_HOBJECT_E_SET_FLAGS(thr
->heap
, holder
, e_idx
, prop_flags
);
1722 DUK_DDD(DUK_DDDPRINT("updated global binding, final result: "
1723 "value -> %!T, prop_flags=0x%08lx",
1724 (duk_tval
*) DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr
->heap
, holder
, e_idx
),
1725 (unsigned long) prop_flags
));
1727 DUK_DDD(DUK_DDDPRINT("redefine, offending property in ancestor"));
1729 DUK_ASSERT(ref
.holder
== thr
->builtins
[DUK_BIDX_GLOBAL
]);
1730 duk_push_tval(ctx
, val
);
1731 duk_hobject_define_property_internal(thr
, ref
.holder
, name
, prop_flags
);
1738 * Not found (in registers or record objects). Declare
1739 * to current variable environment.
1746 if (DUK_HOBJECT_IS_DECENV(env
)) {
1749 DUK_ASSERT(DUK_HOBJECT_IS_OBJENV(env
));
1751 tv
= duk_hobject_find_existing_entry_tval_ptr(thr
->heap
, env
, DUK_HTHREAD_STRING_INT_TARGET(thr
));
1752 DUK_ASSERT(tv
!= NULL
);
1753 DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv
));
1754 holder
= DUK_TVAL_GET_OBJECT(tv
);
1755 DUK_ASSERT(holder
!= NULL
);
1759 * Define new property
1761 * Note: this may fail if the holder is not extensible.
1764 /* XXX: this is awkward as we use an internal method which doesn't handle
1765 * extensibility etc correctly. Basically we'd want to do a [[DefineOwnProperty]]
1766 * or Object.defineProperty() here.
1769 if (!DUK_HOBJECT_HAS_EXTENSIBLE(holder
)) {
1770 goto fail_not_extensible
;
1773 duk_push_hobject(ctx
, holder
);
1774 duk_push_hstring(ctx
, name
);
1775 duk_push_tval(ctx
, val
);
1776 duk_xdef_prop(ctx
, -3, prop_flags
); /* [holder name val] -> [holder] */
1781 fail_existing_attributes
:
1782 fail_not_extensible
:
1783 DUK_ERROR_TYPE(thr
, "declaration failed");
1788 duk_bool_t
duk_js_declvar_activation(duk_hthread
*thr
,
1789 duk_activation
*act
,
1792 duk_small_int_t prop_flags
,
1793 duk_bool_t is_func_decl
) {
1795 duk_tval tv_val_copy
;
1798 * Make a value copy of the input val. This ensures that
1799 * side effects cannot invalidate the pointer.
1802 DUK_TVAL_SET_TVAL(&tv_val_copy
, val
);
1806 * Delayed env creation check
1809 if (!act
->var_env
) {
1810 DUK_ASSERT(act
->lex_env
== NULL
);
1811 duk_js_init_activation_environment_records_delayed(thr
, act
);
1813 DUK_ASSERT(act
->lex_env
!= NULL
);
1814 DUK_ASSERT(act
->var_env
!= NULL
);
1817 DUK_ASSERT(env
!= NULL
);
1818 DUK_ASSERT(DUK_HOBJECT_IS_ENV(env
));
1820 return duk__declvar_helper(thr
, env
, name
, val
, prop_flags
, is_func_decl
);