Closed Bug 882301 Opened 11 years ago Closed 11 years ago

Native methods on types inherited from builtins: method called on incompatible Object

Categories

(Core :: JavaScript Engine, defect)

21 Branch
x86_64
Windows 7
defect
Not set
normal

Tracking

()

RESOLVED INVALID

People

(Reporter: taricorp, Unassigned)

Details

When inheriting from a builtin type, certain native method calls on the resultant object throw TypeError, claiming the type is incompatible.

For example, given the following type:

MyArray = function () {
    Array.apply(this, arguments);
};
MyArray.prototype = new Array();

I see this:
> a = new MyArray();
TypeError: toSource method called on incompatible Object

The object appears to be created correctly, but printout of the value fails. Most methods and properties on it work as expected, following the above instantiation:

> a.length
0
> a.push(0);
1
> a[0]
0

This is not limited to Arrays, and other types appear to be affected to a greater extent:

MyMap = function() { Map.apply(this, arguments); };
MyMap.prototype = new Map();

> m = new MyMap();
[object Object]
> m.set('foo', 'bar');
TypeError: set method called on incompatible Object
> m.get('baz');
TypeError: get method called on incompatible Object

The prototype chain is correct:
> a instanceof Array && m instanceof Map
true

In both cases, method calls on My{Array,Map} should behave as if they were unadulterated instances of their prototypes and not throw TypeErrors.
This issue appears similar to the one reported in #593848.
While I very much agree that this would be desirable, it's not actually something we can change: The builtin objects are specified like this and implementing what you're asking for would be an incompatible change.

Take Map.set for example. In [1], you can see exactly how this method is specified to work. Step 3 of that algorithm is what makes this fail for your MyMap case: since MyMap doesn't have an internal data property [[MyMap]], we have to throw a type error.

The same is true for most of the builtin objects' methods. Additionally, there are quite a few things that wouldn't work as expected, even if we were to ignore the spec. As an example, try running

var a = new MyArray();
a.length;
a[0] = 1;
a.length;

The output you'll see will be 0 for the last a.length, too, as MyArray doesn't have the special handling of properties that makes Array change its length property when appropriate.


Having said all this, there is hope, however: the classes specification coming in es6 will enable extending builtin objects as expected. We haven't yet implemented that, but will do that in the near to medium future.



[1]: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-15.14.4.9
Status: UNCONFIRMED → RESOLVED
Closed: 11 years ago
Resolution: --- → INVALID
I can't say I didn't anticipate the possibility of such a response. :)
I see a few interesting differences between the ES6 draft I was referring to ([1]) and the one you link, but it boils down to a difference in semantics between Type.call(this) from within a constructor and using the actual inheritance mechanism.

Rather interestingly, the Array example I cited appears to work as expected in V8. The language in the ES5 standard seems rather imprecise on the behavior of Array(), so I suppose either interpretation might be acceptable.

In any case, I'll just have to work around this limitation for now. Thanks for your consideration.

[1] http://wiki.ecmascript.org/lib/exe/fetch.php?id=harmony%3Aspecification_drafts&cache=cache&media=harmony:working_draft_ecma-262_edition_6_05-14-13-nomarkup.pdf
(In reply to Peter Marheine from comment #2)
> Rather interestingly, the Array example I cited appears to work as expected
> in V8. The language in the ES5 standard seems rather imprecise on the
> behavior of Array(), so I suppose either interpretation might be acceptable.

Oh, I meant to say something about that: the difference is purely in how our shell deals with printing values vs. how V8 does it.

If you change
> a = new MyArray();
to
> a = new MyArray(); "";
you won't get an error, anymore, as the `toSource` method will be called on the last expression's result in the input.

Apparently, our value printing is a bit more strict than V8's, but that's pretty much it.
Hm, I thought it was working exactly as expected, but after specifically testing your earlier example with MyArray.length, I see you're right in that V8 still isn't doing everything I expect.

Thanks again. :)
You need to log in before you can comment on or make changes to this bug.