| /* |
| * Function built-ins |
| */ |
| |
| #include "duk_internal.h" |
| |
| DUK_INTERNAL duk_ret_t duk_bi_function_constructor(duk_context *ctx) { |
| duk_hthread *thr = (duk_hthread *) ctx; |
| duk_hstring *h_sourcecode; |
| duk_idx_t nargs; |
| duk_idx_t i; |
| duk_small_uint_t comp_flags; |
| duk_hcompiledfunction *func; |
| duk_hobject *outer_lex_env; |
| duk_hobject *outer_var_env; |
| |
| /* normal and constructor calls have identical semantics */ |
| |
| nargs = duk_get_top(ctx); |
| for (i = 0; i < nargs; i++) { |
| duk_to_string(ctx, i); |
| } |
| |
| if (nargs == 0) { |
| duk_push_string(ctx, ""); |
| duk_push_string(ctx, ""); |
| } else if (nargs == 1) { |
| /* XXX: cover this with the generic >1 case? */ |
| duk_push_string(ctx, ""); |
| } else { |
| duk_insert(ctx, 0); /* [ arg1 ... argN-1 body] -> [body arg1 ... argN-1] */ |
| duk_push_string(ctx, ","); |
| duk_insert(ctx, 1); |
| duk_join(ctx, nargs - 1); |
| } |
| |
| /* [ body formals ], formals is comma separated list that needs to be parsed */ |
| |
| DUK_ASSERT_TOP(ctx, 2); |
| |
| /* XXX: this placeholder is not always correct, but use for now. |
| * It will fail in corner cases; see test-dev-func-cons-args.js. |
| */ |
| duk_push_string(ctx, "function("); |
| duk_dup(ctx, 1); |
| duk_push_string(ctx, "){"); |
| duk_dup(ctx, 0); |
| duk_push_string(ctx, "}"); |
| duk_concat(ctx, 5); |
| |
| /* [ body formals source ] */ |
| |
| DUK_ASSERT_TOP(ctx, 3); |
| |
| /* strictness is not inherited, intentional */ |
| comp_flags = DUK_JS_COMPILE_FLAG_FUNCEXPR; |
| |
| duk_push_hstring_stridx(ctx, DUK_STRIDX_COMPILE); /* XXX: copy from caller? */ /* XXX: ignored now */ |
| h_sourcecode = duk_require_hstring(ctx, -2); |
| duk_js_compile(thr, |
| (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_sourcecode), |
| (duk_size_t) DUK_HSTRING_GET_BYTELEN(h_sourcecode), |
| comp_flags); |
| func = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1); |
| DUK_ASSERT(func != NULL); |
| DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) func)); |
| |
| /* [ body formals source template ] */ |
| |
| /* only outer_lex_env matters, as functions always get a new |
| * variable declaration environment. |
| */ |
| |
| outer_lex_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; |
| outer_var_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; |
| |
| duk_js_push_closure(thr, func, outer_var_env, outer_lex_env, 1 /*add_auto_proto*/); |
| |
| /* [ body formals source template closure ] */ |
| |
| return 1; |
| } |
| |
| DUK_INTERNAL duk_ret_t duk_bi_function_prototype(duk_context *ctx) { |
| /* ignore arguments, return undefined (E5 Section 15.3.4) */ |
| DUK_UNREF(ctx); |
| return 0; |
| } |
| |
| DUK_INTERNAL duk_ret_t duk_bi_function_prototype_to_string(duk_context *ctx) { |
| duk_tval *tv; |
| |
| /* |
| * E5 Section 15.3.4.2 places few requirements on the output of |
| * this function: |
| * |
| * - The result is an implementation dependent representation |
| * of the function; in particular |
| * |
| * - The result must follow the syntax of a FunctionDeclaration. |
| * In particular, the function must have a name (even in the |
| * case of an anonymous function or a function with an empty |
| * name). |
| * |
| * - Note in particular that the output does NOT need to compile |
| * into anything useful. |
| */ |
| |
| |
| /* XXX: faster internal way to get this */ |
| duk_push_this(ctx); |
| tv = duk_get_tval(ctx, -1); |
| DUK_ASSERT(tv != NULL); |
| |
| if (DUK_TVAL_IS_OBJECT(tv)) { |
| duk_hobject *obj = DUK_TVAL_GET_OBJECT(tv); |
| const char *func_name; |
| |
| /* Function name: missing/undefined is mapped to empty string, |
| * otherwise coerce to string. |
| */ |
| /* XXX: currently no handling for non-allowed identifier characters, |
| * e.g. a '{' in the function name. |
| */ |
| duk_get_prop_stridx(ctx, -1, DUK_STRIDX_NAME); |
| if (duk_is_undefined(ctx, -1)) { |
| func_name = ""; |
| } else { |
| func_name = duk_to_string(ctx, -1); |
| DUK_ASSERT(func_name != NULL); |
| } |
| |
| /* Indicate function type in the function body using a dummy |
| * directive. |
| */ |
| if (DUK_HOBJECT_HAS_COMPILEDFUNCTION(obj)) { |
| duk_push_sprintf(ctx, "function %s() {\"ecmascript\"}", (const char *) func_name); |
| } else if (DUK_HOBJECT_HAS_NATIVEFUNCTION(obj)) { |
| duk_push_sprintf(ctx, "function %s() {\"native\"}", (const char *) func_name); |
| } else if (DUK_HOBJECT_HAS_BOUND(obj)) { |
| duk_push_sprintf(ctx, "function %s() {\"bound\"}", (const char *) func_name); |
| } else { |
| goto type_error; |
| } |
| } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) { |
| duk_push_lightfunc_tostring(ctx, tv); |
| } else { |
| goto type_error; |
| } |
| |
| return 1; |
| |
| type_error: |
| return DUK_RET_TYPE_ERROR; |
| } |
| |
| DUK_INTERNAL duk_ret_t duk_bi_function_prototype_apply(duk_context *ctx) { |
| duk_idx_t len; |
| duk_idx_t i; |
| |
| DUK_ASSERT_TOP(ctx, 2); /* not a vararg function */ |
| |
| duk_push_this(ctx); |
| if (!duk_is_callable(ctx, -1)) { |
| DUK_DDD(DUK_DDDPRINT("func is not callable")); |
| goto type_error; |
| } |
| duk_insert(ctx, 0); |
| DUK_ASSERT_TOP(ctx, 3); |
| |
| DUK_DDD(DUK_DDDPRINT("func=%!iT, thisArg=%!iT, argArray=%!iT", |
| (duk_tval *) duk_get_tval(ctx, 0), |
| (duk_tval *) duk_get_tval(ctx, 1), |
| (duk_tval *) duk_get_tval(ctx, 2))); |
| |
| /* [ func thisArg argArray ] */ |
| |
| if (duk_is_null_or_undefined(ctx, 2)) { |
| DUK_DDD(DUK_DDDPRINT("argArray is null/undefined, no args")); |
| len = 0; |
| } else if (!duk_is_object(ctx, 2)) { |
| goto type_error; |
| } else { |
| DUK_DDD(DUK_DDDPRINT("argArray is an object")); |
| |
| /* XXX: make this an internal helper */ |
| duk_get_prop_stridx(ctx, 2, DUK_STRIDX_LENGTH); |
| len = (duk_idx_t) duk_to_uint32(ctx, -1); /* ToUint32() coercion required */ |
| duk_pop(ctx); |
| |
| duk_require_stack(ctx, len); |
| |
| DUK_DDD(DUK_DDDPRINT("argArray length is %ld", (long) len)); |
| for (i = 0; i < len; i++) { |
| duk_get_prop_index(ctx, 2, i); |
| } |
| } |
| duk_remove(ctx, 2); |
| DUK_ASSERT_TOP(ctx, 2 + len); |
| |
| /* [ func thisArg arg1 ... argN ] */ |
| |
| DUK_DDD(DUK_DDDPRINT("apply, func=%!iT, thisArg=%!iT, len=%ld", |
| (duk_tval *) duk_get_tval(ctx, 0), |
| (duk_tval *) duk_get_tval(ctx, 1), |
| (long) len)); |
| duk_call_method(ctx, len); |
| return 1; |
| |
| type_error: |
| return DUK_RET_TYPE_ERROR; |
| } |
| |
| DUK_INTERNAL duk_ret_t duk_bi_function_prototype_call(duk_context *ctx) { |
| duk_idx_t nargs; |
| |
| /* Step 1 is not necessary because duk_call_method() will take |
| * care of it. |
| */ |
| |
| /* vararg function, thisArg needs special handling */ |
| nargs = duk_get_top(ctx); /* = 1 + arg count */ |
| if (nargs == 0) { |
| duk_push_undefined(ctx); |
| nargs++; |
| } |
| DUK_ASSERT(nargs >= 1); |
| |
| /* [ thisArg arg1 ... argN ] */ |
| |
| duk_push_this(ctx); /* 'func' in the algorithm */ |
| duk_insert(ctx, 0); |
| |
| /* [ func thisArg arg1 ... argN ] */ |
| |
| DUK_DDD(DUK_DDDPRINT("func=%!iT, thisArg=%!iT, argcount=%ld, top=%ld", |
| (duk_tval *) duk_get_tval(ctx, 0), |
| (duk_tval *) duk_get_tval(ctx, 1), |
| (long) (nargs - 1), |
| (long) duk_get_top(ctx))); |
| duk_call_method(ctx, nargs - 1); |
| return 1; |
| } |
| |
| /* XXX: the implementation now assumes "chained" bound functions, |
| * whereas "collapsed" bound functions (where there is ever only |
| * one bound function which directly points to a non-bound, final |
| * function) would require a "collapsing" implementation which |
| * merges argument lists etc here. |
| */ |
| DUK_INTERNAL duk_ret_t duk_bi_function_prototype_bind(duk_context *ctx) { |
| duk_hobject *h_bound; |
| duk_hobject *h_target; |
| duk_idx_t nargs; |
| duk_idx_t i; |
| |
| /* vararg function, careful arg handling (e.g. thisArg may not be present) */ |
| nargs = duk_get_top(ctx); /* = 1 + arg count */ |
| if (nargs == 0) { |
| duk_push_undefined(ctx); |
| nargs++; |
| } |
| DUK_ASSERT(nargs >= 1); |
| |
| duk_push_this(ctx); |
| if (!duk_is_callable(ctx, -1)) { |
| DUK_DDD(DUK_DDDPRINT("func is not callable")); |
| goto type_error; |
| } |
| |
| /* [ thisArg arg1 ... argN func ] (thisArg+args == nargs total) */ |
| DUK_ASSERT_TOP(ctx, nargs + 1); |
| |
| /* create bound function object */ |
| duk_push_object_helper(ctx, |
| DUK_HOBJECT_FLAG_EXTENSIBLE | |
| DUK_HOBJECT_FLAG_BOUND | |
| DUK_HOBJECT_FLAG_CONSTRUCTABLE | |
| DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION), |
| DUK_BIDX_FUNCTION_PROTOTYPE); |
| h_bound = duk_get_hobject(ctx, -1); |
| DUK_ASSERT(h_bound != NULL); |
| |
| /* [ thisArg arg1 ... argN func boundFunc ] */ |
| duk_dup(ctx, -2); /* func */ |
| duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_TARGET, DUK_PROPDESC_FLAGS_NONE); |
| |
| duk_dup(ctx, 0); /* thisArg */ |
| duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_THIS, DUK_PROPDESC_FLAGS_NONE); |
| |
| duk_push_array(ctx); |
| |
| /* [ thisArg arg1 ... argN func boundFunc argArray ] */ |
| |
| for (i = 0; i < nargs - 1; i++) { |
| duk_dup(ctx, 1 + i); |
| duk_put_prop_index(ctx, -2, i); |
| } |
| duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_ARGS, DUK_PROPDESC_FLAGS_NONE); |
| |
| /* [ thisArg arg1 ... argN func boundFunc ] */ |
| |
| /* bound function 'length' property is interesting */ |
| h_target = duk_get_hobject(ctx, -2); |
| if (h_target == NULL || /* lightfunc */ |
| DUK_HOBJECT_GET_CLASS_NUMBER(h_target) == DUK_HOBJECT_CLASS_FUNCTION) { |
| /* For lightfuncs, simply read the virtual property. */ |
| duk_int_t tmp; |
| duk_get_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH); |
| tmp = duk_to_int(ctx, -1) - (nargs - 1); /* step 15.a */ |
| duk_pop(ctx); |
| duk_push_int(ctx, (tmp < 0 ? 0 : tmp)); |
| } else { |
| duk_push_int(ctx, 0); |
| } |
| duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE); /* attrs in E5 Section 15.3.5.1 */ |
| |
| /* caller and arguments must use the same thrower, [[ThrowTypeError]] */ |
| duk_xdef_prop_stridx_thrower(ctx, -1, DUK_STRIDX_CALLER, DUK_PROPDESC_FLAGS_NONE); |
| duk_xdef_prop_stridx_thrower(ctx, -1, DUK_STRIDX_LC_ARGUMENTS, DUK_PROPDESC_FLAGS_NONE); |
| |
| /* these non-standard properties are copied for convenience */ |
| /* XXX: 'copy properties' API call? */ |
| duk_get_prop_stridx(ctx, -2, DUK_STRIDX_NAME); |
| duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_WC); |
| duk_get_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME); |
| duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC); |
| |
| /* The 'strict' flag is copied to get the special [[Get]] of E5.1 |
| * Section 15.3.5.4 to apply when a 'caller' value is a strict bound |
| * function. Not sure if this is correct, because the specification |
| * is a bit ambiguous on this point but it would make sense. |
| */ |
| if (h_target == NULL) { |
| /* Lightfuncs are always strict. */ |
| DUK_HOBJECT_SET_STRICT(h_bound); |
| } else if (DUK_HOBJECT_HAS_STRICT(h_target)) { |
| DUK_HOBJECT_SET_STRICT(h_bound); |
| } |
| DUK_DDD(DUK_DDDPRINT("created bound function: %!iT", (duk_tval *) duk_get_tval(ctx, -1))); |
| |
| return 1; |
| |
| type_error: |
| return DUK_RET_TYPE_ERROR; |
| } |