blob: 40a769d3d6a19c20f13d90ef472e65065d78302d [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/**
* @fileoverview Constructor for Type. This is temporary and the type registry
* should be used in its place.
* TODO(alexfandrianto): If this is temporary, how can type registry take the
* place of the Type Constructor?
* @private
*/
module.exports = Type;
var kind = require('./kind');
var canonicalize; // Must be lazily-required to avoid circular dependency.
/**
* @summary Creates a new Type.
*
* @description <p>Without o, the Type is incomplete and must be filled in
* further.</p>
* <p>Notably, each type has a kind, which implies the existence of other
* fields. Type can be optionally constructed with an object, which has the
* option of being canonicalized.</p>
* <p>Note: This sidesteps a cyclic dependency with injection. During module
* setup, any calls to the Type constructor with a type object should also set
* skipValidation to true.</p>
* @constructor
* @memberof module:vanadium.vdl
* @param {Object=} o An object whose fields match those of a TypeObject.
* @param {boolean=} skipValidation Flag to skip validation. Defaults to false.
*/
function Type(o, skipValidation) {
if (o === undefined) {
o = {};
} else if (!skipValidation) {
// Canonicalize the given type object.
canonicalize = canonicalize || require('./canonicalize');
o = canonicalize.type(o);
}
this.name = '';
// Copy over o's fields into this type.
// Note: This is a shallow copy. If o is referenced cyclically, the reference
// is lost. Use canonicalize.type instead.
Object.keys(o).forEach(function(k) {
this[k] = o[k];
}, this);
}
Type.prototype._type = new Type();
Type.prototype._type.kind = kind.TYPEOBJECT;
/**
* Checks for equality
* @param {*} other The value to check for equality against.
* @return {boolean} True iff other equals this.
*/
Type.prototype.equals = function(other) {
if (this === other) {
return true;
}
return other && this.name === other.name && (other instanceof Type) &&
this.toString() === other.toString();
};
/**
* Freeze a type, setting its _unique string.
*/
Type.prototype.freeze = function() {
if (!Object.isFrozen(this)) {
var descriptor = {
value: this.toString()
};
Object.defineProperty(this, '_unique', descriptor);
Object.freeze(this);
}
};
/**
* Get a human-readable string for this type.
* @return {string} The human-readable string for this type
*/
Type.prototype.toString = function() {
if (this._unique) {
return this._unique;
}
return uniqueTypeStr(this, []);
};
/**
* <p>Compute a unique type string that breaks cycles.</p>
*
* <p>Note: This logic replicates that of uniqueTypeStr in type_builder.go.</p>
* @private
* @param {Type} t The type whose unique type string is needed
* @param {Array} seen A list of seen type references
* @return {string} The string representation of the given type
*/
function uniqueTypeStr(t, seen) {
if (seen.indexOf(t) !== -1 && t.name !== '') {
return t.name;
}
seen.push(t);
var s = t.name;
if (s !== '') {
s += ' ';
}
switch (t.kind) {
case kind.OPTIONAL:
return s + '?' + uniqueTypeStr(t.elem, seen);
case kind.ENUM:
return s + 'enum{' + t.labels.join(';') + '}';
case kind.ARRAY:
return s + '[' + t.len + ']' + uniqueTypeStr(t.elem, seen);
case kind.LIST:
return s + '[]' + uniqueTypeStr(t.elem, seen);
case kind.SET:
return s + 'set[' + uniqueTypeStr(t.key, seen) + ']';
case kind.MAP:
return s + 'map[' + uniqueTypeStr(t.key, seen) + ']' +
uniqueTypeStr(t.elem, seen);
case kind.STRUCT:
case kind.UNION:
if (t.kind === kind.STRUCT) {
s += 'struct{';
} else {
s += 'union{';
}
t.fields.forEach(function (f, index) {
if (index > 0) {
s += ';';
}
s += f.name + ' ' + uniqueTypeStr(f.type, seen);
});
return s + '}';
default:
return s + t.kind;
}
}