• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CanonicalNumericIndexString(Isolate * isolate,Handle<Object> s,Handle<Object> * index)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 
ConvertToMb(size_t size)33 inline int ConvertToMb(size_t size) {
34   return static_cast<int>(size / static_cast<size_t>(MB));
35 }
36 
37 }  // anonymous namespace
38 
Neuter()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 
StopTrackingWasmMemory(Isolate * isolate)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 
FreeBackingStoreFromMainThread()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
FreeBackingStore(Isolate * isolate,Allocation allocation)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 
set_is_wasm_memory(bool is_wasm_memory)87 void JSArrayBuffer::set_is_wasm_memory(bool is_wasm_memory) {
88   set_bit_field(IsWasmMemory::update(bit_field(), is_wasm_memory));
89 }
90 
Setup(Handle<JSArrayBuffer> array_buffer,Isolate * isolate,bool is_external,void * data,size_t byte_length,SharedFlag shared,bool is_wasm_memory)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 
SetupAllocatingData(Handle<JSArrayBuffer> array_buffer,Isolate * isolate,size_t allocated_length,bool initialize,SharedFlag shared)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 
MaterializeArrayBuffer(Handle<JSTypedArray> typed_array)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 
GetBuffer()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
DefineOwnProperty(Isolate * isolate,Handle<JSTypedArray> o,Handle<Object> key,PropertyDescriptor * desc,ShouldThrow should_throw)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 
type()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 
element_size()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