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 6842 : if (s->IsSmi()) {
22 : result = s;
23 : } else {
24 3403 : result = String::ToNumber(isolate, Handle<String>::cast(s));
25 6806 : 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 6410 : return static_cast<int>(size / static_cast<size_t>(MB));
37 : }
38 :
39 : } // anonymous namespace
40 :
41 5160 : void JSArrayBuffer::Detach() {
42 5160 : CHECK(is_detachable());
43 5160 : CHECK(!was_detached());
44 5160 : 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 5160 : if (isolate->IsArrayBufferDetachingIntact()) {
52 434 : isolate->InvalidateArrayBufferDetachingProtector();
53 : }
54 5160 : }
55 :
56 0 : void JSArrayBuffer::FreeBackingStoreFromMainThread() {
57 0 : if (allocation_base() == nullptr) {
58 0 : return;
59 : }
60 : 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 624847 : void JSArrayBuffer::FreeBackingStore(Isolate* isolate, Allocation allocation) {
69 400653 : if (allocation.is_wasm_memory) {
70 : wasm::WasmMemoryTracker* memory_tracker =
71 176459 : isolate->wasm_engine()->memory_tracker();
72 176468 : if (!memory_tracker->FreeMemoryIfIsWasmMemory(isolate,
73 176459 : allocation.backing_store)) {
74 0 : CHECK(FreePages(GetPlatformPageAllocator(), allocation.allocation_base,
75 : allocation.length));
76 : }
77 : } else {
78 : isolate->array_buffer_allocator()->Free(allocation.allocation_base,
79 224194 : allocation.length);
80 : }
81 400664 : }
82 :
83 405804 : 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 1217417 : for (int i = 0; i < v8::ArrayBuffer::kEmbedderFieldCount; i++) {
90 811612 : array_buffer->SetEmbedderField(i, Smi::kZero);
91 : }
92 : array_buffer->set_byte_length(byte_length);
93 : array_buffer->set_bit_field(0);
94 811613 : array_buffer->clear_padding();
95 : array_buffer->set_is_external(is_external);
96 405807 : array_buffer->set_is_detachable(shared_flag == SharedFlag::kNotShared);
97 405807 : 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 405805 : if (data && !is_external) {
106 395078 : isolate->heap()->RegisterNewArrayBuffer(*array_buffer);
107 : }
108 405807 : }
109 :
110 0 : void JSArrayBuffer::SetupAsEmpty(Handle<JSArrayBuffer> array_buffer,
111 : Isolate* isolate) {
112 45 : Setup(array_buffer, isolate, false, nullptr, 0, SharedFlag::kNotShared);
113 0 : }
114 :
115 223052 : bool JSArrayBuffer::SetupAllocatingData(Handle<JSArrayBuffer> array_buffer,
116 436066 : Isolate* isolate,
117 : size_t allocated_length,
118 : bool initialize,
119 : SharedFlag shared_flag) {
120 : void* data;
121 223052 : CHECK_NOT_NULL(isolate->array_buffer_allocator());
122 223052 : if (allocated_length != 0) {
123 213012 : if (allocated_length >= MB)
124 : isolate->counters()->array_buffer_big_allocations()->AddSample(
125 350 : ConvertToMb(allocated_length));
126 213014 : if (shared_flag == SharedFlag::kShared)
127 : isolate->counters()->shared_array_allocations()->AddSample(
128 6015 : ConvertToMb(allocated_length));
129 213014 : if (initialize) {
130 211900 : data = isolate->array_buffer_allocator()->Allocate(allocated_length);
131 : } else {
132 : data = isolate->array_buffer_allocator()->AllocateUninitialized(
133 1114 : allocated_length);
134 : }
135 213013 : if (data == nullptr) {
136 : isolate->counters()->array_buffer_new_size_failures()->AddSample(
137 45 : ConvertToMb(allocated_length));
138 : SetupAsEmpty(array_buffer, isolate);
139 45 : 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 223008 : allocated_length, shared_flag);
148 223009 : return true;
149 : }
150 :
151 10868 : Handle<JSArrayBuffer> JSTypedArray::MaterializeArrayBuffer(
152 : Handle<JSTypedArray> typed_array) {
153 : DCHECK(typed_array->is_on_heap());
154 :
155 10868 : Isolate* isolate = typed_array->GetIsolate();
156 :
157 : DCHECK(IsFixedTypedArrayElementsKind(typed_array->GetElementsKind()));
158 :
159 : Handle<FixedTypedArrayBase> fixed_typed_array(
160 21736 : 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 21736 : fixed_typed_array->DataSize());
170 10868 : if (backing_store == nullptr) {
171 : isolate->heap()->FatalProcessOutOfMemory(
172 0 : "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 10868 : isolate->heap()->RegisterNewArrayBuffer(*buffer);
184 : memcpy(buffer->backing_store(), fixed_typed_array->DataPtr(),
185 21736 : fixed_typed_array->DataSize());
186 : Handle<FixedTypedArrayBase> new_elements =
187 : isolate->factory()->NewFixedTypedArrayWithExternalPointer(
188 : fixed_typed_array->length(), typed_array->type(),
189 21736 : static_cast<uint8_t*>(buffer->backing_store()));
190 :
191 21736 : typed_array->set_elements(*new_elements);
192 : DCHECK(!typed_array->is_on_heap());
193 :
194 10868 : return buffer;
195 : }
196 :
197 40404249 : Handle<JSArrayBuffer> JSTypedArray::GetBuffer() {
198 40404249 : if (!is_on_heap()) {
199 : Handle<JSArrayBuffer> array_buffer(JSArrayBuffer::cast(buffer()),
200 : GetIsolate());
201 40393355 : return array_buffer;
202 : }
203 : Handle<JSTypedArray> self(*this, GetIsolate());
204 10868 : return MaterializeArrayBuffer(self);
205 : }
206 :
207 : // ES#sec-integer-indexed-exotic-objects-defineownproperty-p-desc
208 : // static
209 3610 : Maybe<bool> JSTypedArray::DefineOwnProperty(Isolate* isolate,
210 : Handle<JSTypedArray> o,
211 : Handle<Object> key,
212 : PropertyDescriptor* desc,
213 : Maybe<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 7634 : 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 3421 : 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 639 : if (numeric_index->IsMinusZero() || !numeric_index->ToUint32(&index)) {
229 81 : RETURN_FAILURE(isolate, GetShouldThrow(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 189 : if (o->WasDetached() || index >= length) {
236 27 : RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
237 : NewTypeError(MessageTemplate::kInvalidTypedArrayIndex));
238 : }
239 : // 3b vi. If IsAccessorDescriptor(Desc) is true, return false.
240 180 : if (PropertyDescriptor::IsAccessorDescriptor(desc)) {
241 513 : RETURN_FAILURE(isolate, GetShouldThrow(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 18 : if ((desc->has_configurable() && desc->configurable()) ||
251 18 : (desc->has_enumerable() && !desc->enumerable()) ||
252 9 : (desc->has_writable() && !desc->writable())) {
253 27 : RETURN_FAILURE(isolate, GetShouldThrow(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 0 : if (desc->has_value()) {
260 0 : if (!desc->has_configurable()) desc->set_configurable(false);
261 0 : if (!desc->has_enumerable()) desc->set_enumerable(true);
262 0 : if (!desc->has_writable()) desc->set_writable(true);
263 0 : Handle<Object> value = desc->value();
264 0 : 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 3394 : return OrdinaryDefineOwnProperty(isolate, o, key, desc, should_throw);
275 : }
276 :
277 20212566 : ExternalArrayType JSTypedArray::type() {
278 40425131 : 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 1668 : TYPED_ARRAYS(INSTANCE_TYPE_TO_ARRAY_TYPE)
284 : #undef INSTANCE_TYPE_TO_ARRAY_TYPE
285 :
286 : default:
287 0 : UNREACHABLE();
288 : }
289 : }
290 :
291 7800 : size_t JSTypedArray::element_size() {
292 15600 : 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 693 : TYPED_ARRAYS(INSTANCE_TYPE_TO_ELEMENT_SIZE)
298 : #undef INSTANCE_TYPE_TO_ELEMENT_SIZE
299 :
300 : default:
301 0 : UNREACHABLE();
302 : }
303 : }
304 :
305 : } // namespace internal
306 178779 : } // namespace v8
|