UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

252 lines 14.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DispatchCallees = exports.FnSig = exports.PropagatedProps = exports.FileInputProps = exports.SigDbInferable = exports.InputProps = exports.ExclusiveCallProps = exports.ImpureProps = exports.CallProp = exports.ArgProp = void 0; exports.callPropWords = callPropWords; exports.fnInfoFromSignature = fnInfoFromSignature; /** * What a single argument of a call is used for, as a bitmask. * @see {@link BuiltInFnInfo#sig} */ var ArgProp; (function (ArgProp) { /** the result is this argument, handed back unchanged, like `x` in `identity(x)`; this is what draws the `Returns` edge */ ArgProp[ArgProp["Alias"] = 1] = "Alias"; /** the result is computed from the argument's value, like `x` in `sum(x)` */ ArgProp[ArgProp["Value"] = 2] = "Value"; /** only the shape is used (length, dimensions, names, other attributes), like `x` in `nrow(x)` */ ArgProp[ArgProp["Shape"] = 4] = "Shape"; /** selects a behavior instead of carrying data, like `na.rm` in `sum(x, na.rm = TRUE)` */ ArgProp[ArgProp["Flag"] = 8] = "Flag"; /** names the resource the call reads or writes, like `file` in `write.csv(x, file)` */ ArgProp[ArgProp["Resource"] = 16] = "Resource"; /** what it refers to may be modified, like `envir` in `assign(x, v, envir = e)` */ ArgProp[ArgProp["Written"] = 32] = "Written"; /** evaluated whenever the call happens, even if the result goes unused, like `x` in `force(x)` */ ArgProp[ArgProp["Forced"] = 64] = "Forced"; /** quoted or evaluated in another frame, like `expr` in `quote(expr)` */ ArgProp[ArgProp["Nse"] = 128] = "Nse"; /** called as a function, like `FUN` in `lapply(x, FUN)` */ ArgProp[ArgProp["Callee"] = 256] = "Callee"; /** only whether it was supplied matters, as with `missing()` */ ArgProp[ArgProp["Presence"] = 512] = "Presence"; /** * the result is one of this argument's values, like `choices` in `match.arg(arg, choices)`. The bounding * argument of a {@link CallProp.Narrows} call; without one such a call yields a value of its own making. */ ArgProp[ArgProp["Bounds"] = 1024] = "Bounds"; /** * only atomic data works here, never a closure, as with `e1` in `e1 > e2`. A bare symbol in such an * argument therefore names a variable even when a function of that name is in scope. */ ArgProp[ArgProp["Atomic"] = 2048] = "Atomic"; /** the open handle the call acts on, like `con` in `close(con)` */ ArgProp[ArgProp["Handle"] = 4096] = "Handle"; })(ArgProp || (exports.ArgProp = ArgProp = {})); /** * What the call as a whole does, as a bitmask. The resource bits ({@link CallProp.File} and its neighbors) * say where the call gets its data from, which is what {@link InputProps} collects. * @see {@link BuiltInFnInfo#props} */ var CallProp; (function (CallProp) { /** computes a result and nothing else, the positive counterpart of `hasUnknownSideEffects` (excludes {@link ImpureProps}) */ CallProp[CallProp["Pure"] = 1] = "Pure"; /** * pure on its own, but it runs code it is handed, so whatever that code does happens too. * The parameter it runs is marked {@link ArgProp.Callee} or {@link ArgProp.Nse}, as with `lapply(x, f)`. */ CallProp[CallProp["MayPure"] = 2] = "MayPure"; /** may signal an error, like `stop()` (see {@link SigDbInferable}) */ CallProp[CallProp["Throws"] = 4] = "Throws"; /** returns invisibly, so the result is not auto-printed */ CallProp[CallProp["Invisible"] = 8] = "Invisible"; /** dispatches on the class of an argument (S3, S4, or S7), a group generic like `+` on either operand */ CallProp[CallProp["Generic"] = 16] = "Generic"; /** a method that is reached by dispatch, like `print.foo` (see {@link SigDbInferable}) */ CallProp[CallProp["Method"] = 32] = "Method"; /** binds, rebinds, or removes names outside of its own frame, like `assign` or `library` */ CallProp[CallProp["Scope"] = 64] = "Scope"; /** the result may differ between two identical calls for a reason neither `Random` nor `Ambient` covers (see {@link SigDbInferable}) */ CallProp[CallProp["NonDet"] = 128] = "NonDet"; /** draws from the random number generator, or sets its state (stated instead of `NonDet`) */ CallProp[CallProp["Random"] = 256] = "Random"; /** depends on ambient state like the clock, the locale, environment variables, or global options (stated instead of `NonDet`) */ CallProp[CallProp["Ambient"] = 512] = "Ambient"; /** touches the file system */ CallProp[CallProp["File"] = 1024] = "File"; /** produces a temporary path; on its own this touches no file system, so a call that also does states `File` too */ CallProp[CallProp["TempFile"] = 2048] = "TempFile"; /** * always reaches the network, like `curl::curl_download`. Calls that only do so for some arguments, like * `read.csv` of a URL, are left to the `network-functions` rule, which decides that per call site. */ CallProp[CallProp["Network"] = 4096] = "Network"; /** runs a system command */ CallProp[CallProp["Process"] = 8192] = "Process"; /** calls native code through the foreign function interface, like `.Call` */ CallProp[CallProp["Ffi"] = 16384] = "Ffi"; /** produces a language object, like `quote` or `deparse` */ CallProp[CallProp["Lang"] = 32768] = "Lang"; /** asks the user, like `readline` or a file chooser */ CallProp[CallProp["User"] = 65536] = "User"; /** draws on a graphics device */ CallProp[CallProp["Graphics"] = 131072] = "Graphics"; /** talks to a database */ CallProp[CallProp["Database"] = 262144] = "Database"; /** reads the resource its `Resource` arguments name */ CallProp[CallProp["Reads"] = 524288] = "Reads"; /** writes the resource its `Resource` arguments name */ CallProp[CallProp["Writes"] = 1048576] = "Writes"; /** may emit to standard output, like `print` or a `cat` without a `file`, and follows a `sink` when one is active */ CallProp[CallProp["Prints"] = 2097152] = "Prints"; /** * the result is bounded no matter what flows in: a count, an index, a logical, or one of the values of the * argument marked {@link ArgProp.Bounds}. So nothing an argument carries reaches the result, which is what * lets the input-sources query stop tracing at `length(x)` or `match.arg(arg, choices)`. */ CallProp[CallProp["Narrows"] = 4194304] = "Narrows"; /** * sets ambient state later calls read back: the working directory, environment variables, options, the * locale, the RNG seed. The counterpart of {@link CallProp.Ambient}; a call doing both states both. */ CallProp[CallProp["Configures"] = 8388608] = "Configures"; /** ends what an opener started: a graphics device, a connection, a sink. Narrower than {@link CallProp.Graphics}. */ CallProp[CallProp["Closes"] = 16777216] = "Closes"; /** yields the paths it matches at run time rather than one it was handed (`list.files`, `Sys.glob`); empty is an answer */ CallProp[CallProp["Glob"] = 33554432] = "Glob"; /** hands back what the program was invoked with, as `commandArgs` and the option parsers built on it do */ CallProp[CallProp["CommandLine"] = 67108864] = "CommandLine"; /** hands back a handle the program is expected to close again, like `file` or `DBI::dbConnect` */ CallProp[CallProp["Opens"] = 134217728] = "Opens"; })(CallProp || (exports.CallProp = CallProp = {})); /** * The {@link CallProp} bits that state an effect beyond computing a result, so no {@link CallProp.Pure} * definition may carry any of them. */ exports.ImpureProps = CallProp.MayPure | CallProp.Scope | CallProp.NonDet | CallProp.Random | CallProp.Ambient | CallProp.File | CallProp.TempFile | CallProp.Network | CallProp.Process | CallProp.Ffi | CallProp.Lang | CallProp.User | CallProp.Graphics | CallProp.Database | CallProp.Reads | CallProp.Writes | CallProp.Prints | CallProp.Configures | CallProp.Closes | CallProp.Opens | CallProp.CommandLine; /** * Which {@link CallProp} bits rule each other out, as `[bit, everything stating it forbids]`. A definition * that carries the left bit must carry none of the right ones; a test checks the {@link DefaultBuiltinConfig} * (and any configured built-ins) against this. Every other pair of bits combines freely. */ exports.ExclusiveCallProps = [ [CallProp.Pure, exports.ImpureProps], [CallProp.NonDet, CallProp.Random | CallProp.Ambient], [CallProp.Random, CallProp.Ambient] ]; /** * The {@link CallProp} bits of calls that bring in data of their own. A function that states its props and * carries none of these derives its result from its arguments, which is what {@link BuiltInIndex#without} * looks for. */ exports.InputProps = CallProp.NonDet | CallProp.Random | CallProp.Ambient | CallProp.File | CallProp.TempFile | CallProp.Network | CallProp.Process | CallProp.Ffi | CallProp.Lang | CallProp.User | CallProp.CommandLine; /** * The {@link CallProp} bits the signature database states itself, so {@link fnInfoFromSignature} can read them * off any package function without anyone writing them down. */ exports.SigDbInferable = CallProp.Throws | CallProp.NonDet | CallProp.Method | CallProp.Generic; /** * The {@link CallProp} bits that say a call takes its data from a file, as {@link CallProp.File} alone also * covers the calls that only write one. */ exports.FileInputProps = CallProp.File | CallProp.Reads; /** * The {@link CallProp} bits that carry over from a callee to its caller: what the called function does, the * calling one does too. Purity does not travel this way, which is why it is not in here. */ exports.PropagatedProps = CallProp.Throws | CallProp.Scope | CallProp.NonDet | CallProp.Prints | CallProp.Random | CallProp.Ambient | CallProp.File | CallProp.TempFile | CallProp.Network | CallProp.Process | CallProp.Ffi | CallProp.Lang | CallProp.User | CallProp.Graphics | CallProp.Database | CallProp.Reads | CallProp.Writes | CallProp.Configures | CallProp.CommandLine; /** * Utility functions for {@link FnSig|function signatures}. */ exports.FnSig = { name: 'FnSig', /** The positional view of a signature; see {@link sigLayout}. */ layout: sigLayout, /** The roles of the argument at a position; see {@link argProp}. */ propAt: argProp, /** The positions carrying any of the given roles; see {@link argsWith}. */ posWith: argsWith }; /** the {@link CallProp} bits as the words a reader wants, in the order they are declared */ const CallPropNames = [ [CallProp.Pure, 'pure'], [CallProp.Throws, 'can throw'], [CallProp.Invisible, 'invisible'], [CallProp.Generic, 'generic'], [CallProp.Method, 's3 method'], [CallProp.Scope, 'changes scope'], [CallProp.NonDet, 'non deterministic'], [CallProp.Random, 'random'], [CallProp.Ambient, 'ambient state'], [CallProp.File, 'file system'], [CallProp.Reads, 'reads'], [CallProp.Writes, 'writes'], [CallProp.Network, 'network'], [CallProp.Prints, 'prints'] ]; /** What a call states about itself, as words rather than as a bit mask, for anything showing it to a reader. */ function callPropWords(props) { return props === undefined ? [] : CallPropNames.filter(([bit]) => (props & bit) !== 0).map(([, word]) => word); } const layouts = new WeakMap(); /** * The positional view of a {@link FnSig}, computed on first use and cached per signature object, * so declaring a signature costs nothing until a call actually needs it. */ function sigLayout(sig) { let layout = layouts.get(sig); if (layout === undefined) { const props = sig.map(p => p[1]); layout = { props, rest: sig.findIndex(p => p[0] === '...'), any: props.reduce((acc, p) => acc | p, 0), alias: props.findIndex(p => (p & ArgProp.Alias) !== 0) }; layouts.set(sig, layout); } return layout; } /** The {@link ArgProp} bits of the argument at `index`, with `...` covering every position from where it appears. */ function argProp({ props, rest }, index) { return (rest >= 0 && index >= rest ? props[rest] : props[index]) ?? 0; } /** The positions of the first `count` arguments that carry any of `prop`. */ function argsWith(layout, count, prop) { const found = []; for (let i = 0; i < count; i++) { if ((exports.FnSig.propAt(layout, i) & prop) !== 0) { found.push(i); } } return found; } /** the {@link DecodedFunction#props} names that have a {@link CallProp} counterpart, together {@link SigDbInferable} */ const SigDbProps = { 'can-throw': CallProp.Throws, 'non-deterministic': CallProp.NonDet, 's3-method': CallProp.Method }; /** the callees that make the calling function itself a generic ({@link CallProp.Generic}) */ exports.DispatchCallees = new Set(['UseMethod', 'standardGeneric', 'S7_dispatch']); /** * The part of a {@link BuiltInFnInfo} that the signature database already knows: the parameter names in order * (`...` included) with the ones R always forces, plus the properties listed in {@link SigDbProps}. Everything * else the database records (`higher-order`, `deprecated`, `recursive`, ...) has no counterpart here and is * dropped, and anything it cannot see (purity, resources, what an argument is used for) stays unset. */ function fnInfoFromSignature(fn) { let props = 0; for (const name of fn.props) { props |= SigDbProps[name] ?? 0; } /* a function whose own body dispatches is the generic, which no property of the database states */ if (fn.callees.some(c => exports.DispatchCallees.has(c))) { props |= CallProp.Generic; } return { sig: fn.signature.map(p => [p.name, (p.forced ? ArgProp.Forced : 0) | (p.default === 'TRUE' || p.default === 'FALSE' ? ArgProp.Flag : 0)]), props }; } //# sourceMappingURL=built-in-props.js.map