reign
Version:
A persistent, typed-objects implementation.
220 lines (195 loc) • 6.68 kB
JavaScript
;
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() }>`;
}
};
};
});
};