Skip to content

Commit 0589ace

Browse files
committed
Fixed: Types should not clear constructor with cache (fixes decorators)
1 parent 3a20968 commit 0589ace

File tree

7 files changed

+93
-15
lines changed

7 files changed

+93
-15
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,8 @@ Other notes:
578578
* Default values must be specified as arguments to the decorator instead of using a property initializer for proper prototype behavior.
579579
* Property names on decorated classes must not be renamed on compile time (i.e. by a minifier) because decorators just receive the original field name as a string.
580580

581+
**ProTip!** Not as pretty, but you can [use decorators in plain JavaScript](https://github.com/dcodeIO/protobuf.js/blob/master/examples/js-decorators.js) as well.
582+
581583
Command line
582584
------------
583585

examples/js-decorators.js

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// This example shows how decorators can be used with plain JavaScript. It's otherwise identical to
2+
// the README example.
3+
4+
/*eslint-disable strict, no-console*/
5+
var protobuf = require("..");
6+
7+
var Type = protobuf.Type,
8+
Field = protobuf.Field,
9+
OneOf = protobuf.OneOf;
10+
11+
function AwesomeSubMessage(properties) {
12+
protobuf.Message.call(this, properties);
13+
}
14+
15+
(AwesomeSubMessage.prototype = Object.create(protobuf.Message)).constructor = AwesomeSubMessage;
16+
17+
Field.d(1, "string", "optional", "awesome default string")(AwesomeSubMessage.prototype, "awesomeField");
18+
19+
var AwesomeEnum = {
20+
ONE: 1,
21+
TWO: 2
22+
};
23+
24+
Type.d("SuperAwesomeMessage")(AwesomeMessage);
25+
function AwesomeMessage(properties) {
26+
protobuf.Message.call(this, properties);
27+
}
28+
29+
(AwesomeMessage.prototype = Object.create(protobuf.Message)).constructor = AwesomeMessage;
30+
31+
Field.d(1, "string", "optional", "awesome default string")(AwesomeMessage.prototype, "awesomeField");
32+
Field.d(2, AwesomeSubMessage)(AwesomeMessage.prototype, "awesomeSubMessage");
33+
Field.d(3, AwesomeEnum, "optional", AwesomeEnum.ONE)(AwesomeMessage.prototype, "awesomeEnum");
34+
OneOf.d("awesomeSubMessage", "awesomeEnum")(AwesomeMessage.prototype, "which");
35+
36+
// example code
37+
var message = new AwesomeMessage({ awesomeField: "hello" });
38+
var buffer = AwesomeMessage.encode(message).finish();
39+
var decoded = AwesomeMessage.decode(buffer);
40+
41+
console.log(decoded, "internal name: " + AwesomeMessage.$type.name);

ext/descriptor/README.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
protobufjs/ext/descriptor
22
=========================
33

4-
Experimental extension for interoperability with descriptor.proto types.
4+
Experimental extension for interoperability with [descriptor.proto](https://github.com/google/protobuf/blob/master/src/google/protobuf/descriptor.proto) types.
55

66
Usage
77
-----
@@ -31,17 +31,17 @@ root = protobuf.Root.fromDescriptor(decoded);
3131
API
3232
---
3333

34-
The extension adds `.fromDescriptor(descriptor[, syntax])` and `#toDescriptor([syntax])` methods to reflection objects and exports the `.google.protobuf` namespace of the internally used `Root` instance containing the following types present in descriptor.proto.
34+
The extension adds `.fromDescriptor(descriptor[, syntax])` and `#toDescriptor([syntax])` methods to reflection objects and exports the `.google.protobuf` namespace of the internally used `Root` instance containing the following types present in descriptor.proto, including sub-types:
3535

3636
| Descriptor type | protobuf.js type | Remarks
3737
|--------------------------|------------------|---------
3838
| FileDescriptorSet | Root |
39-
| FileDescriptorProto | Root | not supported: dependencies, sourceCodeInfo
39+
| FileDescriptorProto | Root | except dependencies, sourceCodeInfo
4040
| FileOptions | Root | not supported
4141
| DescriptorProto | Type |
4242
| MessageOptions | Type | not supported
43-
| FieldDescriptorProto | Field | not supported: defaultValue, jsonValue
44-
| FieldOptions | Field | only packed
43+
| FieldDescriptorProto | Field | except defaultValue
44+
| FieldOptions | Field |
4545
| OneofDescriptorProto | OneOf |
4646
| OneofOptions | OneOf | not supported
4747
| EnumDescriptorProto | Enum |
@@ -56,4 +56,4 @@ The extension adds `.fromDescriptor(descriptor[, syntax])` and `#toDescriptor([s
5656
| SourceCodeInfo | | not supported
5757
| GeneratedCodeInfo | | not supported
5858

59-
Additionally, not all features of descriptor.proto translate perfectly to a protobuf.js root instance. A root instance has only limited knowlege of packages or individual files for example, which is then compensated by guessing.
59+
Note that not all features of descriptor.proto translate perfectly to a protobuf.js root instance. A root instance has only limited knowlege of packages or individual files for example, which is then compensated by guessing and generating fictional file names.

ext/descriptor/index.js

+40-6
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,8 @@ Type.prototype.toDescriptor = function toDescriptor(syntax) {
282282
* @type {Object}
283283
* @property {string} [name] Field name
284284
* @property {number} [number] Field id
285-
* @property {FieldDescriptorProtoLabel} [label] Field rule
286-
* @property {FieldDescriptorProtoType} [type] Field basic type
285+
* @property {FieldDescriptorProto_Label} [label] Field rule
286+
* @property {FieldDescriptorProto_Type} [type] Field basic type
287287
* @property {string} [typeName] Field type name
288288
* @property {string} [extendee] Extended type name
289289
* @property {*} [defaultValue] Not supported
@@ -295,7 +295,7 @@ Type.prototype.toDescriptor = function toDescriptor(syntax) {
295295

296296
/**
297297
* Values of the FieldDescriptorProto.Label enum.
298-
* @typedef FieldDescriptorProtoLabel
298+
* @typedef FieldDescriptorProto_Label
299299
* @type {number}
300300
* @property {number} LABEL_OPTIONAL=1
301301
* @property {number} LABEL_REQUIRED=2
@@ -305,7 +305,7 @@ Type.prototype.toDescriptor = function toDescriptor(syntax) {
305305

306306
/**
307307
* Values of the FieldDescriptorProto.Type enum.
308-
* @typedef FieldDescriptorProtoType
308+
* @typedef FieldDescriptorProto_Type
309309
* @type {number}
310310
* @property {number} TYPE_DOUBLE=1
311311
* @property {number} TYPE_FLOAT=2
@@ -333,9 +333,19 @@ Type.prototype.toDescriptor = function toDescriptor(syntax) {
333333
* @typedef FieldOptionsProperties
334334
* @type {Object}
335335
* @property {boolean} [packed] Whether packed or not (defaults to `false` for proto2 and `true` for proto3)
336+
* @property {FieldOptions_JSType} [jstype] JavaScript value type (not used by protobuf.js)
336337
* @see Part of the {@link descriptor} extension (ext/descriptor)
337338
*/
338339

340+
/**
341+
* Values of the FieldOptions.JSType enum.
342+
* @typedef FieldOptions_JSType
343+
* @type {number}
344+
* @property {number} JS_NORMAL=0
345+
* @property {number} JS_STRING=1
346+
* @property {number} JS_NUMBER=2
347+
*/
348+
339349
// Converts a descriptor type to a protobuf.js basic type
340350
function fromDescriptorType(type) {
341351
switch (type) {
@@ -422,6 +432,9 @@ Field.fromDescriptor = function fromDescriptor(descriptor, syntax) {
422432
descriptor.extendee.length ? descriptor.extendee : undefined
423433
);
424434

435+
if (descriptor.options)
436+
field.options = fromDescriptorOptions(descriptor.options, exports.FieldOptions);
437+
425438
if (packableDescriptorType(descriptor.type)) {
426439
if (syntax === "proto3") { // defaults to packed=true (internal preset is packed=true)
427440
if (descriptor.options && !descriptor.options.packed)
@@ -503,11 +516,14 @@ Field.prototype.toDescriptor = function toDescriptor(syntax) {
503516
if ((descriptor.oneofIndex = this.parent.oneofsArray.indexOf(this.partOf)) < 0)
504517
throw Error("missing oneof");
505518

519+
if (this.options)
520+
descriptor.options = toDescriptorOptions(this.options, exports.FieldOptions);
521+
506522
if (syntax === "proto3") { // defaults to packed=true
507523
if (!this.packed)
508-
descriptor.options = exports.FieldOptions.create({ packed: false });
524+
(descriptor.options || (descriptor.options = exports.FieldOptions.create())).packed = false;
509525
} else if (this.packed) // defaults to packed=false
510-
descriptor.options = exports.FieldOptions.create({ packed: true });
526+
(descriptor.options || (descriptor.options = exports.FieldOptions.create())).packed = true;
511527

512528
return descriptor;
513529
};
@@ -739,6 +755,24 @@ Method.prototype.toDescriptor = function toDescriptor() {
739755
});
740756
};
741757

758+
// --- utility ---
759+
760+
function fromDescriptorOptions(options, type) {
761+
var out = [];
762+
for (var i = 0, key; i < type.fieldsArray.length; ++i)
763+
if ((key = type._fieldsArray[i].name) !== "uninterpretedOption")
764+
if (options.hasOwnProperty(key)) // eslint-disable-line no-prototype-builtins
765+
out.push(key, options[key]);
766+
return out.length ? $protobuf.util.toObject(out) : undefined;
767+
}
768+
769+
function toDescriptorOptions(options, type) {
770+
var out = [];
771+
for (var i = 0, key; i < type.fieldsArray.length; ++i)
772+
out.push(key = type._fieldsArray[i].name, options[key]);
773+
return out.length ? type.fromObject($protobuf.util.toObject(out)) : undefined;
774+
}
775+
742776
// --- exports ---
743777

744778
/**

ext/descriptor/test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ var msg = root.toDescriptor();
3636
// console.log("\nDescriptor", JSON.stringify(msg.toObject(), null, 2));
3737

3838
var buf = descriptor.FileDescriptorSet.encode(msg).finish();
39-
var root2 = protobuf.Root.fromDescriptor(buf, "proto2");
39+
var root2 = protobuf.Root.fromDescriptor(buf, "proto2").resolveAll();
4040

4141
// console.log("\nDecoded proto", JSON.stringify(root2, null, 2));
4242

src/type.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ Object.defineProperties(Type.prototype, {
166166
// Classes and messages reference their reflected type
167167
ctor.$type = ctor.prototype.$type = this;
168168

169-
// Mixin static methods
169+
// Mix in static methods
170170
util.merge(ctor, Message, true);
171171

172172
this._ctor = ctor;
@@ -210,7 +210,7 @@ Type.generateConstructor = function generateConstructor(type) {
210210
};
211211

212212
function clearCache(type) {
213-
type._fieldsById = type._fieldsArray = type._oneofsArray = type._ctor = null;
213+
type._fieldsById = type._fieldsArray = type._oneofsArray = null;
214214
delete type.encode;
215215
delete type.decode;
216216
delete type.verify;

src/util.js

+1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ util.decorateType = function decorateType(ctor, typeName) {
107107

108108
var type = new Type(typeName || ctor.name);
109109
util.decorateRoot.add(type);
110+
type.ctor = ctor; // sets up .encode, .decode etc.
110111
Object.defineProperty(ctor, "$type", { value: type, enumerable: false });
111112
Object.defineProperty(ctor.prototype, "$type", { value: type, enumerable: false });
112113
return type;

0 commit comments

Comments
 (0)