Permalink
Browse files

Fixed: Types should not clear constructor with cache (fixes decorators)

  • Loading branch information...
dcodeIO committed Apr 13, 2017
1 parent 3a20968 commit 0589ace4dc9e5c565ff996cf6e6bf94e63f43c4e
Showing with 93 additions and 15 deletions.
  1. +2 −0 README.md
  2. +41 −0 examples/js-decorators.js
  3. +6 −6 ext/descriptor/README.md
  4. +40 −6 ext/descriptor/index.js
  5. +1 −1 ext/descriptor/test.js
  6. +2 −2 src/type.js
  7. +1 −0 src/util.js
@@ -578,6 +578,8 @@ Other notes:
* Default values must be specified as arguments to the decorator instead of using a property initializer for proper prototype behavior.
* 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.
**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.
Command line
------------
@@ -0,0 +1,41 @@
// This example shows how decorators can be used with plain JavaScript. It's otherwise identical to
// the README example.
/*eslint-disable strict, no-console*/
var protobuf = require("..");
var Type = protobuf.Type,
Field = protobuf.Field,
OneOf = protobuf.OneOf;
function AwesomeSubMessage(properties) {
protobuf.Message.call(this, properties);
}
(AwesomeSubMessage.prototype = Object.create(protobuf.Message)).constructor = AwesomeSubMessage;
Field.d(1, "string", "optional", "awesome default string")(AwesomeSubMessage.prototype, "awesomeField");
var AwesomeEnum = {
ONE: 1,
TWO: 2
};
Type.d("SuperAwesomeMessage")(AwesomeMessage);
function AwesomeMessage(properties) {
protobuf.Message.call(this, properties);
}
(AwesomeMessage.prototype = Object.create(protobuf.Message)).constructor = AwesomeMessage;
Field.d(1, "string", "optional", "awesome default string")(AwesomeMessage.prototype, "awesomeField");
Field.d(2, AwesomeSubMessage)(AwesomeMessage.prototype, "awesomeSubMessage");
Field.d(3, AwesomeEnum, "optional", AwesomeEnum.ONE)(AwesomeMessage.prototype, "awesomeEnum");
OneOf.d("awesomeSubMessage", "awesomeEnum")(AwesomeMessage.prototype, "which");
// example code
var message = new AwesomeMessage({ awesomeField: "hello" });
var buffer = AwesomeMessage.encode(message).finish();
var decoded = AwesomeMessage.decode(buffer);
console.log(decoded, "internal name: " + AwesomeMessage.$type.name);
@@ -1,7 +1,7 @@
protobufjs/ext/descriptor
=========================
Experimental extension for interoperability with descriptor.proto types.
Experimental extension for interoperability with [descriptor.proto](https://github.com/google/protobuf/blob/master/src/google/protobuf/descriptor.proto) types.
Usage
-----
@@ -31,17 +31,17 @@ root = protobuf.Root.fromDescriptor(decoded);
API
---
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.
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:
| Descriptor type | protobuf.js type | Remarks
|--------------------------|------------------|---------
| FileDescriptorSet | Root |
| FileDescriptorProto | Root | not supported: dependencies, sourceCodeInfo
| FileDescriptorProto | Root | except dependencies, sourceCodeInfo
| FileOptions | Root | not supported
| DescriptorProto | Type |
| MessageOptions | Type | not supported
| FieldDescriptorProto | Field | not supported: defaultValue, jsonValue
| FieldOptions | Field | only packed
| FieldDescriptorProto | Field | except defaultValue
| FieldOptions | Field |
| OneofDescriptorProto | OneOf |
| OneofOptions | OneOf | not supported
| EnumDescriptorProto | Enum |
@@ -56,4 +56,4 @@ The extension adds `.fromDescriptor(descriptor[, syntax])` and `#toDescriptor([s
| SourceCodeInfo | | not supported
| GeneratedCodeInfo | | not supported
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.
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.
@@ -282,8 +282,8 @@ Type.prototype.toDescriptor = function toDescriptor(syntax) {
* @type {Object}
* @property {string} [name] Field name
* @property {number} [number] Field id
* @property {FieldDescriptorProtoLabel} [label] Field rule
* @property {FieldDescriptorProtoType} [type] Field basic type
* @property {FieldDescriptorProto_Label} [label] Field rule
* @property {FieldDescriptorProto_Type} [type] Field basic type
* @property {string} [typeName] Field type name
* @property {string} [extendee] Extended type name
* @property {*} [defaultValue] Not supported
@@ -295,7 +295,7 @@ Type.prototype.toDescriptor = function toDescriptor(syntax) {
/**
* Values of the FieldDescriptorProto.Label enum.
* @typedef FieldDescriptorProtoLabel
* @typedef FieldDescriptorProto_Label
* @type {number}
* @property {number} LABEL_OPTIONAL=1
* @property {number} LABEL_REQUIRED=2
@@ -305,7 +305,7 @@ Type.prototype.toDescriptor = function toDescriptor(syntax) {
/**
* Values of the FieldDescriptorProto.Type enum.
* @typedef FieldDescriptorProtoType
* @typedef FieldDescriptorProto_Type
* @type {number}
* @property {number} TYPE_DOUBLE=1
* @property {number} TYPE_FLOAT=2
@@ -333,9 +333,19 @@ Type.prototype.toDescriptor = function toDescriptor(syntax) {
* @typedef FieldOptionsProperties
* @type {Object}
* @property {boolean} [packed] Whether packed or not (defaults to `false` for proto2 and `true` for proto3)
* @property {FieldOptions_JSType} [jstype] JavaScript value type (not used by protobuf.js)
* @see Part of the {@link descriptor} extension (ext/descriptor)
*/
/**
* Values of the FieldOptions.JSType enum.
* @typedef FieldOptions_JSType
* @type {number}
* @property {number} JS_NORMAL=0
* @property {number} JS_STRING=1
* @property {number} JS_NUMBER=2
*/
// Converts a descriptor type to a protobuf.js basic type
function fromDescriptorType(type) {
switch (type) {
@@ -422,6 +432,9 @@ Field.fromDescriptor = function fromDescriptor(descriptor, syntax) {
descriptor.extendee.length ? descriptor.extendee : undefined
);
if (descriptor.options)
field.options = fromDescriptorOptions(descriptor.options, exports.FieldOptions);
if (packableDescriptorType(descriptor.type)) {
if (syntax === "proto3") { // defaults to packed=true (internal preset is packed=true)
if (descriptor.options && !descriptor.options.packed)
@@ -503,11 +516,14 @@ Field.prototype.toDescriptor = function toDescriptor(syntax) {
if ((descriptor.oneofIndex = this.parent.oneofsArray.indexOf(this.partOf)) < 0)
throw Error("missing oneof");
if (this.options)
descriptor.options = toDescriptorOptions(this.options, exports.FieldOptions);
if (syntax === "proto3") { // defaults to packed=true
if (!this.packed)
descriptor.options = exports.FieldOptions.create({ packed: false });
(descriptor.options || (descriptor.options = exports.FieldOptions.create())).packed = false;
} else if (this.packed) // defaults to packed=false
descriptor.options = exports.FieldOptions.create({ packed: true });
(descriptor.options || (descriptor.options = exports.FieldOptions.create())).packed = true;
return descriptor;
};
@@ -739,6 +755,24 @@ Method.prototype.toDescriptor = function toDescriptor() {
});
};
// --- utility ---
function fromDescriptorOptions(options, type) {
var out = [];
for (var i = 0, key; i < type.fieldsArray.length; ++i)
if ((key = type._fieldsArray[i].name) !== "uninterpretedOption")
if (options.hasOwnProperty(key)) // eslint-disable-line no-prototype-builtins
out.push(key, options[key]);
return out.length ? $protobuf.util.toObject(out) : undefined;
}
function toDescriptorOptions(options, type) {
var out = [];
for (var i = 0, key; i < type.fieldsArray.length; ++i)
out.push(key = type._fieldsArray[i].name, options[key]);
return out.length ? type.fromObject($protobuf.util.toObject(out)) : undefined;
}
// --- exports ---
/**
@@ -36,7 +36,7 @@ var msg = root.toDescriptor();
// console.log("\nDescriptor", JSON.stringify(msg.toObject(), null, 2));
var buf = descriptor.FileDescriptorSet.encode(msg).finish();
var root2 = protobuf.Root.fromDescriptor(buf, "proto2");
var root2 = protobuf.Root.fromDescriptor(buf, "proto2").resolveAll();
// console.log("\nDecoded proto", JSON.stringify(root2, null, 2));
@@ -166,7 +166,7 @@ Object.defineProperties(Type.prototype, {
// Classes and messages reference their reflected type
ctor.$type = ctor.prototype.$type = this;
// Mixin static methods
// Mix in static methods
util.merge(ctor, Message, true);
this._ctor = ctor;
@@ -210,7 +210,7 @@ Type.generateConstructor = function generateConstructor(type) {
};
function clearCache(type) {
type._fieldsById = type._fieldsArray = type._oneofsArray = type._ctor = null;
type._fieldsById = type._fieldsArray = type._oneofsArray = null;
delete type.encode;
delete type.decode;
delete type.verify;
@@ -107,6 +107,7 @@ util.decorateType = function decorateType(ctor, typeName) {
var type = new Type(typeName || ctor.name);
util.decorateRoot.add(type);
type.ctor = ctor; // sets up .encode, .decode etc.
Object.defineProperty(ctor, "$type", { value: type, enumerable: false });
Object.defineProperty(ctor.prototype, "$type", { value: type, enumerable: false });
return type;

0 comments on commit 0589ace

Please sign in to comment.