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/execution/protectors-inl.h"
9 #include "src/logging/counters.h"
10 #include "src/objects/property-descriptor.h"
11
12 namespace v8 {
13 namespace internal {
14
15 namespace {
16
CanonicalNumericIndexString(Isolate * isolate,Handle<Object> s,Handle<Object> * index)17 bool CanonicalNumericIndexString(Isolate* isolate, Handle<Object> s,
18 Handle<Object>* index) {
19 DCHECK(s->IsString() || s->IsSmi());
20
21 Handle<Object> result;
22 if (s->IsSmi()) {
23 result = s;
24 } else {
25 result = String::ToNumber(isolate, Handle<String>::cast(s));
26 if (!result->IsMinusZero()) {
27 Handle<String> str = Object::ToString(isolate, result).ToHandleChecked();
28 // Avoid treating strings like "2E1" and "20" as the same key.
29 if (!str->SameValue(*s)) return false;
30 }
31 }
32 *index = result;
33 return true;
34 }
35 } // anonymous namespace
36
Setup(SharedFlag shared,std::shared_ptr<BackingStore> backing_store)37 void JSArrayBuffer::Setup(SharedFlag shared,
38 std::shared_ptr<BackingStore> backing_store) {
39 clear_padding();
40 set_bit_field(0);
41 set_is_shared(shared == SharedFlag::kShared);
42 set_is_detachable(shared != SharedFlag::kShared);
43 for (int i = 0; i < v8::ArrayBuffer::kEmbedderFieldCount; i++) {
44 SetEmbedderField(i, Smi::zero());
45 }
46 set_extension(nullptr);
47 AllocateExternalPointerEntries(GetIsolate());
48 if (!backing_store) {
49 set_backing_store(GetIsolate(), nullptr);
50 set_byte_length(0);
51 } else {
52 Attach(std::move(backing_store));
53 }
54 if (shared == SharedFlag::kShared) {
55 GetIsolate()->CountUsage(
56 v8::Isolate::UseCounterFeature::kSharedArrayBufferConstructed);
57 }
58 }
59
Attach(std::shared_ptr<BackingStore> backing_store)60 void JSArrayBuffer::Attach(std::shared_ptr<BackingStore> backing_store) {
61 DCHECK_NOT_NULL(backing_store);
62 DCHECK_EQ(is_shared(), backing_store->is_shared());
63 DCHECK(!was_detached());
64 Isolate* isolate = GetIsolate();
65 set_backing_store(isolate, backing_store->buffer_start());
66 set_byte_length(backing_store->byte_length());
67 if (backing_store->is_wasm_memory()) set_is_detachable(false);
68 if (!backing_store->free_on_destruct()) set_is_external(true);
69 Heap* heap = isolate->heap();
70 ArrayBufferExtension* extension = EnsureExtension();
71 size_t bytes = backing_store->PerIsolateAccountingLength();
72 extension->set_accounting_length(bytes);
73 extension->set_backing_store(std::move(backing_store));
74 heap->AppendArrayBufferExtension(*this, extension);
75 }
76
Detach(bool force_for_wasm_memory)77 void JSArrayBuffer::Detach(bool force_for_wasm_memory) {
78 if (was_detached()) return;
79
80 if (force_for_wasm_memory) {
81 // Skip the is_detachable() check.
82 } else if (!is_detachable()) {
83 // Not detachable, do nothing.
84 return;
85 }
86
87 Isolate* const isolate = GetIsolate();
88 if (backing_store()) {
89 std::shared_ptr<BackingStore> backing_store;
90 backing_store = RemoveExtension();
91 CHECK_IMPLIES(force_for_wasm_memory, backing_store->is_wasm_memory());
92 }
93
94 if (Protectors::IsArrayBufferDetachingIntact(isolate)) {
95 Protectors::InvalidateArrayBufferDetaching(isolate);
96 }
97
98 DCHECK(!is_shared());
99 DCHECK(!is_asmjs_memory());
100 set_backing_store(isolate, nullptr);
101 set_byte_length(0);
102 set_was_detached(true);
103 }
104
GetBackingStore()105 std::shared_ptr<BackingStore> JSArrayBuffer::GetBackingStore() {
106 if (!extension()) return nullptr;
107 return extension()->backing_store();
108 }
109
EnsureExtension()110 ArrayBufferExtension* JSArrayBuffer::EnsureExtension() {
111 ArrayBufferExtension* extension = this->extension();
112 if (extension != nullptr) return extension;
113
114 extension = new ArrayBufferExtension(std::shared_ptr<BackingStore>());
115 set_extension(extension);
116 return extension;
117 }
118
RemoveExtension()119 std::shared_ptr<BackingStore> JSArrayBuffer::RemoveExtension() {
120 ArrayBufferExtension* extension = this->extension();
121 DCHECK_NOT_NULL(extension);
122 auto result = extension->RemoveBackingStore();
123 // Remove pointer to extension such that the next GC will free it
124 // automatically.
125 set_extension(nullptr);
126 return result;
127 }
128
MarkExtension()129 void JSArrayBuffer::MarkExtension() {
130 ArrayBufferExtension* extension = this->extension();
131 if (extension) {
132 extension->Mark();
133 }
134 }
135
YoungMarkExtension()136 void JSArrayBuffer::YoungMarkExtension() {
137 ArrayBufferExtension* extension = this->extension();
138 if (extension) {
139 extension->YoungMark();
140 }
141 }
142
YoungMarkExtensionPromoted()143 void JSArrayBuffer::YoungMarkExtensionPromoted() {
144 ArrayBufferExtension* extension = this->extension();
145 if (extension) {
146 extension->YoungMarkPromoted();
147 }
148 }
149
GetBuffer()150 Handle<JSArrayBuffer> JSTypedArray::GetBuffer() {
151 Isolate* isolate = GetIsolate();
152 Handle<JSTypedArray> self(*this, isolate);
153 DCHECK(IsTypedArrayElementsKind(self->GetElementsKind()));
154
155 Handle<JSArrayBuffer> array_buffer(JSArrayBuffer::cast(self->buffer()),
156 isolate);
157 if (!is_on_heap()) {
158 // Already is off heap, so return the existing buffer.
159 return array_buffer;
160 }
161
162 // The existing array buffer should be empty.
163 DCHECK_NULL(array_buffer->backing_store());
164
165 // Allocate a new backing store and attach it to the existing array buffer.
166 size_t byte_length = self->byte_length();
167 auto backing_store =
168 BackingStore::Allocate(isolate, byte_length, SharedFlag::kNotShared,
169 InitializedFlag::kUninitialized);
170
171 if (!backing_store) {
172 isolate->heap()->FatalProcessOutOfMemory("JSTypedArray::GetBuffer");
173 }
174
175 // Copy the elements into the backing store of the array buffer.
176 if (byte_length > 0) {
177 memcpy(backing_store->buffer_start(), self->DataPtr(), byte_length);
178 }
179
180 // Attach the backing store to the array buffer.
181 array_buffer->Setup(SharedFlag::kNotShared, std::move(backing_store));
182
183 // Clear the elements of the typed array.
184 self->set_elements(ReadOnlyRoots(isolate).empty_byte_array());
185 self->SetOffHeapDataPtr(isolate, array_buffer->backing_store(), 0);
186 DCHECK(!self->is_on_heap());
187
188 return array_buffer;
189 }
190
191 // ES#sec-integer-indexed-exotic-objects-defineownproperty-p-desc
192 // static
DefineOwnProperty(Isolate * isolate,Handle<JSTypedArray> o,Handle<Object> key,PropertyDescriptor * desc,Maybe<ShouldThrow> should_throw)193 Maybe<bool> JSTypedArray::DefineOwnProperty(Isolate* isolate,
194 Handle<JSTypedArray> o,
195 Handle<Object> key,
196 PropertyDescriptor* desc,
197 Maybe<ShouldThrow> should_throw) {
198 // 1. Assert: IsPropertyKey(P) is true.
199 DCHECK(key->IsName() || key->IsNumber());
200 // 2. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot.
201 // 3. If Type(P) is String, then
202 if (key->IsString() || key->IsSmi()) {
203 // 3a. Let numericIndex be ! CanonicalNumericIndexString(P)
204 // 3b. If numericIndex is not undefined, then
205 Handle<Object> numeric_index;
206 if (CanonicalNumericIndexString(isolate, key, &numeric_index)) {
207 // 3b i. If IsInteger(numericIndex) is false, return false.
208 // 3b ii. If numericIndex = -0, return false.
209 // 3b iii. If numericIndex < 0, return false.
210 size_t index;
211 if (numeric_index->IsMinusZero() ||
212 !numeric_index->ToIntegerIndex(&index)) {
213 RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
214 NewTypeError(MessageTemplate::kInvalidTypedArrayIndex));
215 }
216 // 3b iv. Let length be O.[[ArrayLength]].
217 size_t length = o->length();
218 // 3b v. If numericIndex ≥ length, return false.
219 if (o->WasDetached() || index >= length) {
220 RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
221 NewTypeError(MessageTemplate::kInvalidTypedArrayIndex));
222 }
223 // 3b vi. If IsAccessorDescriptor(Desc) is true, return false.
224 if (PropertyDescriptor::IsAccessorDescriptor(desc)) {
225 RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
226 NewTypeError(MessageTemplate::kRedefineDisallowed, key));
227 }
228 // 3b vii. If Desc has a [[Configurable]] field and if
229 // Desc.[[Configurable]] is true, return false.
230 // 3b viii. If Desc has an [[Enumerable]] field and if Desc.[[Enumerable]]
231 // is false, return false.
232 // 3b ix. If Desc has a [[Writable]] field and if Desc.[[Writable]] is
233 // false, return false.
234 if ((desc->has_configurable() && desc->configurable()) ||
235 (desc->has_enumerable() && !desc->enumerable()) ||
236 (desc->has_writable() && !desc->writable())) {
237 RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw),
238 NewTypeError(MessageTemplate::kRedefineDisallowed, key));
239 }
240 // 3b x. If Desc has a [[Value]] field, then
241 // 3b x 1. Let value be Desc.[[Value]].
242 // 3b x 2. Return ? IntegerIndexedElementSet(O, numericIndex, value).
243 if (desc->has_value()) {
244 if (!desc->has_configurable()) desc->set_configurable(false);
245 if (!desc->has_enumerable()) desc->set_enumerable(true);
246 if (!desc->has_writable()) desc->set_writable(true);
247 Handle<Object> value = desc->value();
248 LookupIterator it(isolate, o, index, LookupIterator::OWN);
249 RETURN_ON_EXCEPTION_VALUE(
250 isolate,
251 DefineOwnPropertyIgnoreAttributes(&it, value, desc->ToAttributes()),
252 Nothing<bool>());
253 }
254 // 3b xi. Return true.
255 return Just(true);
256 }
257 }
258 // 4. Return ! OrdinaryDefineOwnProperty(O, P, Desc).
259 return OrdinaryDefineOwnProperty(isolate, o, key, desc, should_throw);
260 }
261
type()262 ExternalArrayType JSTypedArray::type() {
263 switch (map().elements_kind()) {
264 #define ELEMENTS_KIND_TO_ARRAY_TYPE(Type, type, TYPE, ctype) \
265 case TYPE##_ELEMENTS: \
266 return kExternal##Type##Array;
267
268 TYPED_ARRAYS(ELEMENTS_KIND_TO_ARRAY_TYPE)
269 #undef ELEMENTS_KIND_TO_ARRAY_TYPE
270
271 default:
272 UNREACHABLE();
273 }
274 }
275
element_size()276 size_t JSTypedArray::element_size() {
277 switch (map().elements_kind()) {
278 #define ELEMENTS_KIND_TO_ELEMENT_SIZE(Type, type, TYPE, ctype) \
279 case TYPE##_ELEMENTS: \
280 return sizeof(ctype);
281
282 TYPED_ARRAYS(ELEMENTS_KIND_TO_ELEMENT_SIZE)
283 #undef ELEMENTS_KIND_TO_ELEMENT_SIZE
284
285 default:
286 UNREACHABLE();
287 }
288 }
289
290 } // namespace internal
291 } // namespace v8
292