aran
Version:
JavaScript instrumenter
1,314 lines (1,305 loc) • 112 kB
JavaScript
/* eslint-disable no-use-before-define */
import {
EMPTY,
compileGet,
concatXX,
concat_,
concat__,
concat___,
findFirstIndex,
flatMap,
guard,
hasOwn,
map,
mapIndex,
tuple2,
slice,
some,
get0,
someTree,
isTreeEmpty,
hasTree,
mapTree,
concat_____,
flatenTree,
concat____,
EMPTY_SEQUENCE,
NULL_SEQUENCE,
bindSequence,
callSequence_X_,
callSequence__X_,
callSequence___X,
flatSequence,
liftSequenceX,
liftSequenceXX,
liftSequenceXXXXX,
liftSequenceXXX_,
liftSequenceXX_,
liftSequenceX_,
liftSequenceX_X_,
liftSequence_X,
liftSequence_XXX,
liftSequence_X_,
liftSequence_X__,
liftSequence__X,
liftSequence__X_,
listenSequence,
mapSequence,
zeroSequence,
} from "../../util/index.mjs";
import { AranTypeError } from "../../error.mjs";
import {
makeUpdateResultOperation,
listInitializeVariableEffect,
listUpdateResultEffect,
listWriteSloppyFunctionVariableEffect,
extendWithVariable,
extendCatch,
makeReadErrorExpression,
extendSwitchRegularVariable,
extendNormalRegularVariable,
makeReadVariableExpression,
makeInitVariableOperation,
} from "../scope/index.mjs";
import {
mangleBreakLabel,
mangleContinueLabel,
mangleEmptyBreakLabel,
mangleEmptyContinueLabel,
RETURN_BREAK_LABEL,
} from "../mangle.mjs";
import {
listEffectStatement,
makeApplyExpression,
makeAwaitExpression,
makeBlockStatement,
makeBreakStatement,
makeConditionalEffect,
makeConditionalExpression,
makeSegmentBlock,
makeDebuggerStatement,
makeEffectStatement,
makeExpressionEffect,
makeIfStatement,
makeIntrinsicExpression,
makePrimitiveExpression,
makeReadExpression,
makeSequenceExpression,
makeTryStatement,
makeWhileStatement,
makeTreeSegmentBlock,
listExpressionEffect,
} from "../node.mjs";
import {
listResetCompletionStatement,
transSegmentBody,
transFinallyBody,
} from "./body.mjs";
import { transCatch } from "./catch.mjs";
import { transExpression } from "./expression.mjs";
import { hasEmptyBreak, hasEmptyContinue } from "../query/index.mjs";
import {
transWriteDeclarator,
transInitializeDeclarator,
} from "./declarator.mjs";
import { transEffect } from "./effect.mjs";
import {
makeBinaryExpression,
makeGetExpression,
makeThrowErrorExpression,
} from "../intrinsic.mjs";
import { transCase } from "./case.mjs";
import { transClass } from "./class.mjs";
import { transDefault } from "./default.mjs";
import {
transRegularInit,
transLexicalInit,
transLeftBody,
transLeftHead,
} from "./left.mjs";
import { transHoistedStatement } from "./hoisted.mjs";
import {
makeReadCacheExpression,
makeWriteCacheEffect,
cacheConstant,
cacheWritable,
} from "../cache.mjs";
import { makeIsProperObjectExpression } from "../helper.mjs";
import { forkMeta, nextMeta } from "../meta.mjs";
import {
incorporateSegmentBlock,
incorporateEffect,
incorporateStatement,
initSyntaxErrorExpression,
isBaseDeclarationPrelude,
} from "../prelude/index.mjs";
import { hoist, isCompletion } from "../annotation/index.mjs";
/**
* @type {(
* labels: import("../../util/tree.d.ts").Tree<import("../atom.d.ts").Label>,
* body: import("../../util/sequence.d.ts").Sequence<
* import("../prelude/index.d.ts").BodyPrelude,
* import("../../util/tree.d.ts").Tree<import("../atom.d.ts").Statement>
* >,
* hash: import("../hash.d.ts").Hash,
* ) => import("../../util/sequence.d.ts").Sequence<
* import("../prelude/index.d.ts").BodyPrelude,
* import("../../util/tree.d.ts").Tree<import("../atom.d.ts").Statement>
* >}
*/
export const listControlStatement = (labels, body, hash) => {
if (
!isTreeEmpty(labels) ||
someTree(listenSequence(body), isBaseDeclarationPrelude)
) {
return liftSequenceX(
concat_,
liftSequenceX_(
makeBlockStatement,
incorporateSegmentBlock(
liftSequence__X_(makeTreeSegmentBlock, labels, EMPTY, body, hash),
hash,
),
hash,
),
);
} else {
// This may seem unsafe because it moves meta variables in parent scope.
// But control flow only occurs in proper block.
// So this can only be used in naked block and hence it is safe.
return body;
}
};
/**
* @type {(
* node: import("estree-sentry").ReturnStatement<import("../hash.d.ts").HashProp>,
* ) => node is import("estree-sentry").ReturnStatement<import("../hash.d.ts").HashProp> & {
* argument: import("estree-sentry").Expression<import("../hash.d.ts").HashProp>,
* }}
*/
const hasReturnArgument = (node) => node.argument != null;
/**
* @type {(
* node: import("estree-sentry").ExportNamedDeclaration<import("../hash.d.ts").HashProp>,
* ) => node is import("estree-sentry").ExportNamedDeclaration<import("../hash.d.ts").HashProp> & {
* declaration: (
* | import("estree-sentry").FunctionDeclaration<import("../hash.d.ts").HashProp>
* | import("estree-sentry").ClassDeclaration<import("../hash.d.ts").HashProp>
* | import("estree-sentry").VariableDeclaration<import("../hash.d.ts").HashProp>
* ),
* }}
*/
const hasExportDeclaration = (node) => node.declaration != null;
/**
* @type {<X>(
* node: import("estree-sentry").Node<X>,
* ) => node is import("../estree.d.ts").LexicalForStatement<X>}
*/
const isLexicalForStatement = (node) =>
node.type === "ForStatement" &&
node.init != null &&
node.init.type === "VariableDeclaration" &&
(node.init.kind === "let" || node.init.kind === "const");
const getConsequent = compileGet("consequent");
/**
* @type {<X>(
* node: import("estree-sentry").SwitchCase<X>
* ) => boolean}
*/
const isDefaultSwitchCase = (node) => node.test == null;
/**
* @type {<X>(
* node: import("estree-sentry").SwitchStatement<X>,
* ) => import("estree-sentry").Statement<X>[]}
*/
export const listSwitchRemainder = (node) => {
const index = findFirstIndex(node.cases, isDefaultSwitchCase);
if (index === -1 || index === node.cases.length - 1) {
return EMPTY;
} else {
return flatMap(slice(node.cases, index, node.cases.length), getConsequent);
}
};
/**
* @type {(
* node: import("estree-sentry").Statement<import("../hash.d.ts").HashProp>,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* labeling: import("../labeling.d.ts").StatementLabeling,
* ) => import("../../util/sequence.d.ts").Sequence<
* import("../prelude/index.d.ts").BodyPrelude,
* import("../../util/tree.d.ts").Tree<import("../atom.d.ts").Statement>,
* >}
*/
export const transStatement = (node, meta, scope, { labels, loop }) => {
const { _hash: hash } = node;
switch (node.type) {
case "EmptyStatement": {
return NULL_SEQUENCE;
}
case "DebuggerStatement": {
return zeroSequence(makeDebuggerStatement(hash));
}
case "ReturnStatement": {
return liftSequenceX_(
concat__,
liftSequenceX_(
listEffectStatement,
callSequence___X(
listUpdateResultEffect,
hash,
forkMeta((meta = nextMeta(meta))),
scope,
liftSequence_X(
makeUpdateResultOperation,
"closure",
hasReturnArgument(node)
? transExpression(
node.argument,
forkMeta((meta = nextMeta(meta))),
scope,
)
: zeroSequence(makeIntrinsicExpression("undefined", hash)),
),
),
hash,
),
makeBreakStatement(RETURN_BREAK_LABEL, hash),
);
}
case "ExpressionStatement": {
if (isCompletion(hash, scope.annotation)) {
return liftSequenceX_(
listEffectStatement,
callSequence___X(
listUpdateResultEffect,
hash,
forkMeta((meta = nextMeta(meta))),
scope,
liftSequence_X(
makeUpdateResultOperation,
"program",
transExpression(node.expression, meta, scope),
),
),
hash,
);
} else {
return liftSequenceX_(
listEffectStatement,
transEffect(node.expression, meta, scope),
hash,
);
}
}
case "ThrowStatement": {
return liftSequenceX_(
makeEffectStatement,
liftSequenceX_(
makeExpressionEffect,
liftSequence__X_(
makeApplyExpression,
makeIntrinsicExpression("aran.throwException", hash),
makeIntrinsicExpression("undefined", hash),
liftSequenceX(concat_, transExpression(node.argument, meta, scope)),
hash,
),
hash,
),
hash,
);
}
case "BreakStatement": {
if (node.label == null) {
if (loop.break == null) {
return liftSequenceX_(
makeEffectStatement,
liftSequenceX_(
makeExpressionEffect,
initSyntaxErrorExpression("Illegal break statement", hash),
hash,
),
hash,
);
} else {
return zeroSequence([makeBreakStatement(loop.break, hash)]);
}
} else {
if (hasTree(labels, node.label.name)) {
return NULL_SEQUENCE;
} else {
return zeroSequence(
makeBreakStatement(mangleBreakLabel(node.label.name), hash),
);
}
}
}
case "ContinueStatement": {
if (node.label == null) {
if (loop.continue === null) {
return liftSequenceX_(
makeEffectStatement,
liftSequenceX_(
makeExpressionEffect,
initSyntaxErrorExpression("Illegal continue statement", hash),
hash,
),
hash,
);
} else {
return zeroSequence([makeBreakStatement(loop.continue, hash)]);
}
} else {
if (hasTree(labels, node.label.name)) {
return NULL_SEQUENCE;
} else {
return zeroSequence(
makeBreakStatement(mangleContinueLabel(node.label.name), hash),
);
}
}
}
case "VariableDeclaration": {
if (node.kind === "var") {
return liftSequenceX_(
listEffectStatement,
flatSequence(
map(node.declarations, (node) =>
transWriteDeclarator(
node,
forkMeta((meta = nextMeta(meta))),
scope,
),
),
),
hash,
);
} else {
return liftSequenceX_(
makeEffectStatement,
liftSequenceX_(
makeExpressionEffect,
initSyntaxErrorExpression(
`Illegal ${node.kind} variable declaration in a single-statement this context`,
hash,
),
hash,
),
hash,
);
}
}
case "FunctionDeclaration": {
const mode = scope.mode;
switch (mode) {
case "strict": {
return NULL_SEQUENCE;
}
case "sloppy": {
if (node.async || node.generator) {
return NULL_SEQUENCE;
} else {
// In global scope, functions are not re-assigned.
// f = 123; function f () {} throw { message: typeof f } // number
// This is ugly because it breaks abstractions.
// And this the only place where output node is queried.
const right = makeReadVariableExpression(
hash,
forkMeta((meta = nextMeta(meta))),
scope,
{ variable: node.id.name },
);
if (right.value.type === "ReadExpression") {
return liftSequenceX_(
listEffectStatement,
callSequence___X(
listWriteSloppyFunctionVariableEffect,
hash,
forkMeta((meta = nextMeta(meta))),
scope,
callSequence___X(
makeInitVariableOperation,
hash,
scope.mode,
node.id.name,
right,
),
),
hash,
);
} else {
return NULL_SEQUENCE;
}
}
}
default: {
throw new AranTypeError(mode);
}
}
}
case "ClassDeclaration": {
return liftSequenceX_(
makeEffectStatement,
liftSequenceX_(
makeExpressionEffect,
initSyntaxErrorExpression(
"Illegal class declaration in a single-statement context",
hash,
),
hash,
),
hash,
);
}
case "LabeledStatement": {
return transStatement(node.body, meta, scope, {
labels: [labels, node.label.name],
loop,
});
}
case "BlockStatement": {
return liftSequenceX_(
makeBlockStatement,
transSegmentBody(node, meta, scope, {
labels: mapTree(labels, mangleBreakLabel),
loop,
}),
hash,
);
}
case "WithStatement": {
const mode = scope.mode;
if (mode === "strict") {
return liftSequenceX_(
makeEffectStatement,
liftSequenceX_(
makeExpressionEffect,
initSyntaxErrorExpression("'with' is illegal in strict mode", hash),
hash,
),
hash,
);
} else if (mode === "sloppy") {
return liftSequenceXX(
concat__,
listResetCompletionStatement(
hash,
forkMeta((meta = nextMeta(meta))),
scope,
),
incorporateStatement(
bindSequence(
callSequence_X_(
cacheConstant,
forkMeta((meta = nextMeta(meta))),
transExpression(
node.object,
forkMeta((meta = nextMeta(meta))),
scope,
),
hash,
),
(frame) =>
bindSequence(
cacheConstant(
forkMeta((meta = nextMeta(meta))),
makeConditionalExpression(
makeBinaryExpression(
"==",
makeReadCacheExpression(frame, hash),
makePrimitiveExpression(null, hash),
hash,
),
makeThrowErrorExpression(
"TypeError",
"Cannot convert undefined or null to object",
hash,
),
makeApplyExpression(
makeIntrinsicExpression("Object", hash),
makeIntrinsicExpression("undefined", hash),
[makeReadCacheExpression(frame, hash)],
hash,
),
hash,
),
hash,
),
(record) =>
liftSequenceX(
concat_,
liftSequenceX_(
makeBlockStatement,
callSequence__X_(
transSegmentBody,
node.body,
forkMeta((meta = nextMeta(meta))),
extendWithVariable(
hash,
forkMeta((meta = nextMeta(meta))),
{ record },
scope,
),
{ labels: mapTree(labels, mangleBreakLabel), loop },
),
hash,
),
),
),
),
hash,
),
);
} else {
throw new AranTypeError(mode);
}
}
case "IfStatement": {
return liftSequenceXX(
concat__,
listResetCompletionStatement(
hash,
forkMeta((meta = nextMeta(meta))),
scope,
),
liftSequenceXXX_(
makeIfStatement,
transExpression(node.test, forkMeta((meta = nextMeta(meta))), scope),
transSegmentBody(
node.consequent,
forkMeta((meta = nextMeta(meta))),
scope,
{ labels: mapTree(labels, mangleBreakLabel), loop },
),
node.alternate != null
? transSegmentBody(
node.alternate,
forkMeta((meta = nextMeta(meta))),
scope,
{ labels: mapTree(labels, mangleBreakLabel), loop },
)
: zeroSequence(makeSegmentBlock(EMPTY, EMPTY, EMPTY, hash)),
hash,
),
);
}
case "TryStatement": {
return liftSequenceXXX_(
makeTryStatement,
transSegmentBody(node.block, forkMeta((meta = nextMeta(meta))), scope, {
labels: mapTree(labels, mangleBreakLabel),
loop,
}),
node.handler != null
? transCatch(node.handler, forkMeta((meta = nextMeta(meta))), scope, {
labels: mapTree(labels, mangleBreakLabel),
loop,
})
: incorporateSegmentBlock(
bindSequence(
extendCatch(hash, forkMeta((meta = nextMeta(meta))), {}, scope),
(scope) =>
liftSequence__X_(
makeTreeSegmentBlock,
EMPTY,
EMPTY,
liftSequenceX_(
makeEffectStatement,
liftSequenceX_(
makeExpressionEffect,
liftSequence__X_(
makeApplyExpression,
makeIntrinsicExpression("aran.throwException", hash),
makeIntrinsicExpression("undefined", hash),
liftSequenceX(
concat_,
makeReadErrorExpression(
hash,
forkMeta((meta = nextMeta(meta))),
scope,
{},
),
),
hash,
),
hash,
),
hash,
),
hash,
),
),
hash,
),
//
// Finally completion used only in case of break:
//
// a: try { throw "boum" }
// catch { 123; }
// finally { 456; }
// > 123
//
// a: try { throw "boum"; }
// catch { 123; }
// finally { 456; break a }
// > 456
//
// a: try { throw "boum"; }
// catch { 123; }
// finally { break a }
// > undefined
//
node.finalizer != null
? transFinallyBody(
node.finalizer,
forkMeta((meta = nextMeta(meta))),
scope,
{ labels: mapTree(labels, mangleBreakLabel), loop },
)
: zeroSequence(makeSegmentBlock(EMPTY, EMPTY, EMPTY, hash)),
hash,
);
}
case "WhileStatement": {
const loop = {
break: mangleEmptyBreakLabel(forkMeta((meta = nextMeta(meta)))),
continue: mangleEmptyContinueLabel(forkMeta((meta = nextMeta(meta)))),
};
const inner_label_array = [
hasEmptyContinue(node.body) ? loop.continue : null,
mapTree(labels, mangleContinueLabel),
];
const outer_label_array = [
hasEmptyBreak(node.body) ? loop.break : null,
mapTree(labels, mangleBreakLabel),
];
return liftSequenceXX(
concat__,
listResetCompletionStatement(
hash,
forkMeta((meta = nextMeta(meta))),
scope,
),
listControlStatement(
outer_label_array,
liftSequenceXX_(
makeWhileStatement,
transExpression(
node.test,
forkMeta((meta = nextMeta(meta))),
scope,
),
transSegmentBody(
node.body,
forkMeta((meta = nextMeta(meta))),
scope,
{ labels: inner_label_array, loop },
),
hash,
),
hash,
),
);
}
case "DoWhileStatement": {
const loop = {
break: mangleEmptyBreakLabel(forkMeta((meta = nextMeta(meta)))),
continue: mangleEmptyContinueLabel(forkMeta((meta = nextMeta(meta)))),
};
const inner_label_array = [
hasEmptyContinue(node.body) ? loop.continue : null,
mapTree(labels, mangleContinueLabel),
];
const outer_label_array = [
hasEmptyBreak(node.body) ? loop.break : null,
mapTree(labels, mangleBreakLabel),
];
// We do not reset the completion here because it is reset in the body.
return listControlStatement(
outer_label_array,
bindSequence(
cacheWritable(
forkMeta((meta = nextMeta(meta))),
"aran.deadzone_symbol",
),
(initial) =>
liftSequence_X(
concat__,
makeEffectStatement(
makeWriteCacheEffect(
initial,
makePrimitiveExpression(true, hash),
hash,
),
hash,
),
liftSequenceXX_(
makeWhileStatement,
liftSequence__X_(
makeConditionalExpression,
makeReadCacheExpression(initial, hash),
makeSequenceExpression(
[
makeWriteCacheEffect(
initial,
makePrimitiveExpression(false, hash),
hash,
),
],
makePrimitiveExpression(true, hash),
hash,
),
transExpression(
node.test,
forkMeta((meta = nextMeta(meta))),
scope,
),
hash,
),
transSegmentBody(
node.body,
forkMeta((meta = nextMeta(meta))),
scope,
{ labels: inner_label_array, loop },
),
hash,
),
),
),
hash,
);
}
case "ForStatement": {
const loop = {
break: mangleEmptyBreakLabel(forkMeta((meta = nextMeta(meta)))),
continue: mangleEmptyContinueLabel(forkMeta((meta = nextMeta(meta)))),
};
const outer_label_array = [
hasEmptyBreak(node.body) ? loop.break : null,
mapTree(labels, mangleBreakLabel),
];
const inner_label_array = [
hasEmptyContinue(node.body) ? loop.continue : null,
mapTree(labels, mangleContinueLabel),
];
if (isLexicalForStatement(node)) {
const bindings = hoist(hash, scope.annotation);
return liftSequenceXX(
concat__,
listResetCompletionStatement(
hash,
forkMeta((meta = nextMeta(meta))),
scope,
),
liftSequenceX_(
makeBlockStatement,
incorporateSegmentBlock(
liftSequence__X_(
makeTreeSegmentBlock,
outer_label_array,
EMPTY,
bindSequence(
extendNormalRegularVariable(
hash,
forkMeta((meta = nextMeta(meta))),
{ bindings },
scope,
),
(scope) =>
liftSequenceXX(
concat__,
mapSequence(
transLexicalInit(
node.init,
forkMeta((meta = nextMeta(meta))),
scope,
),
(pair) => {
// eslint-disable-next-line local/no-impure
scope = pair[1];
return pair[0];
},
),
bindSequence(
flatSequence(
map(bindings, ({ 0: variable }) =>
liftSequence_X(
tuple2,
variable,
cacheWritable(
forkMeta((meta = nextMeta(meta))),
"aran.deadzone_symbol",
),
),
),
),
(pairs) =>
liftSequenceXX(
concat__,
liftSequenceX_(
listEffectStatement,
flatSequence(
map(pairs, ({ 0: variable, 1: cache }) =>
liftSequence_X_(
makeWriteCacheEffect,
cache,
makeReadVariableExpression(
hash,
forkMeta((meta = nextMeta(meta))),
scope,
{ variable },
),
hash,
),
),
),
hash,
),
bindSequence(
cacheWritable(
forkMeta((meta = nextMeta(meta))),
"aran.deadzone_symbol",
),
(first) =>
bindSequence(
cacheWritable(
forkMeta((meta = nextMeta(meta))),
"aran.deadzone_symbol",
),
(test) =>
liftSequence__X(
concat___,
makeEffectStatement(
makeWriteCacheEffect(
first,
makePrimitiveExpression(true, hash),
hash,
),
hash,
),
makeEffectStatement(
makeWriteCacheEffect(
test,
makePrimitiveExpression(true, hash),
hash,
),
hash,
),
liftSequence_X_(
makeWhileStatement,
makeConditionalExpression(
makeReadCacheExpression(first, hash),
makePrimitiveExpression(true, hash),
makeReadCacheExpression(test, hash),
hash,
),
incorporateSegmentBlock(
liftSequence__X_(
makeTreeSegmentBlock,
EMPTY,
EMPTY,
bindSequence(
extendNormalRegularVariable(
hash,
forkMeta(
(meta = nextMeta(meta)),
),
{ bindings },
scope,
),
(scope) => {
/** @type {<X>(pair: [X, import("../scope/index.d.ts").Scope]) => X} */
const updateScope = (pair) => {
// eslint-disable-next-line local/no-impure
scope = pair[1];
return pair[0];
};
return liftSequenceXXXXX(
concat_____,
liftSequenceX_(
listEffectStatement,
flatSequence(
map(
pairs,
({
0: variable,
1: cache,
}) =>
mapSequence(
callSequence___X(
listInitializeVariableEffect,
hash,
forkMeta(
(meta =
nextMeta(
meta,
)),
),
scope,
makeInitVariableOperation(
hash,
scope.mode,
variable,
makeReadCacheExpression(
cache,
hash,
),
),
),
updateScope,
),
),
),
hash,
),
liftSequenceX_(
makeEffectStatement,
liftSequence__X_(
makeConditionalEffect,
makeReadCacheExpression(
first,
hash,
),
[
makeWriteCacheEffect(
first,
makePrimitiveExpression(
false,
hash,
),
hash,
),
],
node.update != null
? liftSequenceX(
flatenTree,
liftSequenceX_(
listExpressionEffect,
transExpression(
node.update,
forkMeta(
(meta =
nextMeta(
meta,
)),
),
scope,
),
hash,
),
)
: EMPTY_SEQUENCE,
hash,
),
hash,
),
node.test != null
? liftSequenceX_(
makeEffectStatement,
liftSequence_X_(
makeWriteCacheEffect,
test,
transExpression(
node.test,
forkMeta(
(meta =
nextMeta(meta)),
),
scope,
),
hash,
),
hash,
)
: NULL_SEQUENCE,
liftSequence_X__(
makeIfStatement,
makeReadCacheExpression(
test,
hash,
),
transSegmentBody(
node.body,
forkMeta(
(meta = nextMeta(meta)),
),
scope,
{
labels:
inner_label_array,
loop,
},
),
makeSegmentBlock(
EMPTY,
EMPTY,
EMPTY,
hash,
),
hash,
),
liftSequenceX_(
listEffectStatement,
flatSequence(
map(
pairs,
({
0: variable,
1: cache,
}) =>
liftSequence_X_(
makeWriteCacheEffect,
cache,
makeReadVariableExpression(
hash,
forkMeta(
(meta =
nextMeta(
meta,
)),
),
scope,
{ variable },
),
hash,
),
),
),
hash,
),
);
},
),
hash,
),
hash,
),
hash,
),
),
),
),
),
),
),
),
hash,
),
hash,
),
hash,
),
);
} else {
const ts_node_update = node.update;
return liftSequenceXX(
concat__,
listResetCompletionStatement(
hash,
forkMeta((meta = nextMeta(meta))),
scope,
),
listControlStatement(
outer_label_array,
liftSequenceXX(
concat__,
node.init != null
? transRegularInit(
node.init,
forkMeta((meta = nextMeta(meta))),
scope,
)
: NULL_SEQUENCE,
liftSequenceXX_(
makeWhileStatement,
node.test != null
? transExpression(
node.test,
forkMeta((meta = nextMeta(meta))),
scope,
)
: zeroSequence(makePrimitiveExpression(true, hash)),
ts_node_update != null
? incorporateSegmentBlock(
liftSequence__X_(
makeTreeSegmentBlock,
EMPTY,
EMPTY,
liftSequenceXX(
concat__,
liftSequenceX_(
makeBlockStatement,
transSegmentBody(
node.body,
forkMeta((meta = nextMeta(meta))),
scope,
{ labels: inner_label_array, loop },
),
hash,
),
liftSequenceX_(
listEffectStatement,
transEffect(
ts_node_update,
forkMeta((meta = nextMeta(meta))),
scope,
),
hash,
),
),
hash,
),
hash,
)
: transSegmentBody(
node.body,
forkMeta((meta = nextMeta(meta))),
scope,
{ labels: inner_label_array, loop },
),
hash,
),
),
hash,
),
);
}
}
case "ForInStatement": {
const loop = {
break: mangleEmptyBreakLabel(forkMeta((meta = nextMeta(meta)))),
continue: mangleEmptyContinueLabel(forkMeta((meta = nextMeta(meta)))),
};
const inner_label_array = [
hasEmptyContinue(node.body) ? loop.continue : null,
mapTree(labels, mangleContinueLabel),
];
const outer_label_array = [
hasEmptyBreak(node.body) ? loop.break : EMPTY,
mapTree(labels, mangleBreakLabel),
];
// for ((console.log("obj"), {})[(console.log("prop"), "foo")] in (console.log("right"), {foo:1, bar:2})) {}
//
// Variables in the left hand side belongs to the body of the while but still
// they must be shadowed to the right-hand side.
//
// > for (const x in {a:x, b:2}) { console.log(x) }
// Thrown:
// ReferenceError: Cannot access 'x' before initialization
return liftSequenceXX(
concat__,
listResetCompletionStatement(
hash,
forkMeta((meta = nextMeta(meta))),
scope,
),
listControlStatement(
outer_label_array,
incorporateStatement(
bindSequence(
extendNormalRegularVariable(
hash,
forkMeta((meta = nextMeta(meta))),
{ bindings: hoist(hash, scope.annotation) },
scope,
),
(scope) =>
liftSequenceXX(
concat__,
transLeftHead(
node.left,
forkMeta((meta = nextMeta(meta))),
scope,
),
incorporateStatement(
bindSequence(
callSequence_X_(
cacheConstant,
forkMeta((meta = nextMeta(meta))),
transExpression(
node.right,
forkMeta((meta = nextMeta(meta))),
scope,
),
hash,
),
(right) =>
bindSequence(
cacheConstant(
forkMeta((meta = nextMeta(meta))),
makeApplyExpression(
makeIntrinsicExpression(
"aran.listForInKey",
hash,
),
makeIntrinsicExpression("undefined", hash),
[makeReadCacheExpression(right, hash)],
hash,
),
hash,
),
(keys) =>
bindSequence(
cacheWritable(
forkMeta((meta = nextMeta(meta))),
"aran.deadzone_symbol",
),
(index) =>
liftSequence_X(
concat__,
makeEffectStatement(
makeWriteCacheEffect(
index,
makePrimitiveExpression(0, hash),
hash,
),
hash,
),
liftSequence_X_(
makeWhileStatement,
makeBinaryExpression(
"<",
makeReadCacheExpression(index, hash),
makeGetExpression(
makeReadCacheExpression(keys, hash),
makePrimitiveExpression("length", hash),
hash,
),
hash,
),
incorporateSegmentBlock(
liftSequence__X_(
makeTreeSegmentBlock,
EMPTY,
EMPTY,
bindSequence(
extendNormalRegularVariable(
hash,
forkMeta((meta = nextMeta(meta))),
{
bindings: hoist(
hash,
scope.annotation,
),
},
scope,
),
(scope) =>
liftSequenceXX_(
concat___,
liftSequenceX_(
listEffectStatement,
mapSequence(
transLeftBody(
node.left,
forkMeta(
(meta = nextMeta(meta)),
),
scope,
makeGetExpression(
makeReadCacheExpression(
keys,
hash,
),
makeReadCacheExpression(
index,