Skip to content

Commit

Permalink
Implement ES2023 copy methods on Array and TypedArray (#1569)
Browse files Browse the repository at this point in the history
Add new methods to Array:

* toReversed
* toSorted
* toSpliced
* with

and add the same methods to TypedArray, except for "toSpliced".
  • Loading branch information
andreabergia authored Aug 23, 2024
1 parent d0d8d2d commit 86a2d60
Show file tree
Hide file tree
Showing 17 changed files with 716 additions and 92 deletions.
179 changes: 176 additions & 3 deletions rhino/src/main/java/org/mozilla/javascript/NativeArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,22 @@ protected void initPrototypeId(int id) {
arity = 1;
s = "flatMap";
break;
case Id_toReversed:
arity = 0;
s = "toReversed";
break;
case Id_toSorted:
arity = 1;
s = "toSorted";
break;
case Id_toSpliced:
arity = 2;
s = "toSpliced";
break;
case Id_with:
arity = 2;
s = "with";
break;
default:
throw new IllegalArgumentException(String.valueOf(id));
}
Expand Down Expand Up @@ -532,6 +548,15 @@ public Object execIdCall(
thisObj = ScriptRuntime.toObject(cx, scope, thisObj);
return new NativeArrayIterator(
scope, thisObj, NativeArrayIterator.ARRAY_ITERATOR_TYPE.VALUES);

case Id_toReversed:
return js_toReversed(cx, scope, thisObj, args);
case Id_toSorted:
return js_toSorted(cx, scope, thisObj, args);
case Id_toSpliced:
return js_toSpliced(cx, scope, thisObj, args);
case Id_with:
return js_with(cx, scope, thisObj, args);
}
throw new IllegalArgumentException(
"Array.prototype has no method: " + f.getFunctionName());
Expand Down Expand Up @@ -1350,10 +1375,12 @@ private static Scriptable js_sort(
final Scriptable thisObj,
final Object[] args) {
Scriptable o = ScriptRuntime.toObject(cx, scope, thisObj);

final Comparator<Object> comparator =
Comparator<Object> comparator =
ArrayLikeAbstractOperations.getSortComparator(cx, scope, args);
return sort(cx, o, comparator);
}

private static Scriptable sort(Context cx, Scriptable o, Comparator<Object> comparator) {
long llength = getLengthProperty(cx, o);
final int length = (int) llength;
if (llength != length) {
Expand Down Expand Up @@ -2174,6 +2201,136 @@ private static boolean js_isArray(Object o) {
return "Array".equals(((Scriptable) o).getClassName());
}

private static Object js_toSorted(
Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
Comparator<Object> comparator =
ArrayLikeAbstractOperations.getSortComparator(cx, scope, args);

Scriptable source = ScriptRuntime.toObject(cx, scope, thisObj);
long len = getLengthProperty(cx, source);

if (len > Integer.MAX_VALUE) {
String msg = ScriptRuntime.getMessageById("msg.arraylength.bad");
throw ScriptRuntime.rangeError(msg);
}
Scriptable result = cx.newArray(scope, (int) len);

for (int k = 0; k < len; ++k) {
Object fromValue = getElem(cx, source, k);
setElem(cx, result, k, fromValue);
}

sort(cx, result, comparator);
return result;
}

private static Object js_toReversed(
Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
Scriptable source = ScriptRuntime.toObject(cx, scope, thisObj);
long len = getLengthProperty(cx, source);

if (len > Integer.MAX_VALUE) {
String msg = ScriptRuntime.getMessageById("msg.arraylength.bad");
throw ScriptRuntime.rangeError(msg);
}
Scriptable result = cx.newArray(scope, (int) len);

for (int k = 0; k < len; ++k) {
int from = (int) len - k - 1;
Object fromValue = getElem(cx, source, from);
setElem(cx, result, k, fromValue);
}

return result;
}

private static Object js_toSpliced(
Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
Scriptable source = ScriptRuntime.toObject(cx, scope, thisObj);
long len = getLengthProperty(cx, source);

long actualStart = 0;
if (args.length > 0) {
actualStart =
ArrayLikeAbstractOperations.toSliceIndex(ScriptRuntime.toInteger(args[0]), len);
}

long insertCount = args.length > 2 ? args.length - 2 : 0;

long actualSkipCount;
if (args.length == 0) {
actualSkipCount = 0;
} else if (args.length == 1) {
actualSkipCount = len - actualStart;
} else {
long sc = ScriptRuntime.toLength(args, 1);
actualSkipCount = Math.max(0, Math.min(sc, len - actualStart));
}

long newLen = len + insertCount - actualSkipCount;
if (newLen > NativeNumber.MAX_SAFE_INTEGER) {
throw ScriptRuntime.typeErrorById("msg.arraylength.too.big", newLen);
}
if (newLen > Integer.MAX_VALUE) {
String msg = ScriptRuntime.getMessageById("msg.arraylength.bad");
throw ScriptRuntime.rangeError(msg);
}

Scriptable result = cx.newArray(scope, (int) newLen);

long i = 0;
long r = actualStart + actualSkipCount;

while (i < actualStart) {
Object e = getElem(cx, source, i);
setElem(cx, result, i, e);
i++;
}

for (int j = 2; j < args.length; j++) {
setElem(cx, result, i, args[j]);
i++;
}

while (i < newLen) {
Object e = getElem(cx, source, r);
setElem(cx, result, i, e);
i++;
r++;
}

return result;
}

private static Object js_with(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
Scriptable source = ScriptRuntime.toObject(cx, scope, thisObj);

long len = getLengthProperty(cx, source);
long relativeIndex = args.length > 0 ? (int) ScriptRuntime.toInteger(args[0]) : 0;
long actualIndex = relativeIndex >= 0 ? relativeIndex : len + relativeIndex;

if (actualIndex < 0 || actualIndex >= len) {
throw ScriptRuntime.rangeError("index out of range");
}
if (len > Integer.MAX_VALUE) {
String msg = ScriptRuntime.getMessageById("msg.arraylength.bad");
throw ScriptRuntime.rangeError(msg);
}

Scriptable result = cx.newArray(scope, (int) len);
for (long k = 0; k < len; ++k) {
Object value;
if (k == actualIndex) {
value = args.length > 1 ? args[1] : Undefined.instance;
} else {
value = getElem(cx, source, k);
}
setElem(cx, result, k, value);
}

return result;
}

// methods to implement java.util.List

@Override
Expand Down Expand Up @@ -2563,6 +2720,18 @@ protected int findPrototypeId(String s) {
case "flatMap":
id = Id_flatMap;
break;
case "toReversed":
id = Id_toReversed;
break;
case "toSorted":
id = Id_toSorted;
break;
case "toSpliced":
id = Id_toSpliced;
break;
case "with":
id = Id_with;
break;
default:
id = 0;
break;
Expand Down Expand Up @@ -2606,7 +2775,11 @@ protected int findPrototypeId(String s) {
Id_at = 34,
Id_flat = 35,
Id_flatMap = 36,
SymbolId_unscopables = 37,
Id_toReversed = 37,
Id_toSorted = 38,
Id_toSpliced = 39,
Id_with = 40,
SymbolId_unscopables = 41,
MAX_PROTOTYPE_ID = SymbolId_unscopables;
private static final int ConstructorId_join = -Id_join,
ConstructorId_reverse = -Id_reverse,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,15 @@ private NativeTypedArrayView<T> js_fill(Object[] args) {
}

private Scriptable js_sort(Context cx, Scriptable scope, Object[] args) {
Object[] working = sortTemporaryArray(cx, scope, args);
for (int i = 0; i < length; ++i) {
js_set(i, working[i]);
}

return this;
}

private Object[] sortTemporaryArray(Context cx, Scriptable scope, Object[] args) {
Comparator<Object> comparator;
if (args.length > 0 && Undefined.instance != args[0]) {
comparator = ArrayLikeAbstractOperations.getSortComparator(cx, scope, args);
Expand All @@ -528,12 +537,7 @@ private Scriptable js_sort(Context cx, Scriptable scope, Object[] args) {
// Temporary array to rely on Java's built-in sort, which is stable.
Object[] working = toArray();
Arrays.sort(working, comparator);

for (int i = 0; i < length; ++i) {
js_set(i, working[i]);
}

return this;
return working;
}

private Object js_copyWithin(Object[] args) {
Expand Down Expand Up @@ -628,6 +632,65 @@ private Scriptable typedArraySpeciesCreate(
return newArray;
}

private Object js_toReversed(Context cx, Scriptable scope) {
NativeArrayBuffer newBuffer = new NativeArrayBuffer(length * getBytesPerElement());
Scriptable result =
cx.newObject(
scope,
getClassName(),
new Object[] {newBuffer, 0, length, getBytesPerElement()});

for (int k = 0; k < length; ++k) {
int from = length - k - 1;
Object fromValue = js_get(from);
result.put(k, result, fromValue);
}

return result;
}

private Object js_toSorted(Context cx, Scriptable scope, Object[] args) {
Object[] working = sortTemporaryArray(cx, scope, args);

// Move value in a new typed array of the same type
NativeArrayBuffer newBuffer = new NativeArrayBuffer(length * getBytesPerElement());
Scriptable result =
cx.newObject(
scope,
getClassName(),
new Object[] {newBuffer, 0, length, getBytesPerElement()});
for (int k = 0; k < length; ++k) {
result.put(k, result, working[k]);
}

return result;
}

private Object js_with(Context cx, Scriptable scope, Object[] args) {
long relativeIndex = args.length > 0 ? (int) ScriptRuntime.toInteger(args[0]) : 0;
long actualIndex = relativeIndex >= 0 ? relativeIndex : length + relativeIndex;

Object argsValue = args.length > 1 ? ScriptRuntime.toNumber(args[1]) : 0.0;

if (actualIndex < 0 || actualIndex >= length) {
throw ScriptRuntime.rangeError("index out of range");
}

NativeArrayBuffer newBuffer = new NativeArrayBuffer(length * getBytesPerElement());
Scriptable result =
cx.newObject(
scope,
getClassName(),
new Object[] {newBuffer, 0, length, getBytesPerElement()});

for (int k = 0; k < length; ++k) {
Object fromValue = (k == actualIndex) ? argsValue : js_get(k);
result.put(k, result, fromValue);
}

return result;
}

// Dispatcher

@Override
Expand Down Expand Up @@ -783,6 +846,12 @@ public Object execIdCall(
case Id_reduceRight:
return ArrayLikeAbstractOperations.reduceMethod(
cx, ReduceOperation.REDUCE_RIGHT, scope, thisObj, args);
case Id_toReversed:
return realThis(thisObj, f).js_toReversed(cx, scope);
case Id_toSorted:
return realThis(thisObj, f).js_toSorted(cx, scope, args);
case Id_with:
return realThis(thisObj, f).js_with(cx, scope, args);

case SymbolId_iterator:
return new NativeArrayIterator(scope, thisObj, ARRAY_ITERATOR_TYPE.VALUES);
Expand Down Expand Up @@ -920,6 +989,18 @@ protected void initPrototypeId(int id) {
arity = 1;
s = "reduceRight";
break;
case Id_toSorted:
arity = 1;
s = "toSorted";
break;
case Id_toReversed:
arity = 0;
s = "toReversed";
break;
case Id_with:
arity = 2;
s = "with";
break;
default:
throw new IllegalArgumentException(String.valueOf(id));
}
Expand Down Expand Up @@ -1028,6 +1109,15 @@ protected int findPrototypeId(String s) {
case "reduceRight":
id = Id_reduceRight;
break;
case "toSorted":
id = Id_toSorted;
break;
case "toReversed":
id = Id_toReversed;
break;
case "with":
id = Id_with;
break;
default:
id = 0;
break;
Expand Down Expand Up @@ -1066,7 +1156,10 @@ protected int findPrototypeId(String s) {
Id_findLastIndex = 28,
Id_reduce = 29,
Id_reduceRight = 30,
SymbolId_iterator = 31;
Id_toReversed = 31,
Id_toSorted = 32,
Id_with = 33,
SymbolId_iterator = 34;

protected static final int MAX_PROTOTYPE_ID = SymbolId_iterator;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.javascript.tests.es2023;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.drivers.LanguageVersion;
import org.mozilla.javascript.drivers.RhinoTest;
import org.mozilla.javascript.drivers.ScriptTestsBase;

@RhinoTest("testsrc/jstests/es2023/array-toReversed.js")
@LanguageVersion(Context.VERSION_ES6)
public class ArrayToReversedTest extends ScriptTestsBase {}
Loading

0 comments on commit 86a2d60

Please sign in to comment.