UNPKG

reign

Version:

A persistent, typed-objects implementation.

465 lines (401 loc) 15.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Struct = exports.MIN_TYPE_ID = undefined; var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); exports.make = make; var _backing = require("backing"); var _backing2 = _interopRequireDefault(_backing); var _2 = require("../"); var _util = require("../../util"); var _methods = require("./methods"); var _3 = require("../../"); var _symbols = require("../../symbols"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var MIN_TYPE_ID = exports.MIN_TYPE_ID = Math.pow(2, 20) * 2; var Struct = exports.Struct = function (_TypedObject) { _inherits(Struct, _TypedObject); function Struct() { _classCallCheck(this, Struct); return _possibleConstructorReturn(this, Object.getPrototypeOf(Struct).apply(this, arguments)); } return Struct; }(_2.TypedObject); function make(realm) { var TypeClass = realm.TypeClass; var ReferenceType = realm.ReferenceType; var backing = realm.backing; var typeCounter = 0; return new TypeClass('StructType', function (fields, lengthOrOptions, options) { return function (Partial) { typeCounter++; var capturedTypeCount = typeCounter; Partial[_symbols.$CanBeEmbedded] = true; Partial[_symbols.$CanBeReferenced] = true; var StructArray = void 0; // Issue 285 Object.defineProperties(Partial, { name: { configurable: true, value: "%Struct<0x" + typeCounter.toString(16) + ">" }, flowType: { configurable: true, value: function value() { return 'Object'; } }, Array: { get: function get() { if (StructArray === undefined) { StructArray = new realm.ArrayType(Partial); } return StructArray; } } }); var prototype = Object.create(Struct.prototype); var isFinalized = false; /** * Holds information about the size and layout of the struct. */ var metadata = { byteLength: 0, byteAlignment: 0, canContainReferences: false }; /** * The specialized type which references this kind of struct. */ var Reference = new ReferenceType(Partial); /** * The constructor for struct type instances. */ function constructor(backingOrInput, address, embedded) { if (!isFinalized) { throw new ReferenceError("Cannot create an instance of a struct before it is finalized."); } else if (backingOrInput instanceof _backing2.default) { this[_symbols.$Backing] = backingOrInput; this[_symbols.$Address] = address; this[_symbols.$CanBeReferenced] = !embedded; } else { this[_symbols.$Backing] = backing; this[_symbols.$Address] = createStruct(backing, backingOrInput); this[_symbols.$CanBeReferenced] = true; } } /** * Allocate space for the given struct and write the input if any. */ function createStruct(backing, input) { var address = backing.gc.alloc(metadata.byteLength, Partial.id); Partial.initialize(backing, address, input); return address; } /** * Finalize the layout of the fields within the struct. */ function finalizeLayout(fieldsConfig, lengthOrOptions, options) { //fieldsConfig: StructFieldsConfig, options: StructOptions = {}): typeof Partial { if (isFinalized) { throw new Error("Struct layout is already finalized"); } if (typeof lengthOrOptions === 'number') { (function () { var ElementType = fieldsConfig; fieldsConfig = Array.from({ length: lengthOrOptions }, function (_, index) { return [String(index), ElementType]; }); options = options || {}; })(); } else { options = lengthOrOptions || {}; } var fields = processStructConfig(fieldsConfig, options); var fieldOffsets = {}; var fieldTypes = {}; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = fields[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var field = _step.value; var _name = field.name; var type = field.type; if (type.byteAlignment > metadata.byteAlignment) { metadata.byteAlignment = type.byteAlignment; } field.offset = (0, _util.alignTo)(metadata.byteLength, type.byteAlignment); metadata.byteLength = field.offset + type.byteLength; fieldOffsets[_name] = field.offset; fieldTypes[_name] = type; defineAccessors(field); /* Issue 252 */ if (type[_symbols.$CanContainReferences]) { metadata.canContainReferences = true; } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } metadata.byteLength = (0, _util.alignTo)(metadata.byteLength, metadata.byteAlignment); Object.freeze(fieldOffsets); Object.freeze(fieldTypes); Partial[_symbols.$CanContainReferences] = metadata.canContainReferences; Object.defineProperties(Partial, { id: { value: options.id || MIN_TYPE_ID + capturedTypeCount }, name: { value: options.name || "%StructType<0x" + capturedTypeCount.toString(16) + ">" }, byteLength: { value: metadata.byteLength }, byteAlignment: { value: metadata.byteAlignment }, fieldOffsets: { value: fieldOffsets }, fieldTypes: { value: fieldTypes }, accepts: { value: (0, _methods.createAccepts)(fields) }, initialize: { value: (0, _methods.createInitializeStruct)(Partial, fields) }, store: { value: (0, _methods.createStoreStruct)(Partial, fields) }, load: { value: function value(backing, address, embedded) { return new Partial(backing, address, embedded); } }, clear: { value: (0, _methods.createClearStruct)(fields) }, destructor: { value: (0, _methods.createStructDestructor)(fields) }, equal: { value: (0, _methods.createEqual)(fields) }, compareValues: { value: (0, _methods.createCompareValues)(fields) }, compareAddresses: { value: (0, _methods.createCompareAddresses)(fields) }, compareAddressValue: { value: (0, _methods.createCompareAddressValue)(fields) }, hashValue: { value: (0, _methods.createHashStruct)(fields) }, randomValue: { value: (0, _methods.createRandomValue)(fields) }, flowType: { value: function value() { return "{" + fields.map(function (field) { return field.name + ": " + field.type.flowType() + ";"; }).join('\n') + "}"; } } }); Object.defineProperties(prototype, { toJSON: { value: (0, _methods.createToJSON)(fields) } }); isFinalized = true; realm.registry.add(Partial); return Partial; } /** * Define the getter and setter for a field. */ function defineAccessors(field) { var name = field.name; var type = field.type; var offset = field.offset; // Issue 252 var embedded = type[_symbols.$CanBeEmbedded]; Object.defineProperty(prototype, name, { enumerable: true, get: function get() { return type.load(this[_symbols.$Backing], this[_symbols.$Address] + offset, embedded); }, set: function set(value) { type.store(this[_symbols.$Backing], this[_symbols.$Address] + offset, value); } }); } /** * Normalize the configuration for a struct and return a list of fields. */ function processStructConfig(fields, options) { var normalized = []; var defaults = options.defaults || {}; if (Array.isArray(fields)) { var names = new Set(); var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { var _loop = function _loop() { var _step2$value = _slicedToArray(_step2.value, 2); var name = _step2$value[0]; var type = _step2$value[1]; if (names.has(name)) { throw new TypeError("A field with the name \"" + name + "\" already exists."); } /* Issue 252 */ if (!type || !type[_symbols.$isType]) { throw new TypeError("Field \"" + name + "\" must be a finalized type."); } names.add(name); normalized.push({ name: name, offset: 0, default: defaults.hasOwnProperty(name) ? function () { return defaults[name]; } : function () { return type.emptyValue(true); }, type: type }); }; for (var _iterator2 = fields[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { _loop(); } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } return normalized; } else { var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { var _loop2 = function _loop2() { var name = _step3.value; var type = fields[name]; /* Issue 252 */ if (!type || !type[_symbols.$isType]) { throw new TypeError("Field \"" + name + "\" must be a finalized type."); } normalized.push({ name: name, offset: 0, default: defaults.hasOwnProperty(name) ? function () { return defaults[name]; } : function () { return type.emptyValue(true); }, type: type }); }; for (var _iterator3 = Object.keys(fields)[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { _loop2(); } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } return optimizeFieldLayout(normalized); } } /** * Given an object mapping field names to types, return an array which * contains the fields in an order optimized for the smallest possible struct size, * whilst still respecting each field's alignment requirements. * * @fixme this is not currently very good, can do better. */ function optimizeFieldLayout(fields) { return fields.sort(compareFieldsByByteAlignmentOrName); } /** * Comparator used for sorting fields based on the byteAlignment of their types. * If two fields have the same byte alignment, they will be compared by name instead. */ function compareFieldsByByteAlignmentOrName(a, b) { if (a.type.byteAlignment > b.type.byteAlignment) { return 1; } else if (a.type.byteAlignment < b.type.byteAlignment) { return -1; } else { if (a.name > b.name) { return 1; } else if (a.name < b.name) { return -1; } return 0; } } if (fields != null) { finalizeLayout(fields, lengthOrOptions, options); } return { constructor: constructor, prototype: prototype, gc: true, ref: Reference, finalize: finalizeLayout, cast: function cast(input) { if (input == null) { return null; } else if (input instanceof Partial) { return input; } else { return new Partial(input); } }, emptyValue: function emptyValue(embedded) { return embedded ? null : new Partial(); } }; }; }); };