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