import { IL } from '../../../lib';
import { ModuleRelativeSource } from '../../virtual-machine-types';
import * as B from '../supported-babel-types';
/**
 * The output model of `analyzeScopes()`
 */
export interface AnalysisModel {
    /**
     * All functions in the unit, including nested and arrow functions but not the
     * module entry function.
     */
    functions: FunctionScope[];
    scopes: Map<ScopeNode, Scope>;
    references: Map<ReferencingNode, Reference>;
    bindings: Map<BindingNode, Binding>;
    moduleScope: ModuleScope;
    freeVariables: Set<string>;
    globalSlots: GlobalSlot[];
    thisModuleSlot: GlobalSlot;
    moduleImports: Map<ModuleRelativeSource, GlobalSlot>;
    exportedBindings: Binding[];
}
export declare type Scope = ModuleScope | FunctionScope | ClassScope | BlockScope;
export declare type Slot = GlobalSlot | ClosureSlot | LocalSlot | ArgumentSlot | ModuleImportExportSlot;
export interface GlobalSlot {
    type: 'GlobalSlot';
    name: string;
}
export interface ClosureSlot {
    type: 'ClosureSlot';
    index: number;
    debugName: string;
}
export interface LocalSlot {
    type: 'LocalSlot';
    index: number;
    debugName: string;
}
export interface ArgumentSlot {
    type: 'ArgumentSlot';
    argIndex: number;
}
export interface ModuleImportExportSlot {
    type: 'ModuleImportExportSlot';
    moduleNamespaceObjectSlot: GlobalSlot;
    propertyName: string;
}
export interface ScopeBase {
    node?: ScopeNode;
    bindings: {
        [name: string]: Binding;
    };
    children: Scope[];
    prologue: PrologueStep[];
    epilogue: EpilogueStep[];
    references: Reference[];
    nestedFunctionDeclarations: NestedFunctionDeclaration[];
    lexicalDeclarations: Binding[];
    varDeclarations: Binding[];
    parameterBindings: Binding[];
    closureSlots?: ClosureSlot[];
    /**
     * False if this scope is for a function or if the block can be
     * multiply-instantiated relative to its parent, as in the case with loop
     * bodies. This is used during analysis. If this is true, variables in the
     * block can share the closure slot in the parent's closure scope. If it's
     * false, then the block needs its own closure scope if there are any
     * closure-scoped variables.
     */
    sameInstanceCountAsParent: boolean;
    isTryScope?: boolean;
    isCatchScope?: boolean;
    catchExceptionBinding?: Binding;
    catchExceptionSlotAccess?: SlotAccessInfo;
    /** The outer scope */
    parent: Scope | undefined;
    thisBinding?: Binding;
    embeddingCandidates: FunctionScope[];
    embeddedChildClosure?: FunctionScope;
    accessesParentScope?: boolean;
    isAsyncFunction: boolean;
    awaitExpressions: B.AwaitExpression[];
}
export interface BlockScope extends ScopeBase {
    type: 'BlockScope';
}
export interface FunctionLikeScope extends ScopeBase {
    type: 'FunctionScope' | 'ModuleScope';
    ilFunctionId: IL.FunctionID;
    parent: Scope | undefined;
    functionIsClosure: boolean;
}
export interface ModuleScope extends FunctionLikeScope {
    type: 'ModuleScope';
    parent: undefined;
}
export interface FunctionScope extends FunctionLikeScope {
    type: 'FunctionScope';
    funcName?: string;
    embeddedInParentSlot?: ClosureSlot;
}
export interface ClassScope extends ScopeBase {
    type: 'ClassScope';
    className?: string;
    /**
     * A class contains 3 constructor scopes:
     *
     *  - The physical constructor is associated with the IL constructor function,
     *    and only binds `this`. It is the scope in which non-static property
     *    values are evaluated.
     *  - The virtual constructor is associated with the `constructor` syntax in
     *    the source, so it is optional. It is treated as a `BlockScope` because
     *    it is like a block inside the physical constructor. It binds the
     *    constructor arguments, hoisted variables, and top-level lexical
     *    declarations.
     *  - The static constructor scope is a block where `this` refers to the class
     *    itself, which is considered to be physically a block within the
     *    declaring scope of the class (where `class` declaration occurs).
     */
    physicalConstructorScope: FunctionScope;
    virtualConstructorScope?: BlockScope;
    staticConstructorScope: BlockScope;
}
export declare type PrologueStep = {
    type: 'ScopePush';
    slotCount: number;
} | {
    type: 'ScopeNew';
    slotCount: number;
} | {
    type: 'AsyncStart';
    slotCount: number;
    captureParent: boolean;
} | {
    type: 'InitFunctionDeclaration';
    slot: SlotAccessInfo;
    functionId: string;
    closureType: 'none' | 'embedded' | 'non-embedded';
} | {
    type: 'InitVarDeclaration';
    slot: SlotAccessInfo;
} | {
    type: 'InitLexicalDeclaration';
    slot: SlotAccessInfo;
    nameHint: string;
} | {
    type: 'InitParameter';
    slot: SlotAccessInfo;
    argIndex: number;
} | {
    type: 'InitThis';
    slot: SlotAccessInfo;
} | {
    type: 'InitCatchParam';
    slot: SlotAccessInfo;
} | {
    type: 'DiscardCatchParam';
} | {
    type: 'StartTry';
} | {
    type: 'DummyPushException';
};
export declare type EpilogueStep = {
    type: 'Pop';
    requiredDuringReturn: false;
    count: number;
} | {
    type: 'ScopeDiscard';
    requiredDuringReturn: false;
} | {
    type: 'ScopePop';
    requiredDuringReturn: false;
} | {
    type: 'EndTry';
    requiredDuringReturn: true;
    stackDepthAfter: number;
};
export interface ParameterInitialization {
    argIndex: number;
    slot: SlotAccessInfo;
}
export interface NestedFunctionDeclaration {
    func: B.FunctionDeclaration;
    binding: Binding;
}
export interface Binding {
    scope: Scope;
    kind: 'param' | 'var' | 'const' | 'let' | 'this' | 'function' | 'catch-param' | 'import' | 'class';
    /** The name to which the variable is bound (the declared variable, function or parameter name) */
    name: string;
    /** The slot in which to store the variable. If the variable is not used, the slot can be undefined */
    slot?: Slot;
    /** The variable declaration AST node. Note that `this` bindings don't have a node */
    node?: BindingNode;
    /** Syntactically readonly. E.g. `const` */
    isDeclaredReadonly: boolean;
    /** Is this part of an `export` statement? */
    isExported: boolean;
    /**
     * True if some assignment operation targets this variable (beyond just
     * initialization)
     *
     * This is intended for use in parameter optimization. If a parameter is not
     * assigned to, then the argument slot (`LoadArg`) can be used directly.
     */
    isWrittenTo: boolean;
    isUsed: boolean;
    isAccessedByNestedFunction: boolean;
    selfReference?: Reference;
}
export interface Reference {
    name: string;
    isInLocalFunction: boolean;
    resolvesTo: {
        type: 'Binding';
        binding: Binding;
    } | {
        type: 'FreeVariable';
        name: string;
    } | {
        type: 'RootLevelThis';
    };
    access: SlotAccessInfo;
    /**
     * The scope in which the variable reference occurs
     *
     * Pass 3 uses this to count the the number of slots between a reference and
     * its target closure slot, to generate the relative indexes.
     */
    nearestScope: Scope;
}
export declare type SlotAccessInfo = GlobalSlot | ModuleImportExportSlot | LocalSlot | ArgumentSlot | ClosureSlotAccess | ConstUndefinedAccess;
export interface ClosureSlotAccess {
    type: 'ClosureSlotAccess';
    relativeIndex: number;
}
export interface ConstUndefinedAccess {
    type: 'ConstUndefinedAccess';
}
export declare type ScopeNode = B.Program | B.SupportedFunctionNode | B.Block | B.ForStatement | B.ClassDeclaration | B.ClassExpression;
export declare type BindingNode = B.VariableDeclarator | B.FunctionDeclaration | B.ClassDeclaration | B.Identifier | B.ImportSpecifier | B.ImportDefaultSpecifier | B.ImportNamespaceSpecifier;
export declare type ReferencingNode = B.Identifier | B.ThisExpression;
export declare type ImportSpecifier = B.ImportSpecifier | B.ImportDefaultSpecifier | B.ImportNamespaceSpecifier;
