Skip to content

Commit b2c6867

Browse files
Partially fix some bugs handling the load of cross-edition files.
This will now work as long as all files sharing a package agree on file-level edition and options
1 parent 60f3e51 commit b2c6867

File tree

6 files changed

+211
-35
lines changed

6 files changed

+211
-35
lines changed

cli/targets/proto.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ function buildRoot(root) {
102102
if (pkg.length)
103103
out.push("", "package " + pkg.join(".") + ";");
104104

105-
buildOptions(ptr, ["edition", "syntax"]);
105+
buildOptions(ptr, ["edition"]);
106106
ptr.nestedArray.forEach(build);
107107
}
108108

src/index-light.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ protobuf.types = require("./types");
9898
protobuf.util = require("./util");
9999

100100
// Set up possibly cyclic reflection dependencies
101-
protobuf.ReflectionObject._configure(protobuf.Root);
101+
protobuf.ReflectionObject._configure(protobuf.Root, protobuf.Namespace);
102102
protobuf.Namespace._configure(protobuf.Type, protobuf.Service, protobuf.Enum);
103103
protobuf.Root._configure(protobuf.Type);
104104
protobuf.Field._configure(protobuf.Type);

src/object.js

+20-13
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ ReflectionObject.className = "ReflectionObject";
66
const OneOf = require("./oneof");
77
var util = require("./util");
88

9-
var Root; // cyclic
9+
var Root, Namespace; // cyclic
1010

1111
/* eslint-disable no-warning-comments */
1212
// TODO: Replace with embedded proto.
@@ -163,7 +163,8 @@ ReflectionObject.prototype.onRemove = function onRemove(parent) {
163163
ReflectionObject.prototype.resolve = function resolve() {
164164
if (this.resolved)
165165
return this;
166-
if (this instanceof Root || this.parent && this.parent.resolved) {
166+
var edition = this.getOption("edition");
167+
if ((this instanceof Namespace && edition) || (this.parent && this.parent.resolved)) {
167168
this._resolveFeatures();
168169
this.resolved = true;
169170
}
@@ -177,24 +178,27 @@ ReflectionObject.prototype.resolve = function resolve() {
177178
ReflectionObject.prototype._resolveFeatures = function _resolveFeatures() {
178179
var defaults = {};
179180

180-
var edition = this.root.getOption("edition");
181-
if (this instanceof Root) {
182-
if (this.root.getOption("syntax") === "proto3") {
181+
var protoFeatures = Object.assign(Object.assign({}, this._protoFeatures), this._inferLegacyProtoFeatures(edition));
182+
183+
var edition = this.getOption("edition");
184+
if (this instanceof Namespace && edition) {
185+
// For a namespace marked with a specific edition, reset defaults.
186+
if (edition === "proto2") {
187+
defaults = Object.assign({}, proto2Defaults);
188+
} else if (edition === "proto3") {
183189
defaults = Object.assign({}, proto3Defaults);
184190
} else if (edition === "2023") {
185191
defaults = Object.assign({}, editions2023Defaults);
186192
} else {
187-
defaults = Object.assign({}, proto2Defaults);
193+
throw new Error("Unknown edition: " + edition);
188194
}
195+
this._features = Object.assign(defaults, protoFeatures || {});
196+
return;
189197
}
190198

191-
var protoFeatures = Object.assign(Object.assign({}, this._protoFeatures), this._inferLegacyProtoFeatures(edition));
192-
193-
if (this instanceof Root) {
194-
this._features = Object.assign(defaults, protoFeatures || {});
195199
// fields in Oneofs aren't actually children of them, so we have to
196200
// special-case it
197-
} else if (this.partOf instanceof OneOf) {
201+
if (this.partOf instanceof OneOf) {
198202
var lexicalParentFeaturesCopy = Object.assign({}, this.partOf._features);
199203
this._features = Object.assign(lexicalParentFeaturesCopy, protoFeatures || {});
200204
} else if (this.declaringField) {
@@ -241,8 +245,10 @@ ReflectionObject.prototype.getOption = function getOption(name) {
241245
* @returns {ReflectionObject} `this`
242246
*/
243247
ReflectionObject.prototype.setOption = function setOption(name, value, ifNotSet) {
244-
if (!ifNotSet || !this.options || this.options[name] === undefined)
248+
if (!ifNotSet || !this.options || this.options[name] === undefined) {
249+
if (this.getOption(name) !== value) this.resolved = false;
245250
(this.options || (this.options = {}))[name] = value;
251+
}
246252
return this;
247253
};
248254

@@ -318,6 +324,7 @@ ReflectionObject.prototype.toString = function toString() {
318324
};
319325

320326
// Sets up cyclic dependencies (called in index-light)
321-
ReflectionObject._configure = function(Root_) {
327+
ReflectionObject._configure = function(Root_, Namespace_) {
322328
Root = Root_;
329+
Namespace = Namespace_;
323330
};

src/parse.js

+22-16
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ var base10Re = /^[1-9][0-9]*$/,
3333
* @property {string|undefined} package Package name, if declared
3434
* @property {string[]|undefined} imports Imports, if any
3535
* @property {string[]|undefined} weakImports Weak imports, if any
36-
* @property {string|undefined} syntax Syntax, if specified (either `"proto2"` or `"proto3"`)
3736
* @property {Root} root Populated root instance
3837
*/
3938

@@ -81,9 +80,9 @@ function parse(source, root, options) {
8180
pkg,
8281
imports,
8382
weakImports,
84-
syntax,
85-
edition = false,
86-
isProto3 = false;
83+
edition = "proto2",
84+
isProto3 = false,
85+
isProto2 = true;
8786

8887
var ptr = root;
8988

@@ -145,7 +144,7 @@ function parse(source, root, options) {
145144
try {
146145
target.push([ start = parseId(next()), skip("to", true) ? parseId(next()) : start ]);
147146
} catch (err) {
148-
if (typeRefRe.test(token) && edition) {
147+
if (typeRefRe.test(token) && (!isProto2 && !isProto3)) {
149148
target.push(token);
150149
} else {
151150
throw err;
@@ -228,7 +227,6 @@ function parse(source, root, options) {
228227
}
229228

230229
function parsePackage() {
231-
232230
/* istanbul ignore if */
233231
if (pkg !== undefined)
234232
throw illegal("package");
@@ -240,6 +238,12 @@ function parse(source, root, options) {
240238
throw illegal(pkg, "name");
241239

242240
ptr = ptr.define(pkg);
241+
242+
var oldEdition = ptr.getOption("edition");
243+
if (oldEdition && oldEdition !== edition) {
244+
throw new Error("incompatible editions detected in package " + pkg + ": " + edition + " vs " + oldEdition);
245+
}
246+
ptr.setOption("edition", edition);
243247
skip(";");
244248
}
245249

@@ -265,23 +269,26 @@ function parse(source, root, options) {
265269

266270
function parseSyntax() {
267271
skip("=");
268-
syntax = readString();
269-
isProto3 = syntax === "proto3";
272+
edition = readString();
273+
isProto3 = edition === "proto3";
274+
isProto2 = edition === "proto2";
270275

271276
/* istanbul ignore if */
272-
if (!isProto3 && syntax !== "proto2")
273-
throw illegal(syntax, "syntax");
277+
if (!isProto3 && !isProto2)
278+
throw illegal(edition, "syntax");
274279

275280
// Syntax is needed to understand the meaning of the optional field rule
276281
// Otherwise the meaning is ambiguous between proto2 and proto3
277-
root.setOption("syntax", syntax);
282+
root.setOption("edition", edition);
278283

279284
skip(";");
280285
}
281286

282287
function parseEdition() {
283288
skip("=");
284289
edition = readString();
290+
isProto3 = false;
291+
isProto2 = false;
285292
const supportedEditions = ["2023"];
286293

287294
/* istanbul ignore if */
@@ -361,7 +368,7 @@ function parse(source, root, options) {
361368
break;
362369

363370
case "required":
364-
if (edition)
371+
if (!isProto2)
365372
throw illegal(token);
366373
/* eslint-disable no-fallthrough */
367374
case "repeated":
@@ -372,7 +379,7 @@ function parse(source, root, options) {
372379
/* istanbul ignore if */
373380
if (isProto3) {
374381
parseField(type, "proto3_optional");
375-
} else if (edition) {
382+
} else if (!isProto2) {
376383
throw illegal(token);
377384
} else {
378385
parseField(type, "optional");
@@ -393,7 +400,7 @@ function parse(source, root, options) {
393400

394401
default:
395402
/* istanbul ignore if */
396-
if (!isProto3 && !edition || !typeRefRe.test(token)) {
403+
if (isProto2 || !typeRefRe.test(token)) {
397404
throw illegal(token);
398405
}
399406

@@ -854,7 +861,7 @@ function parse(source, root, options) {
854861

855862
default:
856863
/* istanbul ignore if */
857-
if (!isProto3 && !edition || !typeRefRe.test(token))
864+
if (isProto2 || !typeRefRe.test(token))
858865
throw illegal(token);
859866
push(token);
860867
parseField(parent, "optional", reference);
@@ -924,7 +931,6 @@ function parse(source, root, options) {
924931
"package" : pkg,
925932
"imports" : imports,
926933
weakImports : weakImports,
927-
syntax : syntax,
928934
root : root
929935
};
930936
}

src/root.js

+3
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ function Root(options) {
3535
* @type {string[]}
3636
*/
3737
this.files = [];
38+
39+
// Default to proto2 if not specified.
40+
this.setOption("edition", "proto2", true);
3841
}
3942

4043
/**

0 commit comments

Comments
 (0)