Skip to content

Commit ad5abe7

Browse files
committed
Static pbjs target progress, now generates usable CommonJS code, see #512
1 parent 00f3574 commit ad5abe7

File tree

7 files changed

+758
-89
lines changed

7 files changed

+758
-89
lines changed

cli/targets/static.js

+204-22
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,31 @@ module.exports = static_target;
22

33
static_target.private = true;
44

5-
// This file contains the beginnings of static code generation.
6-
// It doesn't generate anything useful, yet, but can be used as a starting point.
5+
// Currently, this file contains initial static code for CommonJS modules.
76

87
// TBD:
98
// - Generate a single file or scaffold an entire project directory? Both?
109
// - Targets: ES5, ES6, TypeScript? CommonJS? AMD?
11-
// - Is there a need for a minimal runtime composed only of Reader/Writer/minimal util?
12-
// - What about generating comments and typescript definitions for non-ts targets?
10+
// - What about generating typescript definitions for non-ts targets?
1311

1412
var protobuf = require("../..");
1513

1614
var Type = protobuf.Type,
1715
Service = protobuf.Service,
1816
Enum = protobuf.Enum,
1917
Namespace = protobuf.Namespace,
20-
codegen = protobuf.util.codegen;
18+
encoder = protobuf.encoder,
19+
decoder = protobuf.decoder,
20+
verifier = protobuf.verifier,
21+
util = protobuf.util;
2122

2223
var out = [];
24+
var indent = 0;
2325

2426
function static_target(root, options, callback) {
2527
tree = {};
2628
try {
27-
out.push("var protobuf = require(\"protobufjs\");");
28-
out.push("var root = exports;");
29-
buildNamespace("root", root);
29+
buildNamespace("module.exports", root);
3030
callback(null, out.join('\n'));
3131
} catch (err) {
3232
callback(err);
@@ -35,33 +35,215 @@ function static_target(root, options, callback) {
3535
}
3636
}
3737

38+
function push(line) {
39+
if (line === "")
40+
return out.push("");
41+
var ind = "";
42+
for (var i = 0; i < indent; ++i)
43+
ind += " ";
44+
out.push(ind + line);
45+
}
46+
47+
function pushComment(lines) {
48+
push("/**");
49+
lines.forEach(function(line, i) {
50+
push(" * " + line);
51+
});
52+
push(" */");
53+
}
54+
55+
function name(name) {
56+
if (!name)
57+
return "$root";
58+
return name;
59+
}
60+
3861
function buildNamespace(ref, ns) {
3962
if (!ns)
4063
return;
64+
if (ns.name === "") { // root
65+
push(name(ref) + " = (function() {");
66+
++indent;
67+
push('"use strict";');
68+
push("");
69+
push("// Minimal static codegen runtime");
70+
push("var $runtime = require(\"protobufjs/runtime\");")
71+
push("");
72+
push("// Lazily resolved type references");
73+
push("var $lazyTypes = [];");
74+
} else {
75+
push("");
76+
push("/** @alias " + ns.fullName.substring(1) + " */");
77+
push(name(ref) + "." + name(ns.name) + " = (function() {");
78+
++indent;
79+
}
80+
81+
if (ns instanceof Type) {
82+
buildType(undefined, ns);
83+
} else if (ns instanceof Service)
84+
buildService(undefined, ns);
85+
else {
86+
push("");
87+
push("/** @alias " + (ns.name && ns.fullName.substring(1) || "exports") + " */");
88+
push("var " + name(ns.name) + " = {};");
89+
}
90+
4191
ns.nestedArray.forEach(function(nested) {
42-
if (nested instanceof Type)
43-
buildType(ref, nested);
44-
else if (nested instanceof Service)
45-
buildService(ref, nested);
46-
else if (nested instanceof Enum)
47-
buildEnum(ref, nested);
92+
if (nested instanceof Enum)
93+
buildEnum(ns.name, nested);
4894
else if (nested instanceof Namespace)
49-
buildNamespace(ref, nested);
95+
buildNamespace(ns.name, nested);
96+
});
97+
push("");
98+
if (ns.name === "") // root
99+
push("return $runtime.resolve($root, $lazyTypes);");
100+
else
101+
push("return " + name(ns.name) + ";");
102+
--indent;
103+
push("})();");
104+
}
105+
106+
function buildFunction(type, functionName, gen, scope) {
107+
var lines = gen.str(functionName)
108+
.replace("(this.getCtor())", " $root" + type.fullName)
109+
.split(/\n/g);
110+
push(name(type.name) + "." + functionName + " = (function() {");
111+
++indent;
112+
push("/* eslint-disable */");
113+
Object.keys(scope).forEach(function(key) {
114+
push("var " + key + " = " + scope[key] + ";");
115+
});
116+
push("var types; $lazyTypes.push(types = [" + type.fieldsArray.map(function(field) {
117+
return field.resolve().resolvedType
118+
? JSON.stringify(field.resolvedType.fullName.substring(1))
119+
: "null";
120+
}).join(',') + "]);");
121+
push("return " + lines[0]);
122+
lines.slice(1).forEach(function(line) {
123+
if (line === '\t"use strict"')
124+
return;
125+
var prev = indent;
126+
var i = 0;
127+
while (line.charAt(i++) === "\t")
128+
++indent;
129+
push(line.trim());
130+
indent = prev;
50131
});
132+
push("/* eslint-enable */");
133+
--indent;
134+
push("})();");
51135
}
52136

53137
function buildType(ref, type) {
54-
out.push("");
55-
out.push(ref + "." + type.name + " = function " + type.name + "() {};"); // currently just an empty function
56-
buildNamespace(ref + "." + type.name, type);
138+
var fullName = type.fullName.substring(1);
139+
140+
push("");
141+
pushComment([
142+
"Constructs a new " + type.name + ".",
143+
"@exports " + fullName,
144+
"@constructor",
145+
"@param {Object} [properties] Properties to set"
146+
]);
147+
push("function " + name(type.name) + "(properties) {");
148+
++indent;
149+
push("if (properties) {");
150+
++indent;
151+
push("var keys = Object.keys(properties);");
152+
push("for (var i = 0; i < keys.length; ++i)");
153+
++indent;
154+
push("this[keys[i]] = properties[keys[i]];");
155+
--indent;
156+
--indent;
157+
push("}");
158+
--indent;
159+
push("}");
160+
push("");
161+
type.fieldsArray.forEach(function(field) {
162+
field.resolve();
163+
if (typeof field.defaultValue === 'object' && field.defaultValue)
164+
return;
165+
push(name(type.name) + ".prototype." + name(field.name) + " = " +JSON.stringify(field.defaultValue) + ";");
166+
});
167+
168+
// #encode
169+
push("");
170+
pushComment([
171+
"Encodes the specified " + type.name + ".",
172+
"@function",
173+
"@param {" + fullName + "|Object} message " + type.name + " or plain object to encode",
174+
"@param {Writer} [writer] Writer to encode to",
175+
"@returns {Writer} Writer"
176+
]);
177+
buildFunction(type, "encode", encoder.generate(type), {
178+
Writer : "$runtime.Writer",
179+
util : "$runtime.util"
180+
});
181+
182+
// #encodeDelimited
183+
push("");
184+
pushComment([
185+
"Encodes the specified " + type.name + ", length delimited.",
186+
"@param {" + fullName + "|Object} message " + type.name + " or plain object to encode",
187+
"@param {Writer} [writer] Writer to encode to",
188+
"@returns {Writer} Writer"
189+
]);
190+
push(name(type.name) + ".encodeDelimited = function encodeDelimited(message, writer) {");
191+
++indent;
192+
push("return this.encode(message, writer).ldelim();");
193+
--indent;
194+
push("};");
195+
196+
// #decode
197+
push("");
198+
pushComment([
199+
"Decodes a " + type.name + " from the specified reader or buffer.",
200+
"@function",
201+
"@param {Reader|Uint8Array} readerOrBuffer Reader or buffer to decode from",
202+
"@param {number} [length] Message length if known beforehand",
203+
"@returns {" + fullName + "} " + type.name
204+
]);
205+
buildFunction(type, "decode", decoder.generate(type), {
206+
Reader : "$runtime.Reader",
207+
util : "$runtime.util"
208+
});
209+
210+
// #decodeDelimited
211+
push("");
212+
pushComment([
213+
"Decodes a " + type.name + " from the specified reader or buffer, length delimited.",
214+
"@param {Reader|Uint8Array} readerOrBuffer Reader or buffer to decode from",
215+
"@returns {" + fullName + "} " + type.name
216+
]);
217+
push(name(type.name) + ".decodeDelimited = function decodeDelimited(readerOrBuffer) {");
218+
++indent;
219+
push("readerOrBuffer = readerOrBuffer instanceof Reader ? readerOrBuffer : Reader(readerOrBuffer);");
220+
push("return this.decode(readerOrBuffer, readerOrBuffer.uint32());");
221+
--indent;
222+
push("};");
223+
224+
// #verify
225+
push("");
226+
pushComment([
227+
"Verifies a " + type.name + ".",
228+
"@param {" + fullName + "|Object} message " + type.name + " or plain object to verify",
229+
"@returns {?string} `null` if valid, otherwise the reason why it is not"
230+
]);
231+
buildFunction(type, "verify", verifier.generate(type), {});
57232
}
58233

59234
function buildService(ref, service) {
60-
out.push("");
61-
out.push(ref + "." + service.name + " = {};"); // currently just an empty object
235+
push("");
236+
push(name(ref) + "." + name(service.name) + " = {};"); // currently just an empty object
62237
}
63238

64239
function buildEnum(ref, enm) {
65-
out.push("");
66-
out.push(ref + "." + enm.name + " = " + JSON.stringify(enm.values, null, "\t") + ";");
240+
push("");
241+
push(ref + "." + enm.name + " = {");
242+
++indent;
243+
push("");
244+
Object.keys(enm.values).forEach(function(key) {
245+
push(name(key) + ": " + enm.values[key].toString(10) + ",");
246+
});
247+
--indent;
248+
push("};");
67249
}

runtime.js

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"use strict";
2+
3+
/**
4+
* Minimal static code generator runtime.
5+
* @namespace
6+
*/
7+
var runtime = exports;
8+
9+
/**
10+
* @alias Reader
11+
*/
12+
runtime.Reader = require("./src/reader");
13+
14+
/**
15+
* @alias Writer
16+
*/
17+
runtime.Writer = require("./src/writer");
18+
19+
/**
20+
* Runtime utility.
21+
* @memberof runtime
22+
*/
23+
var util = runtime.util = {};
24+
25+
/**
26+
* Converts a number or long to an 8 characters long hash string.
27+
* @param {Long|number} value Value to convert
28+
* @returns {string} Hash
29+
*/
30+
util.longToHash = function longToHash(value) {
31+
return value
32+
? LongBits.from(value).toHash()
33+
: '\0\0\0\0\0\0\0\0';
34+
};
35+
36+
/**
37+
* Tests if two possibly long values are not equal.
38+
* @param {number|Long} a First value
39+
* @param {number|Long} b Second value
40+
* @returns {boolean} `true` if not equal
41+
*/
42+
util.longNeq = function longNeq(a, b) {
43+
return typeof a === 'number'
44+
? typeof b === 'number'
45+
? a !== b
46+
: (a = LongBits.fromNumber(a)).lo !== b.low || a.hi !== b.high
47+
: typeof b === 'number'
48+
? (b = LongBits.fromNumber(b)).lo !== a.low || b.hi !== a.high
49+
: a.low !== b.low || a.high !== b.high;
50+
};
51+
52+
/**
53+
* Resolves lazy type references.
54+
* @param {Object} root Root object
55+
* @param {string[][]} lazyTypes Lazy type references
56+
* @returns {Object} `root`
57+
*/
58+
runtime.resolve = function resolve(root, lazyTypes) {
59+
lazyTypes.forEach(function(types) {
60+
types.forEach(function(path, i) {
61+
if (!path)
62+
return;
63+
path = path.split('.');
64+
var ptr = root;
65+
while (path.length)
66+
ptr = ptr[path.shift()];
67+
types[i] = ptr;
68+
});
69+
});
70+
return root;
71+
};

src/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ protobuf.Reader = require("./reader");
3333
protobuf.BufferReader = protobuf.Reader.BufferReader;
3434
protobuf.encoder = require("./encoder");
3535
protobuf.decoder = require("./decoder");
36+
protobuf.verifier = require("./verifier");
3637

3738
// Reflection
3839
protobuf.ReflectionObject = require("./object");

src/type.js

+7-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ var Enum = require("./enum"),
1616
Writer = require("./writer"),
1717
encoder = require("./encoder"),
1818
decoder = require("./decoder"),
19-
Verifier = require("./verifier"),
19+
verifier = require("./verifier"),
2020
inherits = require("./inherits"),
2121
util = require("./util");
2222

@@ -387,9 +387,10 @@ TypePrototype.decodeDelimited = function decodeDelimited(readerOrBuffer) {
387387
* @returns {?string} `null` if valid, otherwise the reason why it is not
388388
*/
389389
TypePrototype.verify = function verify(message) {
390-
var verifier = new Verifier(this);
391-
this.verify = codegen.supported
392-
? verifier.generate()
393-
: verifier.verify;
394-
return this.verify(message);
390+
return (this.verify = codegen.supported
391+
? verifier.generate(this).eof(this.getFullName() + "$verify", {
392+
types : this.getFieldsArray().map(function(fld) { return fld.resolvedType; })
393+
})
394+
: verifier.fallback
395+
).call(this, message);
395396
};

0 commit comments

Comments
 (0)