Closed Bug 638414 Opened 9 years ago Closed 9 years ago

Missing toString() function on Array.prototype - TypeError: Array.prototype.toString called on incompatible Object

Categories

(Core :: JavaScript Engine, defect)

1.9.2 Branch
x86_64
Windows 7
defect
Not set

Tracking

()

RESOLVED FIXED

People

(Reporter: terje.rosenlund, Unassigned)

Details

Attachments

(1 file)

User-Agent:       Mozilla/5.0 (Windows; U; Windows NT 6.1; nb-NO; rv:1.9.2.14) Gecko/20110218 Firefox/3.6.14
Build Identifier: Mozilla/5.0 (Windows; U; Windows NT 6.1; nb-NO; rv:1.9.2.14) Gecko/20110218 Firefox/3.6.14

trx.mirrorClone = function trx_mirrorClone(obj)
{
	function Clone() { }
	Clone.prototype = obj;
	return new Clone();
}
a = [1,2,3];
b = trx.mirrorClone(a);
	Obj.0 = 1
	Obj.1 = 2
	Obj.2 = 3
c = [a, b];
	Obj.0 = 1,2,3
	Obj.1 = undefined

The same code in IE gives:

	Obj.0 = 1,2,3
	Obj.1 = 1,2,3

Seems like ff allowes such a cloned object to be assigned to a var but not to an object- or array- member

Reproducible: Always
Version: unspecified → 1.9.2 Branch
>    Obj.1 = undefined

How did you determine that, exactly?

When I run the above in a 1.9.2 build and try to print out c[1], I get:

  TypeError: Array.prototype.toString called on incompatible Object

On trunk I get the sort of output you'd sorta expect (though calling Array.prototype.toString on a non-array shouldn't make you expect too much...)
> How did you determine that, exactly?
I have a dump(anything) function and the cause for 'undefined' was something like:

var v;
var s;
for (var i in c)
{  
	try {  val = c[i];  }  catch(e)	{  val = e.message;  }
	s + = 'Obj.' + i + ' = ' + v + '\n';
}
s;

My dump() function can be allowed to go deeper in objects and then I can se that the values are actually there:

Obj.0 = 1,2,3
Obj.0.0 = 1
Obj.0.1 = 2
Obj.0.2 = 3
Obj.1 = undefined
Obj.1.0 = 1
Obj.1.1 = 2
Obj.1.2 = 3

Obj.1 = undefined is still there and is very misleding so I have changed my dump() to:

	  try       {  v = v.toString();  }
	  catch (e) {  v = trx.constructor(v) ? '[' + typeof v + ' ' + trx.typeof(v) + ']' : e.message;  }

Then:

a = [1,2,3];
b = trx.mirrorClone(a);
c = [a, b]
	Obj.0 = 1,2,3
	Obj.1 = [object Array]

And that solved the mystery why I had to do the try/catch in the first place (years from now)

> On trunk I get the sort of output you'd sorta expect (though calling
> Array.prototype.toString on a non-array shouldn't make you expect too much...)

I have read that all objects should have a toString() function attached so even the String object has it. It's no less needed for prototypes I guess so that the problem no longer exists in trunk is good news

The subject on this case became very misleading so I have changed it 
(Objects created from another objects prototype can't be assigend to an object- or array- member)

I pressume this case should be closed as INVALID or maybe a dup? (the reason for change in trunk)
Summary: Objects created from another objects prototype can't be assigend to an object- or array- member → Missing toString() function on Array.prototype - TypeError: Array.prototype.toString called on incompatible Object
There _is_ a toString on Array.prototype in 1.9.2.  It just throws if you call it on a non-Array object.  ;)

This looks like worksforme, in any case.  If someone wants to find the bug where the change to Array.prototype.toString was made, they should feel free to change the resolution to duplicate, of course!
Status: UNCONFIRMED → RESOLVED
Closed: 9 years ago
Resolution: --- → WORKSFORME
Ok, seems like I don't have grasped the full consept of prototypes then?

I named this function mirrorcClone() because objects created by it acts different than what normaly could be called a clone. The differense is that it containes a relationsheep with the object its created from (master). Values from the master shines through on all objects created from the master until you assign values to the local copy(s). Deleting a local value makes the master value show up again. A change on a value in the master is reflected in all objects not having their local copy set. This makes it possible to create objects from a master object and use one or more values in it to inform all cloned objects by changing value(s) in the master.

This looks like pointers in C to me (pointers to vars in a pointer to a struct) and I have guessed that eg. a cloned array has pointers to its master array AND all its members as default. Pointers may be changed to point to localy allocated space for vars and the array itself. Deleting a local value makes its pointer to fall back on its master.

Therefore my object is not an array but a pointer to an array and must be dereferenced to be accessed. Dereferencing is normaly done by javascript itself but clearly not in this case

In the past I have not been able to create an equivalent to pointers to vars in C but this approach makes it possible and gives me very powerfull new possibilities in javascript

I might have misunderstood this consept and would realy appreciate a correction if so
> Therefore my object is not an array but a pointer to an array and must be
> dereferenced to be accessed.

Not quite.  Your object is just an object.  Property lookups on it will look for properties on the prototype chain, though.

So if you have an object X whose prototype is an array A, then looking up toString on X will look on X, then on A, then on the prototype of A, and find it there.  The function it will find is Array.prototype.toString.

Then you call the function, with the |this| object set to X.  This is the same as doing this:

  Array.prototype.toString.call(X);

What that does depends on what the spec says.  The spec for Array.prototype.toString (ECMA-262 edition 5 section 15.4.4.2) is:

  1.  Let 'array' be the result of calling ToObject on the |this| value.
  2.  Let 'func' be the result of calling the [[Get]] internal method of
      'array' with argument "join".
  3.  If IsCallable(func) is false, then let 'func' be the standard
      built-in method Object.prototype.toString (15.2.4.2).
  4.  Return the result of calling the [[Call]] internal method of 'func'
      providing 'array' as the this value and an empty arguments list.
So Let 'array' be the result of calling ToObject on the |this| value has been changed in trunk then

Always a pleasure to be wrong when you are answering my requests - Thank you!
> So Let 'array' be the result of calling ToObject on the |this| value has been
> changed in trunk then

No, that part hasn't changed.  Something after that has, presumably.
Array.prototype.toString used to not be generic -- it would throw if called on anything but an Array.  ES5 changed that to be generic to anything by delegating behavior to a "join" property if one was present.  We implemented that change in bug 562446.
So fixed by bug 562446, not WFM.

/be
Resolution: WORKSFORME → FIXED
(In reply to comment #9)
> Array.prototype.toString used to not be generic

- Was there any generic toString() function in 1.9.2.14 at all?
 (if so I may override default Array.prototype.toString(?))

- When will this fix be in a release?
> - Was there any generic toString() function in 1.9.2.14 at all?

Object.prototype.toString.

> - When will this fix be in a release?

When Firefox 4 shops.  Figure a few weeks at most.
In my root script:

Array.constructor.prototype.toString = Object.constructor.prototype.toString;

Works!
Does'nt that mean that the fix in trunk could be simply to delete the Array.constructor.prototype.toString() function ?

Should cause Array to use first toString() found in prototype-chain = Object.toString() (?)
I'm not sure what comment 14 is asking.
ES5 15.2.4 Properties of the Object Prototype Object
The value of the [[Prototype]] internal property of the Object prototype object is null

ES5 15.4.4: The value of the [[Prototype]] internal property of the Array prototype object is the standard built-in Object prototype object (15.2.4).

Array is derived from Object so a missing toString() function in Array would cause a lookup in the prototype-chain and find it in Object?
Mid-air collision! Posts anyway but no need for answer:)

Changed my root script from: Array.constructor.prototype.toString = Object.constructor.prototype.toString;

to: delete Array.prototype.toString;

Both eliminates TypeError: Array.prototype.toString called on incompatible Object and confirms my assumption
You need to log in before you can comment on or make changes to this bug.