blob: 7fa638fa897c35088dd0a67e06a4dc2042bd3df3 [file] [log] [blame]
// Copyright 2013 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
(function(global, utils) {
"use strict";
%CheckIsBootstrapping();
// -------------------------------------------------------------------
// Imports
// array.js has to come before typedarray.js for this to work
var ArrayToString = utils.ImportNow("ArrayToString");
var GetIterator;
var GetMethod;
var GlobalArray = global.Array;
var GlobalArrayBuffer = global.ArrayBuffer;
var GlobalArrayBufferPrototype = GlobalArrayBuffer.prototype;
var GlobalObject = global.Object;
var InnerArrayJoin;
var InnerArraySort;
var InnerArrayToLocaleString;
var InternalArray = utils.InternalArray;
var MathMax = global.Math.max;
var MathMin = global.Math.min;
var iteratorSymbol = utils.ImportNow("iterator_symbol");
var speciesSymbol = utils.ImportNow("species_symbol");
var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
macro TYPED_ARRAYS(FUNCTION)
FUNCTION(Uint8Array, 1)
FUNCTION(Int8Array, 1)
FUNCTION(Uint16Array, 2)
FUNCTION(Int16Array, 2)
FUNCTION(Uint32Array, 4)
FUNCTION(Int32Array, 4)
FUNCTION(Float32Array, 4)
FUNCTION(Float64Array, 8)
FUNCTION(Uint8ClampedArray, 1)
endmacro
macro DECLARE_GLOBALS(NAME, SIZE)
var GlobalNAME = global.NAME;
endmacro
TYPED_ARRAYS(DECLARE_GLOBALS)
macro IS_ARRAYBUFFER(arg)
(%_ClassOf(arg) === 'ArrayBuffer')
endmacro
macro IS_SHAREDARRAYBUFFER(arg)
(%_ClassOf(arg) === 'SharedArrayBuffer')
endmacro
macro IS_TYPEDARRAY(arg)
(%_IsTypedArray(arg))
endmacro
var GlobalTypedArray = %object_get_prototype_of(GlobalUint8Array);
utils.Import(function(from) {
GetIterator = from.GetIterator;
GetMethod = from.GetMethod;
InnerArrayJoin = from.InnerArrayJoin;
InnerArraySort = from.InnerArraySort;
InnerArrayToLocaleString = from.InnerArrayToLocaleString;
});
// ES2015 7.3.20
function SpeciesConstructor(object, defaultConstructor) {
var constructor = object.constructor;
if (IS_UNDEFINED(constructor)) {
return defaultConstructor;
}
if (!IS_RECEIVER(constructor)) {
throw %make_type_error(kConstructorNotReceiver);
}
var species = constructor[speciesSymbol];
if (IS_NULL_OR_UNDEFINED(species)) {
return defaultConstructor;
}
if (%IsConstructor(species)) {
return species;
}
throw %make_type_error(kSpeciesNotConstructor);
}
// --------------- Typed Arrays ---------------------
// ES6 section 22.2.3.5.1 ValidateTypedArray ( O )
function ValidateTypedArray(array, methodName) {
if (!IS_TYPEDARRAY(array)) throw %make_type_error(kNotTypedArray);
if (%_ArrayBufferViewWasNeutered(array))
throw %make_type_error(kDetachedOperation, methodName);
}
function TypedArrayDefaultConstructor(typedArray) {
switch (%_ClassOf(typedArray)) {
macro TYPED_ARRAY_CONSTRUCTOR_CASE(NAME, ELEMENT_SIZE)
case "NAME":
return GlobalNAME;
endmacro
TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTOR_CASE)
}
// The TypeError should not be generated since all callers should
// have already called ValidateTypedArray.
throw %make_type_error(kIncompatibleMethodReceiver,
"TypedArrayDefaultConstructor", this);
}
function TypedArrayCreate(constructor, arg0, arg1, arg2) {
if (IS_UNDEFINED(arg1)) {
var newTypedArray = new constructor(arg0);
} else {
var newTypedArray = new constructor(arg0, arg1, arg2);
}
ValidateTypedArray(newTypedArray, "TypedArrayCreate");
if (IS_NUMBER(arg0) && %_TypedArrayGetLength(newTypedArray) < arg0) {
throw %make_type_error(kTypedArrayTooShort);
}
return newTypedArray;
}
function TypedArraySpeciesCreate(exemplar, arg0, arg1, arg2) {
var defaultConstructor = TypedArrayDefaultConstructor(exemplar);
var constructor = SpeciesConstructor(exemplar, defaultConstructor);
return TypedArrayCreate(constructor, arg0, arg1, arg2);
}
macro TYPED_ARRAY_CONSTRUCTOR(NAME, ELEMENT_SIZE)
function NAMEConstructByIterable(obj, iterable, iteratorFn) {
if (%IterableToListCanBeElided(iterable)) {
// This .length access is unobservable, because it being observable would
// mean that iteration has side effects, and we wouldn't reach this path.
%typed_array_construct_by_array_like(
obj, iterable, iterable.length, ELEMENT_SIZE);
} else {
var list = new InternalArray();
// Reading the Symbol.iterator property of iterable twice would be
// observable with getters, so instead, we call the function which
// was already looked up, and wrap it in another iterable. The
// __proto__ of the new iterable is set to null to avoid any chance
// of modifications to Object.prototype being observable here.
var iterator = %_Call(iteratorFn, iterable);
var newIterable = {
__proto__: null
};
// TODO(littledan): Computed properties don't work yet in nosnap.
// Rephrase when they do.
newIterable[iteratorSymbol] = function() { return iterator; }
for (var value of newIterable) {
list.push(value);
}
%typed_array_construct_by_array_like(obj, list, list.length, ELEMENT_SIZE);
}
}
// ES#sec-typedarray-typedarray TypedArray ( typedArray )
function NAMEConstructByTypedArray(obj, typedArray) {
// TODO(littledan): Throw on detached typedArray
var srcData = %TypedArrayGetBuffer(typedArray);
var length = %_TypedArrayGetLength(typedArray);
var byteLength = %_ArrayBufferViewGetByteLength(typedArray);
var newByteLength = length * ELEMENT_SIZE;
%typed_array_construct_by_array_like(obj, typedArray, length, ELEMENT_SIZE);
// The spec requires that constructing a typed array using a SAB-backed typed
// array use the ArrayBuffer constructor, not the species constructor. See
// https://tc39.github.io/ecma262/#sec-typedarray-typedarray.
var bufferConstructor = IS_SHAREDARRAYBUFFER(srcData)
? GlobalArrayBuffer
: SpeciesConstructor(srcData, GlobalArrayBuffer);
var prototype = bufferConstructor.prototype;
// TODO(littledan): Use the right prototype based on bufferConstructor's realm
if (IS_RECEIVER(prototype) && prototype !== GlobalArrayBufferPrototype) {
%InternalSetPrototype(%TypedArrayGetBuffer(obj), prototype);
}
}
function NAMEConstructor(arg1, arg2, arg3) {
if (!IS_UNDEFINED(new.target)) {
if (IS_ARRAYBUFFER(arg1) || IS_SHAREDARRAYBUFFER(arg1)) {
%typed_array_construct_by_array_buffer(
this, arg1, arg2, arg3, ELEMENT_SIZE);
} else if (IS_TYPEDARRAY(arg1)) {
NAMEConstructByTypedArray(this, arg1);
} else if (IS_RECEIVER(arg1)) {
var iteratorFn = arg1[iteratorSymbol];
if (IS_UNDEFINED(iteratorFn)) {
%typed_array_construct_by_array_like(
this, arg1, arg1.length, ELEMENT_SIZE);
} else {
NAMEConstructByIterable(this, arg1, iteratorFn);
}
} else {
%typed_array_construct_by_length(this, arg1, ELEMENT_SIZE);
}
} else {
throw %make_type_error(kConstructorNotFunction, "NAME")
}
}
function NAMESubArray(begin, end) {
var beginInt = TO_INTEGER(begin);
if (!IS_UNDEFINED(end)) {
var endInt = TO_INTEGER(end);
var srcLength = %_TypedArrayGetLength(this);
} else {
var srcLength = %_TypedArrayGetLength(this);
var endInt = srcLength;
}
if (beginInt < 0) {
beginInt = MathMax(0, srcLength + beginInt);
} else {
beginInt = MathMin(beginInt, srcLength);
}
if (endInt < 0) {
endInt = MathMax(0, srcLength + endInt);
} else {
endInt = MathMin(endInt, srcLength);
}
if (endInt < beginInt) {
endInt = beginInt;
}
var newLength = endInt - beginInt;
var beginByteOffset =
%_ArrayBufferViewGetByteOffset(this) + beginInt * ELEMENT_SIZE;
return TypedArraySpeciesCreate(this, %TypedArrayGetBuffer(this),
beginByteOffset, newLength);
}
endmacro
TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTOR)
DEFINE_METHOD(
GlobalTypedArray.prototype,
subarray(begin, end) {
switch (%_ClassOf(this)) {
macro TYPED_ARRAY_SUBARRAY_CASE(NAME, ELEMENT_SIZE)
case "NAME":
return %_Call(NAMESubArray, this, begin, end);
endmacro
TYPED_ARRAYS(TYPED_ARRAY_SUBARRAY_CASE)
}
throw %make_type_error(kIncompatibleMethodReceiver,
"get %TypedArray%.prototype.subarray", this);
}
);
// The following functions cannot be made efficient on sparse arrays while
// preserving the semantics, since the calls to the receiver function can add
// or delete elements from the array.
function InnerTypedArrayFilter(f, receiver, array, length, result) {
var result_length = 0;
for (var i = 0; i < length; i++) {
if (i in array) {
var element = array[i];
if (%_Call(f, receiver, element, i, array)) {
%CreateDataProperty(result, result_length, element);
result_length++;
}
}
}
return result;
}
// ES6 draft 07-15-13, section 22.2.3.9
DEFINE_METHOD_LEN(
GlobalTypedArray.prototype,
filter(f, thisArg) {
ValidateTypedArray(this, "%TypeArray%.prototype.filter");
var length = %_TypedArrayGetLength(this);
if (!IS_CALLABLE(f)) throw %make_type_error(kCalledNonCallable, f);
var result = new InternalArray();
InnerTypedArrayFilter(f, thisArg, this, length, result);
var captured = result.length;
var output = TypedArraySpeciesCreate(this, captured);
for (var i = 0; i < captured; i++) {
output[i] = result[i];
}
return output;
},
1 /* Set function length. */
);
// ES6 draft 05-18-15, section 22.2.3.25
DEFINE_METHOD(
GlobalTypedArray.prototype,
sort(comparefn) {
ValidateTypedArray(this, "%TypedArray%.prototype.sort");
if (!IS_UNDEFINED(comparefn) && !IS_CALLABLE(comparefn)) {
throw %make_type_error(kBadSortComparisonFunction, comparefn);
}
var length = %_TypedArrayGetLength(this);
if (IS_UNDEFINED(comparefn)) {
return %TypedArraySortFast(this);
}
return InnerArraySort(this, length, comparefn);
}
);
// ES6 section 22.2.3.27
DEFINE_METHOD(
GlobalTypedArray.prototype,
toLocaleString() {
ValidateTypedArray(this, "%TypedArray%.prototype.toLocaleString");
var length = %_TypedArrayGetLength(this);
return InnerArrayToLocaleString(this, length);
}
);
// ES6 section 22.2.3.14
DEFINE_METHOD(
GlobalTypedArray.prototype,
join(separator) {
ValidateTypedArray(this, "%TypedArray%.prototype.join");
var length = %_TypedArrayGetLength(this);
return InnerArrayJoin(separator, this, length);
}
);
// ES6 draft 08-24-14, section 22.2.2.2
DEFINE_METHOD(
GlobalTypedArray,
of() {
var length = arguments.length;
var array = TypedArrayCreate(this, length);
for (var i = 0; i < length; i++) {
array[i] = arguments[i];
}
return array;
}
);
// ES#sec-iterabletoarraylike Runtime Semantics: IterableToArrayLike( items )
function IterableToArrayLike(items) {
var iterable = GetMethod(items, iteratorSymbol);
if (!IS_UNDEFINED(iterable)) {
var internal_array = new InternalArray();
var i = 0;
for (var value of
{ [iteratorSymbol]() { return GetIterator(items, iterable) } }) {
internal_array[i] = value;
i++;
}
var array = [];
%MoveArrayContents(internal_array, array);
return array;
}
return TO_OBJECT(items);
}
// ES#sec-%typedarray%.from
// %TypedArray%.from ( source [ , mapfn [ , thisArg ] ] )
DEFINE_METHOD_LEN(
GlobalTypedArray,
'from'(source, mapfn, thisArg) {
if (!%IsConstructor(this)) throw %make_type_error(kNotConstructor, this);
var mapping;
if (!IS_UNDEFINED(mapfn)) {
if (!IS_CALLABLE(mapfn)) throw %make_type_error(kCalledNonCallable, this);
mapping = true;
} else {
mapping = false;
}
var arrayLike = IterableToArrayLike(source);
var length = TO_LENGTH(arrayLike.length);
var targetObject = TypedArrayCreate(this, length);
var value, mappedValue;
for (var i = 0; i < length; i++) {
value = arrayLike[i];
if (mapping) {
mappedValue = %_Call(mapfn, thisArg, value, i);
} else {
mappedValue = value;
}
targetObject[i] = mappedValue;
}
return targetObject;
},
1 /* Set function length. */
);
// TODO(bmeurer): Migrate this to a proper builtin.
function TypedArrayConstructor() {
throw %make_type_error(kConstructAbstractClass, "TypedArray");
}
// -------------------------------------------------------------------
%SetCode(GlobalTypedArray, TypedArrayConstructor);
%AddNamedProperty(GlobalTypedArray.prototype, "toString", ArrayToString,
DONT_ENUM);
macro SETUP_TYPED_ARRAY(NAME, ELEMENT_SIZE)
%SetCode(GlobalNAME, NAMEConstructor);
endmacro
TYPED_ARRAYS(SETUP_TYPED_ARRAY)
})