chore: coverage and tidy

This commit is contained in:
cha0s 2024-06-30 14:20:37 -05:00
parent d00bcf23f3
commit 2ab82d1d3e
10 changed files with 73 additions and 169 deletions

View File

@ -41,6 +41,7 @@ export default function evaluate(node, {scope} = {}) {
return evaluators.unary(node, {evaluate, scope});
case 'UpdateExpression':
return evaluators.update(node, {evaluate, scope});
/* v8 ignore next 2 */
default:
throw new EvalError(`astride: Can't evaluate node of type ${node.type}`)
}

View File

@ -1,12 +1,10 @@
import {isSpreadElement} from '@/astride/types.js';
export default function(node, {evaluate, scope}) {
const elements = [];
const asyncSpread = Object.create(null);
let isAsync = false;
for (const index in node.elements) {
const element = node.elements[index];
if (isSpreadElement(element)) {
if ('SpreadElement' === element.type) {
const {async, value} = evaluate(element.argument, {scope});
isAsync = isAsync || async;
if (async) {

View File

@ -1,11 +1,7 @@
import {
isMemberExpression,
} from '@/astride/types.js';
export default function(node, {evaluate, scope}) {
const {operator, left} = node;
const right = evaluate(node.right, {scope});
if (!isMemberExpression(left)) {
if (!('MemberExpression' === left.type)) {
const assign = (value) => {
switch (operator) {
case '=' : return scope.set(left.name, value);

View File

@ -1,7 +1,4 @@
import fastCall from '@/util/fast-call.js';
import {
isMemberExpression,
} from '@/astride/types.js';
export default function(node, {evaluate, scope}) {
let asyncArgs = false;
@ -24,7 +21,7 @@ export default function(node, {evaluate, scope}) {
}
return fastCall(fn, holder, args);
};
if (!isMemberExpression(callee)) {
if (!('MemberExpression' === callee.type)) {
const {async, value} = evaluate(callee, {scope});
if (asyncArgs || async) {
return {

View File

@ -48,10 +48,8 @@ scopeTest('evaluates optional calls', async ({scope}) => {
scope.set('O', {});
expect(evaluate(await expression('g?.(1, 2, 3)'), {scope}).value)
.to.equal(undefined);
// expect(evaluate(await expression('O?.g(1, 2, 3)'), {scope}).value)
// .to.equal(undefined);
// expect(evaluate(await expression('O?.g?.(1, 2, 3)'), {scope}).value)
// .to.equal(undefined);
expect(evaluate(await expression('O?.g?.(1, 2, 3)'), {scope}).value)
.to.equal(undefined);
});
scopeTest('evaluates async calls', async ({scope}) => {

View File

@ -1,25 +1,18 @@
import {
isIdentifier,
isLiteral,
isProperty,
isSpreadElement,
} from '@/astride/types.js';
export default function(node, {evaluate, scope}) {
const {properties} = node;
let isAsync = false;
const entries = [];
for (let i = 0; i < properties.length; i++) {
if (isProperty(properties[i])) {
if ('Property' === properties[i].type) {
const {computed, key, value} = properties[i];
let k;
if (computed) {
k = evaluate(key, {scope});
}
else if (isIdentifier(key)) {
else if ('Identifier' === key.type) {
k = {value: key.name};
}
else if (isLiteral(key)) {
else if ('Literal' === key.type) {
k = {value: key.value};
}
/* v8 ignore next 3 */
@ -35,7 +28,7 @@ export default function(node, {evaluate, scope}) {
entries.push([k.value, v.value]);
}
}
if (isSpreadElement(properties[i])) {
if ('SpreadElement' === properties[i].type) {
const {argument} = properties[i];
const spreading = evaluate(argument, {scope});
isAsync ||= spreading.async;

View File

@ -1,13 +1,6 @@
import evaluate from '@/astride/evaluate.js';
import Scope from '@/astride/scope.js';
import traverse from '@/astride/traverse.js';
import {
isArrayPattern,
isBlockStatement,
isForStatement,
isIdentifier,
isObjectPattern,
} from '@/astride/types.js';
const YIELD_NONE = 0;
const YIELD_PROMISE = 1;
@ -34,8 +27,8 @@ export default class Sandbox {
this.ast,
(node, verb) => {
if (
isBlockStatement(node)
|| isForStatement(node)
'BlockStatement' === node.type
|| 'ForStatement' === node.type
) {
switch (verb) {
case 'enter': {
@ -67,7 +60,7 @@ export default class Sandbox {
if (null === element) {
continue;
}
if (isIdentifier(element)) {
if ('Identifier' === element.type) {
scope.allocate(element.name, init[i]);
}
/* v8 ignore next 3 */
@ -83,7 +76,7 @@ export default class Sandbox {
const {properties} = id;
for (let i = 0; i < properties.length; ++i) {
const property = properties[i];
if (isObjectPattern(property.value)) {
if ('ObjectPattern' === property.value.type) {
this.destructureObject(property.value, init[property.key.name]);
}
else {
@ -135,6 +128,7 @@ export default class Sandbox {
case 'ChainExpression':
case 'ObjectExpression':
case 'Identifier':
case 'LogicalExpression':
case 'MemberExpression':
case 'UnaryExpression':
case 'UpdateExpression': {
@ -156,6 +150,7 @@ export default class Sandbox {
if (result.yield) {
return result;
}
/* v8 ignore next 2 */
break;
}
case 'BlockStatement': {
@ -168,6 +163,7 @@ export default class Sandbox {
if (skipping && child === this.$$execution.stack[depth + 1]) {
skipping = false;
}
/* v8 ignore next 3 */
if (skipping) {
continue;
}
@ -370,6 +366,7 @@ export default class Sandbox {
if (skipping && child === this.$$execution.stack[depth + 1]) {
skipping = false;
}
/* v8 ignore next 3 */
if (skipping) {
continue;
}
@ -390,7 +387,7 @@ export default class Sandbox {
}
else {
const init = this.executeSync(node.init, depth + 1);
if (isIdentifier(id)) {
if ('Identifier' === id.type) {
if (init.yield) {
return {
value: Promise.resolve(init.value)
@ -405,7 +402,7 @@ export default class Sandbox {
};
}
}
else if (isArrayPattern(id)) {
else if ('ArrayPattern' === id.type) {
if (init.yield) {
return {
value: Promise.resolve(init.value)
@ -420,7 +417,7 @@ export default class Sandbox {
};
}
}
else if (isObjectPattern(id)) {
else if ('ObjectPattern' === id.type) {
if (init.yield) {
return {
value: Promise.resolve(init.value)
@ -475,6 +472,7 @@ export default class Sandbox {
yield: YIELD_LOOP_UPDATE,
};
}
/* v8 ignore next 7 */
default:
console.log(
node.type,

View File

@ -147,6 +147,33 @@ test('evaluates conditional branches', async () => {
});
});
test('evaluates conditional expressions', async () => {
const sandbox = new Sandbox(
await parse(`
const x = true || false ? 1 : 2
const y = false && true ? 1 : 2
const a = (await true) ? await 1 : await 2
const b = (await false) ? await 1 : await 2
xx = true || false ? 1 : 2
yy = false && true ? 1 : 2
aa = (await true) ? await 1 : await 2
bb = (await false) ? await 1 : await 2
`),
);
await finish(sandbox);
expect(sandbox.context)
.to.deep.equal({
x: 1,
y: 2,
a: 1,
b: 2,
xx: 1,
yy: 2,
aa: 1,
bb: 2,
});
});
test('evaluates loops', async () => {
const sandbox = new Sandbox(
await parse(`
@ -155,6 +182,7 @@ test('evaluates loops', async () => {
a = 0
b = 0
c = 0
d = 0
for (let i = 0; i < 3; ++i) {
x += 1;
}
@ -165,11 +193,14 @@ test('evaluates loops', async () => {
a += 1;
} while (a < 3);
do {
b += 1;
b += await 1;
} while (await b < 3);
while (c < 3) {
c += 1;
}
while (await d < 3) {
d += await 1;
}
`),
);
await finish(sandbox);
@ -178,6 +209,7 @@ test('evaluates loops', async () => {
a: 3,
b: 3,
c: 3,
d: 3,
x: 3,
y: 3,
});
@ -207,8 +239,20 @@ test('returns values at the top level', async () => {
y = 8
`, {allowReturnOutsideFunction: true}),
);
const {value} = sandbox.run();
expect(value)
expect(sandbox.run().value)
.to.deep.equal([48, 12]);
expect(sandbox.context)
.to.deep.equal({x: 16, y: 4});
sandbox = new Sandbox(
await parse(`
x = 16
y = 4
return await [x * 3, y * 3]
x = 32
y = 8
`, {allowReturnOutsideFunction: true}),
);
expect((await finish(sandbox)).value)
.to.deep.equal([48, 12]);
expect(sandbox.context)
.to.deep.equal({x: 16, y: 4});

View File

@ -15,6 +15,7 @@ export const TRAVERSAL_PATH = {
IfStatement: ['alternate', 'consequent', 'test'],
MemberExpression: ['object', 'property'],
Literal: [],
LogicalExpression: ['left', 'right'],
ObjectExpression: ['properties'],
ObjectPattern: ['properties'],
Program: ['body'],
@ -35,15 +36,9 @@ export default function traverse(node, visitor) {
}
visitor(node, 'enter');
const path = TRAVERSAL_PATH[node.type];
let children;
if (path instanceof Function) {
children = path(node);
}
else if (Array.isArray(path)) {
children = [];
for (const key of path) {
children.push(...(Array.isArray(node[key]) ? node[key] : [node[key]]));
}
const children = [];
for (const key of path) {
children.push(...(Array.isArray(node[key]) ? node[key] : [node[key]]));
}
for (const child of children) {
if (child) {

View File

@ -1,116 +0,0 @@
export function isArrayPattern(node) {
/* v8 ignore next 3 */
if (!node || node.type !== 'ArrayPattern') {
return false;
}
return true;
}
export function isBlockStatement(node) {
/* v8 ignore next 3 */
if (!node || node.type !== 'BlockStatement') {
return false;
}
return true;
}
export function isDoWhileStatement(node) {
/* v8 ignore next 3 */
if (!node || node.type !== 'DoWhileStatement') {
return false;
}
return true;
}
export function isExpressionStatement(node) {
/* v8 ignore next 3 */
if (!node || node.type !== 'ExpressionStatement') {
return false;
}
return true;
}
export function isForStatement(node) {
/* v8 ignore next 3 */
if (!node || node.type !== 'ForStatement') {
return false;
}
return true;
}
export function isIdentifier(node) {
if (!node || node.type !== 'Identifier') {
return false;
}
return true;
}
export function isIfStatement(node) {
if (!node || node.type !== 'IfStatement') {
return false;
}
return true;
}
export function isLiteral(node) {
/* v8 ignore next 3 */
if (!node || node.type !== 'Literal') {
return false;
}
return true;
}
export function isMemberExpression(node) {
/* v8 ignore next 3 */
if (!node || node.type !== 'MemberExpression') {
return false;
}
return true;
}
export function isObjectPattern(node) {
/* v8 ignore next 3 */
if (!node || node.type !== 'ObjectPattern') {
return false;
}
return true;
}
export function isProperty(node) {
/* v8 ignore next 3 */
if (!node || node.type !== 'Property') {
return false;
}
return true;
}
export function isReturnStatement(node) {
/* v8 ignore next 3 */
if (!node || node.type !== 'ReturnStatement') {
return false;
}
return true;
}
export function isSpreadElement(node) {
/* v8 ignore next 3 */
if (!node || node.type !== 'SpreadElement') {
return false;
}
return true;
}
export function isVariableDeclarator(node) {
/* v8 ignore next 3 */
if (!node || node.type !== 'VariableDeclarator') {
return false;
}
return true;
}
export function isWhileStatement(node) {
if (!node || node.type !== 'WhileStatement') {
return false;
}
return true;
}