From 2ab82d1d3eae478f743e7f2b76c854b063628bfd Mon Sep 17 00:00:00 2001 From: cha0s Date: Sun, 30 Jun 2024 14:20:37 -0500 Subject: [PATCH] chore: coverage and tidy --- app/astride/evaluate.js | 1 + app/astride/evaluators/array.js | 4 +- app/astride/evaluators/assignment.js | 6 +- app/astride/evaluators/call.js | 5 +- app/astride/evaluators/call.test.js | 6 +- app/astride/evaluators/object.js | 15 +--- app/astride/sandbox.js | 26 +++--- app/astride/sandbox.test.js | 50 +++++++++++- app/astride/traverse.js | 13 +-- app/astride/types.js | 116 --------------------------- 10 files changed, 73 insertions(+), 169 deletions(-) delete mode 100644 app/astride/types.js diff --git a/app/astride/evaluate.js b/app/astride/evaluate.js index 330689c..6e3f6fe 100644 --- a/app/astride/evaluate.js +++ b/app/astride/evaluate.js @@ -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}`) } diff --git a/app/astride/evaluators/array.js b/app/astride/evaluators/array.js index 16cee39..4bd3c22 100644 --- a/app/astride/evaluators/array.js +++ b/app/astride/evaluators/array.js @@ -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) { diff --git a/app/astride/evaluators/assignment.js b/app/astride/evaluators/assignment.js index 4af221b..c6680eb 100644 --- a/app/astride/evaluators/assignment.js +++ b/app/astride/evaluators/assignment.js @@ -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); diff --git a/app/astride/evaluators/call.js b/app/astride/evaluators/call.js index 2f80a4e..16da9b3 100644 --- a/app/astride/evaluators/call.js +++ b/app/astride/evaluators/call.js @@ -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 { diff --git a/app/astride/evaluators/call.test.js b/app/astride/evaluators/call.test.js index 0529cbe..e9f6849 100644 --- a/app/astride/evaluators/call.test.js +++ b/app/astride/evaluators/call.test.js @@ -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}) => { diff --git a/app/astride/evaluators/object.js b/app/astride/evaluators/object.js index c54343a..868ba65 100644 --- a/app/astride/evaluators/object.js +++ b/app/astride/evaluators/object.js @@ -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; diff --git a/app/astride/sandbox.js b/app/astride/sandbox.js index 348b94b..f3b9527 100644 --- a/app/astride/sandbox.js +++ b/app/astride/sandbox.js @@ -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, diff --git a/app/astride/sandbox.test.js b/app/astride/sandbox.test.js index 319cfca..5eb1493 100644 --- a/app/astride/sandbox.test.js +++ b/app/astride/sandbox.test.js @@ -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}); diff --git a/app/astride/traverse.js b/app/astride/traverse.js index 9f52130..1d801b9 100644 --- a/app/astride/traverse.js +++ b/app/astride/traverse.js @@ -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) { diff --git a/app/astride/types.js b/app/astride/types.js deleted file mode 100644 index d055b62..0000000 --- a/app/astride/types.js +++ /dev/null @@ -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; -}