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);
|
return Buffer.from(hundred);
|
||||||
});
|
});
|
||||||
|
|
||||||
bench("new Buffer(ArrayBuffer(100))", () => {
|
|
||||||
return new Buffer(hundred);
|
|
||||||
});
|
|
||||||
|
|
||||||
var hundredArray = new Uint8Array(100);
|
var hundredArray = new Uint8Array(100);
|
||||||
bench("Buffer.from(Uint8Array(100))", () => {
|
bench("Buffer.from(Uint8Array(100))", () => {
|
||||||
return Buffer.from(hundredArray);
|
return Buffer.from(hundredArray);
|
||||||
});
|
});
|
||||||
|
|
||||||
bench("new Buffer(Uint8Array(100))", () => {
|
|
||||||
return new Buffer(hundredArray);
|
|
||||||
});
|
|
||||||
|
|
||||||
var empty = new Uint8Array(0);
|
var empty = new Uint8Array(0);
|
||||||
bench("Buffer.from(Uint8Array(0))", () => {
|
bench("Buffer.from(Uint8Array(0))", () => {
|
||||||
return Buffer.from(empty);
|
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))", () => {
|
bench("new Buffer(Uint8Array(0))", () => {
|
||||||
return new Buffer(empty);
|
return new Buffer(empty);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -618,8 +618,12 @@ JSC::EncodedJSValue STRING_TOO_LONG(JSC::ThrowScope& throwScope, JSC::JSGlobalOb
|
|||||||
return {};
|
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));
|
throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_BUFFER_OUT_OF_BOUNDS, "Attempt to access memory outside buffer bounds"_s));
|
||||||
return {};
|
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 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 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 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 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 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);
|
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)
|
extern "C" EncodedJSValue WebCore_BufferEncodingType_toJS(JSC::JSGlobalObject* lexicalGlobalObject, WebCore::BufferEncodingType encoding)
|
||||||
{
|
{
|
||||||
// clang-format off
|
// clang-format off
|
||||||
auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
|
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);
|
||||||
switch (encoding) {
|
switch (encoding) {
|
||||||
case WebCore::BufferEncodingType::utf8: return JSC::JSValue::encode(globalObject->commonStrings().utf8String(globalObject));
|
case WebCore::BufferEncodingType::utf8: return JSC::JSValue::encode(globalObject->commonStrings().utf8String(globalObject));
|
||||||
case WebCore::BufferEncodingType::ucs2: return JSC::JSValue::encode(globalObject->commonStrings().ucs2String(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);
|
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
|
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);
|
||||||
auto* subclassStructure = globalObject->JSBufferSubclassStructure();
|
auto* subclassStructure = globalObject->JSBufferSubclassStructure();
|
||||||
|
|
||||||
auto* uint8Array = JSC::JSUint8Array::create(lexicalGlobalObject, subclassStructure, byteLength);
|
auto* uint8Array = JSC::JSUint8Array::create(lexicalGlobalObject, subclassStructure, byteLength);
|
||||||
@@ -320,7 +320,7 @@ JSC::EncodedJSValue JSBuffer__bufferFromPointerAndLengthAndDeinit(JSC::JSGlobalO
|
|||||||
|
|
||||||
JSC::JSUint8Array* uint8Array = nullptr;
|
JSC::JSUint8Array* uint8Array = nullptr;
|
||||||
|
|
||||||
auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
|
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);
|
||||||
auto* subclassStructure = globalObject->JSBufferSubclassStructure();
|
auto* subclassStructure = globalObject->JSBufferSubclassStructure();
|
||||||
|
|
||||||
if (LIKELY(length > 0)) {
|
if (LIKELY(length > 0)) {
|
||||||
@@ -417,7 +417,7 @@ JSC::JSUint8Array* createEmptyBuffer(JSC::JSGlobalObject* lexicalGlobalObject)
|
|||||||
|
|
||||||
JSC::JSUint8Array* createUninitializedBuffer(JSC::JSGlobalObject* lexicalGlobalObject, size_t length)
|
JSC::JSUint8Array* createUninitializedBuffer(JSC::JSGlobalObject* lexicalGlobalObject, size_t length)
|
||||||
{
|
{
|
||||||
auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
|
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);
|
||||||
auto* subclassStructure = globalObject->JSBufferSubclassStructure();
|
auto* subclassStructure = globalObject->JSBufferSubclassStructure();
|
||||||
|
|
||||||
return JSC::JSUint8Array::createUninitialized(lexicalGlobalObject, subclassStructure, length);
|
return JSC::JSUint8Array::createUninitialized(lexicalGlobalObject, subclassStructure, length);
|
||||||
@@ -436,7 +436,7 @@ static inline JSC::JSUint8Array* JSBuffer__bufferFromLengthAsArray(JSC::JSGlobal
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
|
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);
|
||||||
auto* subclassStructure = globalObject->JSBufferSubclassStructure();
|
auto* subclassStructure = globalObject->JSBufferSubclassStructure();
|
||||||
JSC::JSUint8Array* uint8Array = JSC::JSUint8Array::create(lexicalGlobalObject, subclassStructure, static_cast<size_t>(length));
|
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
|
} // 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)
|
static inline JSC::EncodedJSValue createJSBufferFromJS(JSC::JSGlobalObject* lexicalGlobalObject, JSValue newTarget, ArgList args)
|
||||||
{
|
{
|
||||||
auto& vm = JSC::getVM(lexicalGlobalObject);
|
auto& vm = JSC::getVM(lexicalGlobalObject);
|
||||||
@@ -2423,28 +2477,27 @@ static inline JSC::EncodedJSValue createJSBufferFromJS(JSC::JSGlobalObject* lexi
|
|||||||
}
|
}
|
||||||
JSValue distinguishingArg = args.at(0);
|
JSValue distinguishingArg = args.at(0);
|
||||||
JSValue encodingArg = argsCount > 1 ? args.at(1) : JSValue();
|
JSValue encodingArg = argsCount > 1 ? args.at(1) : JSValue();
|
||||||
auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
|
auto* globalObject = defaultGlobalObject(lexicalGlobalObject);
|
||||||
|
|
||||||
if (distinguishingArg.isAnyInt()) {
|
if (distinguishingArg.isAnyInt()) {
|
||||||
throwScope.release();
|
throwScope.release();
|
||||||
if (args.at(1).isString()) {
|
if (args.at(1).isString()) {
|
||||||
return Bun::ERR::INVALID_ARG_TYPE(throwScope, lexicalGlobalObject, "string"_s, "string"_s, distinguishingArg);
|
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()) {
|
} else if (distinguishingArg.isNumber()) {
|
||||||
JSValue lengthValue = distinguishingArg;
|
JSValue lengthValue = distinguishingArg;
|
||||||
Bun::V::validateNumber(throwScope, lexicalGlobalObject, lengthValue, "size"_s, jsNumber(0), jsNumber(Bun::Buffer::kMaxLength));
|
Bun::V::validateNumber(throwScope, lexicalGlobalObject, lengthValue, "size"_s, jsNumber(0), jsNumber(Bun::Buffer::kMaxLength));
|
||||||
RETURN_IF_EXCEPTION(throwScope, {});
|
RETURN_IF_EXCEPTION(throwScope, {});
|
||||||
size_t length = lengthValue.toLength(lexicalGlobalObject);
|
size_t length = lengthValue.toLength(lexicalGlobalObject);
|
||||||
return JSBuffer__bufferFromLength(lexicalGlobalObject, length);
|
return JSValue::encode(allocBuffer(lexicalGlobalObject, length));
|
||||||
} else if (distinguishingArg.isUndefinedOrNull() || distinguishingArg.isBoolean()) {
|
} else if (distinguishingArg.isUndefinedOrNull() || distinguishingArg.isBoolean()) {
|
||||||
auto arg_string = distinguishingArg.toWTFString(globalObject);
|
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);
|
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 throwVMTypeError(globalObject, throwScope, message);
|
||||||
return {};
|
|
||||||
} else if (distinguishingArg.isCell()) {
|
} else if (distinguishingArg.isCell()) {
|
||||||
auto type = distinguishingArg.asCell()->type();
|
auto type = distinguishingArg.asCell()->type();
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case StringType:
|
case StringType:
|
||||||
case StringObjectType:
|
case StringObjectType:
|
||||||
@@ -2452,7 +2505,6 @@ static inline JSC::EncodedJSValue createJSBufferFromJS(JSC::JSGlobalObject* lexi
|
|||||||
throwScope.release();
|
throwScope.release();
|
||||||
return constructBufferFromStringAndEncoding(lexicalGlobalObject, distinguishingArg, encodingArg);
|
return constructBufferFromStringAndEncoding(lexicalGlobalObject, distinguishingArg, encodingArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
case Uint16ArrayType:
|
case Uint16ArrayType:
|
||||||
case Uint32ArrayType:
|
case Uint32ArrayType:
|
||||||
case Int8ArrayType:
|
case Int8ArrayType:
|
||||||
@@ -2465,100 +2517,41 @@ static inline JSC::EncodedJSValue createJSBufferFromJS(JSC::JSGlobalObject* lexi
|
|||||||
case BigUint64ArrayType: {
|
case BigUint64ArrayType: {
|
||||||
// byteOffset and byteLength are ignored in this case, which is consitent with Node.js and new Uint8Array()
|
// 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());
|
JSC::JSArrayBufferView* view = jsCast<JSC::JSArrayBufferView*>(distinguishingArg.asCell());
|
||||||
|
|
||||||
void* data = view->vector();
|
void* data = view->vector();
|
||||||
size_t byteLength = view->length();
|
size_t byteLength = view->length();
|
||||||
|
|
||||||
if (UNLIKELY(!data)) {
|
if (UNLIKELY(!data)) {
|
||||||
throwException(globalObject, throwScope, createRangeError(globalObject, "Buffer is detached"_s));
|
throwException(globalObject, throwScope, createRangeError(globalObject, "Buffer is detached"_s));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* uint8Array = createUninitializedBuffer(lexicalGlobalObject, byteLength);
|
auto* uint8Array = createUninitializedBuffer(lexicalGlobalObject, byteLength);
|
||||||
if (UNLIKELY(!uint8Array)) {
|
if (UNLIKELY(!uint8Array)) {
|
||||||
ASSERT(throwScope.exception());
|
ASSERT(throwScope.exception());
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (byteLength) {
|
if (byteLength) {
|
||||||
uint8Array->setFromTypedArray(lexicalGlobalObject, 0, view, 0, byteLength, CopyType::LeftToRight);
|
uint8Array->setFromTypedArray(lexicalGlobalObject, 0, view, 0, byteLength, CopyType::LeftToRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(uint8Array));
|
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(uint8Array));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case DataViewType:
|
case DataViewType:
|
||||||
case Uint8ArrayType:
|
case Uint8ArrayType:
|
||||||
case Uint8ClampedArrayType: {
|
case Uint8ClampedArrayType: {
|
||||||
// byteOffset and byteLength are ignored in this case, which is consitent with Node.js and new Uint8Array()
|
// 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());
|
JSC::JSArrayBufferView* view = jsCast<JSC::JSArrayBufferView*>(distinguishingArg.asCell());
|
||||||
|
|
||||||
void* data = view->vector();
|
void* data = view->vector();
|
||||||
size_t byteLength = view->byteLength();
|
size_t byteLength = view->byteLength();
|
||||||
|
|
||||||
if (UNLIKELY(!data)) {
|
if (UNLIKELY(!data)) {
|
||||||
throwException(globalObject, throwScope, createRangeError(globalObject, "Buffer is detached"_s));
|
throwException(globalObject, throwScope, createRangeError(globalObject, "Buffer is detached"_s));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* uint8Array = createBuffer(lexicalGlobalObject, static_cast<uint8_t*>(data), byteLength);
|
auto* uint8Array = createBuffer(lexicalGlobalObject, static_cast<uint8_t*>(data), byteLength);
|
||||||
|
|
||||||
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(uint8Array));
|
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(uint8Array));
|
||||||
}
|
}
|
||||||
case ArrayBufferType: {
|
case ArrayBufferType: {
|
||||||
// This closely matches `new Uint8Array(buffer, byteOffset, length)` in JavaScriptCore's implementation.
|
// This closely matches `new Uint8Array(buffer, byteOffset, length)` in JavaScriptCore's implementation.
|
||||||
// See Source/JavaScriptCore/runtime/JSGenericTypedArrayViewConstructorInlines.h
|
// See Source/JavaScriptCore/runtime/JSGenericTypedArrayViewConstructorInlines.h
|
||||||
size_t offset = 0;
|
return constructBufferFromArrayBuffer(throwScope, lexicalGlobalObject, args.size(), distinguishingArg, args.at(1), args.at(2));
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
break;
|
break;
|
||||||
@@ -2566,26 +2559,8 @@ static inline JSC::EncodedJSValue createJSBufferFromJS(JSC::JSGlobalObject* lexi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JSC::JSObject* constructor = lexicalGlobalObject->m_typedArrayUint8.constructor(lexicalGlobalObject);
|
return constructBufferFromArray(throwScope, lexicalGlobalObject, distinguishingArg);
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSC_DEFINE_HOST_FUNCTION(callJSBuffer, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
|
JSC_DEFINE_HOST_FUNCTION(callJSBuffer, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
|
||||||
{
|
{
|
||||||
return createJSBufferFromJS(lexicalGlobalObject, callFrame->thisValue(), ArgList(callFrame));
|
return createJSBufferFromJS(lexicalGlobalObject, callFrame->thisValue(), ArgList(callFrame));
|
||||||
|
|||||||
@@ -254,6 +254,7 @@ JSC_DEFINE_CUSTOM_SETTER(jsBunConfigVerboseFetchSetter, (JSGlobalObject * global
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if OS(WINDOWS)
|
||||||
extern "C" void Bun__Process__editWindowsEnvVar(BunString, BunString);
|
extern "C" void Bun__Process__editWindowsEnvVar(BunString, BunString);
|
||||||
|
|
||||||
JSC_DEFINE_HOST_FUNCTION(jsEditWindowsEnvVar, (JSGlobalObject * global, JSC::CallFrame* callFrame))
|
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()));
|
RELEASE_AND_RETURN(scope, JSValue::encode(jsUndefined()));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
JSValue createEnvironmentVariablesMap(Zig::GlobalObject* globalObject)
|
JSValue createEnvironmentVariablesMap(Zig::GlobalObject* globalObject)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
export default [
|
export default [
|
||||||
// class list for $inherits*() builtins, eg. $inheritsBlob()
|
// class list for $inherits*() builtins, eg. $inheritsBlob()
|
||||||
// tests if a value is an instanceof a native class in a robust cross-realm manner
|
// 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"],
|
["Blob"],
|
||||||
["ReadableStream", "JSReadableStream.h"],
|
["ReadableStream", "JSReadableStream.h"],
|
||||||
["WritableStream", "JSWritableStream.h"],
|
["WritableStream", "JSWritableStream.h"],
|
||||||
["TransformStream", "JSTransformStream.h"],
|
["TransformStream", "JSTransformStream.h"],
|
||||||
|
["ArrayBuffer"],
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,78 +1,43 @@
|
|||||||
// This is marked as a constructor because Node.js allows `new Buffer.from`,
|
// This is marked as a constructor because Node.js allows `new Buffer.from`,
|
||||||
// Some legacy dependencies depend on this, see #3638
|
// Some legacy dependencies depend on this, see #3638
|
||||||
$constructor;
|
$constructor;
|
||||||
export function from(items) {
|
export function from(value, encodingOrOffset, length) {
|
||||||
if ($isUndefinedOrNull(items)) {
|
if (typeof value === "string") return new $Buffer(value, encodingOrOffset);
|
||||||
throw new TypeError(
|
|
||||||
"The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: figure out why private symbol not found
|
if (typeof value === "object" && value !== null) {
|
||||||
if (
|
if ($inheritsArrayBuffer(value)) return new $Buffer(value, encodingOrOffset, length);
|
||||||
typeof items === "string" ||
|
if ($isTypedArrayView(value)) return new $Buffer(value, encodingOrOffset, length);
|
||||||
(typeof items === "object" &&
|
|
||||||
($isTypedArrayView(items) ||
|
const valueOf = value.valueOf && value.valueOf();
|
||||||
items instanceof ArrayBuffer ||
|
if (valueOf != null && valueOf !== value && (typeof valueOf === "string" || typeof valueOf === "object")) {
|
||||||
items instanceof SharedArrayBuffer ||
|
return Buffer.from(valueOf, encodingOrOffset, length);
|
||||||
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 items === "object") {
|
if (value.length !== undefined || $inheritsArrayBuffer(value.buffer)) {
|
||||||
const data = items.data;
|
if (typeof value.length !== "number") return new $Buffer(0);
|
||||||
if (items.type === "Buffer" && Array.isArray(data)) {
|
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);
|
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") {
|
if (typeof primitive === "string") {
|
||||||
switch ($argumentCount()) {
|
return new $Buffer(primitive, encodingOrOffset);
|
||||||
case 1: {
|
|
||||||
return new $Buffer(primitive);
|
|
||||||
}
|
|
||||||
case 2: {
|
|
||||||
return new $Buffer(primitive, $argument(1));
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return new $Buffer(primitive, $argument(1), $argument(2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
throw $ERR_INVALID_ARG_TYPE(
|
||||||
// a function and Uint8Array.from requires it if it exists
|
"first argument",
|
||||||
// That means we cannot use $tailCallFowrardArguments here, sadly
|
["string", "Buffer", "ArrayBuffer", "Array", "Array-like Object"],
|
||||||
return new $Buffer(Uint8Array.from(arrayLike).buffer);
|
value,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isBuffer(bufferlike) {
|
export function isBuffer(bufferlike) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Buffer, SlowBuffer, isAscii, isUtf8, kMaxLength } from "buffer";
|
import { Buffer, SlowBuffer, isAscii, isUtf8, kMaxLength } from "buffer";
|
||||||
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
||||||
import { gc } from "harness";
|
import { gc } from "harness";
|
||||||
|
import vm from "node:vm";
|
||||||
|
|
||||||
const BufferModule = await import("buffer");
|
const BufferModule = await import("buffer");
|
||||||
|
|
||||||
@@ -2116,7 +2117,7 @@ for (let withOverridenBufferWrite of [false, true]) {
|
|||||||
const buf = Buffer.from(ab);
|
const buf = Buffer.from(ab);
|
||||||
|
|
||||||
expect(buf instanceof Buffer).toBe(true);
|
expect(buf instanceof Buffer).toBe(true);
|
||||||
// expect(buf.parent, buf.buffer);
|
expect(buf.parent, buf.buffer);
|
||||||
expect(buf.buffer).toBe(ab);
|
expect(buf.buffer).toBe(ab);
|
||||||
expect(buf.length).toBe(ab.byteLength);
|
expect(buf.length).toBe(ab.byteLength);
|
||||||
|
|
||||||
@@ -2136,13 +2137,12 @@ for (let withOverridenBufferWrite of [false, true]) {
|
|||||||
|
|
||||||
// Now test protecting users from doing stupid things
|
// Now test protecting users from doing stupid things
|
||||||
|
|
||||||
// expect(function () {
|
expect(function () {
|
||||||
// function AB() {}
|
function AB() {}
|
||||||
// Object.setPrototypeOf(AB, ArrayBuffer);
|
Object.setPrototypeOf(AB, ArrayBuffer);
|
||||||
// Object.setPrototypeOf(AB.prototype, ArrayBuffer.prototype);
|
Object.setPrototypeOf(AB.prototype, ArrayBuffer.prototype);
|
||||||
// // Buffer.from(new AB());
|
Buffer.from(new AB());
|
||||||
// }).toThrow();
|
}).toThrow();
|
||||||
// console.log(origAB !== ab);
|
|
||||||
|
|
||||||
// Test the byteOffset and length arguments
|
// 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
|
// Test that ArrayBuffer from a different context is detected correctly
|
||||||
// const arrayBuf = vm.runInNewContext("new ArrayBuffer()");
|
const arrayBuf = vm.runInNewContext("new ArrayBuffer()");
|
||||||
// expect(Buffer.byteLength(arrayBuf)).toBe(0);
|
expect(Buffer.byteLength(arrayBuf)).toBe(0);
|
||||||
|
|
||||||
// Verify that invalid encodings are treated as utf8
|
// Verify that invalid encodings are treated as utf8
|
||||||
for (let i = 1; i < 10; i++) {
|
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