-
Notifications
You must be signed in to change notification settings - Fork 20
Update sorting methods to use abstract closures #78
Changes from 4 commits
32d1e92
a0c6748
7e2a678
333f55d
17779e6
7c57a62
0152db2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,110 @@ <h1>Array Objects</h1> | |
<emu-clause id="sec-properties-of-the-array-prototype-object"> | ||
<h1>Properties of the Array Prototype Object</h1> | ||
|
||
<emu-clause id="sec-array.prototype.sort" oldids="sec-sortcompare"> | ||
<h1>Array.prototype.sort ( _comparefn_ )</h1> | ||
|
||
<p>When the `sort` method is called, the following steps are taken:</p> | ||
<emu-alg> | ||
1. [id="step-array-sort-comparefn"] If _comparefn_ is not *undefined* and IsCallable(_comparefn_) is *false*, throw a *TypeError* exception. | ||
1. Let _obj_ be ? ToObject(*this* value). | ||
1. [id="step-array-sort-len"] Let _len_ be ? LengthOfArrayLike(_obj_). | ||
1. Let _SortCompare_ be a new Abstract Closure with parameters (_x_, _y_) that captures _comparefn_ and performs the following steps when called: | ||
1. <del>If _x_ and _y_ are both *undefined*, return *+0*<sub>𝔽</sub>.</del> | ||
1. <del>If _x_ is *undefined*, return *1*<sub>𝔽</sub>.</del> | ||
1. <del>If _y_ is *undefined*, return *-1*<sub>𝔽</sub>.</del> | ||
1. <del>If _comparefn_ is not *undefined*, then</del> | ||
1. <del>Let _v_ be ? ToNumber(? Call(_comparefn_, *undefined*, « _x_, _y_ »)).</del> | ||
1. <del>If _v_ is *NaN*, return *+0*<sub>𝔽</sub>.</del> | ||
1. <del>Return _v_.</del> | ||
1. <del>[id="step-sortcompare-tostring-x"] Let _xString_ be ? ToString(_x_).</del> | ||
1. <del>[id="step-sortcompare-tostring-y"] Let _yString_ be ? ToString(_y_).</del> | ||
1. <del>Let _xSmaller_ be ! IsLessThan(_xString_, _yString_, *true*).</del> | ||
1. <del>If _xSmaller_ is *true*, return *-1*<sub>𝔽</sub>.</del> | ||
1. <del>Let _ySmaller_ be ! IsLessThan(_yString_, _xString_, *true*).</del> | ||
1. <del>If _ySmaller_ is *true*, return *1*<sub>𝔽</sub>.</del> | ||
1. <del>Return *+0*<sub>𝔽</sub>.</del> | ||
1. <ins>Return ? CompareArrayElements(_x_, _y_, _comparefn_).</ins> | ||
1. <del>Return ? SortIndexedProperties(_obj_, _len_, _SortCompare_).</del> | ||
1. <ins>Let _sortedList_ be ? SortIndexedProperties(_obj_, _len_, _SortCompare_, *true*).</ins> | ||
1. <ins>Let _itemCount_ be the number of elements in _sortedList_.</ins> | ||
1. <ins>Let _j_ be 0.</ins> | ||
1. <ins>Repeat, while _j_ < _itemCount_,</ins> | ||
1. <ins>Perform ? CreateDataPropertyOrThrow(_obj_, ! ToString(𝔽(_j_)), _sortedList_[_j_]).</ins> | ||
1. <ins>Set _j_ to _j_ + 1.</ins> | ||
1. <ins>Repeat, while _j_ < _len_,</ins> | ||
1. <ins>Perform ? DeletePropertyOrThrow(_obj_, ! ToString(𝔽(_j_))).</ins> | ||
1. <ins>Set _j_ to _j_ + 1.</ins> | ||
1. <ins>Return _obj_.</ins> | ||
</emu-alg> | ||
</emu-clause> | ||
|
||
<emu-clause id="sec-comparearrayelements" type="abstract operation"> | ||
<h1> | ||
CompareArrayElements( | ||
_x_: unknown, | ||
_y_: unknown, | ||
_comparefn_: unknown | ||
ljharb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
): either a normal completion containing a Number or an abrupt completion | ||
</h1> | ||
<dl class="header"> | ||
</dl> | ||
<emu-alg> | ||
1. If _x_ and _y_ are both *undefined*, return *+0*<sub>𝔽</sub>. | ||
1. If _x_ is *undefined*, return *1*<sub>𝔽</sub>. | ||
1. If _y_ is *undefined*, return *-1*<sub>𝔽</sub>. | ||
1. If _comparefn_ is not *undefined*, then | ||
1. Let _v_ be ? ToNumber(? Call(_comparefn_, *undefined*, « _x_, _y_ »)). | ||
1. If _v_ is *NaN*, return *+0*<sub>𝔽</sub>. | ||
1. Return _v_. | ||
1. [id="step-sortcompare-tostring-x"] Let _xString_ be ? ToString(_x_). | ||
1. [id="step-sortcompare-tostring-y"] Let _yString_ be ? ToString(_y_). | ||
1. Let _xSmaller_ be ! IsLessThan(_xString_, _yString_, *true*). | ||
1. If _xSmaller_ is *true*, return *-1*<sub>𝔽</sub>. | ||
1. Let _ySmaller_ be ! IsLessThan(_yString_, _xString_, *true*). | ||
1. If _ySmaller_ is *true*, return *1*<sub>𝔽</sub>. | ||
1. Return *+0*<sub>𝔽</sub>. | ||
ljharb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
</emu-alg> | ||
</emu-clause> | ||
|
||
<emu-clause id="sec-sortindexedproperties" type="abstract operation"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
a) toSorted needs to write out to a new array, rather than mutate the original instance |
||
<h1> | ||
SortIndexedProperties ( | ||
_obj_: an Object, | ||
_len_: a non-negative integer, | ||
_SortCompare_: an Abstract Closure with two parameters, | ||
<ins>_checkHasProperty_: a Boolean</ins> | ||
): either a normal completion containing <del>an Object</del><ins>a List</ins> or an abrupt completion | ||
</h1> | ||
<dl class="header"> | ||
</dl> | ||
<emu-alg> | ||
1. Let _items_ be a new empty List. | ||
1. Let _k_ be 0. | ||
1. Repeat, while _k_ < _len_, | ||
1. Let _Pk_ be ! ToString(𝔽(_k_)). | ||
1. <del>Let _kPresent_ be ? HasProperty(_obj_, _Pk_).</del> | ||
1. <ins>If _checkHasProperty_ is *true*, then</ins> | ||
ljharb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
1. <ins>Let _kRead_ be ? HasProperty(_obj_, _Pk_).</ins> | ||
1. <ins>Else,</ins> | ||
1. <ins>Let _kRead_ be *true*.</ins> | ||
1. If <del>_kPresent_</del><ins>_kRead_</ins> is *true*, then | ||
1. Let _kValue_ be ? Get(_obj_, _Pk_). | ||
1. Append _kValue_ to _items_. | ||
1. Set _k_ to _k_ + 1. | ||
1. <del>Let _itemCount_ be the number of elements in _items_.</del> | ||
1. [id="step-array-sort"] Sort _items_ using an implementation-defined sequence of calls to _SortCompare_. If any such call returns an abrupt completion, stop before performing any further calls to _SortCompare_ or steps in this algorithm and return that Completion Record. | ||
1. <del>Let _j_ be 0.</del> | ||
1. <del>Repeat, while _j_ < _itemCount_,</del> | ||
1. <del>Perform ? Set(_obj_, ! ToString(𝔽(_j_)), _items_[_j_], *true*).</del> | ||
1. <del>Set _j_ to _j_ + 1.</del> | ||
1. <del>Repeat, while _j_ < _len_,</del> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Add a note that this only happens when we respect holes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good shout. Note added in |
||
1. <del>Perform ? DeletePropertyOrThrow(_obj_, ! ToString(𝔽(_j_))).</del> | ||
1. <del>Set _j_ to _j_ + 1.</del> | ||
1. <del>Return _obj_.</del> | ||
1. <ins>Return _items_.</ins> | ||
</emu-alg> | ||
|
||
<emu-clause id="sec-array.prototype.toReversed"> | ||
<h1>Array.prototype.toReversed ( )</h1> | ||
|
||
|
@@ -39,7 +143,7 @@ <h1>Array.prototype.toReversed ( )</h1> | |
</emu-clause> | ||
|
||
<emu-clause id="sec-array.prototype.toSorted"> | ||
<h1>Array.prototype.toSorted ( _compareFn_ )</h1> | ||
<h1>Array.prototype.toSorted ( _comparefn_ )</h1> | ||
|
||
<p>When the *toSorted* method is called, the following steps are taken:</p> | ||
|
||
|
@@ -48,19 +152,13 @@ <h1>Array.prototype.toSorted ( _compareFn_ )</h1> | |
1. Let _O_ be ? ToObject(*this* value). | ||
1. Let _len_ be ? LengthOfArrayLike(_O_). | ||
1. Let _A_ be ? ArrayCreate(𝔽(_len_)). | ||
1. Let _items_ be a new empty List. | ||
1. Let _k_ be 0. | ||
1. Repeat, while _k_ < _len_, | ||
1. Let _Pk_ be ! ToString(𝔽(_k_)). | ||
1. Let _kValue_ be ? Get(_O_, _Pk_). | ||
1. Append _kValue_ to _items_. | ||
1. Set _k_ to _k_ + 1. | ||
1. Sort _items_ using an implementation-defined sequence of calls to SortCompare. If any such call returns an abrupt completion, stop before performing any further calls to SortCompare or steps in this algorithm and return that completion. | ||
1. Let _SortCompare_ be a new Abstract Closure with parameters (_x_, _y_) that captures _comparefn_ and performs the following steps when called: | ||
1. Return ? CompareArrayElements(_x_, _y_, _comparefn_). | ||
1. Let _sortedList_ be ? SortIndexedProperties(_obj_, _len_, _SortCompare_, *false*). | ||
1. Let _j_ be 0. | ||
1. For each element _E_ of _items_, do | ||
1. Let _Pj_ be ! ToString(𝔽(_j_)). | ||
1. Perform ! CreateDataPropertyOrThrow(_A_, _Pj_, _E_). | ||
1. Set _j_ to _j_ + 1. | ||
1. Repeat, while _j_ < _len_, | ||
1. Perform ! CreateDataPropertyOrThrow(_A_, ! ToString(𝔽(_j_)), _sortedList_[_j_]). | ||
1. Set _j_ to _j_ + 1. | ||
1. Return _A_. | ||
</emu-alg> | ||
</emu-clause> | ||
|
@@ -197,6 +295,75 @@ <h1>The %TypedArray% Intrinsic Object</h1> | |
<emu-clause id="sec-properties-of-the-%typedarrayprototype%-object"> | ||
<h1>Properties of the %TypedArray% Prototype Object</h1> | ||
|
||
<emu-clause id="sec-%typedarray%.prototype.sort" oldids="sec-typedarraysortcompare"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
<h1>%TypedArray%.prototype.sort ( _comparefn_ )</h1> | ||
|
||
<p>The following steps are performed:</p> | ||
<emu-alg> | ||
1. If _comparefn_ is not *undefined* and IsCallable(_comparefn_) is *false*, throw a *TypeError* exception. | ||
1. Let _obj_ be the *this* value. | ||
1. Perform ? ValidateTypedArray(_obj_). | ||
1. Let _buffer_ be _obj_.[[ViewedArrayBuffer]]. | ||
1. Let _len_ be _obj_.[[ArrayLength]]. | ||
1. NOTE: The following closure performs a numeric comparison rather than the string comparison used in <emu-xref href="#sec-array.prototype.sort"></emu-xref>. | ||
1. Let _SortCompare_ be a new Abstract Closure with parameters (_x_, _y_) that captures _comparefn_ and _buffer_ and performs the following steps when called: | ||
1. <del>Assert: Both Type(_x_) and Type(_y_) are Number or both are BigInt.</del> | ||
1. <del>If _comparefn_ is not *undefined*, then</del> | ||
1. <del>Let _v_ be ? ToNumber(? Call(_comparefn_, *undefined*, « _x_, _y_ »)).</del> | ||
1. <del>If IsDetachedBuffer(_buffer_) is *true*, throw a *TypeError* exception.</del> | ||
1. <del>If _v_ is *NaN*, return *+0*<sub>𝔽</sub>.</del> | ||
1. <del>Return _v_.</del> | ||
1. <del>If _x_ and _y_ are both *NaN*, return *+0*<sub>𝔽</sub>.</del> | ||
1. <del>If _x_ is *NaN*, return *1*<sub>𝔽</sub>.</del> | ||
1. <del>If _y_ is *NaN*, return *-1*<sub>𝔽</sub>.</del> | ||
1. <del>If _x_ < _y_, return *-1*<sub>𝔽</sub>.</del> | ||
1. <del>If _x_ > _y_, return *1*<sub>𝔽</sub>.</del> | ||
1. <del>If _x_ is *-0*<sub>𝔽</sub> and _y_ is *+0*<sub>𝔽</sub>, return *-1*<sub>𝔽</sub>.</del> | ||
1. <del>If _x_ is *+0*<sub>𝔽</sub> and _y_ is *-0*<sub>𝔽</sub>, return *1*<sub>𝔽</sub>.</del> | ||
1. <del>Return *+0*<sub>𝔽</sub>.</del> | ||
1. <ins>Return ? CompareTypedArrayElements(_x_, _y_, _comparefn_, _buffer_).</ins> | ||
1. <del>Return ? SortIndexedProperties(_obj_, _len_, _SortCompare_).</del> | ||
1. <ins>Let _sortedList_ be ? SortIndexedProperties(_obj_, _len_, _SortCompare_, *false*).</ins> | ||
1. <ins>Let _j_ be 0.</ins> | ||
1. <ins>Repeat, while _j_ < _len_,</ins> | ||
1. <ins>Perform ! Set(_obj_, ! ToString(𝔽(_j_)), _sortedList_[_j_], *true*).</ins> | ||
1. <ins>Set _j_ to _j_ + 1.</ins> | ||
1. <ins>Return _obj_.</ins> | ||
</emu-alg> | ||
</emu-clause> | ||
|
||
<emu-clause id="sec-comparetypedarrayelements" type="abstract operation"> | ||
<h1> | ||
CompareTypedArrayElements( | ||
_x_: unknown, | ||
_y_: unknown, | ||
_comparefn_: unknown, | ||
_buffer_: An ArrayBuffer | ||
): either a normal completion containing a Number or an abrupt completion | ||
</h1> | ||
<dl class="header"> | ||
</dl> | ||
<emu-alg> | ||
1. Assert: Both Type(_x_) and Type(_y_) are Number or both are BigInt. | ||
1. If _comparefn_ is not *undefined*, then | ||
1. Let _v_ be ? ToNumber(? Call(_comparefn_, *undefined*, « _x_, _y_ »)). | ||
1. If IsDetachedBuffer(_buffer_) is *true*, throw a *TypeError* exception. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this need to be checked? Isn't it done implicitly with the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope, Get and Set don't fail on detached buffers. Also, this check happens after every call to [struck a false comment here, whoops] There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. This check was there right from the ES6 version of However until ES2021 sorting was defined as performing:
So the buffer could become detached before reading had finished. This was updated to match the current semantics in tc39/ecma262#1585 There were a lot of comments on that PR and maybe this was missed in amongst the noise? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, probably. Let's move discussion of what the behavior should be to tc39/ecma262#2646 (comment) (which is about a similar change to a different TA method). Changing the existing behavior is out of scope for this proposal, I imagine. |
||
1. If _v_ is *NaN*, return *+0*<sub>𝔽</sub>. | ||
1. Return _v_. | ||
1. If _x_ and _y_ are both *NaN*, return *+0*<sub>𝔽</sub>. | ||
1. If _x_ is *NaN*, return *1*<sub>𝔽</sub>. | ||
1. If _y_ is *NaN*, return *-1*<sub>𝔽</sub>. | ||
1. If _x_ < _y_, return *-1*<sub>𝔽</sub>. | ||
1. If _x_ > _y_, return *1*<sub>𝔽</sub>. | ||
1. If _x_ is *-0*<sub>𝔽</sub> and _y_ is *+0*<sub>𝔽</sub>, return *-1*<sub>𝔽</sub>. | ||
1. If _x_ is *+0*<sub>𝔽</sub> and _y_ is *-0*<sub>𝔽</sub>, return *1*<sub>𝔽</sub>. | ||
1. Return *+0*<sub>𝔽</sub>. | ||
</emu-alg> | ||
<emu-note> | ||
This performs a numeric comparison rather than the string comparison used in <emu-xref href="#sec-comparearrayelements"></emu-xref>. | ||
</emu-note> | ||
</emu-clause> | ||
|
||
<emu-clause id="sec-%typedarray%.prototype.toReversed"> | ||
<h1>%TypedArray%.prototype.toReversed ( )</h1> | ||
|
||
|
@@ -219,49 +386,27 @@ <h1>%TypedArray%.prototype.toReversed ( )</h1> | |
</emu-clause> | ||
|
||
<emu-clause id="sec-%typedarray%.prototype.toSorted"> | ||
<h1>%TypedArray%.prototype.toSorted ( _compareFn_ )</h1> | ||
<h1>%TypedArray%.prototype.toSorted ( _comparefn_ )</h1> | ||
|
||
<p>When the *toSorted* method is called, the following steps are taken:</p> | ||
|
||
<emu-alg> | ||
1. If _comparefn_ is not *undefined* and IsCallable(_comparefn_) is *false*, throw a *TypeError* exception. | ||
1. Let _O_ be the *this* value. | ||
1. Perform ? ValidateTypedArray(_O_). | ||
1. Let _buffer_ be _obj_.[[ViewedArrayBuffer]]. | ||
1. Let _len_ be _O_.[[ArrayLength]]. | ||
1. Let _A_ be ? TypedArrayCreateSameType(_O_, « 𝔽(_len_) »). | ||
1. Let _items_ be a new empty List. | ||
1. Let _k_ be 0. | ||
1. Repeat, while _k_ < _len_, | ||
1. Let _Pk_ be ! ToString(𝔽(_k_)). | ||
1. Let _kValue_ be ! Get(_O_, _Pk_). | ||
1. Append _kValue_ to _items_. | ||
1. Set _k_ to _k_ + 1. | ||
1. Sort _items_ using an implementation-defined sequence of calls to SortCompare. If any such call returns an abrupt completion, stop before performing any further calls to SortCompare or steps in this algorithm and return that completion. | ||
1. NOTE: The following closure performs a numeric comparison rather than the string comparison used in <emu-xref href="#sec-array.prototype.toSorted"></emu-xref>. | ||
1. Let _SortCompare_ be a new Abstract Closure with parameters (_x_, _y_) that captures _comparefn_ and _buffer_ and performs the following steps when called: | ||
1. Return ? CompareTypedArrayElements(_x_, _y_, _comparefn_, _buffer_). | ||
1. Let _sortedList_ be ? SortIndexedProperties(_obj_, _len_, _SortCompare_, *false*). | ||
1. Let _j_ be 0. | ||
1. For each element _E_ of _items_, do | ||
1. Let _Pj_ be ! ToString(𝔽(_j_)). | ||
1. Perform ! Set(_A_, _Pj_, _E_, *true*). | ||
1. Set _j_ to _j_ + 1. | ||
1. Repeat, while _j_ < _len_, | ||
1. Perform ! Set(_A_, ! ToString(𝔽(_j_)), _sortedList_[_j_], *true*). | ||
1. Set _j_ to _j_ + 1. | ||
1. Return _A_. | ||
</emu-alg> | ||
<p>The following version of SortCompare is used by %TypedArray%`.prototype.toSorted`.</p> | ||
<p>The abstract operation TypedArraySortCompare performs the following steps when called:</p> | ||
<emu-alg> | ||
1. Assert: Both Type(_x_) and Type(_y_) are Number or both are BigInt. | ||
1. If _comparefn_ is not *undefined*, then | ||
1. Let _v_ be ? ToNumber(? Call(_comparefn_, *undefined*, « _x_, _y_ »)). | ||
1. If IsDetachedBuffer(_buffer_) is *true*, throw a *TypeError* exception. | ||
1. If _v_ is *NaN*, return *+0*<sub>𝔽</sub>. | ||
1. Return _v_. | ||
1. If _x_ and _y_ are both *NaN*, return *+0*<sub>𝔽</sub>. | ||
1. If _x_ is *NaN*, return *1*<sub>𝔽</sub>. | ||
1. If _y_ is *NaN*, return *-1*<sub>𝔽</sub>. | ||
1. If _x_ < _y_, return *-1*<sub>𝔽</sub>. | ||
1. If _x_ > _y_, return *1*<sub>𝔽</sub>. | ||
1. If _x_ is *-0*<sub>𝔽</sub> and _y_ is *+0*<sub>𝔽</sub>, return *-1*<sub>𝔽</sub>. | ||
1. If _x_ is *+0*<sub>𝔽</sub> and _y_ is *-0*<sub>𝔽</sub>, return *1*<sub>𝔽</sub>. | ||
1. Return *+0*<sub>𝔽</sub>. | ||
</emu-alg> | ||
</emu-clause> | ||
|
||
<emu-clause id="sec-%typedarray%.prototype.toSpliced"> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Array.prototype.sort
is updated to move most of the body of the_SortCompare_
inline abstract closure into a new AO that can be shared withtoSorted
.