static inline JSC::EncodedJSValue createJSBufferFromJS()

in src/bun.js/bindings/JSBuffer.cpp [2299:2456]


static inline JSC::EncodedJSValue createJSBufferFromJS(JSC::JSGlobalObject* lexicalGlobalObject, JSValue newTarget, ArgList args)
{
    VM& vm = lexicalGlobalObject->vm();
    auto throwScope = DECLARE_THROW_SCOPE(vm);
    size_t argsCount = args.size();
    if (argsCount == 0) {
        RELEASE_AND_RETURN(throwScope, constructBufferEmpty(lexicalGlobalObject));
    }
    JSValue distinguishingArg = args.at(0);
    JSValue encodingArg = argsCount > 1 ? args.at(1) : JSValue();
    auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);

    if (distinguishingArg.isAnyInt()) {
        throwScope.release();
        return JSBuffer__bufferFromLength(lexicalGlobalObject, distinguishingArg.asAnyInt());
    } else if (distinguishingArg.isCell()) {
        auto type = distinguishingArg.asCell()->type();

        switch (type) {
        case StringType:
        case StringObjectType:
        case DerivedStringObjectType: {
            throwScope.release();
            return constructBufferFromStringAndEncoding(lexicalGlobalObject, distinguishingArg, encodingArg);
        }

        case Uint16ArrayType:
        case Uint32ArrayType:
        case Int8ArrayType:
        case Int16ArrayType:
        case Int32ArrayType:
        case Float16ArrayType:
        case Float32ArrayType:
        case Float64ArrayType:
        case BigInt64ArrayType:
        case BigUint64ArrayType: {
            // byteOffset and byteLength are ignored in this case, which is consitent with Node.js and new Uint8Array()
            JSC::JSArrayBufferView* view = jsCast<JSC::JSArrayBufferView*>(distinguishingArg.asCell());

            void* data = view->vector();
            size_t byteLength = view->length();

            if (UNLIKELY(!data)) {
                throwException(globalObject, throwScope, createRangeError(globalObject, "Buffer is detached"_s));
                return JSValue::encode({});
            }

            auto* uint8Array = createUninitializedBuffer(lexicalGlobalObject, byteLength);
            if (UNLIKELY(!uint8Array)) {
                ASSERT(throwScope.exception());
                return JSValue::encode({});
            }

            if (byteLength) {
                uint8Array->setFromTypedArray(lexicalGlobalObject, 0, view, 0, byteLength, CopyType::LeftToRight);
            }

            RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(uint8Array));
            break;
        }

        case DataViewType:
        case Uint8ArrayType:
        case Uint8ClampedArrayType: {
            // byteOffset and byteLength are ignored in this case, which is consitent with Node.js and new Uint8Array()
            JSC::JSArrayBufferView* view = jsCast<JSC::JSArrayBufferView*>(distinguishingArg.asCell());

            void* data = view->vector();
            size_t byteLength = view->byteLength();

            if (UNLIKELY(!data)) {
                throwException(globalObject, throwScope, createRangeError(globalObject, "Buffer is detached"_s));
                return JSValue::encode({});
            }

            auto* uint8Array = createBuffer(lexicalGlobalObject, static_cast<uint8_t*>(data), byteLength);

            RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(uint8Array));
        }
        case ArrayBufferType: {
            // This closely matches `new Uint8Array(buffer, byteOffset, length)` in JavaScriptCore's implementation.
            // See Source/JavaScriptCore/runtime/JSGenericTypedArrayViewConstructorInlines.h
            size_t offset = 0;
            std::optional<size_t> length;
            if (argsCount > 1) {

                offset = args.at(1).toTypedArrayIndex(globalObject, "byteOffset"_s);

                // TOOD: return Node.js error
                RETURN_IF_EXCEPTION(throwScope, encodedJSValue());

                if (argsCount > 2) {
                    // If the length value is present but undefined, treat it as missing.
                    JSValue lengthValue = args.at(2);
                    if (!lengthValue.isUndefined()) {
                        length = lengthValue.toTypedArrayIndex(globalObject, "length"_s);

                        // TOOD: return Node.js error
                        RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
                    }
                }
            }

            auto* jsBuffer = jsCast<JSC::JSArrayBuffer*>(distinguishingArg.asCell());
            RefPtr<ArrayBuffer> buffer = jsBuffer->impl();
            if (buffer->isDetached()) {
                // TOOD: return Node.js error
                throwTypeError(globalObject, throwScope, "Buffer is detached"_s);
                return JSValue::encode({});
            }

            if (!length) {
                size_t byteLength = buffer->byteLength();
                if (buffer->isResizableOrGrowableShared()) {
                    if (UNLIKELY(offset > byteLength)) {
                        // TOOD: return Node.js error
                        throwNodeRangeError(globalObject, throwScope, "byteOffset exceeds source ArrayBuffer byteLength"_s);
                        return JSValue::encode({});
                    }
                } else {
                    length = (byteLength - offset);
                }
            }

            auto* subclassStructure = globalObject->JSBufferSubclassStructure();
            auto* uint8Array = JSC::JSUint8Array::create(lexicalGlobalObject, subclassStructure, WTFMove(buffer), offset, length);
            if (UNLIKELY(!uint8Array)) {
                throwOutOfMemoryError(globalObject, throwScope);
                return JSC::JSValue::encode({});
            }

            RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(uint8Array));
        }
        default: {
            break;
        }
        }
    }

    JSC::JSObject* constructor = lexicalGlobalObject->m_typedArrayUint8.constructor(lexicalGlobalObject);

    MarkedArgumentBuffer argsBuffer;
    argsBuffer.append(distinguishingArg);
    for (size_t i = 1; i < argsCount; ++i)
        argsBuffer.append(args.at(i));

    JSValue target = newTarget;
    if (!target || !target.isCell()) {
        target = globalObject->JSBufferConstructor();
    }

    JSC::JSObject* object = JSC::construct(lexicalGlobalObject, constructor, target, args, "Buffer failed to construct"_s);
    if (!object) {
        return JSC::JSValue::encode({});
    }

    RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(object));
}