5 #include "duk_internal.h"
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
;
12 duk_small_uint_t comp_flags
;
13 duk_hcompiledfunction
*func
;
14 duk_hobject
*outer_lex_env
;
15 duk_hobject
*outer_var_env
;
17 /* normal and constructor calls have identical semantics */
19 nargs
= duk_get_top(ctx
);
20 for (i
= 0; i
< nargs
; i
++) {
21 duk_to_string(ctx
, i
);
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
, "");
31 duk_insert(ctx
, 0); /* [ arg1 ... argN-1 body] -> [body arg1 ... argN-1] */
32 duk_push_string(ctx
, ",");
34 duk_join(ctx
, nargs
- 1);
37 /* [ body formals ], formals is comma separated list that needs to be parsed */
39 DUK_ASSERT_TOP(ctx
, 2);
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.
44 duk_push_string(ctx
, "function(");
46 duk_push_string(ctx
, "){");
48 duk_push_string(ctx
, "}");
51 /* [ body formals source ] */
53 DUK_ASSERT_TOP(ctx
, 3);
55 /* strictness is not inherited, intentional */
56 comp_flags
= DUK_JS_COMPILE_FLAG_FUNCEXPR
;
58 duk_push_hstring_stridx(ctx
, DUK_STRIDX_COMPILE
); /* XXX: copy from caller? */ /* XXX: ignored now */
59 h_sourcecode
= duk_require_hstring(ctx
, -2);
61 (const duk_uint8_t
*) DUK_HSTRING_GET_DATA(h_sourcecode
),
62 (duk_size_t
) DUK_HSTRING_GET_BYTELEN(h_sourcecode
),
64 func
= (duk_hcompiledfunction
*) duk_get_hobject(ctx
, -1);
65 DUK_ASSERT(func
!= NULL
);
66 DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject
*) func
));
68 /* [ body formals source template ] */
70 /* only outer_lex_env matters, as functions always get a new
71 * variable declaration environment.
74 outer_lex_env
= thr
->builtins
[DUK_BIDX_GLOBAL_ENV
];
75 outer_var_env
= thr
->builtins
[DUK_BIDX_GLOBAL_ENV
];
77 duk_js_push_closure(thr
, func
, outer_var_env
, outer_lex_env
, 1 /*add_auto_proto*/);
79 /* [ body formals source template closure ] */
84 DUK_INTERNAL duk_ret_t
duk_bi_function_prototype(duk_context
*ctx
) {
85 /* ignore arguments, return undefined (E5 Section 15.3.4) */
90 DUK_INTERNAL duk_ret_t
duk_bi_function_prototype_to_string(duk_context
*ctx
) {
94 * E5 Section 15.3.4.2 places few requirements on the output of
97 * - The result is an implementation dependent representation
98 * of the function; in particular
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
105 * - Note in particular that the output does NOT need to compile
106 * into anything useful.
110 /* XXX: faster internal way to get this */
112 tv
= duk_get_tval(ctx
, -1);
113 DUK_ASSERT(tv
!= NULL
);
115 if (DUK_TVAL_IS_OBJECT(tv
)) {
116 duk_hobject
*obj
= DUK_TVAL_GET_OBJECT(tv
);
117 const char *func_name
;
119 /* Function name: missing/undefined is mapped to empty string,
120 * otherwise coerce to string.
122 /* XXX: currently no handling for non-allowed identifier characters,
123 * e.g. a '{' in the function name.
125 duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_NAME
);
126 if (duk_is_undefined(ctx
, -1)) {
129 func_name
= duk_to_string(ctx
, -1);
130 DUK_ASSERT(func_name
!= NULL
);
133 /* Indicate function type in the function body using a dummy
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
);
145 } else if (DUK_TVAL_IS_LIGHTFUNC(tv
)) {
146 duk_push_lightfunc_tostring(ctx
, tv
);
154 return DUK_RET_TYPE_ERROR
;
157 DUK_INTERNAL duk_ret_t
duk_bi_function_prototype_apply(duk_context
*ctx
) {
161 DUK_ASSERT_TOP(ctx
, 2); /* not a vararg function */
164 if (!duk_is_callable(ctx
, -1)) {
165 DUK_DDD(DUK_DDDPRINT("func is not callable"));
169 DUK_ASSERT_TOP(ctx
, 3);
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)));
176 /* [ func thisArg argArray ] */
178 if (duk_is_null_or_undefined(ctx
, 2)) {
179 DUK_DDD(DUK_DDDPRINT("argArray is null/undefined, no args"));
181 } else if (!duk_is_object(ctx
, 2)) {
184 DUK_DDD(DUK_DDDPRINT("argArray is an object"));
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 */
191 duk_require_stack(ctx
, len
);
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
);
199 DUK_ASSERT_TOP(ctx
, 2 + len
);
201 /* [ func thisArg arg1 ... argN ] */
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),
207 duk_call_method(ctx
, len
);
211 return DUK_RET_TYPE_ERROR
;
214 DUK_INTERNAL duk_ret_t
duk_bi_function_prototype_call(duk_context
*ctx
) {
217 /* Step 1 is not necessary because duk_call_method() will take
221 /* vararg function, thisArg needs special handling */
222 nargs
= duk_get_top(ctx
); /* = 1 + arg count */
224 duk_push_undefined(ctx
);
227 DUK_ASSERT(nargs
>= 1);
229 /* [ thisArg arg1 ... argN ] */
231 duk_push_this(ctx
); /* 'func' in the algorithm */
234 /* [ func thisArg arg1 ... argN ] */
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),
240 (long) duk_get_top(ctx
)));
241 duk_call_method(ctx
, nargs
- 1);
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.
251 DUK_INTERNAL duk_ret_t
duk_bi_function_prototype_bind(duk_context
*ctx
) {
252 duk_hobject
*h_bound
;
253 duk_hobject
*h_target
;
257 /* vararg function, careful arg handling (e.g. thisArg may not be present) */
258 nargs
= duk_get_top(ctx
); /* = 1 + arg count */
260 duk_push_undefined(ctx
);
263 DUK_ASSERT(nargs
>= 1);
266 if (!duk_is_callable(ctx
, -1)) {
267 DUK_DDD(DUK_DDDPRINT("func is not callable"));
271 /* [ thisArg arg1 ... argN func ] (thisArg+args == nargs total) */
272 DUK_ASSERT_TOP(ctx
, nargs
+ 1);
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
);
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
);
288 duk_dup(ctx
, 0); /* thisArg */
289 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_INT_THIS
, DUK_PROPDESC_FLAGS_NONE
);
293 /* [ thisArg arg1 ... argN func boundFunc argArray ] */
295 for (i
= 0; i
< nargs
- 1; i
++) {
297 duk_put_prop_index(ctx
, -2, i
);
299 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_INT_ARGS
, DUK_PROPDESC_FLAGS_NONE
);
301 /* [ thisArg arg1 ... argN func boundFunc ] */
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. */
309 duk_get_prop_stridx(ctx
, -2, DUK_STRIDX_LENGTH
);
310 tmp
= duk_to_int(ctx
, -1) - (nargs
- 1); /* step 15.a */
312 duk_push_int(ctx
, (tmp
< 0 ? 0 : tmp
));
314 duk_push_int(ctx
, 0);
316 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_LENGTH
, DUK_PROPDESC_FLAGS_NONE
); /* attrs in E5 Section 15.3.5.1 */
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
);
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
);
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.
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
);
340 DUK_DDD(DUK_DDDPRINT("created bound function: %!iT", (duk_tval
*) duk_get_tval(ctx
, -1)));
345 return DUK_RET_TYPE_ERROR
;