https://github.com/WebKit/WebKit/blob/ab10a90523e06df54bbb8a98e1aed913f79d0af9/Source/JavaScriptCore/runtime/JSArrayBufferView.cpp#L269 Skip to content Toggle navigation Sign up * Product + Actions Automate any workflow + Packages Host and manage packages + Security Find and fix vulnerabilities + Codespaces Instant dev environments + Copilot Write better code with AI + Code review Manage code changes + Issues Plan and track work + Discussions Collaborate outside of code Explore + All features + Documentation + GitHub Skills + Blog * Solutions For + Enterprise + Teams + Startups + Education By Solution + CI/CD & Automation + DevOps + DevSecOps Case Studies + Customer Stories + Resources * Open Source + GitHub Sponsors Fund open source developers + The ReadME Project GitHub community articles Repositories + Topics + Trending + Collections * Pricing [ ] * # In this repository All GitHub | Jump to | * No suggested jump to results * # In this repository All GitHub | Jump to | * # In this organization All GitHub | Jump to | * # In this repository All GitHub | Jump to | Sign in Sign up You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session. {{ message }} WebKit / WebKit Public * Notifications * Fork 968 * Star 6.4k * Code * Pull requests 570 * Actions * Wiki * Security * Insights More * Code * Pull requests * Actions * Wiki * Security * Insights Permalink ab10a90523 Switch branches/tags [ ] Branches Tags Could not load branches Nothing to show {{ refName }} default View all branches Could not load tags Nothing to show {{ refName }} default View all tags Name already in use A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch? Cancel Create WebKit/Source/JavaScriptCore/runtime/JSArrayBufferView.cpp Go to file * Go to file T * Go to line L * * Copy path * Copy permalink This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. @Constellation Constellation Serialize and deserialize resizable ArrayBuffer ... Latest commit a9510ae Nov 24, 2022 History https://bugs.webkit.org/show_bug.cgi?id=248209 rdar://102601423 Reviewed by Ross Kirsling. This patch adds serializing and deserializing of resizable ArrayBuffer and TypedArrays. We add ResizableArrayBufferTag and add a feature serializing resizable ArrayBuffer. But for growable SharedArrayBuffer, nothing is necessary since information is carried via SharedArrayBufferContents already. For TypedArrays, we use UINT64_MAX byteLength marker as a auto-length case. This works since byteLength cannot be UINT64_MAX since it exceeds MAX_ARRAY_BUFFER_SIZE. The other things in TypedArrays are not changed much since these TypedArrays should be resizable / growable ones when the subsequent backing serialized ArrayBuffer is resizable. We also add wrappedAs methods since normal tryCreate has more additional checks for construction. But these checks can fail if the serialized TypedArrays are having ArrayBuffers which is resized to be smaller after the construction. But this is OK since it just makes TypedArrays OOB. wrappedAs methods do not have this check. * Source/JavaScriptCore/runtime/ArrayBufferView.h: (JSC::ArrayBufferView::byteOffsetRaw const): (JSC::ArrayBufferView::byteOffset const): (JSC::ArrayBufferView::byteLengthRaw const): (JSC::ArrayBufferView::byteLength const): * Source/WebCore/bindings/js/SerializedScriptValue.cpp: (WebCore::CloneSerializer::dumpArrayBufferView): (WebCore::CloneSerializer::dumpIfTerminal): (WebCore::CloneDeserializer::readResizableNonSharedArrayBuffer): (WebCore::CloneDeserializer::readArrayBufferViewImpl): (WebCore::CloneDeserializer::readTerminal): Canonical link: https://commits.webkit.org/256998@main 8 contributors Users who have contributed to this file @Constellation @kmiller68 @webkit-commit-queue @shvaikalesh @cdumez @rkirsling @geoffreygaren @mikhailramalho 433 lines (380 sloc) 15.9 KB Raw Blame Edit this file E Open in GitHub Desktop * * View raw * * View blame This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters Show hidden characters /* * Copyright (C) 2013-2021 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "JSArrayBufferView.h" #include "GenericTypedArrayViewInlines.h" #include "JSCInlines.h" #include "JSGenericTypedArrayViewInlines.h" #include "JSTypedArrays.h" #include "TypedArrayController.h" #include "TypedArrays.h" #include namespace JSC { const ClassInfo JSArrayBufferView::s_info = { "ArrayBufferView"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSArrayBufferView) }; const ASCIILiteral typedArrayBufferHasBeenDetachedErrorMessage { " Underlying ArrayBuffer has been detached from the view or out-of-bounds"_s }; JSArrayBufferView::ConstructionContext::ConstructionContext (Structure* structure, size_t length, void* vector) : m_structure(structure) , m_vector(vector, length) , m_length(length) , m_byteOffset(0) , m_mode(FastTypedArray) , m_butterfly(nullptr) { ASSERT(!isResizableOrGrowableSharedTypedArrayIncludingDataView (structure->classInfoForCells())); ASSERT(!Gigacage::isEnabled() || (Gigacage::contains(vector) && Gigacage::contains(static_cast(vector) + length - 1 ))); ASSERT(vector == removeArrayPtrTag(vector)); RELEASE_ASSERT(length <= fastSizeLimit); } JSArrayBufferView::ConstructionContext::ConstructionContext(VM& vm, Structure* structure, size_t length, unsigned elementSize, InitializationMode mode) : m_structure(nullptr) , m_length(length) , m_byteOffset(0) , m_butterfly(nullptr) { ASSERT(!isResizableOrGrowableSharedTypedArrayIncludingDataView (structure->classInfoForCells())); if (length <= fastSizeLimit) { // Attempt GC allocation. void* temp; size_t size = sizeOf(length, elementSize); temp = vm.primitiveGigacageAuxiliarySpace().allocate(vm, size, nullptr, AllocationFailureMode::ReturnNull); if (!temp) return; m_structure = structure; m_vector = VectorType(temp, m_length); m_mode = FastTypedArray; if (mode == ZeroFill) { uint64_t* asWords = static_cast(vector()); for (unsigned i = size / sizeof(uint64_t); i--;) asWords[i] = 0; } return; } CheckedSize size = length; size *= elementSize; if (size.hasOverflowed() || size > MAX_ARRAY_BUFFER_SIZE) return; m_vector = VectorType(Gigacage::tryMalloc(Gigacage::Primitive, size. value()), m_length); if (!m_vector) return; if (mode == ZeroFill) memset(vector(), 0, size); vm.heap.reportExtraMemoryAllocated(size.value()); m_structure = structure; m_mode = OversizeTypedArray; } JSArrayBufferView::ConstructionContext::ConstructionContext(VM& vm, Structure* structure, RefPtr&& arrayBuffer, size_t byteOffset, std::optional length) : m_structure(structure) , m_length(length.value_or(0)) , m_byteOffset(byteOffset) , m_mode(WastefulTypedArray) { if (!arrayBuffer->isResizableOrGrowableShared()) m_mode = WastefulTypedArray; else { if (arrayBuffer->isGrowableShared()) m_mode = length ? GrowableSharedWastefulTypedArray : GrowableSharedAutoLengthWastefulTypedArray; else m_mode = length ? ResizableNonSharedWastefulTypedArray : ResizableNonSharedAutoLengthWastefulTypedArray; } #if ASSERT_ENABLED if (!length) ASSERT(arrayBuffer->isResizableOrGrowableShared()); if (JSC::isResizableOrGrowableShared(m_mode)) ASSERT(isResizableOrGrowableSharedTypedArrayIncludingDataView (structure->classInfoForCells())); else ASSERT(!isResizableOrGrowableSharedTypedArrayIncludingDataView (structure->classInfoForCells())); #endif ASSERT(arrayBuffer->data() == removeArrayPtrTag(arrayBuffer->data ())); m_vector = VectorType(static_cast(arrayBuffer->data()) + byteOffset, m_length); IndexingHeader indexingHeader; indexingHeader.setArrayBuffer(arrayBuffer.get()); m_butterfly = Butterfly::create(vm, nullptr, 0, 0, true, indexingHeader, 0); } JSArrayBufferView::ConstructionContext::ConstructionContext (Structure* structure, RefPtr&& arrayBuffer, size_t byteOffset, std::optional length, DataViewTag) : m_structure(structure) , m_length(length.value_or(0)) , m_byteOffset(byteOffset) , m_mode(DataViewMode) , m_butterfly(nullptr) { if (!arrayBuffer->isResizableOrGrowableShared()) m_mode = DataViewMode; else { if (arrayBuffer->isGrowableShared()) m_mode = length ? GrowableSharedDataViewMode : GrowableSharedAutoLengthDataViewMode; else m_mode = length ? ResizableNonSharedDataViewMode : ResizableNonSharedAutoLengthDataViewMode; } #if ASSERT_ENABLED if (!length) ASSERT(arrayBuffer->isResizableOrGrowableShared()); if (JSC::isResizableOrGrowableShared(m_mode)) ASSERT(isResizableOrGrowableSharedTypedArrayIncludingDataView (structure->classInfoForCells())); else ASSERT(!isResizableOrGrowableSharedTypedArrayIncludingDataView (structure->classInfoForCells())); #endif ASSERT(arrayBuffer->data() == removeArrayPtrTag(arrayBuffer->data ())); m_vector = VectorType(static_cast(arrayBuffer->data()) + byteOffset, m_length); } JSArrayBufferView::JSArrayBufferView(VM& vm, ConstructionContext& context) : Base(vm, context.structure(), nullptr) , m_length(context.length()) , m_byteOffset(context.byteOffset()) , m_mode(context.mode()) { setButterfly(vm, context.butterfly()); ASSERT(context.vector() == removeArrayPtrTag(context.vector())); m_vector.setWithoutBarrier(context.vector(), m_length); } void JSArrayBufferView::finishCreation(VM& vm) { Base::finishCreation(vm); ASSERT(jsDynamicCast(this)); switch (m_mode) { case FastTypedArray: return; case OversizeTypedArray: vm.heap.addFinalizer(this, finalize); return; case WastefulTypedArray: case ResizableNonSharedWastefulTypedArray: case ResizableNonSharedAutoLengthWastefulTypedArray: case GrowableSharedWastefulTypedArray: case GrowableSharedAutoLengthWastefulTypedArray: vm.heap.addReference(this, butterfly()->indexingHeader()-> arrayBuffer()); return; case DataViewMode: case ResizableNonSharedDataViewMode: case ResizableNonSharedAutoLengthDataViewMode: case GrowableSharedDataViewMode: case GrowableSharedAutoLengthDataViewMode: ASSERT(!butterfly()); vm.heap.addReference(this, jsCast(this)-> possiblySharedBuffer()); return; } RELEASE_ASSERT_NOT_REACHED(); } template void JSArrayBufferView::visitChildrenImpl(JSCell* cell, Visitor& visitor) { JSArrayBufferView* thisObject = jsCast(cell); ASSERT_GC_OBJECT_INHERITS(thisObject, info()); Base::visitChildren(cell, visitor); if (thisObject->hasArrayBuffer()) { WTF::loadLoadFence(); ArrayBuffer* buffer = thisObject->possiblySharedBuffer(); RELEASE_ASSERT(buffer); visitor.addOpaqueRoot(buffer); } } DEFINE_VISIT_CHILDREN(JSArrayBufferView); ArrayBuffer* JSArrayBufferView::unsharedBuffer() { ArrayBuffer* result = possiblySharedBuffer(); RELEASE_ASSERT(!result || !result->isShared()); return result; } void JSArrayBufferView::finalize(JSCell* cell) { JSArrayBufferView* thisObject = static_cast (cell); // This JSArrayBufferView could be an OversizeTypedArray that was converted // to a WastefulTypedArray via slowDownAndWasteMemory(). Hence, it is possible // to get to this finalizer and found the mode to be WastefulTypedArray. ASSERT(thisObject->m_mode == OversizeTypedArray || thisObject-> hasArrayBuffer()); if (thisObject->m_mode == OversizeTypedArray) Gigacage::free(Gigacage::Primitive, thisObject->vector()); } JSArrayBuffer* JSArrayBufferView::unsharedJSBuffer(JSGlobalObject* globalObject) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); if (ArrayBuffer* buffer = unsharedBuffer()) return vm.m_typedArrayController->toJS(globalObject, this-> globalObject(), buffer); scope.throwException(globalObject, createOutOfMemoryError (globalObject)); return nullptr; } JSArrayBuffer* JSArrayBufferView::possiblySharedJSBuffer (JSGlobalObject* globalObject) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); if (ArrayBuffer* buffer = possiblySharedBuffer()) return vm.m_typedArrayController->toJS(globalObject, this-> globalObject(), buffer); scope.throwException(globalObject, createOutOfMemoryError (globalObject)); return nullptr; } void JSArrayBufferView::detach() { Locker locker { cellLock() }; RELEASE_ASSERT(hasArrayBuffer()); RELEASE_ASSERT(!isShared()); m_length = 0; m_byteOffset = 0; m_vector.clear(); } ArrayBuffer* JSArrayBufferView::slowDownAndWasteMemory() { ASSERT(!hasArrayBuffer()); // We play this game because we want this to be callable even from places that // don't have access to CallFrame* or the VM, and we only allocate so little // memory here that it's not necessary to trigger a GC - just accounting what // we have done is good enough. The sort of bizarre exception to the "allocating // little memory" is when we transfer a backing buffer into the C heap; this // will temporarily get counted towards heap footprint (incorrectly, in the case // of adopting an oversize typed array) but we don't GC here anyway. That's // almost certainly fine. The worst case is if you created a ton of fast typed // arrays, and did nothing but caused all of them to slow down and waste memory. // In that case, your memory footprint will double before the GC realizes what's // up. But if you do *anything* to trigger a GC watermark check, it will know // that you *had* done those allocations and it will GC appropriately. Heap* heap = Heap::heap(this); VM& vm = heap->vm(); DeferGCForAWhile deferGC(vm); RELEASE_ASSERT(!hasIndexingHeader()); Structure* structure = this->structure(); RefPtr buffer; size_t byteLength = this->byteLength(); switch (m_mode) { case FastTypedArray: { buffer = ArrayBuffer::tryCreate(vector(), byteLength); if (!buffer) return nullptr; break; } case OversizeTypedArray: { // FIXME: consider doing something like "subtracting" from extra memory // cost, since right now this case will cause the GC to think that we reallocated // the whole buffer. buffer = ArrayBuffer::createAdopted(vector(), byteLength); break; } default: RELEASE_ASSERT_NOT_REACHED(); break; } RELEASE_ASSERT(buffer); // Don't create bufferfly until we know we have an ArrayBuffer. setButterfly(vm, Butterfly::createOrGrowArrayRight( butterfly(), vm, this, structure, structure->outOfLineCapacity(), false, 0, 0)); { Locker locker { cellLock() }; butterfly()->indexingHeader()->setArrayBuffer(buffer.get()); m_vector.setWithoutBarrier(buffer->data(), m_length); WTF::storeStoreFence(); m_mode = WastefulTypedArray; // There is no possibility that FastTypedArray or OversizeTypedArray becomes resizable ones since resizable ones do not start with FastTypedArray or OversizeTypedArray. } heap->addReference(this, buffer.get()); return buffer.get(); } // Allocates the full-on native buffer and moves data into the C heap if // necessary. Note that this never allocates in the GC heap. RefPtr JSArrayBufferView::possiblySharedImpl() { ArrayBuffer* buffer = possiblySharedBuffer(); if (!buffer) return nullptr; size_t byteOffset = this->byteOffsetRaw(); size_t length = this->lengthRaw(); switch (type()) { #define FACTORY(type) \ case type ## ArrayType: \ return type ## Array::wrappedAs(*buffer, byteOffset, isAutoLength() ? std::nullopt : std::optional { length }); FOR_EACH_TYPED_ARRAY_TYPE_EXCLUDING_DATA_VIEW(FACTORY) #undef FACTORY case DataViewType: return DataView::wrappedAs(*buffer, byteOffset, isAutoLength() ? std::nullopt : std::optional { length }); default: RELEASE_ASSERT_NOT_REACHED(); return nullptr; } } bool JSArrayBufferView::isIteratorProtocolFastAndNonObservable() { // Excluding DataView. if (!isTypedArrayType(type())) return false; JSGlobalObject* globalObject = this->globalObject(); TypedArrayType typedArrayType = JSC::typedArrayType(type()); if (!globalObject-> isTypedArrayPrototypeIteratorProtocolFastAndNonObservable (typedArrayType)) return false; VM& vm = globalObject->vm(); Structure* structure = this->structure(); // This is the fast case. Many TypedArrays will be an original typed array structure. if (globalObject->isOriginalTypedArrayStructure(structure, true) || globalObject->isOriginalTypedArrayStructure(structure, false)) return true; if (getPrototypeDirect() != globalObject->typedArrayPrototype (typedArrayType)) return false; if (getDirectOffset(vm, vm.propertyNames->iteratorSymbol) != invalidOffset) return false; return true; } } // namespace JSC namespace WTF { using namespace JSC; void printInternal(PrintStream& out, TypedArrayMode mode) { switch (mode) { case FastTypedArray: out.print("FastTypedArray"); return; case OversizeTypedArray: out.print("OversizeTypedArray"); return; case WastefulTypedArray: out.print("WastefulTypedArray"); return; case ResizableNonSharedWastefulTypedArray: out.print("ResizableNonSharedWastefulTypedArray"); return; case ResizableNonSharedAutoLengthWastefulTypedArray: out.print("ResizableNonSharedAutoLengthWastefulTypedArray"); return; case GrowableSharedWastefulTypedArray: out.print("GrowableSharedWastefulTypedArray"); return; case GrowableSharedAutoLengthWastefulTypedArray: out.print("GrowableSharedAutoLengthWastefulTypedArray"); return; case DataViewMode: out.print("DataViewMode"); return; case ResizableNonSharedDataViewMode: out.print("ResizableNonSharedDataViewMode"); return; case ResizableNonSharedAutoLengthDataViewMode: out.print("ResizableNonSharedAutoLengthDataViewMode"); return; case GrowableSharedDataViewMode: out.print("GrowableSharedDataViewMode"); return; case GrowableSharedAutoLengthDataViewMode: out.print("GrowableSharedAutoLengthDataViewMode"); return; } RELEASE_ASSERT_NOT_REACHED(); } } // namespace WTF * Copy lines * Copy permalink * View git blame [ ] Go Footer (c) 2023 GitHub, Inc. Footer navigation * Terms * Privacy * Security * Status * Docs * Contact GitHub * Pricing * API * Training * Blog * About You can't perform that action at this time.