Skip to content

Instantly share code, notes, and snippets.

@dckc
Forked from warner/bootstrap.js
Last active August 2, 2020 21:06
Show Gist options
  • Select an option

  • Save dckc/f8e0b5d838079a994784d599c282cce7 to your computer and use it in GitHub Desktop.

Select an option

Save dckc/f8e0b5d838079a994784d599c282cce7 to your computer and use it in GitHub Desktop.

Revisions

  1. dckc revised this gist Aug 2, 2020. 8 changed files with 48 additions and 20 deletions.
    2 changes: 1 addition & 1 deletion bin/node-vat-worker
    Original file line number Diff line number Diff line change
    @@ -1,2 +1,2 @@
    #!/bin/sh
    node -r esm index.js
    node -r esm ./src/index.js
    3 changes: 2 additions & 1 deletion package.json
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,7 @@
    "xs-run": "yarn xs-build && ./build/bin/lin/debug/make-vat-transcript",
    "xs-build-release": "mkdir -p build; mcconfig -o build -p x-cli-lin -m",
    "xs-run-release": "yarn xs-build-release && ./build/bin/lin/release/make-vat-transcript",
    "test": "tape -r esm 'test/**/*.js'",
    "test": "tape -r esm 'test/**/test-*.js'",
    "build": "exit 0",
    "lint-fix": "eslint --fix '**/*.{js,jsx}'",
    "lint-check": "eslint '**/*.{js,jsx}'",
    @@ -36,6 +36,7 @@
    "@agoric/import-bundle": "^0.0.8",
    "@agoric/install-ses": "^0.2.0",
    "@agoric/produce-promise": "^0.1.3",
    "@agoric/swingset-vat": "^0.6.0",
    "anylogger": "^1.0.4",
    "esm": "^3.2.5"
    },
    2 changes: 1 addition & 1 deletion src/vatWorker.js
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    import { importBundle } from '@agoric/import-bundle';
    import { HandledPromise } from '@agoric/eventual-send';
    // TODO? import anylogger from 'anylogger';
    import { makeLiveSlots } from './swingset/kernel/liveSlots';
    import { makeLiveSlots } from '@agoric/swingset-vat/src/kernel/liveSlots';

    const EOF = new Error('EOF');

    1 change: 0 additions & 1 deletion swingset/capdata.js
    1 change: 0 additions & 1 deletion swingset/kernel/liveSlots.js
    1 change: 0 additions & 1 deletion swingset/parseVatSlots.js
    30 changes: 16 additions & 14 deletions test/kernelSimulator.js
    Original file line number Diff line number Diff line change
    @@ -96,7 +96,7 @@ async function runTranscript(w1, bundle, transcript) {
    }

    // eslint-disable-next-line no-shadow
    async function main(argv, { env, io, bundleSource, spawn }) {
    export async function main(argv, { env, io, bundleSource, spawn }) {
    const { vat1, transcript, workerBin } = options(env);

    const bundle = await bundleSource(vat1);
    @@ -116,17 +116,19 @@ async function main(argv, { env, io, bundleSource, spawn }) {
    await runTranscript(w1, bundle, text);
    }

    main(process.argv, {
    env: process.env,
    // eslint-disable-next-line global-require
    io: {
    if (require.main === module) {
    main(process.argv, {
    env: process.env,
    // eslint-disable-next-line global-require
    readFile: require('fs').promises.readFile,
    },
    // eslint-disable-next-line global-require
    bundleSource: require('@agoric/bundle-source').default,
    // eslint-disable-next-line global-require
    spawn: require('child_process').spawn,
    }).catch(err => {
    console.error(err);
    });
    io: {
    // eslint-disable-next-line global-require
    readFile: require('fs').promises.readFile,
    },
    // eslint-disable-next-line global-require
    bundleSource: require('@agoric/bundle-source').default,
    // eslint-disable-next-line global-require
    spawn: require('child_process').spawn,
    }).catch(err => {
    console.error(err);
    });
    }
    28 changes: 28 additions & 0 deletions test/test-vat1.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,28 @@
    import { test } from 'tape-promise/tape';

    import { main } from './kernelSimulator';

    const resolve = p => new URL(p, import.meta.url).toString().slice('file://'.length);

    test('replay simple transcript with node vatWorker', async t => {
    process.env.WORKERBIN = resolve('../bin/node-vat-worker');
    process.env.VAT1 = resolve('./vat-target.js');
    process.env.TRANSCRIPT = resolve('./transcript.txt');

    process.chdir(resolve('..')); // so node-vat-worker can find stuff in src/
    await main(process.argv, {
    env: process.env,
    // eslint-disable-next-line global-require
    io: {
    // eslint-disable-next-line global-require
    readFile: require('fs').promises.readFile,
    },
    // eslint-disable-next-line global-require
    bundleSource: require('@agoric/bundle-source').default,
    // eslint-disable-next-line global-require
    spawn: require('child_process').spawn,
    });

    t.ok('did not crash');
    t.end();
    })
  2. dckc revised this gist Aug 2, 2020. 2 changed files with 54 additions and 52 deletions.
    52 changes: 52 additions & 0 deletions src/netstring.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,52 @@

    export async function readNetstring(input) {
    let prefix = Buffer.from([]);
    let colonPos = -1;

    const nextChunk = () =>
    new Promise((resolve, _reject) => {
    const rx = data => {
    input.pause();
    input.removeListener('data', rx);
    resolve(data);
    };
    input.on('data', rx);
    input.resume();
    });

    while (colonPos < 0) {
    // eslint-disable-next-line no-await-in-loop
    const more = await nextChunk();
    prefix = Buffer.concat([prefix, more]);
    colonPos = prefix.indexOf(':');
    }
    let len;
    const digits = prefix.slice(0, colonPos).toString('utf-8');
    try {
    len = parseInt(digits, 10);
    } catch (badLen) {
    throw new Error(`bad netstring length ${digits}`);
    }
    // console.error('readNetstring parsed len', { digits, len });
    let data = prefix.slice(colonPos + 1);
    while (data.length <= len) {
    // console.log('netstring: looking for payload', data.length, len);
    // eslint-disable-next-line no-await-in-loop
    const more = await nextChunk(input);
    data = Buffer.concat([data, more]);
    }
    if (data.slice(len).toString('utf-8') !== ',') {
    throw new Error(
    `bad netstring: expected , got ${data.slice(-1)} [${data.slice(-20)}]`,
    );
    }
    return data.slice(0, len).toString('utf-8');
    }

    export async function writeNetstring(out, payload) {
    // ISSUE: non-ASCII length
    // console.log('kernelSimulator send size', content.length);
    await out.write(`${payload.length}:`);
    await out.write(payload);
    await out.write(',');
    }
    54 changes: 2 additions & 52 deletions test/kernelSimulator.js
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,8 @@
    // context: https://github.com/Agoric/agoric-sdk/issues/1299
    import '@agoric/install-ses';

    import { readNetstring, writeNetstring } from '../src/netstring';

    const INFD = 3;
    const OUTFD = 4;

    @@ -13,58 +15,6 @@ function options(env) {
    };
    }

    async function readNetstring(input) {
    let prefix = Buffer.from([]);
    let colonPos = -1;

    const nextChunk = () =>
    new Promise((resolve, _reject) => {
    const rx = data => {
    input.pause();
    input.removeListener('data', rx);
    resolve(data);
    };
    input.on('data', rx);
    input.resume();
    });

    while (colonPos < 0) {
    // eslint-disable-next-line no-await-in-loop
    const more = await nextChunk();
    prefix = Buffer.concat([prefix, more]);
    colonPos = prefix.indexOf(':');
    }
    let len;
    const digits = prefix.slice(0, colonPos).toString('utf-8');
    try {
    len = parseInt(digits, 10);
    } catch (badLen) {
    throw new Error(`bad netstring length ${digits}`);
    }
    // console.error('readNetstring parsed len', { digits, len });
    let data = prefix.slice(colonPos + 1);
    while (data.length <= len) {
    // console.log('netstring: looking for payload', data.length, len);
    // eslint-disable-next-line no-await-in-loop
    const more = await nextChunk(input);
    data = Buffer.concat([data, more]);
    }
    if (data.slice(len).toString('utf-8') !== ',') {
    throw new Error(
    `bad netstring: expected , got ${data.slice(-1)} [${data.slice(-20)}]`,
    );
    }
    return data.slice(0, len).toString('utf-8');
    }

    async function writeNetstring(out, payload) {
    // ISSUE: non-ASCII length
    // console.log('kernelSimulator send size', content.length);
    await out.write(`${payload.length}:`);
    await out.write(payload);
    await out.write(',');
    }

    function makeWorker(child) {
    const format = obj => JSON.stringify(obj);
    const send = obj => writeNetstring(child.stdio[INFD], format(obj));
  3. dckc revised this gist Aug 2, 2020. 16 changed files with 0 additions and 0 deletions.
    File renamed without changes.
    File renamed without changes.
    File renamed without changes.
    File renamed without changes.
    File renamed without changes.
    File renamed without changes.
    File renamed without changes.
    File renamed without changes.
    File renamed without changes.
    File renamed without changes.
    File renamed without changes.
    File renamed without changes.
    File renamed without changes.
    File renamed without changes.
    File renamed without changes.
    File renamed without changes.
  4. dckc revised this gist Aug 1, 2020. 3 changed files with 3 additions and 6 deletions.
    1 change: 1 addition & 0 deletions manifest.json
    Original file line number Diff line number Diff line change
    @@ -34,6 +34,7 @@
    "@agoric/marshal": "xs_modules/@agoric/marshal/marshal",
    "@agoric/produce-promise": "xs_modules/@agoric/produce-promise/producePromise",
    "@agoric/assert": "xs_modules/@agoric/assert/assert",
    "@agoric/types": "xs_modules/@agoric/assert/types",
    "swingset/capdata": "./swingset/capdata",
    "swingset/parseVatSlots": "./swingset/parseVatSlots",
    "swingset/kernel/liveSlots": "./swingset/kernel/liveSlots",
    7 changes: 1 addition & 6 deletions vatWorker.js
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,7 @@ import { makeLiveSlots } from './swingset/kernel/liveSlots';

    const EOF = new Error('EOF');

    // from SwingSet/src/
    // from SwingSet/src/controller.js
    function makeConsole(_tag) {
    const log = console; // TODO? anylogger(tag);
    const cons = {};
    @@ -15,13 +15,8 @@ function makeConsole(_tag) {
    return harden(cons);
    }

    function vatRequire(what) {
    throw Error(`vatRequire unprepared to satisfy require(${what})`);
    }

    function makeVatEndowments(consoleTag) {
    return harden({
    require: vatRequire,
    console: makeConsole(`SwingSet:${consoleTag}`),
    HandledPromise,
    // TODO: re2 is a RegExp work-a-like that disables backtracking expressions for
    1 change: 1 addition & 0 deletions xs_modules/@agoric/assert/types.js
  5. dckc revised this gist Aug 1, 2020. 8 changed files with 106 additions and 69 deletions.
    74 changes: 74 additions & 0 deletions fdchan.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,74 @@
    /**
    * ref Netstrings 19970201 by Bernstein
    * https://cr.yp.to/proto/netstrings.txt
    *
    * Moddable in C
    * https://github.com/Moddable-OpenSource/moddable/blob/public/documentation/xs/XS%20in%20C.md
    *
    * stdio
    * https://pubs.opengroup.org/onlinepubs/009695399/basedefs/stdio.h.html
    */

    #include <assert.h>
    #include <stdio.h>

    #include "xsAll.h"
    #include "xs.h"

    void xs_Reader(xsMachine *the) {
    int argc = xsToInteger(xsArgc);
    if (argc < 1) {
    mxTypeError("expected fd");
    }
    int fd = xsToInteger(xsArg(0));
    FILE *inStream = fdopen(fd, "rb");
    if (!inStream) {
    mxUnknownError("fdopen failed");
    }
    xsSetHostData(xsThis, (void *)((uintptr_t)inStream));

    // modInstrumentationAdjust(Files, +1);
    }

    void xs_Writer(xsMachine *the) {
    int argc = xsToInteger(xsArgc);
    if (argc < 1) {
    mxTypeError("expected fd");
    }
    int fd = xsToInteger(xsArg(0));
    FILE *outStream = fdopen(fd, "wb");
    if (!outStream) {
    mxUnknownError("fdopen failed");
    }
    xsSetHostData(xsThis, (void *)((uintptr_t)outStream));

    // modInstrumentationAdjust(Files, +1);
    }

    void xs_read_netstring(xsMachine *the) {
    size_t len;
    char* buf = NULL;
    FILE *inStream = xsGetHostData(xsThis);
    assert(inStream);

    if (fscanf(inStream, "%9lu", &len) < 1) { goto BARF; } /* >999999999 bytes is bad */
    // fprintf(stderr, "xs_stdin_read_netstring len %lu\n", len);
    if (fgetc(inStream) != ':') { goto BARF; }
    buf = malloc(len + 1); /* malloc(0) is not portable */
    if (!buf) { goto BARF; }
    if (fread(buf, 1, len, inStream) < len) { goto BARF; }
    if (fgetc(inStream) != ',') { goto BARF; }

    xsResult = xsStringBuffer(buf, len);
    free(buf);
    // fprintf(stderr, "xs_stdin_read_nestring return\n");
    return;

    BARF:
    free(buf);
    xsUnknownError("getline failed");
    }

    void xs_fdchan_destructor() {

    }
    9 changes: 9 additions & 0 deletions fdchan.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,9 @@
    export class Reader @ "xs_fdchan_destructor" {
    constructor(fd) @ "xs_Reader"
    read_netstring() @ "xs_read_netstring";
    }

    export class Writer @ "xs_fdchan_destructor" {
    constructor(fd) @ "xs_Writer"
    write(...items) @ "xs_file_write";
    }
    15 changes: 7 additions & 8 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -4,14 +4,15 @@ import '@agoric/install-ses';

    import { main as vatWorker } from './vatWorker';

    function makePipe(readStream, writeStream, io, sleep) {
    readStream.resume();
    const INFD = 3;
    const OUTFD = 4;

    function makePipe(io, sleep) {
    function write(data) {
    let done = 0;
    for (;;) {
    try {
    done += io.writeSync(writeStream.fd, data.slice(done));
    done += io.writeSync(OUTFD, data.slice(done));
    if (done >= data.length) {
    return done;
    }
    @@ -41,7 +42,7 @@ function makePipe(readStream, writeStream, io, sleep) {
    for (;;) {
    // console.error('readMessage', { length: buf.length, len, colonPos, offset });
    try {
    offset += io.readSync(readStream.fd, buf, {
    offset += io.readSync(INFD, buf, {
    offset,
    length: buf.length - offset,
    });
    @@ -83,9 +84,9 @@ function makePipe(readStream, writeStream, io, sleep) {
    });
    }

    async function main({ stdin, stdout, setImmediate, fs, spawnSync }) {
    async function main({ setImmediate, fs, spawnSync }) {
    const sleep = secs => spawnSync('sleep', [secs]);
    const pipe = makePipe(stdin, stdout, fs, sleep);
    const pipe = makePipe(fs, sleep);
    return vatWorker({
    readMessage: pipe.readMessage,
    writeMessage: pipe.writeMessage,
    @@ -95,8 +96,6 @@ async function main({ stdin, stdout, setImmediate, fs, spawnSync }) {

    main({
    setImmediate,
    stdin: process.stdin,
    stdout: process.stdout,
    // eslint-disable-next-line global-require
    spawnSync: require('child_process').spawnSync,
    fs: {
    11 changes: 7 additions & 4 deletions kernelSimulator.js
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,9 @@
    // context: https://github.com/Agoric/agoric-sdk/issues/1299
    import '@agoric/install-ses';

    const INFD = 3;
    const OUTFD = 4;

    function options(env) {
    return {
    vat1: env.VAT1 || 'vat-target.js',
    @@ -64,12 +67,12 @@ async function writeNetstring(out, payload) {

    function makeWorker(child) {
    const format = obj => JSON.stringify(obj);
    const send = obj => writeNetstring(child.stdin, format(obj));
    const send = obj => writeNetstring(child.stdio[INFD], format(obj));

    child.stdout.pause();
    child.stdio[OUTFD].pause();

    const expect = async msgtype => {
    const txt = await readNetstring(child.stdout);
    const txt = await readNetstring(child.stdio[OUTFD]);
    let msg;
    try {
    msg = JSON.parse(txt);
    @@ -150,7 +153,7 @@ async function main(argv, { env, io, bundleSource, spawn }) {

    console.log('spawning', { workerBin });
    const child = await spawn(workerBin, [], {
    stdio: ['pipe', 'pipe', 'inherit'],
    stdio: ['inherit', 'inherit', 'inherit', 'pipe', 'pipe'],
    });
    child.on('exit', (code, signal) => {
    if (code !== 0) {
    13 changes: 8 additions & 5 deletions main.js
    Original file line number Diff line number Diff line change
    @@ -6,18 +6,21 @@ import './console'; // sets globalThis.console. ew.
    import './timer-ticks'; // globalThis.setTimeout. ew.

    import { main as vatWorker } from './vatWorker';
    import { Stdin, Stdout } from './stdio';
    import { Reader, Writer } from './fdchan';

    const INFD = 3;
    const OUTFD = 4;

    export default async function main() {
    const stdin = new Stdin();
    const stdout = new Stdout();
    const inStream = new Reader(INFD);
    const outStream = new Writer(OUTFD);

    return vatWorker({
    setImmediate,
    readMessage: () => stdin.read_netstring(),
    readMessage: () => inStream.read_netstring(),
    writeMessage: message => {
    // ISSUE: should be byte length
    stdout.write(`${message.length}:`, message, ',');
    outStream.write(`${message.length}:`, message, ',');
    },
    });
    }
    2 changes: 1 addition & 1 deletion manifest.json
    Original file line number Diff line number Diff line change
    @@ -18,7 +18,7 @@
    "strip": [],
    "modules": {
    "files": [ "$(MODULES)/files/file/*", "$(MODULES)/files/file/lin/*" ],
    "stdio": [ "./stdio" ],
    "fdchan": [ "./fdchan" ],
    "timer": [
    "$(MODULES)/base/timer/timer",
    "$(MODULES)/base/timer/lin/*",
    42 changes: 0 additions & 42 deletions stdio.c
    Original file line number Diff line number Diff line change
    @@ -1,42 +0,0 @@
    #include <stdio.h>

    #include "xsAll.h"
    #include "xs.h"

    void xs_Stdin(xsMachine *the) {
    xsSetHostData(xsThis, (void *)((uintptr_t)stdin));

    // modInstrumentationAdjust(Files, +1);
    }

    void xs_Stdout(xsMachine *the) {
    xsSetHostData(xsThis, (void *)((uintptr_t)stdout));

    // modInstrumentationAdjust(Files, +1);
    }

    void xs_stdin_read_netstring(xsMachine *the) {
    size_t len;
    char* buf = NULL;

    if (scanf("%9lu", &len) < 1) { goto BARF; } /* >999999999 bytes is bad */
    // fprintf(stderr, "xs_stdin_read_netstring len %lu\n", len);
    if (getchar() != ':') { goto BARF; }
    buf = malloc(len + 1); /* malloc(0) is not portable */
    if (!buf) { goto BARF; }
    if (fread(buf, 1, len, stdin) < len) { goto BARF; }
    if (getchar() != ',') { goto BARF; }

    xsResult = xsStringBuffer(buf, len);
    free(buf);
    // fprintf(stderr, "xs_stdin_read_nestring return\n");
    return;

    BARF:
    free(buf);
    xsUnknownError("getline from stdin failed");
    }

    void xs_stdio_destructor() {

    }
    9 changes: 0 additions & 9 deletions stdio.js
    Original file line number Diff line number Diff line change
    @@ -1,9 +0,0 @@
    export class Stdin @ "xs_stdio_destructor" {
    constructor() @ "xs_Stdin"
    read_netstring() @ "xs_stdin_read_netstring";
    }

    export class Stdout @ "xs_stdio_destructor" {
    constructor() @ "xs_Stdout"
    write(...items) @ "xs_file_write";
    }
  6. dckc revised this gist Aug 1, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion kernelSimulator.js
    Original file line number Diff line number Diff line change
    @@ -78,7 +78,7 @@ function makeWorker(child) {
    throw badJSON;
    }
    if (msg.msgtype !== msgtype) {
    throw new Error(`${msg.msgtype}: ${msg.error}`);
    throw new Error(`expected ${msgtype}; found: ${msg.msgtype}; error: ${msg.error} [${JSON.stringify(msg)}]`);
    }
    return msg;
    };
  7. dckc revised this gist Aug 1, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion kernelSimulator.js
    Original file line number Diff line number Diff line change
    @@ -160,7 +160,7 @@ async function main(argv, { env, io, bundleSource, spawn }) {
    const w1 = makeWorker(child);

    const text = await io.readFile(transcript, 'utf-8');
    runTranscript(w1, bundle, text);
    await runTranscript(w1, bundle, text);
    }

    main(process.argv, {
  8. dckc revised this gist Jul 29, 2020. 1 changed file with 25 additions and 20 deletions.
    45 changes: 25 additions & 20 deletions kernelSimulator.js
    Original file line number Diff line number Diff line change
    @@ -106,23 +106,40 @@ function makeWorker(child) {
    });
    }

    async function eachTranscriptEvent(events, thunk) {
    for (const event of events) {
    async function runTranscript(w1, bundle, transcript) {
    await w1.loadVat(bundle);
    console.log('loadVat done.');

    const events = transcript.split('\n');

    while (events.length > 0) {
    const event = events.shift();
    if (!event) {
    // eslint-disable-next-line no-continue
    continue;
    }
    const found = event.match(/^(?<id>[^ ]+) :: (?<obj>.*)/);
    const found = event.match(/^(?<id>[^ ]+) :: (?<payload>.*)/);
    if (!found) {
    console.log('unexpected transcript format', { line: event });
    // eslint-disable-next-line no-continue
    continue;
    }
    const { id, obj } = found.groups;
    // console.log({ id, obj });
    const { id, payload } = found.groups;

    const obj = JSON.parse(payload);
    if (typeof obj !== 'object') {
    console.log('not a dispatch event', id, obj);
    // eslint-disable-next-line no-continue
    continue;
    }
    console.log('dispatching:', id);
    // eslint-disable-next-line no-await-in-loop
    await thunk(id, JSON.parse(obj));
    await w1.dispatch(obj);
    console.log('dispatch done:', id);
    }

    await w1.finish();
    console.log('END OF TRANSCRIPT.');
    }

    // eslint-disable-next-line no-shadow
    @@ -141,21 +158,9 @@ async function main(argv, { env, io, bundleSource, spawn }) {
    }
    });
    const w1 = makeWorker(child);
    await w1.loadVat(bundle);
    console.log('loadVat done.');

    const events = (await io.readFile(transcript, 'utf-8')).split('\n');
    await eachTranscriptEvent(events, async (id, obj) => {
    console.log('dispatching:', id);
    if (typeof obj !== 'object') {
    console.log('not a dispatch event', id, obj);
    return;
    }
    await w1.dispatch(obj);
    console.log('dispatch done:', id);
    });
    await w1.finish();
    console.log('END OF TRANSCRIPT.');
    const text = await io.readFile(transcript, 'utf-8');
    runTranscript(w1, bundle, text);
    }

    main(process.argv, {
  9. dckc revised this gist Jul 29, 2020. 1 changed file with 50 additions and 47 deletions.
    97 changes: 50 additions & 47 deletions kernelSimulator.js
    Original file line number Diff line number Diff line change
    @@ -10,63 +10,66 @@ function options(env) {
    };
    }

    function makeWorker(child) {
    const format = obj => JSON.stringify(obj);
    const send = obj => {
    const content = format(obj);
    // ISSUE: non-ASCII length
    // console.log('kernelSimulator send size', content.length);
    child.stdin.write(`${content.length}:`);
    child.stdin.write(content);
    child.stdin.write(',');
    };
    async function readNetstring(input) {
    let prefix = Buffer.from([]);
    let colonPos = -1;

    child.stdout.pause();
    const nextChunk = () =>
    new Promise((resolve, _reject) => {
    const rx = data => {
    child.stdout.pause();
    child.stdout.removeListener('data', rx);
    input.pause();
    input.removeListener('data', rx);
    resolve(data);
    };
    child.stdout.on('data', rx);
    child.stdout.resume();
    input.on('data', rx);
    input.resume();
    });

    const readNetstring = async () => {
    let prefix = Buffer.from([]);
    let colonPos = -1;
    while (colonPos < 0) {
    // eslint-disable-next-line no-await-in-loop
    const more = await nextChunk();
    prefix = Buffer.concat([prefix, more]);
    colonPos = prefix.indexOf(':');
    }
    let len;
    const digits = prefix.slice(0, colonPos).toString('utf-8');
    try {
    len = parseInt(digits, 10);
    } catch (badLen) {
    throw new Error(`bad netstring length ${digits}`);
    }
    // console.error('readNetstring parsed len', { digits, len });
    let data = prefix.slice(colonPos + 1);
    while (data.length <= len) {
    // console.log('netstring: looking for payload', data.length, len);
    // eslint-disable-next-line no-await-in-loop
    const more = await nextChunk();
    data = Buffer.concat([data, more]);
    }
    if (data.slice(len).toString('utf-8') !== ',') {
    throw new Error(
    `bad netstring: expected , got ${data.slice(-1)} [${data.slice(-20)}]`,
    );
    }
    return data.slice(0, len).toString('utf-8');
    };
    while (colonPos < 0) {
    // eslint-disable-next-line no-await-in-loop
    const more = await nextChunk();
    prefix = Buffer.concat([prefix, more]);
    colonPos = prefix.indexOf(':');
    }
    let len;
    const digits = prefix.slice(0, colonPos).toString('utf-8');
    try {
    len = parseInt(digits, 10);
    } catch (badLen) {
    throw new Error(`bad netstring length ${digits}`);
    }
    // console.error('readNetstring parsed len', { digits, len });
    let data = prefix.slice(colonPos + 1);
    while (data.length <= len) {
    // console.log('netstring: looking for payload', data.length, len);
    // eslint-disable-next-line no-await-in-loop
    const more = await nextChunk(input);
    data = Buffer.concat([data, more]);
    }
    if (data.slice(len).toString('utf-8') !== ',') {
    throw new Error(
    `bad netstring: expected , got ${data.slice(-1)} [${data.slice(-20)}]`,
    );
    }
    return data.slice(0, len).toString('utf-8');
    }

    async function writeNetstring(out, payload) {
    // ISSUE: non-ASCII length
    // console.log('kernelSimulator send size', content.length);
    await out.write(`${payload.length}:`);
    await out.write(payload);
    await out.write(',');
    }

    function makeWorker(child) {
    const format = obj => JSON.stringify(obj);
    const send = obj => writeNetstring(child.stdin, format(obj));

    child.stdout.pause();

    const expect = async msgtype => {
    const txt = await readNetstring();
    const txt = await readNetstring(child.stdout);
    let msg;
    try {
    msg = JSON.parse(txt);
  10. dckc revised this gist Jul 29, 2020. 1 changed file with 10 additions and 7 deletions.
    17 changes: 10 additions & 7 deletions kernelSimulator.js
    Original file line number Diff line number Diff line change
    @@ -27,22 +27,23 @@ function makeWorker(child) {
    const rx = data => {
    child.stdout.pause();
    child.stdout.removeListener('data', rx);
    resolve(data.toString('utf-8'));
    resolve(data);
    };
    child.stdout.on('data', rx);
    child.stdout.resume();
    });

    const readNetstring = async () => {
    let prefix = '';
    let prefix = Buffer.from([]);
    let colonPos = -1;
    while (colonPos < 0) {
    // eslint-disable-next-line no-await-in-loop
    prefix += await nextChunk();
    const more = await nextChunk();
    prefix = Buffer.concat([prefix, more]);
    colonPos = prefix.indexOf(':');
    }
    let len;
    const digits = prefix.slice(0, -1);
    const digits = prefix.slice(0, colonPos).toString('utf-8');
    try {
    len = parseInt(digits, 10);
    } catch (badLen) {
    @@ -51,15 +52,17 @@ function makeWorker(child) {
    // console.error('readNetstring parsed len', { digits, len });
    let data = prefix.slice(colonPos + 1);
    while (data.length <= len) {
    // console.log('netstring: looking for payload', data.length, len);
    // eslint-disable-next-line no-await-in-loop
    data += await nextChunk();
    const more = await nextChunk();
    data = Buffer.concat([data, more]);
    }
    if (data.slice(-1) !== ',') {
    if (data.slice(len).toString('utf-8') !== ',') {
    throw new Error(
    `bad netstring: expected , got ${data.slice(-1)} [${data.slice(-20)}]`,
    );
    }
    return data.slice(0, -1);
    return data.slice(0, len).toString('utf-8');
    };

    const expect = async msgtype => {
  11. dckc revised this gist Jul 29, 2020. 1 changed file with 9 additions and 3 deletions.
    12 changes: 9 additions & 3 deletions kernelSimulator.js
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,14 @@
    // context: https://github.com/Agoric/agoric-sdk/issues/1299
    import '@agoric/install-ses';

    function options(env) {
    return {
    vat1: env.VAT1 || 'vat-target.js',
    transcript: env.TRANSCRIPT || 'transcript.txt',
    workerBin: env.WORKERBIN || './build/bin/lin/release/xs-vat-worker',
    };
    }

    function makeWorker(child) {
    const format = obj => JSON.stringify(obj);
    const send = obj => {
    @@ -113,9 +121,7 @@ async function eachTranscriptEvent(events, thunk) {

    // eslint-disable-next-line no-shadow
    async function main(argv, { env, io, bundleSource, spawn }) {
    const vat1 = env.VAT1 || 'vat-target.js';
    const transcript = env.TRANSCRIPT || 'transcript.txt';
    const workerBin = env.WORKERBIN || './build/bin/lin/release/xs-vat-worker';
    const { vat1, transcript, workerBin } = options(env);

    const bundle = await bundleSource(vat1);

  12. dckc revised this gist Jul 29, 2020. 1 changed file with 5 additions and 1 deletion.
    6 changes: 5 additions & 1 deletion index.js
    Original file line number Diff line number Diff line change
    @@ -8,9 +8,13 @@ function makePipe(readStream, writeStream, io, sleep) {
    readStream.resume();

    function write(data) {
    let done = 0;
    for (;;) {
    try {
    return io.writeSync(writeStream.fd, data);
    done += io.writeSync(writeStream.fd, data.slice(done));
    if (done >= data.length) {
    return done;
    }
    } catch (writeFailed) {
    if (writeFailed.code === 'EAGAIN') {
    sleep(0.1);
  13. dckc revised this gist Jul 27, 2020. 2 changed files with 43 additions and 26 deletions.
    65 changes: 40 additions & 25 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -7,11 +7,26 @@ import { main as vatWorker } from './vatWorker';
    function makePipe(readStream, writeStream, io, sleep) {
    readStream.resume();

    function write(data) {
    for (;;) {
    try {
    return io.writeSync(writeStream.fd, data);
    } catch (writeFailed) {
    if (writeFailed.code === 'EAGAIN') {
    sleep(0.1);
    // try again
    } else {
    throw writeFailed;
    }
    }
    }
    }

    return harden({
    writeMessage(msg) {
    io.writeSync(writeStream.fd, `${msg.length}:`);
    io.writeSync(writeStream.fd, msg);
    io.writeSync(writeStream.fd, ',');
    write(`${msg.length}:`);
    write(msg);
    write(',');
    },
    readMessage(EOF) {
    let buf = Buffer.from('999999999:', 'utf-8');
    @@ -26,28 +41,6 @@ function makePipe(readStream, writeStream, io, sleep) {
    offset,
    length: buf.length - offset,
    });
    if (len === null) {
    colonPos = buf.indexOf(':');
    if (colonPos > 0) {
    const digits = buf.slice(0, colonPos).toString('utf-8');
    len = parseInt(digits, 10);
    const rest = Buffer.alloc(len + 1);
    // console.error('parsed len. copy', { digits, len, targetStart: 0, sourceStart: colonPos + 1, sourceEnd: offset });
    buf.copy(rest, 0, colonPos + 1, offset);
    buf = rest;
    offset -= colonPos + 1;
    }
    } else if (offset === len + 1) {
    const delim = buf.slice(-1).toString('utf-8');
    if (delim !== ',') {
    throw new Error(
    `bad netstring: length ${len} expected , found [${delim}]`,
    );
    }
    const result = buf.slice(0, -1).toString('utf-8');
    // console.error({ colon: colonPos, len, result: result.slice(0, 20) });
    return result;
    }
    } catch (err) {
    if (err.code === 'EAGAIN') {
    sleep(0.1);
    @@ -59,6 +52,28 @@ function makePipe(readStream, writeStream, io, sleep) {
    throw err;
    }
    }
    if (len === null) {
    colonPos = buf.indexOf(':');
    if (colonPos > 0) {
    const digits = buf.slice(0, colonPos).toString('utf-8');
    len = parseInt(digits, 10);
    const rest = Buffer.alloc(len + 1);
    // console.error('parsed len. copy', { digits, len, targetStart: 0, sourceStart: colonPos + 1, sourceEnd: offset });
    buf.copy(rest, 0, colonPos + 1, offset);
    buf = rest;
    offset -= colonPos + 1;
    }
    } else if (offset === len + 1) {
    const delim = buf.slice(-1).toString('utf-8');
    if (delim !== ',') {
    throw new Error(
    `bad netstring: length ${len} expected , found [${delim}]`,
    );
    }
    const result = buf.slice(0, -1).toString('utf-8');
    // console.error({ colon: colonPos, len, result: result.slice(0, 20) });
    return result;
    }
    }
    },
    });
    4 changes: 3 additions & 1 deletion kernelSimulator.js
    Original file line number Diff line number Diff line change
    @@ -47,7 +47,9 @@ function makeWorker(child) {
    data += await nextChunk();
    }
    if (data.slice(-1) !== ',') {
    throw new Error(`bad netstring: expected , got ${data.slice(-1)}`);
    throw new Error(
    `bad netstring: expected , got ${data.slice(-1)} [${data.slice(-20)}]`,
    );
    }
    return data.slice(0, -1);
    };
  14. dckc revised this gist Jul 27, 2020. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions Makefile
    Original file line number Diff line number Diff line change
    @@ -10,6 +10,9 @@ ZOE1=../zoe/test/swingsetTests/zoe


    zoe: transcript-zoe.txt ./node-vat-worker index.js vatWorker.js kernelSimulator.js
    VAT1=$(ZOE1)/vat-zoe.js TRANSCRIPT=transcript-zoe.txt node -r esm kernelSimulator.js

    zoe-node: transcript-zoe.txt ./node-vat-worker index.js vatWorker.js kernelSimulator.js
    VAT1=$(ZOE1)/vat-zoe.js TRANSCRIPT=transcript-zoe.txt WORKERBIN=./node-vat-worker node -r esm kernelSimulator.js


  15. dckc revised this gist Jul 27, 2020. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions kernelSimulator.js
    Original file line number Diff line number Diff line change
    @@ -7,7 +7,7 @@ function makeWorker(child) {
    const send = obj => {
    const content = format(obj);
    // ISSUE: non-ASCII length
    console.log('kernelSimulator send size', content.length);
    // console.log('kernelSimulator send size', content.length);
    child.stdin.write(`${content.length}:`);
    child.stdin.write(content);
    child.stdin.write(',');
    @@ -40,7 +40,7 @@ function makeWorker(child) {
    } catch (badLen) {
    throw new Error(`bad netstring length ${digits}`);
    }
    console.error('readNetstring parsed len', { digits, len });
    // console.error('readNetstring parsed len', { digits, len });
    let data = prefix.slice(colonPos + 1);
    while (data.length <= len) {
    // eslint-disable-next-line no-await-in-loop
    @@ -62,7 +62,7 @@ function makeWorker(child) {
    throw badJSON;
    }
    if (msg.msgtype !== msgtype) {
    throw new Error(msg.msgtype);
    throw new Error(`${msg.msgtype}: ${msg.error}`);
    }
    return msg;
    };
  16. dckc revised this gist Jul 27, 2020. 1 changed file with 5 additions and 0 deletions.
    5 changes: 5 additions & 0 deletions kernelSimulator.js
    Original file line number Diff line number Diff line change
    @@ -121,6 +121,11 @@ async function main(argv, { env, io, bundleSource, spawn }) {
    const child = await spawn(workerBin, [], {
    stdio: ['pipe', 'pipe', 'inherit'],
    });
    child.on('exit', (code, signal) => {
    if (code !== 0) {
    console.error('unexpected exit:', { code, signal });
    }
    });
    const w1 = makeWorker(child);
    await w1.loadVat(bundle);
    console.log('loadVat done.');
  17. dckc revised this gist Jul 27, 2020. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion package.json
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    {
    "name": "@agoric/vat-worker",
    "name": "@agoric/xs-vat-worker",
    "version": "0.0.1",
    "description": "???",
    "main": "dist/vat-worker.cjs.js",
    @@ -11,6 +11,7 @@
    "xs-build-release": "mkdir -p build; mcconfig -o build -p x-cli-lin -m",
    "xs-run-release": "yarn xs-build-release && ./build/bin/lin/release/make-vat-transcript",
    "test": "tape -r esm 'test/**/*.js'",
    "build": "exit 0",
    "lint-fix": "eslint --fix '**/*.{js,jsx}'",
    "lint-check": "eslint '**/*.{js,jsx}'",
    "lint-fix-jessie": "eslint -c '.eslintrc-jessie.js' --fix '**/*.{js,jsx}'",
  18. dckc revised this gist Jul 27, 2020. 2 changed files with 2 additions and 0 deletions.
    1 change: 1 addition & 0 deletions manifest.json
    Original file line number Diff line number Diff line change
    @@ -26,6 +26,7 @@
    "@agoric/nat": "xs_modules/@agoric/nat/nat.esm",
    "@agoric/install-ses": "xs_modules/@agoric/install-ses/install-ses",
    "@agoric/import-bundle": "xs_modules/@agoric/import-bundle/index",
    "@agoric/compartment-wrapper": "xs_modules/@agoric/import-bundle/compartment-wrapper",
    "@agoric/eventual-send": [
    "xs_modules/@agoric/eventual-send/index"
    ],
    1 change: 1 addition & 0 deletions xs_modules/@agoric/import-bundle/compartment-wrapper.js
  19. dckc revised this gist Jul 27, 2020. 6 changed files with 135 additions and 80 deletions.
    74 changes: 42 additions & 32 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -4,33 +4,50 @@ import '@agoric/install-ses';

    import { main as vatWorker } from './vatWorker';

    function makeLineReader(readStream, io, sleep) {
    let buf = '';

    function checkForLine() {
    const eol = buf.indexOf('\n');
    // console.log({ closed, buf, eol, waiting });
    if (eol >= 0) {
    const line = buf.slice(0, eol);
    buf = buf.slice(eol + 1);
    return line;
    }
    return null;
    }

    function makePipe(readStream, writeStream, io, sleep) {
    readStream.resume();

    return harden({
    readLine(EOF) {
    writeMessage(msg) {
    io.writeSync(writeStream.fd, `${msg.length}:`);
    io.writeSync(writeStream.fd, msg);
    io.writeSync(writeStream.fd, ',');
    },
    readMessage(EOF) {
    let buf = Buffer.from('999999999:', 'utf-8');
    let len = null;
    let colonPos = null;
    let offset = 0;

    for (;;) {
    const line = checkForLine();
    if (line !== null) {
    return line;
    }
    const readBuf = Buffer.alloc(1024);
    let qty;
    // console.error('readMessage', { length: buf.length, len, colonPos, offset });
    try {
    qty = io.readSync(readStream.fd, readBuf);
    offset += io.readSync(readStream.fd, buf, {
    offset,
    length: buf.length - offset,
    });
    if (len === null) {
    colonPos = buf.indexOf(':');
    if (colonPos > 0) {
    const digits = buf.slice(0, colonPos).toString('utf-8');
    len = parseInt(digits, 10);
    const rest = Buffer.alloc(len + 1);
    // console.error('parsed len. copy', { digits, len, targetStart: 0, sourceStart: colonPos + 1, sourceEnd: offset });
    buf.copy(rest, 0, colonPos + 1, offset);
    buf = rest;
    offset -= colonPos + 1;
    }
    } else if (offset === len + 1) {
    const delim = buf.slice(-1).toString('utf-8');
    if (delim !== ',') {
    throw new Error(
    `bad netstring: length ${len} expected , found [${delim}]`,
    );
    }
    const result = buf.slice(0, -1).toString('utf-8');
    // console.error({ colon: colonPos, len, result: result.slice(0, 20) });
    return result;
    }
    } catch (err) {
    if (err.code === 'EAGAIN') {
    sleep(0.1);
    @@ -42,24 +59,17 @@ function makeLineReader(readStream, io, sleep) {
    throw err;
    }
    }
    buf += readBuf.slice(0, qty).toString('utf-8');
    }
    },
    });
    }

    async function main({ stdin, stdout, setImmediate, fs, spawnSync }) {
    const sleep = secs => spawnSync('sleep', [secs]);
    const pipe = makePipe(stdin, stdout, fs, sleep);
    return vatWorker({
    readLine: () => {
    const line = makeLineReader(stdin, fs, sleep).readLine();
    // console.error('readLine:', line.length, line.slice(0, 16));
    return line;
    },
    writeLine: line => {
    fs.writeSync(stdout.fd, line);
    // console.error('writeLine done:', line.length, line.slice(0, 16));
    },
    readMessage: pipe.readMessage,
    writeMessage: pipe.writeMessage,
    setImmediate,
    });
    }
    70 changes: 54 additions & 16 deletions kernelSimulator.js
    Original file line number Diff line number Diff line change
    @@ -3,31 +3,69 @@
    import '@agoric/install-ses';

    function makeWorker(child) {
    const format = obj => `${JSON.stringify(obj)}\n`;
    const send = obj => child.stdin.write(format(obj));
    const format = obj => JSON.stringify(obj);
    const send = obj => {
    const content = format(obj);
    // ISSUE: non-ASCII length
    console.log('kernelSimulator send size', content.length);
    child.stdin.write(`${content.length}:`);
    child.stdin.write(content);
    child.stdin.write(',');
    };

    const expect = msgtype =>
    new Promise((resolve, reject) => {
    child.stdout.pause();
    const nextChunk = () =>
    new Promise((resolve, _reject) => {
    const rx = data => {
    child.stdout.pause();
    child.stdout.removeListener('data', rx);
    try {
    const txt = data.toString('utf-8');
    const msg = JSON.parse(txt);
    if (msg.msgtype === msgtype) {
    resolve(msg);
    } else {
    reject(msg);
    }
    } catch (err) {
    reject(err);
    }
    resolve(data.toString('utf-8'));
    };
    child.stdout.on('data', rx);
    child.stdout.resume();
    });

    child.stdout.pause();
    const readNetstring = async () => {
    let prefix = '';
    let colonPos = -1;
    while (colonPos < 0) {
    // eslint-disable-next-line no-await-in-loop
    prefix += await nextChunk();
    colonPos = prefix.indexOf(':');
    }
    let len;
    const digits = prefix.slice(0, -1);
    try {
    len = parseInt(digits, 10);
    } catch (badLen) {
    throw new Error(`bad netstring length ${digits}`);
    }
    console.error('readNetstring parsed len', { digits, len });
    let data = prefix.slice(colonPos + 1);
    while (data.length <= len) {
    // eslint-disable-next-line no-await-in-loop
    data += await nextChunk();
    }
    if (data.slice(-1) !== ',') {
    throw new Error(`bad netstring: expected , got ${data.slice(-1)}`);
    }
    return data.slice(0, -1);
    };

    const expect = async msgtype => {
    const txt = await readNetstring();
    let msg;
    try {
    msg = JSON.parse(txt);
    } catch (badJSON) {
    console.error('bad JSON ', txt.length, ' chars: [', txt, ']', badJSON);
    throw badJSON;
    }
    if (msg.msgtype !== msgtype) {
    throw new Error(msg.msgtype);
    }
    return msg;
    };

    return harden({
    async loadVat(bundle) {
    9 changes: 5 additions & 4 deletions main.js
    Original file line number Diff line number Diff line change
    @@ -13,10 +13,11 @@ export default async function main() {
    const stdout = new Stdout();

    return vatWorker({
    readLine: () => stdin.getline(),
    writeLine: line => {
    stdout.write(line);
    },
    setImmediate,
    readMessage: () => stdin.read_netstring(),
    writeMessage: message => {
    // ISSUE: should be byte length
    stdout.write(`${message.length}:`, message, ',');
    },
    });
    }
    36 changes: 20 additions & 16 deletions stdio.c
    Original file line number Diff line number Diff line change
    @@ -15,22 +15,26 @@ void xs_Stdout(xsMachine *the) {
    // modInstrumentationAdjust(Files, +1);
    }

    void xs_stdin_getline(xsMachine *the) {
    // "If *lineptr is set to NULL and *n is set 0 before the call,
    // then getline() will allocate a buffer for storing the line." -- getline(3)
    char *lineptr = NULL;
    size_t n = 0;

    size_t nread = getline(&lineptr, &n, stdin);
    if (nread == -1) {
    xsUnknownError("getline from stdin failed");
    // "This buffer should be freed by the user program even if getline() failed." -- ibid.
    free(lineptr);
    return;
    }

    xsResult = xsStringBuffer(lineptr, nread);
    free(lineptr);
    void xs_stdin_read_netstring(xsMachine *the) {
    size_t len;
    char* buf = NULL;

    if (scanf("%9lu", &len) < 1) { goto BARF; } /* >999999999 bytes is bad */
    // fprintf(stderr, "xs_stdin_read_netstring len %lu\n", len);
    if (getchar() != ':') { goto BARF; }
    buf = malloc(len + 1); /* malloc(0) is not portable */
    if (!buf) { goto BARF; }
    if (fread(buf, 1, len, stdin) < len) { goto BARF; }
    if (getchar() != ',') { goto BARF; }

    xsResult = xsStringBuffer(buf, len);
    free(buf);
    // fprintf(stderr, "xs_stdin_read_nestring return\n");
    return;

    BARF:
    free(buf);
    xsUnknownError("getline from stdin failed");
    }

    void xs_stdio_destructor() {
    2 changes: 1 addition & 1 deletion stdio.js
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    export class Stdin @ "xs_stdio_destructor" {
    constructor() @ "xs_Stdin"
    getline() @ "xs_stdin_getline";
    read_netstring() @ "xs_stdin_read_netstring";
    }

    export class Stdout @ "xs_stdio_destructor" {
    24 changes: 13 additions & 11 deletions vatWorker.js
    Original file line number Diff line number Diff line change
    @@ -46,10 +46,10 @@ function makeWorker(io, setImmediate) {
    let dispatch;
    let state;

    const format = msg => `${JSON.stringify(msg)}\n`;
    const format = msg => JSON.stringify(msg);
    const sync = (method, args) => {
    io.writeLine(format({ msgtype: 'syscall', method, args }));
    return JSON.parse(io.readLine());
    io.writeMessage(format({ msgtype: 'syscall', method, args }));
    return JSON.parse(io.readMessage());
    };
    const syscall = harden({
    subscribe(...args) {
    @@ -106,27 +106,29 @@ function makeWorker(io, setImmediate) {
    await loadBundle(name, message.bundle);
    } catch (error) {
    // console.log('load-bundle failed:', error);
    io.writeLine(format({ msgtype: 'load-bundle-nak', error }));
    io.writeMessage(
    format({ msgtype: 'load-bundle-nak', error: error.message }),
    );
    break;
    }
    io.writeLine(format({ msgtype: 'load-bundle-ack' }));
    io.writeMessage(format({ msgtype: 'load-bundle-ack' }));
    break;
    case 'dispatch':
    try {
    await turnCrank(message.type, message.args);
    } catch (error) {
    io.writeLine(
    io.writeMessage(
    format({
    msgtype: 'dispatch-nak',
    error: error instanceof Error ? error.message : error,
    }),
    );
    break;
    }
    io.writeLine(format({ msgtype: 'dispatch-ack' }));
    io.writeMessage(format({ msgtype: 'dispatch-ack' }));
    break;
    case 'finish':
    io.writeLine(format({ msgtype: 'finish-ack' }));
    io.writeMessage(format({ msgtype: 'finish-ack' }));
    break;
    default:
    console.warn('unexpected msgtype', message.msgtype);
    @@ -137,14 +139,14 @@ function makeWorker(io, setImmediate) {
    return harden({ handle });
    }

    export async function main({ readLine, writeLine, setImmediate }) {
    const worker = makeWorker({ readLine, writeLine }, setImmediate);
    export async function main({ readMessage, writeMessage, setImmediate }) {
    const worker = makeWorker({ readMessage, writeMessage }, setImmediate);

    for (;;) {
    let message;
    try {
    // eslint-disable-next-line no-await-in-loop
    message = JSON.parse(readLine(EOF));
    message = JSON.parse(readMessage(EOF));
    } catch (noMessage) {
    if (noMessage === EOF) {
    return;
  20. dckc revised this gist Jul 27, 2020. 1 changed file with 42 additions and 31 deletions.
    73 changes: 42 additions & 31 deletions transcript-zoe.txt
    42 additions, 31 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
  21. dckc revised this gist Jul 24, 2020. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion Makefile
    Original file line number Diff line number Diff line change
    @@ -9,7 +9,9 @@ swingset-kernel-state: bootstrap.js vat-target.js
    ZOE1=../zoe/test/swingsetTests/zoe


    zoe: transcript-zoe.txt
    zoe: transcript-zoe.txt ./node-vat-worker index.js vatWorker.js kernelSimulator.js
    VAT1=$(ZOE1)/vat-zoe.js TRANSCRIPT=transcript-zoe.txt WORKERBIN=./node-vat-worker node -r esm kernelSimulator.js


    transcript-zoe.txt: $(ZOE1)/swingset-kernel-state
    $(NODE_ESM) ../swingset-runner/bin/kerneldump --filedb $(ZOE1)/swingset-kernel-state | grep ^v5.t | grep -v nextID >,zoe-all
  22. dckc revised this gist Jul 24, 2020. 1 changed file with 31 additions and 0 deletions.
    31 changes: 31 additions & 0 deletions transcript-zoe.txt
    31 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
  23. dckc revised this gist Jul 24, 2020. 3 changed files with 42 additions and 0 deletions.
    17 changes: 17 additions & 0 deletions Makefile
    Original file line number Diff line number Diff line change
    @@ -5,3 +5,20 @@ transcript.txt: swingset-kernel-state

    swingset-kernel-state: bootstrap.js vat-target.js
    $(NODE_ESM) ../swingset-runner/bin/runner --filedb run

    ZOE1=../zoe/test/swingsetTests/zoe


    zoe: transcript-zoe.txt

    transcript-zoe.txt: $(ZOE1)/swingset-kernel-state
    $(NODE_ESM) ../swingset-runner/bin/kerneldump --filedb $(ZOE1)/swingset-kernel-state | grep ^v5.t | grep -v nextID >,zoe-all
    python sort_transcript.py <,zoe-all >$@


    $(ZOE1)/swingset-kernel-state: ,zoe-patched
    (cd $(ZOE1) && $(NODE_ESM) ../../../../swingset-runner/bin/runner --filedb run)

    ,zoe-patched: $(ZOE1)/bootstrap.js zoe-test.patch
    cd ../.. && patch -p1 <packages/xs-vat-worker/zoe-test.patch
    touch $@
    5 changes: 5 additions & 0 deletions sort_transcript.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,5 @@
    import sys
    lines = sys.stdin.readlines()
    lines.sort(key=lambda line: int(line.split(" ", 1)[0].split(".")[2]))
    for line in lines:
    sys.stdout.write(line)
    20 changes: 20 additions & 0 deletions zoe-test.patch
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,20 @@
    diff --git a/packages/zoe/test/swingsetTests/zoe/bootstrap.js b/packages/zoe/test/swingsetTests/zoe/bootstrap.js
    index 4d2939a9..3b57eb36 100644
    --- a/packages/zoe/test/swingsetTests/zoe/bootstrap.js
    +++ b/packages/zoe/test/swingsetTests/zoe/bootstrap.js
    @@ -103,7 +103,14 @@ function build(E, log) {
    mintAndSellNFT: await E(zoe).install(mintAndSellNFTBundle.bundle),
    };

    - const [testName, startingExtents] = argv;
    + const argv2 = [
    + 'automaticRefundOk',
    + [
    + [3, 0, 0],
    + [0, 17, 0],
    + ],
    + ];
    + const [testName, startingExtents] = argv2;

    const { aliceP, bobP, carolP, daveP } = makeVats(
    E,
  24. dckc revised this gist Jul 24, 2020. 2 changed files with 7 additions and 16 deletions.
    7 changes: 7 additions & 0 deletions Makefile
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    NODE_ESM=node -r esm

    transcript.txt: swingset-kernel-state
    $(NODE_ESM) ../swingset-runner/bin/kerneldump --filedb ./swingset-kernel-state | grep v1.t |sort >$@

    swingset-kernel-state: bootstrap.js vat-target.js
    $(NODE_ESM) ../swingset-runner/bin/runner --filedb run
    16 changes: 0 additions & 16 deletions run.sh
    Original file line number Diff line number Diff line change
    @@ -1,16 +0,0 @@

    # prepare:
    # git clone https://github.com/Agoric/agoric-sdk
    # cd agoric-sdk
    # yarn
    # git clone (this gist)
    # cd make-vat-transcript

    ../packages/swingset-runner/bin/runner --filedb run
    # if that complains about 'env' not recognizing the -S option, use this:
    # node -r esm ../packages/swingset-runner/bin/runner --filedb run

    ../packages/swingset-runner/bin/kerneldump --filedb ./swingset-kernel-state | grep v1.t |sort
    # ditto

    # that last command emits the transcript for vat-target
  25. dckc revised this gist Jul 24, 2020. 2 changed files with 16 additions and 3 deletions.
    17 changes: 14 additions & 3 deletions index.js
    100644 → 100755
    Original file line number Diff line number Diff line change
    @@ -30,10 +30,12 @@ function makeLineReader(readStream, io, sleep) {
    const readBuf = Buffer.alloc(1024);
    let qty;
    try {
    io.readSync(readStream.fd, readBuf);
    qty = io.readSync(readStream.fd, readBuf);
    } catch (err) {
    if (err.code === 'EAGAIN') {
    sleep(0.1);
    // eslint-disable-next-line no-continue
    continue;
    } else if (err.code === 'EOF') {
    throw EOF;
    } else {
    @@ -49,9 +51,14 @@ function makeLineReader(readStream, io, sleep) {
    async function main({ stdin, stdout, setImmediate, fs, spawnSync }) {
    const sleep = secs => spawnSync('sleep', [secs]);
    return vatWorker({
    readLine: makeLineReader(stdin, fs, sleep).readLine,
    readLine: () => {
    const line = makeLineReader(stdin, fs, sleep).readLine();
    // console.error('readLine:', line.length, line.slice(0, 16));
    return line;
    },
    writeLine: line => {
    fs.writeSync(stdout.fd, line);
    // console.error('writeLine done:', line.length, line.slice(0, 16));
    },
    setImmediate,
    });
    @@ -69,4 +76,8 @@ main({
    // eslint-disable-next-line global-require
    writeSync: require('fs').writeSync,
    },
    });
    })
    .catch(err => {
    console.error(err);
    })
    .then(() => process.exit(0));
    2 changes: 2 additions & 0 deletions node-vat-worker
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,2 @@
    #!/bin/sh
    node -r esm index.js
  26. dckc revised this gist Jul 24, 2020. 2 changed files with 15 additions and 2 deletions.
    8 changes: 7 additions & 1 deletion kernelSimulator.js
    Original file line number Diff line number Diff line change
    @@ -45,6 +45,10 @@ function makeWorker(child) {
    }
    return expect('dispatch-ack');
    },
    async finish() {
    await send({ msgtype: 'finish' });
    await expect('finish-ack');
    },
    });
    }

    @@ -84,7 +88,7 @@ async function main(argv, { env, io, bundleSource, spawn }) {
    console.log('loadVat done.');

    const events = (await io.readFile(transcript, 'utf-8')).split('\n');
    eachTranscriptEvent(events, async (id, obj) => {
    await eachTranscriptEvent(events, async (id, obj) => {
    console.log('dispatching:', id);
    if (typeof obj !== 'object') {
    console.log('not a dispatch event', id, obj);
    @@ -93,6 +97,8 @@ async function main(argv, { env, io, bundleSource, spawn }) {
    await w1.dispatch(obj);
    console.log('dispatch done:', id);
    });
    await w1.finish();
    console.log('END OF TRANSCRIPT.');
    }

    main(process.argv, {
    9 changes: 8 additions & 1 deletion vatWorker.js
    Original file line number Diff line number Diff line change
    @@ -125,9 +125,13 @@ function makeWorker(io, setImmediate) {
    }
    io.writeLine(format({ msgtype: 'dispatch-ack' }));
    break;
    case 'finish':
    io.writeLine(format({ msgtype: 'finish-ack' }));
    break;
    default:
    console.warn('unexpected msgtype', message.msgtype);
    }
    return message.msgtype;
    }

    return harden({ handle });
    @@ -150,6 +154,9 @@ export async function main({ readLine, writeLine, setImmediate }) {
    continue;
    }
    // eslint-disable-next-line no-await-in-loop
    await worker.handle(message);
    const msgtype = await worker.handle(message);
    if (msgtype === 'finish') {
    break;
    }
    }
    }
  27. dckc revised this gist Jul 24, 2020. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion kernelSimulator.js
    Original file line number Diff line number Diff line change
    @@ -38,7 +38,8 @@ function makeWorker(child) {
    await send({ msgtype: 'dispatch', type: d[0], args: d.slice(1) });
    for (const syscall of syscalls) {
    // eslint-disable-next-line no-await-in-loop
    await expect('syscall');
    const request = await expect('syscall');
    console.log('syscall request', request);
    // eslint-disable-next-line no-await-in-loop
    await send({ msgtype: 'syscall-ack', response: syscall.response });
    }
  28. dckc revised this gist Jul 24, 2020. 1 changed file with 30 additions and 33 deletions.
    63 changes: 30 additions & 33 deletions vatWorker.js
    Original file line number Diff line number Diff line change
    @@ -98,42 +98,39 @@ function makeWorker(io, setImmediate) {
    });
    }

    return harden({ loadBundle, dispatch: turnCrank });
    }

    async function handle(worker, message, io) {
    const name = 'WORKER'; // TODO?

    const format = msg => `${JSON.stringify(msg)}\n`;

    switch (message.msgtype) {
    case 'load-bundle':
    try {
    await worker.loadBundle(name, message.bundle);
    } catch (error) {
    console.log('load-bundle failed:', error);
    io.writeLine(format({ msgtype: 'load-bundle-nak', error }));
    async function handle(message) {
    switch (message.msgtype) {
    case 'load-bundle':
    try {
    await loadBundle(name, message.bundle);
    } catch (error) {
    // console.log('load-bundle failed:', error);
    io.writeLine(format({ msgtype: 'load-bundle-nak', error }));
    break;
    }
    io.writeLine(format({ msgtype: 'load-bundle-ack' }));
    break;
    }
    io.writeLine(format({ msgtype: 'load-bundle-ack' }));
    break;
    case 'dispatch':
    try {
    await worker.dispatch(message.type, message.args);
    } catch (error) {
    io.writeLine(
    format({
    msgtype: 'dispatch-nak',
    error: error instanceof Error ? error.message : error,
    }),
    );
    case 'dispatch':
    try {
    await turnCrank(message.type, message.args);
    } catch (error) {
    io.writeLine(
    format({
    msgtype: 'dispatch-nak',
    error: error instanceof Error ? error.message : error,
    }),
    );
    break;
    }
    io.writeLine(format({ msgtype: 'dispatch-ack' }));
    break;
    }
    io.writeLine(format({ msgtype: 'dispatch-ack' }));
    break;
    default:
    console.warn('unexpected msgtype', message.msgtype);
    default:
    console.warn('unexpected msgtype', message.msgtype);
    }
    }

    return harden({ handle });
    }

    export async function main({ readLine, writeLine, setImmediate }) {
    @@ -153,6 +150,6 @@ export async function main({ readLine, writeLine, setImmediate }) {
    continue;
    }
    // eslint-disable-next-line no-await-in-loop
    await handle(worker, message, { readLine, writeLine });
    await worker.handle(message);
    }
    }
  29. dckc revised this gist Jul 23, 2020. 3 changed files with 22 additions and 37 deletions.
    39 changes: 3 additions & 36 deletions main.js
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,5 @@
    // eslint-disable-next-line import/no-unresolved
    import { File } from 'files'; // moddable SDK

    // add harden; align xs's Compartment with Agoric's
    // ISSUE: use a different module name?
    import '@agoric/install-ses';

    import './console'; // sets globalThis.console. ew.
    @@ -9,44 +8,12 @@ import './timer-ticks'; // globalThis.setTimeout. ew.
    import { main as vatWorker } from './vatWorker';
    import { Stdin, Stdout } from './stdio';

    function makeLineReader(infile) {
    let buf = '';

    const checkline = () => {
    const eol = buf.indexOf('\n');
    if (eol >= 0) {
    const line = buf.slice(0, eol);
    buf = buf.slice(eol + 1);
    return line;
    }
    return null;
    };

    return harden({
    readLine(EOF) {
    for (;;) {
    const line = checkline();
    if (line !== null) {
    return line;
    }
    let more;
    try {
    more = infile.read(String);
    } catch (readFailure) {
    throw EOF;
    }
    buf += more;
    }
    },
    });
    }

    export default async function main() {
    const stdin = new Stdin();
    const stdout = new Stdout();

    return vatWorker({
    readLine: makeLineReader(stdin).readLine,
    readLine: () => stdin.getline(),
    writeLine: line => {
    stdout.write(line);
    },
    18 changes: 18 additions & 0 deletions stdio.c
    Original file line number Diff line number Diff line change
    @@ -15,6 +15,24 @@ void xs_Stdout(xsMachine *the) {
    // modInstrumentationAdjust(Files, +1);
    }

    void xs_stdin_getline(xsMachine *the) {
    // "If *lineptr is set to NULL and *n is set 0 before the call,
    // then getline() will allocate a buffer for storing the line." -- getline(3)
    char *lineptr = NULL;
    size_t n = 0;

    size_t nread = getline(&lineptr, &n, stdin);
    if (nread == -1) {
    xsUnknownError("getline from stdin failed");
    // "This buffer should be freed by the user program even if getline() failed." -- ibid.
    free(lineptr);
    return;
    }

    xsResult = xsStringBuffer(lineptr, nread);
    free(lineptr);
    }

    void xs_stdio_destructor() {

    }
    2 changes: 1 addition & 1 deletion stdio.js
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    export class Stdin @ "xs_stdio_destructor" {
    constructor() @ "xs_Stdin"
    read(type, count) @ "xs_file_read";
    getline() @ "xs_stdin_getline";
    }

    export class Stdout @ "xs_stdio_destructor" {
  30. dckc revised this gist Jul 22, 2020. 1 changed file with 56 additions and 15 deletions.
    71 changes: 56 additions & 15 deletions kernelSimulator.js
    Original file line number Diff line number Diff line change
    @@ -2,25 +2,47 @@
    // context: https://github.com/Agoric/agoric-sdk/issues/1299
    import '@agoric/install-ses';

    const VAT1 = './vat-target.js';
    const TRANSCRIPT = 'transcript.txt';
    function makeWorker(child) {
    const format = obj => `${JSON.stringify(obj)}\n`;
    const send = obj => child.stdin.write(format(obj));

    function makeWorker({ stdout }) {
    const send = obj => stdout.write(`${JSON.stringify(obj)}\n`);
    const expect = msgtype =>
    new Promise((resolve, reject) => {
    const rx = data => {
    child.stdout.pause();
    child.stdout.removeListener('data', rx);
    try {
    const txt = data.toString('utf-8');
    const msg = JSON.parse(txt);
    if (msg.msgtype === msgtype) {
    resolve(msg);
    } else {
    reject(msg);
    }
    } catch (err) {
    reject(err);
    }
    };
    child.stdout.on('data', rx);
    child.stdout.resume();
    });

    child.stdout.pause();

    return harden({
    async loadVat(bundle) {
    await send({ msgtype: 'load-bundle', bundle });
    // TODO: wait for ack
    return expect('load-bundle-ack');
    },
    async dispatch({ d, syscalls, _crankNumber }) {
    await send({ msgtype: 'dispatch', type: d[0], args: d.slice(1) });
    for (const syscall of syscalls) {
    // TODO: service syscalls
    // eslint-disable-next-line no-await-in-loop
    await expect('syscall');
    // eslint-disable-next-line no-await-in-loop
    await send({ msgtype: 'syscall-ack', response: syscall.response });
    }
    // TODO: wait for ack
    return expect('dispatch-ack');
    },
    });
    }
    @@ -45,25 +67,44 @@ async function eachTranscriptEvent(events, thunk) {
    }

    // eslint-disable-next-line no-shadow
    async function main({ stdout, io, bundleSource }) {
    const bundle = await bundleSource(VAT1);
    const w1 = makeWorker({ stdout });
    async function main(argv, { env, io, bundleSource, spawn }) {
    const vat1 = env.VAT1 || 'vat-target.js';
    const transcript = env.TRANSCRIPT || 'transcript.txt';
    const workerBin = env.WORKERBIN || './build/bin/lin/release/xs-vat-worker';

    const bundle = await bundleSource(vat1);

    console.log('spawning', { workerBin });
    const child = await spawn(workerBin, [], {
    stdio: ['pipe', 'pipe', 'inherit'],
    });
    const w1 = makeWorker(child);
    await w1.loadVat(bundle);
    console.log('loadVat done.');

    const events = io.readFileSync(TRANSCRIPT, 'utf-8').split('\n');
    const events = (await io.readFile(transcript, 'utf-8')).split('\n');
    eachTranscriptEvent(events, async (id, obj) => {
    console.log('dispatching:', id);
    if (typeof obj !== 'object') {
    // console.log('not a dispatch event', id, obj);
    console.log('not a dispatch event', id, obj);
    return;
    }
    await w1.dispatch(obj);
    console.log('dispatch done:', id);
    });
    }

    main({
    stdout: process.stdout,
    main(process.argv, {
    env: process.env,
    // eslint-disable-next-line global-require
    io: { readFileSync: require('fs').readFileSync },
    io: {
    // eslint-disable-next-line global-require
    readFile: require('fs').promises.readFile,
    },
    // eslint-disable-next-line global-require
    bundleSource: require('@agoric/bundle-source').default,
    // eslint-disable-next-line global-require
    spawn: require('child_process').spawn,
    }).catch(err => {
    console.error(err);
    });