Prevent RED.node.registerNode from overriding a constructor's prototype (#865)

* prevent registry.registerNodeConstructor from overriding a constructors protoype

* fix for node < v5.0.0

* exercise another code path

* altering __proto__ for node < v0.12

* move inheritance code to helper function
This commit is contained in:
Gabe Johnson 2016-04-07 16:18:28 -05:00 committed by Nick O'Leary
parent e1d09349ff
commit b909e32201
3 changed files with 56 additions and 6 deletions

View File

@ -9,5 +9,6 @@
//"unused": true, // Check for unused functions and variables //"unused": true, // Check for unused functions and variables
"loopfunc": true, // allow functions to be defined in loops "loopfunc": true, // allow functions to be defined in loops
//"expr": true, // allow ternery operator syntax... //"expr": true, // allow ternery operator syntax...
"sub": true // don't warn that foo['bar'] should be written as foo.bar "sub": true, // don't warn that foo['bar'] should be written as foo.bar
"proto": true // allow setting of __proto__ in node < v0.12
} }

View File

@ -326,6 +326,27 @@ function getCaller(){
return stack[0].getFileName(); return stack[0].getFileName();
} }
function inheritNode(constructor) {
if(Object.getPrototypeOf(constructor.prototype) === Object.prototype) {
util.inherits(constructor,Node);
} else {
var proto = constructor.prototype;
while(Object.getPrototypeOf(proto) !== Object.prototype) {
proto = Object.getPrototypeOf(proto);
}
//TODO: This is a partial implementation of util.inherits >= node v5.0.0
// which should be changed when support for node < v5.0.0 is dropped
// see: https://github.com/nodejs/node/pull/3455
proto.constructor.super_ = Node;
if(Object.setPrototypeOf) {
Object.setPrototypeOf(proto, Node.prototype);
} else {
// hack for node v0.10
proto.__proto__ = Node.prototype;
}
}
}
function registerNodeConstructor(type,constructor) { function registerNodeConstructor(type,constructor) {
if (nodeConstructors[type]) { if (nodeConstructors[type]) {
throw new Error(type+" already registered"); throw new Error(type+" already registered");
@ -333,7 +354,10 @@ function registerNodeConstructor(type,constructor) {
//TODO: Ensure type is known - but doing so will break some tests //TODO: Ensure type is known - but doing so will break some tests
// that don't have a way to register a node template ahead // that don't have a way to register a node template ahead
// of registering the constructor // of registering the constructor
util.inherits(constructor,Node); if(!(constructor.prototype instanceof Node)) {
inheritNode(constructor);
}
nodeConstructors[type] = constructor; nodeConstructors[type] = constructor;
events.emit("type-registered",type); events.emit("type-registered",type);
} }

View File

@ -20,8 +20,12 @@ var sinon = require("sinon");
var typeRegistry = require("../../../../../red/runtime/nodes/registry/registry"); var typeRegistry = require("../../../../../red/runtime/nodes/registry/registry");
var Node = require("../../../../../red/runtime/nodes/Node");
var events = require("../../../../../red/runtime/events"); var events = require("../../../../../red/runtime/events");
var inherits = require("util").inherits;
describe("red/nodes/registry/registry",function() { describe("red/nodes/registry/registry",function() {
afterEach(function() { afterEach(function() {
@ -428,9 +432,10 @@ describe("red/nodes/registry/registry",function() {
}); });
describe('#registerNodeConstructor', function() { describe('#registerNodeConstructor', function() {
function TestNodeConstructor() { var TestNodeConstructor;
}
beforeEach(function() { beforeEach(function() {
TestNodeConstructor = function TestNodeConstructor() {
};
sinon.stub(events,'emit'); sinon.stub(events,'emit');
}); });
afterEach(function() { afterEach(function() {
@ -452,7 +457,27 @@ describe("red/nodes/registry/registry",function() {
typeRegistry.registerNodeConstructor('node-type',TestNodeConstructor); typeRegistry.registerNodeConstructor('node-type',TestNodeConstructor);
}).should.throw("node-type already registered"); }).should.throw("node-type already registered");
events.emit.calledOnce.should.be.true; events.emit.calledOnce.should.be.true;
}) });
it('extends a constructor with the Node constructor', function() {
TestNodeConstructor.prototype.should.not.be.an.instanceOf(Node);
typeRegistry.registerNodeConstructor('node-type',TestNodeConstructor);
TestNodeConstructor.prototype.should.be.an.instanceOf(Node);
});
it('does not override a constructor\'s prototype', function() {
function Foo(){};
inherits(TestNodeConstructor,Foo);
TestNodeConstructor.prototype.should.be.an.instanceOf(Foo);
TestNodeConstructor.prototype.should.not.be.an.instanceOf(Node);
typeRegistry.registerNodeConstructor('node-type',TestNodeConstructor);
TestNodeConstructor.prototype.should.be.an.instanceOf(Node);
TestNodeConstructor.prototype.should.be.an.instanceOf(Foo);
typeRegistry.registerNodeConstructor('node-type2',TestNodeConstructor);
TestNodeConstructor.prototype.should.be.an.instanceOf(Node);
TestNodeConstructor.prototype.should.be.an.instanceOf(Foo);
});
}); });
}); });