1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/builtins/builtins-utils.h"
6 #include "src/builtins/builtins.h"
7 #include "src/code-stub-assembler.h"
8 #include "src/counters.h"
9 #include "src/objects-inl.h"
10
11 namespace v8 {
12 namespace internal {
13
14 class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
15 public:
TypedArrayBuiltinsAssembler(compiler::CodeAssemblerState * state)16 explicit TypedArrayBuiltinsAssembler(compiler::CodeAssemblerState* state)
17 : CodeStubAssembler(state) {}
18
19 protected:
20 void GenerateTypedArrayPrototypeGetter(const char* method_name,
21 int object_offset);
22 template <IterationKind kIterationKind>
23 void GenerateTypedArrayPrototypeIterationMethod(const char* method_name);
24 };
25
26 // -----------------------------------------------------------------------------
27 // ES6 section 22.2 TypedArray Objects
28
29 // ES6 section 22.2.3.1 get %TypedArray%.prototype.buffer
BUILTIN(TypedArrayPrototypeBuffer)30 BUILTIN(TypedArrayPrototypeBuffer) {
31 HandleScope scope(isolate);
32 CHECK_RECEIVER(JSTypedArray, typed_array, "get TypedArray.prototype.buffer");
33 return *typed_array->GetBuffer();
34 }
35
GenerateTypedArrayPrototypeGetter(const char * method_name,int object_offset)36 void TypedArrayBuiltinsAssembler::GenerateTypedArrayPrototypeGetter(
37 const char* method_name, int object_offset) {
38 Node* receiver = Parameter(0);
39 Node* context = Parameter(3);
40
41 // Check if the {receiver} is actually a JSTypedArray.
42 Label receiver_is_incompatible(this, Label::kDeferred);
43 GotoIf(TaggedIsSmi(receiver), &receiver_is_incompatible);
44 GotoIfNot(HasInstanceType(receiver, JS_TYPED_ARRAY_TYPE),
45 &receiver_is_incompatible);
46
47 // Check if the {receiver}'s JSArrayBuffer was neutered.
48 Node* receiver_buffer =
49 LoadObjectField(receiver, JSTypedArray::kBufferOffset);
50 Label if_receiverisneutered(this, Label::kDeferred);
51 GotoIf(IsDetachedBuffer(receiver_buffer), &if_receiverisneutered);
52 Return(LoadObjectField(receiver, object_offset));
53
54 Bind(&if_receiverisneutered);
55 {
56 // The {receiver}s buffer was neutered, default to zero.
57 Return(SmiConstant(0));
58 }
59
60 Bind(&receiver_is_incompatible);
61 {
62 // The {receiver} is not a valid JSTypedArray.
63 CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context,
64 HeapConstant(
65 factory()->NewStringFromAsciiChecked(method_name, TENURED)),
66 receiver);
67 Unreachable();
68 }
69 }
70
71 // ES6 section 22.2.3.2 get %TypedArray%.prototype.byteLength
TF_BUILTIN(TypedArrayPrototypeByteLength,TypedArrayBuiltinsAssembler)72 TF_BUILTIN(TypedArrayPrototypeByteLength, TypedArrayBuiltinsAssembler) {
73 GenerateTypedArrayPrototypeGetter("get TypedArray.prototype.byteLength",
74 JSTypedArray::kByteLengthOffset);
75 }
76
77 // ES6 section 22.2.3.3 get %TypedArray%.prototype.byteOffset
TF_BUILTIN(TypedArrayPrototypeByteOffset,TypedArrayBuiltinsAssembler)78 TF_BUILTIN(TypedArrayPrototypeByteOffset, TypedArrayBuiltinsAssembler) {
79 GenerateTypedArrayPrototypeGetter("get TypedArray.prototype.byteOffset",
80 JSTypedArray::kByteOffsetOffset);
81 }
82
83 // ES6 section 22.2.3.18 get %TypedArray%.prototype.length
TF_BUILTIN(TypedArrayPrototypeLength,TypedArrayBuiltinsAssembler)84 TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) {
85 GenerateTypedArrayPrototypeGetter("get TypedArray.prototype.length",
86 JSTypedArray::kLengthOffset);
87 }
88
89 template <IterationKind kIterationKind>
GenerateTypedArrayPrototypeIterationMethod(const char * method_name)90 void TypedArrayBuiltinsAssembler::GenerateTypedArrayPrototypeIterationMethod(
91 const char* method_name) {
92 Node* receiver = Parameter(0);
93 Node* context = Parameter(3);
94
95 Label throw_bad_receiver(this, Label::kDeferred);
96 Label throw_typeerror(this, Label::kDeferred);
97
98 GotoIf(TaggedIsSmi(receiver), &throw_bad_receiver);
99
100 Node* map = LoadMap(receiver);
101 Node* instance_type = LoadMapInstanceType(map);
102 GotoIf(Word32NotEqual(instance_type, Int32Constant(JS_TYPED_ARRAY_TYPE)),
103 &throw_bad_receiver);
104
105 // Check if the {receiver}'s JSArrayBuffer was neutered.
106 Node* receiver_buffer =
107 LoadObjectField(receiver, JSTypedArray::kBufferOffset);
108 Label if_receiverisneutered(this, Label::kDeferred);
109 GotoIf(IsDetachedBuffer(receiver_buffer), &if_receiverisneutered);
110
111 Return(CreateArrayIterator(receiver, map, instance_type, context,
112 kIterationKind));
113
114 Variable var_message(this, MachineRepresentation::kTagged);
115 Bind(&throw_bad_receiver);
116 var_message.Bind(SmiConstant(MessageTemplate::kNotTypedArray));
117 Goto(&throw_typeerror);
118
119 Bind(&if_receiverisneutered);
120 var_message.Bind(
121 SmiConstant(Smi::FromInt(MessageTemplate::kDetachedOperation)));
122 Goto(&throw_typeerror);
123
124 Bind(&throw_typeerror);
125 {
126 Node* method_arg = HeapConstant(
127 isolate()->factory()->NewStringFromAsciiChecked(method_name, TENURED));
128 Node* result = CallRuntime(Runtime::kThrowTypeError, context,
129 var_message.value(), method_arg);
130 Return(result);
131 }
132 }
133
TF_BUILTIN(TypedArrayPrototypeValues,TypedArrayBuiltinsAssembler)134 TF_BUILTIN(TypedArrayPrototypeValues, TypedArrayBuiltinsAssembler) {
135 GenerateTypedArrayPrototypeIterationMethod<IterationKind::kValues>(
136 "%TypedArray%.prototype.values()");
137 }
138
TF_BUILTIN(TypedArrayPrototypeEntries,TypedArrayBuiltinsAssembler)139 TF_BUILTIN(TypedArrayPrototypeEntries, TypedArrayBuiltinsAssembler) {
140 GenerateTypedArrayPrototypeIterationMethod<IterationKind::kEntries>(
141 "%TypedArray%.prototype.entries()");
142 }
143
TF_BUILTIN(TypedArrayPrototypeKeys,TypedArrayBuiltinsAssembler)144 TF_BUILTIN(TypedArrayPrototypeKeys, TypedArrayBuiltinsAssembler) {
145 GenerateTypedArrayPrototypeIterationMethod<IterationKind::kKeys>(
146 "%TypedArray%.prototype.keys()");
147 }
148
149 namespace {
150
CapRelativeIndex(Handle<Object> num,int64_t minimum,int64_t maximum)151 int64_t CapRelativeIndex(Handle<Object> num, int64_t minimum, int64_t maximum) {
152 int64_t relative;
153 if (V8_LIKELY(num->IsSmi())) {
154 relative = Smi::cast(*num)->value();
155 } else {
156 DCHECK(num->IsHeapNumber());
157 double fp = HeapNumber::cast(*num)->value();
158 if (V8_UNLIKELY(!std::isfinite(fp))) {
159 // +Infinity / -Infinity
160 DCHECK(!std::isnan(fp));
161 return fp < 0 ? minimum : maximum;
162 }
163 relative = static_cast<int64_t>(fp);
164 }
165 return relative < 0 ? std::max<int64_t>(relative + maximum, minimum)
166 : std::min<int64_t>(relative, maximum);
167 }
168
169 } // namespace
170
BUILTIN(TypedArrayPrototypeCopyWithin)171 BUILTIN(TypedArrayPrototypeCopyWithin) {
172 HandleScope scope(isolate);
173
174 Handle<JSTypedArray> array;
175 const char* method = "%TypedArray%.prototype.copyWithin";
176 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
177 isolate, array, JSTypedArray::Validate(isolate, args.receiver(), method));
178
179 if (V8_UNLIKELY(array->WasNeutered())) return *array;
180
181 int64_t len = array->length_value();
182 int64_t to = 0;
183 int64_t from = 0;
184 int64_t final = len;
185
186 if (V8_LIKELY(args.length() > 1)) {
187 Handle<Object> num;
188 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
189 isolate, num, Object::ToInteger(isolate, args.at<Object>(1)));
190 to = CapRelativeIndex(num, 0, len);
191
192 if (args.length() > 2) {
193 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
194 isolate, num, Object::ToInteger(isolate, args.at<Object>(2)));
195 from = CapRelativeIndex(num, 0, len);
196
197 Handle<Object> end = args.atOrUndefined(isolate, 3);
198 if (!end->IsUndefined(isolate)) {
199 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, num,
200 Object::ToInteger(isolate, end));
201 final = CapRelativeIndex(num, 0, len);
202 }
203 }
204 }
205
206 int64_t count = std::min<int64_t>(final - from, len - to);
207 if (count <= 0) return *array;
208
209 // TypedArray buffer may have been transferred/detached during parameter
210 // processing above. Return early in this case, to prevent potential UAF error
211 // TODO(caitp): throw here, as though the full algorithm were performed (the
212 // throw would have come from ecma262/#sec-integerindexedelementget)
213 // (see )
214 if (V8_UNLIKELY(array->WasNeutered())) return *array;
215
216 // Ensure processed indexes are within array bounds
217 DCHECK_GE(from, 0);
218 DCHECK_LT(from, len);
219 DCHECK_GE(to, 0);
220 DCHECK_LT(to, len);
221 DCHECK_GE(len - count, 0);
222
223 Handle<FixedTypedArrayBase> elements(
224 FixedTypedArrayBase::cast(array->elements()));
225 size_t element_size = array->element_size();
226 to = to * element_size;
227 from = from * element_size;
228 count = count * element_size;
229
230 uint8_t* data = static_cast<uint8_t*>(elements->DataPtr());
231 std::memmove(data + to, data + from, count);
232
233 return *array;
234 }
235
236 } // namespace internal
237 } // namespace v8
238