Skip to content

Commit

Permalink
Merge pull request #59 from Medium/nicks/lazy
Browse files Browse the repository at this point in the history
Evaluate template objects lazily. Reduces medium protogen from 70s -> 2.5s
  • Loading branch information
nicks authored Oct 3, 2016
2 parents 5bcfdd3 + 6cdf8f2 commit be57612
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 36 deletions.
7 changes: 6 additions & 1 deletion lib/descriptors/ExtendDescriptor.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,14 @@ ExtendDescriptor.prototype.inspect = function () {

/** @override */
ExtendDescriptor.prototype.toTemplateObject = function () {
var fields = null
var self = this
return {
name: this._name,
fields: helper.values(this._fields, helper.toTemplateObject)
get fields() {
if (fields) return fields
return ((fields = helper.values(self._fields, helper.toTemplateObject)))
}
}
}

Expand Down
11 changes: 8 additions & 3 deletions lib/descriptors/FieldDescriptor.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ module.exports = FieldDescriptor

/** @override */
FieldDescriptor.prototype.toTemplateObject = function () {
var typeDescriptor = null
var self = this

return {
name: this._name,
camelName: this.getCamelName(),
Expand All @@ -41,9 +44,11 @@ FieldDescriptor.prototype.toTemplateObject = function () {
rawType: this._type,
baseType: this.getBaseType(),
type: this.getType(),
typeDescriptor: this._typeDescriptor ?
this._typeDescriptor.toTemplateObject(true /* skipFields to avoid infinite loops */) :
null,
get typeDescriptor() {
if (typeDescriptor) return typeDescriptor
if (!self._typeDescriptor) return null
return ((typeDescriptor = self._typeDescriptor.toTemplateObject()))
},
tag: this._tag,
isOptional: this._isOptional,
isRepeated: this._isRepeated,
Expand Down
37 changes: 24 additions & 13 deletions lib/descriptors/MessageDescriptor.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,24 +37,35 @@ MessageDescriptor.prototype.inspect = function () {
return util.inspect(this.toTemplateObject(), false, null)
}

/** @override */
MessageDescriptor.prototype.toTemplateObject = function () {
var fields = null
var oneofs = null
var messages = null
var enums = null
var self = this

/**
* @param {boolean=} opt_skipFields
* @override
*/
MessageDescriptor.prototype.toTemplateObject = function (opt_skipFields) {
var obj = {
name: this._name,
fullName: helper.joinPackage(this._package, this._name),
camelName: this.getCamelName(),
options: this._options
}

if (!opt_skipFields) {
obj.fields = helper.values(this._fields, helper.toTemplateObject),
obj.oneofs = this._oneofs.map(helper.toTemplateObject),
obj.messages = helper.values(this._messages, helper.toTemplateObject),
obj.enums = helper.values(this._enums, helper.toTemplateObject)
options: this._options,
get fields() {
if (fields) return fields
return ((fields = helper.values(self._fields, helper.toTemplateObject)))
},
get oneofs() {
if (oneofs) return oneofs
return ((oneofs = helper.values(self._oneofs, helper.toTemplateObject)))
},
get messages() {
if (messages) return messages
return ((messages = helper.values(self._messages, helper.toTemplateObject)))
},
get enums() {
if (enums) return enums
return ((enums = helper.values(self._enums, helper.toTemplateObject)))
}
}

return obj
Expand Down
20 changes: 15 additions & 5 deletions lib/descriptors/MethodDescriptor.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ module.exports = MethodDescriptor

/** @override */
MethodDescriptor.prototype.toTemplateObject = function () {
var inputTypeDescriptor = null
var outputTypeDescriptor = null
var self = this

return {
name: this._name,
camelName: this.getCamelName(),
Expand All @@ -47,10 +51,17 @@ MethodDescriptor.prototype.toTemplateObject = function () {
rawInputType: this._inputType,
rawOutputType: this._outputType,

inputTypeDescriptor: this._inputTypeDescriptor ?
this._inputTypeDescriptor.toTemplateObject() : null,
outputTypeDescriptor: this._outputTypeDescriptor ?
this._outputTypeDescriptor.toTemplateObject() : null,
get inputTypeDescriptor() {
if (inputTypeDescriptor) return inputTypeDescriptor
if (!self._inputTypeDescriptor) return null
return ((inputTypeDescriptor = self._inputTypeDescriptor.toTemplateObject()))
},

get outputTypeDescriptor() {
if (outputTypeDescriptor) return outputTypeDescriptor
if (!self._outputTypeDescriptor) return null
return ((outputTypeDescriptor = self._outputTypeDescriptor.toTemplateObject()))
},

options: this._options
}
Expand Down Expand Up @@ -162,4 +173,3 @@ MethodDescriptor.prototype.addOption = function (name, value) {
MethodDescriptor.prototype.getOption = function (name) {
return this._options[name]
}

38 changes: 26 additions & 12 deletions lib/descriptors/ProtoDescriptor.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ function ProtoDescriptor(filePath) {
this._services = {}
this._enums = {}
this._extends = {}

this._recursing = false
}
util.inherits(ProtoDescriptor, Descriptor)
module.exports = ProtoDescriptor
Expand All @@ -50,23 +48,39 @@ ProtoDescriptor.prototype.inspect = function () {

/** @override */
ProtoDescriptor.prototype.toTemplateObject = function () {
if (this._recursing) {
throw new Error('import loop detected: ' + this.getName())
}
this._recursing = true
var messages = null
var services = null
var extendObjs = null
var enums = null
var imports = null
var self = this

var result = {
name: this.getName(),
package: this._package,
options: this._options,
messages: helper.values(this._messages, helper.toTemplateObject),
services: helper.values(this._services, helper.toTemplateObject),
extends: helper.values(this._messages, helper.toTemplateObject),
enums: helper.values(this._enums, helper.toTemplateObject),
importNames: this._importNames,
imports: this._imports.map(helper.toTemplateObject)
get messages() {
if (messages) return messages
return ((messages = helper.values(self._messages, helper.toTemplateObject)))
},
get services() {
if (services) return services
return ((services = helper.values(self._services, helper.toTemplateObject)))
},
get extends() {
if (extendObjs) return extendObjs
return ((extendObjs = helper.values(self._extends, helper.toTemplateObject)))
},
get enums() {
if (enums) return enums
return ((enums = helper.values(self._enums, helper.toTemplateObject)))
},
get imports() {
if (imports) return imports
return ((imports = self._imports.map(helper.toTemplateObject)))
}
}
this._recursing = false
return result
}

Expand Down
7 changes: 6 additions & 1 deletion lib/descriptors/ServiceDescriptor.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,15 @@ ServiceDescriptor.prototype.inspect = function () {

/** @override */
ServiceDescriptor.prototype.toTemplateObject = function () {
var methods = null
var self = this
return {
name: this._name,
fullName: helper.joinPackage(this._package, this._name),
methods: this._methodSequence.map(helper.toTemplateObject)
get methods() {
if (methods) return methods
return ((methods = self._methodSequence.map(helper.toTemplateObject)))
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion tests/project_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ builder.add(function testTypeResolutionLoop(test) {
var dee = project.getProtos('protos/loop.proto')[0].getMessage('TweedleDee')
var dum = dee.getField('dum')
test.equal('TweedleDum', dum.toTemplateObject().typeDescriptor.name)
test.equal(null, dum.toTemplateObject().typeDescriptor.fields)
test.equal(1, dum.toTemplateObject().typeDescriptor.fields.length)
test.done()
})

Expand Down

0 comments on commit be57612

Please sign in to comment.