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