summaryrefslogtreecommitdiff
path: root/chromium/v8/src/builtins/typed-array-from.tq
blob: aca548f324728eeeaf95a1faccb05ab6dc801a8f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// Copyright 2019 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.

#include 'src/builtins/builtins-typed-array-gen.h'

namespace typed_array {
const kBuiltinNameFrom: constexpr string = '%TypedArray%.from';

type BuiltinsName extends int31 constexpr 'Builtin';
const kTypedArrayPrototypeValues: constexpr BuiltinsName
    generates 'Builtin::kTypedArrayPrototypeValues';
const kArrayPrototypeValues: constexpr BuiltinsName
    generates 'Builtin::kArrayPrototypeValues';

extern builtin IterableToList(implicit context: Context)(JSAny, JSAny): JSArray;

// %TypedArray%.from ( source [ , mapfn [ , thisArg ] ] )
// https://tc39.github.io/ecma262/#sec-%typedarray%.from
transitioning javascript builtin
TypedArrayFrom(js-implicit context: NativeContext, receiver: JSAny)(
    ...arguments): JSTypedArray {
  try {
    const source: JSAny = arguments[0];
    const mapfnObj: JSAny = arguments[1];
    const thisArg = arguments[2];

    // 1. Let C be the this value.
    // 2. If IsConstructor(C) is false, throw a TypeError exception.
    const constructor = Cast<Constructor>(receiver) otherwise NotConstructor;

    // 3. If mapfn is undefined, then let mapping be false.
    // 4. Else,
    //   a. If IsCallable(mapfn) is false, throw a TypeError exception.
    //   b. Let mapping be true.
    const mapping: bool = mapfnObj != Undefined;
    if (mapping && !Is<Callable>(mapfnObj)) deferred {
        ThrowTypeError(MessageTemplate::kCalledNonCallable, mapfnObj);
      }

    // We split up this builtin differently to the way it is written in the
    // spec. We already have great code in the elements accessor for copying
    // from a JSArray into a TypedArray, so we use that when possible. We only
    // avoid calling into the elements accessor when we have a mapping
    // function, because we can't handle that. Here, presence of a mapping
    // function is the slow path. We also combine the two different loops in
    // the specification (starting at 7.e and 13) because they are essentially
    // identical. We also save on code-size this way.

    let finalLength: uintptr;
    let finalSource: JSAny;

    try {
      // 5. Let usingIterator be ? GetMethod(source, @@iterator).
      // TODO(v8:8906): Use iterator::GetIteratorMethod() once it supports
      // labels.
      const usingIterator = GetMethod(source, IteratorSymbolConstant())
          otherwise IteratorIsUndefined, IteratorNotCallable;

      try {
        // TypedArrays and JSArrays have iterators, so normally we would go
        // through the IterableToList case below, which would convert the
        // source to a JSArray (boxing the values if they won't fit in a Smi).
        //
        // However, if we can guarantee that the source object has the
        // built-in iterator and that the %ArrayIteratorPrototype%.next method
        // has not been overridden, then we know the behavior of the iterator:
        // returning the values in the TypedArray sequentially from index 0 to
        // length-1.
        //
        // In this case, we can avoid creating the intermediate array and the
        // associated HeapNumbers, and use the fast path in
        // TypedArrayCopyElements which uses the same ordering as the default
        // iterator.
        //
        // Drop through to the default check_iterator behavior if any of these
        // checks fail.

        // If there is a mapping, we need to gather the values from the
        // iterables before applying the mapping.
        if (mapping) goto UseUserProvidedIterator;

        const iteratorFn =
            Cast<JSFunction>(usingIterator) otherwise UseUserProvidedIterator;

        // Check that the ArrayIterator prototype's "next" method hasn't been
        // overridden.
        if (IsArrayIteratorProtectorCellInvalid()) goto UseUserProvidedIterator;

        typeswitch (source) {
          case (sourceArray: JSArray): {
            // Check that the iterator function is exactly
            // Builtin::kArrayPrototypeValues.
            if (!TaggedEqual(
                    iteratorFn.shared_function_info.function_data,
                    SmiConstant(kArrayPrototypeValues))) {
              goto UseUserProvidedIterator;
            }

            // Source is a JSArray with unmodified iterator behavior. Use the
            // source object directly, taking advantage of the special-case code
            // in TypedArrayCopyElements
            finalLength = Convert<uintptr>(sourceArray.length);
            finalSource = source;
          }
          case (sourceTypedArray: JSTypedArray): {
            const sourceBuffer = sourceTypedArray.buffer;
            if (IsDetachedBuffer(sourceBuffer)) goto UseUserProvidedIterator;

            // Check that the iterator function is exactly
            // Builtin::kTypedArrayPrototypeValues.
            if (!TaggedEqual(
                    iteratorFn.shared_function_info.function_data,
                    SmiConstant(kTypedArrayPrototypeValues)))
              goto UseUserProvidedIterator;

            // Source is a TypedArray with unmodified iterator behavior. Use the
            // source object directly, taking advantage of the special-case code
            // in TypedArrayCopyElements
            finalLength = sourceTypedArray.length;
            finalSource = source;
          }
          case (Object): {
            goto UseUserProvidedIterator;
          }
        }
      } label UseUserProvidedIterator {
        // 6. If usingIterator is not undefined, then
        //  a. Let values be ? IterableToList(source, usingIterator).
        //  b. Let len be the number of elements in values.
        const values: JSArray = IterableToList(source, usingIterator);

        finalLength = Convert<uintptr>(values.length);
        finalSource = values;
      }
    } label IteratorIsUndefined {
      // 7. NOTE: source is not an Iterable so assume it is already an
      // array-like object.

      // 8. Let arrayLike be ! ToObject(source).
      const arrayLike: JSReceiver = ToObject_Inline(context, source);

      // 9. Let len be ? LengthOfArrayLike(arrayLike).
      const length = GetLengthProperty(arrayLike);

      try {
        finalLength = ChangeSafeIntegerNumberToUintPtr(length)
            otherwise IfInvalidLength;
        finalSource = arrayLike;
      } label IfInvalidLength deferred {
        ThrowRangeError(MessageTemplate::kInvalidTypedArrayLength, length);
      }
    } label IteratorNotCallable(_value: JSAny) deferred {
      ThrowTypeError(
          MessageTemplate::kFirstArgumentIteratorSymbolNonCallable,
          kBuiltinNameFrom);
    }

    const finalLengthNum = Convert<Number>(finalLength);

    // 6c/10. Let targetObj be ? TypedArrayCreate(C, «len»).
    const targetObj =
        TypedArrayCreateByLength(constructor, finalLengthNum, kBuiltinNameFrom);

    if (!mapping) {
      // Fast path.
      if (finalLength != 0) {
        // Call runtime.
        TypedArrayCopyElements(context, targetObj, finalSource, finalLengthNum);
      }
      return targetObj;
    }
    // Slow path.

    const mapfn: Callable = Cast<Callable>(mapfnObj) otherwise unreachable;
    const accessor: TypedArrayAccessor =
        GetTypedArrayAccessor(targetObj.elements_kind);

    // 6d-6e and 11-12.
    // 11. Let k be 0.
    // 12. Repeat, while k < len
    for (let k: uintptr = 0; k < finalLength; k++) {
      // 12a. Let Pk be ! ToString(k).
      const kNum = Convert<Number>(k);

      // 12b. Let kValue be ? Get(arrayLike, Pk).
      const kValue: JSAny = GetProperty(finalSource, kNum);

      let mappedValue: JSAny;
      // 12c. If mapping is true, then
      if (mapping) {
        // i. Let mappedValue be ? Call(mapfn, T, « kValue, k »).
        mappedValue = Call(context, mapfn, thisArg, kValue, kNum);
      } else {
        // 12d. Else, let mappedValue be kValue.
        mappedValue = kValue;
      }

      // 12e. Perform ? Set(targetObj, Pk, mappedValue, true).
      // Buffer may be detached during executing ToNumber/ToBigInt.
      accessor.StoreJSAny(context, targetObj, k, mappedValue)
          otherwise IfDetached;

      // 12f. Set k to k + 1. (done by the loop).
    }
    return targetObj;
  } label NotConstructor deferred {
    ThrowTypeError(MessageTemplate::kNotConstructor, receiver);
  } label IfDetached deferred {
    ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameFrom);
  }
}
}