@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
135 lines • 7.59 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.setSourceProvider = setSourceProvider;
exports.processSourceCall = processSourceCall;
exports.sourceRequest = sourceRequest;
exports.standaloneSourceFile = standaloneSourceFile;
const processor_1 = require("../../../../../processor");
const info_1 = require("../../../../../info");
const config_1 = require("../../../../../../config");
const known_call_handling_1 = require("../known-call-handling");
const retriever_1 = require("../../../../../../r-bridge/retriever");
const decorate_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/processing/decorate");
const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
const logger_1 = require("../../../../../logger");
const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type");
const overwrite_1 = require("../../../../../environments/overwrite");
const log_1 = require("../../../../../../util/log");
const fs_1 = __importDefault(require("fs"));
const parser_1 = require("../../../../../../r-bridge/lang-4.x/ast/parser/json/parser");
const shell_executor_1 = require("../../../../../../r-bridge/shell-executor");
const resolve_by_name_1 = require("../../../../../environments/resolve-by-name");
const assert_1 = require("../../../../../../util/assert");
let sourceProvider = (0, retriever_1.requestProviderFromFile)();
function setSourceProvider(provider) {
sourceProvider = provider;
}
function processSourceCall(name, args, rootId, data, config) {
const information = config.includeFunctionCall ?
(0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information
: (0, info_1.initializeCleanDataflowInformation)(rootId, data);
const sourceFileArgument = args[0];
if (!config.forceFollow && (0, config_1.getConfig)().ignoreSourceCalls) {
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Skipping source call ${JSON.stringify(sourceFileArgument)} (disabled in config file)`);
information.graph.markIdForUnknownSideEffects(rootId);
return information;
}
let sourceFile;
if (sourceFileArgument !== r_function_call_1.EmptyArgument && sourceFileArgument?.value?.type === type_1.RType.String) {
sourceFile = [(0, retriever_1.removeRQuotes)(sourceFileArgument.lexeme)];
}
else if (sourceFileArgument !== r_function_call_1.EmptyArgument) {
sourceFile = (0, resolve_by_name_1.resolveValueOfVariable)(sourceFileArgument.value?.lexeme, data.environment, data.completeAst.idMap)?.map(x => {
if (typeof x === 'object' && x && 'str' in x) {
return x.str;
}
else {
return undefined;
}
}).filter(assert_1.isNotUndefined);
}
if (sourceFile && sourceFile.length === 1) {
const path = (0, retriever_1.removeRQuotes)(sourceFile[0]);
const request = sourceProvider.createRequest(path);
// check if the sourced file has already been dataflow analyzed, and if so, skip it
if (data.referenceChain.includes((0, retriever_1.requestFingerprint)(request))) {
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Found loop in dataflow analysis for ${JSON.stringify(request)}: ${JSON.stringify(data.referenceChain)}, skipping further dataflow analysis`);
information.graph.markIdForUnknownSideEffects(rootId);
return information;
}
return sourceRequest(rootId, request, data, information, (0, decorate_1.sourcedDeterministicCountingIdGenerator)(path, name.location));
}
else {
(0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Non-constant argument ${JSON.stringify(sourceFile)} for source is currently not supported, skipping`);
information.graph.markIdForUnknownSideEffects(rootId);
return information;
}
}
function sourceRequest(rootId, request, data, information, getId) {
if (request.request === 'file') {
/* check if the file exists and if not, fail */
if (!fs_1.default.existsSync(request.content)) {
logger_1.dataflowLogger.warn(`Failed to analyze sourced file ${JSON.stringify(request)}: file does not exist`);
information.graph.markIdForUnknownSideEffects(rootId);
return information;
}
}
// parse, normalize and dataflow the sourced file
let normalized;
let dataflow;
try {
const file = request.request === 'file' ? request.content : undefined;
const parsed = (!data.parser.async ? data.parser : new shell_executor_1.RShellExecutor()).parse(request);
normalized = (typeof parsed !== 'string' ?
(0, parser_1.normalizeTreeSitter)({ parsed }, getId, file) : (0, parser_1.normalize)({ parsed }, getId, file));
dataflow = (0, processor_1.processDataflowFor)(normalized.ast, {
...data,
currentRequest: request,
environment: information.environment,
referenceChain: [...data.referenceChain, (0, retriever_1.requestFingerprint)(request)]
});
}
catch (e) {
logger_1.dataflowLogger.warn(`Failed to analyze sourced file ${JSON.stringify(request)}, skipping: ${e.message}`);
information.graph.markIdForUnknownSideEffects(rootId);
return information;
}
// take the entry point as well as all the written references, and give them a control dependency to the source call to show that they are conditional
if (dataflow.graph.hasVertex(dataflow.entryPoint)) {
dataflow.graph.addControlDependency(dataflow.entryPoint, rootId, true);
}
for (const out of dataflow.out) {
dataflow.graph.addControlDependency(out.nodeId, rootId, true);
}
// update our graph with the sourced file's information
const newInformation = { ...information };
newInformation.environment = (0, overwrite_1.overwriteEnvironment)(information.environment, dataflow.environment);
newInformation.graph.mergeWith(dataflow.graph);
// this can be improved, see issue #628
for (const [k, v] of normalized.idMap) {
data.completeAst.idMap.set(k, v);
}
return newInformation;
}
function standaloneSourceFile(inputRequest, data, uniqueSourceId, information) {
const path = inputRequest.request === 'file' ? inputRequest.content : '-inline-';
/* this way we can still pass content */
const request = inputRequest.request === 'file' ? sourceProvider.createRequest(inputRequest.content) : inputRequest;
const fingerprint = (0, retriever_1.requestFingerprint)(request);
// check if the sourced file has already been dataflow analyzed, and if so, skip it
if (data.referenceChain.includes(fingerprint)) {
logger_1.dataflowLogger.info(`Found loop in dataflow analysis for ${JSON.stringify(request)}: ${JSON.stringify(data.referenceChain)}, skipping further dataflow analysis`);
information.graph.markIdForUnknownSideEffects(uniqueSourceId);
return information;
}
return sourceRequest(uniqueSourceId, request, {
...data,
currentRequest: request,
environment: information.environment,
referenceChain: [...data.referenceChain, fingerprint]
}, information, (0, decorate_1.deterministicPrefixIdGenerator)(path + '@' + uniqueSourceId));
}
//# sourceMappingURL=built-in-source.js.map