diff --git a/lib/buffer.js b/lib/buffer.js index 01d2764c6d566b..6c8a8096ad101e 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -35,7 +35,8 @@ const { swap32: _swap32, swap64: _swap64, kMaxLength, - kStringMaxLength + kStringMaxLength, + zeroFill: bindingZeroFill } = internalBinding('buffer'); const { getOwnNonIndexProperties, @@ -72,21 +73,14 @@ const { } = require('internal/errors').codes; const { validateString } = require('internal/validators'); -const internalBuffer = require('internal/buffer'); - -const { setupBufferJS } = internalBuffer; - -const bindingObj = {}; +const { + FastBuffer, + addBufferPrototypeMethods +} = require('internal/buffer'); -class FastBuffer extends Uint8Array {} FastBuffer.prototype.constructor = Buffer; -internalBuffer.FastBuffer = FastBuffer; - Buffer.prototype = FastBuffer.prototype; - -for (const [name, method] of Object.entries(internalBuffer.readWrites)) { - Buffer.prototype[name] = method; -} +addBufferPrototypeMethods(Buffer.prototype); const constants = Object.defineProperties({}, { MAX_LENGTH: { @@ -104,11 +98,11 @@ const constants = Object.defineProperties({}, { Buffer.poolSize = 8 * 1024; var poolSize, poolOffset, allocPool; -setupBufferJS(Buffer.prototype, bindingObj); - +// A toggle used to access the zero fill setting of the array buffer allocator +// in C++. // |zeroFill| can be undefined when running inside an isolate where we // do not own the ArrayBuffer allocator. Zero fill is always on in that case. -const zeroFill = bindingObj.zeroFill || [0]; +const zeroFill = bindingZeroFill || [0]; function createUnsafeBuffer(size) { return new FastBuffer(createUnsafeArrayBuffer(size)); diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js index db593e597d403e..de4a6039255b62 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -593,16 +593,21 @@ function setupGlobalVariables() { } }); - // This, as side effect, removes `setupBufferJS` from the buffer binding, - // and exposes it on `internal/buffer`. - NativeModule.require('internal/buffer'); + const { Buffer } = NativeModule.require('buffer'); + const bufferBinding = internalBinding('buffer'); + + // Only after this point can C++ use Buffer::New() + bufferBinding.setBufferPrototype(Buffer.prototype); + delete bufferBinding.setBufferPrototype; + delete bufferBinding.zeroFill; Object.defineProperty(global, 'Buffer', { - value: NativeModule.require('buffer').Buffer, + value: Buffer, enumerable: false, writable: true, configurable: true }); + process.domain = null; process._exiting = false; } diff --git a/lib/internal/buffer.js b/lib/internal/buffer.js index d201924afa0502..56b73f5489ec35 100644 --- a/lib/internal/buffer.js +++ b/lib/internal/buffer.js @@ -1,17 +1,25 @@ 'use strict'; -const binding = internalBinding('buffer'); const { ERR_BUFFER_OUT_OF_BOUNDS, ERR_INVALID_ARG_TYPE, ERR_OUT_OF_RANGE } = require('internal/errors').codes; const { validateNumber } = require('internal/validators'); -const { setupBufferJS } = binding; - -// Remove from the binding so that function is only available as exported here. -// (That is, for internal use only.) -delete binding.setupBufferJS; +const { + asciiSlice, + base64Slice, + latin1Slice, + hexSlice, + ucs2Slice, + utf8Slice, + asciiWrite, + base64Write, + latin1Write, + hexWrite, + ucs2Write, + utf8Write +} = internalBinding('buffer'); // Temporary buffers to convert numbers. const float32Array = new Float32Array(1); @@ -777,46 +785,63 @@ function writeFloatBackwards(val, offset = 0) { return offset; } -// FastBuffer wil be inserted here by lib/buffer.js +class FastBuffer extends Uint8Array {} + +function addBufferPrototypeMethods(proto) { + proto.readUIntLE = readUIntLE; + proto.readUInt32LE = readUInt32LE; + proto.readUInt16LE = readUInt16LE; + proto.readUInt8 = readUInt8; + proto.readUIntBE = readUIntBE; + proto.readUInt32BE = readUInt32BE; + proto.readUInt16BE = readUInt16BE; + proto.readIntLE = readIntLE; + proto.readInt32LE = readInt32LE; + proto.readInt16LE = readInt16LE; + proto.readInt8 = readInt8; + proto.readIntBE = readIntBE; + proto.readInt32BE = readInt32BE; + proto.readInt16BE = readInt16BE; + + proto.writeUIntLE = writeUIntLE; + proto.writeUInt32LE = writeUInt32LE; + proto.writeUInt16LE = writeUInt16LE; + proto.writeUInt8 = writeUInt8; + proto.writeUIntBE = writeUIntBE; + proto.writeUInt32BE = writeUInt32BE; + proto.writeUInt16BE = writeUInt16BE; + proto.writeIntLE = writeIntLE; + proto.writeInt32LE = writeInt32LE; + proto.writeInt16LE = writeInt16LE; + proto.writeInt8 = writeInt8; + proto.writeIntBE = writeIntBE; + proto.writeInt32BE = writeInt32BE; + proto.writeInt16BE = writeInt16BE; + + proto.readFloatLE = bigEndian ? readFloatBackwards : readFloatForwards; + proto.readFloatBE = bigEndian ? readFloatForwards : readFloatBackwards; + proto.readDoubleLE = bigEndian ? readDoubleBackwards : readDoubleForwards; + proto.readDoubleBE = bigEndian ? readDoubleForwards : readDoubleBackwards; + proto.writeFloatLE = bigEndian ? writeFloatBackwards : writeFloatForwards; + proto.writeFloatBE = bigEndian ? writeFloatForwards : writeFloatBackwards; + proto.writeDoubleLE = bigEndian ? writeDoubleBackwards : writeDoubleForwards; + proto.writeDoubleBE = bigEndian ? writeDoubleForwards : writeDoubleBackwards; + + proto.asciiSlice = asciiSlice; + proto.base64Slice = base64Slice; + proto.latin1Slice = latin1Slice; + proto.hexSlice = hexSlice; + proto.ucs2Slice = ucs2Slice; + proto.utf8Slice = utf8Slice; + proto.asciiWrite = asciiWrite; + proto.base64Write = base64Write; + proto.latin1Write = latin1Write; + proto.hexWrite = hexWrite; + proto.ucs2Write = ucs2Write; + proto.utf8Write = utf8Write; +} + module.exports = { - setupBufferJS, - // Container to export all read write functions. - readWrites: { - readUIntLE, - readUInt32LE, - readUInt16LE, - readUInt8, - readUIntBE, - readUInt32BE, - readUInt16BE, - readIntLE, - readInt32LE, - readInt16LE, - readInt8, - readIntBE, - readInt32BE, - readInt16BE, - writeUIntLE, - writeUInt32LE, - writeUInt16LE, - writeUInt8, - writeUIntBE, - writeUInt32BE, - writeUInt16BE, - writeIntLE, - writeInt32LE, - writeInt16LE, - writeInt8, - writeIntBE, - writeInt32BE, - writeInt16BE, - readFloatLE: bigEndian ? readFloatBackwards : readFloatForwards, - readFloatBE: bigEndian ? readFloatForwards : readFloatBackwards, - readDoubleLE: bigEndian ? readDoubleBackwards : readDoubleForwards, - readDoubleBE: bigEndian ? readDoubleForwards : readDoubleBackwards, - writeFloatLE: bigEndian ? writeFloatBackwards : writeFloatForwards, - writeFloatBE: bigEndian ? writeFloatForwards : writeFloatBackwards, - writeDoubleLE: bigEndian ? writeDoubleBackwards : writeDoubleForwards, - writeDoubleBE: bigEndian ? writeDoubleForwards : writeDoubleBackwards - } + FastBuffer, + addBufferPrototypeMethods }; diff --git a/src/node_buffer.cc b/src/node_buffer.cc index b46b458831dd5b..b728eeced09a1d 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -1057,38 +1057,12 @@ static void EncodeUtf8String(const FunctionCallbackInfo& args) { } -// pass Buffer object to load prototype methods -void SetupBufferJS(const FunctionCallbackInfo& args) { +void SetBufferPrototype(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsObject()); Local proto = args[0].As(); env->set_buffer_prototype_object(proto); - - env->SetMethodNoSideEffect(proto, "asciiSlice", StringSlice); - env->SetMethodNoSideEffect(proto, "base64Slice", StringSlice); - env->SetMethodNoSideEffect(proto, "latin1Slice", StringSlice); - env->SetMethodNoSideEffect(proto, "hexSlice", StringSlice); - env->SetMethodNoSideEffect(proto, "ucs2Slice", StringSlice); - env->SetMethodNoSideEffect(proto, "utf8Slice", StringSlice); - - env->SetMethod(proto, "asciiWrite", StringWrite); - env->SetMethod(proto, "base64Write", StringWrite); - env->SetMethod(proto, "latin1Write", StringWrite); - env->SetMethod(proto, "hexWrite", StringWrite); - env->SetMethod(proto, "ucs2Write", StringWrite); - env->SetMethod(proto, "utf8Write", StringWrite); - - if (auto zero_fill_field = env->isolate_data()->zero_fill_field()) { - CHECK(args[1]->IsObject()); - auto binding_object = args[1].As(); - auto array_buffer = ArrayBuffer::New(env->isolate(), - zero_fill_field, - sizeof(*zero_fill_field)); - auto name = FIXED_ONE_BYTE_STRING(env->isolate(), "zeroFill"); - auto value = Uint32Array::New(array_buffer, 0, 1); - CHECK(binding_object->Set(env->context(), name, value).FromJust()); - } } @@ -1098,7 +1072,7 @@ void Initialize(Local target, void* priv) { Environment* env = Environment::GetCurrent(context); - env->SetMethod(target, "setupBufferJS", SetupBufferJS); + env->SetMethod(target, "setBufferPrototype", SetBufferPrototype); env->SetMethodNoSideEffect(target, "createFromString", CreateFromString); env->SetMethodNoSideEffect(target, "byteLengthUtf8", ByteLengthUtf8); @@ -1123,6 +1097,32 @@ void Initialize(Local target, target->Set(env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), "kStringMaxLength"), Integer::New(env->isolate(), String::kMaxLength)).FromJust(); + + env->SetMethodNoSideEffect(target, "asciiSlice", StringSlice); + env->SetMethodNoSideEffect(target, "base64Slice", StringSlice); + env->SetMethodNoSideEffect(target, "latin1Slice", StringSlice); + env->SetMethodNoSideEffect(target, "hexSlice", StringSlice); + env->SetMethodNoSideEffect(target, "ucs2Slice", StringSlice); + env->SetMethodNoSideEffect(target, "utf8Slice", StringSlice); + + env->SetMethod(target, "asciiWrite", StringWrite); + env->SetMethod(target, "base64Write", StringWrite); + env->SetMethod(target, "latin1Write", StringWrite); + env->SetMethod(target, "hexWrite", StringWrite); + env->SetMethod(target, "ucs2Write", StringWrite); + env->SetMethod(target, "utf8Write", StringWrite); + + // It can be a nullptr when running inside an isolate where we + // do not own the ArrayBuffer allocator. + if (uint32_t* zero_fill_field = env->isolate_data()->zero_fill_field()) { + Local array_buffer = ArrayBuffer::New( + env->isolate(), zero_fill_field, sizeof(*zero_fill_field)); + CHECK(target + ->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "zeroFill"), + Uint32Array::New(array_buffer, 0, 1)) + .FromJust()); + } } } // anonymous namespace diff --git a/src/node_internals.h b/src/node_internals.h index 897905b5a76a69..64535bbfa2f4c9 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -246,6 +246,7 @@ v8::MaybeLocal New(Environment* env, size_t byte_offset, size_t length) { v8::Local ui = v8::Uint8Array::New(ab, byte_offset, length); + CHECK(!env->buffer_prototype_object().IsEmpty()); v8::Maybe mb = ui->SetPrototype(env->context(), env->buffer_prototype_object()); if (mb.IsNothing())