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