Switch to directly-represented embedded values

This commit is contained in:
Tony Garnock-Jones 2024-03-12 21:38:49 +01:00
parent f5ed511c4e
commit 9521fc3dcc
7 changed files with 43 additions and 48 deletions

View File

@ -1,7 +1,7 @@
/// SPDX-License-Identifier: GPL-3.0-or-later /// SPDX-License-Identifier: GPL-3.0-or-later
/// SPDX-FileCopyrightText: Copyright © 2016-2024 Tony Garnock-Jones <tonyg@leastfixedpoint.com> /// SPDX-FileCopyrightText: Copyright © 2016-2024 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
import { IdentitySet, Value, embeddedId, is, fromJS, stringify, Dictionary, KeyedSet, Tuple } from '@preserves/core'; import { IdentitySet, Value, embeddedId, is, fromJS, stringify, Dictionary, KeyedSet, Tuple, Embeddable } from '@preserves/core';
import { Cell, Field, Graph } from './dataflow.js'; import { Cell, Field, Graph } from './dataflow.js';
import { Caveat, runRewrites } from './rewrite.js'; import { Caveat, runRewrites } from './rewrite.js';
import { ActorSpace } from './space.js'; import { ActorSpace } from './space.js';
@ -23,7 +23,11 @@ export type ExitReason = null | { ok: true } | { ok: false, err: unknown };
export type LocalAction = () => void; export type LocalAction = () => void;
export type DetailedAction<T = AnyValue> = LocalAction & { detail: T }; export type DetailedAction<T = AnyValue> = LocalAction & { detail: T };
export type Assertable = Assertion | { __as_preserve__: <T>() => Value<T> } | { __as_preserve__: () => Assertion }; export type Assertable =
| Assertion
| { __as_preserve__: <T extends Embeddable>() => Value<T> }
| { __as_preserve__: () => Assertion }
;
export interface Entity { export interface Entity {
assert(assertion: Assertion, handle: Handle): void; assert(assertion: Assertion, handle: Handle): void;
@ -35,13 +39,7 @@ export interface Entity {
export type Cap = Ref; export type Cap = Ref;
export interface Ref { export class Ref {
readonly relay: Facet;
readonly target: Partial<Entity>;
readonly attenuation?: Caveat[];
}
export class RefImpl implements Ref {
readonly relay: Facet; readonly relay: Facet;
readonly target: Partial<Entity>; readonly target: Partial<Entity>;
readonly attenuation?: Caveat[]; readonly attenuation?: Caveat[];
@ -64,6 +62,10 @@ export class RefImpl implements Ref {
if ('sync' in this.target) sig = sig + 'S'; if ('sync' in this.target) sig = sig + 'S';
return `${this.relay.idChain()}<${sig}>${entityRepr}`; return `${this.relay.idChain()}<${sig}>${entityRepr}`;
} }
static __from_preserve__(v: AnyValue): undefined | Ref {
return typeof v === 'object' && 'relay' in v ? v : void 0;
}
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -351,7 +353,7 @@ export class Turn {
} }
ref<T extends Partial<Entity>>(e: T): Ref { ref<T extends Partial<Entity>>(e: T): Ref {
return new RefImpl(this.activeFacet, e); return new Ref(this.activeFacet, e);
} }
facet(bootProc: LocalAction): Facet { facet(bootProc: LocalAction): Facet {

View File

@ -3,7 +3,7 @@
// Bags and Deltas (which are Bags where item-counts can be negative). // Bags and Deltas (which are Bags where item-counts can be negative).
import { Value, KeyedDictionary, KeyedSet } from '@preserves/core'; import { Value, KeyedDictionary, KeyedSet, Embeddable } from '@preserves/core';
export enum ChangeDescription { export enum ChangeDescription {
PRESENT_TO_ABSENT = -1, PRESENT_TO_ABSENT = -1,
@ -12,7 +12,7 @@ export enum ChangeDescription {
PRESENT_TO_PRESENT = 2, PRESENT_TO_PRESENT = 2,
} }
export class Bag<T, V extends Value<T> = Value<T>> { export class Bag<T extends Embeddable, V extends Value<T> = Value<T>> {
_items: KeyedDictionary<V, number, T>; _items: KeyedDictionary<V, number, T>;
constructor(s?: KeyedSet<V, T>) { constructor(s?: KeyedSet<V, T>) {

View File

@ -2,7 +2,7 @@
/// SPDX-FileCopyrightText: Copyright © 2016-2024 Tony Garnock-Jones <tonyg@leastfixedpoint.com> /// SPDX-FileCopyrightText: Copyright © 2016-2024 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
import { Turn } from "./actor.js"; import { Turn } from "./actor.js";
import { Bytes, Dictionary, DoubleFloat, embed, IdentityMap, is, isEmbedded, Record, Tuple, stringify } from "@preserves/core"; import { Bytes, Dictionary, DoubleFloat, IdentityMap, is, isEmbedded, Record, Tuple, stringify } from "@preserves/core";
import type { Assertion, Handle, Ref } from "./actor.js"; import type { Assertion, Handle, Ref } from "./actor.js";
import type { SturdyValue } from "../transport/sturdy.js"; import type { SturdyValue } from "../transport/sturdy.js";
@ -113,8 +113,7 @@ export function instantiate(t: Template, b: Bindings): Assertion {
if (!isEmbedded(v)) { if (!isEmbedded(v)) {
throw new Error(`Attempt to attenuate non-capability: ${stringify(v)}`); throw new Error(`Attempt to attenuate non-capability: ${stringify(v)}`);
} }
const r = v.embeddedValue; return attenuate(v, ... t.value.attenuation);
return embed(attenuate(r, ... t.value.attenuation));
} }
case 'TRef': { case 'TRef': {
const n = t.value.binding; const n = t.value.binding;

View File

@ -1,7 +1,7 @@
/// SPDX-License-Identifier: GPL-3.0-or-later /// SPDX-License-Identifier: GPL-3.0-or-later
/// SPDX-FileCopyrightText: Copyright © 2016-2024 Tony Garnock-Jones <tonyg@leastfixedpoint.com> /// SPDX-FileCopyrightText: Copyright © 2016-2024 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
import { Set, Dictionary, KeyedDictionary, IdentitySet, stringify, Value } from '@preserves/core'; import { Set, Dictionary, KeyedDictionary, IdentitySet, stringify, Value, Embeddable } from '@preserves/core';
import { AnyValue, Assertion, Ref } from './actor.js'; import { AnyValue, Assertion, Ref } from './actor.js';
import { Bag, ChangeDescription } from './bag.js'; import { Bag, ChangeDescription } from './bag.js';
import * as Stack from './stack.js'; import * as Stack from './stack.js';
@ -142,13 +142,13 @@ export class Index<T> {
} }
} }
function dumpBag<T, V extends Value<T>>(b: Bag<T, V>, indent: string) { function dumpBag<T extends Embeddable, V extends Value<T>>(b: Bag<T, V>, indent: string) {
for (const [v, count] of b.entries()) { for (const [v, count] of b.entries()) {
console.log(indent + stringify(v) + ' = ' + count); console.log(indent + stringify(v) + ' = ' + count);
} }
} }
function dumpSet<V>(s: Set<V>, indent: string) { function dumpSet<V extends Embeddable>(s: Set<V>, indent: string) {
s.forEach(v => console.log(indent + stringify(v))); s.forEach(v => console.log(indent + stringify(v)));
} }

View File

@ -2,7 +2,7 @@
/// SPDX-FileCopyrightText: Copyright © 2016-2024 Tony Garnock-Jones <tonyg@leastfixedpoint.com> /// SPDX-FileCopyrightText: Copyright © 2016-2024 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
import { Actor, Assertion, Entity, Facet, Handle, Ref, Turn } from '../runtime/actor.js'; import { Actor, Assertion, Entity, Facet, Handle, Ref, Turn } from '../runtime/actor.js';
import { BytesLike, Decoder, Dictionary, embed, encode, IdentityMap, mapEmbeddeds, stringify, underlying, Value } from '@preserves/core'; import { BytesLike, Decoder, Dictionary, encode, IdentityMap, mapEmbeddeds, stringify, underlying, Value } from '@preserves/core';
import * as IO from '../gen/protocol.js'; import * as IO from '../gen/protocol.js';
import { wireRefEmbeddedType } from './protocol.js'; import { wireRefEmbeddedType } from './protocol.js';
import { attenuate } from '../runtime/rewrite.js'; import { attenuate } from '../runtime/rewrite.js';
@ -193,7 +193,7 @@ export class Relay {
register(targetRemoteOid: IO.Oid | null, assertion: Assertion, handle: Handle | null): Value<WireRef> { register(targetRemoteOid: IO.Oid | null, assertion: Assertion, handle: Handle | null): Value<WireRef> {
const transient = (handle === null); const transient = (handle === null);
const pins: Array<WireSymbol> = []; const pins: Array<WireSymbol> = [];
const rewritten = mapEmbeddeds(assertion, r => embed(this.rewriteRefOut(r, transient, pins))); const rewritten = mapEmbeddeds(assertion, r => this.rewriteRefOut(r, transient, pins));
if (handle !== null) { if (handle !== null) {
if (targetRemoteOid !== null /* belt and suspenders */) { if (targetRemoteOid !== null /* belt and suspenders */) {
this.grabImportedOid(targetRemoteOid, pins); this.grabImportedOid(targetRemoteOid, pins);
@ -316,7 +316,7 @@ export class Relay {
rewriteIn(a: Value<WireRef>): [Assertion, Array<WireSymbol>] rewriteIn(a: Value<WireRef>): [Assertion, Array<WireSymbol>]
{ {
const pins: Array<WireSymbol> = []; const pins: Array<WireSymbol> = [];
const rewritten = mapEmbeddeds(a, r => embed(this.rewriteRefIn(r, pins))); const rewritten = mapEmbeddeds(a, r => this.rewriteRefIn(r, pins));
return [rewritten, pins]; return [rewritten, pins];
} }

View File

@ -1,7 +1,7 @@
/// SPDX-License-Identifier: GPL-3.0-or-later /// SPDX-License-Identifier: GPL-3.0-or-later
/// SPDX-FileCopyrightText: Copyright © 2016-2024 Tony Garnock-Jones <tonyg@leastfixedpoint.com> /// SPDX-FileCopyrightText: Copyright © 2016-2024 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
import { randomId, Observe, FlexMap, embed, Embedded, Ref, Turn, AnyValue, Dataspace } from "@syndicate-lang/core"; import { randomId, Observe, FlexMap, Ref, Turn, AnyValue, Dataspace } from "@syndicate-lang/core";
import { QuasiValue as Q } from "@syndicate-lang/core"; import { QuasiValue as Q } from "@syndicate-lang/core";
import * as P from "./protocol"; import * as P from "./protocol";
@ -46,7 +46,7 @@ export function spawnGlobalEventFactory(ds: Ref) {
function handler(event: Event) { function handler(event: Event) {
facet.turn(() => { facet.turn(() => {
send message P.GlobalEvent(selector, eventType, embed(create ({ data: event }))); send message P.GlobalEvent(selector, eventType, create ({ data: event }));
}); });
return dealWithPreventDefault(eventType, event); return dealWithPreventDefault(eventType, event);
} }
@ -78,7 +78,7 @@ export function spawnWindowEventFactory(ds: Ref) {
let handler = (event: Event) => { let handler = (event: Event) => {
facet.turn(() => { facet.turn(() => {
send message P.WindowEvent(eventType, embed(create ({ data: event }))); send message P.WindowEvent(eventType, create ({ data: event }));
}); });
return dealWithPreventDefault(eventType, event); return dealWithPreventDefault(eventType, event);
}; };
@ -149,7 +149,7 @@ function spawnUIFragmentFactory(ds: Ref) {
removeNodes(); removeNodes();
selector = newSelector; selector = newSelector;
html = (newHtml as Embedded<Ref>).embeddedValue.target.data as ChildNode[]; html = (newHtml as Ref).target.data as ChildNode[];
orderBy = newOrderBy; orderBy = newOrderBy;
anchorNodes = (selector !== null) ? selectorMatch(document.body, selector) : []; anchorNodes = (selector !== null) ? selectorMatch(document.body, selector) : [];
@ -191,7 +191,7 @@ function spawnUIFragmentFactory(ds: Ref) {
const facet = Turn.activeFacet; const facet = Turn.activeFacet;
function handler(event: Event) { function handler(event: Event) {
facet.turn(() => { facet.turn(() => {
send message P.UIEvent(fragmentId, selector, eventType, embed(create ({ data: event }))); send message P.UIEvent(fragmentId, selector, eventType, create ({ data: event }));
}); });
return dealWithPreventDefault(eventType, event); return dealWithPreventDefault(eventType, event);
} }
@ -507,7 +507,7 @@ export class Anchor {
if (!Array.isArray(html)) { if (!Array.isArray(html)) {
html = html.nodes(); html = html.nodes();
} }
return P.UIFragment(this.fragmentId, selector, embed(create ({ data: html })), orderBy); return P.UIFragment(this.fragmentId, selector, create ({ data: html }), orderBy);
} }
} }

View File

@ -6,7 +6,6 @@ import {
Assertion, Assertion,
Bytes, Bytes,
Dataspace, Dataspace,
Embedded,
IdentitySet, IdentitySet,
Observe, Observe,
QuasiValue as Q, QuasiValue as Q,
@ -23,6 +22,7 @@ import {
isEmbedded, isEmbedded,
stringify, stringify,
underlying, underlying,
Embeddable,
} from "@syndicate-lang/core"; } from "@syndicate-lang/core";
import G = Schemas.gatekeeper; import G = Schemas.gatekeeper;
import S = Schemas.sturdy; import S = Schemas.sturdy;
@ -140,14 +140,10 @@ export function boot(ds = Dataspace.global, debug: boolean = false, WebSocketCon
console.log('tracking', addr.url); console.log('tracking', addr.url);
during G.TransportConnection({ during G.TransportConnection({
"addr": addr, "addr": addr,
"control": $control_e: Embedded, "control": $control: Ref,
"resolved": G.Resolved.accepted($peer_e: Embedded), "resolved": G.Resolved.accepted($peer: Ref),
}) => { }) => {
const entry = { const entry = { addr, control, peer };
addr,
control: control_e.embeddedValue,
peer: peer_e.embeddedValue,
};
candidates.value.add(entry); candidates.value.add(entry);
candidates.changed(); candidates.changed();
on stop { on stop {
@ -237,11 +233,10 @@ export function boot(ds = Dataspace.global, debug: boolean = false, WebSocketCon
}) }) => { }) }) => {
const origin0 = Q.drop_lit(originPatValue); const origin0 = Q.drop_lit(originPatValue);
if (!origin0 || !isEmbedded(origin0)) return; if (!origin0 || !isEmbedded(origin0)) return;
const origin = origin0.embeddedValue; const origin = origin0;
const detail0 = Q.drop_lit(detailPatValue, N.toNoisePathStepDetail); const spec = Q.drop_lit(detailPatValue, N.toNoisePathStepDetail);
if (!detail0) return; if (!spec) return;
const spec: N.NoiseSpec<Ref> = detail0;
const algorithms = SaltyCrypto.Noise_25519_ChaChaPoly_BLAKE2s; const algorithms = SaltyCrypto.Noise_25519_ChaChaPoly_BLAKE2s;
const protocol = const protocol =
@ -378,11 +373,10 @@ export function boot(ds = Dataspace.global, debug: boolean = false, WebSocketCon
}) }) => { }) }) => {
const origin0 = Q.drop_lit(originPatValue); const origin0 = Q.drop_lit(originPatValue);
if (!origin0 || !isEmbedded(origin0)) return; if (!origin0 || !isEmbedded(origin0)) return;
const origin = origin0.embeddedValue; const origin = origin0;
const detail0 = Q.drop_lit(detailPatValue, S.toSturdyPathStepDetail); const parameters = Q.drop_lit(detailPatValue, S.toSturdyPathStepDetail);
if (!detail0) return; if (!parameters) return;
const parameters: S.Parameters = detail0;
at origin { at origin {
assert G.Resolve({ assert G.Resolve({
@ -411,7 +405,7 @@ export function boot(ds = Dataspace.global, debug: boolean = false, WebSocketCon
} }
} }
export function unpackStandardRoute<R>(route: E.StandardRoute<R>): G.Route<R> { export function unpackStandardRoute<R extends Embeddable>(route: E.StandardRoute<R>): G.Route<R> {
if (route._variant === 'general') return route.value; if (route._variant === 'general') return route.value;
const { transports, key, service, sig, oid } = route; const { transports, key, service, sig, oid } = route;
@ -465,15 +459,15 @@ export function contactRemote(
) => void, ) => void,
ds = Dataspace.global, ds = Dataspace.global,
) { ) {
const routeValue = Record.isRecord(route) ? route : G.fromRoute(G.Route(route)); const routeValue = 'pathSteps' in route ? G.fromRoute(G.Route(route)) : route;
at ds { at ds {
during G.ResolvePath({ during G.ResolvePath({
"route": routeValue, "route": routeValue,
"addr": $addr, "addr": $addr,
"control": $control_e: Embedded, "control": $control: Ref,
"resolved": G.Resolved.accepted($resolved_e: Embedded), "resolved": G.Resolved.accepted($resolved: Ref),
}) => { }) => {
connectedFacet(resolved_e.embeddedValue, control_e.embeddedValue, addr); connectedFacet(resolved, control, addr);
} }
} }
} }