Browse Source

Merge pull request #1821 from svaarala/es2015-hasinstance-symbol-support

Add ES2015 @@hasInstance support for instanceof operator
pull/1822/head
Sami Vaarala 7 years ago
committed by GitHub
parent
commit
24a71fb971
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      RELEASES.rst
  2. 38
      src-input/builtins.yaml
  3. 4
      src-input/duk_api_internal.h
  4. 22
      src-input/duk_api_object.c
  5. 16
      src-input/duk_api_stack.c
  6. 11
      src-input/duk_bi_function.c
  7. 10
      src-input/duk_bi_thread.c
  8. 16
      src-input/duk_hobject_props.c
  9. 26
      src-input/duk_hthread_builtins.c
  10. 3
      src-input/duk_js.h
  11. 44
      src-input/duk_js_ops.c
  12. 4
      src-input/strings.yaml
  13. 36
      tests/api/test-instanceof-hasinstance.c
  14. 7
      tests/ecmascript/test-bi-performance.js
  15. 181
      tests/ecmascript/test-expr-instanceof-hasinstance.js
  16. 16
      tools/genbuiltins.py
  17. 2
      website/api/duk_instanceof.yaml

6
RELEASES.rst

@ -3190,6 +3190,10 @@ Planned
2.3.0 (XXXX-XX-XX) 2.3.0 (XXXX-XX-XX)
------------------ ------------------
* Add support for Symbol.hasInstance (@@hasInstance) check for 'instanceof'
operator and duk_instanceof() API call; also add Symbol.hasInstance and
Function.prototype[@@hasInstance] (GH-1821)
* Add duk_random() to allow C code access to the same random number source * Add duk_random() to allow C code access to the same random number source
as Ecmascript code (GH-1815) as Ecmascript code (GH-1815)
@ -3250,6 +3254,8 @@ Planned
* Add a CBOR encoder/decoder as an extra (GH-1781, GH-1800, GH-1801) * Add a CBOR encoder/decoder as an extra (GH-1781, GH-1800, GH-1801)
* Fix performance.now() property attributes to 'wec' (earlier 'wc') (GH-1821)
* Fix debugger StepOver behavior when a tailcall happens in a nested * Fix debugger StepOver behavior when a tailcall happens in a nested
function (not the function where stepping started from) (GH-1786, GH-1787) function (not the function where stepping started from) (GH-1786, GH-1787)

38
src-input/builtins.yaml

@ -871,12 +871,17 @@ objects:
present_if: DUK_USE_FUNCTION_BUILTIN present_if: DUK_USE_FUNCTION_BUILTIN
# ES2015 # ES2015
#- key: # @@hasInstance - key: # @@hasInstance
# type: symbol type: symbol
# variant: wellknown variant: wellknown
# string: "Symbol.hasInstance" string: "Symbol.hasInstance"
# value: XXX value:
# es6: true type: function
native: duk_bi_function_prototype_hasinstance
length: 1
attributes: ""
es6: true
present_if: DUK_USE_SYMBOL_BUILTIN
# Duktape specific %NativeFunctionPrototype% which provides some getters. # Duktape specific %NativeFunctionPrototype% which provides some getters.
# #
@ -3314,13 +3319,14 @@ objects:
attributes: "wc" attributes: "wc"
es6: true es6: true
#- key: "hasInstance" - key: "hasInstance"
# value: value:
# type: symbol type: symbol
# variant: wellknown variant: wellknown
# string: "Symbol.hasInstance" string: "Symbol.hasInstance"
# attributes: "" attributes: ""
# es6: true es6: true
present_if: DUK_USE_SYMBOL_BUILTIN
#- key: "isConcatSpreadable" #- key: "isConcatSpreadable"
# value: # value:
# type: symbol # type: symbol
@ -3335,6 +3341,7 @@ objects:
string: "Symbol.iterator" string: "Symbol.iterator"
attributes: "" attributes: ""
es6: true es6: true
present_if: DUK_USE_SYMBOL_BUILTIN
#- key: "match" #- key: "match"
# value: # value:
# type: symbol # type: symbol
@ -5469,16 +5476,13 @@ objects:
# Firefox and Chrome: data property with 'wec' attributes, # Firefox and Chrome: data property with 'wec' attributes,
# inherited from PerformancePrototype. Use own data property # inherited from PerformancePrototype. Use own data property
# for now. # for now.
# XXX: we'd like to use 'wec' but current built-in init data doesn't
# support it; use 'wc' for consistency so ROM built-ins have the same
# behavior.
- key: "now" - key: "now"
value: value:
type: function type: function
native: duk_bi_performance_now native: duk_bi_performance_now
length: 0 length: 0
nargs: 0 nargs: 0
attributes: "wc" attributes: "wec"
performance_api: true performance_api: true
# Missing until semantics decided. # Missing until semantics decided.
#- key: "timeOrigin" #- key: "timeOrigin"

4
src-input/duk_api_internal.h

@ -142,6 +142,8 @@ DUK_INTERNAL_DECL duk_hobject *duk_to_hobject(duk_hthread *thr, duk_idx_t idx);
DUK_INTERNAL_DECL duk_double_t duk_to_number_m1(duk_hthread *thr); DUK_INTERNAL_DECL duk_double_t duk_to_number_m1(duk_hthread *thr);
DUK_INTERNAL_DECL duk_double_t duk_to_number_m2(duk_hthread *thr); DUK_INTERNAL_DECL duk_double_t duk_to_number_m2(duk_hthread *thr);
DUK_INTERNAL_DECL duk_bool_t duk_to_boolean_top_pop(duk_hthread *thr);
#if defined(DUK_USE_DEBUGGER_SUPPORT) /* only needed by debugger for now */ #if defined(DUK_USE_DEBUGGER_SUPPORT) /* only needed by debugger for now */
DUK_INTERNAL_DECL duk_hstring *duk_safe_to_hstring(duk_hthread *thr, duk_idx_t idx); DUK_INTERNAL_DECL duk_hstring *duk_safe_to_hstring(duk_hthread *thr, duk_idx_t idx);
#endif #endif
@ -283,6 +285,8 @@ DUK_INTERNAL_DECL void duk_xdef_prop_stridx_builtin(duk_hthread *thr, duk_idx_t
DUK_INTERNAL_DECL void duk_xdef_prop_stridx_thrower(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx); /* [] -> [] */ DUK_INTERNAL_DECL void duk_xdef_prop_stridx_thrower(duk_hthread *thr, duk_idx_t obj_idx, duk_small_uint_t stridx); /* [] -> [] */
DUK_INTERNAL_DECL duk_bool_t duk_get_method_stridx(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t stridx);
DUK_INTERNAL_DECL void duk_pack(duk_hthread *thr, duk_idx_t count); DUK_INTERNAL_DECL void duk_pack(duk_hthread *thr, duk_idx_t count);
DUK_INTERNAL_DECL duk_idx_t duk_unpack_array_like(duk_hthread *thr, duk_idx_t idx); DUK_INTERNAL_DECL duk_idx_t duk_unpack_array_like(duk_hthread *thr, duk_idx_t idx);
#if 0 #if 0

22
src-input/duk_api_object.c

@ -105,10 +105,7 @@ DUK_INTERNAL duk_bool_t duk_get_prop_stridx_boolean(duk_hthread *thr, duk_idx_t
if (out_has_prop) { if (out_has_prop) {
*out_has_prop = rc; *out_has_prop = rc;
} }
rc = duk_to_boolean(thr, -1); return duk_to_boolean_top_pop(thr);
DUK_ASSERT(rc == 0 || rc == 1);
duk_pop(thr);
return rc;
} }
DUK_LOCAL duk_bool_t duk__put_prop_shared(duk_hthread *thr, duk_idx_t obj_idx, duk_idx_t idx_key) { DUK_LOCAL duk_bool_t duk__put_prop_shared(duk_hthread *thr, duk_idx_t obj_idx, duk_idx_t idx_key) {
@ -840,6 +837,23 @@ DUK_EXTERNAL duk_bool_t duk_put_global_heapptr(duk_hthread *thr, void *ptr) {
return ret; return ret;
} }
/*
* ES2015 GetMethod()
*/
DUK_INTERNAL duk_bool_t duk_get_method_stridx(duk_hthread *thr, duk_idx_t idx, duk_small_uint_t stridx) {
(void) duk_get_prop_stridx(thr, idx, stridx);
if (duk_is_null_or_undefined(thr, -1)) {
duk_pop_nodecref_unsafe(thr);
return 0;
}
if (!duk_is_callable(thr, -1)) {
DUK_ERROR_TYPE(thr, DUK_STR_NOT_CALLABLE);
DUK_WO_NORETURN(return 0;);
}
return 1;
}
/* /*
* Object prototype * Object prototype
*/ */

16
src-input/duk_api_stack.c

@ -2786,6 +2786,22 @@ DUK_EXTERNAL duk_bool_t duk_to_boolean(duk_hthread *thr, duk_idx_t idx) {
return val; return val;
} }
DUK_INTERNAL duk_bool_t duk_to_boolean_top_pop(duk_hthread *thr) {
duk_tval *tv;
duk_bool_t val;
DUK_ASSERT_API_ENTRY(thr);
tv = duk_require_tval(thr, -1);
DUK_ASSERT(tv != NULL);
val = duk_js_toboolean(tv);
DUK_ASSERT(val == 0 || val == 1);
duk_pop_unsafe(thr);
return val;
}
DUK_EXTERNAL duk_double_t duk_to_number(duk_hthread *thr, duk_idx_t idx) { DUK_EXTERNAL duk_double_t duk_to_number(duk_hthread *thr, duk_idx_t idx) {
duk_tval *tv; duk_tval *tv;
duk_double_t d; duk_double_t d;

11
src-input/duk_bi_function.c

@ -440,3 +440,14 @@ DUK_INTERNAL duk_ret_t duk_bi_native_function_name(duk_hthread *thr) {
fail_type: fail_type:
DUK_DCERROR_TYPE_INVALID_ARGS(thr); DUK_DCERROR_TYPE_INVALID_ARGS(thr);
} }
#if defined(DUK_USE_SYMBOL_BUILTIN)
DUK_INTERNAL duk_ret_t duk_bi_function_prototype_hasinstance(duk_hthread *thr) {
/* This binding: RHS, stack index 0: LHS. */
duk_bool_t ret;
ret = duk_js_instanceof_ordinary(thr, DUK_GET_TVAL_POSIDX(thr, 0), DUK_GET_THIS_TVAL_PTR(thr));
duk_push_boolean(thr, ret);
return 1;
}
#endif /* DUK_USE_SYMBOL_BUILTIN */

10
src-input/duk_bi_thread.c

@ -66,8 +66,9 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_resume(duk_hthread *ctx) {
DUK_ASSERT(thr->heap->curr_thread == thr); DUK_ASSERT(thr->heap->curr_thread == thr);
thr_resume = duk_require_hthread(thr, 0); thr_resume = duk_require_hthread(thr, 0);
is_error = (duk_small_uint_t) duk_to_boolean(thr, 2); DUK_ASSERT(duk_get_top(thr) == 3);
duk_set_top(thr, 2); is_error = (duk_small_uint_t) duk_to_boolean_top_pop(thr);
DUK_ASSERT(duk_get_top(thr) == 2);
/* [ thread value ] */ /* [ thread value ] */
@ -215,8 +216,9 @@ DUK_INTERNAL duk_ret_t duk_bi_thread_yield(duk_hthread *thr) {
DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING); DUK_ASSERT(thr->state == DUK_HTHREAD_STATE_RUNNING);
DUK_ASSERT(thr->heap->curr_thread == thr); DUK_ASSERT(thr->heap->curr_thread == thr);
is_error = (duk_small_uint_t) duk_to_boolean(thr, 1); DUK_ASSERT(duk_get_top(thr) == 2);
duk_set_top(thr, 1); is_error = (duk_small_uint_t) duk_to_boolean_top_pop(thr);
DUK_ASSERT(duk_get_top(thr) == 1);
/* [ value ] */ /* [ value ] */

16
src-input/duk_hobject_props.c

@ -2907,7 +2907,7 @@ DUK_INTERNAL duk_bool_t duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj,
duk_push_hobject(thr, h_target); /* target */ duk_push_hobject(thr, h_target); /* target */
duk_push_tval(thr, tv_key); /* P */ duk_push_tval(thr, tv_key); /* P */
duk_call_method(thr, 2 /*nargs*/); duk_call_method(thr, 2 /*nargs*/);
tmp_bool = duk_to_boolean(thr, -1); tmp_bool = duk_to_boolean_top_pop(thr);
if (!tmp_bool) { if (!tmp_bool) {
/* Target object must be checked for a conflicting /* Target object must be checked for a conflicting
* non-configurable property. * non-configurable property.
@ -2931,7 +2931,7 @@ DUK_INTERNAL duk_bool_t duk_hobject_hasprop(duk_hthread *thr, duk_tval *tv_obj,
} }
} }
duk_pop_2_unsafe(thr); /* [ key trap_result ] -> [] */ duk_pop_unsafe(thr); /* [ key ] -> [] */
return tmp_bool; return tmp_bool;
} }
@ -3495,8 +3495,7 @@ DUK_INTERNAL duk_bool_t duk_hobject_putprop(duk_hthread *thr, duk_tval *tv_obj,
duk_push_tval(thr, tv_val); /* V */ duk_push_tval(thr, tv_val); /* V */
duk_push_tval(thr, tv_obj); /* Receiver: Proxy object */ duk_push_tval(thr, tv_obj); /* Receiver: Proxy object */
duk_call_method(thr, 4 /*nargs*/); duk_call_method(thr, 4 /*nargs*/);
tmp_bool = duk_to_boolean(thr, -1); tmp_bool = duk_to_boolean_top_pop(thr);
duk_pop_nodecref_unsafe(thr);
if (!tmp_bool) { if (!tmp_bool) {
goto fail_proxy_rejected; goto fail_proxy_rejected;
} }
@ -4470,8 +4469,7 @@ DUK_INTERNAL duk_bool_t duk_hobject_delprop(duk_hthread *thr, duk_tval *tv_obj,
duk_push_hobject(thr, h_target); /* target */ duk_push_hobject(thr, h_target); /* target */
duk_dup_m4(thr); /* P */ duk_dup_m4(thr); /* P */
duk_call_method(thr, 2 /*nargs*/); duk_call_method(thr, 2 /*nargs*/);
tmp_bool = duk_to_boolean(thr, -1); tmp_bool = duk_to_boolean_top_pop(thr);
duk_pop_nodecref_unsafe(thr);
if (!tmp_bool) { if (!tmp_bool) {
goto fail_proxy_rejected; /* retval indicates delete failed */ goto fail_proxy_rejected; /* retval indicates delete failed */
} }
@ -4976,7 +4974,7 @@ void duk_hobject_prepare_property_descriptor(duk_hthread *thr,
if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_WRITABLE)) { if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_WRITABLE)) {
is_data_desc = 1; is_data_desc = 1;
if (duk_to_boolean(thr, -1)) { if (duk_to_boolean_top_pop(thr)) {
defprop_flags |= DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE; defprop_flags |= DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_WRITABLE;
} else { } else {
defprop_flags |= DUK_DEFPROP_HAVE_WRITABLE; defprop_flags |= DUK_DEFPROP_HAVE_WRITABLE;
@ -5028,7 +5026,7 @@ void duk_hobject_prepare_property_descriptor(duk_hthread *thr,
} }
if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_ENUMERABLE)) { if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_ENUMERABLE)) {
if (duk_to_boolean(thr, -1)) { if (duk_to_boolean_top_pop(thr)) {
defprop_flags |= DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE; defprop_flags |= DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE;
} else { } else {
defprop_flags |= DUK_DEFPROP_HAVE_ENUMERABLE; defprop_flags |= DUK_DEFPROP_HAVE_ENUMERABLE;
@ -5036,7 +5034,7 @@ void duk_hobject_prepare_property_descriptor(duk_hthread *thr,
} }
if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_CONFIGURABLE)) { if (duk_get_prop_stridx(thr, idx_in, DUK_STRIDX_CONFIGURABLE)) {
if (duk_to_boolean(thr, -1)) { if (duk_to_boolean_top_pop(thr)) {
defprop_flags |= DUK_DEFPROP_HAVE_CONFIGURABLE | DUK_DEFPROP_CONFIGURABLE; defprop_flags |= DUK_DEFPROP_HAVE_CONFIGURABLE | DUK_DEFPROP_CONFIGURABLE;
} else { } else {
defprop_flags |= DUK_DEFPROP_HAVE_CONFIGURABLE; defprop_flags |= DUK_DEFPROP_HAVE_CONFIGURABLE;

26
src-input/duk_hthread_builtins.c

@ -444,11 +444,9 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
* signaled using a single flag bit in the bitstream. * signaled using a single flag bit in the bitstream.
*/ */
if (duk_bd_decode_flag(bd)) { defprop_flags = (duk_small_uint_t) duk_bd_decode_flagged(bd,
defprop_flags = (duk_small_uint_t) duk_bd_decode(bd, DUK__PROP_FLAGS_BITS); DUK__PROP_FLAGS_BITS,
} else { (duk_uint32_t) DUK_PROPDESC_FLAGS_WC);
defprop_flags = DUK_PROPDESC_FLAGS_WC;
}
defprop_flags |= DUK_DEFPROP_FORCE | defprop_flags |= DUK_DEFPROP_FORCE |
DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_HAVE_VALUE |
DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_HAVE_WRITABLE |
@ -553,6 +551,7 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
#if defined(DUK_USE_LIGHTFUNC_BUILTINS) #if defined(DUK_USE_LIGHTFUNC_BUILTINS)
duk_small_int_t lightfunc_eligible; duk_small_int_t lightfunc_eligible;
#endif #endif
duk_small_uint_t defprop_flags;
duk__push_stridx_or_string(thr, bd); duk__push_stridx_or_string(thr, bd);
h_key = duk_known_hstring(thr, -1); h_key = duk_known_hstring(thr, -1);
@ -672,10 +671,19 @@ DUK_INTERNAL void duk_hthread_create_builtin_objects(duk_hthread *thr) {
lightfunc_skip: lightfunc_skip:
#endif #endif
/* XXX: So far all ES builtins are 'wc' but e.g. defprop_flags = (duk_small_uint_t) duk_bd_decode_flagged(bd,
* performance.now() should be 'wec'. DUK__PROP_FLAGS_BITS,
*/ (duk_uint32_t) DUK_PROPDESC_FLAGS_WC);
duk_xdef_prop(thr, (duk_idx_t) i, DUK_PROPDESC_FLAGS_WC); defprop_flags |= DUK_DEFPROP_FORCE |
DUK_DEFPROP_HAVE_VALUE |
DUK_DEFPROP_HAVE_WRITABLE |
DUK_DEFPROP_HAVE_ENUMERABLE |
DUK_DEFPROP_HAVE_CONFIGURABLE;
DUK_ASSERT(DUK_PROPDESC_FLAG_WRITABLE == DUK_DEFPROP_WRITABLE);
DUK_ASSERT(DUK_PROPDESC_FLAG_ENUMERABLE == DUK_DEFPROP_ENUMERABLE);
DUK_ASSERT(DUK_PROPDESC_FLAG_CONFIGURABLE == DUK_DEFPROP_CONFIGURABLE);
duk_def_prop(thr, (duk_idx_t) i, defprop_flags);
/* [ (builtin objects) ] */ /* [ (builtin objects) ] */
} }

3
src-input/duk_js.h

@ -43,6 +43,9 @@ DUK_INTERNAL_DECL duk_small_int_t duk_js_buffer_compare(duk_heap *heap, duk_hbuf
#endif #endif
DUK_INTERNAL_DECL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_t flags); DUK_INTERNAL_DECL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_t flags);
DUK_INTERNAL_DECL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y); DUK_INTERNAL_DECL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y);
#if defined(DUK_USE_SYMBOL_BUILTIN)
DUK_INTERNAL_DECL duk_bool_t duk_js_instanceof_ordinary(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y);
#endif
DUK_INTERNAL_DECL duk_bool_t duk_js_in(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y); DUK_INTERNAL_DECL duk_bool_t duk_js_in(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y);
DUK_INTERNAL_DECL duk_small_uint_t duk_js_typeof_stridx(duk_tval *tv_x); DUK_INTERNAL_DECL duk_small_uint_t duk_js_typeof_stridx(duk_tval *tv_x);

44
src-input/duk_js_ops.c

@ -998,22 +998,19 @@ DUK_INTERNAL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x,
*/ */
/* /*
* E5 Section 11.8.6 describes the main algorithm, which uses * ES2015 Section 7.3.19 describes the OrdinaryHasInstance() algorithm
* [[HasInstance]]. [[HasInstance]] is defined for only * which covers both bound and non-bound functions; in effect the algorithm
* function objects: * includes E5 Sections 11.8.6, 15.3.5.3, and 15.3.4.5.3.
* *
* - Normal functions: * ES2015 Section 12.9.4 describes the instanceof operator which first
* E5 Section 15.3.5.3 * checks @@hasInstance well-known symbol and falls back to
* - Functions established with Function.prototype.bind(): * OrdinaryHasInstance().
* E5 Section 15.3.4.5.3
*
* For other objects, a TypeError is thrown.
* *
* Limited Proxy support: don't support 'getPrototypeOf' trap but * Limited Proxy support: don't support 'getPrototypeOf' trap but
* continue lookup in Proxy target if the value is a Proxy. * continue lookup in Proxy target if the value is a Proxy.
*/ */
DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y) { DUK_LOCAL duk_bool_t duk__js_instanceof_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_bool_t skip_sym_check) {
duk_hobject *func; duk_hobject *func;
duk_hobject *val; duk_hobject *val;
duk_hobject *proto; duk_hobject *proto;
@ -1036,6 +1033,23 @@ DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_
func = duk_require_hobject(thr, -1); func = duk_require_hobject(thr, -1);
DUK_ASSERT(func != NULL); DUK_ASSERT(func != NULL);
#if defined(DUK_USE_SYMBOL_BUILTIN)
/*
* @@hasInstance check, ES2015 Section 12.9.4, Steps 2-4.
*/
if (!skip_sym_check) {
if (duk_get_method_stridx(thr, -1, DUK_STRIDX_WELLKNOWN_SYMBOL_HAS_INSTANCE)) {
/* [ ... lhs rhs func ] */
duk_insert(thr, -3); /* -> [ ... func lhs rhs ] */
duk_swap_top(thr, -2); /* -> [ ... func rhs(this) lhs ] */
duk_call_method(thr, 1);
return duk_to_boolean_top_pop(thr);
}
}
#else
DUK_UNREF(skip_sym_check);
#endif
/* /*
* For bound objects, [[HasInstance]] just calls the target function * For bound objects, [[HasInstance]] just calls the target function
* [[HasInstance]]. If that is again a bound object, repeat until * [[HasInstance]]. If that is again a bound object, repeat until
@ -1189,6 +1203,16 @@ DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_
#endif #endif
} }
#if defined(DUK_USE_SYMBOL_BUILTIN)
DUK_INTERNAL duk_bool_t duk_js_instanceof_ordinary(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y) {
return duk__js_instanceof_helper(thr, tv_x, tv_y, 1 /*skip_sym_check*/);
}
#endif
DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y) {
return duk__js_instanceof_helper(thr, tv_x, tv_y, 0 /*skip_sym_check*/);
}
/* /*
* in * in
*/ */

4
src-input/strings.yaml

@ -482,6 +482,10 @@ strings:
type: symbol type: symbol
variant: wellknown variant: wellknown
string: "Symbol.toPrimitive" string: "Symbol.toPrimitive"
- str:
type: symbol
variant: wellknown
string: "Symbol.hasInstance"
# Misc # Misc
- str: "setPrototypeOf" - str: "setPrototypeOf"

36
tests/api/test-instanceof-hasinstance.c

@ -0,0 +1,36 @@
/*
* duk_instanceof() with rhs having @@hasInstance
*/
/*===
*** test_1 (duk_safe_call)
hasinst called
instanceof: 1
final top: 2
==> rc=0, result='undefined'
===*/
static duk_ret_t test_1(duk_context *ctx, void *udata) {
(void) udata;
/* Function.prototype[@@hasInstance] is not writable or configurable.
* To set it, use duk_def_prop() or Object.defineProperty() to avoid
* the ancestor blocking the write.
*/
duk_eval_string(ctx, "123");
duk_eval_string(ctx, "(function foo() {})");
duk_push_string(ctx, DUK_WELLKNOWN_SYMBOL("Symbol.hasInstance"));
duk_eval_string(ctx, "(function hasinst() { print('hasinst called'); return true; })");
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE);
/* [ lhs rhs ] */
printf("instanceof: %d\n", (int) duk_instanceof(ctx, 0, 1));
printf("final top: %ld\n", (long) duk_get_top(ctx));
return 0;
}
void test(duk_context *ctx) {
TEST_SAFE_CALL(test_1);
}

7
tests/ecmascript/test-bi-performance.js

@ -5,7 +5,7 @@ true
true true true true true true
function function
true true
true false true true true true
undefined undefined
true true
number number
@ -28,9 +28,8 @@ function test() {
print(pd.value !== void 0); print(pd.value !== void 0);
print(pd.writable, pd.enumerable, pd.configurable); print(pd.writable, pd.enumerable, pd.configurable);
// 'performance.now' is a function. // 'performance.now' is a function, property attributes don't follow
// XXX: attributes should be 'wec', not 'wc'; this is due to built-in // the convention of normal built-ins and are 'wec'.
// init data limitations.
print(typeof performance.now); print(typeof performance.now);
pd = Object.getOwnPropertyDescriptor(performance, 'now'); pd = Object.getOwnPropertyDescriptor(performance, 'now');
print(pd.value !== void 0); print(pd.value !== void 0);

181
tests/ecmascript/test-expr-instanceof-hasinstance.js

@ -0,0 +1,181 @@
/*
* instanceof and @@hasInstance
*/
/*===
- No @@hasInstance case; inherited from Function.prototype
false
true
- Function overrides @@hasInstance
false
true
- Function inherits a non-standard @@hasInstance
inherited hasInstance true 123
true
inherited hasInstance true [object Object]
true
- Function inherits a non-standard @@hasInstance, but function itself also provides an undefined @@hasInstance
false
true
true
- Same but overriding value is null
false
true
true
- Same but overriding value is not undefined/null, but also not a callable object
TypeError
- Same, plain non-callable object
TypeError
- @@hasInstance is a getter with side effects
@@hasInstance getter
hasInstance true 123
true
@@hasInstance getter
hasInstance true [object Object]
true
- Function.prototype[@@hasInstance] exists
true
true
false
function false false false
- Function.prototype[@@hasInstance] access to OrdinaryHasInstance()
false
true
false
true
===*/
function basicTest() {
print('- No @@hasInstance case; inherited from Function.prototype');
var rhs = function () {};
print(123 instanceof rhs);
print(Object.create(rhs.prototype) instanceof rhs);
print('- Function overrides @@hasInstance');
var rhs = function () {};
rhs[Symbol.hasInstance] = function (v) {
print('hasInstance', this === rhs, v);
return 1;
}
print(123 instanceof rhs);
print(Object.create(rhs.prototype) instanceof rhs);
print('- Function inherits a non-standard @@hasInstance');
var rhs = function () {};
var o = {};
Object.setPrototypeOf(rhs, o);
o[Symbol.hasInstance] = function (v) {
print('inherited hasInstance', this === rhs, v);
return true;
}
print(123 instanceof rhs);
print(Object.create(rhs.prototype) instanceof rhs);
// In this case we fall back to OrdinaryHasInstance().
print('- Function inherits a non-standard @@hasInstance, but function itself also provides an undefined @@hasInstance');
var rhs = function () {};
var o = {};
Object.setPrototypeOf(rhs, o);
o[Symbol.hasInstance] = function (v) {
print('undefined hasInstance', this === rhs, v);
return 1;
}
rhs[Symbol.hasInstance] = void 0;
print(123 instanceof rhs);
print(Object.create(rhs.prototype) instanceof rhs);
var inst = new rhs();
print(inst instanceof rhs);
// For null value, GetMethod (https://www.ecma-international.org/ecma-262/6.0/#sec-getmethod)
// returns 'undefined' for both undefined AND null, so that InstanceofOperator(O, C)
// handles them the same in Step 4 of https://www.ecma-international.org/ecma-262/6.0/#sec-instanceofoperator.
// In other words, for both undefined and null we must fall back to the
// OrdinaryHasInstance() algorithm. V8 treats null and undefined differently
// (TypeError for null), Firefox treats them the same.
// https://www.ecma-international.org/ecma-262/6.0/#sec-instanceofoperator
// A non-undefined/null value, causes a "not callable" TypeError
print('- Same but overriding value is null');
var rhs = function () {};
var o = {};
Object.setPrototypeOf(rhs, o);
o[Symbol.hasInstance] = function (v) {
print('null hasInstance', this === rhs, v);
return 1;
}
rhs[Symbol.hasInstance] = null;
print(123 instanceof rhs);
print(Object.create(rhs.prototype) instanceof rhs);
var inst = new rhs();
print(inst instanceof rhs);
print('- Same but overriding value is not undefined/null, but also not a callable object');
var rhs = function () {};
var o = {};
Object.setPrototypeOf(rhs, o);
o[Symbol.hasInstance] = function (v) {
print('fail hasInstance', this === rhs, v);
return 1;
}
try {
rhs[Symbol.hasInstance] = true;
print(123 instanceof rhs);
} catch (e) {
print(e.name);
}
print('- Same, plain non-callable object');
var rhs = function () {};
var o = {};
Object.setPrototypeOf(rhs, o);
o[Symbol.hasInstance] = function (v) {
print('fail hasInstance', this === rhs, v);
return 1;
}
try {
rhs[Symbol.hasInstance] = { plain: true };
print(123 instanceof rhs);
} catch (e) {
print(e.name);
}
print('- @@hasInstance is a getter with side effects');
var rhs = function () {};
Object.defineProperty(rhs, Symbol.hasInstance, {
get: function () {
print('@@hasInstance getter');
return function (v) {
print('hasInstance', this === rhs, v);
return 1;
}
}
 });
print(123 instanceof rhs);
print(Object.create(rhs.prototype) instanceof rhs);
print('- Function.prototype[@@hasInstance] exists');
print(Symbol.hasInstance in Function.prototype);
print(Function.prototype[Symbol.hasInstance].call(Error, new RangeError()));
print(Function.prototype[Symbol.hasInstance].call(Error, new Date()));
var pd = Object.getOwnPropertyDescriptor(Function.prototype, Symbol.hasInstance);
print(typeof pd.value, pd.writable, pd.enumerable, pd.configurable);
// Function.prototype[@@hasInstance] allows direct access to the
// ES2015 OrdinaryHasInstance() specification method.
print('- Function.prototype[@@hasInstance] access to OrdinaryHasInstance()');
var rhs = function () {};
rhs[Symbol.hasInstance] = function (v) {
print('hasInstance', this === rhs, v);
return 0;
}
print(123 instanceof rhs);
print(Object.create(rhs.prototype) instanceof rhs);
print(Function.prototype[Symbol.hasInstance].call(rhs, {}));
print(Function.prototype[Symbol.hasInstance].call(rhs, Object.create(rhs.prototype)));
}
try {
basicTest();
} catch (e) {
print(e.stack || e);
}

16
tools/genbuiltins.py

@ -874,11 +874,16 @@ def metadata_add_string_define_names(strlist, special_defs):
if len(v) >= 1 and v[0] == '\x82': if len(v) >= 1 and v[0] == '\x82':
pfx = 'DUK_STRIDX_INT_' pfx = 'DUK_STRIDX_INT_'
v = v[1:] v = v[1:]
elif len(v) >= 1 and v[0] == '\x81' and v[-1] == '\xff':
pfx = 'DUK_STRIDX_WELLKNOWN_'
v = v[1:-1]
else: else:
pfx = 'DUK_STRIDX_' pfx = 'DUK_STRIDX_'
t = re.sub(r'([a-z0-9])([A-Z])', r'\1_\2', v) # add underscores: aB -> a_B t = re.sub(r'([a-z0-9])([A-Z])', r'\1_\2', v) # add underscores: aB -> a_B
t = re.sub(r'\.', '_', t) # replace . with _, e.g. Symbol.iterator
s['define'] = pfx + t.upper() s['define'] = pfx + t.upper()
logger.debug('stridx define: ' + s['define'])
# Add a 'stridx_used' flag for strings which need a stridx. # Add a 'stridx_used' flag for strings which need a stridx.
def metadata_add_string_used_stridx(strlist, used_stridx_meta): def metadata_add_string_used_stridx(strlist, used_stridx_meta):
@ -1389,6 +1394,7 @@ def steal_prop(props, key, allow_accessor=True):
LENGTH_PROPERTY_ATTRIBUTES = 'c' LENGTH_PROPERTY_ATTRIBUTES = 'c'
ACCESSOR_PROPERTY_ATTRIBUTES = 'c' ACCESSOR_PROPERTY_ATTRIBUTES = 'c'
DEFAULT_DATA_PROPERTY_ATTRIBUTES = 'wc' DEFAULT_DATA_PROPERTY_ATTRIBUTES = 'wc'
DEFAULT_FUNC_PROPERTY_ATTRIBUTES = 'wc'
# Encoding constants (must match duk_hthread_builtins.c). # Encoding constants (must match duk_hthread_builtins.c).
PROP_FLAGS_BITS = 3 PROP_FLAGS_BITS = 3
@ -1961,6 +1967,16 @@ def gen_ramobj_initdata_for_props(meta, be, bi, string_to_stridx, natfunc_name_t
assert(magic <= 0xffff) assert(magic <= 0xffff)
be.varuint(magic) be.varuint(magic)
default_attrs = DEFAULT_FUNC_PROPERTY_ATTRIBUTES
attrs = funprop.get('attributes', default_attrs)
attrs = attrs.replace('a', '') # ram bitstream doesn't encode 'accessor' attribute
if attrs != default_attrs:
logger.debug('non-default attributes: %s -> %r (default %r)' % (funprop['key'], attrs, default_attrs))
be.bits(1, 1) # flag: have custom attributes
be.bits(encode_property_flags(attrs), PROP_FLAGS_BITS)
else:
be.bits(0, 1) # flag: no custom attributes
return count_normal_props, count_function_props return count_normal_props, count_function_props
# Get helper maps for RAM objects. # Get helper maps for RAM objects.

2
website/api/duk_instanceof.yaml

@ -8,7 +8,7 @@ stack: |
summary: | summary: |
<p>Compare values at <code>idx1</code> and <code>idx2</code> using the <p>Compare values at <code>idx1</code> and <code>idx2</code> using the
Ecmascript <a href="http://www.ecma-international.org/ecma-262/5.1/#sec-11.8.6">instanceof</a> Ecmascript <a href="https://www.ecma-international.org/ecma-262/6.0/#sec-instanceofoperator">instanceof</a>
operator. Returns 1 if <code>val1 instanceof val2</code>, 0 if not. Throws an error operator. Returns 1 if <code>val1 instanceof val2</code>, 0 if not. Throws an error
if either index is invalid; <code>instanceof</code> itself also throws errors for if either index is invalid; <code>instanceof</code> itself also throws errors for
invalid argument types.</p> invalid argument types.</p>

Loading…
Cancel
Save