UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

130 lines (129 loc) 7.09 kB
import { NodeId } from '../../../r-bridge/lang-4.x/ast/model/processing/node-id'; import { InputTraceType, InputType } from './input-types'; export { InputTraceType, InputType } from './input-types'; import type { DataflowGraph } from '../../../dataflow/graph/graph'; import type { MergeableRecord } from '../../../util/objects'; import { Identifier } from '../../../dataflow/environments/identifier'; import type { FlowrSearchLike } from '../../../search/flowr-search-builder'; /** * An object that a framework hands to its users without any visible definition, like the `input` of a * shiny server function. Reads of such an object (and of its fields) are classified as its given type, * so traces link up to the framework instead of stopping at an opaque parameter. */ export interface LinkedInputObject { /** the name of the object, e.g. `input` */ readonly name: string; /** how reads of the object (or of its fields) are to be classified */ readonly type: InputType; /** * If given, the object only counts as linked if the function binding it declares all of these parameters as well * (e.g., shiny's `function(input, output, session)`). Without this, every `input` would be treated as the framework's. */ readonly withParams?: readonly string[]; /** the package that has to be attached for this object to exist, e.g. `shiny` */ readonly requires?: string; /** if given, only these fields are inputs (`session$clientData` is, `session$userData` is not) and the object itself is none */ readonly fields?: readonly string[]; /** how the framework declares the entries of this object, so a read of `input$n` links to the `textInput("n", …)` defining it */ readonly declaredBy?: LinkedInputDeclaration; } /** The calls that declare the entries of a {@link LinkedInputObject}, and where they carry the entry's name. */ export interface LinkedInputDeclaration { /** the declaring calls, e.g. shiny's `textInput`, `selectInput`, … */ readonly calls: readonly Identifier[]; /** the name of the argument holding the entry's name */ readonly argName: string; /** the index of that argument when it is passed positionally */ readonly argIdx: number; } /** * A call a framework is given a function through, binding its parameters *by position* - which is how R passes * them, so this catches a `shinyApp(ui, function(i, o, s))` that no name-based rule can. */ export interface LinkedInputEntryPoint { /** the call taking the function, e.g. `shiny::shinyApp` */ readonly call: Identifier; /** the name of the argument holding the function */ readonly argName: string; /** the index of that argument when it is passed positionally */ readonly argIdx: number; /** which {@link LinkedInputObject} the framework binds to each parameter, by position; `undefined` leaves one alone */ readonly params: readonly (string | undefined)[]; } /** A function whose result is bounded by one argument (e.g. `match.arg` by its `choices`), classified by that argument alone. */ export interface NarrowingFunction { readonly call: Identifier; /** the name of the bounding argument; omit (with `argIdx`) for a result that is always a bounded, content-independent value */ readonly argName?: string; /** the index of the bounding argument; omit (with `argName`) for an always-`DerivedConstant` result */ readonly argIdx?: number; } /** * Scalar R constant values representable in TypeScript. * `null` corresponds to R's `NULL`. * NA values are not included (they have no direct TS equivalent). */ export type ConstantValue = string | number | boolean | null; /** * Object attached to an input source * @see {@link InputSources} */ export interface InputSource extends MergeableRecord { id: NodeId; types: InputType[]; trace: InputTraceType; /** if the trace is affected by control dependencies, they are classified too, this is a duplicate free array */ cds?: InputType[]; /** * Argument name when this source originates from a named argument of the criterion function call, * or the accessed field when it originates from a {@link LinkedInputObject} (e.g. `n` for `input$n`). */ name?: string; /** the concrete scalar value when the source is a constant or a pure alias of one */ value?: ConstantValue; /** where the framework entry this source reads is declared, e.g. the `textInput("n", …)` behind an `input$n` */ declaredAt?: NodeId[]; } /** * Map of input sources, keyed by the node id of the input source. Each input source is classified with an {@link InputSource} object. */ export type InputSources = InputSource[]; /** * This is either an {@link NodeId|id} of a known functions all of that category (e.g., you can issue a dependencies query before and then pass all * identified ids to this query here). */ export type InputClassifierFunctionIdentifiers = readonly (Identifier | NodeId)[]; /** * For the specifications of `pure` etc. please have a look at {@link InputClassifierFunctionIdentifiers}. */ export interface InputClassifierConfig<Functions extends InputClassifierFunctionIdentifiers | FlowrSearchLike = readonly Identifier[] | FlowrSearchLike> extends Partial<Record<InputType, Functions>> { /** * Functions which are considered to be pure (i.e., deterministic, trusted, safe, idempotent on the lub of the input types) */ [InputTraceType.Pure]?: Functions; /** * Objects provided by a framework rather than by the code itself, like shiny's `input`. * @see {@link LinkedInputObject} */ linkedObjects?: readonly LinkedInputObject[]; /** * Calls that hand a function to a framework, which then binds {@link linkedObjects} to its parameters by position. * @see {@link LinkedInputEntryPoint} */ linkedEntryPoints?: readonly LinkedInputEntryPoint[]; /** * Functions whose result is bounded by one of their arguments (classified by that argument alone). * @see {@link NarrowingFunction} */ narrowing?: readonly NarrowingFunction[]; } /** * Takes the given id which is expected to either be: * - a function call - in this case all arguments are considered to be inputs (additionally to all read edges from the function call in the dataflow graph) * - anything else - in that case the node itself is considered as an "input" - please note that in these scenarios the *return* value will only contain one mapping - that for the id you passed in. * * This method traces the dependencies in the dataflow graph using the specification of functions passed in. * For the scope escape analysis, pass on the full, non-reduced DFG as `fullDfg`, and the packages attached in the * program as `packages` so that bare calls only match the entries of packages that are actually in scope. */ export declare function classifyInput(id: NodeId, dfg: DataflowGraph, config: InputClassifierConfig<InputClassifierFunctionIdentifiers>, fullDfg?: DataflowGraph, packages?: ReadonlySet<string>): InputSources;