1 /*
2  *  Function built-ins
3  */
4 
5 #include "duk_internal.h"
6 
duk_bi_function_constructor(duk_context * ctx)7 DUK_INTERNAL duk_ret_t duk_bi_function_constructor(duk_context *ctx) {
8 	duk_hthread *thr = (duk_hthread *) ctx;
9 	duk_hstring *h_sourcecode;
10 	duk_idx_t nargs;
11 	duk_idx_t i;
12 	duk_small_uint_t comp_flags;
13 	duk_hcompiledfunction *func;
14 	duk_hobject *outer_lex_env;
15 	duk_hobject *outer_var_env;
16 
17 	/* normal and constructor calls have identical semantics */
18 
19 	nargs = duk_get_top(ctx);
20 	for (i = 0; i < nargs; i++) {
21 		duk_to_string(ctx, i);
22 	}
23 
24 	if (nargs == 0) {
25 		duk_push_string(ctx, "");
26 		duk_push_string(ctx, "");
27 	} else if (nargs == 1) {
28 		/* XXX: cover this with the generic >1 case? */
29 		duk_push_string(ctx, "");
30 	} else {
31 		duk_insert(ctx, 0);   /* [ arg1 ... argN-1 body] -> [body arg1 ... argN-1] */
32 		duk_push_string(ctx, ",");
33 		duk_insert(ctx, 1);
34 		duk_join(ctx, nargs - 1);
35 	}
36 
37 	/* [ body formals ], formals is comma separated list that needs to be parsed */
38 
39 	DUK_ASSERT_TOP(ctx, 2);
40 
41 	/* XXX: this placeholder is not always correct, but use for now.
42 	 * It will fail in corner cases; see test-dev-func-cons-args.js.
43 	 */
44 	duk_push_string(ctx, "function(");
45 	duk_dup(ctx, 1);
46 	duk_push_string(ctx, "){");
47 	duk_dup(ctx, 0);
48 	duk_push_string(ctx, "}");
49 	duk_concat(ctx, 5);
50 
51 	/* [ body formals source ] */
52 
53 	DUK_ASSERT_TOP(ctx, 3);
54 
55 	/* strictness is not inherited, intentional */
56 	comp_flags = DUK_JS_COMPILE_FLAG_FUNCEXPR;
57 
58 	duk_push_hstring_stridx(ctx, DUK_STRIDX_COMPILE);  /* XXX: copy from caller? */  /* XXX: ignored now */
59 	h_sourcecode = duk_require_hstring(ctx, -2);
60 	duk_js_compile(thr,
61 	               (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_sourcecode),
62 	               (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sourcecode),
63 	               comp_flags);
64 	func = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1);
65 	DUK_ASSERT(func != NULL);
66 	DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) func));
67 
68 	/* [ body formals source template ] */
69 
70 	/* only outer_lex_env matters, as functions always get a new
71 	 * variable declaration environment.
72 	 */
73 
74 	outer_lex_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
75 	outer_var_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
76 
77 	duk_js_push_closure(thr, func, outer_var_env, outer_lex_env, 1 /*add_auto_proto*/);
78 
79 	/* [ body formals source template closure ] */
80 
81 	return 1;
82 }
83 
duk_bi_function_prototype(duk_context * ctx)84 DUK_INTERNAL duk_ret_t duk_bi_function_prototype(duk_context *ctx) {
85 	/* ignore arguments, return undefined (E5 Section 15.3.4) */
86 	DUK_UNREF(ctx);
87 	return 0;
88 }
89 
duk_bi_function_prototype_to_string(duk_context * ctx)90 DUK_INTERNAL duk_ret_t duk_bi_function_prototype_to_string(duk_context *ctx) {
91 	duk_tval *tv;
92 
93 	/*
94 	 *  E5 Section 15.3.4.2 places few requirements on the output of
95 	 *  this function:
96 	 *
97 	 *    - The result is an implementation dependent representation
98 	 *      of the function; in particular
99 	 *
100 	 *    - The result must follow the syntax of a FunctionDeclaration.
101 	 *      In particular, the function must have a name (even in the
102 	 *      case of an anonymous function or a function with an empty
103 	 *      name).
104 	 *
105 	 *    - Note in particular that the output does NOT need to compile
106 	 *      into anything useful.
107 	 */
108 
109 
110 	/* XXX: faster internal way to get this */
111 	duk_push_this(ctx);
112 	tv = duk_get_tval(ctx, -1);
113 	DUK_ASSERT(tv != NULL);
114 
115 	if (DUK_TVAL_IS_OBJECT(tv)) {
116 		duk_hobject *obj = DUK_TVAL_GET_OBJECT(tv);
117 		const char *func_name;
118 
119 		/* Function name: missing/undefined is mapped to empty string,
120 		 * otherwise coerce to string.
121 		 */
122 		/* XXX: currently no handling for non-allowed identifier characters,
123 		 * e.g. a '{' in the function name.
124 		 */
125 		duk_get_prop_stridx(ctx, -1, DUK_STRIDX_NAME);
126 		if (duk_is_undefined(ctx, -1)) {
127 			func_name = "";
128 		} else {
129 			func_name = duk_to_string(ctx, -1);
130 			DUK_ASSERT(func_name != NULL);
131 		}
132 
133 		/* Indicate function type in the function body using a dummy
134 		 * directive.
135 		 */
136 		if (DUK_HOBJECT_HAS_COMPILEDFUNCTION(obj)) {
137 			duk_push_sprintf(ctx, "function %s() {\"ecmascript\"}", (const char *) func_name);
138 		} else if (DUK_HOBJECT_HAS_NATIVEFUNCTION(obj)) {
139 			duk_push_sprintf(ctx, "function %s() {\"native\"}", (const char *) func_name);
140 		} else if (DUK_HOBJECT_HAS_BOUND(obj)) {
141 			duk_push_sprintf(ctx, "function %s() {\"bound\"}", (const char *) func_name);
142 		} else {
143 			goto type_error;
144 		}
145 	} else if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
146 		duk_push_lightfunc_tostring(ctx, tv);
147 	} else {
148 		goto type_error;
149 	}
150 
151 	return 1;
152 
153  type_error:
154 	return DUK_RET_TYPE_ERROR;
155 }
156 
duk_bi_function_prototype_apply(duk_context * ctx)157 DUK_INTERNAL duk_ret_t duk_bi_function_prototype_apply(duk_context *ctx) {
158 	duk_idx_t len;
159 	duk_idx_t i;
160 
161 	DUK_ASSERT_TOP(ctx, 2);  /* not a vararg function */
162 
163 	duk_push_this(ctx);
164 	if (!duk_is_callable(ctx, -1)) {
165 		DUK_DDD(DUK_DDDPRINT("func is not callable"));
166 		goto type_error;
167 	}
168 	duk_insert(ctx, 0);
169 	DUK_ASSERT_TOP(ctx, 3);
170 
171 	DUK_DDD(DUK_DDDPRINT("func=%!iT, thisArg=%!iT, argArray=%!iT",
172 	                     (duk_tval *) duk_get_tval(ctx, 0),
173 	                     (duk_tval *) duk_get_tval(ctx, 1),
174 	                     (duk_tval *) duk_get_tval(ctx, 2)));
175 
176 	/* [ func thisArg argArray ] */
177 
178 	if (duk_is_null_or_undefined(ctx, 2)) {
179 		DUK_DDD(DUK_DDDPRINT("argArray is null/undefined, no args"));
180 		len = 0;
181 	} else if (!duk_is_object(ctx, 2)) {
182 		goto type_error;
183 	} else {
184 		DUK_DDD(DUK_DDDPRINT("argArray is an object"));
185 
186 		/* XXX: make this an internal helper */
187 		duk_get_prop_stridx(ctx, 2, DUK_STRIDX_LENGTH);
188 		len = (duk_idx_t) duk_to_uint32(ctx, -1);  /* ToUint32() coercion required */
189 		duk_pop(ctx);
190 
191 		duk_require_stack(ctx, len);
192 
193 		DUK_DDD(DUK_DDDPRINT("argArray length is %ld", (long) len));
194 		for (i = 0; i < len; i++) {
195 			duk_get_prop_index(ctx, 2, i);
196 		}
197 	}
198 	duk_remove(ctx, 2);
199 	DUK_ASSERT_TOP(ctx, 2 + len);
200 
201 	/* [ func thisArg arg1 ... argN ] */
202 
203 	DUK_DDD(DUK_DDDPRINT("apply, func=%!iT, thisArg=%!iT, len=%ld",
204 	                     (duk_tval *) duk_get_tval(ctx, 0),
205 	                     (duk_tval *) duk_get_tval(ctx, 1),
206 	                     (long) len));
207 	duk_call_method(ctx, len);
208 	return 1;
209 
210  type_error:
211 	return DUK_RET_TYPE_ERROR;
212 }
213 
duk_bi_function_prototype_call(duk_context * ctx)214 DUK_INTERNAL duk_ret_t duk_bi_function_prototype_call(duk_context *ctx) {
215 	duk_idx_t nargs;
216 
217 	/* Step 1 is not necessary because duk_call_method() will take
218 	 * care of it.
219 	 */
220 
221 	/* vararg function, thisArg needs special handling */
222 	nargs = duk_get_top(ctx);  /* = 1 + arg count */
223 	if (nargs == 0) {
224 		duk_push_undefined(ctx);
225 		nargs++;
226 	}
227 	DUK_ASSERT(nargs >= 1);
228 
229 	/* [ thisArg arg1 ... argN ] */
230 
231 	duk_push_this(ctx);  /* 'func' in the algorithm */
232 	duk_insert(ctx, 0);
233 
234 	/* [ func thisArg arg1 ... argN ] */
235 
236 	DUK_DDD(DUK_DDDPRINT("func=%!iT, thisArg=%!iT, argcount=%ld, top=%ld",
237 	                     (duk_tval *) duk_get_tval(ctx, 0),
238 	                     (duk_tval *) duk_get_tval(ctx, 1),
239 	                     (long) (nargs - 1),
240 	                     (long) duk_get_top(ctx)));
241 	duk_call_method(ctx, nargs - 1);
242 	return 1;
243 }
244 
245 /* XXX: the implementation now assumes "chained" bound functions,
246  * whereas "collapsed" bound functions (where there is ever only
247  * one bound function which directly points to a non-bound, final
248  * function) would require a "collapsing" implementation which
249  * merges argument lists etc here.
250  */
duk_bi_function_prototype_bind(duk_context * ctx)251 DUK_INTERNAL duk_ret_t duk_bi_function_prototype_bind(duk_context *ctx) {
252 	duk_hobject *h_bound;
253 	duk_hobject *h_target;
254 	duk_idx_t nargs;
255 	duk_idx_t i;
256 
257 	/* vararg function, careful arg handling (e.g. thisArg may not be present) */
258 	nargs = duk_get_top(ctx);  /* = 1 + arg count */
259 	if (nargs == 0) {
260 		duk_push_undefined(ctx);
261 		nargs++;
262 	}
263 	DUK_ASSERT(nargs >= 1);
264 
265 	duk_push_this(ctx);
266 	if (!duk_is_callable(ctx, -1)) {
267 		DUK_DDD(DUK_DDDPRINT("func is not callable"));
268 		goto type_error;
269 	}
270 
271 	/* [ thisArg arg1 ... argN func ]  (thisArg+args == nargs total) */
272 	DUK_ASSERT_TOP(ctx, nargs + 1);
273 
274 	/* create bound function object */
275 	duk_push_object_helper(ctx,
276 	                       DUK_HOBJECT_FLAG_EXTENSIBLE |
277 	                       DUK_HOBJECT_FLAG_BOUND |
278 	                       DUK_HOBJECT_FLAG_CONSTRUCTABLE |
279 	                       DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION),
280 	                       DUK_BIDX_FUNCTION_PROTOTYPE);
281 	h_bound = duk_get_hobject(ctx, -1);
282 	DUK_ASSERT(h_bound != NULL);
283 
284 	/* [ thisArg arg1 ... argN func boundFunc ] */
285 	duk_dup(ctx, -2);  /* func */
286 	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE);
287 
288 	duk_dup(ctx, 0);   /* thisArg */
289 	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_THIS, DUK_PROPDESC_FLAGS_NONE);
290 
291 	duk_push_array(ctx);
292 
293 	/* [ thisArg arg1 ... argN func boundFunc argArray ] */
294 
295 	for (i = 0; i < nargs - 1; i++) {
296 		duk_dup(ctx, 1 + i);
297 		duk_put_prop_index(ctx, -2, i);
298 	}
299 	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_ARGS, DUK_PROPDESC_FLAGS_NONE);
300 
301 	/* [ thisArg arg1 ... argN func boundFunc ] */
302 
303 	/* bound function 'length' property is interesting */
304 	h_target = duk_get_hobject(ctx, -2);
305 	if (h_target == NULL ||  /* lightfunc */
306 	    DUK_HOBJECT_GET_CLASS_NUMBER(h_target) == DUK_HOBJECT_CLASS_FUNCTION) {
307 		/* For lightfuncs, simply read the virtual property. */
308 		duk_int_t tmp;
309 		duk_get_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH);
310 		tmp = duk_to_int(ctx, -1) - (nargs - 1);  /* step 15.a */
311 		duk_pop(ctx);
312 		duk_push_int(ctx, (tmp < 0 ? 0 : tmp));
313 	} else {
314 		duk_push_int(ctx, 0);
315 	}
316 	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE);  /* attrs in E5 Section 15.3.5.1 */
317 
318 	/* caller and arguments must use the same thrower, [[ThrowTypeError]] */
319 	duk_xdef_prop_stridx_thrower(ctx, -1, DUK_STRIDX_CALLER, DUK_PROPDESC_FLAGS_NONE);
320 	duk_xdef_prop_stridx_thrower(ctx, -1, DUK_STRIDX_LC_ARGUMENTS, DUK_PROPDESC_FLAGS_NONE);
321 
322 	/* these non-standard properties are copied for convenience */
323 	/* XXX: 'copy properties' API call? */
324 	duk_get_prop_stridx(ctx, -2, DUK_STRIDX_NAME);
325 	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_WC);
326 	duk_get_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME);
327 	duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC);
328 
329 	/* The 'strict' flag is copied to get the special [[Get]] of E5.1
330 	 * Section 15.3.5.4 to apply when a 'caller' value is a strict bound
331 	 * function.  Not sure if this is correct, because the specification
332 	 * is a bit ambiguous on this point but it would make sense.
333 	 */
334 	if (h_target == NULL) {
335 		/* Lightfuncs are always strict. */
336 		DUK_HOBJECT_SET_STRICT(h_bound);
337 	} else if (DUK_HOBJECT_HAS_STRICT(h_target)) {
338 		DUK_HOBJECT_SET_STRICT(h_bound);
339 	}
340 	DUK_DDD(DUK_DDDPRINT("created bound function: %!iT", (duk_tval *) duk_get_tval(ctx, -1)));
341 
342 	return 1;
343 
344  type_error:
345 	return DUK_RET_TYPE_ERROR;
346 }
347