Permalink
Browse files

Ranges and names support for reserved fields, see #676

  • Loading branch information...
dcodeIO committed Feb 24, 2017
1 parent 487f892 commit cbd4c622912688b47658fea00fd53603049b5104

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.

Large diffs are not rendered by default.

Oops, something went wrong.

Large diffs are not rendered by default.

Oops, something went wrong.
BIN +94 Bytes (100%) dist/light/protobuf.min.js.gz
Binary file not shown.

Large diffs are not rendered by default.

Oops, something went wrong.

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
BIN -1 Byte (100%) dist/minimal/protobuf.min.js.gz
Binary file not shown.

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.

Large diffs are not rendered by default.

Oops, something went wrong.

Large diffs are not rendered by default.

Oops, something went wrong.
BIN +113 Bytes (100%) dist/protobuf.min.js.gz
Binary file not shown.

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -1673,9 +1673,9 @@ export class Type extends NamespaceBase {
/**
* Reserved ranges, if any.
* @type {number[][]}
* @type {Array.<number[]|string>}
*/
reserved: number[][];
reserved: (number[]|string)[];
/**
* Message fields by id.
@@ -1726,6 +1726,20 @@ export class Type extends NamespaceBase {
*/
remove(object: ReflectionObject): Type;
/**
* Tests if the specified id is reserved.
* @param {number} id Id to test
* @returns {boolean} `true` if reserved, otherwise `false`
*/
isReservedId(id: number): boolean;
/**
* Tests if the specified name is reserved.
* @param {string} name Name to test
* @returns {boolean} `true` if reserved, otherwise `false`
*/
isReservedName(name: string): boolean;
/**
* Creates a new message of this type using the specified properties.
* @param {Object.<string,*>} [properties] Properties to set
@@ -138,13 +138,15 @@ function parse(source, root, options) {
}
}
function readRange() {
var start = parseId(next());
var end = start;
if (skip("to", true))
end = parseId(next());
function readRanges(target, acceptStrings) {
var token, start;
do {
if (acceptStrings && ((token = peek()) === "\"" || token === "'"))
target.push(readString());
else
target.push([ start = parseId(next()), skip("to", true) ? parseId(next()) : start ]);
} while (skip(",", true));
skip(";");
return [ start, end ];
}
function parseNumber(token, insideTryCatch) {
@@ -290,11 +292,11 @@ function parse(source, root, options) {
break;
case "extensions":
(type.extensions || (type.extensions = [])).push(readRange(type, tokenLower));
readRanges(type.extensions || (type.extensions = []));
break;
case "reserved":
(type.reserved || (type.reserved = [])).push(readRange(type, tokenLower));
readRanges(type.reserved || (type.reserved = []), true);
break;
default:
@@ -96,7 +96,7 @@ function Type(name, options) {
/**
* Reserved ranges, if any.
* @type {number[][]}
* @type {Array.<number[]|string>}
*/
this.reserved = undefined; // toJSON
@@ -273,7 +273,11 @@ Type.prototype.add = function add(object) {
// avoids calling the getter if not absolutely necessary because it's called quite frequently
if (this._fieldsById ? /* istanbul ignore next */ this._fieldsById[object.id] : this.fieldsById[object.id])
throw Error("duplicate id " + object.id + " in " + this);
if (this.isReservedId(object.id))
throw Error("id " + object.id + " is reserved in " + this);
if (this.isReservedName(object.name))
throw Error("name '" + object.name + "' is reserved in " + this);
if (object.parent)
object.parent.remove(object);
this.fields[object.name] = object;
@@ -321,6 +325,32 @@ Type.prototype.remove = function remove(object) {
return Namespace.prototype.remove.call(this, object);
};
/**
* Tests if the specified id is reserved.
* @param {number} id Id to test
* @returns {boolean} `true` if reserved, otherwise `false`
*/
Type.prototype.isReservedId = function isReservedId(id) {
if (this.reserved)
for (var i = 0; i < this.reserved.length; ++i)
if (typeof this.reserved[i] !== "string" && this.reserved[i][0] <= id && this.reserved[i][1] >= id)
return true;
return false;
};
/**
* Tests if the specified name is reserved.
* @param {string} name Name to test
* @returns {boolean} `true` if reserved, otherwise `false`
*/
Type.prototype.isReservedName = function isReservedName(name) {
if (this.reserved)
for (var i = 0; i < this.reserved.length; ++i)
if (this.reserved[i] === name)
return true;
return false;
};
/**
* Creates a new message of this type using the specified properties.
* @param {Object.<string,*>} [properties] Properties to set
@@ -24,8 +24,8 @@ var def2 = {
oneof: ["a"]
}
},
extensions: [1000, 2000],
reserved: [999],
extensions: [[1000, 2000]],
reserved: [[900, 999], "b"],
nested: {
Type: {
values: { ONE: 1, TWO: 2 }
@@ -65,6 +65,7 @@ tape.test("reflected types", function(test) {
id: 1
}
},
reserved: [[900, 999], "b"],
nested: {
Type: { fields: {} },
Enum: { values: {} },
@@ -79,7 +80,7 @@ tape.test("reflected types", function(test) {
},
oneofs: undefined,
extensions: undefined,
reserved: undefined,
reserved: [[900, 999], "b"],
group: undefined,
nested: {
Type: { extensions: undefined, fields: {}, group: undefined, nested: undefined, oneofs: undefined, options: undefined, reserved: undefined },
@@ -99,5 +100,14 @@ tape.test("reflected types", function(test) {
type.add(new protobuf.Field("c", 1, "uint32"));
}, Error, "should throw when trying to add duplicate ids");
test.throws(function() {
type.add(new protobuf.Field("c", 900, "uint32"));
}, Error, "should throw when trying to add reserved ids");
test.throws(function() {
type.add(new protobuf.Field("b", 2, "uint32"));
}, Error, "should throw when trying to add reserved names");
test.end();
});
@@ -7,6 +7,7 @@ message A {\
message B {\
message One {\
extensions 1000 to max;\
reserved 900 to 999, 899, \"a\", 'b';\
}\
}\
message C {\

0 comments on commit cbd4c62

Please sign in to comment.