]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* |
2 | * Function built-ins | |
3 | */ | |
4 | ||
5 | #include "duk_internal.h" | |
6 | ||
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 | ||
11fdf7f2 | 77 | duk_js_push_closure(thr, func, outer_var_env, outer_lex_env, 1 /*add_auto_proto*/); |
7c673cae FG |
78 | |
79 | /* [ body formals source template closure ] */ | |
80 | ||
81 | return 1; | |
82 | } | |
83 | ||
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 | ||
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); | |
11fdf7f2 | 117 | const char *func_name; |
7c673cae | 118 | |
11fdf7f2 TL |
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. | |
7c673cae | 124 | */ |
7c673cae | 125 | duk_get_prop_stridx(ctx, -1, DUK_STRIDX_NAME); |
11fdf7f2 TL |
126 | if (duk_is_undefined(ctx, -1)) { |
127 | func_name = ""; | |
128 | } else { | |
7c673cae FG |
129 | func_name = duk_to_string(ctx, -1); |
130 | DUK_ASSERT(func_name != NULL); | |
7c673cae FG |
131 | } |
132 | ||
11fdf7f2 TL |
133 | /* Indicate function type in the function body using a dummy |
134 | * directive. | |
135 | */ | |
7c673cae | 136 | if (DUK_HOBJECT_HAS_COMPILEDFUNCTION(obj)) { |
11fdf7f2 | 137 | duk_push_sprintf(ctx, "function %s() {\"ecmascript\"}", (const char *) func_name); |
7c673cae | 138 | } else if (DUK_HOBJECT_HAS_NATIVEFUNCTION(obj)) { |
11fdf7f2 | 139 | duk_push_sprintf(ctx, "function %s() {\"native\"}", (const char *) func_name); |
7c673cae | 140 | } else if (DUK_HOBJECT_HAS_BOUND(obj)) { |
11fdf7f2 | 141 | duk_push_sprintf(ctx, "function %s() {\"bound\"}", (const char *) func_name); |
7c673cae FG |
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 | ||
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 | ||
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 | */ | |
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 | } |