UNPKG

reign

Version:

A persistent, typed-objects implementation.

483 lines (448 loc) 13.7 kB
"use strict"; 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) ); }