mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
js: fix Buffer constructor and Buffer.from (#16731)
This commit is contained in:
@@ -39,24 +39,24 @@ bench("Buffer.from(ArrayBuffer(100))", () => {
|
||||
return Buffer.from(hundred);
|
||||
});
|
||||
|
||||
bench("new Buffer(ArrayBuffer(100))", () => {
|
||||
return new Buffer(hundred);
|
||||
});
|
||||
|
||||
var hundredArray = new Uint8Array(100);
|
||||
bench("Buffer.from(Uint8Array(100))", () => {
|
||||
return Buffer.from(hundredArray);
|
||||
});
|
||||
|
||||
bench("new Buffer(Uint8Array(100))", () => {
|
||||
return new Buffer(hundredArray);
|
||||
});
|
||||
|
||||
var empty = new Uint8Array(0);
|
||||
bench("Buffer.from(Uint8Array(0))", () => {
|
||||
return Buffer.from(empty);
|
||||
});
|
||||
|
||||
bench("new Buffer(ArrayBuffer(100))", () => {
|
||||
return new Buffer(hundred);
|
||||
});
|
||||
|
||||
bench("new Buffer(Uint8Array(100))", () => {
|
||||
return new Buffer(hundredArray);
|
||||
});
|
||||
|
||||
bench("new Buffer(Uint8Array(0))", () => {
|
||||
return new Buffer(empty);
|
||||
});
|
||||
|
||||
@@ -618,8 +618,12 @@ JSC::EncodedJSValue STRING_TOO_LONG(JSC::ThrowScope& throwScope, JSC::JSGlobalOb
|
||||
return {};
|
||||
}
|
||||
|
||||
JSC::EncodedJSValue BUFFER_OUT_OF_BOUNDS(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject)
|
||||
JSC::EncodedJSValue BUFFER_OUT_OF_BOUNDS(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, ASCIILiteral name)
|
||||
{
|
||||
if (!name.isEmpty()) {
|
||||
throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_BUFFER_OUT_OF_BOUNDS, makeString("\""_s, name, "\" is outside of buffer bounds"_s)));
|
||||
return {};
|
||||
}
|
||||
throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_BUFFER_OUT_OF_BOUNDS, "Attempt to access memory outside buffer bounds"_s));
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ JSC::EncodedJSValue INVALID_ARG_VALUE(JSC::ThrowScope& throwScope, JSC::JSGlobal
|
||||
JSC::EncodedJSValue UNKNOWN_ENCODING(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const WTF::StringView encoding);
|
||||
JSC::EncodedJSValue INVALID_STATE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, const WTF::String& statemsg);
|
||||
JSC::EncodedJSValue STRING_TOO_LONG(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject);
|
||||
JSC::EncodedJSValue BUFFER_OUT_OF_BOUNDS(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject);
|
||||
JSC::EncodedJSValue BUFFER_OUT_OF_BOUNDS(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, ASCIILiteral name);
|
||||
JSC::EncodedJSValue UNKNOWN_SIGNAL(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue signal, bool triedUppercase = false);
|
||||
JSC::EncodedJSValue SOCKET_BAD_PORT(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue name, JSC::JSValue port, bool allowZero);
|
||||
JSC::EncodedJSValue UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject);
|
||||
|
||||
@@ -116,7 +116,7 @@ static JSC_DECLARE_HOST_FUNCTION(jsBufferPrototypeFunction_write);
|
||||
extern "C" EncodedJSValue WebCore_BufferEncodingType_toJS(JSC::JSGlobalObject* lexicalGlobalObject, WebCore::BufferEncodingType encoding)
|
||||
{
|
||||
// clang-format off
|
||||
auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);
|
||||
switch (encoding) {
|
||||
case WebCore::BufferEncodingType::utf8: return JSC::JSValue::encode(globalObject->commonStrings().utf8String(globalObject));
|
||||
case WebCore::BufferEncodingType::ucs2: return JSC::JSValue::encode(globalObject->commonStrings().ucs2String(globalObject));
|
||||
@@ -218,7 +218,7 @@ static JSUint8Array* allocBuffer(JSC::JSGlobalObject* lexicalGlobalObject, size_
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
#endif
|
||||
|
||||
auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);
|
||||
auto* subclassStructure = globalObject->JSBufferSubclassStructure();
|
||||
|
||||
auto* uint8Array = JSC::JSUint8Array::create(lexicalGlobalObject, subclassStructure, byteLength);
|
||||
@@ -320,7 +320,7 @@ JSC::EncodedJSValue JSBuffer__bufferFromPointerAndLengthAndDeinit(JSC::JSGlobalO
|
||||
|
||||
JSC::JSUint8Array* uint8Array = nullptr;
|
||||
|
||||
auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);
|
||||
auto* subclassStructure = globalObject->JSBufferSubclassStructure();
|
||||
|
||||
if (LIKELY(length > 0)) {
|
||||
@@ -417,7 +417,7 @@ JSC::JSUint8Array* createEmptyBuffer(JSC::JSGlobalObject* lexicalGlobalObject)
|
||||
|
||||
JSC::JSUint8Array* createUninitializedBuffer(JSC::JSGlobalObject* lexicalGlobalObject, size_t length)
|
||||
{
|
||||
auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);
|
||||
auto* subclassStructure = globalObject->JSBufferSubclassStructure();
|
||||
|
||||
return JSC::JSUint8Array::createUninitialized(lexicalGlobalObject, subclassStructure, length);
|
||||
@@ -436,7 +436,7 @@ static inline JSC::JSUint8Array* JSBuffer__bufferFromLengthAsArray(JSC::JSGlobal
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);
|
||||
auto* subclassStructure = globalObject->JSBufferSubclassStructure();
|
||||
JSC::JSUint8Array* uint8Array = JSC::JSUint8Array::create(lexicalGlobalObject, subclassStructure, static_cast<size_t>(length));
|
||||
|
||||
@@ -2412,6 +2412,60 @@ JSC::JSObject* createBufferConstructor(JSC::VM& vm, JSC::JSGlobalObject* globalO
|
||||
|
||||
} // namespace WebCore
|
||||
|
||||
EncodedJSValue constructBufferFromArray(JSC::ThrowScope& throwScope, JSGlobalObject* lexicalGlobalObject, JSValue arrayValue)
|
||||
{
|
||||
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);
|
||||
|
||||
auto* constructor = lexicalGlobalObject->m_typedArrayUint8.constructor(lexicalGlobalObject);
|
||||
MarkedArgumentBuffer argsBuffer;
|
||||
argsBuffer.append(arrayValue);
|
||||
JSValue target = globalObject->JSBufferConstructor();
|
||||
// TODO: I wish we could avoid this - it adds ~30ns of overhead just using JSC::construct.
|
||||
auto* object = JSC::construct(lexicalGlobalObject, constructor, target, argsBuffer, "Buffer failed to construct"_s);
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(object));
|
||||
}
|
||||
|
||||
EncodedJSValue constructBufferFromArrayBuffer(JSC::ThrowScope& throwScope, JSGlobalObject* lexicalGlobalObject, size_t argsCount, JSValue arrayBufferValue, JSValue offsetValue, JSValue lengthValue)
|
||||
{
|
||||
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);
|
||||
|
||||
auto* jsBuffer = jsCast<JSC::JSArrayBuffer*>(arrayBufferValue.asCell());
|
||||
RefPtr<ArrayBuffer> buffer = jsBuffer->impl();
|
||||
if (buffer->isDetached()) {
|
||||
return throwVMTypeError(globalObject, throwScope, "Buffer is detached"_s);
|
||||
}
|
||||
size_t byteLength = buffer->byteLength();
|
||||
size_t offset = 0;
|
||||
size_t length = byteLength;
|
||||
|
||||
if (!offsetValue.isUndefined()) {
|
||||
double offsetD = offsetValue.toNumber(lexicalGlobalObject);
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
if (std::isnan(offsetD)) offsetD = 0;
|
||||
offset = offsetD;
|
||||
if (offset > byteLength) return Bun::ERR::BUFFER_OUT_OF_BOUNDS(throwScope, lexicalGlobalObject, "offset"_s);
|
||||
length -= offset;
|
||||
}
|
||||
|
||||
if (!lengthValue.isUndefined()) {
|
||||
double lengthD = lengthValue.toNumber(lexicalGlobalObject);
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
if (std::isnan(lengthD)) lengthD = 0;
|
||||
length = lengthD;
|
||||
if (length > byteLength - offset) return Bun::ERR::BUFFER_OUT_OF_BOUNDS(throwScope, lexicalGlobalObject, "length"_s);
|
||||
}
|
||||
|
||||
auto* subclassStructure = globalObject->JSBufferSubclassStructure();
|
||||
auto* uint8Array = JSC::JSUint8Array::create(lexicalGlobalObject, subclassStructure, WTFMove(buffer), offset, length);
|
||||
if (UNLIKELY(!uint8Array)) {
|
||||
throwOutOfMemoryError(globalObject, throwScope);
|
||||
return {};
|
||||
}
|
||||
|
||||
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(uint8Array));
|
||||
}
|
||||
|
||||
static inline JSC::EncodedJSValue createJSBufferFromJS(JSC::JSGlobalObject* lexicalGlobalObject, JSValue newTarget, ArgList args)
|
||||
{
|
||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||
@@ -2423,28 +2477,27 @@ static inline JSC::EncodedJSValue createJSBufferFromJS(JSC::JSGlobalObject* lexi
|
||||
}
|
||||
JSValue distinguishingArg = args.at(0);
|
||||
JSValue encodingArg = argsCount > 1 ? args.at(1) : JSValue();
|
||||
auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
|
||||
|
||||
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);
|
||||
if (distinguishingArg.isAnyInt()) {
|
||||
throwScope.release();
|
||||
if (args.at(1).isString()) {
|
||||
return Bun::ERR::INVALID_ARG_TYPE(throwScope, lexicalGlobalObject, "string"_s, "string"_s, distinguishingArg);
|
||||
}
|
||||
return JSBuffer__bufferFromLength(lexicalGlobalObject, distinguishingArg.asAnyInt());
|
||||
auto anyint = distinguishingArg.asAnyInt();
|
||||
if (anyint < 0 or anyint > Bun::Buffer::kMaxLength) return Bun::ERR::OUT_OF_RANGE(throwScope, lexicalGlobalObject, "size"_s, 0, Bun::Buffer::kMaxLength, distinguishingArg);
|
||||
return JSValue::encode(allocBuffer(lexicalGlobalObject, anyint));
|
||||
} else if (distinguishingArg.isNumber()) {
|
||||
JSValue lengthValue = distinguishingArg;
|
||||
Bun::V::validateNumber(throwScope, lexicalGlobalObject, lengthValue, "size"_s, jsNumber(0), jsNumber(Bun::Buffer::kMaxLength));
|
||||
RETURN_IF_EXCEPTION(throwScope, {});
|
||||
size_t length = lengthValue.toLength(lexicalGlobalObject);
|
||||
return JSBuffer__bufferFromLength(lexicalGlobalObject, length);
|
||||
return JSValue::encode(allocBuffer(lexicalGlobalObject, length));
|
||||
} else if (distinguishingArg.isUndefinedOrNull() || distinguishingArg.isBoolean()) {
|
||||
auto arg_string = distinguishingArg.toWTFString(globalObject);
|
||||
auto message = makeString("The first argument must be of type string or an instance of Buffer, ArrayBuffer, Array or an Array-like object. Received "_s, arg_string);
|
||||
throwTypeError(lexicalGlobalObject, throwScope, message);
|
||||
return {};
|
||||
return throwVMTypeError(globalObject, throwScope, message);
|
||||
} else if (distinguishingArg.isCell()) {
|
||||
auto type = distinguishingArg.asCell()->type();
|
||||
|
||||
switch (type) {
|
||||
case StringType:
|
||||
case StringObjectType:
|
||||
@@ -2452,7 +2505,6 @@ static inline JSC::EncodedJSValue createJSBufferFromJS(JSC::JSGlobalObject* lexi
|
||||
throwScope.release();
|
||||
return constructBufferFromStringAndEncoding(lexicalGlobalObject, distinguishingArg, encodingArg);
|
||||
}
|
||||
|
||||
case Uint16ArrayType:
|
||||
case Uint32ArrayType:
|
||||
case Int8ArrayType:
|
||||
@@ -2465,100 +2517,41 @@ static inline JSC::EncodedJSValue createJSBufferFromJS(JSC::JSGlobalObject* lexi
|
||||
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 {};
|
||||
}
|
||||
|
||||
auto* uint8Array = createUninitializedBuffer(lexicalGlobalObject, byteLength);
|
||||
if (UNLIKELY(!uint8Array)) {
|
||||
ASSERT(throwScope.exception());
|
||||
return {};
|
||||
}
|
||||
|
||||
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 {};
|
||||
}
|
||||
|
||||
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, {});
|
||||
|
||||
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, {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {};
|
||||
}
|
||||
|
||||
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 {};
|
||||
}
|
||||
} 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));
|
||||
return constructBufferFromArrayBuffer(throwScope, lexicalGlobalObject, args.size(), distinguishingArg, args.at(1), args.at(2));
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
@@ -2566,26 +2559,8 @@ static inline JSC::EncodedJSValue createJSBufferFromJS(JSC::JSGlobalObject* lexi
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
return constructBufferFromArray(throwScope, lexicalGlobalObject, distinguishingArg);
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(callJSBuffer, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
|
||||
{
|
||||
return createJSBufferFromJS(lexicalGlobalObject, callFrame->thisValue(), ArgList(callFrame));
|
||||
|
||||
@@ -254,6 +254,7 @@ JSC_DEFINE_CUSTOM_SETTER(jsBunConfigVerboseFetchSetter, (JSGlobalObject * global
|
||||
return true;
|
||||
}
|
||||
|
||||
#if OS(WINDOWS)
|
||||
extern "C" void Bun__Process__editWindowsEnvVar(BunString, BunString);
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(jsEditWindowsEnvVar, (JSGlobalObject * global, JSC::CallFrame* callFrame))
|
||||
@@ -274,6 +275,7 @@ JSC_DEFINE_HOST_FUNCTION(jsEditWindowsEnvVar, (JSGlobalObject * global, JSC::Cal
|
||||
}
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(jsUndefined()));
|
||||
}
|
||||
#endif
|
||||
|
||||
JSValue createEnvironmentVariablesMap(Zig::GlobalObject* globalObject)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
export default [
|
||||
// class list for $inherits*() builtins, eg. $inheritsBlob()
|
||||
// tests if a value is an instanceof a native class in a robust cross-realm manner
|
||||
// source-of-truth impl in src/codegen/generate-classes.ts
|
||||
// result in build/debug/codegen/ZigGeneratedClasses.cpp
|
||||
["Blob"],
|
||||
["ReadableStream", "JSReadableStream.h"],
|
||||
["WritableStream", "JSWritableStream.h"],
|
||||
["TransformStream", "JSTransformStream.h"],
|
||||
["ArrayBuffer"],
|
||||
];
|
||||
|
||||
@@ -1,78 +1,43 @@
|
||||
// This is marked as a constructor because Node.js allows `new Buffer.from`,
|
||||
// Some legacy dependencies depend on this, see #3638
|
||||
$constructor;
|
||||
export function from(items) {
|
||||
if ($isUndefinedOrNull(items)) {
|
||||
throw new TypeError(
|
||||
"The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object.",
|
||||
);
|
||||
}
|
||||
export function from(value, encodingOrOffset, length) {
|
||||
if (typeof value === "string") return new $Buffer(value, encodingOrOffset);
|
||||
|
||||
// TODO: figure out why private symbol not found
|
||||
if (
|
||||
typeof items === "string" ||
|
||||
(typeof items === "object" &&
|
||||
($isTypedArrayView(items) ||
|
||||
items instanceof ArrayBuffer ||
|
||||
items instanceof SharedArrayBuffer ||
|
||||
items instanceof String))
|
||||
) {
|
||||
switch ($argumentCount()) {
|
||||
case 1: {
|
||||
return new $Buffer(items);
|
||||
}
|
||||
case 2: {
|
||||
return new $Buffer(items, $argument(1));
|
||||
}
|
||||
default: {
|
||||
return new $Buffer(items, $argument(1), $argument(2));
|
||||
}
|
||||
if (typeof value === "object" && value !== null) {
|
||||
if ($inheritsArrayBuffer(value)) return new $Buffer(value, encodingOrOffset, length);
|
||||
if ($isTypedArrayView(value)) return new $Buffer(value, encodingOrOffset, length);
|
||||
|
||||
const valueOf = value.valueOf && value.valueOf();
|
||||
if (valueOf != null && valueOf !== value && (typeof valueOf === "string" || typeof valueOf === "object")) {
|
||||
return Buffer.from(valueOf, encodingOrOffset, length);
|
||||
}
|
||||
}
|
||||
if (typeof items === "object") {
|
||||
const data = items.data;
|
||||
if (items.type === "Buffer" && Array.isArray(data)) {
|
||||
|
||||
if (value.length !== undefined || $inheritsArrayBuffer(value.buffer)) {
|
||||
if (typeof value.length !== "number") return new $Buffer(0);
|
||||
if (value.length <= 0) return new $Buffer(0);
|
||||
return new $Buffer(value);
|
||||
}
|
||||
const { type, data } = value;
|
||||
if (type === "Buffer" && $isArray(data)) {
|
||||
if (data.length <= 0) return new $Buffer(0);
|
||||
return new $Buffer(data);
|
||||
}
|
||||
}
|
||||
|
||||
var arrayLike = $toObject(
|
||||
items,
|
||||
"The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object.",
|
||||
) as ArrayLike<any>;
|
||||
|
||||
if (!$isJSArray(arrayLike)) {
|
||||
const toPrimitive = $tryGetByIdWithWellKnownSymbol(items, "toPrimitive");
|
||||
|
||||
if (toPrimitive) {
|
||||
const primitive = toPrimitive.$call(items, "string");
|
||||
|
||||
const toPrimitive = $tryGetByIdWithWellKnownSymbol(value, "toPrimitive");
|
||||
if (typeof toPrimitive === "function") {
|
||||
const primitive = toPrimitive.$call(value, "string");
|
||||
if (typeof primitive === "string") {
|
||||
switch ($argumentCount()) {
|
||||
case 1: {
|
||||
return new $Buffer(primitive);
|
||||
}
|
||||
case 2: {
|
||||
return new $Buffer(primitive, $argument(1));
|
||||
}
|
||||
default: {
|
||||
return new $Buffer(primitive, $argument(1), $argument(2));
|
||||
}
|
||||
}
|
||||
return new $Buffer(primitive, encodingOrOffset);
|
||||
}
|
||||
}
|
||||
|
||||
if (!("length" in arrayLike) || $isCallable(arrayLike)) {
|
||||
throw new TypeError(
|
||||
"The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Don't pass the second argument because Node's Buffer.from doesn't accept
|
||||
// a function and Uint8Array.from requires it if it exists
|
||||
// That means we cannot use $tailCallFowrardArguments here, sadly
|
||||
return new $Buffer(Uint8Array.from(arrayLike).buffer);
|
||||
throw $ERR_INVALID_ARG_TYPE(
|
||||
"first argument",
|
||||
["string", "Buffer", "ArrayBuffer", "Array", "Array-like Object"],
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
export function isBuffer(bufferlike) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Buffer, SlowBuffer, isAscii, isUtf8, kMaxLength } from "buffer";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
||||
import { gc } from "harness";
|
||||
import vm from "node:vm";
|
||||
|
||||
const BufferModule = await import("buffer");
|
||||
|
||||
@@ -2116,7 +2117,7 @@ for (let withOverridenBufferWrite of [false, true]) {
|
||||
const buf = Buffer.from(ab);
|
||||
|
||||
expect(buf instanceof Buffer).toBe(true);
|
||||
// expect(buf.parent, buf.buffer);
|
||||
expect(buf.parent, buf.buffer);
|
||||
expect(buf.buffer).toBe(ab);
|
||||
expect(buf.length).toBe(ab.byteLength);
|
||||
|
||||
@@ -2136,13 +2137,12 @@ for (let withOverridenBufferWrite of [false, true]) {
|
||||
|
||||
// Now test protecting users from doing stupid things
|
||||
|
||||
// expect(function () {
|
||||
// function AB() {}
|
||||
// Object.setPrototypeOf(AB, ArrayBuffer);
|
||||
// Object.setPrototypeOf(AB.prototype, ArrayBuffer.prototype);
|
||||
// // Buffer.from(new AB());
|
||||
// }).toThrow();
|
||||
// console.log(origAB !== ab);
|
||||
expect(function () {
|
||||
function AB() {}
|
||||
Object.setPrototypeOf(AB, ArrayBuffer);
|
||||
Object.setPrototypeOf(AB.prototype, ArrayBuffer.prototype);
|
||||
Buffer.from(new AB());
|
||||
}).toThrow();
|
||||
|
||||
// Test the byteOffset and length arguments
|
||||
{
|
||||
@@ -2670,8 +2670,8 @@ for (let withOverridenBufferWrite of [false, true]) {
|
||||
});
|
||||
|
||||
// Test that ArrayBuffer from a different context is detected correctly
|
||||
// const arrayBuf = vm.runInNewContext("new ArrayBuffer()");
|
||||
// expect(Buffer.byteLength(arrayBuf)).toBe(0);
|
||||
const arrayBuf = vm.runInNewContext("new ArrayBuffer()");
|
||||
expect(Buffer.byteLength(arrayBuf)).toBe(0);
|
||||
|
||||
// Verify that invalid encodings are treated as utf8
|
||||
for (let i = 1; i < 10; i++) {
|
||||
|
||||
151
test/js/node/test/parallel/test-buffer-arraybuffer.js
Normal file
151
test/js/node/test/parallel/test-buffer-arraybuffer.js
Normal file
@@ -0,0 +1,151 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
const LENGTH = 16;
|
||||
|
||||
const ab = new ArrayBuffer(LENGTH);
|
||||
const dv = new DataView(ab);
|
||||
const ui = new Uint8Array(ab);
|
||||
const buf = Buffer.from(ab);
|
||||
|
||||
|
||||
assert.ok(buf instanceof Buffer);
|
||||
assert.strictEqual(buf.parent, buf.buffer);
|
||||
assert.strictEqual(buf.buffer, ab);
|
||||
assert.strictEqual(buf.length, ab.byteLength);
|
||||
|
||||
|
||||
buf.fill(0xC);
|
||||
for (let i = 0; i < LENGTH; i++) {
|
||||
assert.strictEqual(ui[i], 0xC);
|
||||
ui[i] = 0xF;
|
||||
assert.strictEqual(buf[i], 0xF);
|
||||
}
|
||||
|
||||
buf.writeUInt32LE(0xF00, 0);
|
||||
buf.writeUInt32BE(0xB47, 4);
|
||||
buf.writeDoubleLE(3.1415, 8);
|
||||
|
||||
assert.strictEqual(dv.getUint32(0, true), 0xF00);
|
||||
assert.strictEqual(dv.getUint32(4), 0xB47);
|
||||
assert.strictEqual(dv.getFloat64(8, true), 3.1415);
|
||||
|
||||
|
||||
// Now test protecting users from doing stupid things
|
||||
|
||||
assert.throws(function() {
|
||||
function AB() { }
|
||||
Object.setPrototypeOf(AB, ArrayBuffer);
|
||||
Object.setPrototypeOf(AB.prototype, ArrayBuffer.prototype);
|
||||
Buffer.from(new AB());
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The first argument must be of type string, Buffer, ArrayBuffer, Array, or Array-like Object.' +
|
||||
' Received an instance of AB'
|
||||
});
|
||||
|
||||
// Test the byteOffset and length arguments
|
||||
{
|
||||
const ab = new Uint8Array(5);
|
||||
ab[0] = 1;
|
||||
ab[1] = 2;
|
||||
ab[2] = 3;
|
||||
ab[3] = 4;
|
||||
ab[4] = 5;
|
||||
const buf = Buffer.from(ab.buffer, 1, 3);
|
||||
assert.strictEqual(buf.length, 3);
|
||||
assert.strictEqual(buf[0], 2);
|
||||
assert.strictEqual(buf[1], 3);
|
||||
assert.strictEqual(buf[2], 4);
|
||||
buf[0] = 9;
|
||||
assert.strictEqual(ab[1], 9);
|
||||
|
||||
assert.throws(() => Buffer.from(ab.buffer, 6), {
|
||||
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
|
||||
name: 'RangeError',
|
||||
message: '"offset" is outside of buffer bounds'
|
||||
});
|
||||
assert.throws(() => Buffer.from(ab.buffer, 3, 6), {
|
||||
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
|
||||
name: 'RangeError',
|
||||
message: '"length" is outside of buffer bounds'
|
||||
});
|
||||
}
|
||||
|
||||
// Test the deprecated Buffer() version also
|
||||
{
|
||||
const ab = new Uint8Array(5);
|
||||
ab[0] = 1;
|
||||
ab[1] = 2;
|
||||
ab[2] = 3;
|
||||
ab[3] = 4;
|
||||
ab[4] = 5;
|
||||
const buf = Buffer(ab.buffer, 1, 3);
|
||||
assert.strictEqual(buf.length, 3);
|
||||
assert.strictEqual(buf[0], 2);
|
||||
assert.strictEqual(buf[1], 3);
|
||||
assert.strictEqual(buf[2], 4);
|
||||
buf[0] = 9;
|
||||
assert.strictEqual(ab[1], 9);
|
||||
|
||||
assert.throws(() => Buffer(ab.buffer, 6), {
|
||||
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
|
||||
name: 'RangeError',
|
||||
message: '"offset" is outside of buffer bounds'
|
||||
});
|
||||
assert.throws(() => Buffer(ab.buffer, 3, 6), {
|
||||
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
|
||||
name: 'RangeError',
|
||||
message: '"length" is outside of buffer bounds'
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
// If byteOffset is not numeric, it defaults to 0.
|
||||
const ab = new ArrayBuffer(10);
|
||||
const expected = Buffer.from(ab, 0);
|
||||
assert.deepStrictEqual(Buffer.from(ab, 'fhqwhgads'), expected);
|
||||
assert.deepStrictEqual(Buffer.from(ab, NaN), expected);
|
||||
assert.deepStrictEqual(Buffer.from(ab, {}), expected);
|
||||
assert.deepStrictEqual(Buffer.from(ab, []), expected);
|
||||
|
||||
// If byteOffset can be converted to a number, it will be.
|
||||
assert.deepStrictEqual(Buffer.from(ab, [1]), Buffer.from(ab, 1));
|
||||
|
||||
// If byteOffset is Infinity, throw.
|
||||
assert.throws(() => {
|
||||
Buffer.from(ab, Infinity);
|
||||
}, {
|
||||
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
|
||||
name: 'RangeError',
|
||||
message: '"offset" is outside of buffer bounds'
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
// If length is not numeric, it defaults to 0.
|
||||
const ab = new ArrayBuffer(10);
|
||||
const expected = Buffer.from(ab, 0, 0);
|
||||
assert.deepStrictEqual(Buffer.from(ab, 0, 'fhqwhgads'), expected);
|
||||
assert.deepStrictEqual(Buffer.from(ab, 0, NaN), expected);
|
||||
assert.deepStrictEqual(Buffer.from(ab, 0, {}), expected);
|
||||
assert.deepStrictEqual(Buffer.from(ab, 0, []), expected);
|
||||
|
||||
// If length can be converted to a number, it will be.
|
||||
assert.deepStrictEqual(Buffer.from(ab, 0, [1]), Buffer.from(ab, 0, 1));
|
||||
|
||||
// If length is Infinity, throw.
|
||||
assert.throws(() => {
|
||||
Buffer.from(ab, 0, Infinity);
|
||||
}, {
|
||||
code: 'ERR_BUFFER_OUT_OF_BOUNDS',
|
||||
name: 'RangeError',
|
||||
message: '"length" is outside of buffer bounds'
|
||||
});
|
||||
}
|
||||
|
||||
// Test an array like entry with the length set to NaN.
|
||||
assert.deepStrictEqual(Buffer.from({ length: NaN }), Buffer.alloc(0));
|
||||
143
test/js/node/test/parallel/test-buffer-from.js
Normal file
143
test/js/node/test/parallel/test-buffer-from.js
Normal file
@@ -0,0 +1,143 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
const { deepStrictEqual, strictEqual, throws } = require('assert');
|
||||
const { runInNewContext } = require('vm');
|
||||
|
||||
const checkString = 'test';
|
||||
|
||||
const check = Buffer.from(checkString);
|
||||
|
||||
class MyString extends String {
|
||||
constructor() {
|
||||
super(checkString);
|
||||
}
|
||||
}
|
||||
|
||||
class MyPrimitive {
|
||||
[Symbol.toPrimitive]() {
|
||||
return checkString;
|
||||
}
|
||||
}
|
||||
|
||||
class MyBadPrimitive {
|
||||
[Symbol.toPrimitive]() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
deepStrictEqual(Buffer.from(new String(checkString)), check);
|
||||
deepStrictEqual(Buffer.from(new MyString()), check);
|
||||
deepStrictEqual(Buffer.from(new MyPrimitive()), check);
|
||||
deepStrictEqual(
|
||||
Buffer.from(runInNewContext('new String(checkString)', { checkString })),
|
||||
check
|
||||
);
|
||||
|
||||
[
|
||||
{},
|
||||
new Boolean(true),
|
||||
{ valueOf() { return null; } },
|
||||
{ valueOf() { return undefined; } },
|
||||
{ valueOf: null },
|
||||
{ __proto__: null },
|
||||
new Number(true),
|
||||
new MyBadPrimitive(),
|
||||
Symbol(),
|
||||
5n,
|
||||
(one, two, three) => {},
|
||||
undefined,
|
||||
null,
|
||||
].forEach((input) => {
|
||||
const errObj = {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
name: 'TypeError',
|
||||
message: 'The first argument must be of type string, Buffer, ArrayBuffer, Array, or Array-like Object.' +
|
||||
common.invalidArgTypeHelper(input)
|
||||
};
|
||||
throws(() => Buffer.from(input), errObj);
|
||||
throws(() => Buffer.from(input, 'hex'), errObj);
|
||||
});
|
||||
|
||||
Buffer.allocUnsafe(10); // Should not throw.
|
||||
Buffer.from('deadbeaf', 'hex'); // Should not throw.
|
||||
|
||||
|
||||
{
|
||||
const u16 = new Uint16Array([0xffff]);
|
||||
const b16 = Buffer.copyBytesFrom(u16);
|
||||
u16[0] = 0;
|
||||
strictEqual(b16.length, 2);
|
||||
strictEqual(b16[0], 255);
|
||||
strictEqual(b16[1], 255);
|
||||
}
|
||||
|
||||
{
|
||||
const u16 = new Uint16Array([0, 0xffff]);
|
||||
const b16 = Buffer.copyBytesFrom(u16, 1, 5);
|
||||
u16[0] = 0xffff;
|
||||
u16[1] = 0;
|
||||
strictEqual(b16.length, 2);
|
||||
strictEqual(b16[0], 255);
|
||||
strictEqual(b16[1], 255);
|
||||
}
|
||||
|
||||
{
|
||||
const u32 = new Uint32Array([0xffffffff]);
|
||||
const b32 = Buffer.copyBytesFrom(u32);
|
||||
u32[0] = 0;
|
||||
strictEqual(b32.length, 4);
|
||||
strictEqual(b32[0], 255);
|
||||
strictEqual(b32[1], 255);
|
||||
strictEqual(b32[2], 255);
|
||||
strictEqual(b32[3], 255);
|
||||
}
|
||||
|
||||
throws(() => {
|
||||
Buffer.copyBytesFrom();
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
});
|
||||
|
||||
['', Symbol(), true, false, {}, [], () => {}, 1, 1n, null, undefined].forEach(
|
||||
(notTypedArray) => throws(() => {
|
||||
Buffer.copyBytesFrom('nope');
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
})
|
||||
);
|
||||
|
||||
['', Symbol(), true, false, {}, [], () => {}, 1n].forEach((notANumber) =>
|
||||
throws(() => {
|
||||
Buffer.copyBytesFrom(new Uint8Array(1), notANumber);
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
})
|
||||
);
|
||||
|
||||
[-1, NaN, 1.1, -Infinity].forEach((outOfRange) =>
|
||||
throws(() => {
|
||||
Buffer.copyBytesFrom(new Uint8Array(1), outOfRange);
|
||||
}, {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
})
|
||||
);
|
||||
|
||||
['', Symbol(), true, false, {}, [], () => {}, 1n].forEach((notANumber) =>
|
||||
throws(() => {
|
||||
Buffer.copyBytesFrom(new Uint8Array(1), 0, notANumber);
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
})
|
||||
);
|
||||
|
||||
[-1, NaN, 1.1, -Infinity].forEach((outOfRange) =>
|
||||
throws(() => {
|
||||
Buffer.copyBytesFrom(new Uint8Array(1), 0, outOfRange);
|
||||
}, {
|
||||
code: 'ERR_OUT_OF_RANGE',
|
||||
})
|
||||
);
|
||||
|
||||
// Invalid encoding is allowed
|
||||
Buffer.from('asd', 1);
|
||||
27
test/js/node/test/parallel/test-buffer-sharedarraybuffer.js
Normal file
27
test/js/node/test/parallel/test-buffer-sharedarraybuffer.js
Normal file
@@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
const sab = new SharedArrayBuffer(24);
|
||||
const arr1 = new Uint16Array(sab);
|
||||
const arr2 = new Uint16Array(12);
|
||||
arr2[0] = 5000;
|
||||
arr1[0] = 5000;
|
||||
arr1[1] = 4000;
|
||||
arr2[1] = 4000;
|
||||
|
||||
const arr_buf = Buffer.from(arr1.buffer);
|
||||
const ar_buf = Buffer.from(arr2.buffer);
|
||||
|
||||
assert.deepStrictEqual(arr_buf, ar_buf);
|
||||
|
||||
arr1[1] = 6000;
|
||||
arr2[1] = 6000;
|
||||
|
||||
assert.deepStrictEqual(arr_buf, ar_buf);
|
||||
|
||||
// Checks for calling Buffer.byteLength on a SharedArrayBuffer.
|
||||
assert.strictEqual(Buffer.byteLength(sab), sab.byteLength);
|
||||
|
||||
Buffer.from({ buffer: sab }); // Should not throw.
|
||||
Reference in New Issue
Block a user