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