@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
137 lines • 7.56 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DependenciesQueryDefinition = exports.DefaultDependencyCategories = exports.Unknown = void 0;
exports.getAllCategories = getAllCategories;
const ansi_1 = require("../../../util/text/ansi");
const time_1 = require("../../../util/text/time");
const joi_1 = __importDefault(require("joi"));
const dependencies_query_executor_1 = require("./dependencies-query-executor");
const library_functions_1 = require("./function-info/library-functions");
const source_functions_1 = require("./function-info/source-functions");
const read_functions_1 = require("./function-info/read-functions");
const write_functions_1 = require("./function-info/write-functions");
const visualize_functions_1 = require("./function-info/visualize-functions");
const type_1 = require("../../../r-bridge/lang-4.x/ast/model/type");
const test_functions_1 = require("./function-info/test-functions");
const identifier_1 = require("../../../dataflow/environments/identifier");
const r_project_1 = require("../../../r-bridge/lang-4.x/ast/model/nodes/r-project");
const model_1 = require("../../../r-bridge/lang-4.x/ast/model/model");
exports.Unknown = 'unknown';
exports.DefaultDependencyCategories = {
'library': {
queryDisplayName: 'Libraries',
functions: library_functions_1.LibraryFunctions,
defaultValue: exports.Unknown,
/* for libraries, we have to additionally track all uses of `::` and `:::`, for this we currently simply traverse all uses */
additionalAnalysis: async (data, ignoreDefault, _functions, _queryResults, result) => {
if (!ignoreDefault) {
r_project_1.RProject.visitAst((await data.analyzer.normalize()).ast, node => {
let ns;
if (node.type === type_1.RType.Symbol && (ns = identifier_1.Identifier.getNamespace(node.content)) !== undefined) {
const dep = data.analyzer.inspectContext().deps.getDependency(ns);
/* we should improve the identification of ':::' */
result.push({
nodeId: node.info.id,
functionName: model_1.RNode.lexeme(node).includes(':::') ? ':::' : '::',
value: ns,
versionConstraints: dep?.versionConstraints,
derivedVersion: dep?.derivedVersion,
namespaceInfo: dep?.namespaceInfo
});
}
});
}
}
},
'source': {
queryDisplayName: 'Sourced Files',
functions: source_functions_1.SourceFunctions,
defaultValue: exports.Unknown
},
'read': {
queryDisplayName: 'Read Data',
functions: read_functions_1.ReadFunctions,
defaultValue: exports.Unknown
},
'write': {
queryDisplayName: 'Written Data',
functions: write_functions_1.WriteFunctions,
defaultValue: 'stdout'
},
'visualize': {
queryDisplayName: 'Visualizations',
functions: visualize_functions_1.VisualizeFunctions
},
'test': {
queryDisplayName: 'Tests',
functions: test_functions_1.TestFunctions
}
};
function printResultSection(title, infos, result) {
if (infos.length <= 0) {
return;
}
result.push(` ╰ ${title}`);
const grouped = infos.reduce(function (groups, i) {
const array = groups.get(i.functionName);
if (array) {
array.push(i);
}
else {
groups.set(i.functionName, [i]);
}
return groups;
}, new Map());
for (const [functionName, infos] of grouped) {
result.push(` ╰ \`${functionName}\``);
result.push(infos.map(i => ` ╰ Node Id: ${i.nodeId}${i.value !== undefined ? `, \`${i.value}\`` : ''}${i.derivedVersion !== undefined ? `, Version: \`${i.derivedVersion.format()}\`` : ''}${i.linkedIds ? `, linked: [${i.linkedIds.join(', ')}]` : ''}`).join('\n'));
}
}
/**
* Gets all dependency categories, including user-defined additional categories.
*/
function getAllCategories(queries) {
let categories = exports.DefaultDependencyCategories;
for (const query of queries) {
if (query.additionalCategories) {
categories = { ...categories, ...query.additionalCategories };
}
}
return categories;
}
const functionInfoSchema = joi_1.default.array().items(joi_1.default.object({
name: joi_1.default.string().required().description('The name of the library function.'),
package: joi_1.default.string().optional().description('The package name of the library function'),
argIdx: joi_1.default.number().optional().description('The index of the argument that contains the library name.'),
argName: joi_1.default.string().optional().description('The name of the argument that contains the library name.'),
})).optional();
exports.DependenciesQueryDefinition = {
executor: dependencies_query_executor_1.executeDependenciesQuery,
asciiSummarizer: (formatter, _analyzer, queryResults, result, queries) => {
const out = queryResults;
result.push(`Query: ${(0, ansi_1.bold)('dependencies', formatter)} (${(0, time_1.printAsMs)(out['.meta'].timing, 0)})`);
for (const [category, value] of Object.entries(getAllCategories(queries))) {
printResultSection(value.queryDisplayName ?? category, out[category] ?? [], result);
}
return true;
},
schema: joi_1.default.object({
type: joi_1.default.string().valid('dependencies').required().description('The type of the query.'),
ignoreDefaultFunctions: joi_1.default.boolean().optional().description('Should the set of functions that are detected by default be ignored/skipped? Defaults to false.'),
...Object.fromEntries(Object.keys(exports.DefaultDependencyCategories).map(c => [`${c}Functions`, functionInfoSchema.description(`The set of ${c} functions to search for.`)])),
enabledCategories: joi_1.default.array().optional().items(joi_1.default.string()).description('A set of flags that determines what types of dependencies are searched for. If unset, all dependency types are searched for.'),
additionalCategories: joi_1.default.object().allow(joi_1.default.object({
queryDisplayName: joi_1.default.string().description('The display name in the query result.'),
functions: functionInfoSchema.description('The functions that this additional category should search for.'),
defaultValue: joi_1.default.string().description('The default value to return when there is no value to gather from the function information.').optional()
})).description('A set of additional, user-supplied dependency categories, whose results will be included in the query return value.').optional()
}).description('The dependencies query retrieves and returns the set of all dependencies in the dataflow graph, which includes libraries, sourced files, read data, and written data.'),
flattenInvolvedNodes: (queryResults, query) => {
const out = queryResults;
return Object.keys(getAllCategories(query)).flatMap(c => out[c] ?? []).map(o => o.nodeId);
}
};
//# sourceMappingURL=dependencies-query-format.js.map