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
instanceofsemantics (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
lrefbe the result of evaluating RelationalExpression. - Let
lvalbeGetValue(lref). - Let
rrefbe the result of evaluating ShiftExpression. - Let
rvalbeGetValue(rref). - If
Type(rval)is notObject, throw aTypeErrorexception. - If
rvaldoes not have a[[HasInstance]]internal method, throw aTypeErrorexception. - Return the result of calling the
[[HasInstance]]internal method ofrvalwith 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 aTypeErrorexception. - If
Fdoes not have a[[HasInstance]]internal method, throw aTypeErrorexception. - While
Fis a bound function: a. SetFto the value ofF's[[TargetFunction]]internal property. b. IfFhas no[[HasInstance]]internal method, throw aTypeErrorexception. (Note:Fcan be another bound function, so we loop until we find the non-bound actual function.) - If
Vis not an object, returnfalse. - Let
Obe the result of calling the[[Get]]internal method ofFwith property name"prototype". (Note: this is the external prototype, not the internal one.) - If
Type(O)is notObject, throw aTypeErrorexception. - Repeat a. Let
Vbe the value of the[[Prototype]]internal property ofV. b. IfVisnull, returnfalse. c. IfOandVrefer to the same object, returntrue.
Notes:
- The initial
rvalmay 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_hobjectpointer 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 aTypeErrorexception. b. IfFdoes not have a[[HasInstance]]internal method, throw aTypeErrorexception. c. IfFis a normal (non-bound) function, break repeat loop. d. IfFis not a bound function, throw aTypeErrorexception. (Note: this should never happen, but is nice to check.) e. SetFto the value ofF's[[TargetFunction]]internal property and repeat from a). (Note:Fmay be another bound function when exiting this step, so we must repeat until the final, non-bound function is found.) - If
Vis not an object, returnfalse. - Let
Obe the result of calling the[[Get]]internal method ofFwith property name"prototype". (Note: this is the external prototype, not the internal one.) - If
Type(O)is notObject, throw aTypeErrorexception. - Repeat a. Let
Vbe the value of the[[Prototype]]internal property ofV. b. IfVisnull, returnfalse. c. IfOandVrefer to the same object, returntrue.