From 201ab84d038a1c264757b82fe7b1166f5a205ee9 Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Mon, 26 Nov 2018 21:58:40 +0000 Subject: [PATCH] Steps toward visualization of dataspaces --- packages/core/src/dataspace.js | 96 +++++++++++++++++++++++++++++++++- packages/core/src/ground.js | 16 +----- packages/core/src/index.js | 6 +-- packages/core/src/skeleton.js | 4 +- packages/syntax/src/plugin.js | 8 ++- 5 files changed, 107 insertions(+), 23 deletions(-) diff --git a/packages/core/src/dataspace.js b/packages/core/src/dataspace.js index 8eb633e..86c96cd 100644 --- a/packages/core/src/dataspace.js +++ b/packages/core/src/dataspace.js @@ -232,6 +232,97 @@ Dataspace.prototype.unsubscribe = function (handler) { Dataspace.prototype.endpointHook = function (facet, endpoint) { }; +Dataspace.prototype._debugString = function (outerIndent) { + const pieces = []; + pieces.push(this.index.root._debugString(outerIndent)); + outerIndent = outerIndent || '\n'; + pieces.push(outerIndent + 'FACET TREE'); + this.actors.forEach((a) => { + pieces.push(outerIndent + ' ' + a.toString()); + function walkFacet(indent, f) { + pieces.push(indent + f.toString()); + f.endpoints.forEach((ep) => { + pieces.push(indent + ' - ' + ep.id + ': ' + (ep.assertion && ep.assertion.toString())); + }); + f.children.forEach((child) => { walkFacet(indent + ' ', child); }); + } + a.rootFacet.children.forEach((child) => { walkFacet(outerIndent + ' ', child); }); + }); + pieces.push(outerIndent + 'ACTORS'); + this.actors.forEach((a) => pieces.push(outerIndent + ' ' + a.toString())); + return pieces.join(''); +}; + +Dataspace.prototype._dotGraph = function () { + let id = 0; + const assertionIds = {}; + function _aId(aStr) { + if (!(aStr in assertionIds)) assertionIds[aStr] = id++; + return assertionIds[aStr]; + } + const assertions = {}; + function _str(a) { + return '' + a; + } + function walkNode(n) { + n.edges.forEach((table) => table.forEach(walkNode)); + n.continuation.leafMap.forEach((cvMap) => cvMap.forEach((leaf) => { + leaf.handlerMap.forEach((handler) => { + handler.callbacks.forEach((cb) => { + const observing_assertion_str = _str(cb.__endpoint.handler.assertion); + leaf.cachedAssertions.forEach((observed_assertion) => { + const observed_assertion_str = _str(observed_assertion); + _aId(observed_assertion_str); + if (!(observed_assertion_str in assertions)) assertions[observed_assertion_str] = {}; + const t = assertions[observed_assertion_str]; + if (!(observing_assertion_str in t)) t[observing_assertion_str] = true; + _aId(observing_assertion_str); + }); + }); + }); + })); + } + walkNode(this.index.root); + const pieces = []; + pieces.push('graph G {'); + pieces.push('\n overlap=false;'); + for (const a in assertionIds) { + pieces.push(`\n assn_${assertionIds[a]} [label=${JSON.stringify(a).replace(/ /g,'\\n')}];`); + } + for (const observed in assertions) { + for (const observing in assertions[observed]) { + pieces.push(`\n assn_${assertionIds[observed]} -- assn_${assertionIds[observing]};`); + } + } + this.actors.forEach((ac) => { + const acId = id++; + pieces.push(`\n ac_${acId} [label=${JSON.stringify(ac.toString())}];`); + function walkFacet(parent) { + return (f) => { + const facetId = id++; + pieces.push(`\n facet_${facetId} [label="Facet ${f.id}"];`); + pieces.push(`\n ${parent} -- facet_${facetId};`); + f.endpoints.forEach((ep) => { + const aStr = _str(ep.assertion); + pieces.push(`\n ep_${ep.id} [label="${ep.id}"];`); + pieces.push(`\n facet_${facetId} -- ep_${ep.id};`); + if (aStr in assertionIds) { + pieces.push(`\n ep_${ep.id} -- assn_${assertionIds[_str(ep.assertion)]};`); + } else { + const aId = _aId(aStr); + pieces.push(`\n assn_${aId} [label=${JSON.stringify(aStr).replace(/ /g,'\\n')}];`); + pieces.push(`\n ep_${ep.id} -- assn_${aId};`); + } + }); + f.children.forEach(walkFacet(`facet_${facetId}`)); + }; + } + ac.rootFacet.children.forEach(walkFacet(`ac_${acId}`)); + }); + pieces.push('\n}'); + return pieces.join(''); +}; + function Actor(dataspace, name, initialAssertions) { this.id = dataspace.nextId++; this.dataspace = dataspace; @@ -610,7 +701,10 @@ Endpoint.prototype._install = function (ds, ac, assertion, handler) { this.assertion = assertion; this.handler = handler; ac.assert(this.assertion); - if (this.handler) { ds.subscribe(this.handler); } + if (this.handler) { + this.handler.callback.__endpoint = this; // for reflection/debugging + ds.subscribe(this.handler); + } }; Endpoint.prototype._uninstall = function (ds, ac, emitPatches) { diff --git a/packages/core/src/ground.js b/packages/core/src/ground.js index 94dba93..aaff4d2 100644 --- a/packages/core/src/ground.js +++ b/packages/core/src/ground.js @@ -106,21 +106,7 @@ function bootModule(mod) { } else { process.on('SIGQUIT', () => { console.log('---------------------------------------------------------------------------'); - console.log(g.index.root._debugString()); - console.log('FACET TREE'); - g.actors.forEach((a) => { - console.log(' ' + a.toString()); - function walkFacet(indent, f) { - console.log(indent + f.toString()); - f.endpoints.forEach((ep) => { - console.log(indent + ' - ' + ep.id + ': ' + (ep.assertion && ep.assertion.toString())); - }); - f.children.forEach((child) => { walkFacet(indent + ' ', child); }); - } - a.rootFacet.children.forEach((child) => { walkFacet(' ', child); }); - }); - console.log('ACTORS'); - g.actors.forEach((a) => console.log(' ' + a.toString())); + console.log(g._dotGraph()); }); g.start(); } diff --git a/packages/core/src/index.js b/packages/core/src/index.js index 41988f5..560c6c0 100644 --- a/packages/core/src/index.js +++ b/packages/core/src/index.js @@ -23,13 +23,13 @@ const Dataspace = require('./dataspace.js'); const Ground = require('./ground.js'); const Assertions = require('./assertions.js'); const Relay = require('./relay.js'); +const Bag = require('./bag.js'); Object.assign(module.exports, require("preserves")); -module.exports.Immutable = require('immutable'); -// ^ for use by import machinery in syntactic extensions +//--------------------------------------------------------------------------- -module.exports.Bag = require("./bag.js"); +module.exports.Bag = Bag; module.exports.Skeleton = Skeleton; module.exports.RandomID = RandomID; diff --git a/packages/core/src/skeleton.js b/packages/core/src/skeleton.js index bdadbcd..db775a3 100644 --- a/packages/core/src/skeleton.js +++ b/packages/core/src/skeleton.js @@ -22,7 +22,7 @@ const { Record } = require("preserves"); const $Special = require('./special.js'); const Bag = require('./bag.js'); -const { Capture, Discard } = require('./assertions.js'); +const { Discard, Capture, Observe } = require('./assertions.js'); const EVENT_ADDED = +1; const EVENT_REMOVED = -1; @@ -364,7 +364,7 @@ function analyzeAssertion(a) { let skeleton = walk(Immutable.List(), a); - return { skeleton, constPaths, constVals, capturePaths }; + return { skeleton, constPaths, constVals, capturePaths, assertion: Observe(a) }; } function OpaquePlaceholder() {} diff --git a/packages/syntax/src/plugin.js b/packages/syntax/src/plugin.js index 18ec54b..81212bf 100644 --- a/packages/syntax/src/plugin.js +++ b/packages/syntax/src/plugin.js @@ -244,17 +244,20 @@ const bindingRegistrationVisitor = { function translateEndpoint(state, path, expectedEvt) { const { node } = path; let info = compilePattern(state, path.get('pattern')); + let _assn = path.scope.generateUidIdentifier("assn"); let _evt = path.scope.generateUidIdentifier("evt"); let _vs = path.scope.generateUidIdentifier("vs"); path.replaceWith(template( `DATASPACE._currentFacet.addEndpoint(function () { CONSTTEMPS; + const ASSN = ASSERTION; let HANDLER = { skeleton: SKELETON, constPaths: CONSTPATHS, constVals: CONSTVALS, capturePaths: CAPTUREPATHS, + assertion: ASSN, callback: DATASPACE.wrap((EVT, VS) => { if (EVT === EXPECTED) { INITS; @@ -264,13 +267,15 @@ function translateEndpoint(state, path, expectedEvt) { } }) }; - return [ASSERTION, HANDLER]; + return [ASSN, HANDLER]; }, ISDYNAMIC);`)({ DATASPACE: state.DataspaceID, HANDLER: path.scope.generateUidIdentifier("handler"), SKELETON: info.skeletonAst, CONSTPATHS: info.constPathsAst, CONSTTEMPS: info.constTemps.map(([n,i]) => template(`const N = I;`)({N:n, I:i})), + ASSN: _assn, + ASSERTION: info.assertionAst, CONSTVALS: info.constValsAst, CAPTUREPATHS: info.capturePathsAst, EVT: _evt, @@ -282,7 +287,6 @@ function translateEndpoint(state, path, expectedEvt) { I: t.numericLiteral(i), })), BODY: maybeTerminalWrap(state, node.terminal, node.body), - ASSERTION: info.assertionAst, ISDYNAMIC: t.booleanLiteral(node.isDynamic), })); }