Skip to content

Commit 452e105

Browse files
authored
Implement TypedArray.from and TypedArray.of
1 parent 1e1f9c4 commit 452e105

4 files changed

Lines changed: 131 additions & 76 deletions

File tree

rhino/src/main/java/org/mozilla/javascript/AbstractEcmaObjectOperations.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ static boolean validateAndApplyPropertyDescriptor(
491491
* <p><a href="https://262.ecma-international.org/12.0/#sec-isconstructor">7.2.4 IsConstructor
492492
* (argument)</a>
493493
*/
494-
static boolean isConstructor(Context cx, Object argument) {
494+
public static boolean isConstructor(Context cx, Object argument) {
495495
/*
496496
The abstract operation IsConstructor takes argument argument (an ECMAScript language value).
497497
It determines if argument is a function object with a [[Construct]] internal method.

rhino/src/main/java/org/mozilla/javascript/typedarrays/NativeTypedArrayView.java

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package org.mozilla.javascript.typedarrays;
88

99
import java.lang.reflect.Array;
10+
import java.util.ArrayList;
1011
import java.util.Arrays;
1112
import java.util.Collection;
1213
import java.util.Comparator;
@@ -24,6 +25,7 @@
2425
import org.mozilla.javascript.Context;
2526
import org.mozilla.javascript.ExternalArrayData;
2627
import org.mozilla.javascript.Function;
28+
import org.mozilla.javascript.IteratorLikeIterable;
2729
import org.mozilla.javascript.LambdaConstructor;
2830
import org.mozilla.javascript.NativeArray;
2931
import org.mozilla.javascript.NativeArrayIterator;
@@ -246,6 +248,11 @@ static void init(
246248
defineMethod(ta, s, "with", 2, NativeTypedArrayView::js_with);
247249
defineMethod(ta, s, SymbolKey.ITERATOR, 0, NativeTypedArrayView::js_iterator);
248250

251+
ta.defineConstructorMethod(
252+
scope, "from", 1, NativeTypedArrayView::js_from, DONTENUM, DONTENUM | READONLY);
253+
ta.defineConstructorMethod(
254+
scope, "of", 0, NativeTypedArrayView::js_of, DONTENUM, DONTENUM | READONLY);
255+
249256
ta = (LambdaConstructor) s.associateValue(TYPED_ARRAY_TAG, ta);
250257
}
251258
constructor.setPrototype(ta);
@@ -1154,7 +1161,8 @@ private NativeTypedArrayView<?> typedArraySpeciesCreate(
11541161
}
11551162
}
11561163
} else {
1157-
throw ScriptRuntime.typeErrorById("msg.typed.array.ctor.incompatible", methodName);
1164+
throw ScriptRuntime.typeErrorById(
1165+
"msg.typed.array.receiver.incompatible", "prototype." + methodName);
11581166
}
11591167

11601168
return (NativeTypedArrayView<?>) newArray;
@@ -1236,6 +1244,120 @@ private static Object js_with(Context cx, Scriptable scope, Scriptable thisObj,
12361244
return result;
12371245
}
12381246

1247+
private static Object js_from(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
1248+
if (args.length < 1) {
1249+
throw ScriptRuntime.typeErrorById("msg.missing.argument");
1250+
}
1251+
final Scriptable items = ScriptRuntime.toObject(scope, args[0]);
1252+
if (!AbstractEcmaObjectOperations.isConstructor(cx, thisObj)) {
1253+
throw ScriptRuntime.typeErrorById("msg.constructor.expected");
1254+
}
1255+
Constructable constructable = (Constructable) thisObj;
1256+
1257+
Function mapFn = null;
1258+
Object mapArg = (args.length >= 2) ? args[1] : Undefined.instance;
1259+
Scriptable mapFnThisArg = Undefined.SCRIPTABLE_UNDEFINED;
1260+
if (!Undefined.isUndefined(mapArg)) {
1261+
if (!(mapArg instanceof Function)) {
1262+
throw ScriptRuntime.typeErrorById("msg.map.function.not");
1263+
}
1264+
mapFn = (Function) mapArg;
1265+
if (args.length >= 3) {
1266+
mapFnThisArg = ScriptableObject.ensureScriptable(args[2]);
1267+
}
1268+
}
1269+
1270+
List<Object> listFromIterator = null;
1271+
Object iteratorProp = ScriptableObject.getProperty(items, SymbolKey.ITERATOR);
1272+
// Optimization: When items is an instance of java.util.List and also have an iterator,
1273+
// we don't use the iterator to avoid copying the contents to determine the length.
1274+
// However, with this the test262 test
1275+
// built-ins/TypedArray/from/iterated-array-changed-by-tonumber.js
1276+
// doesn't pass.
1277+
if (!(iteratorProp == Scriptable.NOT_FOUND)
1278+
&& !(items instanceof List) // NativeArray and NativeTypedArrayView
1279+
&& !Undefined.isUndefined(iteratorProp)) {
1280+
final Object iterator = ScriptRuntime.callIterator(items, cx, scope);
1281+
if (!Undefined.isUndefined(iterator)) {
1282+
try (IteratorLikeIterable it = new IteratorLikeIterable(cx, scope, iterator)) {
1283+
listFromIterator = new ArrayList<>();
1284+
for (Object temp : it) {
1285+
listFromIterator.add(temp);
1286+
}
1287+
}
1288+
}
1289+
}
1290+
1291+
int size;
1292+
if (listFromIterator != null) {
1293+
size = listFromIterator.size();
1294+
} else {
1295+
long sizeLong = AbstractEcmaObjectOperations.lengthOfArrayLike(cx, items);
1296+
if (sizeLong > Integer.MAX_VALUE) {
1297+
throw ScriptRuntime.rangeErrorById("msg.arraylength.bad");
1298+
}
1299+
1300+
size = (int) AbstractEcmaObjectOperations.lengthOfArrayLike(cx, items);
1301+
}
1302+
1303+
Scriptable result = constructable.construct(cx, scope, new Object[] {size});
1304+
if (!(result instanceof NativeTypedArrayView)) {
1305+
throw ScriptRuntime.typeErrorById("msg.typed.array.receiver.incompatible", "from");
1306+
}
1307+
1308+
NativeTypedArrayView<?> typedArray = (NativeTypedArrayView<?>) result;
1309+
if (typedArray.length < size) {
1310+
throw ScriptRuntime.typeErrorById("msg.typed.array.length.too.small");
1311+
}
1312+
1313+
for (int k = 0; k < size; k++) {
1314+
Object temp;
1315+
if (listFromIterator != null) {
1316+
temp = listFromIterator.get(k);
1317+
} else if (items instanceof List<?>) {
1318+
try {
1319+
temp = ((List<?>) items).get(k);
1320+
} catch (IndexOutOfBoundsException e) {
1321+
temp = Undefined.instance;
1322+
}
1323+
} else {
1324+
temp = ScriptRuntime.getObjectIndex(items, k, cx, scope);
1325+
}
1326+
1327+
if (mapFn != null) {
1328+
temp = mapFn.call(cx, scope, mapFnThisArg, new Object[] {temp, k});
1329+
}
1330+
1331+
typedArray.setArrayElement(k, temp);
1332+
}
1333+
1334+
return result;
1335+
}
1336+
1337+
private static Object js_of(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
1338+
if (!AbstractEcmaObjectOperations.isConstructor(cx, thisObj)) {
1339+
throw ScriptRuntime.typeErrorById("msg.constructor.expected");
1340+
}
1341+
Constructable constructable = (Constructable) thisObj;
1342+
1343+
Scriptable result = constructable.construct(cx, scope, new Object[] {args.length});
1344+
1345+
if (!(result instanceof NativeTypedArrayView)) {
1346+
throw ScriptRuntime.typeErrorById("msg.typed.array.receiver.incompatible", "of");
1347+
}
1348+
1349+
NativeTypedArrayView<?> typedArray = (NativeTypedArrayView<?>) result;
1350+
if (typedArray.length < args.length) {
1351+
throw ScriptRuntime.typeErrorById("msg.typed.array.length.too.small");
1352+
}
1353+
1354+
for (int k = 0; k < args.length; k++) {
1355+
typedArray.setArrayElement(k, args[k]);
1356+
}
1357+
1358+
return result;
1359+
}
1360+
12391361
// External Array implementation
12401362

12411363
@Override

rhino/src/main/resources/org/mozilla/javascript/resources/Messages.properties

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,8 +1021,8 @@ msg.promise.all.toobig =\
10211021
msg.promise.any.toobig =\
10221022
Too many inputs to Promise.any
10231023

1024-
msg.typed.array.ctor.incompatible = \
1025-
Method %TypedArray%.prototype.{0} called on incompatible receiver
1024+
msg.typed.array.receiver.incompatible = \
1025+
Method %TypedArray%.{0} called on incompatible receiver
10261026

10271027
msg.typed.array.bad.offset = \
10281028
offset {0} out of range
@@ -1048,6 +1048,9 @@ msg.typed.array.out.of.bounds =\
10481048
msg.typed.array.type.mismatch =\
10491049
Typed arrays content type does not match
10501050

1051+
msg.typed.array.length.too.small =\
1052+
Derived TypedArray constructor created an array which was too small
1053+
10511054
# Error
10521055
msg.iterable.expected =\
10531056
Expected the first argument to be iterable

tests/testsrc/test262.properties

Lines changed: 2 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -2171,28 +2171,11 @@ built-ins/ThrowTypeError 8/14 (57.14%)
21712171
unique-per-realm-non-simple.js
21722172
unique-per-realm-unmapped-args.js
21732173

2174-
built-ins/TypedArray 273/1434 (19.04%)
2175-
from/arylk-get-length-error.js
2176-
from/arylk-to-length-error.js
2177-
from/from-array-mapper-detaches-result.js
2174+
built-ins/TypedArray 256/1434 (17.85%)
21782175
from/from-array-mapper-makes-result-out-of-bounds.js {unsupported: [resizable-arraybuffer]}
2179-
from/from-typedarray-into-itself-mapper-detaches-result.js
21802176
from/from-typedarray-into-itself-mapper-makes-result-out-of-bounds.js {unsupported: [resizable-arraybuffer]}
2181-
from/from-typedarray-mapper-detaches-result.js
21822177
from/from-typedarray-mapper-makes-result-out-of-bounds.js {unsupported: [resizable-arraybuffer]}
2183-
from/iter-access-error.js
2184-
from/iter-invoke-error.js
2185-
from/iter-next-error.js
2186-
from/iter-next-value-error.js
21872178
from/iterated-array-changed-by-tonumber.js
2188-
from/length.js
2189-
from/name.js
2190-
from/not-a-constructor.js
2191-
from/prop-desc.js
2192-
of/length.js
2193-
of/name.js
2194-
of/not-a-constructor.js
2195-
of/prop-desc.js
21962179
of/resized-with-out-of-bounds-and-in-bounds-indices.js {unsupported: [resizable-arraybuffer]}
21972180
prototype/at/BigInt/return-abrupt-from-this-out-of-bounds.js {unsupported: [resizable-arraybuffer]}
21982181
prototype/at/coerced-index-resize.js {unsupported: [resizable-arraybuffer]}
@@ -2443,7 +2426,7 @@ built-ins/TypedArray 273/1434 (19.04%)
24432426
resizable-buffer-length-tracking-1.js {unsupported: [resizable-arraybuffer]}
24442427
resizable-buffer-length-tracking-2.js {unsupported: [resizable-arraybuffer]}
24452428

2446-
built-ins/TypedArrayConstructors 251/736 (34.1%)
2429+
built-ins/TypedArrayConstructors 198/736 (26.9%)
24472430
ctors-bigint/buffer-arg/bufferbyteoffset-throws-from-modulo-element-size-sab.js {unsupported: [SharedArrayBuffer]}
24482431
ctors-bigint/buffer-arg/byteoffset-is-negative-throws-sab.js {unsupported: [SharedArrayBuffer]}
24492432
ctors-bigint/buffer-arg/byteoffset-is-negative-zero-sab.js {unsupported: [SharedArrayBuffer]}
@@ -2566,49 +2549,10 @@ built-ins/TypedArrayConstructors 251/736 (34.1%)
25662549
ctors/typedarray-arg/throw-type-error-before-custom-proto-access.js
25672550
ctors/typedarray-arg/use-custom-proto-if-object.js
25682551
ctors/no-species.js
2569-
from/BigInt/arylk-get-length-error.js
2570-
from/BigInt/arylk-to-length-error.js
2571-
from/BigInt/custom-ctor.js
2572-
from/BigInt/custom-ctor-returns-other-instance.js
2573-
from/BigInt/iter-access-error.js
2574-
from/BigInt/iter-invoke-error.js
2575-
from/BigInt/iter-next-error.js
2576-
from/BigInt/iter-next-value-error.js
2577-
from/BigInt/mapfn-abrupt-completion.js
2578-
from/BigInt/mapfn-arguments.js
2579-
from/BigInt/mapfn-this-with-thisarg.js
25802552
from/BigInt/mapfn-this-without-thisarg-non-strict.js non-strict
2581-
from/BigInt/mapfn-this-without-thisarg-strict.js strict
2582-
from/BigInt/new-instance-empty.js
2583-
from/BigInt/new-instance-from-ordinary-object.js
2584-
from/BigInt/new-instance-using-custom-ctor.js
2585-
from/BigInt/new-instance-with-mapfn.js
2586-
from/BigInt/new-instance-without-mapfn.js
2587-
from/BigInt/property-abrupt-completion.js
2588-
from/BigInt/set-value-abrupt-completion.js
2589-
from/arylk-get-length-error.js
2590-
from/arylk-to-length-error.js
2591-
from/custom-ctor.js
2592-
from/custom-ctor-returns-other-instance.js
2593-
from/iter-access-error.js
2594-
from/iter-invoke-error.js
2595-
from/iter-next-error.js
2596-
from/iter-next-value-error.js
2597-
from/mapfn-abrupt-completion.js
2598-
from/mapfn-arguments.js
2599-
from/mapfn-this-with-thisarg.js
26002553
from/mapfn-this-without-thisarg-non-strict.js non-strict
2601-
from/mapfn-this-without-thisarg-strict.js strict
26022554
from/nan-conversion.js
2603-
from/new-instance-empty.js
2604-
from/new-instance-from-ordinary-object.js
26052555
from/new-instance-from-sparse-array.js
2606-
from/new-instance-from-zero.js
2607-
from/new-instance-using-custom-ctor.js
2608-
from/new-instance-with-mapfn.js
2609-
from/new-instance-without-mapfn.js
2610-
from/property-abrupt-completion.js
2611-
from/set-value-abrupt-completion.js
26122556
internals/DefineOwnProperty/BigInt/key-is-not-canonical-index.js
26132557
internals/DefineOwnProperty/BigInt/key-is-numericindex.js
26142558
internals/DefineOwnProperty/BigInt/key-is-numericindex-accessor-desc-throws.js
@@ -2681,20 +2625,6 @@ built-ins/TypedArrayConstructors 251/736 (34.1%)
26812625
internals/Set/key-is-valid-index-reflect-set.js
26822626
internals/Set/resized-out-of-bounds-to-in-bounds-index.js {unsupported: [resizable-arraybuffer]}
26832627
internals/Set/tonumber-value-throws.js
2684-
of/BigInt/argument-number-value-throws.js
2685-
of/BigInt/custom-ctor.js
2686-
of/BigInt/custom-ctor-returns-other-instance.js
2687-
of/BigInt/new-instance.js
2688-
of/BigInt/new-instance-empty.js
2689-
of/BigInt/new-instance-using-custom-ctor.js
2690-
of/argument-number-value-throws.js
2691-
of/custom-ctor.js
2692-
of/custom-ctor-returns-other-instance.js
2693-
of/nan-conversion.js
2694-
of/new-instance.js
2695-
of/new-instance-empty.js
2696-
of/new-instance-from-zero.js
2697-
of/new-instance-using-custom-ctor.js
26982628

26992629
built-ins/WeakMap 11/119 (9.24%)
27002630
prototype/getOrInsert/adds-object-element.js

0 commit comments

Comments
 (0)