UNPKG

reign

Version:

A persistent, typed-objects implementation.

198 lines (175 loc) 5.74 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.make = make; var _any = require("../../hash-functions/any"); var _any2 = _interopRequireDefault(_any); var _backing = require("backing"); var _backing2 = _interopRequireDefault(_backing); var _ = require("../.."); var _symbols = require("../../symbols"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function make(realm) { const ObjectType = realm.ObjectType; const T = realm.T; function constructor(backingOrInput, address) { if (!(this instanceof TypedObject)) { return TypedObject.cast(backingOrInput); } if (backingOrInput instanceof _backing2.default) { this[_symbols.$Backing] = backingOrInput; this[_symbols.$Address] = address; createAccessors(this); } else if (backingOrInput == null) { this[_symbols.$Backing] = realm.backing; this[_symbols.$Address] = createEmptyObject(realm.backing); } else { this[_symbols.$Backing] = realm.backing; this[_symbols.$Address] = createObject(realm.backing, backingOrInput); // @fixme we should combine createObject and createAccessors to make this faster. createAccessors(this); } } const TypedObject = new ObjectType({ name: 'Object', byteAlignment: 8, // Pointer byteLength: 8, constructor: constructor, cast: function cast(input) { return input == null ? null : Object(input); }, accepts: function accepts(input) { return typeof input === 'object'; }, initialize: function initialize(backing, pointerAddress, initialValue) { if (initialValue == null) { backing.setFloat64(pointerAddress, 0); } else { backing.setFloat64(pointerAddress, createObject(backing, initialValue, true)); } }, store: function store(backing, pointerAddress, value) { const existing = backing.getFloat64(pointerAddress); if (existing === 0) { if (value == null) { // nothing to do return; } } else { backing.gc.unref(existing); backing.setFloat64(pointerAddress, 0); // safety: in case the store() fails. if (value == null) { // all done return; } } backing.setFloat64(pointerAddress, createObject(backing, value, true)); }, load: function load(backing, pointerAddress) { const address = backing.getFloat64(pointerAddress); if (address === 0) { return null; } else { return new TypedObject(backing, address); } }, clear: function clear(backing, pointerAddress) { const address = backing.getFloat64(pointerAddress); if (address !== 0) { backing.setFloat64(pointerAddress, 0); backing.gc.unref(address); } }, destructor: destroyObject, emptyValue: function emptyValue() { return null; }, randomValue: function randomValue() { return null; }, hashValue: _any2.default, flowType: function flowType() { return `Object`; } }); function createAccessors(target) { const backing = target[_symbols.$Backing]; const address = target[_symbols.$Address]; const length = backing.getUint32(address); let current = address + 8; for (let i = 0; i < length; i++) { const name = T.String.load(backing, current); const offset = current + 8; Object.defineProperty(target, name, { enumerable: true, get: function get() { return T.Any.load(this[_symbols.$Backing], offset); }, set: function set(value) { T.Any.store(this[_symbols.$Backing], offset, value); } }); current += 24; } } function createEmptyObject(backing, hasReference) { return backing.gc.calloc(8, TypedObject.id, hasReference ? 1 : 0); } function createObject(backing, input, hasReference) { if (input instanceof TypedObject && input[_symbols.$Backing] === backing) { backing.gc.ref(input[_symbols.$Address]); return input[_symbols.$Address]; } const keys = Object.keys(input); const length = keys.length; const address = backing.gc.alloc(length * 24 + 8, TypedObject.id, hasReference ? 1 : 0); backing.setUint32(address, length); let current = address + 8; for (let i = 0; i < length; i++) { const key = keys[i]; backing.setFloat64(current, realm.strings.add(key)); current += 8; T.Any.initialize(backing, current, input[key]); current += 16; } return address; } function loadObject(backing, address) { const output = { // Issue 252 [_symbols.$Backing]: backing, // Issue 252 [_symbols.$Address]: address }; const length = backing.getUint32(address); let current = address + 8; for (let i = 0; i < length; i++) { output[T.String.load(backing, current)] = T.Any.load(backing, current + 8); current += 24; } return output; } function clearObject(backing, address) { const length = backing.getUint32(address); let current = address + 8; for (let i = 0; i < length; i++) { current += 8; T.Any.clear(backing, current); current += 16; } } function destroyObject(backing, address) { const length = backing.getUint32(address); backing.setUint32(address, 0); // prevent further reads. let current = address + 8; for (let i = 0; i < length; i++) { backing.setFloat64(current, 0); current += 8; T.Any.clear(backing, current); current += 16; } } TypedObject[_symbols.$CanBeEmbedded] = false; return TypedObject; }