Repository navigation
How to inherit from new Buffer implementation #2882
Description
Activity
- addedquestionIssues asking questions about Node.js.Issues asking questions about Node.js.
on Sep 15, 2015 - addedbufferIssues and PRs related to the buffer subsystem.Issues and PRs related to the buffer subsystem.
on Sep 15, 2015 ES6
extendsshould work:> class C extends Buffer { constructor(size) { super(size) } } [Function: C] > (new C(32)).length 32Thanks @bnoordhuis ,
I know it can be seen as going backward, but is there a way in ES5 too?
I think that's going to be less easy for the same reason that it's difficult to inherit from Array or Uint8Array in ES5. You can maybe hack something together through the
__proto__property.Try something like this:
function Ogg() { const ui = new Uint8Array(size); Object.setPrototypeOf(ui, Ogg.prototype); return ui; } Ogg.prototype.__proto__ = Buffer.prototype; Ogg.__proto__ = Buffer;
Yeah, ugly as sin.
This should probably go into the buffer docs.
- addeddocIssues and PRs related to Node.js documentation.Issues and PRs related to Node.js documentation.
on Sep 15, 2015 Thanks a lot for your answers,
@trevnorris :
I succeed to create an instance this way thanks to your help:function ogg_packet (buffer) { if (!Buffer.isBuffer(buffer)) { buffer = new Uint8Array(binding.sizeof_ogg_packet); } if (buffer.length != binding.sizeof_ogg_packet) { throw new Error('"buffer.length" = ' + buffer.length + ', expected ' + binding.sizeof_ogg_packet); } Object.setPrototypeOf(buffer, ogg_packet.prototype); console.log('BUFFER', buffer.toString()); return buffer; } ogg_packet.prototype.__proto__ = Buffer.prototype; ogg_packet.__proto__ = Buffer;
However, it seems that I can't use Buffer methods (at least toString()) since there is a strict equality check of the instance. For example, trying to print buffer.toString() in the constructor function give me that error:
Uncaught TypeError: argument should be a Buffer at TypeError (native) at ogg_packet.Buffer.toString (buffer.js:354:23)@corentingurtner That would be from the
node::Buffer::HasInstance()check. It's strictly checking the hidden class. Unfortunately there's no way I'm aware of to do proto transversal in the C++ API, similar toinstanceof./cc @domenic Know of a way to do this proto transversal check in C++?
If there's not a quick fix then let's open a new issue for this.
@trevnorris you can use v8::Object::GetPrototype(). Something like
objLocal->GetPrototype()->Get(context, v8_str("constructor"))to get the constructor and then check withEqualsagainst the constructor.What you really want though is to check that the appropriate internal data is set on the object. Which I think just would be
val->IsUint8Array(). Otherwise I can fool it with crap likevar notABuffer = {}; notABuffer.__proto__ = Buffer.prototype.@domenic ah yup. sure enough. when I had to reimplement the check I was focused on not allowing users to bypass the
instanceofcheck in JS.I agree that the check should be simplified down to checking internal data. All methods will work from that point regardless of whether it's actually a Buffer instance. I'll make a PR.
+1 This stops me from migrating to 4.x.
@bnoordhuis using classes won't work:
'use strict'; class Foo extends Buffer { constructor(n) { super(n); } foo() { let cntr = 0; for (let i = 0; i < this.length; i++) { cntr += this[i]; } return cntr; } } let foo = new Foo(17).fill('abc'); console.log(foo.foo()); // TypeError: foo.foo is not a function
We are overwriting the prototype in the Buffer constructor, and loosing all the child's class methods. Want to examine this a bit more.
This is fixable once new.target ships in V8. You'd use new.target.prototype instead of Buffer.prototype.
We'll be able to fix this with
new.target(only from V8 4.6):node [vee-eight-4.6●] % more test.js 'use strict'; class A { constructor() { console.log(new.target); } } class B extends A {} new A(); new B();node [vee-eight-4.6●] % ./node test [Function: A] [Function: B]This still requires that
HasInstance()basically becomes anIsUint8Array()check.@domenic The inconsistency must come from from mixing ES5 and ES6 style inheritance together. Take this example:
class B extends Uint8Array { constructor() { super(0); } } class C extends B { constructor() { super(); } } let c = new C(); console.log(c.constructor == C); console.log(c.__proto__ == C.prototype); console.log(c.__proto__.__proto__ == B.prototype); console.log(c.__proto__.__proto__.__proto__ == Uint8Array.prototype);
But now doing the same thing with Buffer:
class C extends Buffer { constructor() { super(0); } } let c = new C(); console.log(c.constructor == Buffer); console.log(c.__proto__ == Buffer.prototype); console.log(c.__proto__.__proto__ == Uint8Array.prototype);
As you can see it completely dropped anything related to
C.What I'm missing is what
new.targetcould help with.@trevnorris right now
allocateis doingObject.setPrototypeOf(ui8, Buffer.prototype), so even if someone doesnew C(), you are returning to them something withBuffer.prototype, notC.prototype.To fix this, you need to pass
new.targetfrom theBufferconstructor definition, toallocate, so thatallocatecan doObject.setPrototypeOf(ui8, newTarget.prototype).Then the returned object will have the correct prototype. In this case it would be
C.prototype, since the target ofnewinnew C()isC.- added a commit that references this issue
on Oct 6, 2015 - added a commit that references this issue
on Oct 8, 2015 - added a commit that references this issue
on Jul 27, 2026
Hi,
I would like to know how a class could inherit from the new Buffer implementation.
I'm trying to upgrade the node-ogg module for node v4 and nan v2.
(Here is my work so far)
I tried this classic inheritance model:
But I get this error when trying to access this.length :
Thanks
Note: I also asked the question on SO:
http://stackoverflow.com/questions/32555714