INSTOF: exposed object class membership check ("instanceof" operator)
Background
Object class membership check is done using the instanceof
operator in ECMAScript code, e.g.:
print(x instanceof Array);
The language semantics of "class membership" are not as clear cut in ECMAScript as in some other languages. But essentially, the instanceof
expression above checks whether Array.prototype
occurs in the internal prototype chain of x
).
This involves:
- An expression for the left-hand-side
- An expression for the right-hand-side
instanceof
semantics (E5 Section 11.8.6)- A call to
[[HasInstance]]
First draft
The instanceof
operator is the only "caller" for [[HasInstance]]
and has the following steps (for evaluating RelationalExpression instanceof ShiftExpression):
- Let
lref
be the result of evaluating RelationalExpression. - Let
lval
beGetValue(lref)
. - Let
rref
be the result of evaluating ShiftExpression. - Let
rval
beGetValue(rref)
. - If
Type(rval)
is notObject
, throw aTypeError
exception. - If
rval
does not have a[[HasInstance]]
internal method, throw aTypeError
exception. - Return the result of calling the
[[HasInstance]]
internal method ofrval
with argumentlval
.
For implementing instanceof
, steps 1-4 can be assumed to be handled by the compiler and map to a certain bytecode sequence. Steps 5-7 are the relevant part.
The following algorithm integrates steps 5-7 from above with the combined [[HasInstance]]
algorithm (lval
is renamed to V
and rval
to F
):
- If
Type(F)
is notObject
, throw aTypeError
exception. - If
F
does not have a[[HasInstance]]
internal method, throw aTypeError
exception. - While
F
is a bound function: a. SetF
to the value ofF
's[[TargetFunction]]
internal property. b. IfF
has no[[HasInstance]]
internal method, throw aTypeError
exception. (Note:F
can be another bound function, so we loop until we find the non-bound actual function.) - If
V
is not an object, returnfalse
. - Let
O
be the result of calling the[[Get]]
internal method ofF
with property name"prototype"
. (Note: this is the external prototype, not the internal one.) - If
Type(O)
is notObject
, throw aTypeError
exception. - Repeat a. Let
V
be the value of the[[Prototype]]
internal property ofV
. b. IfV
isnull
, returnfalse
. c. IfO
andV
refer to the same object, returntrue
.
Notes:
- The initial
rval
may be something other than a callable function, so it needs an explicit check, whereas the[[TargetFunction]]
internal property can only exist with a valid callable object value (E5 Section 15.3.4.5, step 2 checks for this). - Step 3.b seems to be unnecessary:
Function.prototype.bind()
will not create a bound function whose target function is not callable, so they should always have a[[HasInstance]]
internal method. If this is just to add some internal robustness, why not also check that the target function is an object? - In step 7 we assume that the internal prototype is always an object or
null
. If the internal implementation does not constrain this fully, it makes sense to check this explicitly. The current implementation uses anduk_hobject
pointer for the internal prototype, so the prototype is effectively constrained to be either object ornull
. - The loop in step 7 assumes that there are no prototype loops. An explicit sanity check should be inserted.
Cleanup
Steps 1-3 can be combined to a simpler loop with a bit more paranoid checks:
- Repeat a. If
Type(F)
is notObject
, throw aTypeError
exception. b. IfF
does not have a[[HasInstance]]
internal method, throw aTypeError
exception. c. IfF
is a normal (non-bound) function, break repeat loop. d. IfF
is not a bound function, throw aTypeError
exception. (Note: this should never happen, but is nice to check.) e. SetF
to the value ofF
's[[TargetFunction]]
internal property and repeat from a). (Note:F
may be another bound function when exiting this step, so we must repeat until the final, non-bound function is found.) - If
V
is not an object, returnfalse
. - Let
O
be the result of calling the[[Get]]
internal method ofF
with property name"prototype"
. (Note: this is the external prototype, not the internal one.) - If
Type(O)
is notObject
, throw aTypeError
exception. - Repeat a. Let
V
be the value of the[[Prototype]]
internal property ofV
. b. IfV
isnull
, returnfalse
. c. IfO
andV
refer to the same object, returntrue
.