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));
}