evaluators.js: allow transform.rewrite() to change endowments
This reordering of the transforms phasing makes it possible for a
rewriter to affect the actual endowments.
MODIFY
src/evaluators.js
src/evaluators.js
@@ -101,6 +101,20 @@ export function createSafeEvaluatorFactory(
);
endowments = endowmentState.endowments;
+ // We use the the concise method syntax to create an eval without a
+ // [[Construct]] behavior (such that the invocation "new eval()" throws
+ // TypeError: eval is not a constructor"), but which still accepts a
+ // 'this' binding.
+ const safeEval = {
+ eval(src) {
+ src = `${src}`;
+ // Rewrite the source, threading through rewriter state as necessary.
+ const rewriterState = allTransforms.reduce(
+ (rs, transform) => (transform.rewrite ? transform.rewrite(rs) : rs),
+ { src, endowments }
+ );
+ src = rewriterState.src;
+
// todo (shim limitation): scan endowments, throw error if endowment
// overlaps with the const optimization (which would otherwise
// incorrectly shadow endowments), or if endowments includes 'eval'. Also
@@ -111,27 +125,13 @@ export function createSafeEvaluatorFactory(
// explain/spec
const scopeTarget = create(
safeGlobal,
- getOwnPropertyDescriptors(endowments)
+ getOwnPropertyDescriptors(rewriterState.endowments)
);
const scopeProxy = new Proxy(scopeTarget, scopeHandler);
const scopedEvaluator = apply(scopedEvaluatorFactory, safeGlobal, [
scopeProxy
]);
- // We use the the concise method syntax to create an eval without a
- // [[Construct]] behavior (such that the invocation "new eval()" throws
- // TypeError: eval is not a constructor"), but which still accepts a
- // 'this' binding.
- const safeEval = {
- eval(src) {
- src = `${src}`;
- // Rewrite the source, threading through rewriter state as necessary.
- const rewriterState = allTransforms.reduce(
- (rs, transform) => (transform.rewrite ? transform.rewrite(rs) : rs),
- { src }
- );
- src = rewriterState.src;
-
scopeHandler.allowUnsafeEvaluatorOnce();
let err;
try {

MODIFY
test/module/evaluators.js
test/module/evaluators.js
@@ -127,8 +127,7 @@ test('createSafeEvaluatorWhichTakesEndowments - options.sloppyGlobals', t => {
});
test('createSafeEvaluatorWhichTakesEndowments - options.transforms', t => {
- t.plan(4);
-
+ try {
// Mimic repairFunctions.
// eslint-disable-next-line no-proto
sinon.stub(Function.__proto__, 'constructor').callsFake(() => {
@@ -164,16 +163,50 @@ test('createSafeEvaluatorWhichTakesEndowments - options.transforms', t => {
]
};
- // The realmTransforms rewrite ABC.
- t.equal(safeEval('abc', {}), 123);
- t.equal(safeEval('ABC', { ABC: 234 }), 123);
- // The endowed abc is overridden by the realmTransforms.
- t.equal(safeEval('ABC', { ABC: 234, abc: 'notused' }), 123);
- // The specified options.transforms rewrite ABC first.
- t.equal(safeEval('ABC', { def: 789 }, options), 789);
+ t.equal(safeEval('abc', {}), 123, 'no rewrite');
+ t.equal(safeEval('ABC', { ABC: 234 }), 123, 'realmTransforms rewrite ABC');
+ t.equal(
+ safeEval('ABC', { ABC: 234, abc: 'notused' }),
+ 123,
+ 'endowed abc is overridden'
+ );
+ t.equal(
+ safeEval('ABC', { def: 789 }, options),
+ 789,
+ `options.transform rewrite ABC first`
+ );
+
+ const optionsEndow = abcVal => ({
+ transforms: [
+ {
+ rewrite(ss) {
+ ss.endowments.abc = abcVal;
+ return ss;
+ }
+ }
+ ]
+ });
+
+ // The endowed abc is overridden by realmTransforms, then optionsEndow.
+ t.equal(
+ safeEval('abc', { ABC: 234, abc: 'notused' }, optionsEndow(321)),
+ 321,
+ `optionsEndow replace endowment`
+ );
+
+ t.equal(
+ safeEval('ABC', { ABC: 234, abc: 'notused' }, optionsEndow(231)),
+ 231,
+ `optionsEndow replace rewritten endowment`
+ );
// eslint-disable-next-line no-proto
Function.__proto__.constructor.restore();
+ } catch (e) {
+ t.isNot(e, e, 'unexpected exception');
+ } finally {
+ t.end();
+ }
});
test('createSafeEvaluatorWhichTakesEndowments', t => {

evaluators.js: allow transform.rewrite() to change endowments

This reordering of the transforms phasing makes it possible for a
rewriter to affect the actual endowments.