Allen Wirfs-Brock (2013-08-28T17:19:45.000Z)
On Aug 28, 2013, at 5:09 AM, André Bargull wrote:

> This test case [1] from SpiderMonkey failed when I applied the latest spec changes to my ES6 test implementation. Based on the bug report at [2], this might be another web-compatibility issue - but is that really the case? Any input appreciated!
> 
> Thanks,
> André
> 
> 
> [1] https://github.com/mozilla/mozilla-central/blob/master/js/src/jit-test/tests/basic/bug683140.js
> [2] https://bugzilla.mozilla.org/show_bug.cgi?id=683140

Very interesting.  Here is one of the test cases André is talking about.

var g = newGlobal("new-compartment");  //or any non-standard mechanism to get a handle on another realm's global object
g.a = g.Array(10);
print(g.a instanceof Array);
g.a = Array.prototype.slice(g.a);
print(g.a instanceof Array);

--
Output ES<6: false, true
Output ES6:  false, false

Also note that if the instanceof tests above were replaced with Array.isArray(g.a) you would get all trues in both ES5 and ES6.  instanceof is not a reliable test for array-ness when objects flow between realms..

The problem is that in ES<6 slice always returned a new Array instance using the Array of the realm associated with the invoked slice function.  In ES6 slice returns an object that is determine based upon the actual this value passed to slice.  In the default case like above, this will be the a new Array instance using the Array of the realm associated with the this value.

The reason for this change is to enable slice to behave rationally for Array subclasses. For example:

class SubArray extends Array {
   get isSubArray() {return true}
}

let sa = new SubArray(10);
let sal = sa.slice(1);
print(sa.isSubArray);                   //true for Es6 slice, undefined for ES5 slice
print(sal instanceof SubArray); //true for ES6 slice spec. false for ES5 slice spec.
print(sal instanceof Array);        //true for ES6. true for ES5

The way that the result object creation works in the ES6 spec. is approximately:
 
 slice(start, end) {
     ...
     let result = new this.constructor();  //create an instance of the same "class" as the this value
     ...
}


Continuing to allow slice (and most other Array.prototype methods) to work as expected with subclasses and still preserving ES5 cross-realm instanceof compatability for these cases is at best hackish.  Something like: 

slice(start, end) {
     ...
     let resultConstructor = this.constructor; 
     if (realm(resultConstructor) !== thisFunctionsRealm) {
               if (isIntrinsicArrayConstrutorForAnyRealm(resultConstructor) then resultConstructor = thisFunctionsReam.intrinsics("Array");
     }
     let result = new resultConstructor();
     ...
 }

I guess I could put mechanism to support this into the spec...

However, do we actually understand what the real world use case that tripped over this in FF?  It'seasy to write a test case that detects this difference but that sort of instanceof test doesn't seem to make much sense for production code.

Allen 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20130828/17a8e856/attachment.html>
domenic at domenicdenicola.com (2013-08-31T21:07:58.199Z)
Very interesting.  Here is one of the test cases André is talking about.

```js
var g = newGlobal("new-compartment");  //or any non-standard mechanism to get a handle on another realm's global object
g.a = g.Array(10);
print(g.a instanceof Array);
g.a = Array.prototype.slice(g.a);
print(g.a instanceof Array);
```

--
Output ES<6: false, true
Output ES6:  false, false

Also note that if the `instanceof` tests above were replaced with `Array.isArray(g.a)` you would get all trues in both ES5 and ES6.  `instanceof` is not a reliable test for array-ness when objects flow between realms..

The problem is that in ES<6 `slice` always returned a new `Array` instance using the `Array` of the realm associated with the invoked slice function.  In ES6 `slice` returns an object that is determine based upon the actual this value passed to `slice`.  In the default case like above, this will be the a new `Array` instance using the `Array` of the realm associated with the this value.

The reason for this change is to enable `slice` to behave rationally for `Array` subclasses. For example:

```js
class SubArray extends Array {
   get isSubArray() {return true}
}

let sa = new SubArray(10);
let sal = sa.slice(1);
print(sa.isSubArray);                   //true for Es6 slice, undefined for ES5 slice
print(sal instanceof SubArray); //true for ES6 slice spec. false for ES5 slice spec.
print(sal instanceof Array);        //true for ES6. true for ES5
```

The way that the result object creation works in the ES6 spec. is approximately:
 
```js
 slice(start, end) {
     ...
     let result = new this.constructor();  //create an instance of the same "class" as the this value
     ...
}
```


Continuing to allow `slice` (and most other `Array.prototype` methods) to work as expected with subclasses and still preserving ES5 cross-realm `instanceof` compatability for these cases is at best hackish.  Something like: 

```js
slice(start, end) {
     ...
     let resultConstructor = this.constructor; 
     if (realm(resultConstructor) !== thisFunctionsRealm) {
               if (isIntrinsicArrayConstrutorForAnyRealm(resultConstructor) then resultConstructor = thisFunctionsReam.intrinsics("Array");
     }
     let result = new resultConstructor();
     ...
 }
```

I guess I could put mechanism to support this into the spec...

However, do we actually understand what the real world use case that tripped over this in FF?  It's easy to write a test case that detects this difference but that sort of `instanceof` test doesn't seem to make much sense for production code.
domenic at domenicdenicola.com (2013-08-30T17:07:45.577Z)
Very interesting.  Here is one of the test cases André is talking about.

```js
var g = newGlobal("new-compartment");  //or any non-standard mechanism to get a handle on another realm's global object
g.a = g.Array(10);
print(g.a instanceof Array);
g.a = Array.prototype.slice(g.a);
print(g.a instanceof Array);
```

--
Output ES<6: false, true
Output ES6:  false, false

Also note that if the instanceof tests above were replaced with Array.isArray(g.a) you would get all trues in both ES5 and ES6.  instanceof is not a reliable test for array-ness when objects flow between realms..

The problem is that in ES<6 slice always returned a new Array instance using the Array of the realm associated with the invoked slice function.  In ES6 slice returns an object that is determine based upon the actual this value passed to slice.  In the default case like above, this will be the a new Array instance using the Array of the realm associated with the this value.

The reason for this change is to enable slice to behave rationally for Array subclasses. For example:

```js
class SubArray extends Array {
   get isSubArray() {return true}
}

let sa = new SubArray(10);
let sal = sa.slice(1);
print(sa.isSubArray);                   //true for Es6 slice, undefined for ES5 slice
print(sal instanceof SubArray); //true for ES6 slice spec. false for ES5 slice spec.
print(sal instanceof Array);        //true for ES6. true for ES5
```

The way that the result object creation works in the ES6 spec. is approximately:
 
```js
 slice(start, end) {
     ...
     let result = new this.constructor();  //create an instance of the same "class" as the this value
     ...
}
```


Continuing to allow slice (and most other Array.prototype methods) to work as expected with subclasses and still preserving ES5 cross-realm instanceof compatability for these cases is at best hackish.  Something like: 

```js
slice(start, end) {
     ...
     let resultConstructor = this.constructor; 
     if (realm(resultConstructor) !== thisFunctionsRealm) {
               if (isIntrinsicArrayConstrutorForAnyRealm(resultConstructor) then resultConstructor = thisFunctionsReam.intrinsics("Array");
     }
     let result = new resultConstructor();
     ...
 }
```

I guess I could put mechanism to support this into the spec...

However, do we actually understand what the real world use case that tripped over this in FF?  It's easy to write a test case that detects this difference but that sort of instanceof test doesn't seem to make much sense for production code.