reign
Version:
A persistent, typed-objects implementation.
198 lines (175 loc) • 5.74 kB
JavaScript
;
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;
}