Skip to content

Commit 9c9f8ee

Browse files
authored
fix: Remove renamed reflection objects by identity (#2324)
1 parent de18bbe commit 9c9f8ee

6 files changed

Lines changed: 55 additions & 5 deletions

File tree

index.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2666,6 +2666,15 @@ export namespace util {
26662666
*/
26672667
function toObject(array: any[]): { [k: string]: any };
26682668

2669+
/**
2670+
* Removes the first matching value from an object.
2671+
* @param object Object to remove from
2672+
* @param value Value to remove
2673+
* @param [key] Optional key for fast path removal
2674+
* @returns `true` if removed, otherwise `false`
2675+
*/
2676+
function remove(object: ({ [k: string]: any }|undefined), value: any, key?: string): boolean;
2677+
26692678
/**
26702679
* Tests whether the specified name is a reserved word in JS.
26712680
* @param name Name to test

src/namespace.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,8 @@ Namespace.prototype.remove = function remove(object) {
321321
if (object.parent !== this)
322322
throw Error(object + " is not a member of " + this);
323323

324-
delete this.nested[object.name];
324+
if (!util.remove(this.nested, object, object.name))
325+
throw Error(object + " is not a member of " + this);
325326
if (!Object.keys(this.nested).length)
326327
this.nested = undefined;
327328

src/type.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -425,21 +425,19 @@ Type.prototype.remove = function remove(object) {
425425
// See Type#add for the reason why extension fields are excluded here.
426426

427427
/* istanbul ignore if */
428-
if (!this.fields || this.fields[object.name] !== object)
428+
if (!util.remove(this.fields, object, object.name))
429429
throw Error(object + " is not a member of " + this);
430430

431-
delete this.fields[object.name];
432431
object.parent = null;
433432
object.onRemove(this);
434433
return clearCache(this);
435434
}
436435
if (object instanceof OneOf) {
437436

438437
/* istanbul ignore if */
439-
if (!this.oneofs || this.oneofs[object.name] !== object)
438+
if (!util.remove(this.oneofs, object, object.name))
440439
throw Error(object + " is not a member of " + this);
441440

442-
delete this.oneofs[object.name];
443441
object.parent = null;
444442
object.onRemove(this);
445443
return clearCache(this);

src/util.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,28 @@ util.toObject = function toObject(array) {
5858
return object;
5959
};
6060

61+
/**
62+
* Removes the first matching value from an object.
63+
* @param {Object.<string,*>|undefined} object Object to remove from
64+
* @param {*} value Value to remove
65+
* @param {string} [key] Optional key for fast path removal
66+
* @returns {boolean} `true` if removed, otherwise `false`
67+
*/
68+
util.remove = function remove(object, value, key) {
69+
if (!object)
70+
return false;
71+
if (key !== undefined && Object.prototype.hasOwnProperty.call(object, key) && object[key] === value) {
72+
delete object[key];
73+
return true;
74+
}
75+
for (var names = Object.keys(object), i = 0; i < names.length; ++i)
76+
if (object[names[i]] === value) {
77+
delete object[names[i]];
78+
return true;
79+
}
80+
return false;
81+
};
82+
6183
/**
6284
* Tests whether the specified name is a reserved word in JS.
6385
* @param {string} name Name to test

tests/api_namespace.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,13 @@ message TestMessage {\
131131
ns.remove(new protobuf.Enum("Enm"));
132132
}, Error, "should throw when trying to remove non-children");
133133

134+
var renamed = new protobuf.Type("RenamedBefore");
135+
ns.add(renamed);
136+
renamed.name = "RenamedAfter";
137+
ns.remove(renamed);
138+
test.equal(renamed.parent, null, "should remove renamed nested objects");
139+
test.equal(ns.get("RenamedBefore"), null, "should remove renamed nested objects from the original key");
140+
134141
test.throws(function() {
135142
ns.add(new protobuf.Enum("MyEnum", {}));
136143
ns.define("MyEnum");

tests/api_type.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,19 @@ tape.test("reflected types", function(test) {
116116
type.add(new protobuf.Field("b", 2, "uint32"));
117117
}, Error, "should throw when trying to add reserved names");
118118

119+
var renameType = new protobuf.Type("Rename");
120+
var field = new protobuf.Field("before", 1, "string");
121+
var oneof = new protobuf.OneOf("choice");
122+
renameType.add(field);
123+
renameType.add(oneof);
124+
field.name = "after";
125+
oneof.name = "selection";
126+
renameType.remove(field);
127+
renameType.remove(oneof);
128+
test.equal(field.parent, null, "should remove renamed fields");
129+
test.equal(oneof.parent, null, "should remove renamed oneofs");
130+
test.same(renameType.fields, {}, "should remove renamed fields from the original key");
131+
test.same(renameType.oneofs, {}, "should remove renamed oneofs from the original key");
119132

120133
test.end();
121134
});

0 commit comments

Comments
 (0)