import { BindingKind } from "@babel/traverse";
import * as t from "@babel/types";
import { CompilerError, CompilerErrorDetailOptions } from "../CompilerError";
import { assertExhaustive } from "../Utils/utils";
import { Environment, ReactFunctionType } from "./Environment";
import { HookKind } from "./ObjectShape";
import { Type } from "./Types";
export const GeneratedSource = Symbol();
export type SourceLocation = t.SourceLocation | typeof GeneratedSource;
export type ReactiveFunction = {
loc: SourceLocation;
id: string | null;
params: Array<Place | SpreadPattern>;
generator: boolean;
async: boolean;
body: ReactiveBlock;
env: Environment;
directives: Array<string>;
};
export type ReactiveScopeBlock = {
kind: "scope";
scope: ReactiveScope;
instructions: ReactiveBlock;
};
export type PrunedReactiveScopeBlock = {
kind: "pruned-scope";
scope: ReactiveScope;
instructions: ReactiveBlock;
};
export type ReactiveBlock = Array<ReactiveStatement>;
export type ReactiveStatement =
| ReactiveInstructionStatement
| ReactiveTerminalStatement
| ReactiveScopeBlock
| PrunedReactiveScopeBlock;
export type ReactiveInstructionStatement = {
kind: "instruction";
instruction: ReactiveInstruction;
};
export type ReactiveTerminalStatement<
Tterminal extends ReactiveTerminal = ReactiveTerminal,
> = {
kind: "terminal";
terminal: Tterminal;
label: {
id: BlockId;
implicit: boolean;
} | null;
};
export type ReactiveInstruction = {
id: InstructionId;
lvalue: Place | null;
value: ReactiveValue;
loc: SourceLocation;
};
export type ReactiveValue =
| InstructionValue
| ReactiveLogicalValue
| ReactiveSequenceValue
| ReactiveTernaryValue
| ReactiveOptionalCallValue
| ReactiveFunctionValue;
export type ReactiveFunctionValue = {
kind: "ReactiveFunctionValue";
fn: ReactiveFunction;
dependencies: Array<Place>;
returnType: t.FlowType | t.TSType | null;
loc: SourceLocation;
};
export type ReactiveLogicalValue = {
kind: "LogicalExpression";
operator: t.LogicalExpression["operator"];
left: ReactiveValue;
right: ReactiveValue;
loc: SourceLocation;
};
export type ReactiveTernaryValue = {
kind: "ConditionalExpression";
test: ReactiveValue;
consequent: ReactiveValue;
alternate: ReactiveValue;
loc: SourceLocation;
};
export type ReactiveSequenceValue = {
kind: "SequenceExpression";
instructions: Array<ReactiveInstruction>;
id: InstructionId;
value: ReactiveValue;
loc: SourceLocation;
};
export type ReactiveOptionalCallValue = {
kind: "OptionalExpression";
id: InstructionId;
value: ReactiveValue;
optional: boolean;
loc: SourceLocation;
};
export type ReactiveTerminal =
| ReactiveBreakTerminal
| ReactiveContinueTerminal
| ReactiveReturnTerminal
| ReactiveThrowTerminal
| ReactiveSwitchTerminal
| ReactiveDoWhileTerminal
| ReactiveWhileTerminal
| ReactiveForTerminal
| ReactiveForOfTerminal
| ReactiveForInTerminal
| ReactiveIfTerminal
| ReactiveLabelTerminal
| ReactiveTryTerminal;
function _staticInvariantReactiveTerminalHasLocation(
terminal: ReactiveTerminal
): SourceLocation {
return terminal.loc;
}
function _staticInvariantReactiveTerminalHasInstructionId(
terminal: ReactiveTerminal
): InstructionId {
return terminal.id;
}
export type ReactiveTerminalTargetKind = "implicit" | "labeled" | "unlabeled";
export type ReactiveBreakTerminal = {
kind: "break";
target: BlockId;
id: InstructionId;
targetKind: ReactiveTerminalTargetKind;
loc: SourceLocation;
};
export type ReactiveContinueTerminal = {
kind: "continue";
target: BlockId;
id: InstructionId;
targetKind: ReactiveTerminalTargetKind;
loc: SourceLocation;
};
export type ReactiveReturnTerminal = {
kind: "return";
value: Place;
id: InstructionId;
loc: SourceLocation;
};
export type ReactiveThrowTerminal = {
kind: "throw";
value: Place;
id: InstructionId;
loc: SourceLocation;
};
export type ReactiveSwitchTerminal = {
kind: "switch";
test: Place;
cases: Array<{
test: Place | null;
block: ReactiveBlock | void;
}>;
id: InstructionId;
loc: SourceLocation;
};
export type ReactiveDoWhileTerminal = {
kind: "do-while";
loop: ReactiveBlock;
test: ReactiveValue;
id: InstructionId;
loc: SourceLocation;
};
export type ReactiveWhileTerminal = {
kind: "while";
test: ReactiveValue;
loop: ReactiveBlock;
id: InstructionId;
loc: SourceLocation;
};
export type ReactiveForTerminal = {
kind: "for";
init: ReactiveValue;
test: ReactiveValue;
update: ReactiveValue | null;
loop: ReactiveBlock;
id: InstructionId;
loc: SourceLocation;
};
export type ReactiveForOfTerminal = {
kind: "for-of";
init: ReactiveValue;
test: ReactiveValue;
loop: ReactiveBlock;
id: InstructionId;
loc: SourceLocation;
};
export type ReactiveForInTerminal = {
kind: "for-in";
init: ReactiveValue;
loop: ReactiveBlock;
id: InstructionId;
loc: SourceLocation;
};
export type ReactiveIfTerminal = {
kind: "if";
test: Place;
consequent: ReactiveBlock;
alternate: ReactiveBlock | null;
id: InstructionId;
loc: SourceLocation;
};
export type ReactiveLabelTerminal = {
kind: "label";
block: ReactiveBlock;
id: InstructionId;
loc: SourceLocation;
};
export type ReactiveTryTerminal = {
kind: "try";
block: ReactiveBlock;
handlerBinding: Place | null;
handler: ReactiveBlock;
id: InstructionId;
loc: SourceLocation;
};
export type HIRFunction = {
loc: SourceLocation;
id: string | null;
fnType: ReactFunctionType;
env: Environment;
params: Array<Place | SpreadPattern>;
returnType: t.FlowType | t.TSType | null;
context: Array<Place>;
effects: Array<FunctionEffect> | null;
body: HIR;
generator: boolean;
async: boolean;
directives: Array<string>;
};
export type FunctionEffect =
| {
kind: "GlobalMutation";
error: CompilerErrorDetailOptions;
}
| {
kind: "ReactMutation";
error: CompilerErrorDetailOptions;
}
| {
kind: "ContextMutation";
places: ReadonlySet<Place>;
effect: Effect;
loc: SourceLocation;
};
export type HIR = {
entry: BlockId;
blocks: Map<BlockId, BasicBlock>;
};
export type BlockKind = "block" | "value" | "loop" | "sequence" | "catch";
export function isStatementBlockKind(kind: BlockKind): boolean {
return kind === "block" || kind === "catch";
}
export function isExpressionBlockKind(kind: BlockKind): boolean {
return !isStatementBlockKind(kind);
}
export type BasicBlock = {
kind: BlockKind;
id: BlockId;
instructions: Array<Instruction>;
terminal: Terminal;
preds: Set<BlockId>;
phis: Set<Phi>;
};
export type Terminal =
| UnsupportedTerminal
| UnreachableTerminal
| ThrowTerminal
| ReturnTerminal
| GotoTerminal
| IfTerminal
| BranchTerminal
| SwitchTerminal
| ForTerminal
| ForOfTerminal
| ForInTerminal
| DoWhileTerminal
| WhileTerminal
| LogicalTerminal
| TernaryTerminal
| OptionalTerminal
| LabelTerminal
| SequenceTerminal
| MaybeThrowTerminal
| TryTerminal
| ReactiveScopeTerminal
| PrunedScopeTerminal;
export type TerminalWithFallthrough = Terminal & { fallthrough: BlockId };
function _staticInvariantTerminalHasLocation(
terminal: Terminal
): SourceLocation {
return terminal.loc;
}
function _staticInvariantTerminalHasInstructionId(
terminal: Terminal
): InstructionId {
return terminal.id;
}
function _staticInvariantTerminalHasFallthrough(
terminal: Terminal
): BlockId | never | undefined {
return terminal.fallthrough;
}
export type UnsupportedTerminal = {
kind: "unsupported";
id: InstructionId;
loc: SourceLocation;
fallthrough?: never;
};
export type UnreachableTerminal = {
kind: "unreachable";
id: InstructionId;
loc: SourceLocation;
fallthrough?: never;
};
export type ThrowTerminal = {
kind: "throw";
value: Place;
id: InstructionId;
loc: SourceLocation;
fallthrough?: never;
};
export type Case = { test: Place | null; block: BlockId };
export type ReturnTerminal = {
kind: "return";
loc: SourceLocation;
value: Place;
id: InstructionId;
fallthrough?: never;
};
export type GotoTerminal = {
kind: "goto";
block: BlockId;
variant: GotoVariant;
id: InstructionId;
loc: SourceLocation;
fallthrough?: never;
};
export enum GotoVariant {
Break = "Break",
Continue = "Continue",
Try = "Try",
}
export type IfTerminal = {
kind: "if";
test: Place;
consequent: BlockId;
alternate: BlockId;
fallthrough: BlockId;
id: InstructionId;
loc: SourceLocation;
};
export type BranchTerminal = {
kind: "branch";
test: Place;
consequent: BlockId;
alternate: BlockId;
id: InstructionId;
loc: SourceLocation;
fallthrough?: never;
};
export type SwitchTerminal = {
kind: "switch";
test: Place;
cases: Array<Case>;
fallthrough: BlockId;
id: InstructionId;
loc: SourceLocation;
};
export type DoWhileTerminal = {
kind: "do-while";
loop: BlockId;
test: BlockId;
fallthrough: BlockId;
id: InstructionId;
loc: SourceLocation;
};
export type WhileTerminal = {
kind: "while";
loc: SourceLocation;
test: BlockId;
loop: BlockId;
fallthrough: BlockId;
id: InstructionId;
};
export type ForTerminal = {
kind: "for";
loc: SourceLocation;
init: BlockId;
test: BlockId;
update: BlockId | null;
loop: BlockId;
fallthrough: BlockId;
id: InstructionId;
};
export type ForOfTerminal = {
kind: "for-of";
loc: SourceLocation;
init: BlockId;
test: BlockId;
loop: BlockId;
fallthrough: BlockId;
id: InstructionId;
};
export type ForInTerminal = {
kind: "for-in";
loc: SourceLocation;
init: BlockId;
loop: BlockId;
fallthrough: BlockId;
id: InstructionId;
};
export type LogicalTerminal = {
kind: "logical";
operator: t.LogicalExpression["operator"];
test: BlockId;
fallthrough: BlockId;
id: InstructionId;
loc: SourceLocation;
};
export type TernaryTerminal = {
kind: "ternary";
test: BlockId;
fallthrough: BlockId;
id: InstructionId;
loc: SourceLocation;
};
export type LabelTerminal = {
kind: "label";
block: BlockId;
fallthrough: BlockId;
id: InstructionId;
loc: SourceLocation;
};
export type OptionalTerminal = {
kind: "optional";
optional: boolean;
test: BlockId;
fallthrough: BlockId;
id: InstructionId;
loc: SourceLocation;
};
export type SequenceTerminal = {
kind: "sequence";
block: BlockId;
fallthrough: BlockId;
id: InstructionId;
loc: SourceLocation;
};
export type TryTerminal = {
kind: "try";
block: BlockId;
handlerBinding: Place | null;
handler: BlockId;
fallthrough: BlockId;
id: InstructionId;
loc: SourceLocation;
};
export type MaybeThrowTerminal = {
kind: "maybe-throw";
continuation: BlockId;
handler: BlockId;
id: InstructionId;
loc: SourceLocation;
fallthrough?: never;
};
export type ReactiveScopeTerminal = {
kind: "scope";
fallthrough: BlockId;
block: BlockId;
scope: ReactiveScope;
id: InstructionId;
loc: SourceLocation;
};
export type PrunedScopeTerminal = {
kind: "pruned-scope";
fallthrough: BlockId;
block: BlockId;
scope: ReactiveScope;
id: InstructionId;
loc: SourceLocation;
};
export type Instruction = {
id: InstructionId;
lvalue: Place;
value: InstructionValue;
loc: SourceLocation;
};
export type TInstruction<T extends InstructionValue> = {
id: InstructionId;
lvalue: Place;
value: T;
loc: SourceLocation;
};
export type LValue = {
place: Place;
kind: InstructionKind;
};
export type LValuePattern = {
pattern: Pattern;
kind: InstructionKind;
};
export type ArrayExpression = {
kind: "ArrayExpression";
elements: Array<Place | SpreadPattern | Hole>;
loc: SourceLocation;
};
export type Pattern = ArrayPattern | ObjectPattern;
export type Hole = {
kind: "Hole";
};
export type SpreadPattern = {
kind: "Spread";
place: Place;
};
export type ArrayPattern = {
kind: "ArrayPattern";
items: Array<Place | SpreadPattern | Hole>;
};
export type ObjectPattern = {
kind: "ObjectPattern";
properties: Array<ObjectProperty | SpreadPattern>;
};
export type ObjectPropertyKey =
| {
kind: "string";
name: string;
}
| {
kind: "identifier";
name: string;
}
| {
kind: "computed";
name: Place;
};
export type ObjectProperty = {
kind: "ObjectProperty";
key: ObjectPropertyKey;
type: "property" | "method";
place: Place;
};
export type LoweredFunction = {
dependencies: Array<Place>;
func: HIRFunction;
};
export type ObjectMethod = {
kind: "ObjectMethod";
loc: SourceLocation;
loweredFunc: LoweredFunction;
};
export enum InstructionKind {
Const = "Const",
Let = "Let",
Reassign = "Reassign",
Catch = "Catch",
HoistedConst = "HoistedConst",
}
function _staticInvariantInstructionValueHasLocation(
value: InstructionValue
): SourceLocation {
return value.loc;
}
export type Phi = {
kind: "Phi";
id: Identifier;
operands: Map<BlockId, Identifier>;
type: Type;
};
export type ManualMemoDependency = {
root:
| {
kind: "NamedLocal";
value: Place;
}
| { kind: "Global"; identifierName: string };
path: Array<string>;
};
export type StartMemoize = {
kind: "StartMemoize";
manualMemoId: number;
deps: Array<ManualMemoDependency> | null;
loc: SourceLocation;
};
export type FinishMemoize = {
kind: "FinishMemoize";
manualMemoId: number;
decl: Place;
pruned?: true;
loc: SourceLocation;
};
export type MethodCall = {
kind: "MethodCall";
receiver: Place;
property: Place;
args: Array<Place | SpreadPattern>;
loc: SourceLocation;
};
export type CallExpression = {
kind: "CallExpression";
callee: Place;
args: Array<Place | SpreadPattern>;
loc: SourceLocation;
typeArguments?: Array<t.FlowType>;
};
export type LoadLocal = {
kind: "LoadLocal";
place: Place;
loc: SourceLocation;
};
export type InstructionValue =
| LoadLocal
| {
kind: "LoadContext";
place: Place;
loc: SourceLocation;
}
| {
kind: "DeclareLocal";
lvalue: LValue;
type: t.FlowType | t.TSType | null;
loc: SourceLocation;
}
| {
kind: "DeclareContext";
lvalue: {
kind: InstructionKind.Let | InstructionKind.HoistedConst;
place: Place;
};
loc: SourceLocation;
}
| {
kind: "StoreLocal";
lvalue: LValue;
value: Place;
type: t.FlowType | t.TSType | null;
loc: SourceLocation;
}
| {
kind: "StoreContext";
lvalue: {
kind: InstructionKind.Reassign;
place: Place;
};
value: Place;
loc: SourceLocation;
}
| Destructure
| {
kind: "Primitive";
value: number | boolean | string | null | undefined;
loc: SourceLocation;
}
| JSXText
| {
kind: "BinaryExpression";
operator: Exclude<t.BinaryExpression["operator"], "|>">;
left: Place;
right: Place;
loc: SourceLocation;
}
| {
kind: "NewExpression";
callee: Place;
args: Array<Place | SpreadPattern>;
loc: SourceLocation;
}
| CallExpression
| MethodCall
| {
kind: "UnaryExpression";
operator: Exclude<t.UnaryExpression["operator"], "throw" | "delete">;
value: Place;
loc: SourceLocation;
}
| {
kind: "TypeCastExpression";
value: Place;
typeAnnotation: t.FlowType | t.TSType;
type: Type;
loc: SourceLocation;
}
| {
kind: "JsxExpression";
tag: Place | BuiltinTag;
props: Array<JsxAttribute>;
children: Array<Place> | null;
loc: SourceLocation;
openingLoc: SourceLocation;
closingLoc: SourceLocation;
}
| {
kind: "ObjectExpression";
properties: Array<ObjectProperty | SpreadPattern>;
loc: SourceLocation;
}
| ObjectMethod
| ArrayExpression
| { kind: "JsxFragment"; children: Array<Place>; loc: SourceLocation }
| {
kind: "RegExpLiteral";
pattern: string;
flags: string;
loc: SourceLocation;
}
| {
kind: "MetaProperty";
meta: string;
property: string;
loc: SourceLocation;
}
| {
kind: "PropertyStore";
object: Place;
property: string;
value: Place;
loc: SourceLocation;
}
| PropertyLoad
| {
kind: "PropertyDelete";
object: Place;
property: string;
loc: SourceLocation;
}
| {
kind: "ComputedStore";
object: Place;
property: Place;
value: Place;
loc: SourceLocation;
}
| {
kind: "ComputedLoad";
object: Place;
property: Place;
loc: SourceLocation;
}
| {
kind: "ComputedDelete";
object: Place;
property: Place;
loc: SourceLocation;
}
| LoadGlobal
| StoreGlobal
| FunctionExpression
| {
kind: "TaggedTemplateExpression";
tag: Place;
value: { raw: string; cooked?: string };
loc: SourceLocation;
}
| {
kind: "TemplateLiteral";
subexprs: Array<Place>;
quasis: Array<{ raw: string; cooked?: string }>;
loc: SourceLocation;
}
| {
kind: "Await";
value: Place;
loc: SourceLocation;
}
| {
kind: "GetIterator";
collection: Place;
loc: SourceLocation;
}
| {
kind: "IteratorNext";
iterator: Place;
collection: Place;
loc: SourceLocation;
}
| {
kind: "NextPropertyOf";
value: Place;
loc: SourceLocation;
}
| {
kind: "PrefixUpdate";
lvalue: Place;
operation: t.UpdateExpression["operator"];
value: Place;
loc: SourceLocation;
}
| {
kind: "PostfixUpdate";
lvalue: Place;
operation: t.UpdateExpression["operator"];
value: Place;
loc: SourceLocation;
}
| { kind: "Debugger"; loc: SourceLocation }
| StartMemoize
| FinishMemoize
| {
kind: "UnsupportedNode";
node: t.Node;
loc: SourceLocation;
};
export type JsxAttribute =
| { kind: "JsxSpreadAttribute"; argument: Place }
| { kind: "JsxAttribute"; name: string; place: Place };
export type FunctionExpression = {
kind: "FunctionExpression";
name: string | null;
loweredFunc: LoweredFunction;
expr:
| t.ArrowFunctionExpression
| t.FunctionExpression
| t.FunctionDeclaration;
loc: SourceLocation;
};
export type Destructure = {
kind: "Destructure";
lvalue: LValuePattern;
value: Place;
loc: SourceLocation;
};
export type Place = {
kind: "Identifier";
identifier: Identifier;
effect: Effect;
reactive: boolean;
loc: SourceLocation;
};
export type Primitive = {
kind: "Primitive";
value: number | boolean | string | null | undefined;
loc: SourceLocation;
};
export type JSXText = { kind: "JSXText"; value: string; loc: SourceLocation };
export type PropertyLoad = {
kind: "PropertyLoad";
object: Place;
property: string;
loc: SourceLocation;
};
export type LoadGlobal = {
kind: "LoadGlobal";
binding: NonLocalBinding;
loc: SourceLocation;
};
export type StoreGlobal = {
kind: "StoreGlobal";
name: string;
value: Place;
loc: SourceLocation;
};
export type BuiltinTag = {
kind: "BuiltinTag";
name: string;
loc: SourceLocation;
};
export type MutableRange = {
start: InstructionId;
end: InstructionId;
};
export type VariableBinding =
| { kind: "Identifier"; identifier: Identifier; bindingKind: BindingKind }
| NonLocalBinding;
export type NonLocalBinding =
| { kind: "ImportDefault"; name: string; module: string }
| { kind: "ImportNamespace"; name: string; module: string }
| {
kind: "ImportSpecifier";
name: string;
module: string;
imported: string;
}
| { kind: "ModuleLocal"; name: string }
| { kind: "Global"; name: string };
export type Identifier = {
id: IdentifierId;
name: IdentifierName | null;
mutableRange: MutableRange;
scope: ReactiveScope | null;
type: Type;
loc: SourceLocation;
};
export type IdentifierName = ValidatedIdentifier | PromotedIdentifier;
export type ValidatedIdentifier = { kind: "named"; value: ValidIdentifierName };
export type PromotedIdentifier = { kind: "promoted"; value: string };
const opaqueValidIdentifierName = Symbol();
export type ValidIdentifierName = string & {
[opaqueValidIdentifierName]: "ValidIdentifierName";
};
export function makeIdentifierName(name: string): ValidatedIdentifier {
CompilerError.invariant(t.isValidIdentifier(name), {
reason: `Expected a valid identifier name`,
loc: GeneratedSource,
description: `\`${name}\` is not a valid JavaScript identifier`,
suggestions: null,
});
return {
kind: "named",
value: name as ValidIdentifierName,
};
}
export function promoteTemporary(identifier: Identifier): void {
CompilerError.invariant(identifier.name === null, {
reason: `Expected a temporary (unnamed) identifier`,
loc: GeneratedSource,
description: `Identifier already has a name, \`${identifier.name}\``,
suggestions: null,
});
identifier.name = {
kind: "promoted",
value: `#t${identifier.id}`,
};
}
export function isPromotedTemporary(name: string): boolean {
return name.startsWith("#t");
}
export function promoteTemporaryJsxTag(identifier: Identifier): void {
CompilerError.invariant(identifier.name === null, {
reason: `Expected a temporary (unnamed) identifier`,
loc: GeneratedSource,
description: `Identifier already has a name, \`${identifier.name}\``,
suggestions: null,
});
identifier.name = {
kind: "promoted",
value: `#T${identifier.id}`,
};
}
export function isPromotedJsxTemporary(name: string): boolean {
return name.startsWith("#T");
}
export type AbstractValue = {
kind: ValueKind;
reason: ReadonlySet<ValueReason>;
context: ReadonlySet<Place>;
};
export enum ValueReason {
Global = "global",
JsxCaptured = "jsx-captured",
KnownReturnSignature = "known-return-signature",
Context = "context",
State = "state",
ReducerState = "reducer-state",
ReactiveFunctionArgument = "reactive-function-argument",
Other = "other",
}
export enum ValueKind {
MaybeFrozen = "maybefrozen",
Frozen = "frozen",
Primitive = "primitive",
Global = "global",
Mutable = "mutable",
Context = "context",
}
export enum Effect {
Unknown = "<unknown>",
Freeze = "freeze",
Read = "read",
Capture = "capture",
ConditionallyMutate = "mutate?",
Mutate = "mutate",
Store = "store",
}
export function isMutableEffect(
effect: Effect,
location: SourceLocation
): boolean {
switch (effect) {
case Effect.Capture:
case Effect.Store:
case Effect.ConditionallyMutate:
case Effect.Mutate: {
return true;
}
case Effect.Unknown: {
CompilerError.invariant(false, {
reason: "Unexpected unknown effect",
description: null,
loc: location,
suggestions: null,
});
}
case Effect.Read:
case Effect.Freeze: {
return false;
}
default: {
assertExhaustive(effect, `Unexpected effect \`${effect}\``);
}
}
}
export type ReactiveScope = {
id: ScopeId;
range: MutableRange;
dependencies: ReactiveScopeDependencies;
declarations: Map<IdentifierId, ReactiveScopeDeclaration>;
reassignments: Set<Identifier>;
earlyReturnValue: {
value: Identifier;
loc: SourceLocation;
label: BlockId;
} | null;
merged: Set<ScopeId>;
loc: SourceLocation;
};
export type ReactiveScopeDependencies = Set<ReactiveScopeDependency>;
export type ReactiveScopeDeclaration = {
identifier: Identifier;
scope: ReactiveScope;
};
export type ReactiveScopeDependency = {
identifier: Identifier;
path: Array<string>;
};
const opaqueBlockId = Symbol();
export type BlockId = number & { [opaqueBlockId]: "BlockId" };
export function makeBlockId(id: number): BlockId {
CompilerError.invariant(id >= 0 && Number.isInteger(id), {
reason: "Expected block id to be a non-negative integer",
description: null,
loc: null,
suggestions: null,
});
return id as BlockId;
}
const opaqueScopeId = Symbol();
export type ScopeId = number & { [opaqueScopeId]: "ScopeId" };
export function makeScopeId(id: number): ScopeId {
CompilerError.invariant(id >= 0 && Number.isInteger(id), {
reason: "Expected block id to be a non-negative integer",
description: null,
loc: null,
suggestions: null,
});
return id as ScopeId;
}
const opaqueIdentifierId = Symbol();
export type IdentifierId = number & { [opaqueIdentifierId]: "IdentifierId" };
export function makeIdentifierId(id: number): IdentifierId {
CompilerError.invariant(id >= 0 && Number.isInteger(id), {
reason: "Expected identifier id to be a non-negative integer",
description: null,
loc: null,
suggestions: null,
});
return id as IdentifierId;
}
const opaqueInstructionId = Symbol();
export type InstructionId = number & { [opaqueInstructionId]: "IdentifierId" };
export function makeInstructionId(id: number): InstructionId {
CompilerError.invariant(id >= 0 && Number.isInteger(id), {
reason: "Expected instruction id to be a non-negative integer",
description: null,
loc: null,
suggestions: null,
});
return id as InstructionId;
}
export function isObjectMethodType(id: Identifier): boolean {
return id.type.kind == "ObjectMethod";
}
export function isObjectType(id: Identifier): boolean {
return id.type.kind === "Object";
}
export function isPrimitiveType(id: Identifier): boolean {
return id.type.kind === "Primitive";
}
export function isArrayType(id: Identifier): boolean {
return id.type.kind === "Object" && id.type.shapeId === "BuiltInArray";
}
export function isRefValueType(id: Identifier): boolean {
return id.type.kind === "Object" && id.type.shapeId === "BuiltInRefValue";
}
export function isUseRefType(id: Identifier): boolean {
return id.type.kind === "Object" && id.type.shapeId === "BuiltInUseRefId";
}
export function isUseStateType(id: Identifier): boolean {
return id.type.kind === "Object" && id.type.shapeId === "BuiltInUseState";
}
export function isSetStateType(id: Identifier): boolean {
return id.type.kind === "Function" && id.type.shapeId === "BuiltInSetState";
}
export function isUseReducerType(id: Identifier): boolean {
return id.type.kind === "Function" && id.type.shapeId === "BuiltInUseReducer";
}
export function isDispatcherType(id: Identifier): boolean {
return id.type.kind === "Function" && id.type.shapeId === "BuiltInDispatch";
}
export function isUseEffectHookType(id: Identifier): boolean {
return (
id.type.kind === "Function" && id.type.shapeId === "BuiltInUseEffectHook"
);
}
export function isUseLayoutEffectHookType(id: Identifier): boolean {
return (
id.type.kind === "Function" &&
id.type.shapeId === "BuiltInUseLayoutEffectHook"
);
}
export function isUseInsertionEffectHookType(id: Identifier): boolean {
return (
id.type.kind === "Function" &&
id.type.shapeId === "BuiltInUseInsertionEffectHook"
);
}
export function getHookKind(env: Environment, id: Identifier): HookKind | null {
return getHookKindForType(env, id.type);
}
export function isUseOperator(id: Identifier): boolean {
return (
id.type.kind === "Function" && id.type.shapeId === "BuiltInUseOperator"
);
}
export function getHookKindForType(
env: Environment,
type: Type
): HookKind | null {
if (type.kind === "Function") {
const signature = env.getFunctionSignature(type);
return signature?.hookKind ?? null;
}
return null;
}
export * from "./Types";