Comparing version 2.5.4 to 3.0.0
11
ast.js
@@ -62,2 +62,10 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
module.exports.Include = Include; | ||
function Include(id, namespace) { | ||
var self = this; | ||
self.id = id; | ||
self.namespace = namespace; | ||
} | ||
Include.prototype.type = 'Include'; | ||
module.exports.Namespace = Namespace; | ||
@@ -176,3 +184,3 @@ function Namespace(id, scope) { | ||
module.exports.Service = Service; | ||
function Service(id, functions, annotations) { | ||
function Service(id, functions, annotations, baseService) { | ||
var self = this; | ||
@@ -182,2 +190,3 @@ self.id = id; | ||
self.annotations = annotations; | ||
self.baseService = baseService; | ||
} | ||
@@ -184,0 +193,0 @@ Service.prototype.type = 'Service'; |
@@ -35,4 +35,5 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
ThriftBinary.prototype.surface = Buffer; | ||
ThriftBinary.prototype.models = 'type'; | ||
module.exports.BinaryRW = BinaryRW; | ||
module.exports.ThriftBinary = ThriftBinary; |
@@ -57,4 +57,5 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
ThriftBoolean.prototype.surface = Boolean; | ||
ThriftBoolean.prototype.models = 'type'; | ||
module.exports.BooleanRW = BooleanRW; | ||
module.exports.ThriftBoolean = ThriftBoolean; |
@@ -34,4 +34,5 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
ThriftByte.prototype.surface = Boolean; | ||
ThriftByte.prototype.models = 'type'; | ||
module.exports.ByteRW = ByteRW; | ||
module.exports.ThriftByte = ThriftByte; |
12
const.js
@@ -32,8 +32,16 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
ThriftConst.prototype.link = function link(spec) { | ||
ThriftConst.prototype.models = 'value'; | ||
ThriftConst.prototype.link = function link(model) { | ||
var self = this; | ||
if (!self.defined) { | ||
self.defined = true; | ||
self.value = spec.resolveValue(self.valueDefinition); | ||
self.value = model.resolveValue(self.valueDefinition); | ||
self.surface = self.value; | ||
model.consts[self.name] = self.value; | ||
// Alias if first character is not lower-case | ||
if (!/^[a-z]/.test(self.name)) { | ||
model[self.name] = self.value; | ||
} | ||
} | ||
@@ -40,0 +48,0 @@ return self; |
@@ -34,4 +34,5 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
ThriftDouble.prototype.surface = Number; | ||
ThriftDouble.prototype.models = 'type'; | ||
module.exports.DoubleRW = DoubleRW; | ||
module.exports.ThriftDouble = ThriftDouble; |
48
enum.js
@@ -42,7 +42,9 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
self.rw = new EnumRW(self); | ||
self.linked = false; | ||
} | ||
ThriftEnum.prototype.typeid = TYPE.I32; | ||
ThriftEnum.prototype.models = 'type'; | ||
ThriftEnum.prototype.compile = function compile(def, spec) { | ||
ThriftEnum.prototype.compile = function compile(def, model) { | ||
var self = this; | ||
@@ -70,10 +72,10 @@ | ||
var fullName = self.name + '.' + name; | ||
spec.claim(fullName, enumDef.id); | ||
spec.consts[fullName] = new ThriftConst( | ||
new ast.Const( | ||
new ast.Identifier(name), | ||
null, // TODO infer type for default value validation | ||
new ast.Literal(name) | ||
) | ||
var constDef = new ast.Const( | ||
new ast.Identifier(name), | ||
null, // TODO infer type for default value validation | ||
new ast.Literal(name) | ||
); | ||
var constModel = new ThriftConst(constDef); | ||
model.consts[fullName] = constModel; | ||
model.define(fullName, enumDef.id, constModel); | ||
self.namesToValues[name] = value; | ||
@@ -86,10 +88,24 @@ self.namesToNames[name] = name; | ||
ThriftEnum.prototype.link = function link(spec) { | ||
ThriftEnum.prototype.link = function link(model) { | ||
var self = this; | ||
if (self.linked) { | ||
return self; | ||
} | ||
self.linked = true; | ||
model.enums[self.name] = self.namesToNames; | ||
// Alias if first character is not lower-case | ||
// istanbul ignore else | ||
if (!/^[a-z]/.test(self.name)) { | ||
model[self.name] = self.surface; | ||
} | ||
return self; | ||
}; | ||
function EnumRW(spec) { | ||
function EnumRW(model) { | ||
var self = this; | ||
self.spec = spec; | ||
self.model = model; | ||
} | ||
@@ -108,3 +124,3 @@ | ||
return new WriteResult(errors.InvalidEnumerationTypeError({ | ||
enumName: self.spec.name, | ||
enumName: self.model.name, | ||
name: name, | ||
@@ -114,7 +130,7 @@ nameType: typeof name | ||
} | ||
var value = self.spec.namesToValues[name]; | ||
var value = self.model.namesToValues[name]; | ||
// istanbul ignore if | ||
if (value === undefined) { | ||
return new WriteResult(errors.InvalidEnumerationNameError({ | ||
enumName: self.spec.name, | ||
enumName: self.model.name, | ||
name: name | ||
@@ -136,6 +152,6 @@ })); | ||
var value = result.value; | ||
var name = self.spec.valuesToNames[value]; | ||
var name = self.model.valuesToNames[value]; | ||
if (!name) { | ||
return new ReadResult(errors.InvalidEnumerationValueError({ | ||
enumName: self.spec.name, | ||
enumName: self.model.name, | ||
value: value | ||
@@ -142,0 +158,0 @@ })); |
@@ -34,4 +34,5 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
ThriftI16.prototype.surface = Number; | ||
ThriftI16.prototype.models = 'type'; | ||
module.exports.I16RW = I16RW; | ||
module.exports.ThriftI16 = ThriftI16; |
@@ -34,4 +34,5 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
ThriftI32.prototype.surface = Number; | ||
ThriftI32.prototype.models = 'type'; | ||
module.exports.I32RW = I32RW; | ||
module.exports.ThriftI32 = ThriftI32; |
@@ -183,2 +183,3 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
ThriftI64.prototype.typeid = TYPE.I64; | ||
ThriftI64.prototype.models = 'type'; | ||
@@ -185,0 +186,0 @@ module.exports.I64RW = I64RW; |
11
list.js
@@ -31,3 +31,3 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
var self = this; | ||
self.valueType = valueType.name; | ||
self.valueType = valueType; | ||
self.rw = new ListRW(valueType, self); | ||
@@ -39,7 +39,8 @@ } | ||
ThriftList.prototype.surface = Array; | ||
ThriftList.prototype.models = 'type'; | ||
function ListRW(valueType, spec) { | ||
function ListRW(valueType, model) { | ||
var self = this; | ||
self.valueType = valueType; | ||
self.spec = spec; | ||
self.model = model; | ||
} | ||
@@ -124,3 +125,3 @@ | ||
expected: valueType.name, | ||
what: self.spec.name | ||
what: self.model.name | ||
})); | ||
@@ -131,3 +132,3 @@ } | ||
size: size, | ||
what: self.spec.name | ||
what: self.model.name | ||
})); | ||
@@ -134,0 +135,0 @@ } |
@@ -53,3 +53,4 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
ThriftMap.prototype.typeid = TYPE.MAP; | ||
ThriftMap.prototype.models = 'type'; | ||
module.exports.ThriftMap = ThriftMap; |
@@ -9,2 +9,27 @@ # Migration | ||
# Migrating from v2 to v3 | ||
If you have only ever used ThriftRW through the TChannelAsThrift interface, | ||
or if you have only ever used ThriftRW using the getType and getTypeResult | ||
interface, no changes are necessary. | ||
If you have used ThriftRW directly, the way you index types and values has | ||
changed. The thrift object proper now contains aliases to constructors, | ||
enumerations, constants, and other surfaces if they are in PascalCase. | ||
If you were accessing a constant before like `thrift.pi`, you will need to | ||
access it via the index, `thrift.consts.pi`, however `thrift.PI` will still | ||
work. | ||
The `types` index has been removed. | ||
All models including types are index on `thrift.models`. | ||
Each model has a property `models` which is one of the strings "service", | ||
"type", "module", or "value". | ||
The indexes on the Thrift object for "services", "structs", "exceptions", | ||
"unions", etc previously exposed the Thrift Spec/Model object for each class. | ||
They now expose the surface, so constructors for structs, the function index | ||
for services, etc. | ||
The models can only be accessed on the "models" index. | ||
# Upgrading from thriftify to thriftrw | ||
@@ -11,0 +36,0 @@ |
{ | ||
"name": "thriftrw", | ||
"version": "2.5.4", | ||
"version": "3.0.0", | ||
"description": "thrift encoding/decoding using bufrw", | ||
@@ -5,0 +5,0 @@ "keywords": [], |
@@ -147,3 +147,12 @@ # thriftrw | ||
Each service in the IDL gets revealed as an object by the same name on the Thrift instance. | ||
Thrift has internal models for modules, services, types, and values. | ||
These models are indexed by name on each Thrift module in the `models` object. | ||
Each of these models has a "surface", which is a constructor for types, values | ||
for constants, an index of functions for services, and the model itself for | ||
modules. | ||
Modules expose their names on their own object only if they start with a | ||
non-lower-case character, to avoid name collisions with other properties and | ||
methods, but are always also available through the index for their type class, | ||
like `thrift.consts.PI`. | ||
The Meta service is simply `thrift.Meta`. | ||
@@ -198,4 +207,4 @@ The object is an object mapping functions by name, so `thrift.Meta.health` is | ||
The internal ThriftStruct instance is also indexed by name on the thrift | ||
object's `thrift.structs` object. | ||
Structs are indexed by name on the `thrift.structs` object and aliased on the | ||
thrift object if their name does not start with a lower-case letter. | ||
@@ -226,4 +235,4 @@ ### Exceptions | ||
The ThriftException instance internal to the thrift compiler is indexed by name | ||
on `thrift.exceptions`. | ||
Exceptions are indexed by name on the `thrift.exceptions` object and aliased on | ||
the thrift object if their name does not start with a lower-case letter. | ||
@@ -265,4 +274,4 @@ ### Unions | ||
The compiler's internal representation of a ThriftUnion is indexed by name on | ||
`thrift.unions`. | ||
Unions are indexed by name on the `thrift.unions` object and aliased on the | ||
thrift object if their name does not start with a lower-case letter. | ||
@@ -297,2 +306,5 @@ ### Enums | ||
Enums are indexed by name on the `thrift.unions` object and aliased on the | ||
thrift object if their name does not start with a lower-case letter. | ||
### Consts | ||
@@ -312,4 +324,4 @@ | ||
Internal representations of consts are indexed by name on the `thrift.consts` | ||
object. | ||
Consts are indexed by name on the `thrift.consts` object and aliased on the | ||
thrift object if their name does not start with a lower-case letter. | ||
@@ -316,0 +328,0 @@ |
@@ -24,3 +24,2 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
var ast = require('./ast'); | ||
var ThriftStruct = require('./struct').ThriftStruct; | ||
@@ -32,9 +31,10 @@ function ThriftFunction(args) { | ||
self.fullName = self.service.name + '::' + self.name; | ||
self.spec = args.spec; | ||
self.model = args.model; | ||
self.args = null; | ||
self.result = null; | ||
self.strict = args.strict; | ||
self.linked = false; | ||
} | ||
ThriftFunction.prototype.compile = function process(def, spec) { | ||
ThriftFunction.prototype.compile = function process(def, model) { | ||
var self = this; | ||
@@ -44,3 +44,2 @@ | ||
self.args = new ThriftStruct({strict: self.strict}); | ||
var argsId = new ast.Identifier(self.name + '_args'); | ||
@@ -50,3 +49,3 @@ argsId.as = self.fullName + '_args'; | ||
argsStruct.isArgument = true; | ||
self.args = spec.compileStruct(argsStruct); | ||
self.args = model.compileStruct(argsStruct); | ||
self.Arguments = self.args.Constructor; | ||
@@ -69,3 +68,3 @@ | ||
resultStruct.isResult = true; | ||
self.result = spec.compileStruct(resultStruct); | ||
self.result = model.compileStruct(resultStruct); | ||
self.Result = self.result.Constructor; | ||
@@ -77,6 +76,6 @@ | ||
ThriftFunction.prototype.link = function link(spec) { | ||
ThriftFunction.prototype.link = function link(model) { | ||
var self = this; | ||
self.args.link(spec); | ||
self.result.link(spec); | ||
self.args.link(model); | ||
self.result.link(model); | ||
}; | ||
@@ -91,13 +90,18 @@ | ||
self.strict = args.strict; | ||
self.baseService = null; | ||
self.linked = false; | ||
} | ||
ThriftService.prototype.compile = function process(def, spec) { | ||
ThriftService.prototype.models = 'service'; | ||
ThriftService.prototype.compile = function process(def, model) { | ||
var self = this; | ||
self.name = def.id.name; | ||
for (var index = 0; index < def.functions.length; index++) { | ||
self.compileFunction(def.functions[index], spec); | ||
self.compileFunction(def.functions[index], model); | ||
} | ||
self.baseService = def.baseService; | ||
}; | ||
ThriftService.prototype.compileFunction = function processFunction(def, spec) { | ||
ThriftService.prototype.compileFunction = function processFunction(def, model) { | ||
var self = this; | ||
@@ -109,12 +113,45 @@ var thriftFunction = new ThriftFunction({ | ||
}); | ||
thriftFunction.compile(def, spec); | ||
thriftFunction.compile(def, model); | ||
self.addFunction(thriftFunction); | ||
}; | ||
ThriftService.prototype.addFunction = function addFunction(thriftFunction) { | ||
var self = this; | ||
self.functions.push(thriftFunction); | ||
self.functionsByName[thriftFunction.name] = thriftFunction; | ||
if (!self.functionsByName[thriftFunction.name]) { | ||
self.functionsByName[thriftFunction.name] = thriftFunction; | ||
} else { | ||
throw new Error(self.name + '.' + thriftFunction.name + ' already inherited from baseService'); | ||
} | ||
}; | ||
ThriftService.prototype.link = function link(spec) { | ||
ThriftService.prototype.link = function link(model) { | ||
var self = this; | ||
for (var index = 0; index < self.functions.length; index++) { | ||
self.functions[index].link(spec); | ||
var index = 0; | ||
if (self.linked) { | ||
return self; | ||
} | ||
self.linked = true; | ||
if (self.baseService) { | ||
var baseService = model.resolveIdentifier(self.baseService, self.baseService.name, 'service'); | ||
baseService.link(model); | ||
for (index = 0; index < baseService.functions.length; index++) { | ||
var thriftFunction = baseService.functions[index]; | ||
self.addFunction(thriftFunction); | ||
} | ||
} | ||
for (index = 0; index < self.functions.length; index++) { | ||
self.functions[index].link(model); | ||
} | ||
model.services[self.name] = self.surface; | ||
// istanbul ignore else | ||
if (!/^[a-z]/.test(self.name)) { | ||
model[self.name] = self.surface; | ||
} | ||
return self; | ||
@@ -121,0 +158,0 @@ }; |
@@ -66,2 +66,3 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
ThriftSet.prototype.altTypeid = TYPE.LIST; | ||
ThriftSet.prototype.models = 'type'; | ||
@@ -68,0 +69,0 @@ ThriftSet.prototype.arrayForm = { |
@@ -37,4 +37,5 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
ThriftString.prototype.surface = String; | ||
ThriftString.prototype.models = 'type'; | ||
module.exports.StringRW = StringRW; | ||
module.exports.ThriftString = ThriftString; |
@@ -61,11 +61,11 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
ThriftField.prototype.link = function link(spec) { | ||
ThriftField.prototype.link = function link(model) { | ||
var self = this; | ||
self.valueType = spec.resolve(self.valueDefinition); | ||
self.valueType = model.resolve(self.valueDefinition); | ||
assert(self.valueType, 'value type was defined, as returned by resolve'); | ||
}; | ||
ThriftField.prototype.linkValue = function linkValue(spec) { | ||
ThriftField.prototype.linkValue = function linkValue(model) { | ||
var self = this; | ||
self.defaultValue = spec.resolveValue(self.defaultValueDefinition); | ||
self.defaultValue = model.resolveValue(self.defaultValueDefinition); | ||
}; | ||
@@ -87,2 +87,3 @@ | ||
self.isResult = null; | ||
self.isException = options.isException || false; | ||
self.Constructor = null; | ||
@@ -98,2 +99,3 @@ self.surface = null; | ||
ThriftStruct.prototype.isUnion = false; | ||
ThriftStruct.prototype.models = 'type'; | ||
@@ -144,3 +146,3 @@ ThriftStruct.prototype.toBuffer = function toBuffer(struct) { | ||
ThriftStruct.prototype.link = function link(spec) { | ||
ThriftStruct.prototype.link = function link(model) { | ||
var self = this; | ||
@@ -158,3 +160,3 @@ | ||
var field = self.fields[index]; | ||
field.linkValue(spec); | ||
field.linkValue(model); | ||
@@ -193,5 +195,18 @@ // Validate field | ||
for (index = 0; index < self.fields.length; index++) { | ||
self.fields[index].link(spec); | ||
self.fields[index].link(model); | ||
} | ||
if (self.isUnion) { | ||
model.unions[self.name] = self.Constructor; | ||
} else if (self.isException) { | ||
model.exceptions[self.name] = self.Constructor; | ||
} else { | ||
model.structs[self.name] = self.Constructor; | ||
} | ||
// Alias if first character is not lower-case | ||
if (!/^[a-z]/.test(self.name)) { | ||
model[self.name] = self.Constructor; | ||
} | ||
return self; | ||
@@ -260,6 +275,6 @@ }; | ||
function StructRW(spec) { | ||
assert(spec, 'spec required'); | ||
function StructRW(model) { | ||
assert(model, 'model required'); | ||
var self = this; | ||
self.spec = spec; | ||
self.model = model; | ||
} | ||
@@ -271,4 +286,4 @@ | ||
var result; | ||
for (var index = 0; index < self.spec.fields.length; index++) { | ||
var field = self.spec.fields[index]; | ||
for (var index = 0; index < self.model.fields.length; index++) { | ||
var field = self.model.fields[index]; | ||
var value = struct && struct[field.name]; | ||
@@ -282,3 +297,3 @@ | ||
id: field.id, | ||
structName: self.spec.name, | ||
structName: self.model.name, | ||
what: struct | ||
@@ -310,4 +325,4 @@ })); | ||
var result; | ||
for (var index = 0; index < self.spec.fields.length; index++) { | ||
var field = self.spec.fields[index]; | ||
for (var index = 0; index < self.model.fields.length; index++) { | ||
var field = self.model.fields[index]; | ||
var value = struct && struct[field.name]; | ||
@@ -320,3 +335,3 @@ var available = value !== null && value !== undefined; | ||
id: field.id, | ||
structName: self.spec.name, | ||
structName: self.model.name, | ||
what: struct | ||
@@ -364,3 +379,3 @@ })); | ||
var self = this; | ||
var struct = self.spec.create(); | ||
var struct = self.model.create(); | ||
var result; | ||
@@ -391,3 +406,3 @@ | ||
// unrecognized exception. | ||
if (!self.spec.fieldsById[id] && self.spec.isResult) { | ||
if (!self.model.fieldsById[id] && self.model.isResult) { | ||
result = readType(buffer, offset, typeid); | ||
@@ -400,3 +415,3 @@ // result = skipType(buffer, offset, typeid); | ||
offset = result.offset; | ||
self.spec.set( | ||
self.model.set( | ||
struct, | ||
@@ -410,3 +425,3 @@ 'failure', | ||
// skip unrecognized fields from THE FUTURE | ||
if (!self.spec.fieldsById[id]) { | ||
if (!self.model.fieldsById[id]) { | ||
result = skipType(buffer, offset, typeid); | ||
@@ -421,3 +436,3 @@ // istanbul ignore if | ||
var field = self.spec.fieldsById[id]; | ||
var field = self.model.fieldsById[id]; | ||
if ( | ||
@@ -430,3 +445,3 @@ field.valueType.typeid !== typeid && | ||
fieldName: field.name, | ||
structName: self.spec.name, | ||
structName: self.model.name, | ||
typeid: typeid, | ||
@@ -446,7 +461,7 @@ typeName: NAMES[typeid], | ||
// TODO promote return error of set to a ReadResult error | ||
self.spec.set(struct, field.name, result.value); | ||
self.model.set(struct, field.name, result.value); | ||
} | ||
// Validate required fields | ||
var err = self.spec.validateStruct(struct); | ||
var err = self.model.validateStruct(struct); | ||
if (err) { | ||
@@ -456,3 +471,3 @@ return new ReadResult(err); | ||
return new ReadResult(null, offset, self.spec.finalize(struct)); | ||
return new ReadResult(null, offset, self.model.finalize(struct)); | ||
}; | ||
@@ -459,0 +474,0 @@ |
@@ -33,8 +33,8 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
thrift = new Thrift({source: source}); | ||
assert.equal(thrift.ten, 10, 'ten constant'); | ||
assert.equal(thrift.tenForward, 10, 'forward reference'); | ||
assert.deepEqual(thrift.edges, {0: 1, 1: 2}, 'map constant'); | ||
assert.deepEqual(thrift.names, ['a', 'ab', 'abc'], 'list constant'); | ||
assert.deepEqual(thrift.tens, [10, 10, 10], 'list of identifiers'); | ||
assert.equal(thrift.consts.ten, 10, 'ten constant'); | ||
assert.equal(thrift.consts.tenForward, 10, 'forward reference'); | ||
assert.deepEqual(thrift.consts.edges, {0: 1, 1: 2}, 'map constant'); | ||
assert.deepEqual(thrift.consts.names, ['a', 'ab', 'abc'], 'list constant'); | ||
assert.deepEqual(thrift.consts.tens, [10, 10, 10], 'list of identifiers'); | ||
assert.end(); | ||
}); |
@@ -29,7 +29,7 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
var source = fs.readFileSync(path.join(__dirname, 'default.thrift'), 'ascii'); | ||
var spec; | ||
var model; | ||
test('default values on structs work', function t(assert) { | ||
spec = new Thrift({source: source}); | ||
var health = new spec.Health({name: 'grand'}); | ||
model = new Thrift({source: source}); | ||
var health = new model.Health({name: 'grand'}); | ||
assert.equals(health.ok, true, 'default truth value passes through'); | ||
@@ -36,0 +36,0 @@ assert.equals(health.notOk, false, 'default false value passes through'); |
@@ -54,1 +54,3 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
require('./unrecognized-exception'); | ||
require('./include.js'); | ||
require('./type-mismatch'); |
@@ -64,1 +64,49 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
}); | ||
test('service extends from another service', function t(assert) { | ||
assert.deepEqual( | ||
Object.keys(thrift.Qux), | ||
['quux', 'foo', 'bar', 'returnsI32', 'returnsStruct'], | ||
'Service contains function from BaseService' | ||
); | ||
assert.deepEqual( | ||
Object.keys(thrift.Corge), | ||
['grault', 'quux', 'foo', 'bar', 'returnsI32', 'returnsStruct'], | ||
'Service contains function from BaseService' | ||
); | ||
assert.deepEqual( | ||
Object.keys(thrift.Garply), | ||
['waldo', 'quux', 'foo', 'bar', 'returnsI32', 'returnsStruct'], | ||
'Service contains function from BaseService' | ||
); | ||
assert.equal( | ||
thrift.Qux.foo, | ||
thrift.Foo.foo, | ||
'Function Qux.foo is the same as Foo.foo by reference' | ||
); | ||
assert.end(); | ||
}); | ||
test('service extends throws on duplicate function name', function t(assert) { | ||
assert.throws( | ||
duplicateFunction, | ||
/Foo.bar already inherited from baseService/, | ||
'Service extends throws if function with same name already exists' | ||
); | ||
function duplicateFunction() { | ||
var thriftFilePath = path.join( | ||
__dirname, | ||
'service-duplicate-function-error.thrift' | ||
); | ||
return new Thrift({ | ||
source: fs.readFileSync(thriftFilePath, 'ascii') | ||
}); | ||
} | ||
assert.end(); | ||
}); |
@@ -35,2 +35,3 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
var thriftMock = { | ||
structs: {}, | ||
resolve: function resolve() { | ||
@@ -37,0 +38,0 @@ // pretend all fields are boolean |
286
thrift.js
@@ -26,2 +26,4 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
var util = require('util'); | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var idl = require('./thrift-idl'); | ||
@@ -50,2 +52,4 @@ var Result = require('bufrw/result'); | ||
var validThriftIdentifierRE = /^[a-zA-Z_][a-zA-Z0-9_\.]+$/; | ||
function Thrift(options) { | ||
@@ -56,24 +60,70 @@ var self = this; | ||
assert(typeof options === 'object', 'options must be object'); | ||
assert(options.source, 'source required'); | ||
assert(typeof options.source === 'string', 'source must be string'); | ||
assert(options.source || options.thriftFile, | ||
'opts.source or opts.thriftFile required'); | ||
self.thriftFile = options.thriftFile ? | ||
path.resolve(options.thriftFile) : null; | ||
if (options.source) { | ||
assert(typeof options.source === 'string', 'source must be string'); | ||
self.source = options.source; | ||
} | ||
if (self.thriftFile && !self.source) { | ||
self.source = fs.readFileSync(self.thriftFile, 'ascii'); | ||
} | ||
self.strict = options.strict !== undefined ? options.strict : true; | ||
self.claims = Object.create(null); | ||
// [name] :Thrift* implementing {compile, link, &c} | ||
// Heterogenous Thrift model objects by name in a consolidated name-space | ||
// to prevent duplicate references with the same and different types, like | ||
// a service and a struct with the same name in the scope of a Thrift IDL | ||
// module: | ||
self.models = Object.create(null); | ||
// [serviceName][functionName] :{rw, Arguments, Result} | ||
self.services = Object.create(null); | ||
self.types = Object.create(null); | ||
// [constName] :Value | ||
self.consts = Object.create(null); | ||
// [enumName][name] :String | ||
self.enums = Object.create(null); | ||
// [structName] :Constructor | ||
self.structs = Object.create(null); | ||
// [exceptionName] :Constructor | ||
self.exceptions = Object.create(null); | ||
// [unionName] :Constructor | ||
self.unions = Object.create(null); | ||
// [typedefName] :Constructor (might be Array, Object, or Number) | ||
self.typedefs = Object.create(null); | ||
// [moduleName] :Thrift | ||
// Child modules indexed by their local alias. | ||
self.modules = Object.create(null); | ||
// Two passes permits forward references and cyclic references. | ||
// First pass constructs objects. | ||
self.compile(options.source); | ||
// Second pass links field references of structs. | ||
self.link(); | ||
self.surface = self; | ||
self.linked = false; | ||
self.filepathThriftMemo = options.filepathThriftMemo || Object.create(null); | ||
self.allowIncludeAlias = options.allowIncludeAlias || false; | ||
if (self.thriftFile) { | ||
self.dirname = path.dirname(self.thriftFile); | ||
self.filepathThriftMemo[self.thriftFile] = self; | ||
} | ||
if (options.source) { | ||
// Two passes permits forward references and cyclic references. | ||
self.compile(); | ||
self.link(); | ||
} | ||
} | ||
Thrift.loadSync = function loadSync(options) { | ||
var thrift = new Thrift(options); | ||
thrift.compile(); | ||
thrift.link(); | ||
return thrift; | ||
}; | ||
Thrift.prototype.models = 'module'; | ||
Thrift.prototype.getType = function getType(name) { | ||
@@ -86,7 +136,7 @@ var self = this; | ||
var self = this; | ||
var type = self.types[name]; | ||
if (!type) { | ||
var model = self.models[name]; | ||
if (!model || model.models !== 'type') { | ||
return new Result(new Error(util.format('type %s not found', name))); | ||
} | ||
return new Result(null, type.link()); | ||
return new Result(null, model.link(self)); | ||
}; | ||
@@ -106,16 +156,17 @@ | ||
Thrift.prototype.compile = function compile(source) { | ||
Thrift.prototype.compile = function compile() { | ||
var self = this; | ||
var syntax = idl.parse(source); | ||
var syntax = idl.parse(self.source); | ||
assert.equal(syntax.type, 'Program', 'expected a program'); | ||
self.compileDefinitions(syntax.definitions); | ||
self._compile(syntax.headers); | ||
self._compile(syntax.definitions); | ||
}; | ||
Thrift.prototype.claim = function claim(name, def) { | ||
Thrift.prototype.define = function define(name, def, model) { | ||
var self = this; | ||
assert(!self.claims[name], 'duplicate reference to ' + name + ' at ' + def.line + ':' + def.column); | ||
self.claims[name] = true; | ||
assert(!self.models[name], 'duplicate reference to ' + name + ' at ' + def.line + ':' + def.column); | ||
self.models[name] = model; | ||
}; | ||
Thrift.prototype._definitionProcessors = { | ||
Thrift.prototype.compilers = { | ||
// sorted | ||
@@ -125,2 +176,3 @@ Const: 'compileConst', | ||
Exception: 'compileException', | ||
Include: 'compileInclude', | ||
Service: 'compileService', | ||
@@ -132,9 +184,10 @@ Struct: 'compileStruct', | ||
Thrift.prototype.compileDefinitions = function compileDefinitions(defs) { | ||
Thrift.prototype._compile = function _compile(defs) { | ||
var self = this; | ||
for (var index = 0; index < defs.length; index++) { | ||
var def = defs[index]; | ||
var compilerName = self.compilers[def.type]; | ||
// istanbul ignore else | ||
if (self._definitionProcessors[def.type]) { | ||
self[self._definitionProcessors[def.type]](def); | ||
if (compilerName) { | ||
self[compilerName](def); | ||
} | ||
@@ -144,10 +197,60 @@ } | ||
Thrift.prototype.compileInclude = function compileInclude(def) { | ||
var self = this; | ||
assert( | ||
self.dirname, | ||
'Must set opts.thriftFile on instantiation to resolve include paths' | ||
); | ||
if (def.id.lastIndexOf('./', 0) === 0 || | ||
def.id.lastIndexOf('../', 0) === 0) { | ||
var thriftFile = path.resolve(self.dirname, def.id); | ||
var ns = def.namespace && def.namespace.name; | ||
// If include isn't name, get filename sans *.thrift file extension. | ||
if (!self.allowIncludeAlias || !ns) { | ||
var basename = path.basename(def.id); | ||
ns = basename.slice(0, basename.length - 7); | ||
if (!validThriftIdentifierRE.test(ns)) { | ||
throw Error( | ||
'Thrift include filename is not valid thrift identifier' | ||
); | ||
} | ||
} | ||
var model; | ||
if (self.filepathThriftMemo[thriftFile]) { | ||
model = self.filepathThriftMemo[thriftFile]; | ||
} else { | ||
model = new Thrift({ | ||
thriftFile: thriftFile, | ||
strict: self.strict, | ||
filepathThriftMemo: self.filepathThriftMemo, | ||
allowIncludeAlias: true | ||
}); | ||
model.compile(); | ||
} | ||
self.define(ns, def, model); | ||
// Alias if first character is not lower-case | ||
self.modules[ns] = model; | ||
if (!/^[a-z]/.test(ns)) { | ||
self[ns] = model; | ||
} | ||
} else { | ||
throw Error('Include path string must start with either ./ or ../'); | ||
} | ||
}; | ||
Thrift.prototype.compileStruct = function compileStruct(def) { | ||
var self = this; | ||
var spec = new ThriftStruct({strict: self.strict}); | ||
spec.compile(def, self); | ||
self.claim(spec.fullName, def); | ||
self.structs[spec.fullName] = spec; | ||
self.types[spec.fullName] = spec; | ||
return spec; | ||
var model = new ThriftStruct({strict: self.strict}); | ||
model.compile(def, self); | ||
self.define(model.fullName, def, model); | ||
return model; | ||
}; | ||
@@ -157,8 +260,6 @@ | ||
var self = this; | ||
var spec = new ThriftStruct({strict: self.strict}); | ||
spec.compile(def, self); | ||
self.claim(spec.fullName, def); | ||
self.exceptions[spec.fullName] = spec; | ||
self.types[spec.fullName] = spec; | ||
return spec; | ||
var model = new ThriftStruct({strict: self.strict, isException: true}); | ||
model.compile(def, self); | ||
self.define(model.fullName, def, model); | ||
return model; | ||
}; | ||
@@ -168,8 +269,6 @@ | ||
var self = this; | ||
var spec = new ThriftUnion({strict: self.strict}); | ||
spec.compile(def, self); | ||
self.claim(spec.fullName, def); | ||
self.unions[spec.fullName] = spec; | ||
self.types[spec.fullName] = spec; | ||
return spec; | ||
var model = new ThriftUnion({strict: self.strict}); | ||
model.compile(def, self); | ||
self.define(model.fullName, def, model); | ||
return model; | ||
}; | ||
@@ -179,8 +278,6 @@ | ||
var self = this; | ||
var spec = new ThriftTypedef(); | ||
spec.compile(def, self); | ||
self.claim(spec.name, spec); | ||
self.typedefs[spec.name] = spec; | ||
self.types[spec.name] = spec; | ||
return spec; | ||
var model = new ThriftTypedef({strict: self.strict}); | ||
model.compile(def, self); | ||
self.define(model.name, def, model); | ||
return model; | ||
}; | ||
@@ -192,11 +289,9 @@ | ||
service.compile(def, self); | ||
self.claim(service.name, def.id); | ||
self.services[service.name] = service; | ||
self.define(service.name, def.id, service); | ||
}; | ||
Thrift.prototype.compileConst = function compileConst(def, spec) { | ||
Thrift.prototype.compileConst = function compileConst(def, model) { | ||
var self = this; | ||
var thriftConst = new ThriftConst(def); | ||
self.claim(def.id.name, def.id); | ||
self.consts[def.id.name] = thriftConst; | ||
self.define(def.id.name, def.id, thriftConst); | ||
}; | ||
@@ -206,7 +301,5 @@ | ||
var self = this; | ||
var spec = new ThriftEnum(); | ||
spec.compile(def, self); | ||
self.claim(spec.name, def.id); | ||
self.enums[spec.name] = spec; | ||
self.types[spec.name] = spec; | ||
var model = new ThriftEnum(); | ||
model.compile(def, self); | ||
self.define(model.name, def.id, model); | ||
}; | ||
@@ -216,22 +309,14 @@ | ||
var self = this; | ||
var index; | ||
var typeNames = Object.keys(self.types); | ||
for (index = 0; index < typeNames.length; index++) { | ||
var type = self.types[typeNames[index]]; | ||
self[type.name] = type.link(self).surface; | ||
if (self.linked) { | ||
return self; | ||
} | ||
self.linked = true; | ||
var serviceNames = Object.keys(self.services); | ||
for (index = 0; index < serviceNames.length; index++) { | ||
var service = self.services[serviceNames[index]]; | ||
self[service.name] = service.link(self).surface; | ||
var names = Object.keys(self.models); | ||
for (var index = 0; index < names.length; index++) { | ||
self.models[names[index]].link(self); | ||
} | ||
var constNames = Object.keys(self.consts); | ||
for (index = 0; index < constNames.length; index++) { | ||
var thriftConst = self.consts[constNames[index]]; | ||
self.consts[constNames[index]] = thriftConst.link(self); | ||
self[thriftConst.name] = thriftConst.link(self).surface; | ||
} | ||
return self; | ||
}; | ||
@@ -241,14 +326,7 @@ | ||
var self = this; | ||
var err; | ||
// istanbul ignore else | ||
if (def.type === 'BaseType') { | ||
return new self.baseTypes[def.baseType](def.annotations); | ||
} else if (def.type === 'Identifier') { | ||
if (!self.types[def.name]) { | ||
err = new Error('cannot resolve reference to ' + def.name + ' at ' + def.line + ':' + def.column); | ||
err.line = def.line; | ||
err.column = def.column; | ||
throw err; | ||
} | ||
return self.types[def.name].link(self); | ||
// istanbul ignore else | ||
return self.resolveIdentifier(def, def.name, 'type'); | ||
} else if (def.type === 'List') { | ||
@@ -261,9 +339,10 @@ return new ThriftList(self.resolve(def.valueType), def.annotations); | ||
} else { | ||
assert.fail(util.format('Can\'t get reader/writer for definition with unknown type %s', def.type)); | ||
assert.fail(util.format('Can\'t get reader/writer for definition with unknown type %s at %s:%s', def.type, def.line, def.column)); | ||
} | ||
}; | ||
// TODO thread type model and validate / coerce | ||
Thrift.prototype.resolveValue = function resolveValue(def) { | ||
var self = this; | ||
var err; | ||
// istanbul ignore else | ||
if (!def) { | ||
@@ -277,3 +356,2 @@ return null; | ||
return self.resolveMapConst(def); | ||
// istanbul ignore else | ||
} else if (def.type === 'Identifier') { | ||
@@ -285,10 +363,3 @@ if (def.name === 'true') { | ||
} | ||
// istanbul ignore if | ||
if (!self.consts[def.name]) { | ||
err = new Error('cannot resolve reference to ' + def.name + ' at ' + def.line + ':' + def.column); | ||
err.line = def.line; | ||
err.column = def.column; | ||
throw err; | ||
} | ||
return self.consts[def.name].link(self).surface; | ||
return self.resolveIdentifier(def, def.name, 'value').value; | ||
} else { | ||
@@ -318,2 +389,35 @@ assert.fail('unrecognized const type ' + def.type); | ||
Thrift.prototype.resolveIdentifier = function resolveIdentifier(def, name, models) { | ||
var self = this; | ||
var model; | ||
// short circuit if in global namespace of this thrift. | ||
if (self.models[name]) { | ||
model = self.models[name].link(self); | ||
if (model.models !== models) { | ||
err = new Error( | ||
'type mismatch for ' + def.name + ' at ' + def.line + ':' + def.column + | ||
', expects ' + models + ', got ' + model.models | ||
); | ||
err.line = def.line; | ||
err.column = def.column; | ||
throw err; | ||
} | ||
return model; | ||
} | ||
var parts = name.split('.'); | ||
var err; | ||
var module = self.modules[parts.shift()]; | ||
if (module) { | ||
return module.resolveIdentifier(def, parts.join('.'), models); | ||
} else { | ||
err = new Error('cannot resolve reference to ' + def.name + ' at ' + def.line + ':' + def.column); | ||
err.line = def.line; | ||
err.column = def.column; | ||
throw err; | ||
} | ||
}; | ||
module.exports.Thrift = Thrift; |
@@ -30,3 +30,5 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
ThriftTypedef.prototype.compile = function compile(def, spec) { | ||
ThriftTypedef.prototype.models = 'type'; | ||
ThriftTypedef.prototype.compile = function compile(def, model) { | ||
var self = this; | ||
@@ -37,7 +39,8 @@ self.name = def.id.name; | ||
ThriftTypedef.prototype.link = function link(spec) { | ||
ThriftTypedef.prototype.link = function link(model) { | ||
var self = this; | ||
if (!self.to) { | ||
self.to = spec.resolve(self.valueDefinition); | ||
self.to = model.resolve(self.valueDefinition); | ||
} | ||
model.typedefs[self.name] = self.to.surface; | ||
return self.to; | ||
@@ -44,0 +47,0 @@ }; |
@@ -34,2 +34,3 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
ThriftUnion.prototype.isUnion = true; | ||
ThriftUnion.prototype.models = 'type'; | ||
@@ -36,0 +37,0 @@ ThriftUnion.prototype.createConstructor = function createConstructor(name, fields) { |
@@ -34,2 +34,4 @@ // Copyright (c) 2015 Uber Technologies, Inc. | ||
ThriftVoid.prototype.typeid = TYPE.VOID; | ||
ThriftVoid.prototype.models = 'type'; | ||
// istanbul ignore next | ||
@@ -36,0 +38,0 @@ ThriftVoid.prototype.surface = function Void() { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
390256
128
7015
482
18