UNPKG

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
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;