// Copyright 2018 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/objects/js-array-buffer.h" #include "src/objects/js-array-buffer-inl.h" #include "src/execution/protectors-inl.h" #include "src/logging/counters.h" #include "src/objects/property-descriptor.h" namespace v8 { namespace internal { namespace { bool CanonicalNumericIndexString(Isolate* isolate, Handle s, Handle* index) { DCHECK(s->IsString() || s->IsSmi()); Handle result; if (s->IsSmi()) { result = s; } else { result = String::ToNumber(isolate, Handle::cast(s)); if (!result->IsMinusZero()) { Handle str = Object::ToString(isolate, result).ToHandleChecked(); // Avoid treating strings like "2E1" and "20" as the same key. if (!str->SameValue(*s)) return false; } } *index = result; return true; } } // anonymous namespace void JSArrayBuffer::Setup(SharedFlag shared, std::shared_ptr backing_store) { clear_padding(); set_bit_field(0); set_is_shared(shared == SharedFlag::kShared); set_is_detachable(shared != SharedFlag::kShared); for (int i = 0; i < v8::ArrayBuffer::kEmbedderFieldCount; i++) { SetEmbedderField(i, Smi::zero()); } set_extension(nullptr); AllocateExternalPointerEntries(GetIsolate()); if (!backing_store) { set_backing_store(GetIsolate(), nullptr); set_byte_length(0); } else { Attach(std::move(backing_store)); } if (shared == SharedFlag::kShared) { GetIsolate()->CountUsage( v8::Isolate::UseCounterFeature::kSharedArrayBufferConstructed); } } void JSArrayBuffer::Attach(std::shared_ptr backing_store) { DCHECK_NOT_NULL(backing_store); DCHECK_EQ(is_shared(), backing_store->is_shared()); DCHECK(!was_detached()); Isolate* isolate = GetIsolate(); set_backing_store(isolate, backing_store->buffer_start()); set_byte_length(backing_store->byte_length()); if (backing_store->is_wasm_memory()) set_is_detachable(false); if (!backing_store->free_on_destruct()) set_is_external(true); Heap* heap = isolate->heap(); ArrayBufferExtension* extension = EnsureExtension(); size_t bytes = backing_store->PerIsolateAccountingLength(); extension->set_accounting_length(bytes); extension->set_backing_store(std::move(backing_store)); heap->AppendArrayBufferExtension(*this, extension); } void JSArrayBuffer::Detach(bool force_for_wasm_memory) { if (was_detached()) return; if (force_for_wasm_memory) { // Skip the is_detachable() check. } else if (!is_detachable()) { // Not detachable, do nothing. return; } Isolate* const isolate = GetIsolate(); if (backing_store()) { std::shared_ptr backing_store; backing_store = RemoveExtension(); CHECK_IMPLIES(force_for_wasm_memory, backing_store->is_wasm_memory()); } if (Protectors::IsArrayBufferDetachingIntact(isolate)) { Protectors::InvalidateArrayBufferDetaching(isolate); } DCHECK(!is_shared()); DCHECK(!is_asmjs_memory()); set_backing_store(isolate, nullptr); set_byte_length(0); set_was_detached(true); } std::shared_ptr JSArrayBuffer::GetBackingStore() { if (!extension()) return nullptr; return extension()->backing_store(); } ArrayBufferExtension* JSArrayBuffer::EnsureExtension() { ArrayBufferExtension* extension = this->extension(); if (extension != nullptr) return extension; extension = new ArrayBufferExtension(std::shared_ptr()); set_extension(extension); return extension; } std::shared_ptr JSArrayBuffer::RemoveExtension() { ArrayBufferExtension* extension = this->extension(); DCHECK_NOT_NULL(extension); auto result = extension->RemoveBackingStore(); // Remove pointer to extension such that the next GC will free it // automatically. set_extension(nullptr); return result; } void JSArrayBuffer::MarkExtension() { ArrayBufferExtension* extension = this->extension(); if (extension) { extension->Mark(); } } void JSArrayBuffer::YoungMarkExtension() { ArrayBufferExtension* extension = this->extension(); if (extension) { extension->YoungMark(); } } void JSArrayBuffer::YoungMarkExtensionPromoted() { ArrayBufferExtension* extension = this->extension(); if (extension) { extension->YoungMarkPromoted(); } } Handle JSTypedArray::GetBuffer() { Isolate* isolate = GetIsolate(); Handle self(*this, isolate); DCHECK(IsTypedArrayElementsKind(self->GetElementsKind())); Handle array_buffer(JSArrayBuffer::cast(self->buffer()), isolate); if (!is_on_heap()) { // Already is off heap, so return the existing buffer. return array_buffer; } // The existing array buffer should be empty. DCHECK_NULL(array_buffer->backing_store()); // Allocate a new backing store and attach it to the existing array buffer. size_t byte_length = self->byte_length(); auto backing_store = BackingStore::Allocate(isolate, byte_length, SharedFlag::kNotShared, InitializedFlag::kUninitialized); if (!backing_store) { isolate->heap()->FatalProcessOutOfMemory("JSTypedArray::GetBuffer"); } // Copy the elements into the backing store of the array buffer. if (byte_length > 0) { memcpy(backing_store->buffer_start(), self->DataPtr(), byte_length); } // Attach the backing store to the array buffer. array_buffer->Setup(SharedFlag::kNotShared, std::move(backing_store)); // Clear the elements of the typed array. self->set_elements(ReadOnlyRoots(isolate).empty_byte_array()); self->SetOffHeapDataPtr(isolate, array_buffer->backing_store(), 0); DCHECK(!self->is_on_heap()); return array_buffer; } // ES#sec-integer-indexed-exotic-objects-defineownproperty-p-desc // static Maybe JSTypedArray::DefineOwnProperty(Isolate* isolate, Handle o, Handle key, PropertyDescriptor* desc, Maybe should_throw) { // 1. Assert: IsPropertyKey(P) is true. DCHECK(key->IsName() || key->IsNumber()); // 2. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot. // 3. If Type(P) is String, then if (key->IsString() || key->IsSmi()) { // 3a. Let numericIndex be ! CanonicalNumericIndexString(P) // 3b. If numericIndex is not undefined, then Handle numeric_index; if (CanonicalNumericIndexString(isolate, key, &numeric_index)) { // 3b i. If IsInteger(numericIndex) is false, return false. // 3b ii. If numericIndex = -0, return false. // 3b iii. If numericIndex < 0, return false. size_t index; if (numeric_index->IsMinusZero() || !numeric_index->ToIntegerIndex(&index)) { RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw), NewTypeError(MessageTemplate::kInvalidTypedArrayIndex)); } // 3b iv. Let length be O.[[ArrayLength]]. size_t length = o->length(); // 3b v. If numericIndex ≥ length, return false. if (o->WasDetached() || index >= length) { RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw), NewTypeError(MessageTemplate::kInvalidTypedArrayIndex)); } // 3b vi. If IsAccessorDescriptor(Desc) is true, return false. if (PropertyDescriptor::IsAccessorDescriptor(desc)) { RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw), NewTypeError(MessageTemplate::kRedefineDisallowed, key)); } // 3b vii. If Desc has a [[Configurable]] field and if // Desc.[[Configurable]] is true, return false. // 3b viii. If Desc has an [[Enumerable]] field and if Desc.[[Enumerable]] // is false, return false. // 3b ix. If Desc has a [[Writable]] field and if Desc.[[Writable]] is // false, return false. if ((desc->has_configurable() && desc->configurable()) || (desc->has_enumerable() && !desc->enumerable()) || (desc->has_writable() && !desc->writable())) { RETURN_FAILURE(isolate, GetShouldThrow(isolate, should_throw), NewTypeError(MessageTemplate::kRedefineDisallowed, key)); } // 3b x. If Desc has a [[Value]] field, then // 3b x 1. Let value be Desc.[[Value]]. // 3b x 2. Return ? IntegerIndexedElementSet(O, numericIndex, value). if (desc->has_value()) { if (!desc->has_configurable()) desc->set_configurable(false); if (!desc->has_enumerable()) desc->set_enumerable(true); if (!desc->has_writable()) desc->set_writable(true); Handle value = desc->value(); LookupIterator it(isolate, o, index, LookupIterator::OWN); RETURN_ON_EXCEPTION_VALUE( isolate, DefineOwnPropertyIgnoreAttributes(&it, value, desc->ToAttributes()), Nothing()); } // 3b xi. Return true. return Just(true); } } // 4. Return ! OrdinaryDefineOwnProperty(O, P, Desc). return OrdinaryDefineOwnProperty(isolate, o, key, desc, should_throw); } ExternalArrayType JSTypedArray::type() { switch (map().elements_kind()) { #define ELEMENTS_KIND_TO_ARRAY_TYPE(Type, type, TYPE, ctype) \ case TYPE##_ELEMENTS: \ return kExternal##Type##Array; TYPED_ARRAYS(ELEMENTS_KIND_TO_ARRAY_TYPE) #undef ELEMENTS_KIND_TO_ARRAY_TYPE default: UNREACHABLE(); } } size_t JSTypedArray::element_size() { switch (map().elements_kind()) { #define ELEMENTS_KIND_TO_ELEMENT_SIZE(Type, type, TYPE, ctype) \ case TYPE##_ELEMENTS: \ return sizeof(ctype); TYPED_ARRAYS(ELEMENTS_KIND_TO_ELEMENT_SIZE) #undef ELEMENTS_KIND_TO_ELEMENT_SIZE default: UNREACHABLE(); } } } // namespace internal } // namespace v8