@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
144 lines • 7.51 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.executeDependenciesQuery = executeDependenciesQuery;
const query_1 = require("../../query");
const dependencies_query_format_1 = require("./dependencies-query-format");
const graph_1 = require("../../../dataflow/graph/graph");
const log_1 = require("../../../util/log");
const type_1 = require("../../../r-bridge/lang-4.x/ast/model/type");
const retriever_1 = require("../../../r-bridge/retriever");
const r_function_call_1 = require("../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
const visitor_1 = require("../../../r-bridge/lang-4.x/ast/model/processing/visitor");
const assert_1 = require("../../../util/assert");
const objects_1 = require("../../../util/objects");
const SupportedVertexTypes = [type_1.RType.String, type_1.RType.Logical, type_1.RType.Number];
const Unknown = 'unknown';
function executeDependenciesQuery(data, queries) {
if (queries.length !== 1) {
log_1.log.warn('Dependencies query expects only up to one query, but got ', queries.length, 'only using the first query');
}
const now = Date.now();
const [query] = queries;
const ignoreDefault = query.ignoreDefaultFunctions ?? false;
const libraryFunctions = getFunctionsToCheck(query.libraryFunctions, ignoreDefault, dependencies_query_format_1.LibraryFunctions);
const sourceFunctions = getFunctionsToCheck(query.sourceFunctions, ignoreDefault, dependencies_query_format_1.SourceFunctions);
const readFunctions = getFunctionsToCheck(query.readFunctions, ignoreDefault, dependencies_query_format_1.ReadFunctions);
const writeFunctions = getFunctionsToCheck(query.writeFunctions, ignoreDefault, dependencies_query_format_1.WriteFunctions);
const numberOfFunctions = libraryFunctions.length + sourceFunctions.length + readFunctions.length + writeFunctions.length;
const results = numberOfFunctions === 0 ? { kinds: {}, '.meta': { timing: 0 } } : (0, query_1.executeQueriesOfSameType)(data, ...makeCallContextQuery(libraryFunctions, 'library'), ...makeCallContextQuery(sourceFunctions, 'source'), ...makeCallContextQuery(readFunctions, 'read'), ...makeCallContextQuery(writeFunctions, 'write'));
const libraries = getResults(data, results, 'library', libraryFunctions, (id, vertex, argument) => ({
nodeId: id,
functionName: vertex.name,
libraryName: argument ?? Unknown
}), [type_1.RType.Symbol]);
if (!ignoreDefault) {
/* for libraries, we have to additionally track all uses of `::` and `:::`, for this we currently simply traverse all uses */
(0, visitor_1.visitAst)(data.ast.ast, n => {
if (n.type === type_1.RType.Symbol && n.namespace) {
/* we should improve the identification of ':::' */
libraries.push({
nodeId: n.info.id,
functionName: (n.info.fullLexeme ?? n.lexeme).includes(':::') ? ':::' : '::',
libraryName: n.namespace
});
}
});
}
const sourcedFiles = getResults(data, results, 'source', sourceFunctions, (id, vertex, argument, linkedIds) => ({
nodeId: id,
functionName: vertex.name,
file: argument ?? Unknown,
linkedIds: (linkedIds?.length ?? 0) > 0 ? linkedIds : undefined
}));
const readData = getResults(data, results, 'read', readFunctions, (id, vertex, argument, linkedIds) => ({
nodeId: id,
functionName: vertex.name,
source: argument ?? Unknown,
linkedIds: (linkedIds?.length ?? 0) > 0 ? linkedIds : undefined
}));
const writtenData = getResults(data, results, 'write', writeFunctions, (id, vertex, argument, linkedIds) => ({
nodeId: id,
functionName: vertex.name,
// write functions that don't have argIndex are assumed to write to stdout
destination: argument ?? ((linkedIds?.length ?? 0) > 0 ? Unknown : 'stdout'),
linkedIds: (linkedIds?.length ?? 0) > 0 ? linkedIds : undefined
}));
return {
'.meta': {
timing: Date.now() - now
},
libraries, sourcedFiles, readData, writtenData
};
}
function makeCallContextQuery(functions, kind) {
return functions.map(f => ({
type: 'call-context',
callName: f.name,
includeAliases: false,
callNameExact: true,
subkind: f.name,
linkTo: f.linkTo ? { type: 'link-to-last-call', callName: f.linkTo } : undefined,
kind
}));
}
function getResults(data, results, kind, functions, makeInfo, additionalAllowedTypes) {
return Object.entries(results?.kinds[kind]?.subkinds ?? {}).flatMap(([name, results]) => results.flatMap(({ id, linkedIds }) => {
const vertex = data.dataflow.graph.getVertex(id);
const info = functions.find(f => f.name === name);
let index = info.argIdx;
if (info.argName) {
const arg = vertex?.args.findIndex(arg => arg !== r_function_call_1.EmptyArgument && arg.name === info.argName);
if (arg >= 0) {
index = arg;
}
}
const args = index !== undefined ? getArgumentValue(data, vertex, index, additionalAllowedTypes) : undefined;
if (!args) {
return (0, objects_1.compactRecord)(makeInfo(id, vertex, undefined, linkedIds));
}
return args.flatMap(a => (0, objects_1.compactRecord)(makeInfo(id, vertex, a, linkedIds)));
})).filter(assert_1.isNotUndefined) ?? [];
}
function getArgumentValue({ dataflow: { graph } }, vertex, argumentIndex, additionalAllowedTypes) {
if (vertex) {
if (argumentIndex === 'unnamed') {
// return all unnamed arguments
const references = vertex.args.filter(arg => arg !== r_function_call_1.EmptyArgument && !arg.name).map(graph_1.getReferenceOfArgument);
return references.map(ref => {
if (!ref) {
return undefined;
}
let valueNode = graph.idMap?.get(ref);
if (valueNode?.type === type_1.RType.Argument) {
valueNode = valueNode.value;
}
if (valueNode) {
const allowedTypes = [...SupportedVertexTypes, ...additionalAllowedTypes ?? []];
return allowedTypes.includes(valueNode.type) ? (0, retriever_1.removeRQuotes)(valueNode.lexeme) : Unknown;
}
});
}
if (vertex.args.length > argumentIndex) {
const arg = (0, graph_1.getReferenceOfArgument)(vertex.args[argumentIndex]);
if (arg) {
let valueNode = graph.idMap?.get(arg);
if (valueNode?.type === type_1.RType.Argument) {
valueNode = valueNode.value;
}
if (valueNode) {
const allowedTypes = [...SupportedVertexTypes, ...additionalAllowedTypes ?? []];
return [allowedTypes.includes(valueNode.type) ? (0, retriever_1.removeRQuotes)(valueNode.lexeme) : Unknown];
}
}
}
}
return undefined;
}
function getFunctionsToCheck(customFunctions, ignoreDefaultFunctions, defaultFunctions) {
const functions = ignoreDefaultFunctions ? [] : [...defaultFunctions];
if (customFunctions) {
functions.push(...customFunctions);
}
return functions;
}
//# sourceMappingURL=dependencies-query-executor.js.map