diff --git a/src-input/builtins.yaml b/src-input/builtins.yaml index 1c81b6a3d4..73d054bbb5 100644 --- a/src-input/builtins.yaml +++ b/src-input/builtins.yaml @@ -3015,7 +3015,7 @@ objects: internal_prototype: bi_function_prototype # no external prototype native: duk_bi_proxy_constructor - callable: true + callable: false constructable: true es6: true bidx: true @@ -3329,7 +3329,7 @@ objects: internal_prototype: bi_function_prototype varargs: false native: duk_bi_arraybuffer_constructor - callable: true + callable: false constructable: true typedarray: true es6: true @@ -3416,7 +3416,7 @@ objects: internal_prototype: bi_function_prototype varargs: false native: duk_bi_dataview_constructor - callable: true + callable: false constructable: true typedarray: true es6: true @@ -3737,7 +3737,7 @@ objects: internal_prototype: bi_function_prototype varargs: false native: duk_bi_typedarray_constructor - callable: true + callable: false constructable: false nargs: 0 magic: 0 @@ -3852,7 +3852,7 @@ objects: internal_prototype: bi_typedarray_constructor varargs: false native: duk_bi_typedarray_constructor - callable: true + callable: false constructable: true magic: type: typedarray_constructor @@ -3920,7 +3920,7 @@ objects: internal_prototype: bi_typedarray_constructor varargs: false native: duk_bi_typedarray_constructor - callable: true + callable: false constructable: true magic: type: typedarray_constructor @@ -4009,7 +4009,7 @@ objects: internal_prototype: bi_typedarray_constructor varargs: false native: duk_bi_typedarray_constructor - callable: true + callable: false constructable: true magic: type: typedarray_constructor @@ -4077,7 +4077,7 @@ objects: internal_prototype: bi_typedarray_constructor varargs: false native: duk_bi_typedarray_constructor - callable: true + callable: false constructable: true magic: type: typedarray_constructor @@ -4145,7 +4145,7 @@ objects: internal_prototype: bi_typedarray_constructor varargs: false native: duk_bi_typedarray_constructor - callable: true + callable: false constructable: true magic: type: typedarray_constructor @@ -4213,7 +4213,7 @@ objects: internal_prototype: bi_typedarray_constructor varargs: false native: duk_bi_typedarray_constructor - callable: true + callable: false constructable: true magic: type: typedarray_constructor @@ -4281,7 +4281,7 @@ objects: internal_prototype: bi_typedarray_constructor varargs: false native: duk_bi_typedarray_constructor - callable: true + callable: false constructable: true magic: type: typedarray_constructor @@ -4349,7 +4349,7 @@ objects: internal_prototype: bi_typedarray_constructor varargs: false native: duk_bi_typedarray_constructor - callable: true + callable: false constructable: true magic: type: typedarray_constructor @@ -4417,7 +4417,7 @@ objects: internal_prototype: bi_typedarray_constructor varargs: false native: duk_bi_typedarray_constructor - callable: true + callable: false constructable: true magic: type: typedarray_constructor @@ -5100,7 +5100,7 @@ objects: internal_prototype: bi_function_prototype nargs: 0 native: duk_bi_textencoder_constructor - callable: true + callable: false constructable: true bidx: true encoding_api: true @@ -5158,7 +5158,7 @@ objects: internal_prototype: bi_function_prototype nargs: 2 native: duk_bi_textdecoder_constructor - callable: true + callable: false constructable: true bidx: true encoding_api: true diff --git a/src-input/duk_bi_buffer.c b/src-input/duk_bi_buffer.c index 92212faf4a..c599e57686 100644 --- a/src-input/duk_bi_buffer.c +++ b/src-input/duk_bi_buffer.c @@ -661,7 +661,7 @@ DUK_INTERNAL duk_ret_t duk_bi_arraybuffer_constructor(duk_context *ctx) { thr = (duk_hthread *) ctx; DUK_UNREF(thr); - duk_require_constructor_call(ctx); + DUK_ASSERT(!duk_is_constructor_call(ctx)); len = duk_to_int(ctx, 0); if (len < 0) { @@ -722,7 +722,7 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { * buffer functions. */ - duk_require_constructor_call(ctx); + DUK_ASSERT(!duk_is_constructor_call(ctx)); /* We could fit built-in index into magic but that'd make the magic * number dependent on built-in numbering (genbuiltins.py doesn't @@ -1061,7 +1061,7 @@ DUK_INTERNAL duk_ret_t duk_bi_typedarray_constructor(duk_context *ctx) { * buffer functions. */ - duk_require_constructor_call(ctx); + DUK_ASSERT(!duk_is_constructor_call(ctx)); elem_length_signed = duk_require_int(ctx, 0); if (elem_length_signed < 0) { @@ -1086,7 +1086,7 @@ DUK_INTERNAL duk_ret_t duk_bi_dataview_constructor(duk_context *ctx) { duk_uint_t offset; duk_uint_t length; - duk_require_constructor_call(ctx); + DUK_ASSERT(!duk_is_constructor_call(ctx)); h_bufarg = duk__require_bufobj_value(ctx, 0); DUK_ASSERT(h_bufarg != NULL); diff --git a/src-input/duk_bi_encoding.c b/src-input/duk_bi_encoding.c index 8b3c97b35b..d016ca591d 100644 --- a/src-input/duk_bi_encoding.c +++ b/src-input/duk_bi_encoding.c @@ -344,7 +344,8 @@ DUK_INTERNAL duk_ret_t duk_bi_textencoder_constructor(duk_context *ctx) { * does nothing on purpose. */ - duk_require_constructor_call(ctx); + DUK_UNREF(ctx); + DUK_ASSERT(duk_is_constructor_call(ctx)); return 0; } @@ -441,7 +442,7 @@ DUK_INTERNAL duk_ret_t duk_bi_textdecoder_constructor(duk_context *ctx) { duk_bool_t ignore_bom = 0; DUK_ASSERT_TOP(ctx, 2); - duk_require_constructor_call(ctx); + DUK_ASSERT(duk_is_constructor_call(ctx)); if (!duk_is_undefined(ctx, 0)) { /* XXX: For now ignore 'label' (encoding identifier). */ duk_to_string(ctx, 0); diff --git a/src-input/duk_bi_json.c b/src-input/duk_bi_json.c index 915f61ea3c..4f005f4406 100644 --- a/src-input/duk_bi_json.c +++ b/src-input/duk_bi_json.c @@ -2104,6 +2104,7 @@ DUK_LOCAL duk_bool_t duk__enc_value(duk_json_enc_ctx *js_ctx, duk_idx_t idx_hold * value is NOT looked up like for e.g. String objects). */ DUK_ASSERT(h != NULL); + /* FIXME: grep all CALLABLE checks and see if they should be CALLABLE || CONSTRUCTABLE */ if (DUK_HOBJECT_IS_CALLABLE(h)) { #if defined(DUK_USE_JX) || defined(DUK_USE_JC) if (js_ctx->flags & (DUK_JSON_FLAG_EXT_CUSTOM | diff --git a/src-input/duk_bi_proxy.c b/src-input/duk_bi_proxy.c index eb8c9c7a7d..6b409c11da 100644 --- a/src-input/duk_bi_proxy.c +++ b/src-input/duk_bi_proxy.c @@ -89,7 +89,7 @@ DUK_INTERNAL void duk_proxy_ownkeys_postprocess(duk_context *ctx, duk_hobject *h DUK_INTERNAL duk_ret_t duk_bi_proxy_constructor(duk_context *ctx) { DUK_ASSERT_TOP(ctx, 2); /* [ target handler ] */ - duk_require_constructor_call(ctx); + DUK_ASSERT(duk_is_constructor_call(ctx)); duk_push_proxy(ctx); /* [ target handler ] -> [ proxy ] */ return 1; /* replacement */ } diff --git a/src-input/duk_hobject.h b/src-input/duk_hobject.h index 1df2fbb0bb..a742087d6f 100644 --- a/src-input/duk_hobject.h +++ b/src-input/duk_hobject.h @@ -36,6 +36,11 @@ * inspection code. */ +/* FIXME: ES spec: all construct-only functions have [[Call]] and [[Construct]] + * so the modeling is not 1:1. Maybe rename so that _CALLABLE means either one? + * _CONSTRUCTABLE.. + */ + /* XXX: some flags are object subtype specific (e.g. common to all function * subtypes, duk_harray, etc) and could be reused for different subtypes. */ @@ -187,6 +192,7 @@ DUK_HOBJECT_FLAG_NATFUNC) #define DUK_HOBJECT_IS_CALLABLE(h) DUK_HOBJECT_HAS_CALLABLE((h)) +#define DUK_HOBJECT_IS_CONSTRUCTABLE(h) DUK_HOBJECT_HAS_CONSTRUCTABLE((h)) /* Object has any exotic behavior(s). */ #define DUK_HOBJECT_EXOTIC_BEHAVIOR_FLAGS (DUK_HOBJECT_FLAG_EXOTIC_ARRAY | \ diff --git a/src-input/duk_hthread_builtins.c b/src-input/duk_hthread_builtins.c index 10ff9ce5e9..46ce7b91b1 100644 --- a/src-input/duk_hthread_builtins.c +++ b/src-input/duk_hthread_builtins.c @@ -279,8 +279,14 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) { /* Almost all global level Function objects are constructable * but not all: Function.prototype is a non-constructable, - * callable Function. + * callable Function. Some built-ins are only constructable, + * not callable as normal functions, e.g. Uint8Array constructor. */ + if (duk_bd_decode_flag(bd)) { + DUK_ASSERT(DUK_HOBJECT_HAS_CALLABLE(h)); + } else { + DUK_HOBJECT_CLEAR_CALLABLE(h); + } if (duk_bd_decode_flag(bd)) { DUK_ASSERT(DUK_HOBJECT_HAS_CONSTRUCTABLE(h)); } else { diff --git a/src-input/duk_js_call.c b/src-input/duk_js_call.c index 8811357f90..20fb0ceea9 100644 --- a/src-input/duk_js_call.c +++ b/src-input/duk_js_call.c @@ -1204,12 +1204,19 @@ DUK_LOCAL duk_hobject *duk__resolve_target_func_and_this_binding(duk_context *ct func = DUK_TVAL_GET_OBJECT(tv_func); if (*call_flags & DUK_CALL_FLAG_CONSTRUCT) { - if (DUK_UNLIKELY(!DUK_HOBJECT_HAS_CONSTRUCTABLE(func))) { + if (DUK_UNLIKELY(!DUK_HOBJECT_IS_CONSTRUCTABLE(func))) { goto not_constructable; } } else { if (DUK_UNLIKELY(!DUK_HOBJECT_IS_CALLABLE(func))) { - goto not_callable; + if (DUK_HOBJECT_IS_CONSTRUCTABLE(func)) { + /* Friendly error if calling a construct-only + * function using a normal call. + */ + goto constructor_not_callable; + } else { + goto not_callable; + } } } @@ -1255,8 +1262,8 @@ DUK_LOCAL duk_hobject *duk__resolve_target_func_and_this_binding(duk_context *ct #endif { DUK_ASSERT(DUK_HOBJECT_IS_NATFUNC(func)); - DUK_ASSERT(DUK_HOBJECT_HAS_CALLABLE(func)); - DUK_ASSERT(!DUK_HOBJECT_HAS_CONSTRUCTABLE(func)); + DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(func)); + DUK_ASSERT(!DUK_HOBJECT_IS_CONSTRUCTABLE(func)); /* Constructable check already done above. */ if (duk__handle_specialfuncs_for_call(thr, idx_func, func, call_flags, first) != 0) { @@ -1312,20 +1319,23 @@ DUK_LOCAL duk_hobject *duk__resolve_target_func_and_this_binding(duk_context *ct DUK_ASSERT(func == NULL || !DUK_HOBJECT_HAS_BOUNDFUNC(func)); DUK_ASSERT(func == NULL || (DUK_HOBJECT_IS_COMPFUNC(func) || DUK_HOBJECT_IS_NATFUNC(func))); - DUK_ASSERT(func == NULL || (DUK_HOBJECT_HAS_CONSTRUCTABLE(func) || + DUK_ASSERT(func == NULL || (DUK_HOBJECT_IS_CONSTRUCTABLE(func) || (*call_flags & DUK_CALL_FLAG_CONSTRUCT) == 0)); } #endif return func; + constructor_not_callable: + DUK_ERROR_TYPE((duk_hthread *) ctx, DUK_STR_CONSTRUCT_ONLY); + return NULL; /* never executed */ + not_callable: - DUK_ASSERT(tv_func != NULL); #if defined(DUK_USE_VERBOSE_ERRORS) #if defined(DUK_USE_PARANOID_ERRORS) DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not callable", duk_get_type_name(ctx, idx_func)); #else - DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not callable", duk_push_string_tval_readable(ctx, tv_func)); + DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not callable", duk_push_string_readable(ctx, idx_func)); #endif #else DUK_ERROR_TYPE(thr, DUK_STR_NOT_CALLABLE); @@ -1338,7 +1348,7 @@ DUK_LOCAL duk_hobject *duk__resolve_target_func_and_this_binding(duk_context *ct #if defined(DUK_USE_PARANOID_ERRORS) DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not constructable", duk_get_type_name(ctx, idx_func)); #else - DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not constructable", duk_push_string_tval_readable(ctx, tv_func)); + DUK_ERROR_FMT1(thr, DUK_ERR_TYPE_ERROR, "%s not constructable", duk_push_string_readable(ctx, idx_func)); #endif #else DUK_ERROR_TYPE(thr, DUK_STR_NOT_CONSTRUCTABLE); diff --git a/src-input/duk_js_ops.c b/src-input/duk_js_ops.c index d08224e345..cfd645a6c5 100644 --- a/src-input/duk_js_ops.c +++ b/src-input/duk_js_ops.c @@ -1044,20 +1044,17 @@ DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_ /* * For bound objects, [[HasInstance]] just calls the target function * [[HasInstance]]. If that is again a bound object, repeat until - * we find a non-bound Function object. + * we find a non-bound Function object. The bound function chain is + * now "collapsed" so there can be only one bound function in the chain. * - * The bound function chain is now "collapsed" so there can be only - * one bound function in the chain. + * Of native Ecmascript objects, only Function instances have a + * [[HasInstance]] internal property. Custom objects might also have + * it, but not in current implementation. + * + * XXX: add a separate flag, DUK_HOBJECT_FLAG_ALLOW_INSTANCEOF? */ - if (!DUK_HOBJECT_IS_CALLABLE(func)) { - /* - * Note: of native Ecmascript objects, only Function instances - * have a [[HasInstance]] internal property. Custom objects might - * also have it, but not in current implementation. - * - * XXX: add a separate flag, DUK_HOBJECT_FLAG_ALLOW_INSTANCEOF? - */ + if (!(DUK_HOBJECT_IS_CALLABLE(func) || DUK_HOBJECT_IS_CONSTRUCTABLE(func))) { DUK_ERROR_TYPE(thr, "invalid instanceof rval"); } @@ -1070,7 +1067,7 @@ DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_ * functions whose target is not proper. */ DUK_ASSERT(func != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(func)); + DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(func) || DUK_HOBJECT_IS_CONSTRUCTABLE(func)); } /* @@ -1082,6 +1079,7 @@ DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_ DUK_ASSERT(func != NULL); DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func)); DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(func)); + DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(func) || DUK_HOBJECT_IS_CONSTRUCTABLE(func)); /* [ ... lval rval(func) ] */ @@ -1267,7 +1265,7 @@ DUK_INTERNAL duk_small_uint_t duk_js_typeof_stridx(duk_tval *tv_x) { case DUK_TAG_OBJECT: { duk_hobject *obj = DUK_TVAL_GET_OBJECT(tv_x); DUK_ASSERT(obj != NULL); - if (DUK_HOBJECT_IS_CALLABLE(obj)) { + if (DUK_HOBJECT_IS_CALLABLE(obj) || DUK_HOBJECT_IS_CONSTRUCTABLE(obj)) { stridx = DUK_STRIDX_LC_FUNCTION; } else { stridx = DUK_STRIDX_LC_OBJECT; diff --git a/tools/genbuiltins.py b/tools/genbuiltins.py index 2c08b8015d..14fcabae4c 100644 --- a/tools/genbuiltins.py +++ b/tools/genbuiltins.py @@ -1699,17 +1699,14 @@ def _natidx(native_name): assert(length is not None) be.bits(0, 1) # flag: default nargs OK - # All Function-classed global level objects are callable - # (have [[Call]]) but not all are constructable (have - # [[Construct]]). Flag that. - - assert(bi.has_key('callable')) - assert(bi['callable'] == True) - assert(prop_name is not None) assert(isinstance(prop_name['value'], str)) _stridx_or_string(prop_name['value']) + if bi.get('callable', False): + be.bits(1, 1) # flag: callable + else: + be.bits(0, 1) # flag: not callable if bi.get('constructable', False): be.bits(1, 1) # flag: constructable else: