@aire-ux/aire-condensation
Version:
Client-side serialization library for Aire-UX
114 lines (113 loc) • 4.01 kB
JavaScript
import TypeRegistry from "./type-registry";
import RemoteRegistry from "./remote-registry";
import { Dynamic, allocate, isPointer, Region, } from "./types";
import { BooleanDeserializer, NumberDeserializer, StringDeserializer, TypeRegistrationDeserializer, } from "./deserializer";
/**
* root context for all operations
*/
export class Condensation {
static get typeRegistry() {
return Condensation.registry;
}
static deserializerFor(type) {
const result = this.deserializerConfigurations.get(type);
if (result) {
return result;
}
const cfg = Condensation.registry.resolveConfiguration(type);
return new TypeRegistrationDeserializer(type, cfg);
}
static newContext() {
return new DefaultCondensationContext();
}
static defaultContext() {
if (!Condensation.context) {
Condensation.context = Condensation.newContext();
}
return Condensation.context;
}
}
Condensation.deserializerConfigurations = new Map();
class DefaultCondensationContext {
constructor(region = new Region("default")) {
this.region = region;
}
create(t, ...args) {
const actualParams = this.formalParams(t, "constructor", 'constructor', ...args);
return allocate(new t(...actualParams), this.region);
}
addressOf(t) {
return this.region.addressOf(t);
}
delete(address) {
return this.region.delete(address);
}
invokeDirect(value, op, ...args) {
const operation = value[op];
if (!operation) {
throw new Error(`Type ${typeof value} has no method named '${op}'`);
}
const formals = this.formalParams(Object.getPrototypeOf(value).constructor, "method", op, ...args);
return operation.apply(value, formals);
}
invoke(address, op, ...args) {
let v;
if (isPointer(address)) {
v = address;
}
else {
v = this.locate(address);
}
if (!v) {
throw new Error(`Null pointer exception at ${address} while trying to invoke ${op}`);
}
const operation = v[op];
if (!operation) {
throw new Error(`Type ${typeof v} has no method named '${op}'`);
}
const formals = this.formalParams(Object.getPrototypeOf(v).constructor, "method", op, ...args);
return operation.apply(v, formals);
}
locate(address) {
return this.region.values[address.value];
}
move(address, target) {
const v = this.delete(address);
return allocate(v, target);
}
formalParams(t, type, operation, ...args) {
const remotes = Condensation.remoteRegistry, remote = remotes.resolve(t), ctorArgs = remote.definitions.filter((definition) => definition.invocationType === type
&& definition.invocationTarget == operation);
if (ctorArgs.length !== args.length) {
throw new Error(`Error: ${type} argument count mismatch. Expected ${ctorArgs.length}, got ${args.length}`);
}
ctorArgs.sort((lhs, rhs) => lhs.index - rhs.index);
return ctorArgs.map((def, idx) => {
const doc = args[idx], jsonValue = JSON.parse(doc);
if (def.type !== Dynamic) {
const deserializer = Condensation.deserializerFor(def.type);
return deserializer.read(jsonValue);
}
else {
return jsonValue;
}
});
}
}
export function register(...registrations) {
for (let reg of registrations) {
Condensation.deserializerConfigurations.set(reg.type, reg.deserializer);
}
}
Condensation.registry = new TypeRegistry();
Condensation.remoteRegistry = new RemoteRegistry();
register({
type: String,
deserializer: new StringDeserializer(),
}, {
type: Boolean,
deserializer: new BooleanDeserializer(),
}, {
type: Number,
deserializer: new NumberDeserializer(),
});