• 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 
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