Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -714,10 +714,11 @@ export class Namespace extends NamespaceBase {
* Constructs a namespace from JSON.
* @param name Namespace name
* @param json JSON object
* @param [depth] Current nesting depth, defaults to `0`
* @returns Created namespace
* @throws {TypeError} If arguments are invalid
*/
public static fromJSON(name: string, json: { [k: string]: any }): Namespace;
public static fromJSON(name: string, json: { [k: string]: any }, depth?: number): Namespace;

/**
* Converts an array of reflection objects to JSON.
Expand Down Expand Up @@ -769,9 +770,10 @@ export abstract class NamespaceBase extends ReflectionObject {
/**
* Adds nested objects to this namespace from nested object descriptors.
* @param nestedJson Any nested object descriptors
* @param [depth] Current nesting depth, defaults to `0`
* @returns `this`
*/
public addJSON(nestedJson: { [k: string]: AnyNestedObject }): Namespace;
public addJSON(nestedJson: { [k: string]: AnyNestedObject }, depth?: number): Namespace;

/**
* Gets the nested object of the specified name.
Expand Down Expand Up @@ -1318,9 +1320,10 @@ export class Root extends NamespaceBase {
* Loads a namespace descriptor into a root namespace.
* @param json Namespace descriptor
* @param [root] Root namespace, defaults to create a new one if omitted
* @param [depth] Current nesting depth, defaults to `0`
* @returns Root namespace
*/
public static fromJSON(json: INamespace, root?: Root): Root;
public static fromJSON(json: INamespace, root?: Root, depth?: number): Root;

/**
* Resolves the path of an imported file, relative to the importing origin.
Expand Down Expand Up @@ -1471,10 +1474,11 @@ export class Service extends NamespaceBase {
* Constructs a service from a service descriptor.
* @param name Service name
* @param json Service descriptor
* @param [depth] Current nesting depth, defaults to `0`
* @returns Created service
* @throws {TypeError} If arguments are invalid
*/
public static fromJSON(name: string, json: IService): Service;
public static fromJSON(name: string, json: IService, depth?: number): Service;

/**
* Converts this service to a service descriptor.
Expand Down Expand Up @@ -1625,9 +1629,10 @@ export class Type extends NamespaceBase {
* Creates a message type from a message type descriptor.
* @param name Message name
* @param json Message type descriptor
* @param [depth] Current nesting depth, defaults to `0`
* @returns Created message type
*/
public static fromJSON(name: string, json: IType): Type;
public static fromJSON(name: string, json: IType, depth?: number): Type;

/**
* Converts this message type to a message type descriptor.
Expand Down Expand Up @@ -2199,6 +2204,14 @@ export namespace util {
/** Node's fs module if available. */
let fs: { [k: string]: any };

/**
* Checks a recursion depth.
* @param depth Depth of recursion
* @returns Depth of recursion
* @throws {Error} If depth exceeds util.recursionLimit
*/
function checkDepth(depth: (number|undefined)): number;

/**
* Converts an object's values to an array.
* @param object Object to convert
Expand Down
12 changes: 8 additions & 4 deletions src/namespace.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ var Type, // cyclic
* @function
* @param {string} name Namespace name
* @param {Object.<string,*>} json JSON object
* @param {number} [depth] Current nesting depth, defaults to `0`
* @returns {Namespace} Created namespace
* @throws {TypeError} If arguments are invalid
*/
Namespace.fromJSON = function fromJSON(name, json) {
return new Namespace(name, json.options).addJSON(json.nested);
Namespace.fromJSON = function fromJSON(name, json, depth) {
depth = util.checkDepth(depth);
return new Namespace(name, json.options).addJSON(json.nested, depth);
};

/**
Expand Down Expand Up @@ -191,9 +193,11 @@ Namespace.prototype.toJSON = function toJSON(toJSONOptions) {
/**
* Adds nested objects to this namespace from nested object descriptors.
* @param {Object.<string,AnyNestedObject>} nestedJson Any nested object descriptors
* @param {number} [depth] Current nesting depth, defaults to `0`
* @returns {Namespace} `this`
*/
Namespace.prototype.addJSON = function addJSON(nestedJson) {
Namespace.prototype.addJSON = function addJSON(nestedJson, depth) {
depth = util.checkDepth(depth);
var ns = this;
/* istanbul ignore else */
if (nestedJson) {
Expand All @@ -208,7 +212,7 @@ Namespace.prototype.addJSON = function addJSON(nestedJson) {
? Service.fromJSON
: nested.id !== undefined
? Field.fromJSON
: Namespace.fromJSON )(names[i], nested)
: Namespace.fromJSON )(names[i], nested, depth + 1)
);
}
}
Expand Down
65 changes: 35 additions & 30 deletions src/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,8 @@ function parse(source, root, options) {
}


function parseCommon(parent, token) {
function parseCommon(parent, token, depth) {
depth = util.checkDepth(depth);
switch (token) {

case "option":
Expand All @@ -310,19 +311,19 @@ function parse(source, root, options) {
return true;

case "message":
parseType(parent, token);
parseType(parent, token, depth + 1);
return true;

case "enum":
parseEnum(parent, token);
return true;

case "service":
parseService(parent, token);
parseService(parent, token, depth + 1);
return true;

case "extend":
parseExtension(parent, token);
parseExtension(parent, token, depth);
return true;
}
return false;
Expand Down Expand Up @@ -350,15 +351,16 @@ function parse(source, root, options) {
}
}

function parseType(parent, token) {
function parseType(parent, token, depth) {
depth = util.checkDepth(depth);

/* istanbul ignore if */
if (!nameRe.test(token = next()))
throw illegal(token, "type name");

var type = new Type(token);
ifBlock(type, function parseType_block(token) {
if (parseCommon(type, token))
if (parseCommon(type, token, depth))
return;

switch (token) {
Expand All @@ -372,22 +374,22 @@ function parse(source, root, options) {
throw illegal(token);
/* eslint-disable no-fallthrough */
case "repeated":
parseField(type, token);
parseField(type, token, undefined, depth + 1);
break;

case "optional":
/* istanbul ignore if */
if (edition === "proto3") {
parseField(type, "proto3_optional");
parseField(type, "proto3_optional", undefined, depth + 1);
} else if (edition !== "proto2") {
throw illegal(token);
} else {
parseField(type, "optional");
parseField(type, "optional", undefined, depth + 1);
}
break;

case "oneof":
parseOneOf(type, token);
parseOneOf(type, token, depth + 1);
break;

case "extensions":
Expand All @@ -405,7 +407,7 @@ function parse(source, root, options) {
}

push(token);
parseField(type, "optional");
parseField(type, "optional", undefined, depth + 1);
break;
}
});
Expand All @@ -415,10 +417,10 @@ function parse(source, root, options) {
}
}

function parseField(parent, rule, extend) {
function parseField(parent, rule, extend, depth) {
var type = next();
if (type === "group") {
parseGroup(parent, rule);
parseGroup(parent, rule, depth);
return;
}
// Type names can consume multiple tokens, in multiple variants:
Expand Down Expand Up @@ -475,7 +477,8 @@ function parse(source, root, options) {
}
}

function parseGroup(parent, rule) {
function parseGroup(parent, rule, depth) {
depth = util.checkDepth(depth);
if (edition >= 2023) {
throw illegal("group");
}
Expand Down Expand Up @@ -503,20 +506,20 @@ function parse(source, root, options) {
break;
case "required":
case "repeated":
parseField(type, token);
parseField(type, token, undefined, depth + 1);
break;

case "optional":
/* istanbul ignore if */
if (edition === "proto3") {
parseField(type, "proto3_optional");
parseField(type, "proto3_optional", undefined, depth + 1);
} else {
parseField(type, "optional");
parseField(type, "optional", undefined, depth + 1);
}
break;

case "message":
parseType(type, token);
parseType(type, token, depth + 1);
break;

case "enum":
Expand Down Expand Up @@ -575,7 +578,7 @@ function parse(source, root, options) {
parent.add(field);
}

function parseOneOf(parent, token) {
function parseOneOf(parent, token, depth) {

/* istanbul ignore if */
if (!nameRe.test(token = next()))
Expand All @@ -588,7 +591,7 @@ function parse(source, root, options) {
skip(";");
} else {
push(token);
parseField(oneof, "optional");
parseField(oneof, "optional", undefined, depth);
}
});
parent.add(oneof);
Expand Down Expand Up @@ -693,7 +696,8 @@ function parse(source, root, options) {
setParsedOption(parent, option, optionValue, propName);
}

function parseOptionValue(parent, name) {
function parseOptionValue(parent, name, depth) {
depth = util.checkDepth(depth);
// { a: "foo" b { c: "bar" } }
if (skip("{", true)) {
var objectResult = {};
Expand All @@ -716,7 +720,7 @@ function parse(source, root, options) {
// option (my_option) = {
// repeated_value: [ "foo", "bar" ]
// };
value = parseOptionValue(parent, name + "." + token);
value = parseOptionValue(parent, name + "." + token, depth + 1);
} else if (peek() === "[") {
value = [];
var lastValue;
Expand Down Expand Up @@ -781,15 +785,16 @@ function parse(source, root, options) {
return parent;
}

function parseService(parent, token) {
function parseService(parent, token, depth) {
depth = util.checkDepth(depth);

/* istanbul ignore if */
if (!nameRe.test(token = next()))
throw illegal(token, "service name");

var service = new Service(token);
ifBlock(service, function parseService_block(token) {
if (parseCommon(service, token)) {
if (parseCommon(service, token, depth)) {
return;
}

Expand Down Expand Up @@ -855,7 +860,7 @@ function parse(source, root, options) {
parent.add(method);
}

function parseExtension(parent, token) {
function parseExtension(parent, token, depth) {

/* istanbul ignore if */
if (!typeRefRe.test(token = next()))
Expand All @@ -867,15 +872,15 @@ function parse(source, root, options) {

case "required":
case "repeated":
parseField(parent, token, reference);
parseField(parent, token, reference, depth + 1);
break;

case "optional":
/* istanbul ignore if */
if (edition === "proto3") {
parseField(parent, "proto3_optional", reference);
parseField(parent, "proto3_optional", reference, depth + 1);
} else {
parseField(parent, "optional", reference);
parseField(parent, "optional", reference, depth + 1);
}
break;

Expand All @@ -884,7 +889,7 @@ function parse(source, root, options) {
if (edition === "proto2" || !typeRefRe.test(token))
throw illegal(token);
push(token);
parseField(parent, "optional", reference);
parseField(parent, "optional", reference, depth + 1);
break;
}
});
Expand Down Expand Up @@ -936,7 +941,7 @@ function parse(source, root, options) {
default:

/* istanbul ignore else */
if (parseCommon(ptr, token)) {
if (parseCommon(ptr, token, 0)) {
head = false;
continue;
}
Expand Down
6 changes: 4 additions & 2 deletions src/root.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,16 @@ function Root(options) {
* Loads a namespace descriptor into a root namespace.
* @param {INamespace} json Namespace descriptor
* @param {Root} [root] Root namespace, defaults to create a new one if omitted
* @param {number} [depth] Current nesting depth, defaults to `0`
* @returns {Root} Root namespace
*/
Root.fromJSON = function fromJSON(json, root) {
Root.fromJSON = function fromJSON(json, root, depth) {
depth = util.checkDepth(depth);
if (!root)
root = new Root();
if (json.options)
root.setOptions(json.options);
return root.addJSON(json.nested).resolveAll();
return root.addJSON(json.nested, depth).resolveAll();
};

/**
Expand Down
6 changes: 4 additions & 2 deletions src/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,19 @@ function Service(name, options) {
* Constructs a service from a service descriptor.
* @param {string} name Service name
* @param {IService} json Service descriptor
* @param {number} [depth] Current nesting depth, defaults to `0`
* @returns {Service} Created service
* @throws {TypeError} If arguments are invalid
*/
Service.fromJSON = function fromJSON(name, json) {
Service.fromJSON = function fromJSON(name, json, depth) {
depth = util.checkDepth(depth);
var service = new Service(name, json.options);
/* istanbul ignore else */
if (json.methods)
for (var names = Object.keys(json.methods), i = 0; i < names.length; ++i)
service.add(Method.fromJSON(names[i], json.methods[names[i]]));
if (json.nested)
service.addJSON(json.nested);
service.addJSON(json.nested, depth);
if (json.edition)
service._edition = json.edition;
service.comment = json.comment;
Expand Down
Loading
Loading