src/IsolatedApi.js
import { assert } from "akutils";
import IsolatedProperty from "./IsolatedProperty";
import reshadow from "./reshadow";
/*
Questions:
* How integrate with time travel debugging
*/
export default class IsolatedApi {
constructor(store) {
this.store = store;
this.records = {};
}
all(owner) {
const record = this._record(owner);
return record.all();
}
count(owner) {
const record = this._record(owner);
return record.count();
}
entries(owner) {
const record = this._record(owner);
return record.entries();
}
get(key, owner) {
const record = this._record(owner);
return record && record.get(key);
}
invalidated(iso) {
const record = this._record(iso.owner());
record.invalidated(iso);
}
obsolete(iso) {
const owner = iso.owner();
const record = this._findRecord(owner);
record && record.obsolete(iso);
}
remove(key, owner) {
const record = this._record(owner);
record && record.remove(key);
}
removeAllFor(owner) {
const record = this._record(owner);
record && record.removeAll();
}
set(key, isoType, owner) {
const record = this._record(owner);
assert( a => a.is(isoType.getManagedType(), "Managed type required for isolated objects") );
record.set(key, isoType);
}
/**
Invoked by {@link IsolatedObjectShadowImpl#defineChildProperties} to perform updates based
on processed actions.
@param {Property} owner - the property managing isolated properties.
*/
update(owner) {
const record = this._record(owner);
record && record.update();
}
willUnshadow(iso) {
const record = this._record(iso.owner());
record.willUnshadow(iso);
}
_findRecord(owner) {
return this.records[owner.pid()];
}
_record(owner) {
const records = this.records;
const pid = owner.pid();
if (!records[pid]) {
records[pid] = new OwnerRecord(this, owner);
}
return records[pid];
}
}
class OwnerRecord {
constructor(api, owner) {
this._owner = owner;
this._invalidated = false;
this._kv = { };
this._nextKv = { };
}
all() {
return Object.values(this._kv);
}
count() {
return Object.keys(this._kv).length;
}
entries() {
return { ...this._kv }
}
get(key) {
return this._kv[key];
}
invalidated(iso) {
this._invalidated = true;
}
obsolete(iso) {
}
remove(key) {
const iso = this._kv[key];
const impl = iso && iso.__();
if (impl) {
impl.invalidate()
impl.obsoleteTree();
impl.blockFurtherUpdates(false);
}
}
removeAll() {
const kv = this._kv;
for (let key in kv) {
this.remove(key);
}
}
set(key, isoType) {
const owner = this._owner;
const prevIso = this._kv[key];
const name = `<iso:${owner.dotPath()}:${key}>`;
// create and record the new isolated property
const iso = this._nextKv[key] = new IsolatedProperty(isoType);
iso.setKey(key);
iso.setOwner(owner);
iso.setStore(owner.store());
// kill prevIso
if (prevIso) {
prevIso.__().blockFurtherUpdates(true);
}
}
update() {
if (!this.invalidated && this._died.length === 0) { return }
const kv = this._kv;
const nextKv = this._nextKv;
const time = this._owner.__().time();
var iso, impl;
// resahdow live properties
for (let name in kv) {
iso = kv[name];
impl = iso.__();
if (impl.replaced() || !impl.isActive()) {
impl.obsoleteTree();
delete kv[name];
} else if (!impl.isValid()) {
impl.willShadow(false);
reshadow(time, impl.nextState(), impl);
}
}
// shadow the new iso properties (not replacements)
for (let name in nextKv) {
const nextIso = nextKv[name];
const state = nextIso.initialState();
const nextImpl = nextIso.shader().shadowProperty(time, name, state);
this._kv[name] = nextIso;
}
this._nextKv = {};
this._invalidated = false;
}
willUnshadow(iso) {
}
}