chakra-core / ChakraCore

ChakraCore is an open source Javascript engine with a C API.
MIT License
9.12k stars 1.2k forks source link

Realization of TypedArray's [[DefineOwnProperty]] is inconsistent with other engines #3532

Open sunlili opened 7 years ago

sunlili commented 7 years ago

Hello, The following code behaves inconsistent with other engines.

var get_cnt = 0;
var int16 = new Int16Array(4);
class dummy {
    constructor() {
        print("in constructor");
        return int16;
    }
}

var handler = {
    get: function(oTarget, sKey) {
        print("get " + sKey.toString());
        if (sKey.toString()=="constructor") {
            return { [Symbol.species] : dummy };
        } else if (Number(sKey.toString()) != NaN) {
            get_cnt++;  
            return 0x10;
        }
        return Reflect.get(oTarget, sKey);
    },
    has: function (oTarget, sKey) {
        print("has " + sKey.toString());
        return Reflect.has(oTarget, sKey);
    },
};

var words1 = ["abc", "aaaaaaa", "abcd", "aaaaabcd", "abcde", "aaaaabcdef"];
var words2 = ["abc", "aaaaaaa", "abcd", "aaaaabcd", "abcde", "aaaaabcdef"];
var p = new Proxy(words2, handler);

var longWords = words1.filter.call(p, function(word){
    return true;
});

print(longWords);

Chakra 1.7-release result:

get length
get constructor
in constructor
has 0
get 0
has 1
get 1
has 2
get 2
has 3
get 3
has 4
get 4
has 5
get 5
has 6
has 7
has 8
has 9
has 10
has 11
has 12
has 13
has 14
has 15
16,16,16,16

spidermonkey result:

get length
get constructor
in constructor
has 0
get 0
int16Array.js:31:17 TypeError: can't redefine non-configurable property 0

v8 result:

get length
get constructor
in constructor
has 0
get 0
int16Array.js:31: TypeError: Cannot redefine property: 0
var longWords = words1.filter.call(p, function(word){
                              ^
TypeError: Cannot redefine property: 0
    at Proxy.filter (<anonymous>)
    at int16Array.js:31:31

ICT BT group 2017.8.15

dilijev commented 7 years ago

Seems to have to do with the following spec sections.

Reduced repro with some notes in comments. Produces shorter output, shown below.

class dummy {
    constructor() {
        print("in constructor");
        return new Int16Array(4);
    }
}

var handler = {
    get: function(oTarget, sKey) {
        print("get " + sKey.toString());
        if (sKey.toString()=="constructor") {
            // return function(){}; // no repro
            // return { [Symbol.species] : Int16Array }; // ch = sm; disagree with d8
            // return { [Symbol.species] : Uint32Array }; // ch = sm; disagree with d8
            return { [Symbol.species] : dummy };
        } else if (Number(sKey.toString()) != NaN) {
            return 4;
        }
        print(`reflect.get ${oTarget} ${sKey}`);
        return Reflect.get(oTarget, sKey);
    },
    has: function (oTarget, sKey) {
        print("has " + sKey.toString());
        return Reflect.has(oTarget, sKey);
    },
};

var words1 = ["a", 42, {}];
var p = new Proxy(words1, handler);

// var longWords = words1.filter.call(p, function(word){
var boundFilter = (Array.prototype.filter.bind(p));
// var boundFilter = (Array.prototype.concat.bind(p));
// var boundFilter = (Array.prototype.map.bind(p));
// var boundFilter = (Array.prototype.slice.bind(p));
// var boundFilter = (Array.prototype.splice.bind(p)); // disagreement between d8, sm, ch
var longWords = boundFilter(function(word){
    print("word = " + word);
    return true;
});

print(longWords);

Output:

>eshost -h d8,ch-1.7.1,sm -ts test.js
┌──────────┬───────────────────────────────────────────────────────┐
│ ch-1.7.1 │ get length                                            │
│          │ get constructor                                       │
│          │ in constructor                                        │
│          │ has 0                                                 │
│          │ get 0                                                 │
│          │ word = 4                                              │
│          │ has 1                                                 │
│          │ get 1                                                 │
│          │ word = 4                                              │
│          │ has 2                                                 │
│          │ get 2                                                 │
│          │ word = 4                                              │
│          │ has 3                                                 │
│          │ 4,4,4,0                                               │
├──────────┼───────────────────────────────────────────────────────┤
│ d8       │ get length                                            │
│          │ get constructor                                       │
│          │ in constructor                                        │
│          │ has 0                                                 │
│          │ get 0                                                 │
│          │ word = 4                                              │
│          │ TypeError: Cannot redefine property: 0                │
├──────────┼───────────────────────────────────────────────────────┤
│ sm       │ get length                                            │
│          │ get constructor                                       │
│          │ in constructor                                        │
│          │ has 0                                                 │
│          │ get 0                                                 │
│          │ word = 4                                              │
│          │ TypeError: can't redefine non-configurable property 0 │
└──────────┴───────────────────────────────────────────────────────┘
dilijev commented 6 years ago

Still repros (included jsc in this output):

## Source
class dummy {
    constructor() {
        print("in constructor");
        return new Int16Array(4);
    }
}

var handler = {
    get: function(oTarget, sKey) {
        print("get " + sKey.toString());
        if (sKey.toString()=="constructor") {
            // return function(){}; // no repro
            // return { [Symbol.species] : Int16Array }; // ch = sm; disagree with d8
            // return { [Symbol.species] : Uint32Array }; // ch = sm; disagree with d8
            return { [Symbol.species] : dummy };
        } else if (Number(sKey.toString()) != NaN) {
            return 4;
        }
        print(`reflect.get ${oTarget} ${sKey}`);
        return Reflect.get(oTarget, sKey);
    },
    has: function (oTarget, sKey) {
        print("has " + sKey.toString());
        return Reflect.has(oTarget, sKey);
    },
};

var words1 = ["a", 42, {}];
var p = new Proxy(words1, handler);

// var longWords = words1.filter.call(p, function(word){
var boundFilter = (Array.prototype.filter.bind(p));
// var boundFilter = (Array.prototype.concat.bind(p));
// var boundFilter = (Array.prototype.map.bind(p));
// var boundFilter = (Array.prototype.slice.bind(p));
// var boundFilter = (Array.prototype.splice.bind(p)); // disagreement between d8, sm, ch
var longWords = boundFilter(function(word){
    print("word = " + word);
    return true;
});

print(longWords);

┌──────────┬───────────────────────────────────────────────────────────────────────────────────────────┐
│ jsvu-ch  │ get length                                                                                │
│          │ get constructor                                                                           │
│          │ in constructor                                                                            │
│          │ has 0                                                                                     │
│          │ get 0                                                                                     │
│          │ word = 4                                                                                  │
│          │ has 1                                                                                     │
│          │ get 1                                                                                     │
│          │ word = 4                                                                                  │
│          │ has 2                                                                                     │
│          │ get 2                                                                                     │
│          │ word = 4                                                                                  │
│          │ has 3                                                                                     │
│          │ 4,4,4,0                                                                                   │
├──────────┼───────────────────────────────────────────────────────────────────────────────────────────┤
│ jsvu-jsc │ get length                                                                                │
│          │ get constructor                                                                           │
│          │ in constructor                                                                            │
│          │ has 0                                                                                     │
│          │ get 0                                                                                     │
│          │ word = 4                                                                                  │
│          │ TypeError: Attempting to configure non-configurable property on a typed array at index: 0 │
├──────────┼───────────────────────────────────────────────────────────────────────────────────────────┤
│ jsvu-sm  │ get length                                                                                │
│          │ get constructor                                                                           │
│          │ in constructor                                                                            │
│          │ has 0                                                                                     │
│          │ get 0                                                                                     │
│          │ word = 4                                                                                  │
│          │ TypeError: can't redefine non-configurable property 0                                     │
├──────────┼───────────────────────────────────────────────────────────────────────────────────────────┤
│ jsvu-v8  │ get length                                                                                │
│          │ get constructor                                                                           │
│          │ in constructor                                                                            │
│          │ has 0                                                                                     │
│          │ get 0                                                                                     │
│          │ word = 4                                                                                  │
│          │ TypeError: Cannot redefine property: 0                                                    │
└──────────┴───────────────────────────────────────────────────────────────────────────────────────────┘
LouisLaf commented 6 years ago

Looks like we'll need to duplicate all the typed arrays vtables to fix this and maintain perf.