UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

135 lines 7.59 kB
"use strict"; 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