Home | History | Annotate | Download | only in builtins
      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:
     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
     30 BUILTIN(TypedArrayPrototypeBuffer) {
     31   HandleScope scope(isolate);
     32   CHECK_RECEIVER(JSTypedArray, typed_array, "get TypedArray.prototype.buffer");
     33   return *typed_array->GetBuffer();
     34 }
     35 
     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
     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
     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
     84 TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) {
     85   GenerateTypedArrayPrototypeGetter("get TypedArray.prototype.length",
     86                                     JSTypedArray::kLengthOffset);
     87 }
     88 
     89 template <IterationKind kIterationKind>
     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 
    134 TF_BUILTIN(TypedArrayPrototypeValues, TypedArrayBuiltinsAssembler) {
    135   GenerateTypedArrayPrototypeIterationMethod<IterationKind::kValues>(
    136       "%TypedArray%.prototype.values()");
    137 }
    138 
    139 TF_BUILTIN(TypedArrayPrototypeEntries, TypedArrayBuiltinsAssembler) {
    140   GenerateTypedArrayPrototypeIterationMethod<IterationKind::kEntries>(
    141       "%TypedArray%.prototype.entries()");
    142 }
    143 
    144 TF_BUILTIN(TypedArrayPrototypeKeys, TypedArrayBuiltinsAssembler) {
    145   GenerateTypedArrayPrototypeIterationMethod<IterationKind::kKeys>(
    146       "%TypedArray%.prototype.keys()");
    147 }
    148 
    149 namespace {
    150 
    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 
    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