From 4af4219cc7d0ee75076a268008c0871b0c7029fa Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Sun, 25 Apr 2021 00:15:15 +0200 Subject: [PATCH] Avoid hand-written WireRef (un)marshalling, taking advantage of the new Pointer representation from preserves --- package.json | 4 +- schemas/gatekeeper.prs | 5 ++ schemas/protocol.prs | 2 +- schemas/sturdy.prs | 5 +- src/distributed/sandbox.ts | 3 +- src/distributed/server.ts | 7 +- src/examples/box.ts | 4 +- src/examples/client.ts | 4 +- src/examples/main.ts | 18 ++-- src/examples/secure-chat-client.ts | 4 +- src/examples/secure-chat-moderator.ts | 10 ++- src/examples/simple-chat.ts | 5 +- src/examples/sturdy-demo.ts | 3 +- src/gen/box-protocol.ts | 2 - src/gen/dataspace.ts | 6 +- src/gen/gatekeeper.ts | 78 +++++++++++++++++ src/gen/protocol.ts | 14 +-- src/gen/secure-chat-protocol.ts | 12 +-- src/gen/simple-chat-protocol.ts | 2 - src/gen/sturdy.ts | 119 +++++++++++++++----------- src/gen/worker.ts | 2 - src/runtime/bag.ts | 4 +- src/runtime/rewrite.ts | 15 ++-- src/tools/attenuate.ts | 4 +- src/transport/protocol.ts | 50 ++++------- src/transport/relay.ts | 45 +++++----- src/transport/sturdy.ts | 6 +- src/worker/wload.ts | 17 ++-- 28 files changed, 262 insertions(+), 188 deletions(-) create mode 100644 schemas/gatekeeper.prs create mode 100644 src/gen/gatekeeper.ts diff --git a/package.json b/package.json index bb4e764..2e297e1 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,8 @@ "typescript": "^4.2.3" }, "dependencies": { - "@preserves/core": "^0.11.0", - "@preserves/schema": "^0.4.0" + "@preserves/core": "^0.12.0", + "@preserves/schema": "^0.5.1" }, "scripts": { "regenerate": "rm -rf ./src/gen && preserves-schema-ts --module Actor=./src/runtime/actor.ts --module Protocol=./src/transport/protocol.ts --output ./src/gen './schemas/**/*.prs'", diff --git a/schemas/gatekeeper.prs b/schemas/gatekeeper.prs new file mode 100644 index 0000000..1be31ba --- /dev/null +++ b/schemas/gatekeeper.prs @@ -0,0 +1,5 @@ +version 1 . +pointer Actor.Ref . + +Resolve = . +Bind = . diff --git a/schemas/protocol.prs b/schemas/protocol.prs index 27abc63..574f634 100644 --- a/schemas/protocol.prs +++ b/schemas/protocol.prs @@ -1,5 +1,5 @@ version 1 . -pointer Protocol.WireRef . +pointer sturdy.WireRef . Assertion = any . Handle = int . diff --git a/schemas/sturdy.prs b/schemas/sturdy.prs index 9a63d5d..1eadb35 100644 --- a/schemas/sturdy.prs +++ b/schemas/sturdy.prs @@ -1,5 +1,4 @@ version 1 . -pointer Actor.Ref . ; Each Attenuation is a stage. The sequence of Attenuations is run RIGHT-TO-LEFT. ; That is, the newest Attenuations are at the right. @@ -15,8 +14,8 @@ Caveat = Rewrite / Alts . Rewrite = . Alts = . -Resolve = . -Bind = . +Oid = int . +WireRef = @mine [0 @oid Oid] / @yours [1 @oid Oid @attenuation Caveat ...]. ;--------------------------------------------------------------------------- diff --git a/src/distributed/sandbox.ts b/src/distributed/sandbox.ts index 61d866c..9db0bec 100644 --- a/src/distributed/sandbox.ts +++ b/src/distributed/sandbox.ts @@ -1,7 +1,8 @@ import { Actor, Ref, Turn } from "../runtime/actor"; import { Relay, spawnRelay } from "../transport/relay"; import { sturdyDecode } from "../transport/sturdy"; -import { Resolve, asSturdyRef, fromResolve } from "../gen/sturdy"; +import { asSturdyRef } from "../gen/sturdy"; +import { Resolve, fromResolve } from "../gen/gatekeeper"; import { during } from "../runtime/dataspace"; import * as net from 'net'; diff --git a/src/distributed/server.ts b/src/distributed/server.ts index 1d9f19b..fb5ec0c 100644 --- a/src/distributed/server.ts +++ b/src/distributed/server.ts @@ -6,8 +6,9 @@ import * as net from 'net'; import { mint, sturdyEncode, validate } from '../transport/sturdy.js'; import { KEY_LENGTH } from '../transport/cryptography.js'; import { attenuate } from '../runtime/rewrite.js'; -import { Bytes, is } from '@preserves/core'; -import { $bind, Attenuation, Bind, fromBind, fromSturdyRef, toBind, toResolve, _val } from '../gen/sturdy.js'; +import { Bytes, embed, is } from '@preserves/core'; +import { Attenuation, fromSturdyRef, _val } from '../gen/sturdy.js'; +import { $bind, Bind, fromBind, toBind, toResolve } from '../gen/gatekeeper.js'; new Actor(t => { t.activeFacet.preventInertCheck(); @@ -52,7 +53,7 @@ new Actor(t => { r.caveatChain.forEach(cs => cavs.push(... cs)); const attenuated_ds = attenuate(b.target, ... cavs); let replyHandle: Handle | undefined; - t.freshen(t => replyHandle = t.assert(a.observer, attenuated_ds)); + t.freshen(t => replyHandle = t.assert(a.observer, embed(attenuated_ds))); return t => t.retract(replyHandle); })); }); diff --git a/src/examples/box.ts b/src/examples/box.ts index b663335..a251640 100644 --- a/src/examples/box.ts +++ b/src/examples/box.ts @@ -1,3 +1,4 @@ +import { Pointer } from "@preserves/core"; import { BoxState, $SetBox, fromBoxState } from "../gen/box-protocol.js"; import { fromObserve, Observe } from "../gen/dataspace.js"; import { Assertion, Handle, Ref, Turn } from "../runtime/actor.js"; @@ -6,9 +7,10 @@ let startTime = Date.now(); let prevValue = 0; export default function (t: Turn, arg: Assertion) { - const [ds, LIMIT, REPORT_EVERY]: [Ref, number, number] = Array.isArray(arg) && arg.length === 3 + const [ds_p, LIMIT, REPORT_EVERY]: [Pointer, number, number] = Array.isArray(arg) && arg.length === 3 ? arg as any : [arg, 50000, 2500]; + const ds = ds_p.embeddedValue; console.log('Spawning Box', LIMIT, REPORT_EVERY); let valueHandle: Handle | undefined; function setValue(t: Turn, value: number) { diff --git a/src/examples/client.ts b/src/examples/client.ts index c82c418..2a447a4 100644 --- a/src/examples/client.ts +++ b/src/examples/client.ts @@ -1,8 +1,10 @@ +import { Pointer } from "@preserves/core"; import { $BoxState, fromSetBox, SetBox } from "../gen/box-protocol.js"; import { fromObserve, Observe } from "../gen/dataspace.js"; import { Assertion, Ref, Turn } from "../runtime/actor.js"; -export default function (t: Turn, ds: Ref) { +export default function (t: Turn, ds_p: Pointer) { + const ds = ds_p.embeddedValue; console.log('Spawning Client'); let count = 0; t.assert(ds, fromObserve(Observe({ diff --git a/src/examples/main.ts b/src/examples/main.ts index 9d202e5..c81bda2 100644 --- a/src/examples/main.ts +++ b/src/examples/main.ts @@ -1,7 +1,7 @@ -import { Actor, Assertion, Ref, Turn } from '../runtime/actor.js'; -import { Dictionary } from '@preserves/core'; +import { Actor, Assertion, Turn } from '../runtime/actor.js'; +import { Dictionary, embed } from '@preserves/core'; import { Dataspace } from '../runtime/dataspace.js'; -import { attenuate, CRec, Lit, Pattern, PCompound, rfilter, ConstructorSpec } from '../runtime/rewrite.js'; +import { _ptr, attenuate, CRec, Lit, Pattern, PCompound, rfilter, ConstructorSpec } from '../runtime/rewrite.js'; import { $BoxState, $SetBox } from '../gen/box-protocol.js'; import { $Observe } from '../gen/dataspace.js'; import { spawnWorker } from '../worker/index.js'; @@ -38,7 +38,7 @@ new Actor(async (t: Turn) => { })), Pattern.PCompound(PCompound({ ctor: ConstructorSpec.CRec(CRec({ label: $Observe, arity: 2 })), - members: new Dictionary([ + members: new Dictionary<_ptr, Pattern>([ [0, Pattern.Lit(Lit($SetBox))]]) })))); @@ -51,16 +51,16 @@ new Actor(async (t: Turn) => { })), Pattern.PCompound(PCompound({ ctor: ConstructorSpec.CRec(CRec({ label: $Observe, arity: 2 })), - members: new Dictionary([ + members: new Dictionary<_ptr, Pattern>([ [0, Pattern.Lit(Lit($BoxState))]]) })))); const boxpath = path.join(__dirname, 'box.js'); const clientpath = path.join(__dirname, 'client.js'); - // spawnModule(t, boxpath, [ds_for_box, 500000, 25000]); - spawnWorker(t, boxpath, [ds_for_box, 50000, 2500]); + // spawnModule(t, boxpath, [embed(ds_for_box), 500000, 25000]); + spawnWorker(t, boxpath, [embed(ds_for_box), 50000, 2500]); - spawnModule(t, clientpath, ds_for_client); - // spawnWorker(t, clientpath, ds_for_client); + spawnModule(t, clientpath, embed(ds_for_client)); + // spawnWorker(t, clientpath, embed(ds_for_client)); }); diff --git a/src/examples/secure-chat-client.ts b/src/examples/secure-chat-client.ts index 34a6f9e..346902f 100644 --- a/src/examples/secure-chat-client.ts +++ b/src/examples/secure-chat-client.ts @@ -2,8 +2,10 @@ import { $joinedUser, $says, $user, asJoin, asSays, asUserInfo, fromNickClaim, f import { during, observe } from "../runtime/dataspace.js"; import { Assertion, Ref, Turn } from "../runtime/actor.js"; import { attachReadline } from './readline.js'; +import { Pointer } from "@preserves/core"; -export default function (t: Turn, ds: Ref) { +export default function (t: Turn, ds_ptr: Pointer) { + const ds = ds_ptr.embeddedValue; observe(t, ds, $joinedUser, during(async (t, j0) => { const j = asJoin(j0); const facet = t.facet(t => runSession(t, j.uid, j.handle)); diff --git a/src/examples/secure-chat-moderator.ts b/src/examples/secure-chat-moderator.ts index bd698b4..931c077 100644 --- a/src/examples/secure-chat-moderator.ts +++ b/src/examples/secure-chat-moderator.ts @@ -15,11 +15,12 @@ import { import { Assertion, Handle, Ref, Turn } from "../runtime/actor.js"; import { observe, during, $Observe, asObserve, Dataspace } from "../runtime/dataspace.js"; import { attenuate, rfilter, pRec, pPointer, pString, pLit } from "../runtime/rewrite.js"; -import { attenuate as sturdyAttenuate, fromBind, Bind, KEY_LENGTH, sturdyEncode, fromSturdyRef, mint } from "../transport/sturdy.js"; -import { Bytes } from "@preserves/core"; +import { attenuate as sturdyAttenuate, KEY_LENGTH, sturdyEncode, fromSturdyRef, mint } from "../transport/sturdy.js"; +import { Bind, fromBind } from "../gen/gatekeeper.js"; +import { Bytes, Pointer } from "@preserves/core"; -export default function (t: Turn, gatekeeperDs: Ref) { - let nextUserId: UserId = 0; +export default function (t: Turn, gatekeeperDs_ptr: Pointer) { + const gatekeeperDs = gatekeeperDs_ptr.embeddedValue; const ds = t.ref(new Dataspace()); @@ -34,6 +35,7 @@ export default function (t: Turn, gatekeeperDs: Ref) { const nicks = new Map(); + let nextUserId: UserId = 0; observe(t, ds, $Observe, during(async (t, o0) => { const o = asObserve(o0); if (o.label !== $joinedUser) return null; diff --git a/src/examples/simple-chat.ts b/src/examples/simple-chat.ts index 96cb193..811dc50 100644 --- a/src/examples/simple-chat.ts +++ b/src/examples/simple-chat.ts @@ -2,8 +2,11 @@ import { $Present, $Says, asPresent, asSays, fromPresent, fromSays, Present, Say import { during, observe } from "../runtime/dataspace.js"; import { Assertion, Handle, Ref, Turn } from "../runtime/actor.js"; import { attachReadline } from './readline.js'; +import { Pointer } from "@preserves/core"; + +export default function (t: Turn, ds_ptr: Pointer) { + const ds = ds_ptr.embeddedValue; -export default function (t: Turn, ds: Ref) { let username = ''; let usernameHandle: Handle | undefined; diff --git a/src/examples/sturdy-demo.ts b/src/examples/sturdy-demo.ts index f2fde90..f16b927 100644 --- a/src/examples/sturdy-demo.ts +++ b/src/examples/sturdy-demo.ts @@ -2,7 +2,6 @@ import { newKey } from '../transport/cryptography.js'; import { attenuate, KEY_LENGTH, mint, Caveat, Rewrite, sturdyEncode, validate } from '../transport/sturdy.js'; import * as RW from '../runtime/rewrite.js'; import { Bytes, Dictionary } from '@preserves/core'; -import { Ref } from '../runtime/actor.js'; async function main() { const m1 = await mint('hello world', new Bytes(KEY_LENGTH)); @@ -15,7 +14,7 @@ async function main() { label: Symbol.for('says'), arity: 2 })), - members: new Dictionary([ + members: new Dictionary([ [0, RW.Pattern.Lit(RW.Lit('Tony'))]]) })) })), diff --git a/src/gen/box-protocol.ts b/src/gen/box-protocol.ts index 21058e2..1b2e00d 100644 --- a/src/gen/box-protocol.ts +++ b/src/gen/box-protocol.ts @@ -13,8 +13,6 @@ export type BoxState = {"value": number}; export type SetBox = {"value": number}; -export const _toPtr = (v: _val) => {let result: undefined | _ptr; result = _i_Actor.toRef(v); return result;}; - export function BoxState(value: number): BoxState {return {"value": value};} export function SetBox(value: number): SetBox {return {"value": value};} diff --git a/src/gen/dataspace.ts b/src/gen/dataspace.ts index f968ad9..93f18dc 100644 --- a/src/gen/dataspace.ts +++ b/src/gen/dataspace.ts @@ -10,8 +10,6 @@ export type _val = _.Value<_ptr>; export type Observe = {"label": symbol, "observer": _ptr}; -export const _toPtr = (v: _val) => {let result: undefined | _ptr; result = _i_Actor.toRef(v); return result;}; - export function Observe({label, observer}: {label: symbol, observer: _ptr}): Observe {return {"label": label, "observer": observer};} export function asObserve(v: _val): Observe { @@ -30,7 +28,7 @@ export function toObserve(v: _val): undefined | Observe { _tmp1 = typeof v[0] === 'symbol' ? v[0] : void 0; if (_tmp1 !== void 0) { let _tmp2: (_ptr) | undefined; - _tmp2 = _toPtr(v[1]); + _tmp2 = _.isPointer<_ptr>(v[1]) ? v[1].embeddedValue : void 0; if (_tmp2 !== void 0) {result = {"label": _tmp1, "observer": _tmp2};}; }; }; @@ -38,5 +36,5 @@ export function toObserve(v: _val): undefined | Observe { return result; } -export function fromObserve(_v: Observe): _val {return _.Record($Observe, [_v["label"], _v["observer"]]);} +export function fromObserve(_v: Observe): _val {return _.Record($Observe, [_v["label"], _.embed(_v["observer"])]);} diff --git a/src/gen/gatekeeper.ts b/src/gen/gatekeeper.ts new file mode 100644 index 0000000..16cda4a --- /dev/null +++ b/src/gen/gatekeeper.ts @@ -0,0 +1,78 @@ +import * as _ from "@preserves/core"; +import * as _i_Actor from "../runtime/actor"; +import * as _i_sturdy from "./sturdy"; + +export const $bind = Symbol.for("bind"); +export const $resolve = Symbol.for("resolve"); + +export type _ptr = _i_Actor.Ref; + +export type _val = _.Value<_ptr>; + +export type Resolve = {"sturdyref": _i_sturdy.SturdyRef, "observer": _ptr}; + +export type Bind = {"oid": _val, "key": _.Bytes, "target": _ptr}; + + +export function Resolve({sturdyref, observer}: {sturdyref: _i_sturdy.SturdyRef, observer: _ptr}): Resolve {return {"sturdyref": sturdyref, "observer": observer};} + +export function Bind({oid, key, target}: {oid: _val, key: _.Bytes, target: _ptr}): Bind {return {"oid": oid, "key": key, "target": target};} + +export function asResolve(v: _val): Resolve { + let result = toResolve(v); + if (result === void 0) throw new TypeError(`Invalid Resolve: ${_.stringify(v)}`); + return result; +} + +export function toResolve(v: _val): undefined | Resolve { + let result: undefined | Resolve; + if (_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v)) { + let _tmp0: (null) | undefined; + _tmp0 = _.is(v.label, $resolve) ? null : void 0; + if (_tmp0 !== void 0) { + let _tmp1: (_i_sturdy.SturdyRef) | undefined; + _tmp1 = _i_sturdy.toSturdyRef(v[0]); + if (_tmp1 !== void 0) { + let _tmp2: (_ptr) | undefined; + _tmp2 = _.isPointer<_ptr>(v[1]) ? v[1].embeddedValue : void 0; + if (_tmp2 !== void 0) {result = {"sturdyref": _tmp1, "observer": _tmp2};}; + }; + }; + }; + return result; +} + +export function fromResolve(_v: Resolve): _val { + return _.Record($resolve, [_i_sturdy.fromSturdyRef(_v["sturdyref"]), _.embed(_v["observer"])]); +} + +export function asBind(v: _val): Bind { + let result = toBind(v); + if (result === void 0) throw new TypeError(`Invalid Bind: ${_.stringify(v)}`); + return result; +} + +export function toBind(v: _val): undefined | Bind { + let result: undefined | Bind; + if (_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v)) { + let _tmp0: (null) | undefined; + _tmp0 = _.is(v.label, $bind) ? null : void 0; + if (_tmp0 !== void 0) { + let _tmp1: (_val) | undefined; + _tmp1 = v[0]; + if (_tmp1 !== void 0) { + let _tmp2: (_.Bytes) | undefined; + _tmp2 = _.Bytes.isBytes(v[1]) ? v[1] : void 0; + if (_tmp2 !== void 0) { + let _tmp3: (_ptr) | undefined; + _tmp3 = _.isPointer<_ptr>(v[2]) ? v[2].embeddedValue : void 0; + if (_tmp3 !== void 0) {result = {"oid": _tmp1, "key": _tmp2, "target": _tmp3};}; + }; + }; + }; + }; + return result; +} + +export function fromBind(_v: Bind): _val {return _.Record($bind, [_v["oid"], _v["key"], _.embed(_v["target"])]);} + diff --git a/src/gen/protocol.ts b/src/gen/protocol.ts index 411661a..a9dc987 100644 --- a/src/gen/protocol.ts +++ b/src/gen/protocol.ts @@ -1,12 +1,12 @@ import * as _ from "@preserves/core"; -import * as _i_Protocol from "../transport/protocol"; +import * as _i_sturdy from "./sturdy"; export const $assert = Symbol.for("assert"); export const $message = Symbol.for("message"); export const $retract = Symbol.for("retract"); export const $sync = Symbol.for("sync"); -export type _ptr = _i_Protocol.WireRef; +export type _ptr = _i_sturdy.WireRef; export type _val = _.Value<_ptr>; @@ -36,12 +36,6 @@ export type Message = {"body": Assertion}; export type Sync = {"peer": _ptr}; -export const _toPtr = (v: _val) => { - let result: undefined | _ptr; - result = _i_Protocol.toWireRef(v); - return result; -}; - export function Assertion(value: _val): Assertion {return value;} export function Handle(value: number): Handle {return value;} @@ -285,12 +279,12 @@ export function toSync(v: _val): undefined | Sync { _tmp0 = _.is(v.label, $sync) ? null : void 0; if (_tmp0 !== void 0) { let _tmp1: (_ptr) | undefined; - _tmp1 = _toPtr(v[0]); + _tmp1 = _.isPointer<_ptr>(v[0]) ? v[0].embeddedValue : void 0; if (_tmp1 !== void 0) {result = {"peer": _tmp1};}; }; }; return result; } -export function fromSync(_v: Sync): _val {return _.Record($sync, [_v["peer"]]);} +export function fromSync(_v: Sync): _val {return _.Record($sync, [_.embed(_v["peer"])]);} diff --git a/src/gen/secure-chat-protocol.ts b/src/gen/secure-chat-protocol.ts index 90987b7..19bcd9e 100644 --- a/src/gen/secure-chat-protocol.ts +++ b/src/gen/secure-chat-protocol.ts @@ -24,8 +24,6 @@ export type Says = {"who": UserId, "what": string}; export type NickConflict = null; -export const _toPtr = (v: _val) => {let result: undefined | _ptr; result = _i_Actor.toRef(v); return result;}; - export function UserId(value: number): UserId {return value;} export function Join({uid, handle}: {uid: UserId, handle: _ptr}): Join {return {"uid": uid, "handle": handle};} @@ -70,7 +68,7 @@ export function toJoin(v: _val): undefined | Join { _tmp1 = toUserId(v[0]); if (_tmp1 !== void 0) { let _tmp2: (_ptr) | undefined; - _tmp2 = _toPtr(v[1]); + _tmp2 = _.isPointer<_ptr>(v[1]) ? v[1].embeddedValue : void 0; if (_tmp2 !== void 0) {result = {"uid": _tmp1, "handle": _tmp2};}; }; }; @@ -78,7 +76,7 @@ export function toJoin(v: _val): undefined | Join { return result; } -export function fromJoin(_v: Join): _val {return _.Record($joinedUser, [fromUserId(_v["uid"]), _v["handle"]]);} +export function fromJoin(_v: Join): _val {return _.Record($joinedUser, [fromUserId(_v["uid"]), _.embed(_v["handle"])]);} export function asNickClaim(v: _val): NickClaim { let result = toNickClaim(v); @@ -99,7 +97,7 @@ export function toNickClaim(v: _val): undefined | NickClaim { _tmp2 = typeof v[1] === 'string' ? v[1] : void 0; if (_tmp2 !== void 0) { let _tmp3: (_ptr) | undefined; - _tmp3 = _toPtr(v[2]); + _tmp3 = _.isPointer<_ptr>(v[2]) ? v[2].embeddedValue : void 0; if (_tmp3 !== void 0) {result = {"uid": _tmp1, "name": _tmp2, "k": _tmp3};}; }; }; @@ -108,7 +106,9 @@ export function toNickClaim(v: _val): undefined | NickClaim { return result; } -export function fromNickClaim(_v: NickClaim): _val {return _.Record($claimNick, [fromUserId(_v["uid"]), _v["name"], _v["k"]]);} +export function fromNickClaim(_v: NickClaim): _val { + return _.Record($claimNick, [fromUserId(_v["uid"]), _v["name"], _.embed(_v["k"])]); +} export function asUserInfo(v: _val): UserInfo { let result = toUserInfo(v); diff --git a/src/gen/simple-chat-protocol.ts b/src/gen/simple-chat-protocol.ts index c2ae0eb..fc1a447 100644 --- a/src/gen/simple-chat-protocol.ts +++ b/src/gen/simple-chat-protocol.ts @@ -13,8 +13,6 @@ export type Present = {"username": string}; export type Says = {"who": string, "what": string}; -export const _toPtr = (v: _val) => {let result: undefined | _ptr; result = _i_Actor.toRef(v); return result;}; - export function Present(username: string): Present {return {"username": username};} export function Says({who, what}: {who: string, what: string}): Says {return {"who": who, "what": what};} diff --git a/src/gen/sturdy.ts b/src/gen/sturdy.ts index d52c31a..4b459ba 100644 --- a/src/gen/sturdy.ts +++ b/src/gen/sturdy.ts @@ -1,6 +1,7 @@ import * as _ from "@preserves/core"; -import * as _i_Actor from "../runtime/actor"; +export const $0 = 0; +export const $1 = 1; export const $Boolean = Symbol.for("Boolean"); export const $ByteString = Symbol.for("ByteString"); export const $Double = Symbol.for("Double"); @@ -20,10 +21,9 @@ export const $not = Symbol.for("not"); export const $or = Symbol.for("or"); export const $rec = Symbol.for("rec"); export const $ref = Symbol.for("ref"); -export const $resolve = Symbol.for("resolve"); export const $rewrite = Symbol.for("rewrite"); -export type _ptr = _i_Actor.Ref; +export type _ptr = any; export type _val = _.Value<_ptr>; @@ -40,9 +40,12 @@ export type Rewrite = {"pattern": Pattern, "template": Template}; export type Alts = {"alternatives": Array}; -export type Resolve = {"sturdyref": SturdyRef, "observer": _ptr}; +export type Oid = number; -export type Bind = {"oid": _val, "key": _.Bytes, "target": _ptr}; +export type WireRef = ( + {"_variant": "mine", "oid": Oid} | + {"_variant": "yours", "oid": Oid, "attenuation": Array} +); export type ConstructorSpec = ( {"_variant": "CRec", "value": CRec} | @@ -106,8 +109,6 @@ export type TCompound = {"ctor": ConstructorSpec, "members": TCompoundMembers}; export type TCompoundMembers = _.KeyedDictionary<_val, Template, _ptr>; -export const _toPtr = (v: _val) => {let result: undefined | _ptr; result = _i_Actor.toRef(v); return result;}; - export function SturdyRef( {oid, caveatChain, sig}: {oid: _val, caveatChain: Array, sig: _.Bytes} ): SturdyRef {return {"oid": oid, "caveatChain": caveatChain, "sig": sig};} @@ -123,9 +124,12 @@ export function Rewrite({pattern, template}: {pattern: Pattern, template: Templa export function Alts(alternatives: Array): Alts {return {"alternatives": alternatives};} -export function Resolve({sturdyref, observer}: {sturdyref: SturdyRef, observer: _ptr}): Resolve {return {"sturdyref": sturdyref, "observer": observer};} +export function Oid(value: number): Oid {return value;} -export function Bind({oid, key, target}: {oid: _val, key: _.Bytes, target: _ptr}): Bind {return {"oid": oid, "key": key, "target": target};} +export namespace WireRef { + export function mine(oid: Oid): WireRef {return {"_variant": "mine", "oid": oid};}; + export function yours({oid, attenuation}: {oid: Oid, attenuation: Array}): WireRef {return {"_variant": "yours", "oid": oid, "attenuation": attenuation};}; +} export namespace ConstructorSpec { export function CRec(value: CRec): ConstructorSpec {return {"_variant": "CRec", "value": value};}; @@ -347,53 +351,61 @@ export function toAlts(v: _val): undefined | Alts { export function fromAlts(_v: Alts): _val {return _.Record($or, [_v["alternatives"].map(v => fromRewrite(v))]);} -export function asResolve(v: _val): Resolve { - let result = toResolve(v); - if (result === void 0) throw new TypeError(`Invalid Resolve: ${_.stringify(v)}`); +export function asOid(v: _val): Oid { + let result = toOid(v); + if (result === void 0) throw new TypeError(`Invalid Oid: ${_.stringify(v)}`); return result; } -export function toResolve(v: _val): undefined | Resolve { - let result: undefined | Resolve; - if (_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v)) { +export function toOid(v: _val): undefined | Oid { + let _tmp0: (number) | undefined; + let result: undefined | Oid; + _tmp0 = typeof v === 'number' ? v : void 0; + if (_tmp0 !== void 0) {result = _tmp0;}; + return result; +} + +export function fromOid(_v: Oid): _val {return _v;} + +export function asWireRef(v: _val): WireRef { + let result = toWireRef(v); + if (result === void 0) throw new TypeError(`Invalid WireRef: ${_.stringify(v)}`); + return result; +} + +export function toWireRef(v: _val): undefined | WireRef { + let result: undefined | WireRef; + if (_.Array.isArray(v) && v.length === 2) { let _tmp0: (null) | undefined; - _tmp0 = _.is(v.label, $resolve) ? null : void 0; + _tmp0 = _.is(v[0], $0) ? null : void 0; if (_tmp0 !== void 0) { - let _tmp1: (SturdyRef) | undefined; - _tmp1 = toSturdyRef(v[0]); - if (_tmp1 !== void 0) { - let _tmp2: (_ptr) | undefined; - _tmp2 = _toPtr(v[1]); - if (_tmp2 !== void 0) {result = {"sturdyref": _tmp1, "observer": _tmp2};}; - }; + let _tmp1: (Oid) | undefined; + _tmp1 = toOid(v[1]); + if (_tmp1 !== void 0) {result = {"_variant": "mine", "oid": _tmp1};}; }; }; - return result; -} - -export function fromResolve(_v: Resolve): _val {return _.Record($resolve, [fromSturdyRef(_v["sturdyref"]), _v["observer"]]);} - -export function asBind(v: _val): Bind { - let result = toBind(v); - if (result === void 0) throw new TypeError(`Invalid Bind: ${_.stringify(v)}`); - return result; -} - -export function toBind(v: _val): undefined | Bind { - let result: undefined | Bind; - if (_.Record.isRecord<_val, _.Tuple<_val>, _ptr>(v)) { - let _tmp0: (null) | undefined; - _tmp0 = _.is(v.label, $bind) ? null : void 0; - if (_tmp0 !== void 0) { - let _tmp1: (_val) | undefined; - _tmp1 = v[0]; - if (_tmp1 !== void 0) { - let _tmp2: (_.Bytes) | undefined; - _tmp2 = _.Bytes.isBytes(v[1]) ? v[1] : void 0; - if (_tmp2 !== void 0) { - let _tmp3: (_ptr) | undefined; - _tmp3 = _toPtr(v[2]); - if (_tmp3 !== void 0) {result = {"oid": _tmp1, "key": _tmp2, "target": _tmp3};}; + if (result === void 0) { + if (_.Array.isArray(v) && v.length >= 2) { + let _tmp2: (null) | undefined; + _tmp2 = _.is(v[0], $1) ? null : void 0; + if (_tmp2 !== void 0) { + let _tmp3: (Oid) | undefined; + _tmp3 = toOid(v[1]); + if (_tmp3 !== void 0) { + let _tmp4: (Array<_val>) | undefined; + let _tmp5: (Array) | undefined; + _tmp4 = v.slice(2); + { + _tmp5 = []; + for (const _tmp6 of _tmp4) { + let _tmp7: (Caveat) | undefined; + _tmp7 = toCaveat(_tmp6); + if (_tmp7 !== void 0) {_tmp5.push(_tmp7); continue;}; + _tmp5 = void 0; + break; + }; + if (_tmp5 !== void 0) {result = {"_variant": "yours", "oid": _tmp3, "attenuation": _tmp5};}; + }; }; }; }; @@ -401,7 +413,14 @@ export function toBind(v: _val): undefined | Bind { return result; } -export function fromBind(_v: Bind): _val {return _.Record($bind, [_v["oid"], _v["key"], _v["target"]]);} +export function fromWireRef(_v: WireRef): _val { + switch (_v._variant) { + case "mine": {return [$0, fromOid(_v["oid"])];}; + case "yours": { + return [$1, fromOid(_v["oid"]), ... _v["attenuation"].map(v => fromCaveat(v))]; + }; + }; +} export function asConstructorSpec(v: _val): ConstructorSpec { let result = toConstructorSpec(v); diff --git a/src/gen/worker.ts b/src/gen/worker.ts index 2c41c2c..05261cc 100644 --- a/src/gen/worker.ts +++ b/src/gen/worker.ts @@ -10,8 +10,6 @@ export type _val = _.Value<_ptr>; export type Instance = {"name": string, "argument": _val}; -export const _toPtr = (v: _val) => {let result: undefined | _ptr; result = _i_Actor.toRef(v); return result;}; - export function Instance({name, argument}: {name: string, argument: _val}): Instance {return {"name": name, "argument": argument};} export function asInstance(v: _val): Instance { diff --git a/src/runtime/bag.ts b/src/runtime/bag.ts index d00acbe..cacaf80 100644 --- a/src/runtime/bag.ts +++ b/src/runtime/bag.ts @@ -1,6 +1,6 @@ // Bags and Deltas (which are Bags where item-counts can be negative). -import { Value, Set, Dictionary, DefaultPointer } from '@preserves/core'; +import { Value, Set, Dictionary, GenericPointer } from '@preserves/core'; export enum ChangeDescription { PRESENT_TO_ABSENT = -1, @@ -9,7 +9,7 @@ export enum ChangeDescription { PRESENT_TO_PRESENT = 2, } -export class Bag { +export class Bag { _items: Dictionary; constructor(s?: Set) { diff --git a/src/runtime/rewrite.ts b/src/runtime/rewrite.ts index 5a7ed03..858ebdc 100644 --- a/src/runtime/rewrite.ts +++ b/src/runtime/rewrite.ts @@ -23,6 +23,7 @@ import { TRef, Template, _val, + _ptr, } from '../gen/sturdy.js'; export * from '../gen/sturdy.js'; @@ -90,9 +91,9 @@ export function match(p: Pattern, v: Assertion): Bindings | null { } return true; case 'CDict': - if (!Dictionary.isDictionary(v)) return false; + if (!Dictionary.isDictionary(v)) return false; for (const [key, pp] of members) { - const vv = v.get(key as Assertion); + const vv = v.get(key); if (vv === void 0) return false; if (!walk(pp, vv)) return false; } @@ -118,7 +119,7 @@ export function instantiate(t: Template, b: Bindings): Assertion { return v; } case 'Lit': - return t.value.value as Assertion; + return t.value.value; case 'TCompound': { const ctor = t.value.ctor; const members = t.value.members; @@ -143,7 +144,7 @@ export function instantiate(t: Template, b: Bindings): Assertion { case 'CDict': { const v = new Dictionary(); for (const [key, tt] of members) { - v.set(key as Assertion, walk(tt)); + v.set(key, walk(tt)); } return v; } @@ -233,21 +234,21 @@ export function forwarder(t: Turn, ref: Ref): { proxy: Ref, revoker: Ref } { export function pRec(label: _val, ... members: Array): Pattern { return Pattern.PCompound(PCompound({ ctor: ConstructorSpec.CRec(CRec({ label: label, arity: members.length })), - members: PCompoundMembers(new Dictionary( + members: PCompoundMembers(new Dictionary<_ptr, Pattern>( members.map((p, i) => [i, p] as const).filter(e => e[1]._variant !== 'PDiscard')))})); } export function pArr(... members: Array): Pattern { return Pattern.PCompound(PCompound({ ctor: ConstructorSpec.CArr(CArr(members.length)), - members: PCompoundMembers(new Dictionary( + members: PCompoundMembers(new Dictionary<_ptr, Pattern>( members.map((p, i) => [i, p] as const).filter(e => e[1]._variant !== 'PDiscard')))})); } export function pDict(... entries: [_val, Pattern][]): Pattern { return Pattern.PCompound(PCompound({ ctor: ConstructorSpec.CDict(CDict()), - members: PCompoundMembers(new Dictionary(entries))})); + members: PCompoundMembers(new Dictionary<_ptr, Pattern>(entries))})); } export function pLit(value: _val): Pattern { diff --git a/src/tools/attenuate.ts b/src/tools/attenuate.ts index 9e4da42..cf2a236 100644 --- a/src/tools/attenuate.ts +++ b/src/tools/attenuate.ts @@ -1,10 +1,10 @@ import { Bytes, Reader } from '@preserves/core'; -import { fromSturdyRef, toSturdyRef, toCaveat, attenuate, sturdyDecode, sturdyEncode } from '../transport/sturdy.js'; +import { fromSturdyRef, toSturdyRef, toCaveat, attenuate, sturdyDecode, sturdyEncode, _ptr } from '../transport/sturdy.js'; const [ base, pat ] = process.argv.slice(2); const baseCap = toSturdyRef(sturdyDecode(Bytes.fromHex(base ?? ''))); if (baseCap === void 0) throw new Error("Cannot decode sturdyref"); -const cs0 = new Reader(pat).next(); +const cs0 = new Reader<_ptr>(pat).next(); if (!Array.isArray(cs0)) throw new Error("Expected array of caveats"); const cs = cs0.map(c => toCaveat(c) ?? (()=>{ throw new Error("Cannot decode caveat"); })()); attenuate(baseCap, ... cs).then(derived => { diff --git a/src/transport/protocol.ts b/src/transport/protocol.ts index 15f0558..d0d46ea 100644 --- a/src/transport/protocol.ts +++ b/src/transport/protocol.ts @@ -1,42 +1,24 @@ -import { Attenuation, toAttenuation } from '../gen/sturdy.js'; +import * as S from '../gen/sturdy.js'; import * as IO from '../gen/protocol.js'; -import { Assertion, Ref } from '../runtime/actor.js'; -import { mapPointers } from '@preserves/core'; -import { pointerNotAllowed } from './sturdy.js'; +import { Ref } from '../runtime/actor.js'; +import { Decoder, DecoderState, Encoder, EncoderState, GenericPointer, neverPointerType, PointerType, Value } from '@preserves/core'; export type WireSymbol = { oid: IO.Oid, ref: Ref, count: number }; -export type WireRef = - | { loc: "mine", oid: IO.Oid } - | { loc: "your", oid: IO.Oid, attenuation: Attenuation }; +export const wireRefPointerType: PointerType = { + decode(s: DecoderState): S.WireRef { + return S.asWireRef(new Decoder(s).next()); + }, -export function myRef(oid: IO.Oid): WireRef & { loc: "mine" } { - return { loc: 'mine', oid }; -} + encode(s: EncoderState, v: S.WireRef): void { + new Encoder(s, neverPointerType).push(S.fromWireRef(v)); + }, -export function yourRef(oid: IO.Oid, attenuation: Attenuation): WireRef & { loc: "your" } { - return { loc: 'your', oid, attenuation }; -} + fromValue(v: Value): S.WireRef { + return S.asWireRef(v as S._val); + }, -export function toWireRef(v: IO._val): WireRef | undefined { - if (Array.isArray(v) && v.length >= 2) { - switch (v[0]) { - case 0: { - const oid = v[1]; - if (typeof oid === 'number') return myRef(oid); - break; - } - case 1: { - const oid = v[1]; - if (typeof oid === 'number') { - const attenuation = toAttenuation(mapPointers(v.slice(2), pointerNotAllowed) as Array); - if (attenuation !== void 0) return yourRef(oid, attenuation); - } - break; - } - default: - break; - } + toValue(v: S.WireRef): Value { + return S.fromWireRef(v) as Value; } - return void 0; -} +}; diff --git a/src/transport/relay.ts b/src/transport/relay.ts index 44551cd..7c8144b 100644 --- a/src/transport/relay.ts +++ b/src/transport/relay.ts @@ -1,11 +1,10 @@ import { Actor, Assertion, Entity, Facet, Handle, Ref, Turn } from '../runtime/actor.js'; -import { BytesLike, canonicalString, Decoder, encode, FlexMap, IdentityMap, mapPointers, underlying, Value } from '@preserves/core'; +import { BytesLike, Decoder, Dictionary, embed, encode, IdentityMap, mapPointers, underlying, Value } from '@preserves/core'; import * as IO from '../gen/protocol.js'; -import { myRef, toWireRef, WireRef, WireSymbol, yourRef } from './protocol.js'; +import { wireRefPointerType, WireSymbol } from './protocol.js'; import { queueTask } from '../runtime/task.js'; import { attenuate } from '../runtime/rewrite.js'; -import { Attenuation, fromAttenuation } from '../gen/sturdy.js'; -import { pointerNotAllowed } from './sturdy.js'; +import { fromAttenuation, WireRef } from '../gen/sturdy.js'; export class SyncPeerEntity implements Entity { readonly relay: Relay; @@ -136,8 +135,10 @@ export class Relay { debug: boolean; trustPeer: boolean; - readonly decoder = new Decoder(void 0, { includeAnnotations: false }) - .replacePointerDecoder(d => toWireRef(d.next())); + readonly decoder = new Decoder(void 0, { + includeAnnotations: false, + pointerType: wireRefPointerType, + }); constructor(t: Turn, options: RelayOptions) { this.facet = t.activeFacet; @@ -152,14 +153,14 @@ export class Relay { rewriteOut(assertion: Assertion, transient: boolean): [Value, Array] { const exported: Array = []; - const rewritten = mapPointers(assertion, r => this.rewriteRefOut(r, transient, exported)); + const rewritten = mapPointers(assertion, r => embed(this.rewriteRefOut(r, transient, exported))); return [rewritten, exported]; } rewriteIn(t: Turn, a: Value): [Assertion, Array] { const imported: Array = []; - const rewritten = mapPointers(a, r => this.rewriteRefIn(t, r, imported)); + const rewritten = mapPointers(a, r => embed(this.rewriteRefIn(t, r, imported))); return [rewritten, imported]; } @@ -177,12 +178,12 @@ export class Relay { if (r.target instanceof RelayEntity && r.target.relay === this) { if (r.attenuation === void 0 || r.attenuation.length === 0) { // No extra conditions on this reference since it was sent to us. - return yourRef(r.target.oid, []); + return WireRef.yours({ oid: r.target.oid, attenuation: [] }); } else { // This reference has been attenuated since it was sent to us. // Do we trust the peer to enforce such attenuation on our behalf? if (this.trustPeer) { - return yourRef(r.target.oid, r.attenuation); + return WireRef.yours({ oid: r.target.oid, attenuation: r.attenuation }); } else { // fall through: treat the attenuated ref as a local ref, and re-export it. } @@ -195,7 +196,7 @@ export class Relay { return { oid: this.nextLocalOid++, ref: r, count: 0 }; }); exported.push(e); - return myRef(e.oid); + return WireRef.mine(e.oid); } releaseRefOut(e: WireSymbol) { @@ -203,18 +204,18 @@ export class Relay { } rewriteRefIn(t: Turn, n: WireRef, imported: Array): Ref { - switch (n.loc) { - case 'your': { + switch (n._variant) { + case 'yours': { const r = this.lookupLocal(n.oid); if (n.attenuation.length === 0 || r === INERT_REF) { return r; } else { - type AttenuatedRef = Ref & { __attenuations?: FlexMap }; + type AttenuatedRef = Ref & { __attenuations?: Dictionary }; const ar = r as AttenuatedRef; if (ar.__attenuations === void 0) { - ar.__attenuations = new FlexMap(canonicalString); + ar.__attenuations = new Dictionary(); } - return ar.__attenuations.getOrSet(n.attenuation, () => + return ar.__attenuations.getOrSet(fromAttenuation(n.attenuation), () => attenuate(r, ... n.attenuation)); } } @@ -231,15 +232,9 @@ export class Relay { if (this.pendingTurn.length === 0) { queueTask(() => { if (this.debug) console.log('OUT', IO.fromTurn(this.pendingTurn).asPreservesText()); - this.w(underlying(encode(IO.fromTurn(this.pendingTurn), { + this.w(underlying(encode(IO.fromTurn(this.pendingTurn), { canonical: true, - encodePointer: (e, n) => { - switch (n.loc) { - case 'mine': return e.push([0, n.oid]); - case 'your': return e.push([1, n.oid, ... mapPointers( - fromAttenuation(n.attenuation), pointerNotAllowed) as Array]); - } - }, + pointerType: wireRefPointerType, }))); this.pendingTurn = []; }); @@ -323,7 +318,7 @@ export function spawnRelay(t: Turn, options: RelayActorOptions): Promise(v, { canonical: true, includeAnnotations: false, - encodePointer: pointerNotAllowed, + pointerType: neverPointerType, }); } export function sturdyDecode(bs: Bytes): SturdyValue { return decode(bs, { includeAnnotations: false, - decodePointer: pointerNotAllowed, + pointerType: neverPointerType, }); } diff --git a/src/worker/wload.ts b/src/worker/wload.ts index 80e2d73..3798688 100644 --- a/src/worker/wload.ts +++ b/src/worker/wload.ts @@ -1,13 +1,9 @@ // Web Worker loader -import { Actor, Turn, Assertion, Ref, __setNextActorId } from '../runtime/actor.js'; -import { Record } from '@preserves/core'; +import { Actor, Turn, Ref, __setNextActorId } from '../runtime/actor.js'; import { parentPort, threadId } from 'worker_threads'; import { Relay, spawnRelay } from '../transport/relay.js'; - -const _Instance = Symbol.for('Instance'); -const Instance = Record.makeConstructor<{moduleName: string, arg: Assertion}>()( - _Instance, ['moduleName', 'arg']); +import { toInstance } from '../gen/worker.js'; const STARTING_ACTOR_ID = (threadId & (2 ** 20 - 1)) * 1000000000; __setNextActorId(STARTING_ACTOR_ID); @@ -30,13 +26,14 @@ new Actor(t => { p.on('close', () => Turn.for(t.activeFacet, t => t.stopActor())); }, initialRef: t.ref({ - async assert(t, inst) { - if (!Instance.isClassOf(inst)) return; + async assert(t, inst0) { + const inst = toInstance(inst0); + if (inst === void 0) return; if (taskState.state !== "start_pending") return; taskState = { state: "starting" }; - const m = await import(Instance._.moduleName(inst)); + const m = await import(inst.name); t.freshen(t => t.spawn(t => { t.activeFacet.actor.atExit(() => { console.log('Worker terminating'); @@ -49,7 +46,7 @@ new Actor(t => { state: "running", shutdownRef: t.ref({ message(t) { t.stopActor(); } }), }; - m.default(t, Instance._.arg(inst)); + m.default(t, inst.argument); } })); },