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 *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;
44 	duk_hobject *env;
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_hcompiledfunction * f)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;
76 
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.
81 	 */
82 	DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, f) != NULL);
83 	DUK_UNREF(thr);
84 
85 	tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, f);
86 	tv_end = DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(thr->heap, f);
87 	while (tv < tv_end) {
88 		DUK_TVAL_INCREF(thr, tv);
89 		tv++;
90 	}
91 
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);
96 		funcs++;
97 	}
98 }
99 
100 /* Push a new closure on the stack.
101  *
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).
105  */
106 
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,
111 	DUK_STRIDX_NAME,
112 	DUK_STRIDX_INT_PC2LINE,
113 	DUK_STRIDX_FILE_NAME,
114 	DUK_STRIDX_INT_SOURCE
115 };
116 
117 DUK_INTERNAL
duk_js_push_closure(duk_hthread * thr,duk_hcompiledfunction * fun_temp,duk_hobject * outer_var_env,duk_hobject * outer_lex_env,duk_bool_t add_auto_proto)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;
125 	duk_small_uint_t i;
126 	duk_uint_t len_value;
127 
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);
135 
136 	duk_push_compiledfunction(ctx);
137 	duk_push_hobject(ctx, &fun_temp->obj);  /* -> [ ... closure template ] */
138 
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);
145 
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));
149 
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.
153 	 */
154 
155 	DUK_HBUFFER_INCREF(thr, DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, fun_clos));
156 	duk__inc_data_inner_refcounts(thr, fun_temp);
157 
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;
163 #endif
164 
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);
168 
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).
171 	 */
172 	DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, &fun_clos->obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
173 
174 	/*
175 	 *  Init/assert flags, copying them where appropriate.  Some flags
176 	 *  (like NEWENV) are processed separately below.
177 	 */
178 
179 	/* XXX: copy flags using a mask */
180 
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);
191 	}
192 	if (DUK_HOBJECT_HAS_NOTAIL(&fun_temp->obj)) {
193 		DUK_HOBJECT_SET_NOTAIL(&fun_clos->obj);
194 	}
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
199 		 * so copy it too.
200 		 */
201 		DUK_HOBJECT_SET_NAMEBINDING(&fun_clos->obj);
202 	}
203 	if (DUK_HOBJECT_HAS_CREATEARGS(&fun_temp->obj)) {
204 		DUK_HOBJECT_SET_CREATEARGS(&fun_clos->obj);
205 	}
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 	/*
211 	 *  Setup environment record properties based on the template and
212 	 *  its flags.
213 	 *
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).
219 	 *
220 	 *  _Lexenv is always set; _Varenv defaults to _Lexenv if missing,
221 	 *  so _Varenv is only set if _Lexenv != _Varenv.
222 	 *
223 	 *  This is relatively complex, see doc/identifier-handling.rst.
224 	 */
225 
226 	if (DUK_HOBJECT_HAS_NEWENV(&fun_temp->obj)) {
227 		DUK_HOBJECT_SET_NEWENV(&fun_clos->obj);
228 
229 		if (DUK_HOBJECT_HAS_NAMEBINDING(&fun_temp->obj)) {
230 			duk_hobject *proto;
231 
232 			/*
233 			 *  Named function expression, name needs to be bound
234 			 *  in an intermediate environment record.  The "outer"
235 			 *  lexical/variable environment will thus be:
236 			 *
237 			 *  a) { funcname: <func>, __prototype: outer_lex_env }
238 			 *  b) { funcname: <func>, __prototype:  <globalenv> }  (if outer_lex_env missing)
239 			 */
240 
241 			DUK_ASSERT(duk_has_prop_stridx(ctx, -1, DUK_STRIDX_NAME));  /* required if NAMEBINDING set */
242 
243 			if (outer_lex_env) {
244 				proto = outer_lex_env;
245 			} else {
246 				proto = thr->builtins[DUK_BIDX_GLOBAL_ENV];
247 			}
248 
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),
253 			                                    proto);
254 
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).
258 			 */
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 */
263 
264 			/* [ ... closure template env ] */
265 
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
269 			 */
270 
271 			/* [ ... closure template ] */
272 		} else {
273 			/*
274 			 *  Other cases (function declaration, anonymous function expression,
275 			 *  strict direct eval code).  The "outer" environment will be whatever
276 			 *  the caller gave us.
277 			 */
278 
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
283 			 */
284 
285 			/* [ ... closure template ] */
286 		}
287 	} else {
288 		/*
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
293 		 *  function.
294 		 */
295 
296 		DUK_ASSERT(!DUK_HOBJECT_HAS_NAMEBINDING(&fun_temp->obj));
297 
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);
300 
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);
304 		}
305 	}
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)));
312 	duk_pop_2(ctx);
313 #endif
314 
315 	/*
316 	 *  Copy some internal properties directly
317 	 *
318 	 *  The properties will be writable and configurable, but not enumerable.
319 	 */
320 
321 	/* [ ... closure template ] */
322 
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)));
326 
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);
333 		} else {
334 			DUK_DDD(DUK_DDDPRINT("copying property, stridx=%ld -> not found", (long) stridx));
335 			duk_pop(ctx);
336 		}
337 	}
338 
339 	/*
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.
343 	 */
344 
345 	/* [ ... closure template ] */
346 
347 	len_value = 0;
348 
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);
355 	} else {
356 		/* XXX: what to do if _Formals is not empty but compiler has
357 		 * optimized it away -- read length from an explicit property
358 		 * then?
359 		 */
360 	}
361 	duk_pop(ctx);
362 
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);
365 
366 	/*
367 	 *  "prototype" is, by default, a fresh object with the "constructor"
368 	 *  property.
369 	 *
370 	 *  Note that this creates a circular reference for every function
371 	 *  instance (closure) which prevents refcount-based collection of
372 	 *  function instances.
373 	 *
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?
378 	 */
379 
380 	/* [ ... closure template ] */
381 
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 ] */
388 	}
389 
390 	/*
391 	 *  "arguments" and "caller" must be mapped to throwers for strict
392 	 *  mode and bound functions (E5 Section 15.3.5).
393 	 *
394 	 *  XXX: This is expensive to have for every strict function instance.
395 	 *  Try to implement as virtual properties or on-demand created properties.
396 	 */
397 
398 	/* [ ... closure template ] */
399 
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);
403 	} else {
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"));
406 		duk_push_null(ctx);
407 		duk_xdef_prop_stridx(ctx, -3, DUK_STRIDX_CALLER, DUK_PROPDESC_FLAGS_NONE);
408 #else
409 		DUK_DDD(DUK_DDDPRINT("function is non-strict and non-standard 'caller' property not used"));
410 #endif
411 	}
412 
413 	/*
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.
420 	 *
421 	 *  XXX: make optional?  costs something per function.
422 	 */
423 
424 	/* [ ... closure template ] */
425 
426 	if (duk_get_prop_stridx(ctx, -1, DUK_STRIDX_NAME)) {
427 		/* [ ... closure template name ] */
428 		DUK_ASSERT(duk_is_string(ctx, -1));
429 	} else {
430 		/* [ ... closure template undefined ] */
431 		duk_pop(ctx);
432 		duk_push_hstring_stridx(ctx, DUK_STRIDX_EMPTY_STRING);
433 	}
434 	duk_xdef_prop_stridx(ctx, -3, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE);  /* -> [ ... closure template ] */
435 
436 	/*
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
442 	 *  through the API).
443 	 */
444 
445 	duk_compact(ctx, -2);
446 
447 	/*
448 	 *  Some assertions (E5 Section 13.2).
449 	 */
450 
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);
461 
462 	/*
463 	 *  Finish
464 	 */
465 
466 	/* [ ... closure template ] */
467 
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)));
471 
472 	duk_pop(ctx);
473 
474 	/* [ ... closure ] */
475 }
476 
477 /*
478  *  Delayed activation environment record initialization (for functions
479  *  with NEWENV).
480  *
481  *  The non-delayed initialization is handled by duk_handle_call().
482  */
483 
484 /* shared helper */
485 DUK_INTERNAL
duk_create_activation_environment_record(duk_hthread * thr,duk_hobject * func,duk_size_t idx_bottom)486 duk_hobject *duk_create_activation_environment_record(duk_hthread *thr,
487                                                       duk_hobject *func,
488                                                       duk_size_t idx_bottom) {
489 	duk_context *ctx = (duk_context *) thr;
490 	duk_hobject *env;
491 	duk_hobject *parent;
492 	duk_tval *tv;
493 
494 	DUK_ASSERT(thr != NULL);
495 	DUK_ASSERT(func != NULL);
496 
497 	tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, func, DUK_HTHREAD_STRING_INT_LEXENV(thr));
498 	if (tv) {
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);
502 	} else {
503 		parent = thr->builtins[DUK_BIDX_GLOBAL_ENV];
504 	}
505 
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 */
513 
514 	/* open scope information, for compiled functions only */
515 
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);
523 	}
524 
525 	return env;
526 }
527 
528 DUK_INTERNAL
duk_js_init_activation_environment_records_delayed(duk_hthread * thr,duk_activation * act)529 void duk_js_init_activation_environment_records_delayed(duk_hthread *thr,
530                                                         duk_activation *act) {
531 	duk_context *ctx = (duk_context *) thr;
532 	duk_hobject *func;
533 	duk_hobject *env;
534 	duk_size_t act_off;
535 
536 	DUK_ASSERT(act != NULL);
537 	act_off = (duk_size_t) ((duk_uint8_t *) act - (duk_uint8_t *) thr->callstack);
538 	func = DUK_ACT_GET_FUNC(act);
539 	DUK_ASSERT(func != NULL);
540 	DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func));  /* bound functions are never in act 'func' */
541 
542 	/*
543 	 *  Delayed initialization only occurs for 'NEWENV' functions.
544 	 */
545 
546 	DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func));
547 	DUK_ASSERT(act->lex_env == NULL);
548 	DUK_ASSERT(act->var_env == NULL);
549 
550 	env = duk_create_activation_environment_record(thr, func, act->idx_bottom);
551 	DUK_ASSERT(env != NULL);
552 	act = (duk_activation *) ((duk_uint8_t *) thr->callstack + act_off);
553 
554 	DUK_DDD(DUK_DDDPRINT("created delayed fresh env: %!ipO", (duk_heaphdr *) env));
555 #ifdef DUK_USE_DDDPRINT
556 	{
557 		duk_hobject *p = env;
558 		while (p) {
559 			DUK_DDD(DUK_DDDPRINT("  -> %!ipO", (duk_heaphdr *) p));
560 			p = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, p);
561 		}
562 	}
563 #endif
564 
565 	act->lex_env = env;
566 	act->var_env = env;
567 	DUK_HOBJECT_INCREF(thr, env);  /* XXX: incref by count (here 2 times) */
568 	DUK_HOBJECT_INCREF(thr, env);
569 
570 	duk_pop(ctx);
571 }
572 
573 /*
574  *  Closing environment records.
575  *
576  *  The environment record MUST be closed with the thread where its activation
577  *  is.  In other words (if 'env' is open):
578  *
579  *    - 'thr' must match _env.thread
580  *    - 'func' must match _env.callee
581  *    - 'regbase' must match _env.regbase
582  *
583  *  These are not looked up from the env to minimize code size.
584  *
585  *  XXX: should access the own properties directly instead of using the API
586  */
587 
duk_js_close_environment_record(duk_hthread * thr,duk_hobject * env,duk_hobject * func,duk_size_t regbase)588 DUK_INTERNAL void duk_js_close_environment_record(duk_hthread *thr, duk_hobject *env, duk_hobject *func, duk_size_t regbase) {
589 	duk_context *ctx = (duk_context *) thr;
590 	duk_uint_fast32_t i;
591 
592 	DUK_ASSERT(thr != NULL);
593 	DUK_ASSERT(env != NULL);
594 	/* func is NULL for lightfuncs */
595 
596 	if (!DUK_HOBJECT_IS_DECENV(env) || DUK_HOBJECT_HAS_ENVRECCLOSED(env)) {
597 		DUK_DDD(DUK_DDDPRINT("environment record not a declarative record, "
598 		                     "or already closed: %!iO",
599 		                     (duk_heaphdr *) env));
600 		return;
601 	}
602 
603 	DUK_DDD(DUK_DDDPRINT("closing environment record: %!iO, func: %!iO, regbase: %ld",
604 	                     (duk_heaphdr *) env, (duk_heaphdr *) func, (long) regbase));
605 
606 	duk_push_hobject(ctx, env);
607 
608 	/* assertions: env must be closed in the same thread as where it runs */
609 #ifdef DUK_USE_ASSERTIONS
610 	{
611 		/* [... env] */
612 
613 		if (duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_CALLEE)) {
614 			DUK_ASSERT(duk_is_object(ctx, -1));
615 			DUK_ASSERT(duk_get_hobject(ctx, -1) == (duk_hobject *) func);
616 		}
617 		duk_pop(ctx);
618 
619 		if (duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_THREAD)) {
620 			DUK_ASSERT(duk_is_object(ctx, -1));
621 			DUK_ASSERT(duk_get_hobject(ctx, -1) == (duk_hobject *) thr);
622 		}
623 		duk_pop(ctx);
624 
625 		if (duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_REGBASE)) {
626 			DUK_ASSERT(duk_is_number(ctx, -1));
627 			DUK_ASSERT(duk_get_number(ctx, -1) == (double) regbase);
628 		}
629 		duk_pop(ctx);
630 
631 		/* [... env] */
632 	}
633 #endif
634 
635 	if (func != NULL && DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
636 		duk_hobject *varmap;
637 		duk_hstring *key;
638 		duk_tval *tv;
639 		duk_uint_t regnum;
640 
641 		/* XXX: additional conditions when to close variables? we don't want to do it
642 		 * unless the environment may have "escaped" (referenced in a function closure).
643 		 * With delayed environments, the existence is probably good enough of a check.
644 		 */
645 
646 		/* XXX: any way to detect faster whether something needs to be closed?
647 		 * We now look up _Callee and then skip the rest.
648 		 */
649 
650 		/* Note: we rely on the _Varmap having a bunch of nice properties, like:
651 		 *  - being compacted and unmodified during this process
652 		 *  - not containing an array part
653 		 *  - having correct value types
654 		 */
655 
656 		/* [... env] */
657 
658 		if (!duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_CALLEE)) {
659 			DUK_DDD(DUK_DDDPRINT("env has no callee property, nothing to close; re-delete the control properties just in case"));
660 			duk_pop(ctx);
661 			goto skip_varmap;
662 		}
663 
664 		/* [... env callee] */
665 
666 		if (!duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_VARMAP)) {
667 			DUK_DDD(DUK_DDDPRINT("callee has no varmap property, nothing to close; delete the control properties"));
668 			duk_pop_2(ctx);
669 			goto skip_varmap;
670 		}
671 		varmap = duk_require_hobject(ctx, -1);
672 		DUK_ASSERT(varmap != NULL);
673 
674 		DUK_DDD(DUK_DDDPRINT("varmap: %!O", (duk_heaphdr *) varmap));
675 
676 		/* [... env callee varmap] */
677 
678 		DUK_DDD(DUK_DDDPRINT("copying bound register values, %ld bound regs", (long) DUK_HOBJECT_GET_ENEXT(varmap)));
679 
680 		for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(varmap); i++) {
681 			key = DUK_HOBJECT_E_GET_KEY(thr->heap, varmap, i);
682 			DUK_ASSERT(key != NULL);   /* assume keys are compacted */
683 
684 			DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, varmap, i));  /* assume plain values */
685 
686 			tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, varmap, i);
687 			DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));  /* assume value is a number */
688 			regnum = (duk_uint_t) DUK_TVAL_GET_NUMBER(tv);
689 			DUK_ASSERT_DISABLE(regnum >= 0);  /* unsigned */
690 			DUK_ASSERT(regnum < ((duk_hcompiledfunction *) func)->nregs);  /* regnum is sane */
691 			DUK_ASSERT(thr->valstack + regbase + regnum >= thr->valstack);
692 			DUK_ASSERT(thr->valstack + regbase + regnum < thr->valstack_top);
693 
694 			/* XXX: slightly awkward */
695 			duk_push_hstring(ctx, key);
696 			duk_push_tval(ctx, thr->valstack + regbase + regnum);
697 			DUK_DDD(DUK_DDDPRINT("closing identifier '%s' -> reg %ld, value %!T",
698 			                     (const char *) duk_require_string(ctx, -2),
699 			                     (long) regnum,
700 			                     (duk_tval *) duk_get_tval(ctx, -1)));
701 
702 			/* [... env callee varmap key val] */
703 
704 			/* if property already exists, overwrites silently */
705 			duk_xdef_prop(ctx, -5, DUK_PROPDESC_FLAGS_WE);  /* writable but not deletable */
706 		}
707 
708 		duk_pop_2(ctx);
709 
710 		/* [... env] */
711 	}
712 
713  skip_varmap:
714 
715 	/* [... env] */
716 
717 	duk_del_prop_stridx(ctx, -1, DUK_STRIDX_INT_CALLEE);
718 	duk_del_prop_stridx(ctx, -1, DUK_STRIDX_INT_THREAD);
719 	duk_del_prop_stridx(ctx, -1, DUK_STRIDX_INT_REGBASE);
720 
721 	duk_pop(ctx);
722 
723 	DUK_HOBJECT_SET_ENVRECCLOSED(env);
724 
725 	DUK_DDD(DUK_DDDPRINT("environment record after being closed: %!O",
726 	                     (duk_heaphdr *) env));
727 }
728 
729 /*
730  *  GETIDREF: a GetIdentifierReference-like helper.
731  *
732  *  Provides a parent traversing lookup and a single level lookup
733  *  (for HasBinding).
734  *
735  *  Instead of returning the value, returns a bunch of values allowing
736  *  the caller to read, write, or delete the binding.  Value pointers
737  *  are duk_tval pointers which can be mutated directly as long as
738  *  refcounts are properly updated.  Note that any operation which may
739  *  reallocate valstacks or compact objects may invalidate the returned
740  *  duk_tval (but not object) pointers, so caller must be very careful.
741  *
742  *  If starting environment record 'env' is given, 'act' is ignored.
743  *  However, if 'env' is NULL, the caller may identify, in 'act', an
744  *  activation which hasn't had its declarative environment initialized
745  *  yet.  The activation registers are then looked up, and its parent
746  *  traversed normally.
747  *
748  *  The 'out' structure values are only valid if the function returns
749  *  success (non-zero).
750  */
751 
752 /* lookup name from an open declarative record's registers */
753 DUK_LOCAL
duk__getid_open_decl_env_regs(duk_hthread * thr,duk_hstring * name,duk_hobject * env,duk__id_lookup_result * out)754 duk_bool_t duk__getid_open_decl_env_regs(duk_hthread *thr,
755                                          duk_hstring *name,
756                                          duk_hobject *env,
757                                          duk__id_lookup_result *out) {
758 	duk_hthread *env_thr;
759 	duk_hobject *env_func;
760 	duk_size_t env_regbase;
761 	duk_hobject *varmap;
762 	duk_tval *tv;
763 	duk_size_t reg_rel;
764 	duk_size_t idx;
765 
766 	DUK_ASSERT(thr != NULL);
767 	DUK_ASSERT(name != NULL);
768 	DUK_ASSERT(env != NULL);
769 	DUK_ASSERT(out != NULL);
770 
771 	DUK_ASSERT(DUK_HOBJECT_IS_DECENV(env));
772 
773 	tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_CALLEE(thr));
774 	if (!tv) {
775 		/* env is closed, should be missing _Callee, _Thread, _Regbase */
776 		DUK_ASSERT(duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_CALLEE(thr)) == NULL);
777 		DUK_ASSERT(duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_THREAD(thr)) == NULL);
778 		DUK_ASSERT(duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_REGBASE(thr)) == NULL);
779 		return 0;
780 	}
781 
782 	DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv));
783 	DUK_ASSERT(DUK_TVAL_GET_OBJECT(tv) != NULL);
784 	DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(DUK_TVAL_GET_OBJECT(tv)));
785 	env_func = DUK_TVAL_GET_OBJECT(tv);
786 	DUK_ASSERT(env_func != NULL);
787 
788 	tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env_func, DUK_HTHREAD_STRING_INT_VARMAP(thr));
789 	if (!tv) {
790 		return 0;
791 	}
792 	DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv));
793 	varmap = DUK_TVAL_GET_OBJECT(tv);
794 	DUK_ASSERT(varmap != NULL);
795 
796 	tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, varmap, name);
797 	if (!tv) {
798 		return 0;
799 	}
800 	DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
801 	reg_rel = (duk_size_t) DUK_TVAL_GET_NUMBER(tv);
802 	DUK_ASSERT_DISABLE(reg_rel >= 0);  /* unsigned */
803 	DUK_ASSERT(reg_rel < ((duk_hcompiledfunction *) env_func)->nregs);
804 
805 	tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_THREAD(thr));
806 	DUK_ASSERT(tv != NULL);
807 	DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv));
808 	DUK_ASSERT(DUK_TVAL_GET_OBJECT(tv) != NULL);
809 	DUK_ASSERT(DUK_HOBJECT_IS_THREAD(DUK_TVAL_GET_OBJECT(tv)));
810 	env_thr = (duk_hthread *) DUK_TVAL_GET_OBJECT(tv);
811 	DUK_ASSERT(env_thr != NULL);
812 
813 	/* Note: env_thr != thr is quite possible and normal, so careful
814 	 * with what thread is used for valstack lookup.
815 	 */
816 
817 	tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_REGBASE(thr));
818 	DUK_ASSERT(tv != NULL);
819 	DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
820 	env_regbase = (duk_size_t) DUK_TVAL_GET_NUMBER(tv);
821 
822 	idx = env_regbase + reg_rel;
823 	tv = env_thr->valstack + idx;
824 	DUK_ASSERT(tv >= env_thr->valstack && tv < env_thr->valstack_end);  /* XXX: more accurate? */
825 
826 	out->value = tv;
827 	out->attrs = DUK_PROPDESC_FLAGS_W;  /* registers are mutable, non-deletable */
828 	out->this_binding = NULL;  /* implicit this value always undefined for
829 	                            * declarative environment records.
830 	                            */
831 	out->env = env;
832 	out->holder = NULL;
833 
834 	return 1;
835 }
836 
837 /* lookup name from current activation record's functions' registers */
838 DUK_LOCAL
duk__getid_activation_regs(duk_hthread * thr,duk_hstring * name,duk_activation * act,duk__id_lookup_result * out)839 duk_bool_t duk__getid_activation_regs(duk_hthread *thr,
840                                       duk_hstring *name,
841                                       duk_activation *act,
842                                       duk__id_lookup_result *out) {
843 	duk_tval *tv;
844 	duk_hobject *func;
845 	duk_hobject *varmap;
846 	duk_size_t reg_rel;
847 	duk_size_t idx;
848 
849 	DUK_ASSERT(thr != NULL);
850 	DUK_ASSERT(name != NULL);
851 	DUK_ASSERT(act != NULL);
852 	DUK_ASSERT(out != NULL);
853 
854 	func = DUK_ACT_GET_FUNC(act);
855 	DUK_ASSERT(func != NULL);
856 	DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func));
857 
858 	if (!DUK_HOBJECT_IS_COMPILEDFUNCTION(func)) {
859 		return 0;
860 	}
861 
862 	tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, func, DUK_HTHREAD_STRING_INT_VARMAP(thr));
863 	if (!tv) {
864 		return 0;
865 	}
866 	DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv));
867 	varmap = DUK_TVAL_GET_OBJECT(tv);
868 	DUK_ASSERT(varmap != NULL);
869 
870 	tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, varmap, name);
871 	if (!tv) {
872 		return 0;
873 	}
874 	DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
875 	reg_rel = (duk_size_t) DUK_TVAL_GET_NUMBER(tv);
876 	DUK_ASSERT_DISABLE(reg_rel >= 0);
877 	DUK_ASSERT(reg_rel < ((duk_hcompiledfunction *) func)->nregs);
878 
879 	idx = act->idx_bottom + reg_rel;
880 	DUK_ASSERT(idx >= act->idx_bottom);
881 	tv = thr->valstack + idx;
882 
883 	out->value = tv;
884 	out->attrs = DUK_PROPDESC_FLAGS_W;  /* registers are mutable, non-deletable */
885 	out->this_binding = NULL;  /* implicit this value always undefined for
886 	                            * declarative environment records.
887 	                            */
888 	out->env = NULL;
889 	out->holder = NULL;
890 
891 	return 1;
892 }
893 
894 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)895 duk_bool_t duk__get_identifier_reference(duk_hthread *thr,
896                                          duk_hobject *env,
897                                          duk_hstring *name,
898                                          duk_activation *act,
899                                          duk_bool_t parents,
900                                          duk__id_lookup_result *out) {
901 	duk_tval *tv;
902 	duk_tval *tv_target;
903 	duk_tval tv_name;
904 	duk_uint_t sanity;
905 
906 	DUK_ASSERT(thr != NULL);
907 	DUK_ASSERT(env != NULL || act != NULL);
908 	DUK_ASSERT(name != NULL);
909 	DUK_ASSERT(out != NULL);
910 
911 	DUK_ASSERT(!env || DUK_HOBJECT_IS_ENV(env));
912 	DUK_ASSERT(!env || !DUK_HOBJECT_HAS_ARRAY_PART(env));
913 
914 	/*
915 	 *  Conceptually, we look for the identifier binding by starting from
916 	 *  'env' and following to chain of environment records (represented
917 	 *  by the prototype chain).
918 	 *
919 	 *  If 'env' is NULL, the current activation does not yet have an
920 	 *  allocated declarative environment record; this should be treated
921 	 *  exactly as if the environment record existed but had no bindings
922 	 *  other than register bindings.
923 	 *
924 	 *  Note: we assume that with the DUK_HOBJECT_FLAG_NEWENV cleared
925 	 *  the environment will always be initialized immediately; hence
926 	 *  a NULL 'env' should only happen with the flag set.  This is the
927 	 *  case for: (1) function calls, and (2) strict, direct eval calls.
928 	 */
929 
930 	if (env == NULL && act != NULL) {
931 		duk_hobject *func;
932 
933 		DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference: env is NULL, activation is non-NULL -> "
934 		                     "delayed env case, look up activation regs first"));
935 
936 		/*
937 		 *  Try registers
938 		 */
939 
940 		if (duk__getid_activation_regs(thr, name, act, out)) {
941 			DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
942 			                     "name=%!O -> value=%!T, attrs=%ld, this=%!T, env=%!O, holder=%!O "
943 			                     "(found from register bindings when env=NULL)",
944 			                     (duk_heaphdr *) name, (duk_tval *) out->value,
945 			                     (long) out->attrs, (duk_tval *) out->this_binding,
946 			                     (duk_heaphdr *) out->env, (duk_heaphdr *) out->holder));
947 			return 1;
948 		}
949 
950 		DUK_DDD(DUK_DDDPRINT("not found in current activation regs"));
951 
952 		/*
953 		 *  Not found in registers, proceed to the parent record.
954 		 *  Here we need to determine what the parent would be,
955 		 *  if 'env' was not NULL (i.e. same logic as when initializing
956 		 *  the record).
957 		 *
958 		 *  Note that environment initialization is only deferred when
959 		 *  DUK_HOBJECT_HAS_NEWENV is set, and this only happens for:
960 		 *    - Function code
961 		 *    - Strict eval code
962 		 *
963 		 *  We only need to check _Lexenv here; _Varenv exists only if it
964 		 *  differs from _Lexenv (and thus _Lexenv will also be present).
965 		 */
966 
967 		if (!parents) {
968 			DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference failed, no parent traversal "
969 			                     "(not found from register bindings when env=NULL)"));
970 			goto fail_not_found;
971 		}
972 
973 		func = DUK_ACT_GET_FUNC(act);
974 		DUK_ASSERT(func != NULL);
975 		DUK_ASSERT(DUK_HOBJECT_HAS_NEWENV(func));
976 
977 		tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, func, DUK_HTHREAD_STRING_INT_LEXENV(thr));
978 		if (tv) {
979 			DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv));
980 			env = DUK_TVAL_GET_OBJECT(tv);
981 		} else {
982 			DUK_ASSERT(duk_hobject_find_existing_entry_tval_ptr(thr->heap, func, DUK_HTHREAD_STRING_INT_VARENV(thr)) == NULL);
983 			env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
984 		}
985 
986 		DUK_DDD(DUK_DDDPRINT("continue lookup from env: %!iO",
987 		                     (duk_heaphdr *) env));
988 	}
989 
990 	/*
991 	 *  Prototype walking starting from 'env'.
992 	 *
993 	 *  ('act' is not needed anywhere here.)
994 	 */
995 
996 	sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
997 	while (env != NULL) {
998 		duk_small_int_t cl;
999 		duk_int_t attrs;
1000 
1001 		DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference, name=%!O, considering env=%p -> %!iO",
1002 		                     (duk_heaphdr *) name,
1003 		                     (void *) env,
1004 		                     (duk_heaphdr *) env));
1005 
1006 		DUK_ASSERT(env != NULL);
1007 		DUK_ASSERT(DUK_HOBJECT_IS_ENV(env));
1008 		DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(env));
1009 
1010 		cl = DUK_HOBJECT_GET_CLASS_NUMBER(env);
1011 		DUK_ASSERT(cl == DUK_HOBJECT_CLASS_OBJENV || cl == DUK_HOBJECT_CLASS_DECENV);
1012 		if (cl == DUK_HOBJECT_CLASS_DECENV) {
1013 			/*
1014 			 *  Declarative environment record.
1015 			 *
1016 			 *  Identifiers can never be stored in ancestors and are
1017 			 *  always plain values, so we can use an internal helper
1018 			 *  and access the value directly with an duk_tval ptr.
1019 			 *
1020 			 *  A closed environment is only indicated by it missing
1021 			 *  the "book-keeping" properties required for accessing
1022 			 *  register-bound variables.
1023 			 */
1024 
1025 			if (DUK_HOBJECT_HAS_ENVRECCLOSED(env)) {
1026 				/* already closed */
1027 				goto skip_regs;
1028 			}
1029 
1030 			if (duk__getid_open_decl_env_regs(thr, name, env, out)) {
1031 				DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
1032 				                     "name=%!O -> value=%!T, attrs=%ld, this=%!T, env=%!O, holder=%!O "
1033 				                     "(declarative environment record, scope open, found in regs)",
1034 				                     (duk_heaphdr *) name, (duk_tval *) out->value,
1035 				                     (long) out->attrs, (duk_tval *) out->this_binding,
1036 				                     (duk_heaphdr *) out->env, (duk_heaphdr *) out->holder));
1037 				return 1;
1038 			}
1039 		 skip_regs:
1040 
1041 			tv = duk_hobject_find_existing_entry_tval_ptr_and_attrs(thr->heap, env, name, &attrs);
1042 			if (tv) {
1043 				out->value = tv;
1044 				out->attrs = attrs;
1045 				out->this_binding = NULL;  /* implicit this value always undefined for
1046 				                            * declarative environment records.
1047 				                            */
1048 				out->env = env;
1049 				out->holder = env;
1050 
1051 				DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
1052 				                     "name=%!O -> value=%!T, attrs=%ld, this=%!T, env=%!O, holder=%!O "
1053 				                     "(declarative environment record, found in properties)",
1054 				                     (duk_heaphdr *) name, (duk_tval *) out->value,
1055 				                     (long) out->attrs, (duk_tval *) out->this_binding,
1056 				                     (duk_heaphdr *) out->env, (duk_heaphdr *) out->holder));
1057 				return 1;
1058 			}
1059 		} else {
1060 			/*
1061 			 *  Object environment record.
1062 			 *
1063 			 *  Binding (target) object is an external, uncontrolled object.
1064 			 *  Identifier may be bound in an ancestor property, and may be
1065 			 *  an accessor.  Target can also be a Proxy which we must support
1066 			 *  here.
1067 			 */
1068 
1069 			/* XXX: we could save space by using _Target OR _This.  If _Target, assume
1070 			 * this binding is undefined.  If _This, assumes this binding is _This, and
1071 			 * target is also _This.  One property would then be enough.
1072 			 */
1073 
1074 			duk_hobject *target;
1075 			duk_bool_t found;
1076 
1077 			DUK_ASSERT(cl == DUK_HOBJECT_CLASS_OBJENV);
1078 
1079 			tv_target = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_TARGET(thr));
1080 			DUK_ASSERT(tv_target != NULL);
1081 			DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv_target));
1082 			target = DUK_TVAL_GET_OBJECT(tv_target);
1083 			DUK_ASSERT(target != NULL);
1084 
1085 			/* Target may be a Proxy or property may be an accessor, so we must
1086 			 * use an actual, Proxy-aware hasprop check here.
1087 			 *
1088 			 * out->holder is NOT set to the actual duk_hobject where the
1089 			 * property is found, but rather the object binding target object.
1090 			 */
1091 
1092 			if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(target)) {
1093 				DUK_ASSERT(name != NULL);
1094 				DUK_TVAL_SET_STRING(&tv_name, name);
1095 
1096 				found = duk_hobject_hasprop(thr, tv_target, &tv_name);
1097 			} else {
1098 				/* XXX: duk_hobject_hasprop() would be correct for
1099 				 * non-Proxy objects too, but it is about ~20-25%
1100 				 * slower at present so separate code paths for
1101 				 * Proxy and non-Proxy now.
1102 				 */
1103 				found = duk_hobject_hasprop_raw(thr, target, name);
1104 			}
1105 
1106 			if (found) {
1107 				out->value = NULL;  /* can't get value, may be accessor */
1108 				out->attrs = 0;     /* irrelevant when out->value == NULL */
1109 				tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_THIS(thr));
1110 				out->this_binding = tv;  /* may be NULL */
1111 				out->env = env;
1112 				out->holder = target;
1113 
1114 				DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference successful: "
1115 				                     "name=%!O -> value=%!T, attrs=%ld, this=%!T, env=%!O, holder=%!O "
1116 				                     "(object environment record)",
1117 				                     (duk_heaphdr *) name, (duk_tval *) out->value,
1118 				                     (long) out->attrs, (duk_tval *) out->this_binding,
1119 				                     (duk_heaphdr *) out->env, (duk_heaphdr *) out->holder));
1120 				return 1;
1121 			}
1122 		}
1123 
1124 		if (!parents) {
1125 			DUK_DDD(DUK_DDDPRINT("duk__get_identifier_reference failed, no parent traversal "
1126 			                     "(not found from first traversed env)"));
1127 			goto fail_not_found;
1128 		}
1129 
1130                 if (sanity-- == 0) {
1131                         DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT);
1132                 }
1133 		env = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, env);
1134 	};
1135 
1136 	/*
1137 	 *  Not found (even in global object)
1138 	 */
1139 
1140  fail_not_found:
1141 	return 0;
1142 }
1143 
1144 /*
1145  *  HASVAR: check identifier binding from a given environment record
1146  *  without traversing its parents.
1147  *
1148  *  This primitive is not exposed to user code as such, but is used
1149  *  internally for e.g. declaration binding instantiation.
1150  *
1151  *  See E5 Sections:
1152  *    10.2.1.1.1 HasBinding(N)
1153  *    10.2.1.2.1 HasBinding(N)
1154  *
1155  *  Note: strictness has no bearing on this check.  Hence we don't take
1156  *  a 'strict' parameter.
1157  */
1158 
1159 #if 0  /*unused*/
1160 DUK_INTERNAL
1161 duk_bool_t duk_js_hasvar_envrec(duk_hthread *thr,
1162                                 duk_hobject *env,
1163                                 duk_hstring *name) {
1164 	duk__id_lookup_result ref;
1165 	duk_bool_t parents;
1166 
1167 	DUK_DDD(DUK_DDDPRINT("hasvar: thr=%p, env=%p, name=%!O "
1168 	                     "(env -> %!dO)",
1169 	                     (void *) thr, (void *) env, (duk_heaphdr *) name,
1170 	                     (duk_heaphdr *) env));
1171 
1172 	DUK_ASSERT(thr != NULL);
1173 	DUK_ASSERT(env != NULL);
1174 	DUK_ASSERT(name != NULL);
1175 
1176         DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(env);
1177         DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name);
1178 
1179 	DUK_ASSERT(DUK_HOBJECT_IS_ENV(env));
1180 	DUK_ASSERT(!DUK_HOBJECT_HAS_ARRAY_PART(env));
1181 
1182 	/* lookup results is ignored */
1183 	parents = 0;
1184 	return duk__get_identifier_reference(thr, env, name, NULL, parents, &ref);
1185 }
1186 #endif
1187 
1188 /*
1189  *  GETVAR
1190  *
1191  *  See E5 Sections:
1192  *    11.1.2 Identifier Reference
1193  *    10.3.1 Identifier Resolution
1194  *    11.13.1 Simple Assignment  [example of where the Reference is GetValue'd]
1195  *    8.7.1 GetValue (V)
1196  *    8.12.1 [[GetOwnProperty]] (P)
1197  *    8.12.2 [[GetProperty]] (P)
1198  *    8.12.3 [[Get]] (P)
1199  *
1200  *  If 'throw' is true, always leaves two values on top of stack: [val this].
1201  *
1202  *  If 'throw' is false, returns 0 if identifier cannot be resolved, and the
1203  *  stack will be unaffected in this case.  If identifier is resolved, returns
1204  *  1 and leaves [val this] on top of stack.
1205  *
1206  *  Note: the 'strict' flag of a reference returned by GetIdentifierReference
1207  *  is ignored by GetValue.  Hence we don't take a 'strict' parameter.
1208  *
1209  *  The 'throw' flag is needed for implementing 'typeof' for an unreferenced
1210  *  identifier.  An unreference identifier in other contexts generates a
1211  *  ReferenceError.
1212  */
1213 
1214 DUK_LOCAL
duk__getvar_helper(duk_hthread * thr,duk_hobject * env,duk_activation * act,duk_hstring * name,duk_bool_t throw_flag)1215 duk_bool_t duk__getvar_helper(duk_hthread *thr,
1216                               duk_hobject *env,
1217                               duk_activation *act,
1218                               duk_hstring *name,
1219                               duk_bool_t throw_flag) {
1220 	duk_context *ctx = (duk_context *) thr;
1221 	duk__id_lookup_result ref;
1222 	duk_tval tv_tmp_obj;
1223 	duk_tval tv_tmp_key;
1224 	duk_bool_t parents;
1225 
1226 	DUK_DDD(DUK_DDDPRINT("getvar: thr=%p, env=%p, act=%p, name=%!O "
1227 	                     "(env -> %!dO)",
1228 	                     (void *) thr, (void *) env, (void *) act,
1229 	                     (duk_heaphdr *) name, (duk_heaphdr *) env));
1230 
1231 	DUK_ASSERT(thr != NULL);
1232 	DUK_ASSERT(name != NULL);
1233 	/* env and act may be NULL */
1234 
1235         DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(env);
1236         DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name);
1237 
1238 	parents = 1;     /* follow parent chain */
1239 	if (duk__get_identifier_reference(thr, env, name, act, parents, &ref)) {
1240 		if (ref.value) {
1241 			DUK_ASSERT(ref.this_binding == NULL);  /* always for register bindings */
1242 			duk_push_tval(ctx, ref.value);
1243 			duk_push_undefined(ctx);
1244 		} else {
1245 			DUK_ASSERT(ref.holder != NULL);
1246 
1247 			/* Note: getprop may invoke any getter and invalidate any
1248 			 * duk_tval pointers, so this must be done first.
1249 			 */
1250 
1251 			if (ref.this_binding) {
1252 				duk_push_tval(ctx, ref.this_binding);
1253 			} else {
1254 				duk_push_undefined(ctx);
1255 			}
1256 
1257 			DUK_TVAL_SET_OBJECT(&tv_tmp_obj, ref.holder);
1258 			DUK_TVAL_SET_STRING(&tv_tmp_key, name);
1259 			(void) duk_hobject_getprop(thr, &tv_tmp_obj, &tv_tmp_key);  /* [this value] */
1260 
1261 			/* ref.value, ref.this.binding invalidated here by getprop call */
1262 
1263 			duk_insert(ctx, -2);  /* [this value] -> [value this] */
1264 		}
1265 
1266 		return 1;
1267 	} else {
1268 		if (throw_flag) {
1269 			DUK_ERROR_FMT1(thr, DUK_ERR_REFERENCE_ERROR,
1270 			               "identifier '%s' undefined",
1271 			               (const char *) DUK_HSTRING_GET_DATA(name));
1272 		}
1273 
1274 		return 0;
1275 	}
1276 }
1277 
1278 DUK_INTERNAL
duk_js_getvar_envrec(duk_hthread * thr,duk_hobject * env,duk_hstring * name,duk_bool_t throw_flag)1279 duk_bool_t duk_js_getvar_envrec(duk_hthread *thr,
1280                                 duk_hobject *env,
1281                                 duk_hstring *name,
1282                                 duk_bool_t throw_flag) {
1283 	return duk__getvar_helper(thr, env, NULL, name, throw_flag);
1284 }
1285 
1286 DUK_INTERNAL
duk_js_getvar_activation(duk_hthread * thr,duk_activation * act,duk_hstring * name,duk_bool_t throw_flag)1287 duk_bool_t duk_js_getvar_activation(duk_hthread *thr,
1288                                     duk_activation *act,
1289                                     duk_hstring *name,
1290                                     duk_bool_t throw_flag) {
1291 	DUK_ASSERT(act != NULL);
1292 	return duk__getvar_helper(thr, act->lex_env, act, name, throw_flag);
1293 }
1294 
1295 /*
1296  *  PUTVAR
1297  *
1298  *  See E5 Sections:
1299  *    11.1.2 Identifier Reference
1300  *    10.3.1 Identifier Resolution
1301  *    11.13.1 Simple Assignment  [example of where the Reference is PutValue'd]
1302  *    8.7.2 PutValue (V,W)  [see especially step 3.b, undefined -> automatic global in non-strict mode]
1303  *    8.12.4 [[CanPut]] (P)
1304  *    8.12.5 [[Put]] (P)
1305  *
1306  *  Note: may invalidate any valstack (or object) duk_tval pointers because
1307  *  putting a value may reallocate any object or any valstack.  Caller beware.
1308  */
1309 
1310 DUK_LOCAL
duk__putvar_helper(duk_hthread * thr,duk_hobject * env,duk_activation * act,duk_hstring * name,duk_tval * val,duk_bool_t strict)1311 void duk__putvar_helper(duk_hthread *thr,
1312                         duk_hobject *env,
1313                         duk_activation *act,
1314                         duk_hstring *name,
1315                         duk_tval *val,
1316                         duk_bool_t strict) {
1317 	duk__id_lookup_result ref;
1318 	duk_tval tv_tmp_obj;
1319 	duk_tval tv_tmp_key;
1320 	duk_bool_t parents;
1321 
1322 	DUK_DDD(DUK_DDDPRINT("putvar: thr=%p, env=%p, act=%p, name=%!O, val=%p, strict=%ld "
1323 	                     "(env -> %!dO, val -> %!T)",
1324 	                     (void *) thr, (void *) env, (void *) act,
1325 	                     (duk_heaphdr *) name, (void *) val, (long) strict,
1326 	                     (duk_heaphdr *) env, (duk_tval *) val));
1327 
1328 	DUK_ASSERT(thr != NULL);
1329 	DUK_ASSERT(name != NULL);
1330 	DUK_ASSERT(val != NULL);
1331 	/* env and act may be NULL */
1332 
1333         DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(env);
1334         DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name);
1335 	DUK_ASSERT_REFCOUNT_NONZERO_TVAL(val);
1336 
1337 	/*
1338 	 *  In strict mode E5 protects 'eval' and 'arguments' from being
1339 	 *  assigned to (or even declared anywhere).  Attempt to do so
1340 	 *  should result in a compile time SyntaxError.  See the internal
1341 	 *  design documentation for details.
1342 	 *
1343 	 *  Thus, we should never come here, run-time, for strict code,
1344 	 *  and name 'eval' or 'arguments'.
1345 	 */
1346 
1347 	DUK_ASSERT(!strict ||
1348 	           (name != DUK_HTHREAD_STRING_EVAL(thr) &&
1349 	            name != DUK_HTHREAD_STRING_LC_ARGUMENTS(thr)));
1350 
1351 	/*
1352 	 *  Lookup variable and update in-place if found.
1353 	 */
1354 
1355 	parents = 1;     /* follow parent chain */
1356 
1357 	if (duk__get_identifier_reference(thr, env, name, act, parents, &ref)) {
1358 		if (ref.value && (ref.attrs & DUK_PROPDESC_FLAG_WRITABLE)) {
1359 			/* Update duk_tval in-place if pointer provided and the
1360 			 * property is writable.  If the property is not writable
1361 			 * (immutable binding), use duk_hobject_putprop() which
1362 			 * will respect mutability.
1363 			 */
1364 			duk_tval *tv_val;
1365 
1366 			DUK_ASSERT(ref.this_binding == NULL);  /* always for register bindings */
1367 
1368 			tv_val = ref.value;
1369 			DUK_ASSERT(tv_val != NULL);
1370 			DUK_TVAL_SET_TVAL_UPDREF(thr, tv_val, val);  /* side effects */
1371 
1372 			/* ref.value and ref.this_binding invalidated here */
1373 		} else {
1374 			DUK_ASSERT(ref.holder != NULL);
1375 
1376 			DUK_TVAL_SET_OBJECT(&tv_tmp_obj, ref.holder);
1377 			DUK_TVAL_SET_STRING(&tv_tmp_key, name);
1378 			(void) duk_hobject_putprop(thr, &tv_tmp_obj, &tv_tmp_key, val, strict);
1379 
1380 			/* ref.value and ref.this_binding invalidated here */
1381 		}
1382 
1383 		return;
1384 	}
1385 
1386 	/*
1387 	 *  Not found: write to global object (non-strict) or ReferenceError
1388 	 *  (strict); see E5 Section 8.7.2, step 3.
1389 	 */
1390 
1391 	if (strict) {
1392 		DUK_DDD(DUK_DDDPRINT("identifier binding not found, strict => reference error"));
1393 		DUK_ERROR(thr, DUK_ERR_REFERENCE_ERROR, "identifier not defined");
1394 	}
1395 
1396 	DUK_DDD(DUK_DDDPRINT("identifier binding not found, not strict => set to global"));
1397 
1398 	DUK_TVAL_SET_OBJECT(&tv_tmp_obj, thr->builtins[DUK_BIDX_GLOBAL]);
1399 	DUK_TVAL_SET_STRING(&tv_tmp_key, name);
1400 	(void) duk_hobject_putprop(thr, &tv_tmp_obj, &tv_tmp_key, val, 0);  /* 0 = no throw */
1401 
1402 	/* NB: 'val' may be invalidated here because put_value may realloc valstack,
1403 	 * caller beware.
1404 	 */
1405 }
1406 
1407 DUK_INTERNAL
duk_js_putvar_envrec(duk_hthread * thr,duk_hobject * env,duk_hstring * name,duk_tval * val,duk_bool_t strict)1408 void duk_js_putvar_envrec(duk_hthread *thr,
1409                           duk_hobject *env,
1410                           duk_hstring *name,
1411                           duk_tval *val,
1412                           duk_bool_t strict) {
1413 	duk__putvar_helper(thr, env, NULL, name, val, strict);
1414 }
1415 
1416 DUK_INTERNAL
duk_js_putvar_activation(duk_hthread * thr,duk_activation * act,duk_hstring * name,duk_tval * val,duk_bool_t strict)1417 void duk_js_putvar_activation(duk_hthread *thr,
1418                               duk_activation *act,
1419                               duk_hstring *name,
1420                               duk_tval *val,
1421                               duk_bool_t strict) {
1422 	DUK_ASSERT(act != NULL);
1423 	duk__putvar_helper(thr, act->lex_env, act, name, val, strict);
1424 }
1425 
1426 /*
1427  *  DELVAR
1428  *
1429  *  See E5 Sections:
1430  *    11.4.1 The delete operator
1431  *    10.2.1.1.5 DeleteBinding (N)  [declarative environment record]
1432  *    10.2.1.2.5 DeleteBinding (N)  [object environment record]
1433  *
1434  *  Variable bindings established inside eval() are deletable (configurable),
1435  *  other bindings are not, including variables declared in global level.
1436  *  Registers are always non-deletable, and the deletion of other bindings
1437  *  is controlled by the configurable flag.
1438  *
1439  *  For strict mode code, the 'delete' operator should fail with a compile
1440  *  time SyntaxError if applied to identifiers.  Hence, no strict mode
1441  *  run-time deletion of identifiers should ever happen.  This function
1442  *  should never be called from strict mode code!
1443  */
1444 
1445 DUK_LOCAL
duk__delvar_helper(duk_hthread * thr,duk_hobject * env,duk_activation * act,duk_hstring * name)1446 duk_bool_t duk__delvar_helper(duk_hthread *thr,
1447                               duk_hobject *env,
1448                               duk_activation *act,
1449                               duk_hstring *name) {
1450 	duk__id_lookup_result ref;
1451 	duk_bool_t parents;
1452 
1453 	DUK_DDD(DUK_DDDPRINT("delvar: thr=%p, env=%p, act=%p, name=%!O "
1454 	                     "(env -> %!dO)",
1455 	                     (void *) thr, (void *) env, (void *) act,
1456 	                     (duk_heaphdr *) name, (duk_heaphdr *) env));
1457 
1458 	DUK_ASSERT(thr != NULL);
1459 	DUK_ASSERT(name != NULL);
1460 	/* env and act may be NULL */
1461 
1462         DUK_ASSERT_REFCOUNT_NONZERO_HEAPHDR(name);
1463 
1464 	parents = 1;     /* follow parent chain */
1465 
1466 	if (duk__get_identifier_reference(thr, env, name, act, parents, &ref)) {
1467 		if (ref.value && !(ref.attrs & DUK_PROPDESC_FLAG_CONFIGURABLE)) {
1468 			/* Identifier found in registers (always non-deletable)
1469 			 * or declarative environment record and non-configurable.
1470 			 */
1471 			return 0;
1472 		}
1473 		DUK_ASSERT(ref.holder != NULL);
1474 
1475 		return duk_hobject_delprop_raw(thr, ref.holder, name, 0);
1476 	}
1477 
1478 	/*
1479 	 *  Not found (even in global object).
1480 	 *
1481 	 *  In non-strict mode this is a silent SUCCESS (!), see E5 Section 11.4.1,
1482 	 *  step 3.b.  In strict mode this case is a compile time SyntaxError so
1483 	 *  we should not come here.
1484 	 */
1485 
1486 	DUK_DDD(DUK_DDDPRINT("identifier to be deleted not found: name=%!O "
1487 	                     "(treated as silent success)",
1488 	                     (duk_heaphdr *) name));
1489 	return 1;
1490 }
1491 
1492 #if 0  /*unused*/
1493 DUK_INTERNAL
1494 duk_bool_t duk_js_delvar_envrec(duk_hthread *thr,
1495                                 duk_hobject *env,
1496                                 duk_hstring *name) {
1497 	return duk__delvar_helper(thr, env, NULL, name);
1498 }
1499 #endif
1500 
1501 DUK_INTERNAL
duk_js_delvar_activation(duk_hthread * thr,duk_activation * act,duk_hstring * name)1502 duk_bool_t duk_js_delvar_activation(duk_hthread *thr,
1503                                     duk_activation *act,
1504                                     duk_hstring *name) {
1505 	DUK_ASSERT(act != NULL);
1506 	return duk__delvar_helper(thr, act->lex_env, act, name);
1507 }
1508 
1509 /*
1510  *  DECLVAR
1511  *
1512  *  See E5 Sections:
1513  *    10.4.3 Entering Function Code
1514  *    10.5 Declaration Binding Instantion
1515  *    12.2 Variable Statement
1516  *    11.1.2 Identifier Reference
1517  *    10.3.1 Identifier Resolution
1518  *
1519  *  Variable declaration behavior is mainly discussed in Section 10.5,
1520  *  and is not discussed in the execution semantics (Sections 11-13).
1521  *
1522  *  Conceptually declarations happen when code (global, eval, function)
1523  *  is entered, before any user code is executed.  In practice, register-
1524  *  bound identifiers are 'declared' automatically (by virtue of being
1525  *  allocated to registers with the initial value 'undefined').  Other
1526  *  identifiers are declared in the function prologue with this primitive.
1527  *
1528  *  Since non-register bindings eventually back to an internal object's
1529  *  properties, the 'prop_flags' argument is used to specify binding
1530  *  type:
1531  *
1532  *    - Immutable binding: set DUK_PROPDESC_FLAG_WRITABLE to false
1533  *    - Non-deletable binding: set DUK_PROPDESC_FLAG_CONFIGURABLE to false
1534  *    - The flag DUK_PROPDESC_FLAG_ENUMERABLE should be set, although it
1535  *      doesn't really matter for internal objects
1536  *
1537  *  All bindings are non-deletable mutable bindings except:
1538  *
1539  *    - Declarations in eval code (mutable, deletable)
1540  *    - 'arguments' binding in strict function code (immutable)
1541  *    - Function name binding of a function expression (immutable)
1542  *
1543  *  Declarations may go to declarative environment records (always
1544  *  so for functions), but may also go to object environment records
1545  *  (e.g. global code).  The global object environment has special
1546  *  behavior when re-declaring a function (but not a variable); see
1547  *  E5.1 specification, Section 10.5, step 5.e.
1548  *
1549  *  Declarations always go to the 'top-most' environment record, i.e.
1550  *  we never check the record chain.  It's not an error even if a
1551  *  property (even an immutable or non-deletable one) of the same name
1552  *  already exists.
1553  *
1554  *  If a declared variable already exists, its value needs to be updated
1555  *  (if possible).  Returns 1 if a PUTVAR needs to be done by the caller;
1556  *  otherwise returns 0.
1557  */
1558 
1559 DUK_LOCAL
duk__declvar_helper(duk_hthread * thr,duk_hobject * env,duk_hstring * name,duk_tval * val,duk_small_int_t prop_flags,duk_bool_t is_func_decl)1560 duk_bool_t duk__declvar_helper(duk_hthread *thr,
1561                                duk_hobject *env,
1562                                duk_hstring *name,
1563                                duk_tval *val,
1564                                duk_small_int_t prop_flags,
1565                                duk_bool_t is_func_decl) {
1566 	duk_context *ctx = (duk_context *) thr;
1567 	duk_hobject *holder;
1568 	duk_bool_t parents;
1569 	duk__id_lookup_result ref;
1570 	duk_tval *tv;
1571 
1572 	DUK_DDD(DUK_DDDPRINT("declvar: thr=%p, env=%p, name=%!O, val=%!T, prop_flags=0x%08lx, is_func_decl=%ld "
1573 	                     "(env -> %!iO)",
1574 	                     (void *) thr, (void *) env, (duk_heaphdr *) name,
1575 	                     (duk_tval *) val, (unsigned long) prop_flags,
1576 	                     (unsigned int) is_func_decl, (duk_heaphdr *) env));
1577 
1578 	DUK_ASSERT(thr != NULL);
1579 	DUK_ASSERT(env != NULL);
1580 	DUK_ASSERT(name != NULL);
1581 	DUK_ASSERT(val != NULL);
1582 
1583 	/* Note: in strict mode the compiler should reject explicit
1584 	 * declaration of 'eval' or 'arguments'.  However, internal
1585 	 * bytecode may declare 'arguments' in the function prologue.
1586 	 * We don't bother checking (or asserting) for these now.
1587 	 */
1588 
1589 	/* Note: val is a stable duk_tval pointer.  The caller makes
1590 	 * a value copy into its stack frame, so 'tv_val' is not subject
1591 	 * to side effects here.
1592 	 */
1593 
1594 	/*
1595 	 *  Check whether already declared.
1596 	 *
1597 	 *  We need to check whether the binding exists in the environment
1598 	 *  without walking its parents.  However, we still need to check
1599 	 *  register-bound identifiers and the prototype chain of an object
1600 	 *  environment target object.
1601 	 */
1602 
1603 	parents = 0;  /* just check 'env' */
1604 	if (duk__get_identifier_reference(thr, env, name, NULL, parents, &ref)) {
1605 		duk_int_t e_idx;
1606 		duk_int_t h_idx;
1607 		duk_small_int_t flags;
1608 
1609 		/*
1610 		 *  Variable already declared, ignore re-declaration.
1611 		 *  The only exception is the updated behavior of E5.1 for
1612 		 *  global function declarations, E5.1 Section 10.5, step 5.e.
1613 		 *  This behavior does not apply to global variable declarations.
1614 		 */
1615 
1616 		if (!(is_func_decl && env == thr->builtins[DUK_BIDX_GLOBAL_ENV])) {
1617 			DUK_DDD(DUK_DDDPRINT("re-declare a binding, ignoring"));
1618 			return 1;  /* 1 -> needs a PUTVAR */
1619 		}
1620 
1621 		/*
1622 		 *  Special behavior in E5.1.
1623 		 *
1624 		 *  Note that even though parents == 0, the conflicting property
1625 		 *  may be an inherited property (currently our global object's
1626 		 *  prototype is Object.prototype).  Step 5.e first operates on
1627 		 *  the existing property (which is potentially in an ancestor)
1628 		 *  and then defines a new property in the global object (and
1629 		 *  never modifies the ancestor).
1630 		 *
1631 		 *  Also note that this logic would become even more complicated
1632 		 *  if the conflicting property might be a virtual one.  Object
1633 		 *  prototype has no virtual properties, though.
1634 		 *
1635 		 *  XXX: this is now very awkward, rework.
1636 		 */
1637 
1638 		DUK_DDD(DUK_DDDPRINT("re-declare a function binding in global object, "
1639 		                     "updated E5.1 processing"));
1640 
1641 		DUK_ASSERT(ref.holder != NULL);
1642 		holder = ref.holder;
1643 
1644 		/* holder will be set to the target object, not the actual object
1645 		 * where the property was found (see duk__get_identifier_reference()).
1646 		 */
1647 		DUK_ASSERT(DUK_HOBJECT_GET_CLASS_NUMBER(holder) == DUK_HOBJECT_CLASS_GLOBAL);
1648 		DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(holder));  /* global object doesn't have array part */
1649 
1650 		/* XXX: use a helper for prototype traversal; no loop check here */
1651 		/* must be found: was found earlier, and cannot be inherited */
1652 		for (;;) {
1653 			DUK_ASSERT(holder != NULL);
1654 			duk_hobject_find_existing_entry(thr->heap, holder, name, &e_idx, &h_idx);
1655 			if (e_idx >= 0) {
1656 				break;
1657 			}
1658 			/* SCANBUILD: NULL pointer dereference, doesn't actually trigger,
1659 			 * asserted above.
1660 			 */
1661 			holder = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, holder);
1662 		}
1663 		DUK_ASSERT(holder != NULL);
1664 		DUK_ASSERT(e_idx >= 0);
1665 		/* SCANBUILD: scan-build produces a NULL pointer dereference warning
1666 		 * below; it never actually triggers because holder is actually never
1667 		 * NULL.
1668 		 */
1669 
1670 		/* ref.holder is global object, holder is the object with the
1671 		 * conflicting property.
1672 		 */
1673 
1674 		flags = DUK_HOBJECT_E_GET_FLAGS(thr->heap, holder, e_idx);
1675 		if (!(flags & DUK_PROPDESC_FLAG_CONFIGURABLE)) {
1676 			if (flags & DUK_PROPDESC_FLAG_ACCESSOR) {
1677 				DUK_DDD(DUK_DDDPRINT("existing property is a non-configurable "
1678 				                     "accessor -> reject"));
1679 				goto fail_existing_attributes;
1680 			}
1681 			if (!((flags & DUK_PROPDESC_FLAG_WRITABLE) &&
1682 			      (flags & DUK_PROPDESC_FLAG_ENUMERABLE))) {
1683 				DUK_DDD(DUK_DDDPRINT("existing property is a non-configurable "
1684 				                     "plain property which is not writable and "
1685 				                     "enumerable -> reject"));
1686 				goto fail_existing_attributes;
1687 			}
1688 
1689 			DUK_DDD(DUK_DDDPRINT("existing property is not configurable but "
1690 			                     "is plain, enumerable, and writable -> "
1691 			                     "allow redeclaration"));
1692 		}
1693 
1694 		if (holder == ref.holder) {
1695 			/* XXX: if duk_hobject_define_property_internal() was updated
1696 			 * to handle a pre-existing accessor property, this would be
1697 			 * a simple call (like for the ancestor case).
1698 			 */
1699 			DUK_DDD(DUK_DDDPRINT("redefine, offending property in global object itself"));
1700 
1701 			if (flags & DUK_PROPDESC_FLAG_ACCESSOR) {
1702 				duk_hobject *tmp;
1703 
1704 				tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, holder, e_idx);
1705 				DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, holder, e_idx, NULL);
1706 				DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp);
1707 				DUK_UNREF(tmp);
1708 				tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, holder, e_idx);
1709 				DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, holder, e_idx, NULL);
1710 				DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp);
1711 				DUK_UNREF(tmp);
1712 			} else {
1713 				tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, holder, e_idx);
1714 				DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv);
1715 			}
1716 
1717 			/* Here val would be potentially invalid if we didn't make
1718 			 * a value copy at the caller.
1719 			 */
1720 
1721 			tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, holder, e_idx);
1722 			DUK_TVAL_SET_TVAL(tv, val);
1723 			DUK_TVAL_INCREF(thr, tv);
1724 			DUK_HOBJECT_E_SET_FLAGS(thr->heap, holder, e_idx, prop_flags);
1725 
1726 			DUK_DDD(DUK_DDDPRINT("updated global binding, final result: "
1727 			                     "value -> %!T, prop_flags=0x%08lx",
1728 			                     (duk_tval *) DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, holder, e_idx),
1729 			                     (unsigned long) prop_flags));
1730 		} else {
1731 			DUK_DDD(DUK_DDDPRINT("redefine, offending property in ancestor"));
1732 
1733 			DUK_ASSERT(ref.holder == thr->builtins[DUK_BIDX_GLOBAL]);
1734 			duk_push_tval(ctx, val);
1735 			duk_hobject_define_property_internal(thr, ref.holder, name, prop_flags);
1736 		}
1737 
1738 		return 0;
1739 	}
1740 
1741 	/*
1742 	 *  Not found (in registers or record objects).  Declare
1743 	 *  to current variable environment.
1744 	 */
1745 
1746 	/*
1747 	 *  Get holder object
1748 	 */
1749 
1750 	if (DUK_HOBJECT_IS_DECENV(env)) {
1751 		holder = env;
1752 	} else {
1753 		DUK_ASSERT(DUK_HOBJECT_IS_OBJENV(env));
1754 
1755 		tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, env, DUK_HTHREAD_STRING_INT_TARGET(thr));
1756 		DUK_ASSERT(tv != NULL);
1757 		DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv));
1758 		holder = DUK_TVAL_GET_OBJECT(tv);
1759 		DUK_ASSERT(holder != NULL);
1760 	}
1761 
1762 	/*
1763 	 *  Define new property
1764 	 *
1765 	 *  Note: this may fail if the holder is not extensible.
1766 	 */
1767 
1768 	/* XXX: this is awkward as we use an internal method which doesn't handle
1769 	 * extensibility etc correctly.  Basically we'd want to do a [[DefineOwnProperty]]
1770 	 * or Object.defineProperty() here.
1771 	 */
1772 
1773 	if (!DUK_HOBJECT_HAS_EXTENSIBLE(holder)) {
1774 		goto fail_not_extensible;
1775 	}
1776 
1777 	duk_push_hobject(ctx, holder);
1778 	duk_push_hstring(ctx, name);
1779 	duk_push_tval(ctx, val);
1780 	duk_xdef_prop(ctx, -3, prop_flags);  /* [holder name val] -> [holder] */
1781 	duk_pop(ctx);
1782 
1783 	return 0;
1784 
1785  fail_existing_attributes:
1786  fail_not_extensible:
1787 	DUK_ERROR_TYPE(thr, "declaration failed");
1788 	return 0;
1789 }
1790 
1791 DUK_INTERNAL
duk_js_declvar_activation(duk_hthread * thr,duk_activation * act,duk_hstring * name,duk_tval * val,duk_small_int_t prop_flags,duk_bool_t is_func_decl)1792 duk_bool_t duk_js_declvar_activation(duk_hthread *thr,
1793                                      duk_activation *act,
1794                                      duk_hstring *name,
1795                                      duk_tval *val,
1796                                      duk_small_int_t prop_flags,
1797                                      duk_bool_t is_func_decl) {
1798 	duk_hobject *env;
1799 	duk_tval tv_val_copy;
1800 	duk_size_t act_off;
1801 
1802 	DUK_ASSERT(act != NULL);
1803 	act_off = (duk_size_t) ((duk_uint8_t *) act - (duk_uint8_t *) thr->callstack);
1804 
1805 	/*
1806 	 *  Make a value copy of the input val.  This ensures that
1807 	 *  side effects cannot invalidate the pointer.
1808 	 */
1809 
1810 	DUK_TVAL_SET_TVAL(&tv_val_copy, val);
1811 	val = &tv_val_copy;
1812 
1813 	/*
1814 	 *  Delayed env creation check
1815 	 */
1816 
1817 	if (!act->var_env) {
1818 		DUK_ASSERT(act->lex_env == NULL);
1819 		duk_js_init_activation_environment_records_delayed(thr, act);
1820 		act = (duk_activation *) ((duk_uint8_t *) thr->callstack + act_off);
1821 	}
1822 	DUK_ASSERT(act->lex_env != NULL);
1823 	DUK_ASSERT(act->var_env != NULL);
1824 
1825 	env = act->var_env;
1826 	DUK_ASSERT(env != NULL);
1827 	DUK_ASSERT(DUK_HOBJECT_IS_ENV(env));
1828 
1829 	return duk__declvar_helper(thr, env, name, val, prop_flags, is_func_decl);
1830 }
1831