diff --git a/lib/buffer.js b/lib/buffer.js index 8f4e34d289fc27..4d4f325c07a3cb 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -300,8 +300,28 @@ function byteLength(string, encoding) { Buffer.byteLength = byteLength; + +Buffer.prototype.copy = function(target, targetStart, sourceStart, sourceEnd) { + if (!(this instanceof Buffer)) + throw new TypeError('this is not an instance of Buffer'); + + if (!(target instanceof Buffer)) + throw new TypeError('target is not an instance of Buffer'); + + return binding.copy(this, target, targetStart, sourceStart, sourceEnd); +}; + // toString(encoding, start=0, end=buffer.length) Buffer.prototype.toString = function(encoding, start, end) { + if (!(this instanceof Buffer)) { + // If no arguments, then this is probably an implicit coercion, and we + // shouldn't throw. + if (arguments.length === 0) + return Object.prototype.toString.call(this); + else + throw new TypeError('this is not an instance of Buffer'); + } + var loweredCase = false; start = start >>> 0; @@ -315,26 +335,26 @@ Buffer.prototype.toString = function(encoding, start, end) { while (true) { switch (encoding) { case 'hex': - return this.hexSlice(start, end); + return binding.hexSlice(this, start, end); case 'utf8': case 'utf-8': - return this.utf8Slice(start, end); + return binding.utf8Slice(this, start, end); case 'ascii': - return this.asciiSlice(start, end); + return binding.asciiSlice(this, start, end); case 'binary': - return this.binarySlice(start, end); + return binding.binarySlice(this, start, end); case 'base64': - return this.base64Slice(start, end); + return binding.base64Slice(this, start, end); case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': - return this.ucs2Slice(start, end); + return binding.ucs2Slice(this, start, end); default: if (loweredCase) @@ -345,6 +365,30 @@ Buffer.prototype.toString = function(encoding, start, end) { } }; +Buffer.prototype.asciiSlice = function(start, end) { + return this.toString('ascii', start, end); +}; + +Buffer.prototype.base64Slice = function(start, end) { + return this.toString('base64', start, end); +}; + +Buffer.prototype.binarySlice = function(start, end) { + return this.toString('binary', start, end); +}; + +Buffer.prototype.hexSlice = function(start, end) { + return this.toString('hex', start, end); +}; + +Buffer.prototype.ucs2Slice = function(start, end) { + return this.toString('ucs2', start, end); +}; + +Buffer.prototype.utf8Slice = function(start, end) { + return this.toString('utf8', start, end); +}; + Buffer.prototype.equals = function equals(b) { if (!(b instanceof Buffer)) @@ -371,6 +415,9 @@ Buffer.prototype.inspect = function inspect() { Buffer.prototype.compare = function compare(b) { + if (!(this instanceof Buffer)) + throw new TypeError('this is not an instance of Buffer'); + if (!(b instanceof Buffer)) throw new TypeError('Argument must be a Buffer'); @@ -382,6 +429,9 @@ Buffer.prototype.compare = function compare(b) { Buffer.prototype.indexOf = function indexOf(val, byteOffset) { + if (!(this instanceof Buffer)) + throw new TypeError('this is not an instance of Buffer'); + if (byteOffset > 0x7fffffff) byteOffset = 0x7fffffff; else if (byteOffset < -0x80000000) @@ -400,6 +450,9 @@ Buffer.prototype.indexOf = function indexOf(val, byteOffset) { Buffer.prototype.fill = function fill(val, start, end) { + if (!(this instanceof Buffer)) + throw new TypeError('this is not an instance of Buffer'); + start = start >> 0; end = (end === undefined) ? this.length : end >> 0; @@ -446,6 +499,12 @@ var writeWarned = false; const writeMsg = '.write(string, encoding, offset, length) is deprecated.' + ' Use write(string[, offset[, length]][, encoding]) instead.'; Buffer.prototype.write = function(string, offset, length, encoding) { + if (!(this instanceof Buffer)) + throw new TypeError('this is not an instance of Buffer'); + + if (typeof string !== 'string') + throw new TypeError('Argument must be a string'); + // Buffer#write(string); if (offset === undefined) { encoding = 'utf8'; @@ -502,27 +561,29 @@ Buffer.prototype.write = function(string, offset, length, encoding) { for (;;) { switch (encoding) { case 'hex': - return this.hexWrite(string, offset, length); + if (string.length % 2 !== 0) + throw new TypeError('Invalid hex string'); + return binding.hexWrite(this, string, offset, length); case 'utf8': case 'utf-8': - return this.utf8Write(string, offset, length); + return binding.utf8Write(this, string, offset, length); case 'ascii': - return this.asciiWrite(string, offset, length); + return binding.asciiWrite(this, string, offset, length); case 'binary': - return this.binaryWrite(string, offset, length); + return binding.binaryWrite(this, string, offset, length); case 'base64': // Warning: maxLength not taken into account in base64Write - return this.base64Write(string, offset, length); + return binding.base64Write(this, string, offset, length); case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': - return this.ucs2Write(string, offset, length); + return binding.ucs2Write(this, string, offset, length); default: if (loweredCase) @@ -533,6 +594,30 @@ Buffer.prototype.write = function(string, offset, length, encoding) { } }; +Buffer.prototype.asciiWrite = function(string, offset, length) { + return this.write(string, offset, length, 'ascii'); +}; + +Buffer.prototype.base64Write = function(string, offset, length) { + return this.write(string, offset, length, 'base64'); +}; + +Buffer.prototype.binaryWrite = function(string, offset, length) { + return this.write(string, offset, length, 'binary'); +}; + +Buffer.prototype.hexWrite = function(string, offset, length) { + return this.write(string, offset, length, 'hex'); +}; + +Buffer.prototype.ucs2Write = function(string, offset, length) { + return this.write(string, offset, length, 'ucs2'); +}; + +Buffer.prototype.utf8Write = function(string, offset, length) { + return this.write(string, offset, length, 'utf8'); +}; + Buffer.prototype.toJSON = function() { return { @@ -545,6 +630,9 @@ Buffer.prototype.toJSON = function() { // TODO(trevnorris): currently works like Array.prototype.slice(), which // doesn't follow the new standard for throwing on out of range indexes. Buffer.prototype.slice = function(start, end) { + if (!(this instanceof Buffer)) + throw new TypeError('this is not an instance of Buffer'); + var len = this.length; start = ~~start; end = end === undefined ? len : ~~end; @@ -578,7 +666,9 @@ Buffer.prototype.slice = function(start, end) { }; -function checkOffset(offset, ext, length) { +function checkOffset(buffer, offset, ext, length) { + if (!(buffer instanceof Buffer)) + throw new TypeError('this is not an instance of Buffer'); if (offset + ext > length) throw new RangeError('index out of range'); } @@ -588,7 +678,7 @@ Buffer.prototype.readUIntLE = function(offset, byteLength, noAssert) { offset = offset >>> 0; byteLength = byteLength >>> 0; if (!noAssert) - checkOffset(offset, byteLength, this.length); + checkOffset(this, offset, byteLength, this.length); var val = this[offset]; var mul = 1; @@ -604,7 +694,7 @@ Buffer.prototype.readUIntBE = function(offset, byteLength, noAssert) { offset = offset >>> 0; byteLength = byteLength >>> 0; if (!noAssert) - checkOffset(offset, byteLength, this.length); + checkOffset(this, offset, byteLength, this.length); var val = this[offset + --byteLength]; var mul = 1; @@ -618,7 +708,7 @@ Buffer.prototype.readUIntBE = function(offset, byteLength, noAssert) { Buffer.prototype.readUInt8 = function(offset, noAssert) { offset = offset >>> 0; if (!noAssert) - checkOffset(offset, 1, this.length); + checkOffset(this, offset, 1, this.length); return this[offset]; }; @@ -626,7 +716,7 @@ Buffer.prototype.readUInt8 = function(offset, noAssert) { Buffer.prototype.readUInt16LE = function(offset, noAssert) { offset = offset >>> 0; if (!noAssert) - checkOffset(offset, 2, this.length); + checkOffset(this, offset, 2, this.length); return this[offset] | (this[offset + 1] << 8); }; @@ -634,7 +724,7 @@ Buffer.prototype.readUInt16LE = function(offset, noAssert) { Buffer.prototype.readUInt16BE = function(offset, noAssert) { offset = offset >>> 0; if (!noAssert) - checkOffset(offset, 2, this.length); + checkOffset(this, offset, 2, this.length); return (this[offset] << 8) | this[offset + 1]; }; @@ -642,7 +732,7 @@ Buffer.prototype.readUInt16BE = function(offset, noAssert) { Buffer.prototype.readUInt32LE = function(offset, noAssert) { offset = offset >>> 0; if (!noAssert) - checkOffset(offset, 4, this.length); + checkOffset(this, offset, 4, this.length); return ((this[offset]) | (this[offset + 1] << 8) | @@ -654,7 +744,7 @@ Buffer.prototype.readUInt32LE = function(offset, noAssert) { Buffer.prototype.readUInt32BE = function(offset, noAssert) { offset = offset >>> 0; if (!noAssert) - checkOffset(offset, 4, this.length); + checkOffset(this, offset, 4, this.length); return (this[offset] * 0x1000000) + ((this[offset + 1] << 16) | @@ -667,7 +757,7 @@ Buffer.prototype.readIntLE = function(offset, byteLength, noAssert) { offset = offset >>> 0; byteLength = byteLength >>> 0; if (!noAssert) - checkOffset(offset, byteLength, this.length); + checkOffset(this, offset, byteLength, this.length); var val = this[offset]; var mul = 1; @@ -687,7 +777,7 @@ Buffer.prototype.readIntBE = function(offset, byteLength, noAssert) { offset = offset >>> 0; byteLength = byteLength >>> 0; if (!noAssert) - checkOffset(offset, byteLength, this.length); + checkOffset(this, offset, byteLength, this.length); var i = byteLength; var mul = 1; @@ -706,7 +796,7 @@ Buffer.prototype.readIntBE = function(offset, byteLength, noAssert) { Buffer.prototype.readInt8 = function(offset, noAssert) { offset = offset >>> 0; if (!noAssert) - checkOffset(offset, 1, this.length); + checkOffset(this, offset, 1, this.length); var val = this[offset]; return !(val & 0x80) ? val : (0xff - val + 1) * -1; }; @@ -715,7 +805,7 @@ Buffer.prototype.readInt8 = function(offset, noAssert) { Buffer.prototype.readInt16LE = function(offset, noAssert) { offset = offset >>> 0; if (!noAssert) - checkOffset(offset, 2, this.length); + checkOffset(this, offset, 2, this.length); var val = this[offset] | (this[offset + 1] << 8); return (val & 0x8000) ? val | 0xFFFF0000 : val; }; @@ -724,7 +814,7 @@ Buffer.prototype.readInt16LE = function(offset, noAssert) { Buffer.prototype.readInt16BE = function(offset, noAssert) { offset = offset >>> 0; if (!noAssert) - checkOffset(offset, 2, this.length); + checkOffset(this, offset, 2, this.length); var val = this[offset + 1] | (this[offset] << 8); return (val & 0x8000) ? val | 0xFFFF0000 : val; }; @@ -733,7 +823,7 @@ Buffer.prototype.readInt16BE = function(offset, noAssert) { Buffer.prototype.readInt32LE = function(offset, noAssert) { offset = offset >>> 0; if (!noAssert) - checkOffset(offset, 4, this.length); + checkOffset(this, offset, 4, this.length); return (this[offset]) | (this[offset + 1] << 8) | @@ -745,7 +835,7 @@ Buffer.prototype.readInt32LE = function(offset, noAssert) { Buffer.prototype.readInt32BE = function(offset, noAssert) { offset = offset >>> 0; if (!noAssert) - checkOffset(offset, 4, this.length); + checkOffset(this, offset, 4, this.length); return (this[offset] << 24) | (this[offset + 1] << 16) | @@ -757,7 +847,7 @@ Buffer.prototype.readInt32BE = function(offset, noAssert) { Buffer.prototype.readFloatLE = function readFloatLE(offset, noAssert) { offset = offset >>> 0; if (!noAssert) - checkOffset(offset, 4, this.length); + checkOffset(this, offset, 4, this.length); return binding.readFloatLE(this, offset); }; @@ -765,7 +855,7 @@ Buffer.prototype.readFloatLE = function readFloatLE(offset, noAssert) { Buffer.prototype.readFloatBE = function readFloatBE(offset, noAssert) { offset = offset >>> 0; if (!noAssert) - checkOffset(offset, 4, this.length); + checkOffset(this, offset, 4, this.length); return binding.readFloatBE(this, offset); }; @@ -773,7 +863,7 @@ Buffer.prototype.readFloatBE = function readFloatBE(offset, noAssert) { Buffer.prototype.readDoubleLE = function readDoubleLE(offset, noAssert) { offset = offset >>> 0; if (!noAssert) - checkOffset(offset, 8, this.length); + checkOffset(this, offset, 8, this.length); return binding.readDoubleLE(this, offset); }; @@ -781,7 +871,7 @@ Buffer.prototype.readDoubleLE = function readDoubleLE(offset, noAssert) { Buffer.prototype.readDoubleBE = function readDoubleBE(offset, noAssert) { offset = offset >>> 0; if (!noAssert) - checkOffset(offset, 8, this.length); + checkOffset(this, offset, 8, this.length); return binding.readDoubleBE(this, offset); }; diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 61b80c622a809e..ab853f85ab63cc 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -19,7 +19,7 @@ } while (0) #define ARGS_THIS(argT) \ - Local obj = argT; \ + Local obj = argT.As(); \ size_t obj_length = obj->GetIndexedPropertiesExternalArrayDataLength(); \ char* obj_data = static_cast( \ obj->GetIndexedPropertiesExternalArrayData()); \ @@ -223,8 +223,8 @@ template void StringSlice(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - ARGS_THIS(args.This()) - SLICE_START_END(args[0], args[1], obj_length) + ARGS_THIS(args[0]) + SLICE_START_END(args[1], args[2], obj_length) args.GetReturnValue().Set( StringBytes::Encode(env->isolate(), obj_data + start, length, encoding)); @@ -235,8 +235,8 @@ template <> void StringSlice(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - ARGS_THIS(args.This()) - SLICE_START_END(args[0], args[1], obj_length) + ARGS_THIS(args[0]) + SLICE_START_END(args[1], args[2], obj_length) length /= 2; const char* data = obj_data + start; @@ -299,16 +299,13 @@ void Base64Slice(const FunctionCallbackInfo& args) { } -// bytesCopied = buffer.copy(target[, targetStart][, sourceStart][, sourceEnd]); +// bytesCopied = copy(buffer, target[, targetStart][, sourceStart][, sourceEnd]) void Copy(const FunctionCallbackInfo &args) { Environment* env = Environment::GetCurrent(args); - Local target = args[0]->ToObject(env->isolate()); + ARGS_THIS(args[0]) - if (!HasInstance(target)) - return env->ThrowTypeError("first arg should be a Buffer"); - - ARGS_THIS(args.This()) + Local target = args[1].As(); size_t target_length = target->GetIndexedPropertiesExternalArrayDataLength(); char* target_data = static_cast( target->GetIndexedPropertiesExternalArrayData()); @@ -316,9 +313,9 @@ void Copy(const FunctionCallbackInfo &args) { size_t source_start; size_t source_end; - CHECK_NOT_OOB(ParseArrayIndex(args[1], 0, &target_start)); - CHECK_NOT_OOB(ParseArrayIndex(args[2], 0, &source_start)); - CHECK_NOT_OOB(ParseArrayIndex(args[3], obj_length, &source_end)); + CHECK_NOT_OOB(ParseArrayIndex(args[2], 0, &target_start)); + CHECK_NOT_OOB(ParseArrayIndex(args[3], 0, &source_start)); + CHECK_NOT_OOB(ParseArrayIndex(args[4], obj_length, &source_end)); // Copy 0 bytes; we're done if (target_start >= target_length || source_start >= source_end) @@ -340,7 +337,7 @@ void Copy(const FunctionCallbackInfo &args) { void Fill(const FunctionCallbackInfo& args) { - ARGS_THIS(args[0].As()) + ARGS_THIS(args[0]); size_t start = args[2]->Uint32Value(); size_t end = args[3]->Uint32Value(); @@ -383,21 +380,15 @@ template void StringWrite(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - ARGS_THIS(args.This()) - - if (!args[0]->IsString()) - return env->ThrowTypeError("Argument must be a string"); + ARGS_THIS(args[0]) - Local str = args[0]->ToString(env->isolate()); - - if (encoding == HEX && str->Length() % 2 != 0) - return env->ThrowTypeError("Invalid hex string"); + Local str = args[1].As(); size_t offset; size_t max_length; - CHECK_NOT_OOB(ParseArrayIndex(args[1], 0, &offset)); - CHECK_NOT_OOB(ParseArrayIndex(args[2], obj_length - offset, &max_length)); + CHECK_NOT_OOB(ParseArrayIndex(args[2], 0, &offset)); + CHECK_NOT_OOB(ParseArrayIndex(args[3], obj_length - offset, &max_length)); max_length = MIN(obj_length - offset, max_length); @@ -459,7 +450,7 @@ static inline void Swizzle(char* start, unsigned int len) { template void ReadFloatGeneric(const FunctionCallbackInfo& args) { - ARGS_THIS(args[0].As()); + ARGS_THIS(args[0]); uint32_t offset = args[1]->Uint32Value(); CHECK_LE(offset + sizeof(T), obj_length); @@ -500,8 +491,8 @@ void ReadDoubleBE(const FunctionCallbackInfo& args) { template -uint32_t WriteFloatGeneric(const FunctionCallbackInfo& args) { - ARGS_THIS(args[0].As()) +void WriteFloatGeneric(const FunctionCallbackInfo& args) { + ARGS_THIS(args[0]); T val = args[1]->NumberValue(); uint32_t offset = args[2]->Uint32Value(); @@ -517,27 +508,28 @@ uint32_t WriteFloatGeneric(const FunctionCallbackInfo& args) { if (endianness != GetEndianness()) Swizzle(na.bytes, sizeof(na.bytes)); memcpy(ptr, na.bytes, sizeof(na.bytes)); - return offset + sizeof(na.bytes); + + args.GetReturnValue().Set(static_cast(offset + sizeof(na.bytes))); } void WriteFloatLE(const FunctionCallbackInfo& args) { - args.GetReturnValue().Set(WriteFloatGeneric(args)); + WriteFloatGeneric(args); } void WriteFloatBE(const FunctionCallbackInfo& args) { - args.GetReturnValue().Set(WriteFloatGeneric(args)); + WriteFloatGeneric(args); } void WriteDoubleLE(const FunctionCallbackInfo& args) { - args.GetReturnValue().Set(WriteFloatGeneric(args)); + WriteFloatGeneric(args); } void WriteDoubleBE(const FunctionCallbackInfo& args) { - args.GetReturnValue().Set(WriteFloatGeneric(args)); + WriteFloatGeneric(args); } @@ -605,11 +597,7 @@ int32_t IndexOf(const char* haystack, void IndexOfString(const FunctionCallbackInfo& args) { - ASSERT(args[0]->IsObject()); - ASSERT(args[1]->IsString()); - ASSERT(args[2]->IsNumber()); - - ARGS_THIS(args[0].As()); + ARGS_THIS(args[0]); node::Utf8Value str(args.GetIsolate(), args[1]); int32_t offset_i32 = args[2]->Int32Value(); uint32_t offset; @@ -636,11 +624,7 @@ void IndexOfString(const FunctionCallbackInfo& args) { void IndexOfBuffer(const FunctionCallbackInfo& args) { - ASSERT(args[0]->IsObject()); - ASSERT(args[1]->IsObject()); - ASSERT(args[2]->IsNumber()); - - ARGS_THIS(args[0].As()); + ARGS_THIS(args[0]); Local buf = args[1].As(); int32_t offset_i32 = args[2]->Int32Value(); size_t buf_length = buf->GetIndexedPropertiesExternalArrayDataLength(); @@ -673,11 +657,7 @@ void IndexOfBuffer(const FunctionCallbackInfo& args) { void IndexOfNumber(const FunctionCallbackInfo& args) { - ASSERT(args[0]->IsObject()); - ASSERT(args[1]->IsNumber()); - ASSERT(args[2]->IsNumber()); - - ARGS_THIS(args[0].As()); + ARGS_THIS(args[0]); uint32_t needle = args[1]->Uint32Value(); int32_t offset_i32 = args[2]->Int32Value(); uint32_t offset; @@ -715,22 +695,6 @@ void SetupBufferJS(const FunctionCallbackInfo& args) { Local proto = proto_v.As(); - env->SetMethod(proto, "asciiSlice", AsciiSlice); - env->SetMethod(proto, "base64Slice", Base64Slice); - env->SetMethod(proto, "binarySlice", BinarySlice); - env->SetMethod(proto, "hexSlice", HexSlice); - env->SetMethod(proto, "ucs2Slice", Ucs2Slice); - env->SetMethod(proto, "utf8Slice", Utf8Slice); - - env->SetMethod(proto, "asciiWrite", AsciiWrite); - env->SetMethod(proto, "base64Write", Base64Write); - env->SetMethod(proto, "binaryWrite", BinaryWrite); - env->SetMethod(proto, "hexWrite", HexWrite); - env->SetMethod(proto, "ucs2Write", Ucs2Write); - env->SetMethod(proto, "utf8Write", Utf8Write); - - env->SetMethod(proto, "copy", Copy); - // for backwards compatibility proto->ForceSet(env->offset_string(), Uint32::New(env->isolate(), 0), @@ -761,6 +725,22 @@ void Initialize(Handle target, env->SetMethod(target, "writeDoubleLE", WriteDoubleLE); env->SetMethod(target, "writeFloatBE", WriteFloatBE); env->SetMethod(target, "writeFloatLE", WriteFloatLE); + + env->SetMethod(target, "copy", Copy); + + env->SetMethod(target, "asciiSlice", AsciiSlice); + env->SetMethod(target, "base64Slice", Base64Slice); + env->SetMethod(target, "binarySlice", BinarySlice); + env->SetMethod(target, "hexSlice", HexSlice); + env->SetMethod(target, "ucs2Slice", Ucs2Slice); + env->SetMethod(target, "utf8Slice", Utf8Slice); + + env->SetMethod(target, "asciiWrite", AsciiWrite); + env->SetMethod(target, "base64Write", Base64Write); + env->SetMethod(target, "binaryWrite", BinaryWrite); + env->SetMethod(target, "hexWrite", HexWrite); + env->SetMethod(target, "ucs2Write", Ucs2Write); + env->SetMethod(target, "utf8Write", Utf8Write); } diff --git a/test/parallel/test-buffer.js b/test/parallel/test-buffer.js index 9b16b7b028476a..73c7b8d9a9f909 100644 --- a/test/parallel/test-buffer.js +++ b/test/parallel/test-buffer.js @@ -1179,3 +1179,35 @@ var ps = Buffer.poolSize; Buffer.poolSize = 0; assert.equal(Buffer(1).parent, undefined); Buffer.poolSize = ps; + +// GH-1485. Do not core dump when `this` is a non-buffer. +assert.equal(Buffer.prototype.toString(), "[object Object]"); + +[null, undefined, {}, 0].forEach(function(v) { + assert.throws(function() { + Buffer.prototype.write.call(v); + }); + + assert.throws(function() { + Buffer.prototype.copy.call(v, new Buffer(0)); + }); + assert.throws(function() { + (new Buffer(0)).copy(v); + }); + + assert.throws(function() { + Buffer.prototype.fill.call(v); + }); + + assert.throws(function() { + Buffer.prototype.indexOf.call(v, 0); + }); + + assert.throws(function() { + Buffer.prototype.readDoubleLE.call(v, 0, false); + }); + + assert.throws(function() { + Buffer.prototype.writeDoubleLE.call(v, 0, 0, false); + }); +});