@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
130 lines • 11.3 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.InputSourcesDefinition = exports.DefaultInputClassifierConfig = void 0;
const ansi_1 = require("../../../util/text/ansi");
const time_1 = require("../../../util/text/time");
const joi_1 = __importDefault(require("joi"));
const simple_input_classifier_1 = require("./simple-input-classifier");
const slice_query_parser_1 = require("../../../cli/repl/parser/slice-query-parser");
const input_sources_query_executor_1 = require("./input-sources-query-executor");
const range_1 = require("../../../util/range");
const flowr_search_builder_1 = require("../../../search/flowr-search-builder");
const linter_format_1 = require("../../../linter/linter-format");
const record_1 = require("../../../util/record");
const read_functions_1 = require("../dependencies-query/function-info/read-functions");
const input_source_functions_1 = require("./input-source-functions");
const built_in_props_1 = require("../../../dataflow/environments/built-in-props");
const query_fn_props_1 = require("../../../dataflow/environments/query-fn-props");
const builtIns = query_fn_props_1.BuiltInIndex.default();
/**
* Which functions belong to which input type is stated with the functions themselves, in the
* {@link DefaultBuiltinConfig|built-in configuration}: a function that states its props and carries none of the
* {@link InputProps} derives its result from its arguments, the others bring in data of their own, and a
* {@link CallProp.Narrows} one bounds its result no matter what flows in.
* Add a function there (or override its props with your own built-in definitions) and it shows up here.
*/
exports.DefaultInputClassifierConfig = {
/*
* every {@link CallProp.Pure} built-in is in here (a test checks it), but the label alone is too narrow:
* what matters for provenance is that the call invents no data of its own, not that it has no effect at
* all. `x <- z <- 'x'` has to stay constant across the assignments, and `print(x)` hands `x` back, yet
* neither is `Pure` (they rebind a name, they write to the console). So the set is every built-in that
* states its props and claims none of the {@link InputProps}.
*/
[simple_input_classifier_1.InputTraceType.Pure]: builtIns.without(built_in_props_1.InputProps),
[simple_input_classifier_1.InputType.File]: [...read_functions_1.ReadFunctions.map(readFunction => readFunction.name), ...builtIns.withAll(built_in_props_1.FileInputProps)],
[simple_input_classifier_1.InputType.TempFile]: builtIns.with(built_in_props_1.CallProp.TempFile),
[simple_input_classifier_1.InputType.Glob]: builtIns.with(built_in_props_1.CallProp.Glob),
[simple_input_classifier_1.InputType.Network]: flowr_search_builder_1.Q.fromQuery({ type: 'linter', rules: ['network-functions'] }, linter_format_1.LintingResultCertainty.Certain),
[simple_input_classifier_1.InputType.Random]: flowr_search_builder_1.Q.fromQuery({ type: 'linter', rules: ['seeded-randomness'] }),
[simple_input_classifier_1.InputType.System]: builtIns.with(built_in_props_1.CallProp.Process),
[simple_input_classifier_1.InputType.Ffi]: builtIns.with(built_in_props_1.CallProp.Ffi),
[simple_input_classifier_1.InputType.Lang]: builtIns.with(built_in_props_1.CallProp.Lang),
[simple_input_classifier_1.InputType.Options]: builtIns.with(built_in_props_1.CallProp.Ambient),
[simple_input_classifier_1.InputType.CommandLine]: builtIns.with(built_in_props_1.CallProp.CommandLine),
[simple_input_classifier_1.InputType.User]: builtIns.with(built_in_props_1.CallProp.User),
linkedObjects: input_source_functions_1.LinkedInputObjects,
linkedEntryPoints: input_source_functions_1.LinkedInputEntryPoints,
narrowing: (0, input_source_functions_1.narrowingFunctions)(builtIns)
};
function inputSourcesQueryLineParser(output, line, _config) {
const criterion = (0, slice_query_parser_1.sliceCriteriaParser)(line[0]);
if (!criterion || criterion.length !== 1) {
output.stderr(output.formatter.format('Invalid input sources query format, a single slicing criterion must be given in the form "(criterion1)"', { color: 1 /* Colors.Red */, effect: ansi_1.ColorEffect.Foreground, style: 1 /* FontStyles.Bold */ }));
return { query: [] };
}
return { query: [{
type: 'input-sources',
criterion: criterion[0],
}], rCode: (0, slice_query_parser_1.queryLineCode)(line) };
}
exports.InputSourcesDefinition = {
title: 'Input Sources Query',
executor: input_sources_query_executor_1.executeInputSourcesQuery,
asciiSummarizer: async (formatter, analyzer, queryResults, result) => {
const out = queryResults;
result.push(`Query: ${(0, ansi_1.bold)('input-sources', formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
const nast = (await analyzer.normalize()).idMap;
for (const [key, sources] of Object.entries(out.results)) {
result.push(` ╰ Input Sources for ${key}`);
for (const { id, trace, types, name, value, declaredAt } of sources) {
const kNode = nast.get(id);
const kLoc = kNode ? range_1.SourceLocation.format(range_1.SourceLocation.fromNode(kNode)) : 'unknown location';
const nameStr = name !== undefined ? `, name: ${name}` : '';
const valueStr = value !== undefined ? `, value: ${JSON.stringify(value)}` : '';
const declStr = declaredAt ? `, declared at: ${declaredAt.map(d => {
const dNode = nast.get(d);
return dNode ? range_1.SourceLocation.format(range_1.SourceLocation.fromNode(dNode)) : String(d);
}).join(', ')}` : '';
result.push(` ╰ ${kLoc} (id: ${id}), type: ${JSON.stringify(types)}, trace: ${trace}${nameStr}${valueStr}${declStr}`);
}
}
return true;
},
fromLine: inputSourcesQueryLineParser,
completer: slice_query_parser_1.criteriaQueryCompleter,
syntax: '@input-sources (<criterion>) <code | file://path>',
schema: joi_1.default.object({
type: joi_1.default.string().valid('input-sources').required().description('The type of the query.'),
criterion: joi_1.default.alternatives(joi_1.default.string(), joi_1.default.array().items(joi_1.default.string())).required().description('The slicing criterion or array of criteria to use.'),
config: joi_1.default.object({
[simple_input_classifier_1.InputTraceType.Pure]: joi_1.default.array().items(joi_1.default.string()).optional().description('Deterministic/pure functions: functions that preserve constantness of their inputs (e.g., arithmetic, parse).'),
[simple_input_classifier_1.InputType.File]: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that read from the filesystem and produce data (e.g., read.csv, readRDS).'),
[simple_input_classifier_1.InputType.TempFile]: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that produce a temporary file path, which on its own touches no file system (e.g., tempfile, tempdir).'),
[simple_input_classifier_1.InputType.Glob]: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that answer with the paths they match at run time (e.g., list.files, Sys.glob).'),
[simple_input_classifier_1.InputType.Network]: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that fetch data from the network (e.g., download.file, url connections).'),
[simple_input_classifier_1.InputType.Random]: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that produce randomness (e.g., runif, rnorm).'),
[simple_input_classifier_1.InputType.System]: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that execute system commands (e.g., system, system2, shell, pipe).'),
[simple_input_classifier_1.InputType.Ffi]: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that call native code via the R FFI (.C, .Call, .Fortran, .External, dyn.load).'),
[simple_input_classifier_1.InputType.Lang]: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that produce language objects (e.g., substitute, quote, bquote, expression).'),
[simple_input_classifier_1.InputType.Options]: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that access or set global options (e.g., options, getOption).'),
[simple_input_classifier_1.InputType.CommandLine]: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that hand back what the program was invoked with (e.g., commandArgs).'),
[simple_input_classifier_1.InputType.User]: joi_1.default.array().items(joi_1.default.string()).optional().description('Functions that read interactive user input (e.g., file.choose, readline, menu, askYesNo).'),
linkedObjects: joi_1.default.array().items(joi_1.default.object({
name: joi_1.default.string().required().description('Name of the object, e.g. input.'),
type: joi_1.default.string().valid(...record_1.Record.values(simple_input_classifier_1.InputType)).required().description('How reads of the object (or of its fields) are classified.'),
withParams: joi_1.default.array().items(joi_1.default.string()).optional().description('Only link the object if the function binding it declares all of these parameters as well.')
})).optional().description('Objects a framework provides without a definition in the code, e.g. shiny\'s input.'),
linkedEntryPoints: joi_1.default.array().items(joi_1.default.object({
call: joi_1.default.string().required().description('The call taking the function, e.g. shiny::shinyApp.'),
argName: joi_1.default.string().required().description('Name of the argument holding the function.'),
argIdx: joi_1.default.number().required().description('Index of that argument when it is passed positionally.'),
params: joi_1.default.array().items(joi_1.default.string().allow(null)).required().description('Which linkedObject the framework binds to each parameter, by position.')
})).optional().description('Calls that hand a function to a framework, which binds its objects to the parameters by position.')
}).optional()
}).description('Input Sources query definition'),
flattenInvolvedNodes: (queryResults) => {
const flattened = [];
const out = queryResults;
for (const obj of Object.values(out.results)) {
for (const e of obj) {
flattened.push(e.id);
}
}
return flattened;
}
};
//# sourceMappingURL=input-sources-query-format.js.map