primitive boxing in instanceof due to Symbol.hasInstance

# Bradley Meck (9 years ago)

Kyle Simpson caused the discovery of this oddity :

people.mozilla.org/~jorendorff/es6-draft.html#sec-instanceofoperator calls GetMethod on C without checking if it is an Object, this means 1 instanceof Foo could be affected by mutating Number.prototype[Symbol.hasInstance].

I am assuming this is unintended?

# Kevin Smith (9 years ago)

people.mozilla.org/~jorendorff/es6-draft.html#sec-instanceofoperator calls GetMethod on C without checking if it is an Object, this means 1 instanceof Foo could be affected by mutating Number.prototype[Symbol.hasInstance].

I assume you mean "without checking if O is an object"

I am assuming this is unintended?

IIUC the point of @@hasInstance is to allow the programmer to define completely arbitrary semantics for "instanceof" when applied to "C", even if C is a built-in. Do you see a problem with this behavior?

# Bradley Meck (9 years ago)

Yes, with to O being an Object.

This seems a bit strange to me since things like:

Number.prototype.toString = function () {return 'overriden';}

(1)+'' // "1"


String(1) // "1"

(1).toString() // "overriden"

Implicit string coercion does not box the object. String constructor does not box the object.

Only the . operator which requires an Object boxes the object.

instanceof works on non-objects but I think is the only case of an operator working on both primitives and objects causing boxing.

Is there a reason constructors could/should be allowed to override instanceof for primitives? This just seems odd to have this cause boxing while 1 instanceof Number is false.

# Andrea Giammarchi (9 years ago)

point of views I guess, you could fix forever the inconsistency via

Number.prototype[Symbol.hasInstance] = function () {
  return typeof this.valueOf() === 'number';
};

and fix String, Boolean, Symbol too (why not) and whatever else. instanceof is the new typeof ^_^ (naa, just joking)

Best

# Bradley Meck (9 years ago)

Well this also has slightly odd behavior since the Object operation will fail for OrdinaryHastInstance if for primitives people.mozilla.org/~jorendorff/es6-draft.html#sec

# Andrea Giammarchi (9 years ago)

so Object(unknown) instanceof Number it is then ¯_(ツ)_/¯

# Bradley Meck (9 years ago)

Would not work since @@hasInstance takes priority over OrdinaryHasInstance and would box it.

# Allen Wirfs-Brock (9 years ago)

On Jun 15, 2015, at 10:06 AM, Bradley Meck wrote:

Kyle Simpson caused the discovery of this oddity :

people.mozilla.org/~jorendorff/es6-draft.html#sec-instanceofoperator calls GetMethod on C without checking if it is an Object, this means 1 instanceof Foo could be affected by mutating Number.prototype[Symbol.hasInstance].

I am assuming this is unintended?

No, it is exactly as intended:

GetMethod users GetV people.mozilla.org/~jorendorff/es6-draft.html#sec-getv which handles accessing properties for primitives. For primitive values, it uses the built-in wrapper prototype to lookup properties, but it does not actually create a boxed primitive value.

this object boxing is a characteristic of the callee, not the caller. Non-srict functions box primitive this values that are passed to them, strict mode functions do not.

This is all the standard method invocation behavior, and the instanceof operator just does a normal method invocation if it finds a Symbol.hasInstance method. Because Function.prototype implements Symbol.hasInstacce all function haves the default legacy behavior when appearing right-side operand of instanceof

But instanceof does not depend upon the existence of a a Symbol.hasInstance method. It falls back to performing the default (legacy) instanceof check if a callable hasInstance property value is not found.

But, back to the original question: Exactly what do you expect to be boxed for 1 instance of Foo? The C that GetMethod is applied to is Foo which would normally be a constructor or some other "type-like" object implements. It could be a primitive numeric value (eg, 5 instanceof 4) which will cause the fallback to be taken using Number.prototype has a Symbol.hasInstance callable property. But, even if it does why would you expect anything to box?

# Bradley Meck (9 years ago)

On the surface O instanceof C boxes C, but when using GetMethod O turns O into an Object. This means instanceof is temporarily turning O into an Object.

For primitives, all the operators in JS have been unable to be altered until this was pointed out. 1 instanceof C is able to be changed by mutating %NumberPrototype% , "" instanceof C by mutating %StringPrototype% , etc.

Compared to other operators this is new and a bit surprising that primitive behaviors are mutable.

I would expect this behavior by boxing primitives via Object(primitive) instanceof C but not for primitives.

# Allen Wirfs-Brock (9 years ago)

On Jun 15, 2015, at 9:21 PM, Bradley Meck wrote:

On the surface O instanceof C boxes C, but when using GetMethod O turns O into an Object. This means instanceof is temporarily turning O into an Object.

Read the spec people.mozilla.org/~jorendorff/es6-draft.html#sec-relational-operators-runtime-semantics-evaluation

o instanceof C performs InstanceOfOperator(O,C) which performs GetMethod(C, @@hasInstance)

so GetMethod doesn't even see the value of o.

For primitives, all the operators in JS have been unable to be altered until this was pointed out. 1 instanceof C is able to be changed by mutating %NumberPrototype% , "" instanceof C by mutating %StringPrototype% , etc.

In ES6 certain operations (instanceof and the internal ToPrimitive which is used by several operator) were made extensible because Host and built-in objects have always been able to extend their semantics. For example, WebIDL has it's own unique definition of instanceof that it uses for DOM objects. Extensibility of these operations via JS code is necessary in order to enable such libraries to be directly self-hosted using JS code.

Compared to other operators this is new and a bit surprising that primitive behaviors are mutable.

I would expect this behavior by boxing primitives via Object(primitive) instanceof C but not for primitives.

O is the argument to hasInstance methods. JS has never auto-boxed primitive argument values.

# Bradley Meck (9 years ago)

You are correct, it is not the primitive being boxed, but the primitive being passed into @@hasInstance.

Still consider it very strange that I can change if 1 instanceof Number but if that was intentional /shrug. Typeof I guess will be the safe way to detect things.

# Allen Wirfs-Brock (9 years ago)

On Jun 16, 2015, at 6:04 AM, Bradley Meck wrote:

You are correct, it is not the primitive being boxed, but the primitive being passed into @@hasInstance.

people.mozilla.org/~jorendorff/es6-draft.html#sec-instanceofoperator doesn't box O passed into the the @@hasInstance method. See step 4.a. Are you using a transpiler? Maybe it is buggy.