UNPKG

reign

Version:

A persistent, typed-objects implementation.

220 lines (195 loc) 6.68 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MIN_TYPE_ID = undefined; exports.make = make; var _ = require("../"); var _2 = require("../../"); var _backing = require("backing"); var _backing2 = _interopRequireDefault(_backing); var _symbols = require("../../symbols"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const MIN_TYPE_ID = exports.MIN_TYPE_ID = Math.pow(2, 20) * 3; /** * Makes a ReferenceType type class for the given realm. */ function make(realm) { const TypeClass = realm.TypeClass; let typeCounter = 0; return new TypeClass('ReferenceType', Target => { return Reference => { typeCounter++; const name = typeof Target.name === 'string' && Target.name.length > 0 ? `Reference<${ Target.name }>` : `%Reference<0x${ typeCounter.toString(16) }>`; const id = MIN_TYPE_ID + typeCounter; Reference[_symbols.$CanBeEmbedded] = true; Reference[_symbols.$CanBeReferenced] = false; Reference[_symbols.$CanContainReferences] = true; if (!Target[_symbols.$CanBeReferenced]) { throw new TypeError(`Type ${ Target.name } cannot be referenced.`); } let ReferenceArray; // Issue 285 Object.defineProperties(Reference, { Array: { get: function get() { if (ReferenceArray === undefined) { ReferenceArray = new realm.ArrayType(Reference); } return ReferenceArray; } } }); /** * Initialize a reference to the given object at the given address. */ function initializeReference(backing, pointerAddress, value) { if (value == null) { backing.setFloat64(pointerAddress, 0); } else if (value instanceof Target) { if (!value[_symbols.$CanBeReferenced]) { throw new ReferenceError(`Cannot reference value of type ${ Target.name }`); } const address = value[_symbols.$Address]; backing.gc.ref(address); backing.setFloat64(pointerAddress, address); } else { const address = backing.gc.alloc(Target.byteLength, Target.id, 1); Target.initialize(backing, address, value); backing.setFloat64(pointerAddress, address); } } /** * Store a reference to the given object at the given address. */ function storeReference(backing, pointerAddress, value) { const existing = backing.getFloat64(pointerAddress); if (value == null) { if (existing !== 0) { backing.setFloat64(pointerAddress, 0); } return; } else if (existing !== 0) { backing.gc.unref(existing); } if (value instanceof Target) { if (!value[_symbols.$CanBeReferenced]) { throw new ReferenceError(`Cannot reference value of type ${ Target.name }`); } const address = value[_symbols.$Address]; backing.gc.ref(address); backing.setFloat64(pointerAddress, address); } else { const address = backing.gc.alloc(Target.byteLength, Target.id, 1); Target.initialize(backing, address, value); backing.setFloat64(pointerAddress, address); } } /** * Load an object based on the reference stored at the given address. */ function loadReference(backing, pointerAddress) { const address = backing.getFloat64(pointerAddress); if (address === 0) { return null; } else { return Target.load(backing, address); } } /** * Remove a reference at the given address. */ function clearReference(backing, pointerAddress) { const address = backing.getFloat64(pointerAddress); if (address !== 0) { backing.setFloat64(pointerAddress, 0); backing.gc.unref(address); } } /** * Destroy a reference at the given address. */ function referenceDestructor(backing, pointerAddress) { const address = backing.getFloat64(pointerAddress); if (address !== 0) { backing.setFloat64(pointerAddress, 0); backing.gc.unref(address); } } return { id: id, name: name, byteLength: 8, byteAlignment: 8, initialize: initializeReference, store: storeReference, load: loadReference, clear: clearReference, destructor: referenceDestructor, emptyValue: function emptyValue() { return null; }, equal: function equal(valueA, valueB) { if (valueA === valueB) { return true; } else if (!valueA || !valueB) { return false; } else { return Target.equal(valueA, valueB); } }, compareValues: function compareValues(valueA, valueB) { return Target.compareValues(valueA, valueB); }, compareAddresses: function compareAddresses(backing, addressA, addressB) { if (addressA === addressB) { return 0; } else if (addressA === 0) { return -1; } else if (addressB === 0) { return 1; } const pointerA = backing.getFloat64(addressA); const pointerB = backing.getFloat64(addressB); if (pointerA === pointerB) { return 0; } else if (pointerA === 0) { return -1; } else if (pointerB === 0) { return 1; } else { return Target.compareAddresses(backing, pointerA, pointerB); } }, compareAddressValue: function compareAddressValue(backing, address, value) { const pointer = backing.getFloat64(address); if (pointer === 0) { if (value == null) { return 0; } else { return -1; } } return Target.compareAddressValue(backing, pointer, value); }, randomValue: function randomValue() { if (Math.random() < 0.5) { return null; } else { return Target.randomValue(); } }, hashValue: function hashValue(input) { if (input == null) { return 4; } else { return Target.hashValue(input); } }, flowType: function flowType() { return `Reference<${ Target.flowType() }>`; } }; }; }); };