UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

172 lines (171 loc) 8.84 kB
import type { DataflowProcessorInformation } from './processor'; import type { NodeId } from '../r-bridge/lang-4.x/ast/model/processing/node-id'; import type { IdentifierReference } from './environments/identifier'; import type { REnvironmentInformation } from './environments/environment'; import { DataflowGraph } from './graph/graph'; import type { GenericDifferenceInformation, WriteableDifferenceReport } from '../util/diff'; import type { HookInformation } from './hooks'; /** * A control dependency links a vertex to the control flow element which * may have an influence on its execution. * Within `if(p) a else b`, `a` and `b` have a control dependency on the `if` (which in turn decides based on `p`). * @see {@link happensInEveryBranch} - to check whether a list of control dependencies is exhaustive * @see {@link negateControlDependency} - to easily negate a control dependency */ export interface ControlDependency { /** The id of the node that causes the control dependency to be active (e.g., the condition of an if) */ readonly id: NodeId; /** when does this control dependency trigger (if the condition is true or false)? */ readonly when?: boolean; /** whether this control dependency was created due to iteration (e.g., a loop) */ readonly byIteration?: boolean; /** * any file-exist assumptions made */ readonly file?: string; } /** * Negates the given control dependency (i.e., flips the `when` flag). * This keeps undefined `when` values intact as undefined. */ export declare function negateControlDependency(cd: ControlDependency): ControlDependency; /** * Classifies the type of exit point encountered. * @see {@link ExitPoint} */ export declare const enum ExitPointType { /** The exit point is the implicit (last executed expression of a function/block) */ Default = 0, /** The exit point is an explicit `return` call (or an alias of it) */ Return = 1, /** The exit point is an explicit `break` call (or an alias of it) */ Break = 2, /** The exit point is an explicit `next` call (or an alias of it) */ Next = 3, /** The exit point is caused by an error being thrown, e.g., by `stop` or `stopifnot` */ Error = 4 } /** * Checks whether the given exit point type propagates calls (i.e., whether it aborts the current function execution). */ export declare function doesExitPointPropagateCalls(type: ExitPointType): boolean; /** * An exit point describes the position which ends the current control flow structure. * This may be as innocent as the last expression or explicit with a `return`/`break`/`next`. * @see {@link ExitPointType} - for the different types of exit points * @see {@link addNonDefaultExitPoints} - to easily modify lists of exit points * @see {@link alwaysExits} - to check whether a list of control dependencies always triggers an exit * @see {@link filterOutLoopExitPoints} - to remove loop exit points from a list */ export interface ExitPoint { /** What kind of exit point is this one? May be used to filter for exit points of specific causes. */ readonly type: ExitPointType; /** The id of the node which causes the exit point! */ readonly nodeId: NodeId; /** * Control dependencies which influence if the exit point triggers * (e.g., if the `return` is contained within an `if` statement). * @see {@link happensInEveryBranch} - to check whether control dependencies are exhaustive */ readonly cds?: ControlDependency[]; } /** * Adds all non-default exit points to the existing list and updates the `invertExitCds` accordingly. */ export declare function addNonDefaultExitPoints(existing: ExitPoint[], invertExitCds: ControlDependency[], activeCds: ControlDependency[] | undefined, add: readonly ExitPoint[]): void; /** * Overwrites the existing exit points with the given ones, taking care of cds. */ export declare function overwriteExitPoints(existing: readonly ExitPoint[], replace: ExitPoint[]): ExitPoint[]; /** The control flow information for the current DataflowInformation. */ export interface DataflowCfgInformation { /** The entry node into the subgraph */ entryPoint: NodeId; /** * All already identified exit points (active 'return'/'break'/'next'-likes) of the respective structure. * This also tracks (local knowledge of) exceptions thrown within the structure. * See the {@link ExitPointType#Error|Error} type for more information. */ exitPoints: readonly ExitPoint[]; /** Registered hooks within the current subtree */ hooks: HookInformation[]; } /** * The dataflow information is one of the fundamental structures we have in the dataflow analysis. * It is continuously updated during the dataflow analysis * and holds its current state for the respective subtree processed. * Each processor during the dataflow analysis may use the information from its children * to produce a new state of the dataflow information. * * You may initialize a new dataflow information with {@link initializeCleanDataflowInformation}. * @see {@link DataflowCfgInformation} - the control flow aspects */ export interface DataflowInformation extends DataflowCfgInformation { /** * References that have not been identified as read or write and will be so on higher processors. * * For example, when we analyze the `x` vertex in `x <- 3`, we will first create an unknown reference for `x` * as we have not yet seen the assignment! * @see {@link IdentifierReference} - a reference on a variable, parameter, function call, ... */ unknownReferences: readonly IdentifierReference[]; /** * References which are read within the current subtree. * @see {@link IdentifierReference} - a reference on a variable, parameter, function call, ... */ in: readonly IdentifierReference[]; /** * References which are written to within the current subtree * @see {@link IdentifierReference} - a reference on a variable, parameter, function call, ... */ out: readonly IdentifierReference[]; /** Current environments used for name resolution, probably updated on the next expression-list processing */ environment: REnvironmentInformation; /** The current constructed dataflow graph */ graph: DataflowGraph; } /** * Helper object for {@link DataflowInformation} */ export declare const DataflowInformation: { readonly name: "DataflowInformation"; /** * Initializes an empty {@link DataflowInformation} object with the given entry point and data. * This is to be used as a "starting point" when processing leaf nodes during the dataflow extraction. * @see {@link DataflowInformation} */ readonly initialize: <T>(this: void, entryPoint: NodeId, data: Pick<DataflowProcessorInformation<T>, "environment" | "completeAst">) => DataflowInformation; /** * Type guard to check whether the given information is a {@link DataflowInformation}. */ readonly is: (info: unknown) => info is DataflowInformation; }; /** * Checks whether the given control dependencies are exhaustive (i.e. if for every control dependency on a boolean, * the list contains a dependency on the `true` and on the `false` case). * @see {@link happensInEveryBranchSet} - for the set-based version */ export declare function happensInEveryBranch(cds: readonly ControlDependency[] | undefined): boolean; /** * Checks whether the given control dependencies are exhaustive (i.e. if for every control dependency on a boolean, * the list contains a dependency on the `true` and on the `false` case). * @see {@link happensInEveryBranch} - for the array-based version */ export declare function happensInEveryBranchSet(cds: ReadonlySet<ControlDependency> | undefined): boolean; /** * Checks whether the given dataflow information always exits (i.e., if there is a non-default exit point in every branch). * @see {@link ExitPoint} - for the different types of exit points */ export declare function alwaysExits(data: DataflowInformation): boolean; /** * Filters out exit points which end their cascade within a loop. */ export declare function filterOutLoopExitPoints(exitPoints: readonly ExitPoint[]): readonly ExitPoint[]; /** * Calculates the difference between two control dependencies. */ export declare function diffControlDependency<Report extends WriteableDifferenceReport>(a: ControlDependency | undefined, b: ControlDependency | undefined, info: GenericDifferenceInformation<Report>): void; /** * Calculates the difference between two lists of control dependencies. */ export declare function diffControlDependencies<Report extends WriteableDifferenceReport>(a: ControlDependency[] | undefined, b: ControlDependency[] | undefined, info: GenericDifferenceInformation<Report>): void;