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-inl.h"
      6 #include "src/builtins/builtins.h"
      7 #include "src/counters.h"
      8 #include "src/elements.h"
      9 #include "src/objects-inl.h"
     10 #include "src/objects/js-array-buffer-inl.h"
     11 
     12 namespace v8 {
     13 namespace internal {
     14 
     15 // -----------------------------------------------------------------------------
     16 // ES6 section 22.2 TypedArray Objects
     17 
     18 // ES6 section 22.2.3.1 get %TypedArray%.prototype.buffer
     19 BUILTIN(TypedArrayPrototypeBuffer) {
     20   HandleScope scope(isolate);
     21   CHECK_RECEIVER(JSTypedArray, typed_array,
     22                  "get %TypedArray%.prototype.buffer");
     23   return *typed_array->GetBuffer();
     24 }
     25 
     26 namespace {
     27 
     28 int64_t CapRelativeIndex(Handle<Object> num, int64_t minimum, int64_t maximum) {
     29   int64_t relative;
     30   if (V8_LIKELY(num->IsSmi())) {
     31     relative = Smi::ToInt(*num);
     32   } else {
     33     DCHECK(num->IsHeapNumber());
     34     double fp = HeapNumber::cast(*num)->value();
     35     if (V8_UNLIKELY(!std::isfinite(fp))) {
     36       // +Infinity / -Infinity
     37       DCHECK(!std::isnan(fp));
     38       return fp < 0 ? minimum : maximum;
     39     }
     40     relative = static_cast<int64_t>(fp);
     41   }
     42   return relative < 0 ? std::max<int64_t>(relative + maximum, minimum)
     43                       : std::min<int64_t>(relative, maximum);
     44 }
     45 
     46 }  // namespace
     47 
     48 BUILTIN(TypedArrayPrototypeCopyWithin) {
     49   HandleScope scope(isolate);
     50 
     51   Handle<JSTypedArray> array;
     52   const char* method = "%TypedArray%.prototype.copyWithin";
     53   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     54       isolate, array, JSTypedArray::Validate(isolate, args.receiver(), method));
     55 
     56   int64_t len = array->length_value();
     57   int64_t to = 0;
     58   int64_t from = 0;
     59   int64_t final = len;
     60 
     61   if (V8_LIKELY(args.length() > 1)) {
     62     Handle<Object> num;
     63     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     64         isolate, num, Object::ToInteger(isolate, args.at<Object>(1)));
     65     to = CapRelativeIndex(num, 0, len);
     66 
     67     if (args.length() > 2) {
     68       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     69           isolate, num, Object::ToInteger(isolate, args.at<Object>(2)));
     70       from = CapRelativeIndex(num, 0, len);
     71 
     72       Handle<Object> end = args.atOrUndefined(isolate, 3);
     73       if (!end->IsUndefined(isolate)) {
     74         ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, num,
     75                                            Object::ToInteger(isolate, end));
     76         final = CapRelativeIndex(num, 0, len);
     77       }
     78     }
     79   }
     80 
     81   int64_t count = std::min<int64_t>(final - from, len - to);
     82   if (count <= 0) return *array;
     83 
     84   // TypedArray buffer may have been transferred/detached during parameter
     85   // processing above. Return early in this case, to prevent potential UAF error
     86   // TODO(caitp): throw here, as though the full algorithm were performed (the
     87   // throw would have come from ecma262/#sec-integerindexedelementget)
     88   // (see )
     89   if (V8_UNLIKELY(array->WasNeutered())) return *array;
     90 
     91   // Ensure processed indexes are within array bounds
     92   DCHECK_GE(from, 0);
     93   DCHECK_LT(from, len);
     94   DCHECK_GE(to, 0);
     95   DCHECK_LT(to, len);
     96   DCHECK_GE(len - count, 0);
     97 
     98   Handle<FixedTypedArrayBase> elements(
     99       FixedTypedArrayBase::cast(array->elements()), isolate);
    100   size_t element_size = array->element_size();
    101   to = to * element_size;
    102   from = from * element_size;
    103   count = count * element_size;
    104 
    105   uint8_t* data = static_cast<uint8_t*>(elements->DataPtr());
    106   std::memmove(data + to, data + from, count);
    107 
    108   return *array;
    109 }
    110 
    111 BUILTIN(TypedArrayPrototypeFill) {
    112   HandleScope scope(isolate);
    113 
    114   Handle<JSTypedArray> array;
    115   const char* method = "%TypedArray%.prototype.fill";
    116   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    117       isolate, array, JSTypedArray::Validate(isolate, args.receiver(), method));
    118   ElementsKind kind = array->GetElementsKind();
    119 
    120   Handle<Object> obj_value = args.atOrUndefined(isolate, 1);
    121   if (kind == BIGINT64_ELEMENTS || kind == BIGUINT64_ELEMENTS) {
    122     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, obj_value,
    123                                        BigInt::FromObject(isolate, obj_value));
    124   } else {
    125     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, obj_value,
    126                                        Object::ToNumber(isolate, obj_value));
    127   }
    128 
    129   int64_t len = array->length_value();
    130   int64_t start = 0;
    131   int64_t end = len;
    132 
    133   if (args.length() > 2) {
    134     Handle<Object> num = args.atOrUndefined(isolate, 2);
    135     if (!num->IsUndefined(isolate)) {
    136       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    137           isolate, num, Object::ToInteger(isolate, num));
    138       start = CapRelativeIndex(num, 0, len);
    139 
    140       num = args.atOrUndefined(isolate, 3);
    141       if (!num->IsUndefined(isolate)) {
    142         ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    143             isolate, num, Object::ToInteger(isolate, num));
    144         end = CapRelativeIndex(num, 0, len);
    145       }
    146     }
    147   }
    148 
    149   int64_t count = end - start;
    150   if (count <= 0) return *array;
    151 
    152   if (V8_UNLIKELY(array->WasNeutered())) return *array;
    153 
    154   // Ensure processed indexes are within array bounds
    155   DCHECK_GE(start, 0);
    156   DCHECK_LT(start, len);
    157   DCHECK_GE(end, 0);
    158   DCHECK_LE(end, len);
    159   DCHECK_LE(count, len);
    160 
    161   return ElementsAccessor::ForKind(kind)->Fill(array, obj_value,
    162                                                static_cast<uint32_t>(start),
    163                                                static_cast<uint32_t>(end));
    164 }
    165 
    166 BUILTIN(TypedArrayPrototypeIncludes) {
    167   HandleScope scope(isolate);
    168 
    169   Handle<JSTypedArray> array;
    170   const char* method = "%TypedArray%.prototype.includes";
    171   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    172       isolate, array, JSTypedArray::Validate(isolate, args.receiver(), method));
    173 
    174   if (args.length() < 2) return ReadOnlyRoots(isolate).false_value();
    175 
    176   int64_t len = array->length_value();
    177   if (len == 0) return ReadOnlyRoots(isolate).false_value();
    178 
    179   int64_t index = 0;
    180   if (args.length() > 2) {
    181     Handle<Object> num;
    182     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    183         isolate, num, Object::ToInteger(isolate, args.at<Object>(2)));
    184     index = CapRelativeIndex(num, 0, len);
    185   }
    186 
    187   // TODO(cwhan.tunz): throw. See the above comment in CopyWithin.
    188   if (V8_UNLIKELY(array->WasNeutered()))
    189     return ReadOnlyRoots(isolate).false_value();
    190 
    191   Handle<Object> search_element = args.atOrUndefined(isolate, 1);
    192   ElementsAccessor* elements = array->GetElementsAccessor();
    193   Maybe<bool> result = elements->IncludesValue(isolate, array, search_element,
    194                                                static_cast<uint32_t>(index),
    195                                                static_cast<uint32_t>(len));
    196   MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
    197   return *isolate->factory()->ToBoolean(result.FromJust());
    198 }
    199 
    200 BUILTIN(TypedArrayPrototypeIndexOf) {
    201   HandleScope scope(isolate);
    202 
    203   Handle<JSTypedArray> array;
    204   const char* method = "%TypedArray%.prototype.indexOf";
    205   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    206       isolate, array, JSTypedArray::Validate(isolate, args.receiver(), method));
    207 
    208   int64_t len = array->length_value();
    209   if (len == 0) return Smi::FromInt(-1);
    210 
    211   int64_t index = 0;
    212   if (args.length() > 2) {
    213     Handle<Object> num;
    214     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    215         isolate, num, Object::ToInteger(isolate, args.at<Object>(2)));
    216     index = CapRelativeIndex(num, 0, len);
    217   }
    218 
    219   // TODO(cwhan.tunz): throw. See the above comment in CopyWithin.
    220   if (V8_UNLIKELY(array->WasNeutered())) return Smi::FromInt(-1);
    221 
    222   Handle<Object> search_element = args.atOrUndefined(isolate, 1);
    223   ElementsAccessor* elements = array->GetElementsAccessor();
    224   Maybe<int64_t> result = elements->IndexOfValue(isolate, array, search_element,
    225                                                  static_cast<uint32_t>(index),
    226                                                  static_cast<uint32_t>(len));
    227   MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
    228   return *isolate->factory()->NewNumberFromInt64(result.FromJust());
    229 }
    230 
    231 BUILTIN(TypedArrayPrototypeLastIndexOf) {
    232   HandleScope scope(isolate);
    233 
    234   Handle<JSTypedArray> array;
    235   const char* method = "%TypedArray%.prototype.lastIndexOf";
    236   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    237       isolate, array, JSTypedArray::Validate(isolate, args.receiver(), method));
    238 
    239   int64_t len = array->length_value();
    240   if (len == 0) return Smi::FromInt(-1);
    241 
    242   int64_t index = len - 1;
    243   if (args.length() > 2) {
    244     Handle<Object> num;
    245     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    246         isolate, num, Object::ToInteger(isolate, args.at<Object>(2)));
    247     // Set a negative value (-1) for returning -1 if num is negative and
    248     // len + num is still negative. Upper bound is len - 1.
    249     index = std::min<int64_t>(CapRelativeIndex(num, -1, len), len - 1);
    250   }
    251 
    252   if (index < 0) return Smi::FromInt(-1);
    253 
    254   // TODO(cwhan.tunz): throw. See the above comment in CopyWithin.
    255   if (V8_UNLIKELY(array->WasNeutered())) return Smi::FromInt(-1);
    256 
    257   Handle<Object> search_element = args.atOrUndefined(isolate, 1);
    258   ElementsAccessor* elements = array->GetElementsAccessor();
    259   Maybe<int64_t> result = elements->LastIndexOfValue(
    260       array, search_element, static_cast<uint32_t>(index));
    261   MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
    262   return *isolate->factory()->NewNumberFromInt64(result.FromJust());
    263 }
    264 
    265 BUILTIN(TypedArrayPrototypeReverse) {
    266   HandleScope scope(isolate);
    267 
    268   Handle<JSTypedArray> array;
    269   const char* method = "%TypedArray%.prototype.reverse";
    270   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    271       isolate, array, JSTypedArray::Validate(isolate, args.receiver(), method));
    272 
    273   ElementsAccessor* elements = array->GetElementsAccessor();
    274   elements->Reverse(*array);
    275   return *array;
    276 }
    277 
    278 }  // namespace internal
    279 }  // namespace v8
    280