feat: async
This commit is contained in:
parent
8e12cc6af4
commit
f736922f66
|
@ -24,94 +24,153 @@ export default class Sandbox {
|
|||
|
||||
evaluateArrayExpression(node) {
|
||||
const elements = [];
|
||||
let isAsync = false;
|
||||
for (let i = 0; i < node.elements.length; i++) {
|
||||
elements.push(this.evaluate(node.elements[i]));
|
||||
const {async, value} = this.evaluate(node.elements[i]);
|
||||
// eslint-disable-next-line no-bitwise
|
||||
isAsync |= async;
|
||||
elements.push(value);
|
||||
}
|
||||
return elements;
|
||||
return {
|
||||
async: !!isAsync,
|
||||
value: isAsync ? Promise.all(elements) : elements,
|
||||
};
|
||||
}
|
||||
|
||||
evaluateAssignmentExpression(node) {
|
||||
const {left, right} = node;
|
||||
const scope = this.nodeScope(node);
|
||||
const value = this.evaluate(right);
|
||||
if (types.isMemberExpression(left)) {
|
||||
const {
|
||||
computed,
|
||||
object,
|
||||
optional,
|
||||
property,
|
||||
} = left;
|
||||
const O = this.evaluate(object);
|
||||
const P = computed ? this.evaluate(property) : property.name;
|
||||
const {Vasync, value} = this.evaluate(right);
|
||||
if (!types.isMemberExpression(left)) {
|
||||
if (Vasync) {
|
||||
return {
|
||||
async: true,
|
||||
value: value.then((value) => scope.set(left.name, value)),
|
||||
};
|
||||
}
|
||||
scope.set(left.name, value);
|
||||
return {value};
|
||||
}
|
||||
/* eslint-disable no-param-reassign */
|
||||
const assign = (O, P, value, optional) => {
|
||||
// eslint-disable-next-line object-curly-newline
|
||||
if (optional) {
|
||||
if (O) {
|
||||
O[P] = value;
|
||||
}
|
||||
// eslint-disable-next-line babel/no-unused-expressions
|
||||
O && (O[P] = value);
|
||||
}
|
||||
else {
|
||||
O[P] = value;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
/* eslint-enable no-param-reassign */
|
||||
const makeAsync = (O, P, value, optional) => (
|
||||
Promise
|
||||
.all([O, P, value])
|
||||
.then(([O, P, value]) => assign(O, P, value, optional))
|
||||
);
|
||||
const {
|
||||
computed,
|
||||
object,
|
||||
optional,
|
||||
property,
|
||||
} = left;
|
||||
const O = this.evaluate(object);
|
||||
const P = computed ? this.evaluate(property) : {value: property.name};
|
||||
// eslint-disable-next-line no-bitwise
|
||||
if (Vasync | O.async | P.async) {
|
||||
return {
|
||||
async: true,
|
||||
value: makeAsync(O.value, P.value, value, optional),
|
||||
};
|
||||
}
|
||||
else {
|
||||
scope.set(left.name, value);
|
||||
}
|
||||
return value;
|
||||
return {value: assign(O.value, P.value, value, optional)};
|
||||
}
|
||||
|
||||
evaluateAwaitExpression({argument}) {
|
||||
return {
|
||||
async: true,
|
||||
value: this.evaluate(argument).value,
|
||||
};
|
||||
}
|
||||
|
||||
evaluateBinaryExpression(node) {
|
||||
const left = this.evaluate(node.left);
|
||||
const right = this.evaluate(node.right);
|
||||
switch (node.operator) {
|
||||
/* eslint-disable no-multi-spaces, switch-colon-spacing */
|
||||
case '+' : return left + right;
|
||||
case '-' : return left - right;
|
||||
case '/' : return left / right;
|
||||
case '%' : return left % right;
|
||||
case '*' : return left * right;
|
||||
case '>' : return left > right;
|
||||
case '<' : return left < right;
|
||||
case 'in' : return left in right;
|
||||
case '>=' : return left >= right;
|
||||
case '<=' : return left <= right;
|
||||
case '**' : return left ** right;
|
||||
case '===': return left === right;
|
||||
case '!==': return left !== right;
|
||||
/* eslint-disable no-bitwise */
|
||||
case '^' : return left ^ right;
|
||||
case '&' : return left & right;
|
||||
case '|' : return left | right;
|
||||
case '>>' : return left >> right;
|
||||
case '<<' : return left << right;
|
||||
case '>>>': return left >>> right;
|
||||
/* eslint-enable no-bitwise */
|
||||
/* eslint-disable eqeqeq */
|
||||
case '==' : return left == right;
|
||||
case '!=' : return left != right;
|
||||
/* eslint-enable eqeqeq, no-multi-spaces, switch-colon-spacing */
|
||||
case 'instanceof': return left instanceof right;
|
||||
default:
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("evaluateBinaryExpression(): Can't handle operator", node.operator);
|
||||
return undefined;
|
||||
const binary = (left, right) => {
|
||||
switch (node.operator) {
|
||||
/* eslint-disable no-multi-spaces, switch-colon-spacing */
|
||||
case '+' : return left + right;
|
||||
case '-' : return left - right;
|
||||
case '/' : return left / right;
|
||||
case '%' : return left % right;
|
||||
case '*' : return left * right;
|
||||
case '>' : return left > right;
|
||||
case '<' : return left < right;
|
||||
case 'in' : return left in right;
|
||||
case '>=' : return left >= right;
|
||||
case '<=' : return left <= right;
|
||||
case '**' : return left ** right;
|
||||
case '===': return left === right;
|
||||
case '!==': return left !== right;
|
||||
/* eslint-disable no-bitwise */
|
||||
case '^' : return left ^ right;
|
||||
case '&' : return left & right;
|
||||
case '|' : return left | right;
|
||||
case '>>' : return left >> right;
|
||||
case '<<' : return left << right;
|
||||
case '>>>': return left >>> right;
|
||||
/* eslint-enable no-bitwise */
|
||||
/* eslint-disable eqeqeq */
|
||||
case '==' : return left == right;
|
||||
case '!=' : return left != right;
|
||||
/* eslint-enable eqeqeq, no-multi-spaces, switch-colon-spacing */
|
||||
case 'instanceof': return left instanceof right;
|
||||
default:
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("evaluateBinaryExpression(): Can't handle operator", node.operator);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
const {async: lasync, value: left} = this.evaluate(node.left);
|
||||
const {async: rasync, value: right} = this.evaluate(node.right);
|
||||
if (lasync || rasync) {
|
||||
return {
|
||||
async: true,
|
||||
value: Promise.all([left, right]).then(([left, right]) => binary(left, right)),
|
||||
};
|
||||
}
|
||||
return {value: binary(left, right)};
|
||||
}
|
||||
|
||||
evaluateCallExpression(node) {
|
||||
let asyncArgs = false;
|
||||
const args = [];
|
||||
for (let i = 0; i < node.arguments.length; i++) {
|
||||
const arg = node.arguments[i];
|
||||
args.push(this.evaluate(arg));
|
||||
const {async, value} = this.evaluate(arg);
|
||||
// eslint-disable-next-line no-bitwise
|
||||
asyncArgs |= async;
|
||||
args.push(value);
|
||||
}
|
||||
const invoke = (callee, args, optional) => {
|
||||
if (optional) {
|
||||
return callee?.(...args);
|
||||
}
|
||||
return callee(...args);
|
||||
};
|
||||
const {callee, optional: callOptional} = node;
|
||||
if (types.isMemberExpression(callee)) {
|
||||
const {
|
||||
computed,
|
||||
object,
|
||||
optional: memberOptional,
|
||||
property,
|
||||
} = callee;
|
||||
const O = this.evaluate(object);
|
||||
const P = computed ? this.evaluate(property) : property.name;
|
||||
if (!types.isMemberExpression(callee)) {
|
||||
const {async, value} = this.evaluate(callee);
|
||||
if (asyncArgs || async) {
|
||||
return {
|
||||
async: true,
|
||||
value: Promise
|
||||
.all([value, Promise.all(args)])
|
||||
.then(([callee, args]) => invoke(callee, args, callOptional)),
|
||||
};
|
||||
}
|
||||
return {value: invoke(value, args, callOptional)};
|
||||
}
|
||||
const invokeMember = (O, P, args, callOptional, memberOptional) => {
|
||||
if (callOptional) {
|
||||
if (memberOptional) {
|
||||
return O?.[P]?.(...args);
|
||||
|
@ -122,25 +181,38 @@ export default class Sandbox {
|
|||
return O?.[P](...args);
|
||||
}
|
||||
return O[P](...args);
|
||||
};
|
||||
const {
|
||||
computed,
|
||||
object,
|
||||
optional: memberOptional,
|
||||
property,
|
||||
} = callee;
|
||||
const O = this.evaluate(object);
|
||||
const P = computed ? this.evaluate(property) : {value: property.name};
|
||||
if (asyncArgs || O.async || P.async) {
|
||||
return {
|
||||
async: true,
|
||||
value: Promise
|
||||
.all([O.value, P.value, Promise.all(args)])
|
||||
.then(([O, P, args]) => invokeMember(O, P, args, callOptional, memberOptional)),
|
||||
};
|
||||
}
|
||||
if (callOptional) {
|
||||
return this.evaluate(callee)?.(...args);
|
||||
}
|
||||
return this.evaluate(callee)(...args);
|
||||
return {value: invokeMember(O.value, P.value, args, callOptional, memberOptional)};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
evaluateDirectiveLiteral({value}) {
|
||||
return value;
|
||||
return {value};
|
||||
}
|
||||
|
||||
evaluateIdentifier(node) {
|
||||
return this.nodeScope(node).get(node.name);
|
||||
return {value: this.nodeScope(node).get(node.name)};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
evaluateLiteral({value}) {
|
||||
return value;
|
||||
return {value};
|
||||
}
|
||||
|
||||
evaluateMemberExpression({
|
||||
|
@ -149,45 +221,76 @@ export default class Sandbox {
|
|||
optional,
|
||||
property,
|
||||
}) {
|
||||
const member = (O, P) => (optional ? O?.[P] : O[P]);
|
||||
const O = this.evaluate(object);
|
||||
const P = computed ? this.evaluate(property) : property.name;
|
||||
return optional ? O?.[P] : O[P];
|
||||
const P = computed ? this.evaluate(property) : {value: property.name};
|
||||
if (O.async || P.async) {
|
||||
return {
|
||||
async: true,
|
||||
value: Promise.all([O.value, P.value]).then(([O, P]) => member(O, P)),
|
||||
};
|
||||
}
|
||||
return {value: member(O.value, P.value)};
|
||||
}
|
||||
|
||||
evaluateObjectExpression({properties}) {
|
||||
let isAsync = false;
|
||||
const entries = [];
|
||||
for (let i = 0; i < properties.length; i++) {
|
||||
const {computed, key, value} = properties[i];
|
||||
entries.push([computed ? this.evaluate(key) : key.name, this.evaluate(value)]);
|
||||
const k = computed ? this.evaluate(key) : {value: key.name};
|
||||
const v = this.evaluate(value);
|
||||
// eslint-disable-next-line no-bitwise
|
||||
isAsync |= k.async | v.async;
|
||||
if (k.async || v.async) {
|
||||
entries.push(Promise.all([k.value, v.value]));
|
||||
}
|
||||
else {
|
||||
entries.push([k.value, v.value]);
|
||||
}
|
||||
}
|
||||
return Object.fromEntries(entries);
|
||||
return {
|
||||
async: !!isAsync,
|
||||
value: isAsync
|
||||
? Promise.all(entries).then(Object.fromEntries)
|
||||
: Object.fromEntries(entries),
|
||||
};
|
||||
}
|
||||
|
||||
evaluateUpdateExpression(node) {
|
||||
const {argument, operator, prefix} = node;
|
||||
let value = this.evaluate(argument);
|
||||
const {async, value} = this.evaluate(argument);
|
||||
const scope = this.nodeScope(node);
|
||||
if (prefix) {
|
||||
const update = (value) => {
|
||||
if (prefix) {
|
||||
switch (operator) {
|
||||
case '++':
|
||||
return scope.set(argument.name, value + 1);
|
||||
case '--':
|
||||
return scope.set(argument.name, value - 1);
|
||||
default:
|
||||
}
|
||||
}
|
||||
switch (operator) {
|
||||
case '++':
|
||||
scope.set(argument.name, ++value);
|
||||
scope.set(argument.name, value + 1);
|
||||
return value;
|
||||
case '--':
|
||||
scope.set(argument.name, --value);
|
||||
scope.set(argument.name, value - 1);
|
||||
return value;
|
||||
default:
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("evaluateUpdateExpression(): Can't handle", operator);
|
||||
return undefined;
|
||||
};
|
||||
if (async) {
|
||||
return {
|
||||
async: true,
|
||||
value: Promise.resolve(value).then((value) => update(value)),
|
||||
};
|
||||
}
|
||||
switch (operator) {
|
||||
case '++':
|
||||
scope.set(argument.name, value + 1);
|
||||
return value;
|
||||
case '--':
|
||||
scope.set(argument.name, value - 1);
|
||||
return value;
|
||||
default:
|
||||
}
|
||||
return undefined;
|
||||
return {value: update(value)};
|
||||
}
|
||||
|
||||
next() {
|
||||
|
@ -242,12 +345,16 @@ export default class Sandbox {
|
|||
return this.nodeScope(this.ast);
|
||||
}
|
||||
|
||||
run(max = 10000) {
|
||||
async run(max = 10000) {
|
||||
for (
|
||||
let current = this.runner.next();
|
||||
let {done, value: {async, value}} = this.runner.next();
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
--max > 0 && current.done !== true;
|
||||
current = this.runner.next()
|
||||
--max > 0
|
||||
&& done !== true
|
||||
&& async
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
&& await value;
|
||||
{done, value: {async, value}} = this.runner.next()
|
||||
// eslint-disable-next-line no-empty
|
||||
) {}
|
||||
return this;
|
||||
|
@ -257,6 +364,7 @@ export default class Sandbox {
|
|||
const flat = [
|
||||
'ArrayExpression',
|
||||
'AssignmentExpression',
|
||||
'AwaitExpression',
|
||||
'BinaryExpression',
|
||||
'CallExpression',
|
||||
'DirectiveLiteral',
|
||||
|
@ -310,26 +418,63 @@ export default class Sandbox {
|
|||
if (types.isForStatement(node)) {
|
||||
scope = scope.push();
|
||||
}
|
||||
this.setNextScope(node, scope);
|
||||
if (types.isVariableDeclarator(node)) {
|
||||
const {id, init} = node;
|
||||
scope.allocate(id.name, this.evaluate(init));
|
||||
const {async, value} = this.evaluate(init);
|
||||
if (async) {
|
||||
yield {
|
||||
async: true,
|
||||
value: Promise.resolve(value).then((value) => {
|
||||
scope.allocate(id.name, value);
|
||||
}),
|
||||
};
|
||||
}
|
||||
else {
|
||||
scope.allocate(id.name, value);
|
||||
}
|
||||
}
|
||||
this.setNextScope(node, scope);
|
||||
// Blocks...
|
||||
if (types.isIfStatement(node)) {
|
||||
keys = [this.evaluate(node.test) ? 'consequent' : 'alternate'];
|
||||
const {async, value} = this.evaluate(node.test);
|
||||
if (async) {
|
||||
yield {
|
||||
async: true,
|
||||
value: Promise.resolve(value).then((value) => {
|
||||
keys = [value ? 'consequent' : 'alternate'];
|
||||
}),
|
||||
};
|
||||
}
|
||||
else {
|
||||
keys = [value ? 'consequent' : 'alternate'];
|
||||
}
|
||||
}
|
||||
// Loops...
|
||||
let loop = false;
|
||||
if (types.isForStatement(node)) {
|
||||
this.traverse(node.init).next();
|
||||
const {value} = this.traverse(node.init).next();
|
||||
if (value?.async) {
|
||||
yield value;
|
||||
}
|
||||
}
|
||||
do {
|
||||
if (
|
||||
types.isForStatement(node)
|
||||
|| types.isWhileStatement(node)
|
||||
) {
|
||||
keys = this.evaluate(node.test) ? ['body'] : [];
|
||||
const {async, value} = this.evaluate(node.test);
|
||||
if (async) {
|
||||
yield {
|
||||
async: true,
|
||||
// eslint-disable-next-line no-loop-func
|
||||
value: Promise.resolve(value).then((value) => {
|
||||
keys = value ? ['body'] : [];
|
||||
}),
|
||||
};
|
||||
}
|
||||
else {
|
||||
keys = value ? ['body'] : [];
|
||||
}
|
||||
loop = keys.length > 0;
|
||||
}
|
||||
// Recur...
|
||||
|
@ -339,17 +484,32 @@ export default class Sandbox {
|
|||
}
|
||||
// Loops...
|
||||
if (types.isForStatement(node)) {
|
||||
this.traverse(node.update).next();
|
||||
const {value} = this.traverse(node.update).next();
|
||||
if (value?.async) {
|
||||
yield value;
|
||||
}
|
||||
if (loop) {
|
||||
yield;
|
||||
yield {loop: 'for', value: undefined};
|
||||
}
|
||||
}
|
||||
if (types.isDoWhileStatement(node)) {
|
||||
loop = this.evaluate(node.test);
|
||||
yield;
|
||||
const test = this.evaluate(node.test);
|
||||
if (test.async) {
|
||||
yield {
|
||||
async: true,
|
||||
// eslint-disable-next-line no-loop-func
|
||||
value: Promise.resolve(test.value).then((value) => {
|
||||
loop = value;
|
||||
}),
|
||||
};
|
||||
}
|
||||
else {
|
||||
loop = test.value;
|
||||
}
|
||||
yield {loop: 'doWhile', value: undefined};
|
||||
}
|
||||
if (types.isWhileStatement(node) && loop) {
|
||||
yield;
|
||||
yield {loop: 'while', value: undefined};
|
||||
}
|
||||
} while (loop);
|
||||
// Scope...
|
||||
|
|
|
@ -67,10 +67,11 @@ export default class Scope {
|
|||
while (walk) {
|
||||
if (walk.context[key]) {
|
||||
walk.context[key].value = value;
|
||||
return;
|
||||
return value;
|
||||
}
|
||||
walk = walk.parent;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,15 @@ import {expect} from 'chai';
|
|||
import Sandbox from '../src/sandbox';
|
||||
|
||||
it('evaluates ArrayExpression', () => {
|
||||
expect(new Sandbox(parse('[1, 2, 3]')).next())
|
||||
expect(new Sandbox(parse('[1, 2, 3]')).next().value)
|
||||
.to.deep.include({value: [1, 2, 3]});
|
||||
});
|
||||
|
||||
it('evaluates async ArrayExpression', async () => {
|
||||
const o = {allowAwaitOutsideFunction: true};
|
||||
const {async, value} = new Sandbox(parse('[await 1, 2, 3]', o)).next().value;
|
||||
expect(async)
|
||||
.to.be.true;
|
||||
expect(await value)
|
||||
.to.deep.equal([1, 2, 3]);
|
||||
});
|
||||
|
|
|
@ -4,6 +4,6 @@ import {expect} from 'chai';
|
|||
import Sandbox from '../src/sandbox';
|
||||
|
||||
it('evaluates AssignmentExpression', () => {
|
||||
expect(new Sandbox(parse('let foo = 69; foo = 420')).next())
|
||||
expect(new Sandbox(parse('let foo = 69; foo = 420')).next().value)
|
||||
.to.deep.include({value: 420});
|
||||
});
|
||||
|
|
18
packages/sandbox/test/await-expression.js
Normal file
18
packages/sandbox/test/await-expression.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
import {parse} from '@babel/parser';
|
||||
import {expect} from 'chai';
|
||||
|
||||
import Sandbox from '../src/sandbox';
|
||||
|
||||
it('evaluates AwaitExpression', async () => {
|
||||
const context = {
|
||||
wait: () => new Promise((resolve) => setTimeout(() => resolve(420), 0)),
|
||||
};
|
||||
const o = {allowAwaitOutsideFunction: true};
|
||||
const sandbox = new Sandbox(parse('const test = await wait(); test', o), context);
|
||||
const {value} = sandbox.next();
|
||||
expect(value.async)
|
||||
.to.be.true;
|
||||
await value.value;
|
||||
expect(sandbox.next().value.value)
|
||||
.to.equal(420);
|
||||
});
|
|
@ -4,48 +4,48 @@ import {expect} from 'chai';
|
|||
import Sandbox from '../src/sandbox';
|
||||
|
||||
it('evaluates BinaryExpression', () => {
|
||||
expect(new Sandbox(parse('1 + 2')).next())
|
||||
expect(new Sandbox(parse('1 + 2')).next().value)
|
||||
.to.deep.include({value: 3});
|
||||
expect(new Sandbox(parse('1 - 2')).next())
|
||||
expect(new Sandbox(parse('1 - 2')).next().value)
|
||||
.to.deep.include({value: -1});
|
||||
expect(new Sandbox(parse('4 / 2')).next())
|
||||
expect(new Sandbox(parse('4 / 2')).next().value)
|
||||
.to.deep.include({value: 2});
|
||||
expect(new Sandbox(parse('10 % 3')).next())
|
||||
expect(new Sandbox(parse('10 % 3')).next().value)
|
||||
.to.deep.include({value: 1});
|
||||
expect(new Sandbox(parse('1 * 2')).next())
|
||||
expect(new Sandbox(parse('1 * 2')).next().value)
|
||||
.to.deep.include({value: 2});
|
||||
expect(new Sandbox(parse('1 > 2')).next())
|
||||
expect(new Sandbox(parse('1 > 2')).next().value)
|
||||
.to.deep.include({value: false});
|
||||
expect(new Sandbox(parse('1 < 2')).next())
|
||||
expect(new Sandbox(parse('1 < 2')).next().value)
|
||||
.to.deep.include({value: true});
|
||||
expect(new Sandbox(parse('const foo = {a: 69}; "a" in foo')).next())
|
||||
expect(new Sandbox(parse('const foo = {a: 69}; "a" in foo')).next().value)
|
||||
.to.deep.include({value: true});
|
||||
expect(new Sandbox(parse('const foo = {a: 69}; "b" in foo')).next())
|
||||
expect(new Sandbox(parse('const foo = {a: 69}; "b" in foo')).next().value)
|
||||
.to.deep.include({value: false});
|
||||
expect(new Sandbox(parse('1 >= 2')).next())
|
||||
expect(new Sandbox(parse('1 >= 2')).next().value)
|
||||
.to.deep.include({value: false});
|
||||
expect(new Sandbox(parse('1 <= 2')).next())
|
||||
expect(new Sandbox(parse('1 <= 2')).next().value)
|
||||
.to.deep.include({value: true});
|
||||
expect(new Sandbox(parse('2 ** 3')).next())
|
||||
expect(new Sandbox(parse('2 ** 3')).next().value)
|
||||
.to.deep.include({value: 8});
|
||||
expect(new Sandbox(parse('1 === 2')).next())
|
||||
expect(new Sandbox(parse('1 === 2')).next().value)
|
||||
.to.deep.include({value: false});
|
||||
expect(new Sandbox(parse('1 !== 2')).next())
|
||||
expect(new Sandbox(parse('1 !== 2')).next().value)
|
||||
.to.deep.include({value: true});
|
||||
expect(new Sandbox(parse('7 & 3')).next())
|
||||
expect(new Sandbox(parse('7 & 3')).next().value)
|
||||
.to.deep.include({value: 3});
|
||||
expect(new Sandbox(parse('1 | 2')).next())
|
||||
expect(new Sandbox(parse('1 | 2')).next().value)
|
||||
.to.deep.include({value: 3});
|
||||
expect(new Sandbox(parse('16 >> 2')).next())
|
||||
expect(new Sandbox(parse('16 >> 2')).next().value)
|
||||
.to.deep.include({value: 4});
|
||||
expect(new Sandbox(parse('16 >>> 5')).next())
|
||||
expect(new Sandbox(parse('16 >>> 5')).next().value)
|
||||
.to.deep.include({value: 0});
|
||||
expect(new Sandbox(parse('1 << 2')).next())
|
||||
expect(new Sandbox(parse('1 << 2')).next().value)
|
||||
.to.deep.include({value: 4});
|
||||
expect(new Sandbox(parse('1 ^ 2')).next())
|
||||
expect(new Sandbox(parse('1 ^ 2')).next().value)
|
||||
.to.deep.include({value: 3});
|
||||
expect(new Sandbox(parse('1 == 2')).next())
|
||||
expect(new Sandbox(parse('1 == 2')).next().value)
|
||||
.to.deep.include({value: false});
|
||||
expect(new Sandbox(parse('1 != 2')).next())
|
||||
expect(new Sandbox(parse('1 != 2')).next().value)
|
||||
.to.deep.include({value: true});
|
||||
});
|
||||
|
|
|
@ -4,6 +4,6 @@ import {expect} from 'chai';
|
|||
import Sandbox from '../src/sandbox';
|
||||
|
||||
it('evaluates CallExpression', () => {
|
||||
expect(new Sandbox(parse('test()'), {test: () => 69}).next())
|
||||
expect(new Sandbox(parse('test()'), {test: () => 69}).next().value)
|
||||
.to.deep.include({value: 69});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@ import Sandbox from '../src/sandbox';
|
|||
|
||||
it('evaluates DoWhileStatement', () => {
|
||||
const sandbox = new Sandbox(parse('let i = 0; do { i++; } while (false); i'));
|
||||
expect(sandbox.next()).to.deep.include({value: 0});
|
||||
expect(sandbox.next()).to.deep.include({value: undefined});
|
||||
expect(sandbox.next()).to.deep.include({value: 1});
|
||||
expect(sandbox.next().value).to.deep.include({value: 0});
|
||||
expect(sandbox.next().value).to.deep.include({value: undefined});
|
||||
expect(sandbox.next().value).to.deep.include({value: 1});
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@ import Sandbox from '../src/sandbox';
|
|||
it('evaluates ForStatement', () => {
|
||||
const sandbox = new Sandbox(parse('for (let i = 0; i < 5; ++i) { i; }'));
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
expect(sandbox.next()).to.deep.include({value: i});
|
||||
expect(sandbox.next().value).to.deep.include({value: i});
|
||||
// Loop...
|
||||
sandbox.next();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,6 @@ import {expect} from 'chai';
|
|||
import Sandbox from '../src/sandbox';
|
||||
|
||||
it('evaluates Identifier', () => {
|
||||
expect(new Sandbox(parse('const test = 69; test')).next())
|
||||
expect(new Sandbox(parse('const test = 69; test')).next().value)
|
||||
.to.deep.include({value: 69});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@ import Sandbox from '../src/sandbox';
|
|||
|
||||
it('evaluates IfStatement', () => {
|
||||
expect(new Sandbox(parse('if (false) { 69; }')).next())
|
||||
.to.deep.include({value: undefined});
|
||||
expect(new Sandbox(parse('if (true) { 69; }')).next())
|
||||
.to.deep.include({done: true});
|
||||
expect(new Sandbox(parse('if (true) { 69; }')).next().value)
|
||||
.to.deep.include({value: 69});
|
||||
});
|
||||
|
|
|
@ -4,10 +4,10 @@ import {expect} from 'chai';
|
|||
import Sandbox from '../src/sandbox';
|
||||
|
||||
it('evaluates Literal', () => {
|
||||
expect(new Sandbox(parse('69')).next())
|
||||
expect(new Sandbox(parse('69')).next().value)
|
||||
.to.deep.include({value: 69});
|
||||
expect(new Sandbox(parse('"420"')).next())
|
||||
expect(new Sandbox(parse('"420"')).next().value)
|
||||
.to.deep.include({value: '420'});
|
||||
expect(new Sandbox(parse('420.69')).next())
|
||||
expect(new Sandbox(parse('420.69')).next().value)
|
||||
.to.deep.include({value: 420.69});
|
||||
});
|
||||
|
|
|
@ -4,6 +4,21 @@ import {expect} from 'chai';
|
|||
import Sandbox from '../src/sandbox';
|
||||
|
||||
it('evaluates MemberExpression', () => {
|
||||
expect(new Sandbox(parse('a.b.c'), {a: {b: {c: 69}}}).next())
|
||||
expect(new Sandbox(parse('a.b.c'), {a: {b: {c: 69}}}).next().value)
|
||||
.to.deep.include({value: 69});
|
||||
});
|
||||
|
||||
it('evaluates async MemberExpression', async () => {
|
||||
const o = {allowAwaitOutsideFunction: true};
|
||||
const sandbox = new Sandbox(parse('const aa = await a; aa.b.c', o), {a: {b: {c: 69}}});
|
||||
let {async, value} = sandbox.next().value;
|
||||
expect(async)
|
||||
.to.be.true;
|
||||
expect(await value)
|
||||
.to.equal(undefined);
|
||||
({async, value} = sandbox.next().value);
|
||||
expect(async)
|
||||
.to.be.undefined;
|
||||
expect(value)
|
||||
.to.equal(69);
|
||||
});
|
||||
|
|
|
@ -4,6 +4,6 @@ import {expect} from 'chai';
|
|||
import Sandbox from '../src/sandbox';
|
||||
|
||||
it('evaluates ObjectExpression', () => {
|
||||
expect(new Sandbox(parse('({a: 1, b: 2})')).next())
|
||||
expect(new Sandbox(parse('({a: 1, b: 2})')).next().value)
|
||||
.to.deep.include({value: {a: 1, b: 2}});
|
||||
});
|
||||
|
|
13
packages/sandbox/test/run.js
Normal file
13
packages/sandbox/test/run.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
import {parse} from '@babel/parser';
|
||||
import {expect} from 'chai';
|
||||
|
||||
import Sandbox from '../src/sandbox';
|
||||
|
||||
it('runs', async () => {
|
||||
const context = {
|
||||
wait: () => new Promise((resolve) => setTimeout(() => resolve(), 0)),
|
||||
}
|
||||
const o = {allowAwaitOutsideFunction: true};
|
||||
const sandbox = new Sandbox(parse(`await wait(); 1 + 3 * 2;`, o), context);
|
||||
await sandbox.run();
|
||||
});
|
|
@ -5,13 +5,13 @@ import Sandbox from '../src/sandbox';
|
|||
|
||||
it('scopes BlockStatement', () => {
|
||||
const sandbox = new Sandbox(parse('if (true) { const foo = 69; foo; } foo;'));
|
||||
expect(sandbox.next())
|
||||
expect(sandbox.next().value)
|
||||
.to.deep.include({value: 69});
|
||||
expect(sandbox.next())
|
||||
expect(sandbox.next().value)
|
||||
.to.deep.include({value: undefined});
|
||||
});
|
||||
|
||||
it('scopes ForStatement', () => {
|
||||
expect(new Sandbox(parse('for (let i = 0; i < 5; ++i) {} i;')).next())
|
||||
expect(new Sandbox(parse('for (let i = 0; i < 5; ++i) {} i;')).next().value)
|
||||
.to.deep.include({value: undefined});
|
||||
});
|
||||
|
|
|
@ -4,12 +4,12 @@ import {expect} from 'chai';
|
|||
import Sandbox from '../src/sandbox';
|
||||
|
||||
it('evaluates UpdateExpression', () => {
|
||||
expect(new Sandbox(parse('a++'), {a: 1}).next())
|
||||
expect(new Sandbox(parse('a++'), {a: 1}).next().value)
|
||||
.to.deep.include({value: 1});
|
||||
expect(new Sandbox(parse('++a'), {a: 1}).next())
|
||||
expect(new Sandbox(parse('++a'), {a: 1}).next().value)
|
||||
.to.deep.include({value: 2});
|
||||
expect(new Sandbox(parse('a--'), {a: 1}).next())
|
||||
expect(new Sandbox(parse('a--'), {a: 1}).next().value)
|
||||
.to.deep.include({value: 1});
|
||||
expect(new Sandbox(parse('--a'), {a: 1}).next())
|
||||
expect(new Sandbox(parse('--a'), {a: 1}).next().value)
|
||||
.to.deep.include({value: 0});
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@ import Sandbox from '../src/sandbox';
|
|||
it('evaluates WhileStatement', () => {
|
||||
const sandbox = new Sandbox(parse('let i = 0; while (i < 5) { i++; }'));
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
expect(sandbox.next()).to.deep.include({value: i});
|
||||
expect(sandbox.next().value).to.deep.include({value: i});
|
||||
// Loop...
|
||||
sandbox.next();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user