|
7 | 7 | package org.mozilla.javascript.typedarrays; |
8 | 8 |
|
9 | 9 | import java.lang.reflect.Array; |
| 10 | +import java.util.ArrayList; |
10 | 11 | import java.util.Arrays; |
11 | 12 | import java.util.Collection; |
12 | 13 | import java.util.Comparator; |
|
24 | 25 | import org.mozilla.javascript.Context; |
25 | 26 | import org.mozilla.javascript.ExternalArrayData; |
26 | 27 | import org.mozilla.javascript.Function; |
| 28 | +import org.mozilla.javascript.IteratorLikeIterable; |
27 | 29 | import org.mozilla.javascript.LambdaConstructor; |
28 | 30 | import org.mozilla.javascript.NativeArray; |
29 | 31 | import org.mozilla.javascript.NativeArrayIterator; |
@@ -246,6 +248,11 @@ static void init( |
246 | 248 | defineMethod(ta, s, "with", 2, NativeTypedArrayView::js_with); |
247 | 249 | defineMethod(ta, s, SymbolKey.ITERATOR, 0, NativeTypedArrayView::js_iterator); |
248 | 250 |
|
| 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 | + |
249 | 256 | ta = (LambdaConstructor) s.associateValue(TYPED_ARRAY_TAG, ta); |
250 | 257 | } |
251 | 258 | constructor.setPrototype(ta); |
@@ -1154,7 +1161,8 @@ private NativeTypedArrayView<?> typedArraySpeciesCreate( |
1154 | 1161 | } |
1155 | 1162 | } |
1156 | 1163 | } else { |
1157 | | - throw ScriptRuntime.typeErrorById("msg.typed.array.ctor.incompatible", methodName); |
| 1164 | + throw ScriptRuntime.typeErrorById( |
| 1165 | + "msg.typed.array.receiver.incompatible", "prototype." + methodName); |
1158 | 1166 | } |
1159 | 1167 |
|
1160 | 1168 | return (NativeTypedArrayView<?>) newArray; |
@@ -1236,6 +1244,120 @@ private static Object js_with(Context cx, Scriptable scope, Scriptable thisObj, |
1236 | 1244 | return result; |
1237 | 1245 | } |
1238 | 1246 |
|
| 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 | + |
1239 | 1361 | // External Array implementation |
1240 | 1362 |
|
1241 | 1363 | @Override |
|
0 commit comments