Skip to content

Commit d2d47d9

Browse files
committed
fix: change tree traversal order and feature resolution algorithm
1 parent da2df8a commit d2d47d9

File tree

7 files changed

+236
-154
lines changed

7 files changed

+236
-154
lines changed

src/enum.js

+38-5
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,17 @@ function Enum(name, values, options, comment, comments, valuesOptions) {
5757
this.valuesOptions = valuesOptions;
5858

5959
/**
60-
* Values features, if any
60+
* Resolved values features, if any
6161
* @type {Object<string, Object<string, *>>|undefined}
6262
*/
6363
this._valuesFeatures = {};
6464

65+
/**
66+
* Unresolved values features, if any
67+
* @type {Object<string, Object<string, *>>|undefined}
68+
*/
69+
this._proto_valuesFeatures = {};
70+
6571
/**
6672
* Reserved ranges, if any.
6773
* @type {Array.<number[]|string>}
@@ -78,6 +84,28 @@ function Enum(name, values, options, comment, comments, valuesOptions) {
7884
this.valuesById[ this.values[keys[i]] = values[keys[i]] ] = keys[i];
7985
}
8086

87+
/**
88+
* Resolves value features
89+
* @returns {Enum} `this`
90+
*/
91+
Enum.prototype.resolve = function resolve() {
92+
93+
if (this.resolved)
94+
return this;
95+
96+
for (var key of Object.keys(this._proto_valuesFeatures)) {
97+
98+
if (this.parent) {
99+
var parentFeaturesCopy = Object.assign({}, this.parent._features);
100+
this._valuesFeatures[key] = Object.assign(parentFeaturesCopy, this._proto_valuesFeatures[key] || {});
101+
} else {
102+
this._valuesFeatures[key] = Object.assign({}, this._proto_valuesFeatures[key]);
103+
}
104+
}
105+
return ReflectionObject.prototype.resolve.call(this);
106+
};
107+
108+
81109
/**
82110
* Enum descriptor.
83111
* @interface IEnum
@@ -158,14 +186,19 @@ Enum.prototype.add = function add(name, id, comment, options) {
158186
for (var key of Object.keys(this.valuesOptions)) {
159187
var features = Array.isArray(this.valuesOptions[key]) ? this.valuesOptions[key].find(x => {return Object.prototype.hasOwnProperty.call(x, "features");}) : this.valuesOptions[key] === "features";
160188
if (features) {
161-
if (!this._valuesFeatures) {
162-
this._valuesFeatures = {};
163-
}
164-
this._valuesFeatures[key] = features.features || {};
189+
this._proto_valuesFeatures[key] = features.features;
190+
} else {
191+
this._proto_valuesFeatures[key] = {};
165192
}
166193
}
167194
}
168195

196+
for (var key of Object.keys(this.values)) {
197+
if (!this._proto_valuesFeatures[key]) {
198+
this._proto_valuesFeatures[key] = {}
199+
}
200+
}
201+
169202
this.comments[name] = comment || null;
170203
return this;
171204
};

src/namespace.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -302,12 +302,13 @@ Namespace.prototype.define = function define(path, json) {
302302
*/
303303
Namespace.prototype.resolveAll = function resolveAll() {
304304
var nested = this.nestedArray, i = 0;
305+
this.resolve();
305306
while (i < nested.length)
306307
if (nested[i] instanceof Namespace)
307308
nested[i++].resolveAll();
308309
else
309310
nested[i++].resolve();
310-
return this.resolve();
311+
return this;
311312
};
312313

313314
/**

src/object.js

+18-21
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ var util = require("./util");
77

88
var Root; // cyclic
99

10+
// TODO: Replace with embedded proto.
1011
var editions2023Defaults = {enum_type: "OPEN", field_presence: "EXPLICIT", json_format: "ALLOW", message_encoding: "LENGTH_PREFIXED", repeated_field_encoding: "PACKED", utf8_validation: "VERIFY"};
1112
var proto2Defaults = {enum_type: "CLOSED", field_presence: "EXPLICIT", json_format: "LEGACY_BEST_EFFORT", message_encoding: "LENGTH_PREFIXED", repeated_field_encoding: "EXPANDED", utf8_validation: "NONE"};
1213
var proto3Defaults = {enum_type: "OPEN", field_presence: "IMPLICIT", json_format: "ALLOW", message_encoding: "LENGTH_PREFIXED", repeated_field_encoding: "PACKED", utf8_validation: "VERIFY"};
@@ -51,7 +52,7 @@ function ReflectionObject(name, options) {
5152
this._features = {};
5253

5354
/**
54-
* Resolved Features.
55+
* Unresolved Features.
5556
*/
5657
this._proto_features = null;
5758

@@ -160,10 +161,10 @@ ReflectionObject.prototype.onRemove = function onRemove(parent) {
160161
ReflectionObject.prototype.resolve = function resolve() {
161162
if (this.resolved)
162163
return this;
163-
if (this.root instanceof Root || this.parent) {
164+
if (this instanceof Root || this.parent && this.parent.resolved)
164165
this._resolveFeatures();
166+
if (this.root instanceof Root)
165167
this.resolved = true;
166-
}
167168
return this;
168169
};
169170

@@ -174,28 +175,24 @@ ReflectionObject.prototype.resolve = function resolve() {
174175
ReflectionObject.prototype._resolveFeatures = function _resolveFeatures() {
175176
var defaults = {};
176177

177-
if (this.root.getOption("syntax") === "proto2") {
178-
defaults = Object.assign({}, proto2Defaults);
179-
} else if (this.root.getOption("syntax") === "proto3") {
180-
defaults = Object.assign({}, proto3Defaults);
181-
} else if (this.root.getOption("edition") === "2023") {
182-
defaults = Object.assign({}, editions2023Defaults);
178+
if (this instanceof Root) {
179+
if (this.root.getOption("syntax") === "proto2") {
180+
defaults = Object.assign({}, proto2Defaults);
181+
} else if (this.root.getOption("syntax") === "proto3") {
182+
defaults = Object.assign({}, proto3Defaults);
183+
} else if (this.root.getOption("edition") === "2023") {
184+
defaults = Object.assign({}, editions2023Defaults);
185+
}
183186
}
184187

185-
if (this.parent) {
186-
// This is an annoying workaround since we can't use the spread operator
187-
// (Breaks the bundler and eslint)
188-
// If we don't create a shallow copy, we end up also altering the parent's
189-
// features
190-
var parentFeaturesMerged = Object.assign(defaults, this.parent._proto_features);
191-
this._features = Object.assign(parentFeaturesMerged, this._proto_features || {});
192-
this._proto_features = this._features;
193-
this.parent._resolveFeatures();
194-
} else {
188+
if (this instanceof Root) {
195189
this._features = Object.assign(defaults, this._proto_features || {});
190+
} else if (this.parent) {
191+
var parentFeaturesCopy = Object.assign({}, this.parent._features);
192+
this._features = Object.assign(parentFeaturesCopy, this._proto_features || {});
193+
} else {
194+
this._features = Object.assign({}, this._proto_features);
196195
}
197-
this._proto_features = this._features;
198-
199196
};
200197

201198
/**

src/parse.js

+2
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ function parse(source, root, options) {
147147
} catch (err) {
148148
if (typeRefRe.test(token) && edition) {
149149
target.push(token);
150+
} else {
151+
throw err;
150152
}
151153
}
152154
}

src/service.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,11 @@ Service.prototype.get = function get(name) {
106106
* @override
107107
*/
108108
Service.prototype.resolveAll = function resolveAll() {
109+
Namespace.prototype.resolve.call(this);
109110
var methods = this.methodsArray;
110111
for (var i = 0; i < methods.length; ++i)
111112
methods[i].resolve();
112-
return Namespace.prototype.resolve.call(this);
113+
return this;
113114
};
114115

115116
/**

src/type.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -299,13 +299,14 @@ Type.prototype.toJSON = function toJSON(toJSONOptions) {
299299
* @override
300300
*/
301301
Type.prototype.resolveAll = function resolveAll() {
302+
Namespace.prototype.resolveAll.call(this);
302303
var fields = this.fieldsArray, i = 0;
303304
while (i < fields.length)
304305
fields[i++].resolve();
305306
var oneofs = this.oneofsArray; i = 0;
306307
while (i < oneofs.length)
307308
oneofs[i++].resolve();
308-
return Namespace.prototype.resolveAll.call(this);
309+
return this;
309310
};
310311

311312
/**

0 commit comments

Comments
 (0)