• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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