reign
Version:
A persistent, typed-objects implementation.
483 lines (448 loc) • 13.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createInitializeStruct = createInitializeStruct;
exports.createStoreStruct = createStoreStruct;
exports.createAccepts = createAccepts;
exports.createToJSON = createToJSON;
exports.createClearStruct = createClearStruct;
exports.createStructDestructor = createStructDestructor;
exports.createCompareAddresses = createCompareAddresses;
exports.createEqual = createEqual;
exports.createCompareValues = createCompareValues;
exports.createCompareAddressValue = createCompareAddressValue;
exports.createHashStruct = createHashStruct;
exports.createRandomValue = createRandomValue;
exports.isValidIdentifier = isValidIdentifier;
var _backing = require("backing");
var _backing2 = _interopRequireDefault(_backing);
var _symbols = require("../../symbols");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Creates a function which can initialize a new struct, either
* via a config object or by using default (empty) field values.
*/
function createInitializeStruct(Partial, fields) {
const defaults = [];
const setValues = fields.map((field, index) => {
const name = field.name;
const type = field.type;
const offset = field.offset;
defaults.push(field.default);
if (isValidIdentifier(name)) {
return `
type${ index }.initialize(backing, address + ${ offset }, (tmp = input.${ name }) !== undefined ? tmp : defaults[${ index }]());
`;
} else {
const sanitizedName = JSON.stringify(name);
return `
type${ index }.initialize(backing, address + ${ offset }, (tmp = input[${ sanitizedName }]) !== undefined ? tmp : defaults[${ index }]());
`;
}
}).join('');
const setEmpty = fields.map((_ref, index) => {
let name = _ref.name;
let type = _ref.type;
let offset = _ref.offset;
return `
type${ index }.initialize(backing, address + ${ offset }, defaults[${ index }]());
`;
}).join('');
const argNames = ['$Address', 'Partial', 'defaults', ...fields.map((_, index) => `type${ index }`)];
const args = [_symbols.$Address, Partial, defaults, ...fields.map(field => field.type)];
const body = `
"use strict";
return function initializeStruct (backing, address, input) {
if (input == null) {
${ setEmpty }
}
else if (input instanceof Partial) {
backing.copy(address, input[$Address], Partial.byteLength);
}
else {
var tmp;
${ setValues }
}
};
`;
return Function(...argNames, body)(...args);
}
/**
* Creates a function which can write a struct, either
* via a config object or by using default (empty) field values.
*/
function createStoreStruct(Partial, fields) {
const defaults = [];
const setValues = fields.map((field, index) => {
const name = field.name;
const type = field.type;
const offset = field.offset;
defaults.push(field.default);
if (isValidIdentifier(name)) {
return `
type${ index }.store(backing, address + ${ offset }, (tmp = input.${ name }) !== undefined ? tmp : defaults[${ index }]);
`;
} else {
const sanitizedName = JSON.stringify(name);
return `
type${ index }.store(backing, address + ${ offset }, (tmp = input[${ sanitizedName }]) !== undefined ? tmp : defaults[${ index }]);
`;
}
}).join('');
const setEmpty = fields.map((_ref2, index) => {
let name = _ref2.name;
let type = _ref2.type;
let offset = _ref2.offset;
return `
type${ index }.store(backing, address + ${ offset }, defaults[${ index }]);
`;
}).join('');
const argNames = ['$Address', 'Partial', 'defaults', ...fields.map((_, index) => `type${ index }`)];
const args = [_symbols.$Address, Partial, defaults, ...fields.map(field => field.type)];
const body = `
"use strict";
return function storeStruct (backing, address, input) {
if (input == null) {
${ setEmpty }
}
else if (input instanceof Partial) {
backing.copy(address, input[$Address], Partial.byteLength);
}
else {
var tmp;
${ setValues }
}
};
`;
return Function(...argNames, body)(...args);
}
/**
* Create the `.accepts()` method for a list of fields.
*/
function createAccepts(fields) {
const argNames = ['$Address', ...fields.map((_, index) => `type${ index }`)];
const args = [_symbols.$Address, ...fields.map(field => field.type)];
const body = `
"use strict";
return function accepts (input) {
if (input == null || typeof input !== 'object') {
return false;
}
${ fields.map((_ref3, index) => {
let name = _ref3.name;
let type = _ref3.type;
let offset = _ref3.offset;
return `
if (!type${ index }.accepts(input${ isValidIdentifier(name) ? `.${ name }` : `[${ JSON.stringify(name) }]` })) {
return false;
}
`;
}).join('') }
return true;
};
`;
return Function(...argNames, body)(...args);
}
/**
* Create the `.toJSON()` method for a list of fields.
*/
function createToJSON(fields) {
return Function(`
"use strict";
return {
${ fields.map(_ref4 => {
let name = _ref4.name;
if (isValidIdentifier(name)) {
return `${ name }: this.${ name }`;
} else {
const sanitizedName = JSON.stringify(name);
return `${ sanitizedName }: this[${ sanitizedName }]`;
}
}).join(',\n ') }
};`);
}
/**
* Create a function which can clear the given struct fields.
*/
function createClearStruct(fields) {
const clearable = fields.filter(_ref5 => {
let type = _ref5.type;
return typeof type.clear === 'function';
});
const clearers = clearable.map(field => field.type.clear);
const names = clearable.map((_, index) => `clear_${ index }`);
const body = `
"use strict";
return function clearStruct (backing, address) {
${ names.map((name, index) => `${ name }(backing, address + ${ clearable[index].offset });`).join('\n ') }
};
`;
return Function(...names, body)(...clearers);
}
/**
* Create a function which can destroy the given struct fields.
*/
function createStructDestructor(fields) {
const clearable = fields.filter(_ref6 => {
let type = _ref6.type;
/* Issue 252 */
return type[_symbols.$CanContainReferences] && typeof type.clear === 'function';
});
const clearers = clearable.map(field => field.type);
const names = clearable.map((_, index) => `clearable_${ index }`);
const body = `
"use strict";
return function destructor (backing, address) {
${ names.map((name, index) => `${ name }.clear(backing, address + ${ clearable[index].offset });`).join('\n ') }
};
`;
return Function(...names, body)(...clearers);
}
/**
* Create a function which can compare two structs by address.
*/
function createCompareAddresses(fields) {
const checkAddresses = fields.map((_ref7, index) => {
let offset = _ref7.offset;
return `
else if ((tmp = type${ index }.compareAddresses(backing, a + ${ offset }, b + ${ offset })) !== 0) {
return tmp;
}
`;
}).join('');
const argNames = fields.map((_, index) => `type${ index }`);
const args = fields.map(_ref8 => {
let type = _ref8.type;
return type;
});
const body = `
"use strict";
return function compareAddresses (backing, a, b) {
var tmp;
if (a === b) {
return 0;
}
else if (a === 0) {
return -1;
}
else if (b === 0) {
return 1;
}
${ checkAddresses }
else {
return 0;
}
};
`;
return Function(...argNames, body)(...args);
}
/**
* Create a function which can determine whether two structs are structurally equal.
*/
function createEqual(fields) {
const checkValues = fields.map((_ref9, index) => {
let name = _ref9.name;
let type = _ref9.type;
if (typeof type.equal !== 'function') {
throw new Error(`Type ${ type.name } does not have an equal() method.`);
}
if (isValidIdentifier(name)) {
return `
else if (!type${ index }.equal(a.${ name }, b.${ name })) {
return false;
}
`;
} else {
const sanitizedName = JSON.stringify(name);
return `
else if (!type${ index }.equal(a[${ sanitizedName }], b[${ sanitizedName }])) {
return false;
}
`;
}
}).join('');
const argNames = fields.map((_, index) => `type${ index }`);
const args = fields.map(_ref10 => {
let type = _ref10.type;
return type;
});
const body = `
"use strict";
return function equal (a, b) {
if (a === b) {
return true;
}
else if (a == null || b == null) {
return false;
}
${ checkValues }
else {
return true;
}
};
`;
return Function(...argNames, body)(...args);
}
/**
* Create a function which can compare two struct instances.
*/
function createCompareValues(fields) {
const checkValues = fields.map((_ref11, index) => {
let name = _ref11.name;
if (isValidIdentifier(name)) {
return `
else if ((tmp = type${ index }.compareValues(a.${ name }, b.${ name })) !== 0) {
return tmp;
}
`;
} else {
const sanitizedName = JSON.stringify(name);
return `
else if ((tmp = type${ index }.compareValues(a[${ sanitizedName }], b[${ sanitizedName }])) !== 0) {
return tmp;
}
`;
}
}).join('');
const argNames = fields.map((_, index) => `type${ index }`);
const args = fields.map(_ref12 => {
let type = _ref12.type;
return type;
});
const body = `
"use strict";
return function compareValues (a, b) {
var tmp;
if (a === b) {
return 0;
}
else if (a == null) {
return -1;
}
else if (b == null) {
return 1;
}
${ checkValues }
else {
return 0;
}
};
`;
return Function(...argNames, body)(...args);
}
/**
* Create a function which can compare a struct stored at a given address with a given value.
*/
function createCompareAddressValue(fields) {
const checkAddressValues = fields.map((_ref13, index) => {
let name = _ref13.name;
let offset = _ref13.offset;
if (isValidIdentifier(name)) {
return `
else if ((tmp = type${ index }.compareAddressValue(backing, address + ${ offset }, value.${ name })) !== 0) {
return tmp;
}
`;
} else {
const sanitizedName = JSON.stringify(name);
return `
else if ((tmp = type${ index }.compareAddressValue(backing, address, value[${ sanitizedName }])) !== 0) {
return tmp;
}
`;
}
}).join('');
const argNames = fields.map((_, index) => `type${ index }`);
const args = fields.map(_ref14 => {
let type = _ref14.type;
return type;
});
const body = `
"use strict";
return function compareAddressValue (backing, address, value) {
var tmp;
if (value == null || typeof value !== 'object') {
return 1;
}
else if (value[$Address] === address) {
return 0;
}
${ checkAddressValues }
else {
return 0;
}
};
`;
return Function('$Address', ...argNames, body)(_symbols.$Address, ...args);
}
/**
* Create a function which can hash structs with the given fields.
*/
function createHashStruct(fields) {
const checkValues = fields.map((_ref15, index) => {
let name = _ref15.name;
let type = _ref15.type;
if (typeof type.hashValue !== 'function') {
throw new Error(`Type ${ type.name } does not have a method called "hashValue".`);
}
if (isValidIdentifier(name)) {
return `
hash ^= type${ index }.hashValue(input.${ name });
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
`;
} else {
const sanitizedName = JSON.stringify(name);
return `
hash ^= type${ index }.hashValue(input[${ sanitizedName }]);
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
`;
}
}).join('');
const argNames = fields.map((_, index) => `type${ index }`);
const args = fields.map(_ref16 => {
let type = _ref16.type;
return type;
});
const body = `
"use strict";
return function hashStruct (input) {
var hash = 0x811c9dc5;
${ checkValues }
return hash >>> 0;
};
`;
return Function(...argNames, body)(...args);
}
/**
* Creates a function which can return random objects with the same shape as the struct.
*/
function createRandomValue(fields) {
const properties = fields.map((_ref17, index) => {
let name = _ref17.name;
if (isValidIdentifier(name)) {
return ` ${ name }: type${ index }.randomValue()`;
} else {
const sanitizedName = JSON.stringify(name);
return ` ${ sanitizedName }: type${ index }.randomValue()`;
}
}).join(',\n');
const argNames = fields.map((_, index) => `type${ index }`);
const args = fields.map(_ref18 => {
let type = _ref18.type;
return type;
});
const body = `
"use strict";
return function randomValue () {
return new this({
${ properties }
});
};
`;
return Function(...argNames, body)(...args);
}
function isValidIdentifier(name) {
return (/^([A-Za-z_$])([A-Za-z_$0-9]*)$/.test(name)
);
}