microvium
Version:
A compact, embeddable scripting engine for microcontrollers for executing small scripts written in a subset of JavaScript.
244 lines (243 loc) • 8.32 kB
TypeScript
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;