Skip to content

Commit

Permalink
feat(codegen): unfold constant expressions and eliminate dead branches
Browse files Browse the repository at this point in the history
  • Loading branch information
P0lip committed Mar 6, 2022
1 parent 09b8ea0 commit c85e8ac
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 35 deletions.
44 changes: 10 additions & 34 deletions src/__tests__/codegen.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -398,19 +398,15 @@ const tree = {
},
"$.baz..baz": function (scope) {
if (scope.depth < 1) return;
let pos = 0;
if (scope.path[0] !== "baz") return;
if (scope.depth < pos + 1 || (pos = scope.property !== "baz" ? -1 : scope.depth, pos === -1)) return;
if (scope.depth !== pos) return;
if (scope.property !== "baz") return;
scope.emit("$.baz..baz", 0, false);
},
"$.baz.bar..baz": function (scope) {
if (scope.depth < 2) return;
let pos = 0;
if (scope.path[0] !== "baz") return;
if (scope.depth < pos + 1 || scope.path[pos + 1] !== "bar") return;
if (scope.depth < pos + 2 || (pos = scope.property !== "baz" ? -1 : scope.depth, pos === -1)) return;
if (scope.depth !== pos) return;
if (scope.path[1] !== "bar") return;
if (scope.property !== "baz") return;
scope.emit("$.baz.bar..baz", 0, false);
},
"$..foo..bar..baz": function (scope) {
Expand Down Expand Up @@ -444,48 +440,40 @@ const tree = {
},
"$.components.schemas..[?(@property !== 'properties' && @ && (@ && @.example !== void 0 || @.default !== void 0))]": function (scope) {
if (scope.depth < 2) return;
let pos = 0;
if (scope.path[0] !== "components") return;
if (scope.depth < pos + 1 || scope.path[pos + 1] !== "schemas") return;
if (scope.depth < pos + 2 || (pos = !(scope.sandbox.property !== 'properties' && scope.sandbox.value && (scope.sandbox.value && scope.sandbox.value.example !== void 0 || scope.sandbox.value.default !== void 0)) ? -1 : scope.depth, pos === -1)) return;
if (scope.depth !== pos) return;
if (scope.path[1] !== "schemas") return;
if (!(scope.sandbox.property !== 'properties' && scope.sandbox.value && (scope.sandbox.value && scope.sandbox.value.example !== void 0 || scope.sandbox.value.default !== void 0))) return;
scope.emit("$.components.schemas..[?(@property !== 'properties' && @ && (@ && @.example !== void 0 || @.default !== void 0))]", 0, false);
},
"$..address.street[?(@.number > 20)]": function (scope) {
if (scope.depth < 2) return;
let pos = 0;
if (!(scope.sandbox.value.number > 20)) return;
if (scope.path[scope.depth - 1] !== "street") return;
if (scope.path[scope.depth - 2] !== "address") return;
scope.emit("$..address.street[?(@.number > 20)]", 0, false);
},
"$.bar..[?(@.example && @.schema)].test": function (scope) {
if (scope.depth < 2) return;
let pos = 0;
if (scope.path[0] !== "bar") return;
if (scope.property !== "test") return;
if (!(scope.sandbox.at(-2).value.example && scope.sandbox.at(-2).value.schema)) return;
scope.emit("$.bar..[?(@.example && @.schema)].test", 0, false);
},
"$..[?(@.name && @.name.match(/1_1$/))].name^^": function (scope) {
if (scope.depth < 1) return;
let pos = 0;
if (scope.property !== "name") return;
if (!(scope.sandbox.at(-2).value.name && scope.sandbox.at(-2).value.name.match(/1_1$/))) return;
scope.emit("$..[?(@.name && @.name.match(/1_1$/))].name^^", 2, false);
},
"$.bar[?( @property >= 400 )]..foo": function (scope) {
if (scope.depth < 2) return;
let pos = 0;
if (scope.path[0] !== "bar") return;
if (!(scope.sandbox.at(2).property >= 400)) return;
if (scope.depth < pos + 2 || (pos = scope.property !== "foo" ? -1 : scope.depth, pos === -1)) return;
if (scope.depth !== pos) return;
if (scope.property !== "foo") return;
scope.emit("$.bar[?( @property >= 400 )]..foo", 0, false);
},
"$.paths..content.*.examples": function (scope) {
if (scope.depth < 3) return;
let pos = 0;
if (scope.path[0] !== "paths") return;
if (scope.property !== "examples") return;
if (scope.path[scope.depth - 2] !== "content") return;
Expand Down Expand Up @@ -530,7 +518,6 @@ export default function (input, callbacks) {
const tree = {
"$..examples.*": function (scope) {
if (scope.depth < 1) return;
let pos = 0;
if (scope.path[scope.depth - 1] !== "examples") return;
scope.emit("$..examples.*", 0, false);
},
Expand All @@ -548,10 +535,7 @@ const tree = {
},
"$.examples..*": function (scope) {
if (scope.depth < 1) return;
let pos = 0;
if (scope.path[0] !== "examples") return;
if ((pos = scope.depth < 1 ? -1 : scope.depth, pos === -1)) return;
if (scope.depth !== pos) return;
scope.emit("$.examples..*", 0, false);
},
"$.examples.*": function (scope) {
Expand Down Expand Up @@ -731,10 +715,8 @@ export default function (input, callbacks) {
const tree = {
"$.info..[?(@property.startsWith('foo'))]": function (scope) {
if (scope.depth < 1) return;
let pos = 0;
if (scope.path[0] !== "info") return;
if (scope.depth < pos + 1 || (pos = !String(scope.sandbox.property).startsWith('foo') ? -1 : scope.depth, pos === -1)) return;
if (scope.depth !== pos) return;
if (!String(scope.sandbox.property).startsWith('foo')) return;
scope.emit("$.info..[?(@property.startsWith('foo'))]", 0, false);
},
"$.info.*[?(@property.startsWith('foo'))]": function (scope) {
Expand Down Expand Up @@ -790,10 +772,8 @@ const zones = {
const tree = {
"$.store..[price,bar,baz]": function (scope) {
if (scope.depth < 1) return;
let pos = 0;
if (scope.path[0] !== "store") return;
if ((scope.depth < pos + 1 || (pos = scope.property !== "price" ? -1 : scope.depth, pos === -1)) && (scope.depth < pos + 1 || (pos = scope.property !== "bar" ? -1 : scope.depth, pos === -1)) && (scope.depth < pos + 1 || (pos = scope.property !== "baz" ? -1 : scope.depth, pos === -1))) return;
if (scope.depth !== pos) return;
if (scope.property !== "price" && scope.property !== "bar" && scope.property !== "baz") return;
scope.emit("$.store..[price,bar,baz]", 0, false);
},
"$.book": function (scope) {
Expand Down Expand Up @@ -835,15 +815,13 @@ const zones = {
const tree = {
"$.paths[*][*]..content[*].examples[*]": function (scope) {
if (scope.depth < 6) return;
let pos = 0;
if (scope.path[0] !== "paths") return;
if (scope.path[scope.depth - 1] !== "examples") return;
if (scope.path[scope.depth - 3] !== "content") return;
scope.emit("$.paths[*][*]..content[*].examples[*]", 0, false);
},
"$.paths[*][*]..parameters[*].examples[*]": function (scope) {
if (scope.depth < 6) return;
let pos = 0;
if (scope.path[0] !== "paths") return;
if (scope.path[scope.depth - 1] !== "examples") return;
if (scope.path[scope.depth - 3] !== "parameters") return;
Expand Down Expand Up @@ -884,11 +862,9 @@ const zones = {
const tree = {
"$.data[*][*][city,street]..id": function (scope) {
if (scope.depth < 4) return;
let pos = 0;
if (scope.path[0] !== "data") return;
if ((scope.depth < pos + 3 || scope.path[pos + 3] !== "city") && (scope.depth < pos + 3 || scope.path[pos + 3] !== "street")) return;
if (scope.depth < pos + 4 || (pos = scope.property !== "id" ? -1 : scope.depth, pos === -1)) return;
if (scope.depth !== pos) return;
if (scope.path[3] !== "city" && scope.path[3] !== "street") return;
if (scope.property !== "id") return;
scope.emit("$.data[*][*][city,street]..id", 0, false);
}
};
Expand Down
5 changes: 5 additions & 0 deletions src/codegen/baseline/generators.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export function generateMemberExpression(iterator, { deep, value }) {
const isLastNode =
iterator.nextNode === null || iterator.nextNode === 'KeyExpression';

iterator.feedback.mutatesPos ||= !isLastNode;

const right = b.sequenceExpression([
b.assignmentExpression(
'=',
Expand Down Expand Up @@ -278,6 +280,9 @@ export function generateFilterScriptExpression(iterator, { deep, value }) {

if (iterator.feedback.bailed || !deep || iterator.state.inverted) return node;

iterator.feedback.mutatesPos ||=
iterator.nextNode !== null && iterator.nextNode !== 'KeyExpression';

const assignment = b.sequenceExpression([
b.assignmentExpression(
'=',
Expand Down
2 changes: 2 additions & 0 deletions src/codegen/baseline/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as b from '../ast/builders.mjs';
import fastPaths from '../fast-paths/index.mjs';
import { isDeep } from '../guards.mjs';
import Iterator from '../iterator.mjs';
import optimizer from '../optimizer/index.mjs';
import generateEmitCall from '../templates/emit-call.mjs';
import fnParams from '../templates/fn-params.mjs';
import internalScope from '../templates/internal-scope.mjs';
Expand Down Expand Up @@ -197,6 +198,7 @@ export default function baseline(jsonPaths, opts) {
tree.push(b.stringLiteral(id), placement);
}

optimizer(branch, iterator);
tree.push(b.blockStatement(branch), 'tree-method');

zone?.attach();
Expand Down
5 changes: 4 additions & 1 deletion src/codegen/iterator.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ export default class Iterator {
}
}

Object.assign(this.feedback, feedback);
Object.assign(this.feedback, {
...feedback,
mutatesPos: this.feedback.mutatesPos,
});
}
}
92 changes: 92 additions & 0 deletions src/codegen/optimizer/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import internalScope from '../templates/internal-scope.mjs';
import scope from '../templates/scope.mjs';

function dropNode(branch, i) {
branch.splice(i, 1);
return i - 1;
}

function leftOrRight(node, left, right) {
if (left === null) {
return right;
} else if (right === null) {
return left;
}

node.left = left;
node.right = right;

return node;
}

function reduceBinaryExpression(node) {
if (node.operator === '<' && node.left === scope.depth) {
return null;
}

return leftOrRight(node, eliminate(node.left), eliminate(node.right));
}

function eliminate(node) {
switch (node.type) {
case 'AssignmentExpression':
if (node.left !== internalScope.pos) {
return node;
}

return eliminate(node.right);
case 'ConditionalExpression':
if (
node.consequent.type === 'NumericLiteral' &&
node.consequent.value === -1
) {
return eliminate(node.test);
}

return node;
case 'SequenceExpression':
return eliminate(node.expressions[0]);
case 'LogicalExpression':
return leftOrRight(node, eliminate(node.left), eliminate(node.right));
case 'BinaryExpression':
return reduceBinaryExpression(node);
case 'IfStatement':
return eliminate(node.test);
case 'Identifier':
if (node === internalScope.pos) {
return null;
}

return node;
case 'MemberExpression':
node.property = eliminate(node.property);
return node;
default:
return node;
}
}

export default function optimizer(branch, iterator) {
if (iterator.feedback.mutatesPos) return;

let i = Math.max(0, Math.min(1, iterator.length));

for (; i < branch.length; i++) {
const node = branch[i];
if (
node.type === 'VariableDeclaration' &&
node.kind === 'let' &&
node.declarations[0].id === internalScope.pos
) {
i = dropNode(branch, i);
continue;
}

const test = eliminate(node);
if (test === null || test === scope.depth) {
i = dropNode(branch, i);
} else {
node.test = test;
}
}
}

0 comments on commit c85e8ac

Please sign in to comment.