/**
* Copyright (c) 2012-2016 André Bargull
* Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms.
*
* <https://github.com/anba/es6draft>
*/
package com.github.anba.es6draft.runtime.objects.binary;
import static com.github.anba.es6draft.runtime.AbstractOperations.*;
import static com.github.anba.es6draft.runtime.internal.Errors.newInternalError;
import static com.github.anba.es6draft.runtime.internal.Errors.newRangeError;
import static com.github.anba.es6draft.runtime.internal.Errors.newTypeError;
import static com.github.anba.es6draft.runtime.internal.Properties.createProperties;
import static com.github.anba.es6draft.runtime.objects.ArrayIteratorPrototype.CreateArrayIterator;
import static com.github.anba.es6draft.runtime.objects.atomics.SharedArrayBufferConstructor.IsSharedMemory;
import static com.github.anba.es6draft.runtime.objects.atomics.SharedArrayBufferConstructor.SharedDataBlockID;
import static com.github.anba.es6draft.runtime.objects.binary.ArrayBufferConstructor.CloneArrayBuffer;
import static com.github.anba.es6draft.runtime.objects.binary.ArrayBufferConstructor.GetValueFromBuffer;
import static com.github.anba.es6draft.runtime.objects.binary.ArrayBufferConstructor.IsDetachedBuffer;
import static com.github.anba.es6draft.runtime.objects.binary.ArrayBufferConstructor.SetValueInBuffer;
import static com.github.anba.es6draft.runtime.objects.binary.TypedArrayConstructor.TypedArraySpeciesCreate;
import static com.github.anba.es6draft.runtime.types.Undefined.UNDEFINED;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Comparator;
import com.github.anba.es6draft.runtime.ExecutionContext;
import com.github.anba.es6draft.runtime.Realm;
import com.github.anba.es6draft.runtime.internal.CompatibilityOption;
import com.github.anba.es6draft.runtime.internal.Initializable;
import com.github.anba.es6draft.runtime.internal.Messages;
import com.github.anba.es6draft.runtime.internal.Properties.Accessor;
import com.github.anba.es6draft.runtime.internal.Properties.AliasFunction;
import com.github.anba.es6draft.runtime.internal.Properties.Attributes;
import com.github.anba.es6draft.runtime.internal.Properties.CompatibilityExtension;
import com.github.anba.es6draft.runtime.internal.Properties.Function;
import com.github.anba.es6draft.runtime.internal.Properties.Optional;
import com.github.anba.es6draft.runtime.internal.Properties.Prototype;
import com.github.anba.es6draft.runtime.internal.Properties.Value;
import com.github.anba.es6draft.runtime.objects.ArrayIteratorObject.ArrayIterationKind;
import com.github.anba.es6draft.runtime.types.BuiltinSymbol;
import com.github.anba.es6draft.runtime.types.Callable;
import com.github.anba.es6draft.runtime.types.Intrinsics;
import com.github.anba.es6draft.runtime.types.ScriptObject;
import com.github.anba.es6draft.runtime.types.Type;
import com.github.anba.es6draft.runtime.types.builtins.NativeFunction;
import com.github.anba.es6draft.runtime.types.builtins.OrdinaryObject;
/**
* <h1>22 Indexed Collections</h1><br>
* <h2>22.2 TypedArray Objects</h2>
* <ul>
* <li>22.2.3 Properties of the %TypedArrayPrototype% Object
* </ul>
*/
public final class TypedArrayPrototypePrototype extends OrdinaryObject implements Initializable {
/**
* Constructs a new TypedArray prototype object.
*
* @param realm
* the realm object
*/
public TypedArrayPrototypePrototype(Realm realm) {
super(realm);
}
@Override
public void initialize(Realm realm) {
createProperties(realm, this, Properties.class);
createProperties(realm, this, AdditionalProperties.class);
}
/**
* Marker class for {@code %TypedArray%.prototype.values}.
*/
private static final class TypedArrayPrototypeValues {
}
/**
* Returns {@code true} if <var>values</var> is the built-in {@code %TypedArray%.prototype.values} function for the
* requested realm.
*
* @param realm
* the function realm
* @param values
* the values function
* @return {@code true} if <var>values</var> is the built-in {@code %TypedArray%.prototype.values} function
*/
public static boolean isBuiltinValues(Realm realm, Object values) {
return NativeFunction.isNative(realm, values, TypedArrayPrototypeValues.class);
}
/**
* 22.2.3.5.1 Runtime Semantics: ValidateTypedArray ( O )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @return the validated typed array object
*/
public static TypedArrayObject ValidateTypedArray(ExecutionContext cx, Object thisValue) {
/* steps 1-3 */
if (!(thisValue instanceof TypedArrayObject)) {
throw newTypeError(cx, Messages.Key.IncompatibleObject);
}
TypedArrayObject typedArray = (TypedArrayObject) thisValue;
/* step 4 */
ArrayBuffer buffer = typedArray.getBuffer();
/* step 5 */
if (IsDetachedBuffer(buffer)) {
throw newTypeError(cx, Messages.Key.BufferDetached);
}
/* step 6 (not applicable) */
return typedArray;
}
/**
* 22.2.3 Properties of the %TypedArrayPrototype% Object
*/
public enum Properties {
;
private static ArrayBufferView thisArrayBufferView(ExecutionContext cx, Object m) {
if (m instanceof ArrayBufferView) {
return (ArrayBufferView) m;
}
throw newTypeError(cx, Messages.Key.IncompatibleObject);
}
private static TypedArrayObject thisTypedArrayObject(ExecutionContext cx, Object thisValue) {
if (thisValue instanceof TypedArrayObject) {
return (TypedArrayObject) thisValue;
}
throw newTypeError(cx, Messages.Key.IncompatibleObject);
}
private static long ToArrayIndex(ExecutionContext cx, Object index, long length) {
double relativeIndex = ToInteger(cx, index);
if (relativeIndex < 0) {
return (long) Math.max(length + relativeIndex, 0);
}
return (long) Math.min(relativeIndex, length);
}
@Prototype
public static final Intrinsics __proto__ = Intrinsics.ObjectPrototype;
/**
* 22.2.3.4 %TypedArray%.prototype.constructor
*/
@Value(name = "constructor")
public static final Intrinsics constructor = Intrinsics.TypedArray;
/**
* 22.2.3.1 get %TypedArray%.prototype.buffer
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @return the array buffer object
*/
@Accessor(name = "buffer", type = Accessor.Type.Getter)
public static Object buffer(ExecutionContext cx, Object thisValue) {
/* steps 1-3 */
ArrayBufferView view = thisArrayBufferView(cx, thisValue);
/* steps 4-5 */
return view.getBuffer();
}
/**
* 22.2.3.2 get %TypedArray%.prototype.byteLength
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @return the typed array length in bytes
*/
@Accessor(name = "byteLength", type = Accessor.Type.Getter)
public static Object byteLength(ExecutionContext cx, Object thisValue) {
/* steps 1-3 */
ArrayBufferView view = thisArrayBufferView(cx, thisValue);
/* steps 4-5 */
if (IsDetachedBuffer(view.getBuffer())) {
return 0;
}
/* steps 6-7 */
return view.getByteLength();
}
/**
* 22.2.3.3 get %TypedArray%.prototype.byteOffset
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @return the byte offset
*/
@Accessor(name = "byteOffset", type = Accessor.Type.Getter)
public static Object byteOffset(ExecutionContext cx, Object thisValue) {
/* steps 1-3 */
ArrayBufferView view = thisArrayBufferView(cx, thisValue);
/* steps 4-5 */
if (IsDetachedBuffer(view.getBuffer())) {
return 0;
}
/* steps 6-7 */
return view.getByteOffset();
}
/**
* 22.2.3.17 get %TypedArray%.prototype.length
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @return the typed array length
*/
@Accessor(name = "length", type = Accessor.Type.Getter)
public static Object length(ExecutionContext cx, Object thisValue) {
/* steps 1-4 */
TypedArrayObject typedArray = thisTypedArrayObject(cx, thisValue);
/* steps 5-6 */
if (IsDetachedBuffer(typedArray.getBuffer())) {
return 0;
}
/* steps 7-8 */
return typedArray.getArrayLength();
}
/**
* 22.2.3.22 %TypedArray%.prototype.set ( overloaded [ , offset ])
* <ul>
* <li>22.2.3.22.1 %TypedArray%.prototype.set (array [ , offset ] )
* <li>22.2.3.22.2 %TypedArray%.prototype.set(typedArray [, offset ] )
* </ul>
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param array
* the source array
* @param offset
* the target offset
* @return the undefined value
*/
@Function(name = "set", arity = 1)
public static Object set(ExecutionContext cx, Object thisValue, Object array, Object offset) {
if (!(array instanceof TypedArrayObject)) {
// 22.2.3.22.1
/* steps 1-5 */
TypedArrayObject target = thisTypedArrayObject(cx, thisValue);
/* steps 6-7 */
double targetOffset = ToInteger(cx, offset);
/* step 8 */
if (targetOffset < 0) {
throw newRangeError(cx, Messages.Key.InvalidByteOffset);
}
/* step 9 */
ArrayBuffer targetBuffer = target.getBuffer();
/* step 10 */
if (IsDetachedBuffer(targetBuffer)) {
throw newTypeError(cx, Messages.Key.BufferDetached);
}
/* step 11 */
long targetLength = target.getArrayLength();
/* steps 12, 14 */
ElementType targetType = target.getElementType();
/* step 13 */
int targetElementSize = targetType.size();
/* step 15 */
long targetByteOffset = target.getByteOffset();
/* steps 16-17 */
ScriptObject src = ToObject(cx, array);
/* steps 18-19 */
long srcLength = ToLength(cx, Get(cx, src, "length"));
/* step 20 */
if (srcLength + targetOffset > targetLength) {
throw newRangeError(cx, Messages.Key.ArrayOffsetOutOfRange);
}
long targetIndex = (long) targetOffset;
/* step 21 */
long targetByteIndex = targetIndex * targetElementSize + targetByteOffset;
/* step 23 */
long limit = targetByteIndex + targetElementSize * srcLength;
/* steps 22, 24 */
for (long k = 0; targetByteIndex < limit; ++k, targetByteIndex += targetElementSize) {
/* step 24.a */
long pk = k;
/* steps 24.b-c */
double kNumber = ToNumber(cx, Get(cx, src, pk));
/* step 24.d */
if (IsDetachedBuffer(targetBuffer)) {
throw newTypeError(cx, Messages.Key.BufferDetached);
}
/* step 24.e */
SetValueInBuffer(targetBuffer, targetByteIndex, targetType, kNumber);
}
/* step 25 */
return UNDEFINED;
} else {
// 22.2.3.22.2
TypedArrayObject typedArray = (TypedArrayObject) array;
/* steps 1-5 */
TypedArrayObject target = thisTypedArrayObject(cx, thisValue);
/* steps 6-7 */
double targetOffset = ToInteger(cx, offset);
/* step 8 */
if (targetOffset < 0) {
throw newRangeError(cx, Messages.Key.InvalidByteOffset);
}
/* step 9 */
ArrayBuffer targetBuffer = target.getBuffer();
/* step 10 */
if (IsDetachedBuffer(targetBuffer)) {
throw newTypeError(cx, Messages.Key.BufferDetached);
}
/* step 12 */
ArrayBuffer srcBuffer = typedArray.getBuffer();
/* step 13 */
if (IsDetachedBuffer(srcBuffer)) {
throw newTypeError(cx, Messages.Key.BufferDetached);
}
/* step 11 */
long targetLength = target.getArrayLength();
/* steps 14-15 */
ElementType targetType = target.getElementType();
/* step 16 */
int targetElementSize = targetType.size();
/* step 17 */
long targetByteOffset = target.getByteOffset();
/* steps 18-19 */
ElementType srcType = typedArray.getElementType();
/* step 20 */
int srcElementSize = srcType.size();
/* step 21 */
long srcLength = typedArray.getArrayLength();
/* step 22 */
long srcByteOffset = typedArray.getByteOffset();
/* step 23 */
if (srcLength + targetOffset > targetLength) {
throw newRangeError(cx, Messages.Key.ArrayOffsetOutOfRange);
}
long targetIndex = (long) targetOffset;
/* steps 24-25 */
// Extension: SharedArrayBuffer
boolean doClone;
if (IsSharedMemory(srcBuffer) && IsSharedMemory(targetBuffer)) {
ByteBuffer srcBlock = srcBuffer.getData();
ByteBuffer targetBlock = targetBuffer.getData();
Object srcId = SharedDataBlockID(srcBlock);
Object targetId = SharedDataBlockID(targetBlock);
doClone = srcId == targetId;
} else {
doClone = false;
}
long srcByteIndex;
if (srcBuffer == targetBuffer || doClone) {
srcBuffer = CloneArrayBuffer(cx, targetBuffer, srcByteOffset, Intrinsics.ArrayBuffer);
assert !IsDetachedBuffer(targetBuffer);
srcByteIndex = 0;
} else {
srcByteIndex = srcByteOffset;
}
/* step 26 */
long targetByteIndex = targetIndex * targetElementSize + targetByteOffset;
/* steps 27-29 */
if (srcType != targetType) {
/* step 27 */
long limit = targetByteIndex + targetElementSize * srcLength;
/* step 28 */
for (; targetByteIndex < limit; srcByteIndex += srcElementSize, targetByteIndex += targetElementSize) {
/* step 28.a */
double value = GetValueFromBuffer(srcBuffer, srcByteIndex, srcType);
/* step 28.b */
SetValueInBuffer(targetBuffer, targetByteIndex, targetType, value);
}
} else {
/* steps 27, 29 */
long countByteLength = targetElementSize * srcLength;
ByteBuffer srcData = srcBuffer.getData();
ByteBuffer targetData = targetBuffer.getData();
assert (srcByteIndex + countByteLength) <= srcData.capacity();
assert (targetByteIndex + countByteLength) <= targetData.capacity();
assert srcData != targetData;
srcData.limit((int) (srcByteIndex + countByteLength)).position(
(int) srcByteIndex);
targetData.limit((int) (targetByteIndex + countByteLength)).position(
(int) targetByteIndex);
targetData.put(srcData);
srcData.clear();
targetData.clear();
}
/* step 30 */
return UNDEFINED;
}
}
/**
* 22.2.3.26 %TypedArray%.prototype.subarray( [ begin [ , end ] ] )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param begin
* the begin position
* @param end
* the end position
* @return the new typed array
*/
@Function(name = "subarray", arity = 2)
public static Object subarray(ExecutionContext cx, Object thisValue, Object begin, Object end) {
/* steps 1-4 */
TypedArrayObject array = thisTypedArrayObject(cx, thisValue);
/* step 5 */
ArrayBuffer buffer = array.getBuffer();
/* step 6 */
long srcLength = array.getArrayLength();
/* steps 7-8 */
long beginIndex = ToArrayIndex(cx, begin, srcLength);
/* steps 9-10 */
long endIndex = Type.isUndefined(end) ? srcLength : ToArrayIndex(cx, end, srcLength);
/* step 11 */
long newLength = Math.max(endIndex - beginIndex, 0);
/* steps 12-13 */
int elementSize = array.getElementType().size();
/* step 14 */
long srcByteOffset = array.getByteOffset();
/* step 15 */
long beginByteOffset = srcByteOffset + beginIndex * elementSize;
/* steps 16-17 */
return TypedArraySpeciesCreate(cx, array, buffer, beginByteOffset, newLength);
}
/**
* 22.2.3.28 %TypedArray%.prototype.toString ( )
*
* @param cx
* the execution context
* @return the string representation
*/
@Value(name = "toString")
public static Object toString(ExecutionContext cx) {
return Get(cx, cx.getIntrinsic(Intrinsics.ArrayPrototype), "toString");
}
/**
* 22.2.3.27 %TypedArray%.prototype.toLocaleString ([ reserved1 [ , reserved2 ] ])
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param locales
* the optional locales array
* @param options
* the optional options object
* @return the locale specific string representation
*/
@Function(name = "toLocaleString", arity = 0)
public static Object toLocaleString(ExecutionContext cx, Object thisValue, Object locales,
Object options) {
// 22.1.3.26 Array.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] )<br>
// 13.4.1 Array.prototype.toLocaleString([locales [, options ]])
/* steps 1-2 */
TypedArrayObject array = ValidateTypedArray(cx, thisValue);
/* steps 3-4 */
long len = array.getArrayLength();
/* step 5 */
String separator = cx.getRealm().getListSeparator();
/* step 6 */
if (len == 0) {
return "";
}
/* steps 7-8 */
Double firstElement = array.elementGetDirect(cx, 0);
/* steps 9-10 */
StringBuilder r = new StringBuilder();
r.append(ToString(cx, Invoke(cx, firstElement, "toLocaleString", locales, options)));
/* steps 11-12 */
for (long k = 1; k < len; ++k) {
Double nextElement = array.elementGetDirect(cx, k);
r.append(separator).append(
ToString(cx, Invoke(cx, nextElement, "toLocaleString", locales, options)));
}
/* step 13 */
return r.toString();
}
/**
* 22.2.3.14 %TypedArray%.prototype.join ( separator )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param separator
* the separator string
* @return the result string
*/
@Function(name = "join", arity = 1)
public static Object join(ExecutionContext cx, Object thisValue, Object separator) {
// 22.1.3.12 Array.prototype.join (separator)
/* steps 1-2 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* steps 3-4 */
long len = o.getArrayLength();
/* step 5 */
if (Type.isUndefined(separator)) {
separator = ",";
}
/* steps 6-7 */
String sep = ToFlatString(cx, separator);
/* step 8 */
if (len == 0) {
return "";
}
/* step 9 */
double element0 = o.elementGetDirect(cx, 0);
/* steps 10-11 */
StringBuilder r = new StringBuilder();
r.append(ToString(element0));
/* steps 12-13 */
for (long k = 1; k < len; ++k) {
double element = o.elementGetDirect(cx, k);
r.append(sep).append(ToString(element));
}
/* step 14 */
return r.toString();
}
/**
* 22.2.3.21 %TypedArray%.prototype.reverse ( )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @return this typed array object
*/
@Function(name = "reverse", arity = 0)
public static Object reverse(ExecutionContext cx, Object thisValue) {
// 22.1.3.20 Array.prototype.reverse ( )
/* steps 1-2 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* steps 3-4 */
long len = o.getArrayLength();
/* step 5 */
final long middle = len / 2L;
/* steps 6-7 */
for (long lower = 0; lower != middle; ++lower) {
long upper = len - lower - 1;
long upperP = upper;
long lowerP = lower;
double lowerValue = o.elementGetDirect(cx, lowerP);
double upperValue = o.elementGetDirect(cx, upperP);
o.elementSetDirect(cx, lowerP, upperValue);
o.elementSetDirect(cx, upperP, lowerValue);
}
/* step 8 */
return o;
}
/**
* 22.2.3.23 %TypedArray%.prototype.slice ( start, end )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param start
* the start position
* @param end
* the end position
* @return the new typed array
*/
@Function(name = "slice", arity = 2)
public static Object slice(ExecutionContext cx, Object thisValue, Object start, Object end) {
/* steps 1-2 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* step 3 */
long len = o.getArrayLength();
/* steps 4-5 */
long k = ToArrayIndex(cx, start, len);
/* steps 6-7 */
long finall = Type.isUndefined(end) ? len : ToArrayIndex(cx, end, len);
/* step 8 */
long count = Math.max(finall - k, 0);
/* step 9 */
TypedArrayObject a = TypedArraySpeciesCreate(cx, o, count);
/* steps 10-11 */
ElementType srcType = o.getElementType();
/* steps 12-13 */
ElementType targetType = a.getElementType();
/* steps 14-15 */
if (srcType != targetType) {
/* step 14 */
/* steps 14.a-b */
for (long n = 0; k < finall; ++k, ++n) {
/* step 14.b.i */
long pk = k;
/* step 14.b.ii */
double kvalue = o.elementGetDirect(cx, pk);
/* step 14.b.iii */
a.elementSetDirect(cx, n, kvalue);
}
} else if (count > 0) {
/* step 15 */
/* step 15.a */
ArrayBuffer srcBuffer = o.getBuffer();
/* step 15.b */
if (IsDetachedBuffer(srcBuffer)) {
throw newTypeError(cx, Messages.Key.BufferDetached);
}
/* step 15.c */
ArrayBuffer targetBuffer = a.getBuffer();
/* step 15.d */
int elementSize = srcType.size();
/* step 15.e (note) */
/* step 15.f */
long srcByteOffset = o.getByteOffset();
/* step 15.g */
long targetByteIndex = a.getByteOffset();
/* step 15.h */
long srcByteIndex = srcByteOffset + k * elementSize;
/* steps 15.i-j */
ByteBuffer srcData = srcBuffer.getData();
ByteBuffer targetData = targetBuffer.getData();
long countByteLength = count * elementSize;
assert (srcByteIndex + countByteLength) <= srcData.capacity();
assert countByteLength <= targetData.capacity();
assert srcData != targetData;
srcData.limit((int) (srcByteIndex + countByteLength)).position((int) srcByteIndex);
targetData.limit((int) countByteLength).position((int) targetByteIndex);
targetData.put(srcData);
srcData.clear();
targetData.clear();
}
/* step 16 */
return a;
}
private static final class FunctionComparator implements Comparator<Double> {
private final ExecutionContext cx;
private final Callable comparefn;
private final ArrayBuffer buffer;
FunctionComparator(ExecutionContext cx, Callable comparefn, ArrayBuffer buffer) {
this.cx = cx;
this.comparefn = comparefn;
this.buffer = buffer;
}
@Override
public int compare(Double x, Double y) {
double c = ToNumber(cx, comparefn.call(cx, UNDEFINED, x, y));
if (IsDetachedBuffer(buffer)) {
throw newTypeError(cx, Messages.Key.BufferDetached);
}
return (c < 0 ? -1 : c > 0 ? 1 : 0);
}
}
/**
* 22.2.3.25 %TypedArray%.prototype.sort ( comparefn )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param comparefn
* the comparator function
* @return this typed array object
*/
@Function(name = "sort", arity = 1)
public static Object sort(ExecutionContext cx, Object thisValue, Object comparefn) {
/* steps 1-3 */
TypedArrayObject obj = ValidateTypedArray(cx, thisValue);
/* step 4 */
long len = obj.getArrayLength();
// return if array is empty or has only one element
if (len <= 1) {
return obj;
}
// handle OOM early
if (len > Integer.MAX_VALUE) {
throw newInternalError(cx, Messages.Key.OutOfMemory);
}
int length = (int) len;
if (!Type.isUndefined(comparefn)) {
if (!IsCallable(comparefn)) {
throw newTypeError(cx, Messages.Key.NotCallable);
}
Double[] elements = new Double[length];
for (int i = 0; i < length; ++i) {
elements[i] = obj.elementGetDirect(cx, i);
}
Comparator<Double> comparator = new FunctionComparator(cx, (Callable) comparefn, obj.getBuffer());
try {
Arrays.sort(elements, comparator);
} catch (IllegalArgumentException e) {
// `IllegalArgumentException: Comparison method violates its general contract!`
// just ignore this exception...
}
for (int i = 0; i < length; ++i) {
obj.elementSetDirect(cx, i, (double) elements[i]);
}
} else {
double[] elements = new double[length];
for (int i = 0; i < length; ++i) {
elements[i] = obj.elementGetDirect(cx, i);
}
Arrays.sort(elements);
for (int i = 0; i < length; ++i) {
obj.elementSetDirect(cx, i, elements[i]);
}
}
return obj;
}
/**
* 22.2.3.13 %TypedArray%.prototype.indexOf (searchElement [ , fromIndex ] )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param searchElement
* the search element
* @param fromIndex
* the optional start index
* @return the result index
*/
@Function(name = "indexOf", arity = 1)
public static Object indexOf(ExecutionContext cx, Object thisValue, Object searchElement,
@Optional(Optional.Default.NONE) Object fromIndex) {
// 22.1.3.11 Array.prototype.indexOf ( searchElement [ , fromIndex ] )
/* steps 1-2 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* steps 3-4 */
long len = o.getArrayLength();
/* step 5 */
if (len == 0) {
return -1;
}
/* steps 6-7 */
long n;
if (fromIndex != null) {
n = (long) ToInteger(cx, fromIndex);
} else {
n = 0;
}
/* step 8 */
if (n >= len) {
return -1;
}
/* steps 9-10 */
long k;
if (n >= 0) {
k = n;
} else {
k = len - Math.abs(n);
if (k < 0) {
k = 0;
}
}
/* step 11 */
if (Type.isNumber(searchElement) && !Double.isNaN(Type.numberValue(searchElement))) {
double needle = Type.numberValue(searchElement);
for (; k < len; ++k) {
long pk = k;
double elementk = o.elementGetDirect(cx, pk);
boolean same = needle == elementk; // StrictEqualityComparison
if (same) {
return k;
}
}
} else if (k < len && IsDetachedBuffer(o.getBuffer())) {
throw newTypeError(cx, Messages.Key.BufferDetached);
}
/* step 12 */
return -1;
}
/**
* 22.2.3.16 %TypedArray%.prototype.lastIndexOf ( searchElement [ , fromIndex ] )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param searchElement
* the search element
* @param fromIndex
* the optional start index
* @return the result index
*/
@Function(name = "lastIndexOf", arity = 1)
public static Object lastIndexOf(ExecutionContext cx, Object thisValue,
Object searchElement, @Optional(Optional.Default.NONE) Object fromIndex) {
// 22.1.3.14 Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] )
/* steps 1-2 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* steps 3-4 */
long len = o.getArrayLength();
/* step 5 */
if (len == 0) {
return -1;
}
/* steps 6-7 */
long n;
if (fromIndex != null) {
n = (long) ToInteger(cx, fromIndex);
} else {
n = len - 1;
}
/* steps 8-9 */
long k;
if (n >= 0) {
k = Math.min(n, len - 1);
} else {
k = len - Math.abs(n);
}
/* step 10 */
if (Type.isNumber(searchElement) && !Double.isNaN(Type.numberValue(searchElement))) {
double needle = Type.numberValue(searchElement);
for (; k >= 0; --k) {
long pk = k;
double elementk = o.elementGetDirect(cx, pk);
boolean same = needle == elementk; // StrictEqualityComparison
if (same) {
return k;
}
}
} else if (k >= 0 && IsDetachedBuffer(o.getBuffer())) {
throw newTypeError(cx, Messages.Key.BufferDetached);
}
/* step 11 */
return -1;
}
/**
* 22.2.3.7 %TypedArray%.prototype.every ( callbackfn [ , thisArg ] )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param callbackfn
* the callback function
* @param thisArg
* the optional this-argument for the callback function
* @return {@code true} if every element matches
*/
@Function(name = "every", arity = 1)
public static Object every(ExecutionContext cx, Object thisValue, Object callbackfn,
Object thisArg) {
// 22.1.3.5 Array.prototype.every ( callbackfn [ , thisArg ] )
/* steps 1-2 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* steps 3-4 */
long len = o.getArrayLength();
/* step 5 */
if (!IsCallable(callbackfn)) {
throw newTypeError(cx, Messages.Key.NotCallable);
}
Callable callback = (Callable) callbackfn;
/* step 6 (omitted) */
/* steps 7-8 */
for (long k = 0; k < len; ++k) {
long pk = k;
Double kvalue = o.elementGetDirect(cx, pk);
boolean testResult = ToBoolean(callback.call(cx, thisArg, kvalue, k, o));
if (!testResult) {
return false;
}
}
/* step 9 */
return true;
}
/**
* 22.2.3.24 %TypedArray%.prototype.some ( callbackfn [ , thisArg ] )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param callbackfn
* the callback function
* @param thisArg
* the optional this-argument for the callback function
* @return {@code true} if some elements match
*/
@Function(name = "some", arity = 1)
public static Object some(ExecutionContext cx, Object thisValue, Object callbackfn,
Object thisArg) {
// 22.1.3.23 Array.prototype.some ( callbackfn [ , thisArg ] )
/* steps 1-2 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* steps 3-4 */
long len = o.getArrayLength();
/* step 5 */
if (!IsCallable(callbackfn)) {
throw newTypeError(cx, Messages.Key.NotCallable);
}
Callable callback = (Callable) callbackfn;
/* step 6 (omitted) */
/* steps 7-8 */
for (long k = 0; k < len; ++k) {
long pk = k;
Double kvalue = o.elementGetDirect(cx, pk);
boolean testResult = ToBoolean(callback.call(cx, thisArg, kvalue, k, o));
if (testResult) {
return true;
}
}
/* step 9 */
return false;
}
/**
* 22.2.3.12 %TypedArray%.prototype.forEach ( callbackfn [ , thisArg ] )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param callbackfn
* the callback function
* @param thisArg
* the optional this-argument for the callback function
* @return the undefined value
*/
@Function(name = "forEach", arity = 1)
public static Object forEach(ExecutionContext cx, Object thisValue, Object callbackfn,
Object thisArg) {
// 22.1.3.10 Array.prototype.forEach ( callbackfn [ , thisArg ] )
/* steps 1-2 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* steps 3-4 */
long len = o.getArrayLength();
/* step 5 */
if (!IsCallable(callbackfn)) {
throw newTypeError(cx, Messages.Key.NotCallable);
}
Callable callback = (Callable) callbackfn;
/* step 6 (omitted) */
/* steps 7-8 */
for (long k = 0; k < len; ++k) {
long pk = k;
Double kvalue = o.elementGetDirect(cx, pk);
callback.call(cx, thisArg, kvalue, k, o);
}
/* step 9 */
return UNDEFINED;
}
/**
* 22.2.3.18 %TypedArray%.prototype.map ( callbackfn [ , thisArg ] )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param callbackfn
* the callback function
* @param thisArg
* the optional this-argument for the callback function
* @return the mapped value
*/
@Function(name = "map", arity = 1)
public static Object map(ExecutionContext cx, Object thisValue, Object callbackfn, Object thisArg) {
/* steps 1-2 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* step 3 */
long len = o.getArrayLength();
/* step 4 */
if (!IsCallable(callbackfn)) {
throw newTypeError(cx, Messages.Key.NotCallable);
}
Callable callback = (Callable) callbackfn;
/* step 5 (omitted) */
/* step 6 */
TypedArrayObject a = TypedArraySpeciesCreate(cx, o, len);
/* steps 7-8 */
for (long k = 0; k < len; ++k) {
long pk = k;
Double kvalue = o.elementGetDirect(cx, pk);
Object mappedValue = callback.call(cx, thisArg, kvalue, k, o);
a.elementSetDirect(cx, pk, ToNumber(cx, mappedValue));
}
/* step 9 */
return a;
}
/**
* 22.2.3.9 %TypedArray%.prototype.filter ( callbackfn [ , thisArg ] )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param callbackfn
* the callback function
* @param thisArg
* the optional this-argument for the callback function
* @return the filtered value
*/
@Function(name = "filter", arity = 1)
public static Object filter(ExecutionContext cx, Object thisValue, Object callbackfn, Object thisArg) {
/* steps 1-2 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* step 3 */
long len = o.getArrayLength();
/* step 4 */
if (!IsCallable(callbackfn)) {
throw newTypeError(cx, Messages.Key.NotCallable);
}
Callable callback = (Callable) callbackfn;
/* step 5 (omitted) */
/* steps 6, 8 */
int captured = 0;
double[] kept = new double[(int) Math.min(len, 1024)];
/* steps 7, 9 */
for (long k = 0; k < len; ++k) {
long pk = k;
double kvalue = o.elementGetDirect(cx, pk);
boolean selected = ToBoolean(callback.call(cx, thisArg, kvalue, k, o));
if (selected) {
if (captured == kept.length) {
kept = Arrays.copyOf(kept, captured + (captured >> 1));
}
kept[captured++] = kvalue;
}
}
/* step 10 */
TypedArrayObject a = TypedArraySpeciesCreate(cx, o, captured);
/* steps 11-12 */
for (int n = 0; n < captured; ++n) {
double e = kept[n];
a.elementSetDirect(cx, n, e);
}
/* step 13 */
return a;
}
/**
* 22.2.3.19 %TypedArray%.prototype.reduce ( callbackfn [, initialValue] )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param callbackfn
* the callback function
* @param initialValue
* the initial value
* @return the reduced value
*/
@Function(name = "reduce", arity = 1)
public static Object reduce(ExecutionContext cx, Object thisValue, Object callbackfn,
@Optional(Optional.Default.NONE) Object initialValue) {
// 22.1.3.18 Array.prototype.reduce ( callbackfn [ , initialValue ] )
/* steps 1-2 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* steps 3-4 */
long len = o.getArrayLength();
/* step 5 */
if (!IsCallable(callbackfn)) {
throw newTypeError(cx, Messages.Key.NotCallable);
}
Callable callback = (Callable) callbackfn;
/* step 6 */
if (len == 0 && initialValue == null) {
throw newTypeError(cx, Messages.Key.ReduceInitialValue);
}
/* step 7 */
long k = 0;
/* steps 8-9 */
Object accumulator;
if (initialValue != null) {
accumulator = initialValue;
} else {
accumulator = o.elementGetDirect(cx, k++);
}
/* step 10 */
for (; k < len; ++k) {
long pk = k;
Double kvalue = o.elementGetDirect(cx, pk);
accumulator = callback.call(cx, UNDEFINED, accumulator, kvalue, k, o);
}
/* step 11 */
return accumulator;
}
/**
* 22.2.3.20 %TypedArray%.prototype.reduceRight ( callbackfn [, initialValue] )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param callbackfn
* the callback function
* @param initialValue
* the initial value
* @return the reduced value
*/
@Function(name = "reduceRight", arity = 1)
public static Object reduceRight(ExecutionContext cx, Object thisValue, Object callbackfn,
@Optional(Optional.Default.NONE) Object initialValue) {
// 22.1.3.19 Array.prototype.reduceRight ( callbackfn [ , initialValue ] )
/* steps 1-2 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* steps 3-4 */
long len = o.getArrayLength();
/* step 5 */
if (!IsCallable(callbackfn)) {
throw newTypeError(cx, Messages.Key.NotCallable);
}
Callable callback = (Callable) callbackfn;
/* step 6 */
if (len == 0 && initialValue == null) {
throw newTypeError(cx, Messages.Key.ReduceInitialValue);
}
/* step 7 */
long k = len - 1;
/* steps 8-9 */
Object accumulator;
if (initialValue != null) {
accumulator = initialValue;
} else {
accumulator = o.elementGetDirect(cx, k--);
}
/* step 10 */
for (; k >= 0; --k) {
long pk = k;
Double kvalue = o.elementGetDirect(cx, pk);
accumulator = callback.call(cx, UNDEFINED, accumulator, kvalue, k, o);
}
/* step 11 */
return accumulator;
}
/**
* 22.2.3.10 %TypedArray%.prototype.find (predicate [ , thisArg ] )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param predicate
* the predicate function
* @param thisArg
* the optional this-argument for the predicate function
* @return the result value
*/
@Function(name = "find", arity = 1)
public static Object find(ExecutionContext cx, Object thisValue, Object predicate,
Object thisArg) {
// 22.1.3.8 Array.prototype.find ( predicate [ , thisArg ] )
/* steps 1-2 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* steps 3-4 */
long len = o.getArrayLength();
/* step 5 */
if (!IsCallable(predicate)) {
throw newTypeError(cx, Messages.Key.NotCallable);
}
Callable pred = (Callable) predicate;
/* step 6 (omitted) */
/* steps 7-8 */
for (long k = 0; k < len; ++k) {
long pk = k;
Double kvalue = o.elementGetDirect(cx, pk);
boolean testResult = ToBoolean(pred.call(cx, thisArg, kvalue, k, o));
if (testResult) {
return kvalue;
}
}
/* step 9 */
return UNDEFINED;
}
/**
* 22.2.3.11 %TypedArray%.prototype.findIndex ( predicate [ , thisArg ] )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param predicate
* the predicate function
* @param thisArg
* the optional this-argument for the predicate function
* @return the result index
*/
@Function(name = "findIndex", arity = 1)
public static Object findIndex(ExecutionContext cx, Object thisValue, Object predicate,
Object thisArg) {
// 22.1.3.9 Array.prototype.findIndex ( predicate [ , thisArg ] )
/* steps 1-2 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* steps 3-4 */
long len = o.getArrayLength();
/* step 5 */
if (!IsCallable(predicate)) {
throw newTypeError(cx, Messages.Key.NotCallable);
}
Callable pred = (Callable) predicate;
/* step 6 (omitted) */
/* steps 7-8 */
for (long k = 0; k < len; ++k) {
long pk = k;
Double kvalue = o.elementGetDirect(cx, pk);
boolean testResult = ToBoolean(pred.call(cx, thisArg, kvalue, k, o));
if (testResult) {
return k;
}
}
/* step 9 */
return -1;
}
/**
* 22.2.3.8 %TypedArray%.prototype.fill (value [ , start [ , end ] ] )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param value
* the fill value
* @param start
* the start index
* @param end
* the end index
* @return this typed array object
*/
@Function(name = "fill", arity = 1)
public static Object fill(ExecutionContext cx, Object thisValue, Object value,
Object start, Object end) {
// 22.1.3.6 Array.prototype.fill (value [ , start [ , end ] ] )
/* steps 1-2 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* steps 3-4 */
long len = o.getArrayLength();
/* steps 5-7 */
long k = ToArrayIndex(cx, start, len);
/* steps 8-10 */
long finall = Type.isUndefined(end) ? len : ToArrayIndex(cx, end, len);
/* step 11 */
for (; k < finall; ++k) {
long pk = k;
o.elementSetDirect(cx, pk, ToNumber(cx, value));
}
/* step 12 */
return o;
}
/**
* 22.2.3.5 %TypedArray%.prototype.copyWithin (target, start [, end ] )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param target
* the target index
* @param start
* the start index
* @param end
* the end index
* @return this typed array object
*/
@Function(name = "copyWithin", arity = 2)
public static Object copyWithin(ExecutionContext cx, Object thisValue, Object target,
Object start, Object end) {
// 22.1.3.3 Array.prototype.copyWithin (target, start [, end ] )
/* steps 1-2 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* steps 3-4 */
long len = o.getArrayLength();
/* steps 5-7 */
long to = ToArrayIndex(cx, target, len);
/* steps 8-10 */
long from = ToArrayIndex(cx, start, len);
/* steps 11-13 */
long finall = Type.isUndefined(end) ? len : ToArrayIndex(cx, end, len);
/* step 14 */
long count = Math.min(finall - from, len - to);
/* steps 15-17 */
if (count > 0) {
ArrayBuffer buffer = o.getBuffer();
if (IsDetachedBuffer(buffer)) {
throw newTypeError(cx, Messages.Key.BufferDetached);
}
int elementSize = o.getElementType().size();
long toByteIndex = to * elementSize;
long fromByteIndex = from * elementSize;
long countByteLength = count * elementSize;
ByteBuffer data = buffer.getData();
assert toByteIndex < data.capacity();
assert (fromByteIndex + countByteLength) <= data.capacity();
ByteBuffer dup = data.duplicate();
data.limit((int) (toByteIndex + countByteLength)).position((int) toByteIndex);
dup.limit((int) (fromByteIndex + countByteLength)).position((int) fromByteIndex);
data.put(dup).clear();
}
/* step 18 */
return o;
}
/**
* 22.2.3.6 %TypedArray%.prototype.entries ( )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @return the entries iterator
*/
@Function(name = "entries", arity = 0)
public static Object entries(ExecutionContext cx, Object thisValue) {
/* steps 1-3 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* step 4 */
return CreateArrayIterator(cx, o, ArrayIterationKind.KeyValue);
}
/**
* 22.2.3.15 %TypedArray%.prototype.keys ( )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @return the keys iterator
*/
@Function(name = "keys", arity = 0)
public static Object keys(ExecutionContext cx, Object thisValue) {
/* steps 1-3 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* step 4 */
return CreateArrayIterator(cx, o, ArrayIterationKind.Key);
}
/**
* 22.2.3.29 %TypedArray%.prototype.values ( )<br>
* 22.2.3.30 %TypedArray%.prototype [ @@iterator ] ( )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @return the values iterator
*/
@Function(name = "values", arity = 0, nativeId = TypedArrayPrototypeValues.class)
@AliasFunction(name = "[Symbol.iterator]", symbol = BuiltinSymbol.iterator)
public static Object values(ExecutionContext cx, Object thisValue) {
/* steps 1-3 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* step 4 */
return CreateArrayIterator(cx, o, ArrayIterationKind.Value);
}
/**
* 22.2.3.31 get %TypedArray%.prototype [ @@toStringTag ]
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @return the toString tag
*/
@Accessor(name = "get [Symbol.toStringTag]", symbol = BuiltinSymbol.toStringTag,
type = Accessor.Type.Getter, attributes = @Attributes(writable = false,
enumerable = false, configurable = true))
public static Object toStringTag(ExecutionContext cx, Object thisValue) {
/* steps 1-3 */
if (!(thisValue instanceof TypedArrayObject)) {
return UNDEFINED;
}
TypedArrayObject array = (TypedArrayObject) thisValue;
/* steps 4-6 */
return array.getTypedArrayName();
}
}
/**
* Proposed ECMAScript 7 additions
*/
@CompatibilityExtension(CompatibilityOption.ArrayIncludes)
public enum AdditionalProperties {
;
/**
* %TypedArray%.prototype.includes ( searchElement [ , fromIndex ] )
*
* @param cx
* the execution context
* @param thisValue
* the function this-value
* @param searchElement
* the search element
* @param fromIndex
* the optional start index
* @return the result index
*/
@Function(name = "includes", arity = 1)
public static Object includes(ExecutionContext cx, Object thisValue, Object searchElement,
Object fromIndex) {
/* step 1 */
TypedArrayObject o = ValidateTypedArray(cx, thisValue);
/* step 2 */
long len = o.getArrayLength();
/* step 3 */
if (len == 0) {
return false;
}
/* step 4 */
long n = (long) ToInteger(cx, fromIndex);
/* steps 5-6 */
long k;
if (n >= 0) {
k = n;
} else {
k = len + n;
if (k < 0) {
k = 0;
}
}
/* step 7 */
if (Type.isNumber(searchElement)) {
double needle = Type.numberValue(searchElement);
for (; k < len; ++k) {
/* step 7.a */
double element = o.elementGetDirect(cx, k);
/* step 7.b */
if (SameValueZero(needle, element)) {
return true;
}
}
} else if (k < len && IsDetachedBuffer(o.getBuffer())) {
throw newTypeError(cx, Messages.Key.BufferDetached);
}
/* step 8 */
return false;
}
}
}