aran
Version:
JavaScript instrumenter
738 lines (727 loc) • 23.8 kB
JavaScript
/* eslint-disable no-use-before-define */
import {
EMPTY,
concat_,
concat___,
everyNarrow,
flatenTree,
some,
tuple2,
EMPTY_SEQUENCE,
bindSequence,
callSequence_X_,
callSequence____X,
liftSequenceX,
liftSequenceX_,
liftSequence_X_,
liftSequence_X__,
liftSequence__X_,
mapSequence,
liftSequenceXX,
concat__,
flatSequence,
map,
liftSequence_XX,
liftSequence_X,
callSequence___X,
} from "../../util/index.mjs";
import { AranExecError, AranTypeError } from "../../error.mjs";
import {
makeApplyExpression,
makeConditionalEffect,
makeConditionalExpression,
makeExpressionEffect,
makeIntrinsicExpression,
makePrimitiveExpression,
} from "../node.mjs";
import {
makeArrayExpression,
makeBinaryExpression,
makeGetExpression,
makeThrowErrorExpression,
} from "../intrinsic.mjs";
import { transNameExpression } from "./expression.mjs";
import { transKey } from "./key.mjs";
import {
cacheConstant,
cacheWritable,
makeWriteCacheEffect,
makeReadCacheExpression,
} from "../cache.mjs";
import { listSetMemberEffect } from "../member.mjs";
import {
listNextIteratorEffect,
listReturnIteratorEffect,
makeNextIteratorExpression,
} from "../helper.mjs";
import { transObject } from "./object.mjs";
import { forkMeta, nextMeta } from "../meta.mjs";
import {
incorporateEffect,
incorporateFirstEffect,
initSyntaxErrorExpression,
} from "../prelude/index.mjs";
import { convertKey, duplicateKey, makePublicKeyExpression } from "../key.mjs";
import { ANONYMOUS_NAME } from "../name.mjs";
import {
listInitializeVariableEffect,
listWriteVariableEffect,
makeInitVariableOperation,
makeSaveVariableOperation,
} from "../scope/index.mjs";
/**
* @type {(
* node: null | import("estree-sentry").Node<import("../hash.d.ts").HashProp>,
* ) => node is import("estree-sentry").RestElement<import("../hash.d.ts").HashProp>}
*/
const isRestElement = (node) => node !== null && node.type === "RestElement";
/**
* @type {(
* node: (
* | import("estree-sentry").PatternProperty<import("../hash.d.ts").HashProp>
* | import("estree-sentry").RestElement<import("../hash.d.ts").HashProp>
* ),
* ) => node is import("estree-sentry").PatternProperty<import("../hash.d.ts").HashProp>}
*/
const isPatternProperty = (node) => node.type === "Property";
/**
* @type {(
* site: import("estree-sentry").RestablePattern<import("../hash.d.ts").HashProp>,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* kind: "write" | "initialize",
* iterator: import("../cache.d.ts").Cache,
* next: import("../cache.d.ts").Cache,
* done: import("../cache.d.ts").WritableCache,
* ) => import("../../util/sequence.d.ts").Sequence<
* import("../prelude/index.d.ts").BodyPrelude,
* [
* import("../../util/tree.d.ts").Tree<import("../atom.d.ts").Effect>,
* import("../scope/index.d.ts").Scope,
* ],
* >}
*/
const transItem = (node, meta, scope, kind, iterator, next, done) => {
const { _hash: hash } = node;
if (node.type === "RestElement") {
return transPattern(
node.argument,
meta,
scope,
kind,
makeConditionalExpression(
makeReadCacheExpression(done, hash),
makeArrayExpression([], hash),
makeApplyExpression(
makeIntrinsicExpression("aran.listIteratorRest", hash),
makeIntrinsicExpression("undefined", hash),
[
makeReadCacheExpression(iterator, hash),
makeReadCacheExpression(next, hash),
],
hash,
),
hash,
),
);
} else {
return callSequence____X(
transPattern,
node,
forkMeta((meta = nextMeta(meta))),
scope,
kind,
makeNextIteratorExpression(hash, forkMeta((meta = nextMeta(meta))), {
asynchronous: false,
iterator,
next,
done,
}),
);
}
};
/**
* @type {(
* node: import("estree-sentry").PatternProperty<import("../hash.d.ts").HashProp>,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* kind: "write" | "initialize",
* object: import("../cache.d.ts").Cache,
* ) => import("../../util/sequence.d.ts").Sequence<
* import("../prelude/index.d.ts").BodyPrelude,
* [
* import("../../util/tree.d.ts").Tree<import("../atom.d.ts").Effect>,
* import("../scope/index.d.ts").Scope,
* ],
* >}
*/
const transProperty = (node, meta, scope, kind, object) => {
const { _hash: hash } = node;
return incorporateFirstEffect(
bindSequence(
callSequence_X_(
cacheConstant,
forkMeta((meta = nextMeta(meta))),
bindSequence(
transKey(
node.key,
forkMeta((meta = nextMeta(meta))),
scope,
node.computed,
),
(key) =>
makePublicKeyExpression(hash, convertKey(hash, key), {
message: "Illegal private key in destructuring pattern",
}),
),
hash,
),
(key) =>
transPattern(
node.value,
forkMeta((meta = nextMeta(meta))),
scope,
kind,
makeGetExpression(
makeReadCacheExpression(object, hash),
makeReadCacheExpression(key, hash),
hash,
),
),
),
hash,
);
};
/**
* @type {(
* node: import("estree-sentry").PatternProperty<import("../hash.d.ts").HashProp>,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* kind: "write" | "initialize",
* object: import("../cache.d.ts").Cache,
* keys: import("../cache.d.ts").Cache,
* ) => import("../../util/sequence.d.ts").Sequence<
* import("../prelude/index.d.ts").BodyPrelude,
* [
* import("../../util/tree.d.ts").Tree<import("../atom.d.ts").Effect>,
* import("../scope/index.d.ts").Scope,
* ],
* >}
*/
// https://262.ecma-international.org/14.0#sec-destructuring-binding-patterns-runtime-semantics-restbindinginitialization
const transRestProperty = (node, meta, scope, kind, object, keys) => {
const { _hash: hash } = node;
return liftSequenceX_(
tuple2,
incorporateEffect(
bindSequence(
bindSequence(
mapSequence(
transKey(
node.key,
forkMeta((meta = nextMeta(meta))),
scope,
node.computed,
),
(key) => convertKey(hash, key),
),
(key) => duplicateKey(hash, forkMeta((meta = nextMeta(meta))), key),
),
([key1, key2]) =>
liftSequenceXX(
concat__,
liftSequenceX_(
makeExpressionEffect,
liftSequence__X_(
makeApplyExpression,
makeIntrinsicExpression("Reflect.set", hash),
makeIntrinsicExpression("undefined", hash),
liftSequence_X_(
concat___,
makeReadCacheExpression(keys, hash),
makePublicKeyExpression(hash, key1, {
message: "Illegal private key in destructuring pattern",
}),
makePrimitiveExpression(null, hash),
),
hash,
),
hash,
),
mapSequence(
callSequence____X(
transPattern,
node.value,
forkMeta((meta = nextMeta(meta))),
scope,
kind,
liftSequence_X_(
makeGetExpression,
makeReadCacheExpression(object, hash),
makePublicKeyExpression(hash, key2, {
message: "Illegal private key in destructuring pattern",
}),
hash,
),
),
(pair) => {
// eslint-disable-next-line local/no-impure
scope = pair[1];
return pair[0];
},
),
),
),
hash,
),
scope,
);
};
/**
* @type {(
* node: import("estree-sentry").RestElement<import("../hash.d.ts").HashProp>,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* kind: "write" | "initialize",
* object: import("../cache.d.ts").Cache,
* keys: import("../cache.d.ts").Cache,
* ) => import("../../util/sequence.d.ts").Sequence<
* import("../prelude/index.d.ts").BodyPrelude,
* [
* import("../../util/tree.d.ts").Tree<import("../atom.d.ts").Effect>,
* import("../scope/index.d.ts").Scope,
* ],
* >}
*/
// https://262.ecma-international.org/14.0#sec-destructuring-binding-patterns-runtime-semantics-restbindinginitialization
const transRestObject = (node, meta, scope, kind, object, keys) => {
const { _hash: hash } = node;
return transPattern(
node.argument,
meta,
scope,
kind,
makeApplyExpression(
makeIntrinsicExpression("aran.sliceObject", hash),
makeIntrinsicExpression("undefined", hash),
[
makeApplyExpression(
makeIntrinsicExpression("Object", hash),
makeIntrinsicExpression("undefined", hash),
[makeReadCacheExpression(object, hash)],
hash,
),
makeReadCacheExpression(keys, hash),
],
hash,
),
);
};
/**
* @type {(
* node: import("estree-sentry").RestablePattern<import("../hash.d.ts").HashProp>,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* kind: "write" | "initialize",
* right: import("../atom.d.ts").Expression,
* ) => import("../../util/sequence.d.ts").Sequence<
* import("../prelude/index.d.ts").BodyPrelude,
* [
* import("../../util/tree.d.ts").Tree<import("../atom.d.ts").Effect>,
* import("../scope/index.d.ts").Scope,
* ],
* >}
*/
export const transPattern = (node, meta, scope, kind, right) => {
const { _hash: hash } = node;
switch (node.type) {
case "Identifier": {
switch (kind) {
case "write": {
return liftSequenceX_(
tuple2,
callSequence___X(
listWriteVariableEffect,
hash,
meta,
scope,
makeSaveVariableOperation(hash, scope.mode, node.name, right),
),
scope,
);
}
case "initialize": {
return callSequence___X(
listInitializeVariableEffect,
hash,
meta,
scope,
makeInitVariableOperation(hash, scope.mode, node.name, right),
);
}
default: {
throw new AranTypeError(kind);
}
}
}
case "MemberExpression": {
return liftSequenceX_(
tuple2,
bindSequence(
transObject(node.object, forkMeta((meta = nextMeta(meta))), scope),
(object) =>
bindSequence(
transKey(
node.property,
forkMeta((meta = nextMeta(meta))),
scope,
node.computed,
),
(key) =>
listSetMemberEffect(
hash,
forkMeta((meta = nextMeta(meta))),
scope,
{ object, key, value: right },
),
),
),
scope,
);
}
case "AssignmentPattern": {
return liftSequenceX_(
tuple2,
incorporateEffect(
bindSequence(
cacheConstant(forkMeta((meta = nextMeta(meta))), right, hash),
(right) =>
mapSequence(
callSequence____X(
transPattern,
node.left,
forkMeta((meta = nextMeta(meta))),
scope,
kind,
liftSequence_X__(
makeConditionalExpression,
makeBinaryExpression(
"===",
makeReadCacheExpression(right, hash),
makeIntrinsicExpression("undefined", hash),
hash,
),
transNameExpression(
node.right,
forkMeta((meta = nextMeta(meta))),
scope,
node.left.type === "Identifier"
? {
type: "assignment",
variable: node.left.name,
}
: ANONYMOUS_NAME,
),
makeReadCacheExpression(right, hash),
hash,
),
),
(pair) => {
// eslint-disable-next-line local/no-impure
scope = pair[1];
return pair[0];
},
),
),
hash,
),
scope,
);
}
case "ArrayPattern": {
/**
* @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 liftSequenceX_(
tuple2,
incorporateEffect(
bindSequence(
cacheConstant(forkMeta((meta = nextMeta(meta))), right, hash),
(right) =>
bindSequence(
cacheConstant(
forkMeta((meta = nextMeta(meta))),
makeApplyExpression(
makeGetExpression(
makeReadCacheExpression(right, hash),
makeIntrinsicExpression("Symbol.iterator", hash),
hash,
),
makeReadCacheExpression(right, hash),
[],
hash,
),
hash,
),
(iterator) =>
bindSequence(
cacheConstant(
forkMeta((meta = nextMeta(meta))),
makeGetExpression(
makeReadCacheExpression(iterator, hash),
makePrimitiveExpression("next", hash),
hash,
),
hash,
),
(next) =>
bindSequence(
cacheWritable(
forkMeta((meta = nextMeta(meta))),
"aran.deadzone_symbol",
),
(done) =>
liftSequence_XX(
concat___,
makeWriteCacheEffect(
done,
makePrimitiveExpression(false, hash),
hash,
),
flatSequence(
map(node.elements, (node) => {
if (node === null) {
return listNextIteratorEffect(
hash,
forkMeta((meta = nextMeta(meta))),
{
asynchronous: false,
iterator,
next,
done,
},
);
} else {
return liftSequenceX(
updateScope,
transItem(
node,
forkMeta((meta = nextMeta(meta))),
scope,
kind,
iterator,
next,
done,
),
);
}
}),
),
// Not only it is not necessary to return the iterator
// But the 'next' variable will be outdated and `next.done`
// might be false which would cause the iterator to return.
some(node.elements, isRestElement)
? EMPTY_SEQUENCE
: liftSequenceX(
concat_,
liftSequence__X_(
makeConditionalEffect,
makeReadCacheExpression(done, hash),
EMPTY,
liftSequenceX(
flatenTree,
listReturnIteratorEffect(
hash,
forkMeta((meta = nextMeta(meta))),
{ iterator },
),
),
hash,
),
),
),
),
),
),
),
hash,
),
scope,
);
}
case "ObjectPattern": {
/**
* @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 liftSequenceX_(
tuple2,
incorporateEffect(
bindSequence(
cacheConstant(forkMeta((meta = nextMeta(meta))), right, hash),
(right) =>
liftSequence_X(
concat__,
makeConditionalEffect(
makeBinaryExpression(
"==",
makeReadCacheExpression(right, hash),
makePrimitiveExpression(null, hash),
hash,
),
[
makeExpressionEffect(
makeThrowErrorExpression(
"TypeError",
"Cannot destructure nullish value",
hash,
),
hash,
),
],
EMPTY,
hash,
),
everyNarrow(node.properties, isPatternProperty)
? flatSequence(
map(node.properties, (node) =>
liftSequenceX(
updateScope,
transProperty(
node,
forkMeta((meta = nextMeta(meta))),
scope,
kind,
right,
),
),
),
)
: incorporateEffect(
bindSequence(
cacheConstant(
forkMeta((meta = nextMeta(meta))),
makeApplyExpression(
makeIntrinsicExpression("aran.createObject", hash),
makeIntrinsicExpression("undefined", hash),
[makePrimitiveExpression(null, hash)],
hash,
),
hash,
),
(keys) =>
flatSequence(
map(node.properties, (node) => {
switch (node.type) {
case "Property": {
return liftSequenceX(
updateScope,
transRestProperty(
node,
forkMeta((meta = nextMeta(meta))),
scope,
kind,
right,
keys,
),
);
}
case "RestElement": {
return liftSequenceX(
updateScope,
transRestObject(
node,
forkMeta((meta = nextMeta(meta))),
scope,
kind,
right,
keys,
),
);
}
default: {
throw new AranTypeError(node);
}
}
}),
),
),
hash,
),
),
),
hash,
),
scope,
);
}
case "RestElement": {
return liftSequenceX_(
tuple2,
liftSequenceX_(
makeExpressionEffect,
initSyntaxErrorExpression("Illegal rest element", hash),
hash,
),
scope,
);
}
default: {
throw new AranTypeError(node);
}
}
};
/**
* @type {(
* node: import("estree-sentry").RestablePattern<import("../hash.d.ts").HashProp>,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* right: import("../atom.d.ts").Expression,
* ) => import("../../util/sequence.d.ts").Sequence<
* import("../prelude/index.d.ts").BodyPrelude,
* import("../../util/tree.d.ts").Tree<import("../atom.d.ts").Effect>,
* >}
*/
export const transWritePattern = (node, meta, scope1, right) =>
mapSequence(
transPattern(node, meta, scope1, "write", right),
({ 0: part, 1: scope2 }) => {
if (scope1 !== scope2) {
throw new AranExecError(
"scope should not have been modified in assignment",
{ node, scope1, scope2 },
);
} else {
return part;
}
},
);
/**
* @type {(
* node: import("estree-sentry").RestablePattern<import("../hash.d.ts").HashProp>,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* right: import("../atom.d.ts").Expression,
* ) => import("../../util/sequence.d.ts").Sequence<
* import("../prelude/index.d.ts").BodyPrelude,
* [
* import("../../util/tree.d.ts").Tree<import("../atom.d.ts").Effect>,
* import("../scope/index.d.ts").Scope,
* ],
* >}
*/
export const transInitializePattern = (node, meta, scope, right) =>
transPattern(node, meta, scope, "initialize", right);