Skip to content

Commit b1607f9

Browse files
authored
Merge pull request #1102 from Agoric/1101-bundle-tildot
add tildot transform to bundle-source closes #1101
2 parents 54f1b7e + 8a54d36 commit b1607f9

File tree

11 files changed

+183
-95
lines changed

11 files changed

+183
-95
lines changed

packages/bundle-source/.eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/dist/
22
/scripts/
3+
/demo/tildot/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function foo(carol, message) {
2+
carol~.bar(message);
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { foo } from './file2.js';
2+
3+
const arg2 = foo({}, 'inhibit treeshaking');
4+
export function blah(arg1) {
5+
let p = bob~.foo(arg1, arg2);
6+
return p;
7+
}

packages/bundle-source/package.json

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
},
1919
"dependencies": {
2020
"@agoric/acorn-eventual-send": "^2.0.4",
21+
"@agoric/babel-parser": "^7.6.4",
22+
"@agoric/transform-eventual-send": "^1.2.4",
23+
"@babel/generator": "^7.6.4",
2124
"@agoric/evaluate": "^2.2.5",
2225
"@agoric/harden": "^0.0.8",
2326
"@rollup/plugin-commonjs": "^11.0.2",

packages/bundle-source/src/index.js

+20-4
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import { rollup as rollup0 } from 'rollup';
22
import path from 'path';
33
import resolve0 from '@rollup/plugin-node-resolve';
44
import commonjs0 from '@rollup/plugin-commonjs';
5-
import eventualSend from '@agoric/acorn-eventual-send';
6-
import * as acorn from 'acorn';
5+
import * as babelParser from '@agoric/babel-parser';
6+
import babelGenerate from '@babel/generator';
7+
import { makeTransform } from '@agoric/transform-eventual-send';
78

89
import { SourceMapConsumer } from 'source-map';
910

@@ -15,6 +16,18 @@ const SUPPORTED_FORMATS = ['getExport', 'nestedEvaluate'];
1516
const IMPORT_RE = new RegExp('\\b(import)' + '(\\s*(?:\\(|/[/*]))', 'g');
1617
const HTML_COMMENT_RE = new RegExp(`(?:${'<'}!--|--${'>'})`, 'g');
1718

19+
export function tildotPlugin() {
20+
const transformer = makeTransform(babelParser, babelGenerate);
21+
return {
22+
transform(code, _id) {
23+
return {
24+
code: transformer(code),
25+
map: null,
26+
};
27+
},
28+
};
29+
}
30+
1831
export default async function bundleSource(
1932
startFilename,
2033
moduleFormat = DEFAULT_MODULE_FORMAT,
@@ -36,8 +49,11 @@ export default async function bundleSource(
3649
treeshake: false,
3750
preserveModules: moduleFormat === 'nestedEvaluate',
3851
external: ['@agoric/evaluate', '@agoric/harden', ...externals],
39-
plugins: [resolvePlugin({ preferBuiltins: true }), commonjsPlugin()],
40-
acornInjectPlugins: [eventualSend(acorn)],
52+
plugins: [
53+
resolvePlugin({ preferBuiltins: true }),
54+
tildotPlugin(),
55+
commonjsPlugin(),
56+
],
4157
});
4258
const { output } = await bundle.generate({
4359
exports: 'named',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { test } from 'tape-promise/tape';
2+
import bundleSource from '..';
3+
4+
test('tildot transform', async t => {
5+
try {
6+
const bundle = await bundleSource(
7+
`${__dirname}/../demo/tildot`,
8+
'getExport',
9+
);
10+
// console.log(bundle.source);
11+
t.equal(bundle.source.indexOf('~.'), -1);
12+
// this is overspecified (whitespace, choice of quotation marks), sorry
13+
t.notEqual(
14+
bundle.source.indexOf(
15+
`HandledPromise.applyMethod(bob, "foo", [arg1, arg2])`,
16+
),
17+
-1,
18+
);
19+
t.notEqual(
20+
bundle.source.indexOf(
21+
`HandledPromise.applyMethod(carol, "bar", [message])`,
22+
),
23+
-1,
24+
);
25+
} catch (e) {
26+
t.isNot(e, e, 'unexpected exception');
27+
} finally {
28+
t.end();
29+
}
30+
});

packages/default-evaluate-options/src/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { parse } from '@agoric/babel-parser';
22
import generate from '@babel/generator';
3-
import makeEventualSendTransformer from '@agoric/transform-eventual-send';
3+
import makeEventualSendTransformer from '@agoric/transform-eventual-send/src/rewriter';
44

55
export default function makeDefaultEvaluateOptions() {
66
const shims = [];
+16-89
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,17 @@
1-
import eventualSendBundle from './bundles/eventual-send';
2-
3-
function makeEventualSendTransformer(parser, generate) {
4-
let HandledPromise;
5-
let evaluateProgram;
6-
let myRequire;
7-
let recursive = false;
8-
const transform = {
9-
closeOverSES(s) {
10-
// FIXME: This will go away when we can bundle an @agoric/harden that understands SES.
11-
myRequire = name => {
12-
if (name === '@agoric/harden') {
13-
return s.global.SES.harden;
14-
}
15-
throw Error(`Unrecognized require ${name}`);
16-
};
17-
// FIXME: This should be replaced with ss.evaluateProgram support in SES.
18-
evaluateProgram = (src, endowments = {}) => s.evaluate(src, endowments);
19-
},
20-
rewrite(ss) {
21-
const source = ss.src;
22-
const endowments = ss.endowments || {};
23-
if (!recursive && !('HandledPromise' in endowments)) {
24-
// Use a getter to postpone initialization.
25-
Object.defineProperty(endowments, 'HandledPromise', {
26-
get() {
27-
if (!HandledPromise) {
28-
// Get a HandledPromise endowment for the evaluator.
29-
// It will be hardened in the evaluator's context.
30-
const nestedEvaluate = src =>
31-
(evaluateProgram || ss.evaluateProgram)(src, {
32-
require: myRequire || require,
33-
nestedEvaluate,
34-
});
35-
const {
36-
source: evSendSrc,
37-
moduleFormat,
38-
sourceMap,
39-
} = eventualSendBundle;
40-
if (
41-
moduleFormat === 'getExport' ||
42-
moduleFormat === 'nestedEvaluate'
43-
) {
44-
recursive = true;
45-
try {
46-
const ns = nestedEvaluate(`(${evSendSrc}\n${sourceMap})`)();
47-
HandledPromise = ns.HandledPromise;
48-
} finally {
49-
recursive = false;
50-
}
51-
} else {
52-
throw Error(`Unrecognized moduleFormat ${moduleFormat}`);
53-
}
54-
}
55-
return HandledPromise;
56-
},
57-
});
58-
}
59-
60-
// Parse with eventualSend enabled, rewriting to
61-
// HandledPromise.get/applyFunction/applyMethod(...)
62-
const parseFunc = parser.parse;
63-
const ast = (parseFunc || parser)(source, {
64-
plugins: ['eventualSend'],
65-
});
66-
// Create the source from the ast.
67-
const output = generate(ast, { retainLines: true }, source);
68-
69-
// Work around Babel appending semicolons.
70-
const maybeSource = output.code;
71-
const actualSource =
72-
ss.sourceType === 'expression' &&
73-
maybeSource.endsWith(';') &&
74-
!source.endsWith(';')
75-
? maybeSource.slice(0, -1)
76-
: maybeSource;
77-
78-
return {
79-
...ss,
80-
ast,
81-
endowments,
82-
src: actualSource,
83-
};
84-
},
85-
};
86-
87-
return [transform];
1+
export function makeTransform(parser, generate) {
2+
function transform(source) {
3+
// Parse with eventualSend enabled, rewriting to
4+
// HandledPromise.get/applyFunction/applyMethod(...). Whoever finally
5+
// evaluates this code must provide a HandledPromise object, probably as
6+
// an endowment.
7+
const parseFunc = parser.parse;
8+
const ast = (parseFunc || parser)(source, {
9+
sourceType: 'module',
10+
plugins: ['eventualSend'],
11+
});
12+
// Create the source from the ast.
13+
const output = generate(ast, { retainLines: true }, source);
14+
return output.code;
15+
}
16+
return transform;
8817
}
89-
90-
export default makeEventualSendTransformer;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { makeTransform } from './index';
2+
import eventualSendBundle from './bundles/eventual-send';
3+
4+
// this transformer is meant for the SES1 evaluation format, with mutable
5+
// endowments and stuff
6+
7+
function makeEventualSendTransformer(parser, generate) {
8+
const transformer = makeTransform(parser, generate);
9+
let HandledPromise;
10+
let evaluateProgram;
11+
let myRequire;
12+
let recursive = false;
13+
const transform = {
14+
closeOverSES(s) {
15+
// FIXME: This will go away when we can bundle an @agoric/harden that understands SES.
16+
myRequire = name => {
17+
if (name === '@agoric/harden') {
18+
return s.global.SES.harden;
19+
}
20+
throw Error(`Unrecognized require ${name}`);
21+
};
22+
// FIXME: This should be replaced with ss.evaluateProgram support in SES.
23+
evaluateProgram = (src, endowments = {}) => s.evaluate(src, endowments);
24+
},
25+
rewrite(ss) {
26+
const source = ss.src;
27+
const endowments = ss.endowments || {};
28+
if (!recursive && !('HandledPromise' in endowments)) {
29+
// Use a getter to postpone initialization.
30+
Object.defineProperty(endowments, 'HandledPromise', {
31+
get() {
32+
if (!HandledPromise) {
33+
// Get a HandledPromise endowment for the evaluator.
34+
// It will be hardened in the evaluator's context.
35+
const nestedEvaluate = src =>
36+
(evaluateProgram || ss.evaluateProgram)(src, {
37+
require: myRequire || require,
38+
nestedEvaluate,
39+
});
40+
const {
41+
source: evSendSrc,
42+
moduleFormat,
43+
sourceMap,
44+
} = eventualSendBundle;
45+
if (
46+
moduleFormat === 'getExport' ||
47+
moduleFormat === 'nestedEvaluate'
48+
) {
49+
recursive = true;
50+
try {
51+
const ns = nestedEvaluate(`(${evSendSrc}\n${sourceMap})`)();
52+
HandledPromise = ns.HandledPromise;
53+
} finally {
54+
recursive = false;
55+
}
56+
} else {
57+
throw Error(`Unrecognized moduleFormat ${moduleFormat}`);
58+
}
59+
}
60+
return HandledPromise;
61+
},
62+
});
63+
}
64+
65+
const maybeSource = transformer(source);
66+
67+
// Work around Babel appending semicolons.
68+
const actualSource =
69+
ss.sourceType === 'expression' &&
70+
maybeSource.endsWith(';') &&
71+
!source.endsWith(';')
72+
? maybeSource.slice(0, -1)
73+
: maybeSource;
74+
75+
return {
76+
...ss,
77+
endowments,
78+
src: actualSource,
79+
};
80+
},
81+
};
82+
83+
return [transform];
84+
}
85+
86+
export default makeEventualSendTransformer;

packages/transform-eventual-send/test/ses-test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import eventualSend from '@agoric/acorn-eventual-send';
1010
import * as acorn from 'acorn';
1111
import * as astring from 'astring';
1212

13-
import makeEventualSendTransformer from '../src';
13+
import makeEventualSendTransformer from '../src/rewriter';
1414

1515
// FIXME: This should be unnecessary when SES has support
1616
// for passing `evaluateProgram` through to the rewriter state.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { test } from 'tape';
2+
import * as babelParser from '@agoric/babel-parser';
3+
import babelGenerate from '@babel/generator';
4+
import { makeTransform } from '../src';
5+
6+
test('transformer', t => {
7+
const source = `let p = bob~.foo(arg1, arg2);`;
8+
const transformer = makeTransform(babelParser, babelGenerate);
9+
const output = transformer(source);
10+
t.equal(
11+
output,
12+
`let p = HandledPromise.applyMethod(bob, "foo", [arg1, arg2]);`,
13+
);
14+
t.end();
15+
});

0 commit comments

Comments
 (0)