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