typedarray.js 13.6 KB
Newer Older
1
// Copyright 2013 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5
(function(global, utils) {
6

7 8
"use strict";

9
%CheckIsBootstrapping();
10

11 12 13
// -------------------------------------------------------------------
// Imports

14 15
// array.js has to come before typedarray.js for this to work
var ArrayToString = utils.ImportNow("ArrayToString");
16 17
var GetIterator;
var GetMethod;
18 19
var GlobalArray = global.Array;
var GlobalArrayBuffer = global.ArrayBuffer;
20
var GlobalArrayBufferPrototype = GlobalArrayBuffer.prototype;
21
var GlobalObject = global.Object;
22 23 24 25 26
var InnerArrayFind;
var InnerArrayFindIndex;
var InnerArrayJoin;
var InnerArraySort;
var InnerArrayToLocaleString;
27
var InternalArray = utils.InternalArray;
28 29
var MathMax = global.Math.max;
var MathMin = global.Math.min;
30
var iteratorSymbol = utils.ImportNow("iterator_symbol");
31
var speciesSymbol = utils.ImportNow("species_symbol");
32
var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
33

34
macro TYPED_ARRAYS(FUNCTION)
35 36 37 38 39 40 41 42 43
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)
44
endmacro
45

46
macro DECLARE_GLOBALS(NAME, SIZE)
47 48 49 50 51
var GlobalNAME = global.NAME;
endmacro

TYPED_ARRAYS(DECLARE_GLOBALS)

52 53
var GlobalTypedArray = %object_get_prototype_of(GlobalUint8Array);

54
utils.Import(function(from) {
55 56
  GetIterator = from.GetIterator;
  GetMethod = from.GetMethod;
57 58 59 60 61
  InnerArrayFind = from.InnerArrayFind;
  InnerArrayFindIndex = from.InnerArrayFindIndex;
  InnerArrayJoin = from.InnerArrayJoin;
  InnerArraySort = from.InnerArraySort;
  InnerArrayToLocaleString = from.InnerArrayToLocaleString;
62
});
63

64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
// 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);
}

83 84
// --------------- Typed Arrays ---------------------

85 86 87 88 89 90 91 92
// 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);
}

93 94
function TypedArrayDefaultConstructor(typedArray) {
  switch (%_ClassOf(typedArray)) {
95
macro TYPED_ARRAY_CONSTRUCTOR_CASE(NAME, ELEMENT_SIZE)
96 97 98 99 100 101 102
    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.
103
  throw %make_type_error(kIncompatibleMethodReceiver,
104 105 106 107 108 109 110 111 112
                      "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);
  }
113
  ValidateTypedArray(newTypedArray, "TypedArrayCreate");
114
  if (IS_NUMBER(arg0) && %_TypedArrayGetLength(newTypedArray) < arg0) {
115
    throw %make_type_error(kTypedArrayTooShort);
116 117 118 119
  }
  return newTypedArray;
}

120
function TypedArraySpeciesCreate(exemplar, arg0, arg1, arg2) {
121
  var defaultConstructor = TypedArrayDefaultConstructor(exemplar);
122
  var constructor = SpeciesConstructor(exemplar, defaultConstructor);
123 124 125
  return TypedArrayCreate(constructor, arg0, arg1, arg2);
}

126
macro TYPED_ARRAY_CONSTRUCTOR(NAME, ELEMENT_SIZE)
127
function NAMEConstructByIterable(obj, iterable, iteratorFn) {
128 129 130 131 132 133
  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 {
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
    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);
151 152 153
  }
}

154 155 156 157 158 159 160
// 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;
161
  %typed_array_construct_by_array_like(obj, typedArray, length, ELEMENT_SIZE);
162 163 164 165 166 167
  // 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);
168 169 170
  var prototype = bufferConstructor.prototype;
  // TODO(littledan): Use the right prototype based on bufferConstructor's realm
  if (IS_RECEIVER(prototype) && prototype !== GlobalArrayBufferPrototype) {
171
    %InternalSetPrototype(%TypedArrayGetBuffer(obj), prototype);
172 173 174
  }
}

175
function NAMEConstructor(arg1, arg2, arg3) {
176
  if (!IS_UNDEFINED(new.target)) {
binji's avatar
binji committed
177
    if (IS_ARRAYBUFFER(arg1) || IS_SHAREDARRAYBUFFER(arg1)) {
178
      %typed_array_construct_by_array_buffer(
179
          this, arg1, arg2, arg3, ELEMENT_SIZE);
180 181
    } else if (IS_TYPEDARRAY(arg1)) {
      NAMEConstructByTypedArray(this, arg1);
182
    } else if (IS_RECEIVER(arg1)) {
183
      var iteratorFn = arg1[iteratorSymbol];
184
      if (IS_UNDEFINED(iteratorFn)) {
185 186
        %typed_array_construct_by_array_like(
            this, arg1, arg1.length, ELEMENT_SIZE);
187 188 189
      } else {
        NAMEConstructByIterable(this, arg1, iteratorFn);
      }
190
    } else {
191
      %typed_array_construct_by_length(this, arg1, ELEMENT_SIZE);
192
    }
193
  } else {
194
    throw %make_type_error(kConstructorNotFunction, "NAME")
195
  }
196
}
197

198 199 200
function NAMESubArray(begin, end) {
  var beginInt = TO_INTEGER(begin);
  if (!IS_UNDEFINED(end)) {
201 202 203 204 205
    var endInt = TO_INTEGER(end);
    var srcLength = %_TypedArrayGetLength(this);
  } else {
    var srcLength = %_TypedArrayGetLength(this);
    var endInt = srcLength;
206
  }
207

208
  if (beginInt < 0) {
209
    beginInt = MathMax(0, srcLength + beginInt);
210
  } else {
211
    beginInt = MathMin(beginInt, srcLength);
212
  }
213

214
  if (endInt < 0) {
215
    endInt = MathMax(0, srcLength + endInt);
216
  } else {
217
    endInt = MathMin(endInt, srcLength);
218
  }
219

220 221
  if (endInt < beginInt) {
    endInt = beginInt;
222
  }
223

224 225 226
  var newLength = endInt - beginInt;
  var beginByteOffset =
      %_ArrayBufferViewGetByteOffset(this) + beginInt * ELEMENT_SIZE;
227 228
  return TypedArraySpeciesCreate(this, %TypedArrayGetBuffer(this),
                                 beginByteOffset, newLength);
229
}
230 231 232 233
endmacro

TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTOR)

234 235 236 237
DEFINE_METHOD(
  GlobalTypedArray.prototype,
  subarray(begin, end) {
    switch (%_ClassOf(this)) {
238
macro TYPED_ARRAY_SUBARRAY_CASE(NAME, ELEMENT_SIZE)
239 240
      case "NAME":
        return %_Call(NAMESubArray, this, begin, end);
241 242
endmacro
TYPED_ARRAYS(TYPED_ARRAY_SUBARRAY_CASE)
243 244 245
    }
    throw %make_type_error(kIncompatibleMethodReceiver,
                        "get %TypedArray%.prototype.subarray", this);
246
  }
247
);
248 249


250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
// 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;
}

267 268

// ES6 draft 07-15-13, section 22.2.3.9
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
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. */
);
287 288 289


// ES6 draft 07-15-13, section 22.2.3.10
290 291 292 293
DEFINE_METHOD_LEN(
  GlobalTypedArray.prototype,
  find(predicate, thisArg) {
    ValidateTypedArray(this, "%TypedArray%.prototype.find");
294

295
    var length = %_TypedArrayGetLength(this);
296

297 298 299 300
    return InnerArrayFind(predicate, thisArg, this, length);
  },
  1  /* Set function length. */
);
301 302 303


// ES6 draft 07-15-13, section 22.2.3.11
304 305 306 307
DEFINE_METHOD_LEN(
  GlobalTypedArray.prototype,
  findIndex(predicate, thisArg) {
    ValidateTypedArray(this, "%TypedArray%.prototype.findIndex");
308

309
    var length = %_TypedArrayGetLength(this);
310

311 312 313 314
    return InnerArrayFindIndex(predicate, thisArg, this, length);
  },
  1  /* Set function length. */
);
315 316 317


// ES6 draft 05-18-15, section 22.2.3.25
318 319 320 321
DEFINE_METHOD(
  GlobalTypedArray.prototype,
  sort(comparefn) {
    ValidateTypedArray(this, "%TypedArray%.prototype.sort");
322

323 324 325 326
    if (!IS_UNDEFINED(comparefn) && !IS_CALLABLE(comparefn)) {
      throw %make_type_error(kBadSortComparisonFunction, comparefn);
    }

327
    var length = %_TypedArrayGetLength(this);
328

329 330 331
    if (IS_UNDEFINED(comparefn)) {
      return %TypedArraySortFast(this);
    }
332

333 334 335
    return InnerArraySort(this, length, comparefn);
  }
);
336 337 338


// ES6 section 22.2.3.27
339 340 341 342
DEFINE_METHOD(
  GlobalTypedArray.prototype,
  toLocaleString() {
    ValidateTypedArray(this, "%TypedArray%.prototype.toLocaleString");
343

344
    var length = %_TypedArrayGetLength(this);
345

346 347 348
    return InnerArrayToLocaleString(this, length);
  }
);
349 350 351


// ES6 section 22.2.3.14
352 353 354 355
DEFINE_METHOD(
  GlobalTypedArray.prototype,
  join(separator) {
    ValidateTypedArray(this, "%TypedArray%.prototype.join");
356

357
    var length = %_TypedArrayGetLength(this);
358

359 360 361
    return InnerArrayJoin(separator, this, length);
  }
);
362 363 364


// ES6 draft 08-24-14, section 22.2.2.2
365 366 367 368 369 370 371 372 373
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;
374
  }
375
);
376 377


378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
// 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 ] ] )
399 400 401 402 403 404 405 406
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;
407
    } else {
408
      mapping = false;
409
    }
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
    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. */
);
427

428 429
// TODO(bmeurer): Migrate this to a proper builtin.
function TypedArrayConstructor() {
430
  throw %make_type_error(kConstructAbstractClass, "TypedArray");
431 432
}

433 434
// -------------------------------------------------------------------

435
%SetCode(GlobalTypedArray, TypedArrayConstructor);
436

437

438
%AddNamedProperty(GlobalTypedArray.prototype, "toString", ArrayToString,
439 440
                  DONT_ENUM);

441

442
macro SETUP_TYPED_ARRAY(NAME, ELEMENT_SIZE)
443
  %SetCode(GlobalNAME, NAMEConstructor);
444
  %FunctionSetPrototype(GlobalNAME, new GlobalObject());
445 446
  %InternalSetPrototype(GlobalNAME, GlobalTypedArray);
  %InternalSetPrototype(GlobalNAME.prototype, GlobalTypedArray.prototype);
447

448
  %AddNamedProperty(GlobalNAME, "BYTES_PER_ELEMENT", ELEMENT_SIZE,
449
                    READ_ONLY | DONT_ENUM | DONT_DELETE);
450

451
  %AddNamedProperty(GlobalNAME.prototype,
452
                    "constructor", global.NAME, DONT_ENUM);
453 454 455
  %AddNamedProperty(GlobalNAME.prototype,
                    "BYTES_PER_ELEMENT", ELEMENT_SIZE,
                    READ_ONLY | DONT_ENUM | DONT_DELETE);
456 457 458
endmacro

TYPED_ARRAYS(SETUP_TYPED_ARRAY)
459

460
})