aran
Version:
JavaScript instrumenter
1,366 lines (1,335 loc) • 41.6 kB
JavaScript
import {
EMPTY,
concatXX,
concat_,
concat__,
concat___,
concat____,
filterNarrow,
flat,
flatMap,
flatenTree,
hasOwn,
map,
tuple2,
NULL_SEQUENCE,
bindSequence,
callSequence_X_,
callSequence__X_,
callSequence___X,
flatSequence,
liftSequenceX,
liftSequenceXX,
liftSequenceXX__,
liftSequenceX_,
liftSequenceX_X,
liftSequenceX_X_,
liftSequenceX__,
liftSequenceX___,
liftSequence_X_,
liftSequence_X__,
liftSequence__X,
liftSequence__XX_,
liftSequence__X_,
liftSequence__X__,
mapSequence,
zeroSequence,
reduceEntry,
} from "../../util/index.mjs";
import { AranExecError, AranTypeError } from "../../error.mjs";
import {
makeAccessorDescriptor,
makeAccessorDescriptorExpression,
makeBinaryExpression,
makeDataDescriptor,
makeDataDescriptorExpression,
makeGetExpression,
makeThrowErrorExpression,
} from "../intrinsic.mjs";
import {
listEffectStatement,
makeApplyExpression,
makeBlockStatement,
makeRoutineBlock,
makeConditionalEffect,
makeConditionalExpression,
makeExpressionEffect,
makeIntrinsicExpression,
makePrimitiveExpression,
makeSequenceExpression,
makeClosureExpression,
makeTreeSegmentBlock,
makeTreeRoutineBlock,
} from "../node.mjs";
import { transExpression, transNameExpression } from "./expression.mjs";
import { transFunction } from "./function.mjs";
import { transKey } from "./key.mjs";
import { transBody } from "./statement.mjs";
import {
makeReadCacheExpression,
makeWriteCacheEffect,
cacheConstant,
cacheWritable,
} from "../cache.mjs";
import { hoist } from "../annotation/index.mjs";
import { forkMeta, nextMeta } from "../meta.mjs";
import { makeNameExpression } from "../name.mjs";
import { cacheKey, convertKey, makeKeyExpression } from "../key.mjs";
import {
incorporateExpression,
incorporateStatement,
incorporateSegmentBlock,
incorporateRoutineBlock,
initSyntaxErrorExpression,
} from "../prelude/index.mjs";
import { INITIAL_STATEMENT_LABELING } from "../labeling.mjs";
import {
extendClosureRoutine,
extendIllegalVariable,
extendNormalRegularVariable,
extendPrivate,
extendProxyVariable,
extendStaticBlockRoutine,
extendStrict,
listCallSuperEffect,
listDefinePrivateEffect,
listInitializePrivateEffect,
listInitializeVariableEffect,
listRegisterCollectionPrivateEffect,
listRegisterSingletonPrivateEffect,
makeCallSuperOperation,
makeDefinePrivateOperation,
makeFinalizeResultExpression,
makeInitializePrivateOperation,
makeInitVariableOperation,
makeReadInputExpression,
makeReadThisExpression,
makeRegisterCollectionPrivateOperation,
} from "../scope/index.mjs";
const ARGUMENTS = /** @type {import("estree-sentry").VariableName} */ (
"arguments"
);
/**
* @type {(
* node: (
* | import("estree-sentry").ClassEntry<import("../hash.d.ts").HashProp>
* ),
* ) => node is (
* | import("estree-sentry").MethodDefinition<import("../hash.d.ts").HashProp>
* | import("estree-sentry").PropertyDefinition<import("../hash.d.ts").HashProp>
* )}
*/
const isDefinition = (node) =>
node.type === "PropertyDefinition" || node.type === "MethodDefinition";
/**
* @type {(
* node: (
* | import("estree-sentry").StaticBlock<import("../hash.d.ts").HashProp>
* | import("estree-sentry").PropertyDefinition<import("../hash.d.ts").HashProp>
* | import("estree-sentry").MethodDefinition<import("../hash.d.ts").HashProp>
* ),
* ) => node is import("estree-sentry").ConstructorMethodDefinition<import("../hash.d.ts").HashProp>}
*/
const isConstructor = (node) =>
node.type === "MethodDefinition" && node.kind === "constructor";
/**
* @type {(
* node: (
* | import("estree-sentry").ClassEntry<import("../hash.d.ts").HashProp>
* ),
* ) => node is (
* | import("estree-sentry").PlainMethodDefinition<import("../hash.d.ts").HashProp>
* | import("estree-sentry").GetterMethodDefinition<import("../hash.d.ts").HashProp>
* | import("estree-sentry").SetterMethodDefinition<import("../hash.d.ts").HashProp>
* )}
*/
const isMethodDefinition = (node) =>
node.type === "MethodDefinition" && node.kind !== "constructor";
/**
* @type {(
* node: import("estree-sentry").ClassEntry<import("../hash.d.ts").HashProp>,
* ) => node is import("../estree.d.ts").StaticPrelude<import("../hash.d.ts").HashProp>}
*/
const isStaticPrelude = (node) =>
node.type === "StaticBlock" ||
(node.type === "PropertyDefinition" && node.static);
/**
* @type {(
* node: import("estree-sentry").ClassEntry<import("../hash.d.ts").HashProp>,
* ) => node is import("../estree.d.ts").InstancePropertyDefinition<import("../hash.d.ts").HashProp>}
*/
const isInstancePrelude = (node) =>
node.type === "PropertyDefinition" && !node.static;
//////////////
// Key Pass //
//////////////
/**
* @type {(
* node: import("estree-sentry").ClassBody<import("../hash.d.ts").HashProp>,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* ) => import("../../util/sequence.d.ts").Sequence<
* (
* | import("../prelude/index.d.ts").BodyPrelude
* | import("../prelude/index.d.ts").PrefixPrelude
* ),
* { [k in import("../hash.d.ts").Hash] ?: import("../key.d.ts").Key },
* >}
*/
const transKeyPass = (node, meta, scope) =>
mapSequence(
flatSequence(
map(filterNarrow(node.body, isDefinition), (node) =>
mapSequence(
bindSequence(
transKey(
node.key,
forkMeta((meta = nextMeta(meta))),
scope,
node.computed,
),
(key) =>
cacheKey(
node._hash,
forkMeta((meta = nextMeta(meta))),
convertKey(node._hash, key),
),
),
(key) => tuple2(node._hash, key),
),
),
),
reduceEntry,
);
//////////////////
// Private Pass //
//////////////////
const PRIVATE_KIND = {
method: /** @type {"method"} */ ("method"),
get: /** @type {"getter"} */ ("getter"),
set: /** @type {"setter"} */ ("setter"),
};
/**
* @type {(
* node: import("estree-sentry").ClassEntry<import("../hash.d.ts").HashProp>,
* ) => [
* import("estree-sentry").PrivateKeyName,
* import("../scope/private/index.d.ts").PrivateKind,
* ][]}
*/
const listPrivateEntry = (node) => {
switch (node.type) {
case "StaticBlock": {
return [];
}
case "MethodDefinition": {
if (
node.key.type === "PrivateIdentifier" &&
node.kind !== "constructor"
) {
return [
[
node.key.name,
`${node.static ? "singleton" : "collection"}-${
PRIVATE_KIND[node.kind]
}`,
],
];
} else {
return [];
}
}
case "PropertyDefinition": {
if (node.key.type === "PrivateIdentifier") {
return [
[
node.key.name,
`${node.static ? "singleton" : "collection"}-property`,
],
];
} else {
return [];
}
}
default: {
throw new AranTypeError(node);
}
}
};
/**
* @type {(
* node: import("estree-sentry").ClassBody<import("../hash.d.ts").HashProp>,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* ) => import("../../util/sequence.d.ts").Sequence<
* (
* | import("../prelude/index.d.ts").BodyPrelude
* | import("../prelude/index.d.ts").PrefixPrelude
* ),
* import("../scope/index.d.ts").Scope,
* >}
*/
const transPrivatePass = (node, meta, scope) =>
extendPrivate(node._hash, meta, flatMap(node.body, listPrivateEntry), scope);
//////////////////////
// Constructor Pass //
//////////////////////
/**
* @type {(
* hash: import("../hash.d.ts").Hash,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* options: {
* derived: boolean,
* field: import("../cache.d.ts").Cache,
* name: import("../name.d.ts").Name,
* },
* ) => import("../../util/sequence.d.ts").Sequence<
* import("../prelude/index.d.ts").BodyPrelude,
* import("../atom.d.ts").Expression,
* >}
*/
const makeDefaultConstructor = (hash, meta, scope, { derived, field, name }) =>
bindSequence(
cacheWritable(forkMeta((meta = nextMeta(meta))), "aran.deadzone_symbol"),
(self) =>
liftSequenceX__(
makeSequenceExpression,
liftSequenceX_X_(
concat____,
liftSequence_X_(
makeWriteCacheEffect,
self,
// no yield|await here
liftSequence__X_(
makeClosureExpression,
"function",
false,
incorporateRoutineBlock(
bindSequence(
extendClosureRoutine(
hash,
forkMeta((meta = nextMeta(meta))),
{
type: "constructor",
generator: false,
asynchronous: false,
derived,
field,
self,
},
scope,
),
(scope) =>
liftSequence__XX_(
makeRoutineBlock,
EMPTY,
null,
liftSequenceX(
flatenTree,
derived
? liftSequenceX_(
listEffectStatement,
callSequence___X(
listCallSuperEffect,
hash,
forkMeta((meta = nextMeta(meta))),
scope,
liftSequenceX(
makeCallSuperOperation,
makeReadInputExpression(
hash,
forkMeta((meta = nextMeta(meta))),
scope,
{},
),
),
),
hash,
)
: NULL_SEQUENCE,
),
makeFinalizeResultExpression(
hash,
forkMeta((meta = nextMeta(meta))),
scope,
{ result: null },
),
hash,
),
),
hash,
),
hash,
),
hash,
),
makeExpressionEffect(
makeApplyExpression(
makeIntrinsicExpression("Reflect.defineProperty", hash),
makeIntrinsicExpression("undefined", hash),
[
makeReadCacheExpression(self, hash),
makePrimitiveExpression("length", hash),
makeDataDescriptorExpression(
{
value: makePrimitiveExpression(0, hash),
writable: false,
enumerable: false,
configurable: true,
},
hash,
),
],
hash,
),
hash,
),
liftSequenceX_(
makeExpressionEffect,
liftSequence__X_(
makeApplyExpression,
makeIntrinsicExpression("Reflect.defineProperty", hash),
makeIntrinsicExpression("undefined", hash),
liftSequence__X(
concat___,
makeReadCacheExpression(self, hash),
makePrimitiveExpression("name", hash),
liftSequenceX_(
makeDataDescriptorExpression,
liftSequenceX___(
makeDataDescriptor,
makeNameExpression(
hash,
forkMeta((meta = nextMeta(meta))),
name,
),
false,
false,
true,
),
hash,
),
),
hash,
),
hash,
),
makeExpressionEffect(
makeApplyExpression(
makeIntrinsicExpression("Reflect.defineProperty", hash),
makeIntrinsicExpression("undefined", hash),
[
makeReadCacheExpression(self, hash),
makePrimitiveExpression("prototype", hash),
makeDataDescriptorExpression(
{
value: makeApplyExpression(
makeIntrinsicExpression("Object.defineProperty", hash),
makeIntrinsicExpression("undefined", hash),
[
makeApplyExpression(
makeIntrinsicExpression("Object.create", hash),
makeIntrinsicExpression("undefined", hash),
[makeIntrinsicExpression("Object.prototype", hash)],
hash,
),
makePrimitiveExpression("constructor", hash),
makeDataDescriptorExpression(
{
value: makeReadCacheExpression(self, hash),
writable: true,
enumerable: false,
configurable: true,
},
hash,
),
],
hash,
),
writable: false,
enumerable: false,
configurable: false,
},
hash,
),
],
hash,
),
hash,
),
),
makeReadCacheExpression(self, hash),
hash,
),
);
/**
* @type {(
* node: import("estree-sentry").ClassBody<import("../hash.d.ts").HashProp>,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* options: {
* derived: boolean,
* name: import("../name.d.ts").Name,
* field: import("../cache.d.ts").Cache,
* },
* ) => import("../../util/sequence.d.ts").Sequence<
* import("../prelude/index.d.ts").BodyPrelude,
* import("../atom.d.ts").Expression,
* >}
*/
const transConstructorPass = (node, meta, scope, { derived, name, field }) => {
const { _hash: hash } = node;
const constructor_node_array = filterNarrow(node.body, isConstructor);
if (constructor_node_array.length === 0) {
return makeDefaultConstructor(hash, meta, scope, {
derived,
name,
field,
});
} else if (constructor_node_array.length === 1) {
return transFunction(
constructor_node_array[0].value,
meta,
scope,
{ type: "constructor", derived, field },
name,
);
} else {
return initSyntaxErrorExpression("Multiple constructor definitions", hash);
}
};
/////////////////
// Method Pass //
/////////////////
const METHOD_KIND = {
method: /** @type {"init"} */ ("init"),
get: /** @type {"get"} */ ("get"),
set: /** @type {"set"} */ ("set"),
};
/**
* @type {(
* node: (
* | import("estree-sentry").PlainMethodDefinition<import("../hash.d.ts").HashProp>
* | import("estree-sentry").GetterMethodDefinition<import("../hash.d.ts").HashProp>
* | import("estree-sentry").SetterMethodDefinition<import("../hash.d.ts").HashProp>
* ),
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* options: {
* key: import("../key.d.ts").Key,
* konstructor: import("../cache.d.ts").Cache,
* prototype: 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>,
* >}
*/
const transMethod = (node, meta, scope, { key, konstructor, prototype }) => {
const { _hash: hash } = node;
const method = transFunction(
node.value,
meta,
scope,
{
type: "method",
proto: node.static ? konstructor : prototype,
},
{
type: "property",
kind: METHOD_KIND[node.kind],
key,
},
);
switch (key.access) {
case "private": {
return callSequence___X(
listInitializePrivateEffect,
hash,
meta,
scope,
liftSequence__X(
makeInitializePrivateOperation,
PRIVATE_KIND[node.kind],
key.data,
method,
),
);
}
case "public": {
return liftSequenceX(
concat_,
liftSequenceX___(
makeConditionalEffect,
liftSequence__X_(
makeApplyExpression,
makeIntrinsicExpression("Reflect.defineProperty", hash),
makeIntrinsicExpression("undefined", hash),
liftSequence__X(
concat___,
makeReadCacheExpression(
node.static ? konstructor : prototype,
hash,
),
makeKeyExpression(hash, key),
node.kind === "method"
? liftSequenceX_(
makeDataDescriptorExpression,
liftSequenceX___(
makeDataDescriptor,
method,
true,
false,
true,
),
hash,
)
: liftSequenceX_(
makeAccessorDescriptorExpression,
liftSequenceXX__(
makeAccessorDescriptor,
node.kind === "get" ? method : zeroSequence(null),
node.kind === "set" ? method : zeroSequence(null),
false,
true,
),
hash,
),
),
hash,
),
EMPTY,
[
makeExpressionEffect(
makeThrowErrorExpression(
"TypeError",
"Cannot assign method",
hash,
),
hash,
),
],
hash,
),
);
}
default: {
throw new AranTypeError(key);
}
}
};
/**
* @type {(
* node: import("estree-sentry").ClassBody<import("../hash.d.ts").HashProp>,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* options: {
* keys: {
* [k in import("../hash.d.ts").Hash] ?: import("../key.d.ts").Key
* },
* konstructor: import("../cache.d.ts").Cache,
* prototype: 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>,
* >}
*/
const transMethodPass = (node, meta, scope, { keys, konstructor, prototype }) =>
flatSequence(
map(filterNarrow(node.body, isMethodDefinition), (node) => {
const { _hash: hash } = node;
if (hasOwn(keys, hash)) {
return transMethod(node, forkMeta((meta = nextMeta(meta))), scope, {
key: keys[hash],
konstructor,
prototype,
});
} else {
throw new AranExecError("missing key", { hash, keys });
}
}),
);
///////////////////
// Property Pass //
///////////////////
/**
* @type {(
* node: import("estree-sentry").PropertyDefinition<import("../hash.d.ts").HashProp>,
* ) => node is import("estree-sentry").PropertyDefinition<import("../hash.d.ts").HashProp> & {
* value: import("estree-sentry").Expression<import("../hash.d.ts").HashProp>,
* }}
*/
const hasPropertyValue = (node) => node.value != null;
/**
* @type {(
* node: import("estree-sentry").PropertyDefinition<import("../hash.d.ts").HashProp>,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* key: import("../key.d.ts").Key,
* ) => import("../../util/sequence.d.ts").Sequence<
* import("../prelude/index.d.ts").BodyPrelude,
* import("../../util/tree.d.ts").Tree<import("../atom.d.ts").Effect>,
* >}
*/
const transProperty = (node, meta, scope, key) => {
const { _hash: hash } = node;
const value = hasPropertyValue(node)
? transNameExpression(
node.value,
forkMeta((meta = nextMeta(meta))),
scope,
{ type: "property", kind: "init", key },
)
: zeroSequence(makeIntrinsicExpression("undefined", hash));
switch (key.access) {
case "private": {
return callSequence___X(
listDefinePrivateEffect,
hash,
forkMeta((meta = nextMeta(meta))),
scope,
liftSequenceX_X(
makeDefinePrivateOperation,
makeReadThisExpression(
hash,
forkMeta((meta = nextMeta(meta))),
scope,
{},
),
key.data,
value,
),
);
}
case "public": {
return liftSequenceX(
concat_,
liftSequenceX___(
makeConditionalEffect,
liftSequence__X_(
makeApplyExpression,
makeIntrinsicExpression("Reflect.defineProperty", hash),
makeIntrinsicExpression("undefined", hash),
liftSequenceX_X(
concat___,
makeReadThisExpression(
hash,
forkMeta((meta = nextMeta(meta))),
scope,
{},
),
makeKeyExpression(hash, key),
liftSequenceX_(
makeDataDescriptorExpression,
liftSequenceX___(makeDataDescriptor, value, true, true, true),
hash,
),
),
hash,
),
[],
[
makeExpressionEffect(
makeThrowErrorExpression(
"TypeError",
"Cannot assign instance property",
hash,
),
hash,
),
],
hash,
),
);
}
default: {
throw new AranTypeError(key);
}
}
};
/**
* @type {(
* node: import("../estree.d.ts").StaticPrelude<import("../hash.d.ts").HashProp>,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* keys: {[k in import("../hash.d.ts").Hash] ?: import("../key.d.ts").Key },
* ) => import("../../util/sequence.d.ts").Sequence<
* import("../prelude/index.d.ts").BodyPrelude,
* import("../../util/tree.d.ts").Tree<import("../atom.d.ts").Statement>,
* >}
*/
const transClassPrelude = (node, meta, scope, keys) => {
const { _hash: hash } = node;
switch (node.type) {
case "StaticBlock": {
return liftSequenceX(
concat_,
liftSequenceX_(
makeBlockStatement,
incorporateSegmentBlock(
liftSequence__X_(
makeTreeSegmentBlock,
EMPTY,
EMPTY,
incorporateStatement(
callSequence__X_(
transBody,
node.body,
forkMeta((meta = nextMeta(meta))),
callSequence___X(
extendStaticBlockRoutine,
hash,
forkMeta((meta = nextMeta(meta))),
{},
callSequence___X(
extendIllegalVariable,
hash,
forkMeta((meta = nextMeta(meta))),
{
record: { [ARGUMENTS]: "static property initializer" },
},
extendNormalRegularVariable(
hash,
forkMeta((meta = nextMeta(meta))),
{ bindings: hoist(hash, scope.annotation) },
scope,
),
),
),
INITIAL_STATEMENT_LABELING,
),
hash,
),
hash,
),
hash,
),
hash,
),
);
}
case "PropertyDefinition": {
if (hasOwn(keys, hash)) {
return liftSequenceX_(
listEffectStatement,
transProperty(node, meta, scope, keys[hash]),
hash,
);
} else {
throw new AranExecError("missing key", { hash, keys });
}
}
default: {
throw new AranTypeError(node);
}
}
};
/**
* @type {(
* node: import("estree-sentry").ClassBody<import("../hash.d.ts").HashProp>,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* options: {
* keys: {
* [k in import("../hash.d.ts").Hash] ?: import("../key.d.ts").Key
* },
* konstructor: import("../cache.d.ts").Cache,
* },
* ) => import("../../util/sequence.d.ts").Sequence<
* import("../prelude/index.d.ts").BodyPrelude,
* import("../atom.d.ts").Expression,
* >}
*/
const transClassPreludePass = (node, meta, scope, { keys, konstructor }) => {
const { _hash: hash } = node;
return liftSequence__X_(
makeClosureExpression,
"function",
false,
incorporateRoutineBlock(
bindSequence(
callSequence___X(
extendClosureRoutine,
hash,
forkMeta((meta = nextMeta(meta))),
{
type: /** @type {"method"} */ ("method"),
proto: konstructor,
asynchronous: false,
generator: false,
},
extendIllegalVariable(
hash,
forkMeta((meta = nextMeta(meta))),
{ record: { [ARGUMENTS]: "class property initializer" } },
scope,
),
),
(scope) =>
liftSequence__X__(
makeTreeRoutineBlock,
[],
null,
flatSequence(
map(filterNarrow(node.body, isStaticPrelude), (node) =>
transClassPrelude(
node,
forkMeta((meta = nextMeta(meta))),
scope,
keys,
),
),
),
makeIntrinsicExpression("undefined", hash),
hash,
),
),
hash,
),
hash,
);
};
/**
* @type {(
* node: import("estree-sentry").ClassBody<import("../hash.d.ts").HashProp>,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* options: {
* keys: {[k in import("../hash.d.ts").Hash] ?: import("../key.d.ts").Key},
* prototype: import("../cache.d.ts").Cache,
* },
* ) => import("../../util/sequence.d.ts").Sequence<
* import("../prelude/index.d.ts").BodyPrelude,
* import("../atom.d.ts").Expression,
* >}
*/
const transInstancePreludePass = (
node,
meta,
scope,
{ keys, prototype }, // await|yield forbidden here
) => {
const { _hash: hash } = node;
return liftSequence__X_(
makeClosureExpression,
"function",
false,
incorporateRoutineBlock(
bindSequence(
callSequence___X(
extendClosureRoutine,
hash,
forkMeta((meta = nextMeta(meta))),
{
type: /** @type {"method"} */ ("method"),
proto: prototype,
asynchronous: false,
generator: false,
},
extendIllegalVariable(
hash,
forkMeta((meta = nextMeta(meta))),
{ record: { [ARGUMENTS]: "instance property initializer" } },
scope,
),
),
(scope) =>
liftSequence__X__(
makeRoutineBlock,
[],
null,
liftSequenceXX(
concatXX,
liftSequenceX_(
listEffectStatement,
callSequence___X(
listRegisterCollectionPrivateEffect,
hash,
forkMeta((meta = nextMeta(meta))),
scope,
liftSequenceX(
makeRegisterCollectionPrivateOperation,
makeReadThisExpression(
hash,
forkMeta((meta = nextMeta(meta))),
scope,
{},
),
),
),
hash,
),
liftSequenceX(
flat,
flatSequence(
map(filterNarrow(node.body, isInstancePrelude), (node) => {
const { _hash: hash } = node;
if (hasOwn(keys, hash)) {
return liftSequenceX_(
listEffectStatement,
transProperty(
node,
forkMeta((meta = nextMeta(meta))),
scope,
keys[hash],
),
hash,
);
} else {
throw new AranExecError("missing key", { hash, keys });
}
}),
),
),
),
makeIntrinsicExpression("undefined", hash),
hash,
),
),
hash,
),
hash,
);
};
//////////
// Main //
//////////
/**
* @type {(
* node: import("estree-sentry").ClassBody<import("../hash.d.ts").HashProp>,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* options: {
* zuper: import("../cache.d.ts").Cache | null,
* keys: {
* [k in import("../hash.d.ts").Hash] ?: import("../key.d.ts").Key
* },
* field: import("../cache.d.ts").WritableCache,
* konstructor: import("../cache.d.ts").Cache,
* prototype: 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>,
* >}
*/
const transClassInner = (
node,
meta,
scope,
{ zuper, keys, field, konstructor, prototype },
) => {
const { _hash: hash } = node;
return flatSequence([
listRegisterSingletonPrivateEffect(
hash,
forkMeta((meta = nextMeta(meta))),
scope,
{ target: makeReadCacheExpression(konstructor, hash) },
),
zeroSequence(
zuper === null
? null
: makeExpressionEffect(
makeApplyExpression(
makeIntrinsicExpression("Reflect.setPrototypeOf", hash),
makeIntrinsicExpression("undefined", hash),
[
makeReadCacheExpression(prototype, hash),
makeConditionalExpression(
makeBinaryExpression(
"==",
makeReadCacheExpression(zuper, hash),
makePrimitiveExpression(null, hash),
hash,
),
makePrimitiveExpression(null, hash),
makeGetExpression(
makeReadCacheExpression(zuper, hash),
makePrimitiveExpression("prototype", hash),
hash,
),
hash,
),
],
hash,
),
hash,
),
),
zeroSequence(
makeExpressionEffect(
makeApplyExpression(
makeIntrinsicExpression("Reflect.setPrototypeOf", hash),
makeIntrinsicExpression("undefined", hash),
[
makeReadCacheExpression(konstructor, hash),
zuper === null
? makeIntrinsicExpression("Function.prototype", hash)
: makeConditionalExpression(
makeBinaryExpression(
"===",
makeReadCacheExpression(zuper, hash),
makePrimitiveExpression(null, hash),
hash,
),
makeIntrinsicExpression("Function.prototype", hash),
makeReadCacheExpression(zuper, hash),
hash,
),
],
hash,
),
hash,
),
),
transMethodPass(node, forkMeta((meta = nextMeta(meta))), scope, {
keys,
konstructor,
prototype,
}),
liftSequenceX_(
makeExpressionEffect,
liftSequenceX___(
makeApplyExpression,
transClassPreludePass(node, forkMeta((meta = nextMeta(meta))), scope, {
keys,
konstructor,
}),
makeReadCacheExpression(konstructor, hash),
[],
hash,
),
hash,
),
liftSequence_X_(
makeWriteCacheEffect,
field,
transInstancePreludePass(node, forkMeta((meta = nextMeta(meta))), scope, {
keys,
prototype,
}),
hash,
),
]);
};
/**
* @type {(
* node: import("estree-sentry").ClassBody<import("../hash.d.ts").HashProp>,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* options: {
* id: null | import("estree-sentry").VariableIdentifier<unknown>,
* zuper: import("../cache.d.ts").Cache | null,
* name: import("../name.d.ts").Name,
* },
* ) => import("../../util/sequence.d.ts").Sequence<
* import("../prelude/index.d.ts").BodyPrelude,
* import("../atom.d.ts").Expression,
* >}
*/
const transClassBody = (node, meta, scope, { id, name, zuper }) => {
const { _hash: hash } = node;
return incorporateExpression(
bindSequence(
transPrivatePass(node, forkMeta((meta = nextMeta(meta))), scope),
(scope) =>
bindSequence(
transKeyPass(node, forkMeta((meta = nextMeta(meta))), scope),
(keys) =>
bindSequence(
cacheWritable(forkMeta((meta = nextMeta(meta))), "undefined"),
(field) =>
bindSequence(
callSequence_X_(
cacheConstant,
forkMeta((meta = nextMeta(meta))),
transConstructorPass(
node,
forkMeta((meta = nextMeta(meta))),
scope,
{ name, derived: zuper !== null, field },
),
hash,
),
(konstructor) =>
bindSequence(
cacheConstant(
forkMeta((meta = nextMeta(meta))),
makeGetExpression(
makeReadCacheExpression(konstructor, hash),
makePrimitiveExpression("prototype", hash),
hash,
),
hash,
),
(prototype) =>
liftSequenceX__(
makeSequenceExpression,
liftSequenceX(
flatenTree,
liftSequenceXX(
concat__,
id == null
? NULL_SEQUENCE
: mapSequence(
callSequence___X(
listInitializeVariableEffect,
hash,
forkMeta((meta = nextMeta(meta))),
scope,
makeInitVariableOperation(
hash,
scope.mode,
id.name,
makeReadCacheExpression(
konstructor,
hash,
),
),
),
(pair) => {
// eslint-disable-next-line local/no-impure
scope = pair[1];
return pair[0];
},
),
transClassInner(
node,
forkMeta((meta = nextMeta(meta))),
scope,
{ zuper, keys, field, konstructor, prototype },
),
),
),
makeReadCacheExpression(konstructor, hash),
hash,
),
),
),
),
),
),
hash,
);
};
/**
* @type {(
* frame: import("../annotation/hoisting.d.ts").FrameEntry[],
* ) => import("estree-sentry").VariableName | null}
*/
const getSelfVariable = (bindings) => {
if (bindings.length === 0) {
return null;
} else if (bindings.length === 1) {
const { 0: variable, 1: kinds } = bindings[0];
if (kinds.length !== 1) {
throw new AranExecError("Self class binding should only have one kind", {
bindings,
});
}
const kind = kinds[0];
if (kind !== "class-self") {
throw new AranExecError(
"Self class binding should be of kind class-self",
{ bindings },
);
}
return variable;
} else {
throw new AranExecError(
"Class frame should only have one entry (the self binding)",
{ bindings },
);
}
};
/**
* @type {(
* node: import("estree-sentry").Class<import("../hash.d.ts").HashProp>,
* meta: import("../meta.d.ts").Meta,
* scope: import("../scope/index.d.ts").Scope,
* name: import("../name.d.ts").Name,
* ) => import("../../util/sequence.d.ts").Sequence<
* import("../prelude/index.d.ts").BodyPrelude,
* import("../atom.d.ts").Expression,
* >}
*/
export const transClass = (node, meta, parent_scope, name) => {
const { _hash: hash } = node;
const scope = extendStrict(parent_scope);
const self_variable = getSelfVariable(hoist(hash, scope.annotation));
return incorporateExpression(
bindSequence(
cacheWritable(forkMeta((meta = nextMeta(meta))), "aran.deadzone_symbol"),
(self) =>
bindSequence(
extendProxyVariable(
hash,
forkMeta((meta = nextMeta(meta))),
{
bindings:
self_variable === null
? []
: [[self_variable, { proxy: self, kind: "class-self" }]],
},
extendStrict(scope),
),
(scope) =>
node.superClass != null
? bindSequence(
callSequence_X_(
cacheConstant,
forkMeta((meta = nextMeta(meta))),
transExpression(
node.superClass,
forkMeta((meta = nextMeta(meta))),
scope,
),
hash,
),
(zuper) =>
liftSequence_X__(
makeConditionalExpression,
makeConditionalExpression(
makeBinaryExpression(
"===",
makeReadCacheExpression(zuper, hash),
makePrimitiveExpression(null, hash),
hash,
),
makePrimitiveExpression(true, hash),
makeApplyExpression(
makeIntrinsicExpression("aran.isConstructor", hash),
makeIntrinsicExpression("undefined", hash),
[makeReadCacheExpression(zuper, hash)],
hash,
),
hash,
),
transClassBody(
node.body,
forkMeta((meta = nextMeta(meta))),
scope,
{ id: node.id, name, zuper },
),
makeThrowErrorExpression(
"TypeError",
"parent class should be a constructor",
hash,
),
hash,
),
)
: transClassBody(
node.body,
forkMeta((meta = nextMeta(meta))),
scope,
{ id: node.id, name, zuper: null },
),
),
),
hash,
);
};