Home | History | Annotate | Download | only in objects
      1 // Copyright 2018 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/objects/js-array-buffer.h"
      6 #include "src/objects/js-array-buffer-inl.h"
      7 #include "src/property-descriptor.h"
      8 
      9 namespace v8 {
     10 namespace internal {
     11 
     12 namespace {
     13 
     14 bool CanonicalNumericIndexString(Isolate* isolate, Handle<Object> s,
     15                                  Handle<Object>* index) {
     16   DCHECK(s->IsString() || s->IsSmi());
     17 
     18   Handle<Object> result;
     19   if (s->IsSmi()) {
     20     result = s;
     21   } else {
     22     result = String::ToNumber(isolate, Handle<String>::cast(s));
     23     if (!result->IsMinusZero()) {
     24       Handle<String> str = Object::ToString(isolate, result).ToHandleChecked();
     25       // Avoid treating strings like "2E1" and "20" as the same key.
     26       if (!str->SameValue(*s)) return false;
     27     }
     28   }
     29   *index = result;
     30   return true;
     31 }
     32 
     33 inline int ConvertToMb(size_t size) {
     34   return static_cast<int>(size / static_cast<size_t>(MB));
     35 }
     36 
     37 }  // anonymous namespace
     38 
     39 void JSArrayBuffer::Neuter() {
     40   CHECK(is_neuterable());
     41   CHECK(!was_neutered());
     42   CHECK(is_external());
     43   set_backing_store(nullptr);
     44   set_byte_length(Smi::kZero);
     45   set_was_neutered(true);
     46   set_is_neuterable(false);
     47   // Invalidate the neutering protector.
     48   Isolate* const isolate = GetIsolate();
     49   if (isolate->IsArrayBufferNeuteringIntact()) {
     50     isolate->InvalidateArrayBufferNeuteringProtector();
     51   }
     52 }
     53 
     54 void JSArrayBuffer::StopTrackingWasmMemory(Isolate* isolate) {
     55   DCHECK(is_wasm_memory());
     56   isolate->wasm_engine()->memory_tracker()->ReleaseAllocation(isolate,
     57                                                               backing_store());
     58   set_is_wasm_memory(false);
     59 }
     60 
     61 void JSArrayBuffer::FreeBackingStoreFromMainThread() {
     62   if (allocation_base() == nullptr) {
     63     return;
     64   }
     65   FreeBackingStore(GetIsolate(), {allocation_base(), allocation_length(),
     66                                   backing_store(), is_wasm_memory()});
     67   // Zero out the backing store and allocation base to avoid dangling
     68   // pointers.
     69   set_backing_store(nullptr);
     70 }
     71 
     72 // static
     73 void JSArrayBuffer::FreeBackingStore(Isolate* isolate, Allocation allocation) {
     74   if (allocation.is_wasm_memory) {
     75     wasm::WasmMemoryTracker* memory_tracker =
     76         isolate->wasm_engine()->memory_tracker();
     77     if (!memory_tracker->FreeMemoryIfIsWasmMemory(isolate,
     78                                                   allocation.backing_store)) {
     79       CHECK(FreePages(allocation.allocation_base, allocation.length));
     80     }
     81   } else {
     82     isolate->array_buffer_allocator()->Free(allocation.allocation_base,
     83                                             allocation.length);
     84   }
     85 }
     86 
     87 void JSArrayBuffer::set_is_wasm_memory(bool is_wasm_memory) {
     88   set_bit_field(IsWasmMemory::update(bit_field(), is_wasm_memory));
     89 }
     90 
     91 void JSArrayBuffer::Setup(Handle<JSArrayBuffer> array_buffer, Isolate* isolate,
     92                           bool is_external, void* data, size_t byte_length,
     93                           SharedFlag shared, bool is_wasm_memory) {
     94   DCHECK_EQ(array_buffer->GetEmbedderFieldCount(),
     95             v8::ArrayBuffer::kEmbedderFieldCount);
     96   for (int i = 0; i < v8::ArrayBuffer::kEmbedderFieldCount; i++) {
     97     array_buffer->SetEmbedderField(i, Smi::kZero);
     98   }
     99   array_buffer->set_bit_field(0);
    100   array_buffer->set_is_external(is_external);
    101   array_buffer->set_is_neuterable(shared == SharedFlag::kNotShared);
    102   array_buffer->set_is_shared(shared == SharedFlag::kShared);
    103   array_buffer->set_is_wasm_memory(is_wasm_memory);
    104 
    105   Handle<Object> heap_byte_length =
    106       isolate->factory()->NewNumberFromSize(byte_length);
    107   CHECK(heap_byte_length->IsSmi() || heap_byte_length->IsHeapNumber());
    108   array_buffer->set_byte_length(*heap_byte_length);
    109   // Initialize backing store at last to avoid handling of |JSArrayBuffers| that
    110   // are currently being constructed in the |ArrayBufferTracker|. The
    111   // registration method below handles the case of registering a buffer that has
    112   // already been promoted.
    113   array_buffer->set_backing_store(data);
    114 
    115   if (data && !is_external) {
    116     isolate->heap()->RegisterNewArrayBuffer(*array_buffer);
    117   }
    118 }
    119 
    120 bool JSArrayBuffer::SetupAllocatingData(Handle<JSArrayBuffer> array_buffer,
    121                                         Isolate* isolate,
    122                                         size_t allocated_length,
    123                                         bool initialize, SharedFlag shared) {
    124   void* data;
    125   CHECK_NOT_NULL(isolate->array_buffer_allocator());
    126   if (allocated_length != 0) {
    127     if (allocated_length >= MB)
    128       isolate->counters()->array_buffer_big_allocations()->AddSample(
    129           ConvertToMb(allocated_length));
    130     if (shared == SharedFlag::kShared)
    131       isolate->counters()->shared_array_allocations()->AddSample(
    132           ConvertToMb(allocated_length));
    133     if (initialize) {
    134       data = isolate->array_buffer_allocator()->Allocate(allocated_length);
    135     } else {
    136       data = isolate->array_buffer_allocator()->AllocateUninitialized(
    137           allocated_length);
    138     }
    139     if (data == nullptr) {
    140       isolate->counters()->array_buffer_new_size_failures()->AddSample(
    141           ConvertToMb(allocated_length));
    142       return false;
    143     }
    144   } else {
    145     data = nullptr;
    146   }
    147 
    148   const bool is_external = false;
    149   JSArrayBuffer::Setup(array_buffer, isolate, is_external, data,
    150                        allocated_length, shared);
    151   return true;
    152 }
    153 
    154 Handle<JSArrayBuffer> JSTypedArray::MaterializeArrayBuffer(
    155     Handle<JSTypedArray> typed_array) {
    156   DCHECK(typed_array->is_on_heap());
    157 
    158   Isolate* isolate = typed_array->GetIsolate();
    159 
    160   DCHECK(IsFixedTypedArrayElementsKind(typed_array->GetElementsKind()));
    161 
    162   Handle<FixedTypedArrayBase> fixed_typed_array(
    163       FixedTypedArrayBase::cast(typed_array->elements()), isolate);
    164 
    165   Handle<JSArrayBuffer> buffer(JSArrayBuffer::cast(typed_array->buffer()),
    166                                isolate);
    167   // This code does not know how to materialize from wasm buffers.
    168   DCHECK(!buffer->is_wasm_memory());
    169 
    170   void* backing_store =
    171       isolate->array_buffer_allocator()->AllocateUninitialized(
    172           fixed_typed_array->DataSize());
    173   if (backing_store == nullptr) {
    174     isolate->heap()->FatalProcessOutOfMemory(
    175         "JSTypedArray::MaterializeArrayBuffer");
    176   }
    177   buffer->set_is_external(false);
    178   DCHECK(buffer->byte_length()->IsSmi() ||
    179          buffer->byte_length()->IsHeapNumber());
    180   DCHECK(NumberToInt32(buffer->byte_length()) == fixed_typed_array->DataSize());
    181   // Initialize backing store at last to avoid handling of |JSArrayBuffers| that
    182   // are currently being constructed in the |ArrayBufferTracker|. The
    183   // registration method below handles the case of registering a buffer that has
    184   // already been promoted.
    185   buffer->set_backing_store(backing_store);
    186   // RegisterNewArrayBuffer expects a valid length for adjusting counters.
    187   isolate->heap()->RegisterNewArrayBuffer(*buffer);
    188   memcpy(buffer->backing_store(), fixed_typed_array->DataPtr(),
    189          fixed_typed_array->DataSize());
    190   Handle<FixedTypedArrayBase> new_elements =
    191       isolate->factory()->NewFixedTypedArrayWithExternalPointer(
    192           fixed_typed_array->length(), typed_array->type(),
    193           static_cast<uint8_t*>(buffer->backing_store()));
    194 
    195   typed_array->set_elements(*new_elements);
    196   DCHECK(!typed_array->is_on_heap());
    197 
    198   return buffer;
    199 }
    200 
    201 Handle<JSArrayBuffer> JSTypedArray::GetBuffer() {
    202   if (!is_on_heap()) {
    203     Handle<JSArrayBuffer> array_buffer(JSArrayBuffer::cast(buffer()),
    204                                        GetIsolate());
    205     return array_buffer;
    206   }
    207   Handle<JSTypedArray> self(this, GetIsolate());
    208   return MaterializeArrayBuffer(self);
    209 }
    210 
    211 // ES#sec-integer-indexed-exotic-objects-defineownproperty-p-desc
    212 // static
    213 Maybe<bool> JSTypedArray::DefineOwnProperty(Isolate* isolate,
    214                                             Handle<JSTypedArray> o,
    215                                             Handle<Object> key,
    216                                             PropertyDescriptor* desc,
    217                                             ShouldThrow should_throw) {
    218   // 1. Assert: IsPropertyKey(P) is true.
    219   DCHECK(key->IsName() || key->IsNumber());
    220   // 2. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot.
    221   // 3. If Type(P) is String, then
    222   if (key->IsString() || key->IsSmi()) {
    223     // 3a. Let numericIndex be ! CanonicalNumericIndexString(P)
    224     // 3b. If numericIndex is not undefined, then
    225     Handle<Object> numeric_index;
    226     if (CanonicalNumericIndexString(isolate, key, &numeric_index)) {
    227       // 3b i. If IsInteger(numericIndex) is false, return false.
    228       // 3b ii. If numericIndex = -0, return false.
    229       // 3b iii. If numericIndex < 0, return false.
    230       // FIXME: the standard allows up to 2^53 elements.
    231       uint32_t index;
    232       if (numeric_index->IsMinusZero() || !numeric_index->ToUint32(&index)) {
    233         RETURN_FAILURE(isolate, should_throw,
    234                        NewTypeError(MessageTemplate::kInvalidTypedArrayIndex));
    235       }
    236       // 3b iv. Let length be O.[[ArrayLength]].
    237       uint32_t length = o->length()->Number();
    238       // 3b v. If numericIndex  length, return false.
    239       if (index >= length) {
    240         RETURN_FAILURE(isolate, should_throw,
    241                        NewTypeError(MessageTemplate::kInvalidTypedArrayIndex));
    242       }
    243       // 3b vi. If IsAccessorDescriptor(Desc) is true, return false.
    244       if (PropertyDescriptor::IsAccessorDescriptor(desc)) {
    245         RETURN_FAILURE(isolate, should_throw,
    246                        NewTypeError(MessageTemplate::kRedefineDisallowed, key));
    247       }
    248       // 3b vii. If Desc has a [[Configurable]] field and if
    249       //         Desc.[[Configurable]] is true, return false.
    250       // 3b viii. If Desc has an [[Enumerable]] field and if Desc.[[Enumerable]]
    251       //          is false, return false.
    252       // 3b ix. If Desc has a [[Writable]] field and if Desc.[[Writable]] is
    253       //        false, return false.
    254       if ((desc->has_configurable() && desc->configurable()) ||
    255           (desc->has_enumerable() && !desc->enumerable()) ||
    256           (desc->has_writable() && !desc->writable())) {
    257         RETURN_FAILURE(isolate, should_throw,
    258                        NewTypeError(MessageTemplate::kRedefineDisallowed, key));
    259       }
    260       // 3b x. If Desc has a [[Value]] field, then
    261       //   3b x 1. Let value be Desc.[[Value]].
    262       //   3b x 2. Return ? IntegerIndexedElementSet(O, numericIndex, value).
    263       if (desc->has_value()) {
    264         if (!desc->has_configurable()) desc->set_configurable(false);
    265         if (!desc->has_enumerable()) desc->set_enumerable(true);
    266         if (!desc->has_writable()) desc->set_writable(true);
    267         Handle<Object> value = desc->value();
    268         RETURN_ON_EXCEPTION_VALUE(isolate,
    269                                   SetOwnElementIgnoreAttributes(
    270                                       o, index, value, desc->ToAttributes()),
    271                                   Nothing<bool>());
    272       }
    273       // 3b xi. Return true.
    274       return Just(true);
    275     }
    276   }
    277   // 4. Return ! OrdinaryDefineOwnProperty(O, P, Desc).
    278   return OrdinaryDefineOwnProperty(isolate, o, key, desc, should_throw);
    279 }
    280 
    281 ExternalArrayType JSTypedArray::type() {
    282   switch (elements()->map()->instance_type()) {
    283 #define INSTANCE_TYPE_TO_ARRAY_TYPE(Type, type, TYPE, ctype) \
    284   case FIXED_##TYPE##_ARRAY_TYPE:                            \
    285     return kExternal##Type##Array;
    286 
    287     TYPED_ARRAYS(INSTANCE_TYPE_TO_ARRAY_TYPE)
    288 #undef INSTANCE_TYPE_TO_ARRAY_TYPE
    289 
    290     default:
    291       UNREACHABLE();
    292   }
    293 }
    294 
    295 size_t JSTypedArray::element_size() {
    296   switch (elements()->map()->instance_type()) {
    297 #define INSTANCE_TYPE_TO_ELEMENT_SIZE(Type, type, TYPE, ctype) \
    298   case FIXED_##TYPE##_ARRAY_TYPE:                              \
    299     return sizeof(ctype);
    300 
    301     TYPED_ARRAYS(INSTANCE_TYPE_TO_ELEMENT_SIZE)
    302 #undef INSTANCE_TYPE_TO_ELEMENT_SIZE
    303 
    304     default:
    305       UNREACHABLE();
    306   }
    307 }
    308 
    309 }  // namespace internal
    310 }  // namespace v8
    311