Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upSortCompare (Array#sort) #302
Comments
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
|
Not trying to be obtuse: what's the ECMA262 bug here? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ljharb
Jan 20, 2016
Member
If SpiderMonkey and Chakra follow the spec, but v8 and JSC don't, then this is a v8 and a JSC bug. @Yaffle can you confirm that SpiderMonkey and Chakra follow the spec here?
|
If SpiderMonkey and Chakra follow the spec, but v8 and JSC don't, then this is a v8 and a JSC bug. @Yaffle can you confirm that SpiderMonkey and Chakra follow the spec here? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bterlson
Jan 20, 2016
Member
Also how does the first and second case differ? Maybe the first means to assign undefined rather than delete the property?
|
Also how does the first and second case differ? Maybe the first means to assign undefined rather than delete the property? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bterlson
Jan 20, 2016
Member
Here's the results I get, with the update I suggest above:
spidermonkey
Undefined elements
Holes
Holes with prototype property
chakra
Undefined elements
Holes
Holes with prototype property
d8
Undefined elements
comparator was called for an undefined value
Holes
comparator was called for a hole
Holes with prototype property
comparator was called for a value from prototype
Anyone using eshost can use the following script:
print('##### Undefined elements');
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var counter = 0;
array.sort(function (a, b) {
if (counter === 0) {
for (var i = 0; i < array.length; i += 1) {
array[i] = undefined;
}
}
if (counter === 1) {
if (a == undefined || b == undefined) {
print("comparator was called for an undefined value");
}
}
counter += 1;
return a - b;
});
print('##### Holes');
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var counter = 0;
array.sort(function (a, b) {
if (counter === 0) {
for (var i = 0; i < array.length; i += 1) {
delete array[i];
}
}
if (counter === 1) {
if (a == undefined || b == undefined) {
print("comparator was called for a hole");
}
}
counter += 1;
return a - b;
});
print('##### Holes with prototype property');
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var counter = 0;
array.sort(function (a, b) {
if (counter === 0) {
for (var i = 0; i < array.length; i += 1) {
delete array[i];
Object.prototype[i.toString()] = 42;
}
}
if (counter === 1) {
if (a === 42 || b === 42) {
print("comparator was called for a value from prototype");
}
}
counter += 1;
return a - b;
});|
Here's the results I get, with the update I suggest above: spidermonkeyUndefined elementsHolesHoles with prototype propertychakraUndefined elementsHolesHoles with prototype propertyd8Undefined elementscomparator was called for an undefined value Holescomparator was called for a hole Holes with prototype propertycomparator was called for a value from prototype Anyone using eshost can use the following script: print('##### Undefined elements');
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var counter = 0;
array.sort(function (a, b) {
if (counter === 0) {
for (var i = 0; i < array.length; i += 1) {
array[i] = undefined;
}
}
if (counter === 1) {
if (a == undefined || b == undefined) {
print("comparator was called for an undefined value");
}
}
counter += 1;
return a - b;
});
print('##### Holes');
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var counter = 0;
array.sort(function (a, b) {
if (counter === 0) {
for (var i = 0; i < array.length; i += 1) {
delete array[i];
}
}
if (counter === 1) {
if (a == undefined || b == undefined) {
print("comparator was called for a hole");
}
}
counter += 1;
return a - b;
});
print('##### Holes with prototype property');
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var counter = 0;
array.sort(function (a, b) {
if (counter === 0) {
for (var i = 0; i < array.length; i += 1) {
delete array[i];
Object.prototype[i.toString()] = 42;
}
}
if (counter === 1) {
if (a === 42 || b === 42) {
print("comparator was called for a value from prototype");
}
}
counter += 1;
return a - b;
}); |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Yaffle
Jan 20, 2016
Contributor
@bterlson , the spec tells when and how to call comparefn:
The arguments for calls to SortCompare are values returned by a previous call to the [[Get]] internal method, unless the properties accessed by those previous calls did not exist according to HasOwnProperty. If both perspective arguments to SortCompare correspond to non-existent properties, use +0 instead of calling SortCompare. If only the first perspective argument is non-existent use +1. If only the second perspective argument is non-existent use -1.
and steps forSortComparehas checks forundefinedvalues.
Well... not a big bug...
|
@bterlson , the spec tells when and how to call
Well... not a big bug... |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Yaffle
Jan 20, 2016
Contributor
@ljharb , SpiderMonkey may switch to "self-hosted" Array#sort ( https://bugzilla.mozilla.org/show_bug.cgi?id=715181 ) and there is a suggestion to use same behaviour for undefined values and holes as in V8 and JSC
|
@ljharb , SpiderMonkey may switch to "self-hosted" Array#sort ( https://bugzilla.mozilla.org/show_bug.cgi?id=715181 ) and there is a suggestion to use same behaviour for undefined values and holes as in V8 and JSC |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
bterlson
Jan 20, 2016
Member
@Yaffle in other words, you are requesting a spec change to the v8/jsc approach? If so, can you expand on why it is necessary and/or desirable behavior? Otherwise this seems like a bug in V8 and JSC that SM should not copy...
|
@Yaffle in other words, you are requesting a spec change to the v8/jsc approach? If so, can you expand on why it is necessary and/or desirable behavior? Otherwise this seems like a bug in V8 and JSC that SM should not copy... |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
allenwb
Jan 20, 2016
Member
The special treatment of non-existent properties dates back to the ES3 spec. ES5 changes the language used to describe it from prose to the use of [[HasProperty]]. ES6 moved the test from inside of SortCompare into a guard on calling SortCompare. None of them call a user provided comparefn for non-existent properties.
I don't recall the exact motivation for the ES6 refactoring but I'm pretty use that there are either es-discuss or bugs.ecmascript.org discussions that relate it it.
Note that ES3 spec. has an explicit change from ES1&2 which did not explicitly check for non-existent properties and which explicitly stated that the handling of non-existent properties was implementation-dependent.
Clearly, when ES3 was being developed a decision was made that undefined values and holes should not have the same treatment. In particular, that holes sort after undefined at the end of the array. I don't see why we would want to change that long standing design design.
|
The special treatment of non-existent properties dates back to the ES3 spec. ES5 changes the language used to describe it from prose to the use of [[HasProperty]]. ES6 moved the test from inside of SortCompare into a guard on calling SortCompare. None of them call a user provided comparefn for non-existent properties. I don't recall the exact motivation for the ES6 refactoring but I'm pretty use that there are either es-discuss or bugs.ecmascript.org discussions that relate it it. Note that ES3 spec. has an explicit change from ES1&2 which did not explicitly check for non-existent properties and which explicitly stated that the handling of non-existent properties was implementation-dependent. Clearly, when ES3 was being developed a decision was made that undefined values and holes should not have the same treatment. In particular, that holes sort after undefined at the end of the array. I don't see why we would want to change that long standing design design. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
rossberg
Jan 21, 2016
Member
@allenwb, motivation might be that this semantics induces a non-zero runtime cost while having a zero real-world benefit.
|
@allenwb, motivation might be that this semantics induces a non-zero runtime cost while having a zero real-world benefit. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
anba
Jan 21, 2016
Contributor
I don't recall the exact motivation for the ES6 refactoring but I'm pretty use that there are either es-discuss or bugs.ecmascript.org discussions that relate it it.
|
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Yaffle
Jan 26, 2016
Contributor
https://hg.mozilla.org/mozilla-central/rev/1c4b0a89fd5b - Firefox 47 does not use HasOwnProperty check and patches holes (note: the body of comparefn should not be equal to return a-b):
var array = [1, 2, 3, 4, 5, 6, 7, 8, , 10];
array.sort(function (a, b) {
return a < b ? -1 : +1;
});
console.log(array, "9" in array);
|
https://hg.mozilla.org/mozilla-central/rev/1c4b0a89fd5b - Firefox 47 does not use HasOwnProperty check and patches holes (note: the body of comparefn should not be equal to
|
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ljharb
Jan 26, 2016
Member
@Yaffle that doesn't patch holes - I get Array [ 1, 2, 3, 4, 5, 6, 7, 8, 10, <1 empty slot> ] in the FF 46 console. It just seems to sort holes at the end (which is what #302 (comment) says)
|
@Yaffle that doesn't patch holes - I get |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Yaffle
Jan 26, 2016
Contributor
@ljharb , the change was applied recently, do you use the latest nightly build?
|
@ljharb , the change was applied recently, do you use the latest nightly build? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
|
I used v46. On the latest, v47, I see the same behavior. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
|
somehow I see |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ljharb
Jan 26, 2016
Member
Ah, oops, yes I see the change you mean. 46 said "empty slot" but 47 says "undefined". Thanks.
|
Ah, oops, yes I see the change you mean. 46 said "empty slot" but 47 says "undefined". Thanks. |
Yaffle commentedJan 20, 2016
http://tc39.github.io/ecma262/#sec-sortcompare
According to spec
comparefnwill be never called for undefined values, but v8 and WebKit implementations do not implement this behavior:The spec only tells that the
sort orderis implementation defined...v8 and WebKit do this for performance reasons, probably, because they can move undefined values and holes to the end of an array before the sorting and call
comparefndirectly:https://github.com/v8/v8/blob/master/src/js/array.js#L1194
https://github.com/WebKit/webkit/blob/cc80ecf5f0902e724a6e398d152323120e4e3b82/Source/JavaScriptCore/builtins/ArrayPrototype.js#L473