UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

140 lines 7.15 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.processEvalCall = processEvalCall; 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 log_1 = require("../../../../../../util/log"); const built_in_source_1 = require("./built-in-source"); const edge_1 = require("../../../../../graph/edge"); const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type"); const resolve_by_name_1 = require("../../../../../environments/resolve-by-name"); const append_1 = require("../../../../../environments/append"); const assert_1 = require("../../../../../../util/assert"); const arrays_1 = require("../../../../../../util/arrays"); function processEvalCall(name, args, rootId, data, config) { if (args.length !== 1 || args[0] === r_function_call_1.EmptyArgument || !args[0].value) { logger_1.dataflowLogger.warn(`Expected exactly one argument for eval currently, but got ${args.length} instead, skipping`); return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data }).information; } const information = config.includeFunctionCall ? (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: [true] }).information : (0, info_1.initializeCleanDataflowInformation)(rootId, data); const evalArgument = args[0]; if (config.includeFunctionCall) { information.graph.addEdge(rootId, args[0].value.info.id, edge_1.EdgeType.Returns); } if (!(0, config_1.getConfig)().solver.evalStrings) { (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Skipping eval call ${JSON.stringify(evalArgument)} (disabled in config file)`); information.graph.markIdForUnknownSideEffects(rootId); return information; } const code = resolveEvalToCode(evalArgument.value, data.environment, data.completeAst.idMap); if (code) { const idGenerator = (0, decorate_1.sourcedDeterministicCountingIdGenerator)(name.lexeme + '::' + rootId, name.location); data = { ...data, controlDependencies: [...(data.controlDependencies ?? []), { id: rootId, when: true }] }; const originalInfo = { ...information }; const result = []; for (const c of code) { const codeRequest = (0, retriever_1.requestFromInput)(c); const r = (0, built_in_source_1.sourceRequest)(rootId, codeRequest, data, originalInfo, idGenerator); result.push(r); // add a returns edge from the eval to the result for (const e of r.exitPoints) { information.graph.addEdge(rootId, e, edge_1.EdgeType.Returns); } } return { graph: result.reduce((acc, r) => acc.mergeWith(r.graph), information.graph), environment: result.reduce((acc, r) => (0, append_1.appendEnvironment)(acc, r.environment), information.environment), entryPoint: rootId, out: [...information.out, ...result.flatMap(r => r.out)], in: [...information.in, ...result.flatMap(r => r.in)], unknownReferences: [...information.unknownReferences, ...result.flatMap(r => r.unknownReferences)], exitPoints: [...information.exitPoints, ...result.flatMap(r => r.exitPoints)], }; } (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Non-constant argument ${JSON.stringify(args)} for eval is currently not supported, skipping`); information.graph.markIdForUnknownSideEffects(rootId); return information; } function resolveEvalToCode(evalArgument, env, idMap) { const val = evalArgument; if (val.type === type_1.RType.FunctionCall && val.named && val.functionName.content === 'parse') { const arg = val.arguments.find(v => v !== r_function_call_1.EmptyArgument && v.name?.content === 'text'); const nArg = val.arguments.find(v => v !== r_function_call_1.EmptyArgument && v.name?.content === 'n'); if (nArg !== undefined || arg === undefined || arg === r_function_call_1.EmptyArgument) { return undefined; } if (arg.value?.type === type_1.RType.String) { return [arg.value.content.str]; } else if (arg.value?.type === type_1.RType.Symbol) { const resolve = (0, resolve_by_name_1.resolveValueOfVariable)(arg.value.content, env, idMap); if (resolve && resolve.every(r => typeof r === 'object' && r !== null && 'str' in r)) { return resolve.map(r => r.str); } } else if (arg.value?.type === type_1.RType.FunctionCall && arg.value.named && ['paste', 'paste0'].includes(arg.value.functionName.content)) { return handlePaste(arg.value.arguments, env, idMap, arg.value.functionName.content === 'paste' ? [' '] : ['']); } return undefined; } else if (val.type === type_1.RType.Symbol) { // const resolved = resolveValueOfVariable(val.content, env); // see https://github.com/flowr-analysis/flowr/pull/1467 return undefined; } else { return undefined; } } function getAsString(val, env, idMap) { if (!val) { return undefined; } if (val.type === type_1.RType.String) { return [val.content.str]; } else if (val.type === type_1.RType.Symbol) { const resolved = (0, resolve_by_name_1.resolveValueOfVariable)(val.content, env, idMap); if (resolved && resolved.every(r => typeof r === 'object' && r !== null && 'str' in r)) { return resolved.map(r => r.str); } } return undefined; } function handlePaste(args, env, idMap, sepDefault) { const sepArg = args.find(v => v !== r_function_call_1.EmptyArgument && v.name?.content === 'sep'); if (sepArg) { const res = sepArg !== r_function_call_1.EmptyArgument && sepArg.value ? getAsString(sepArg.value, env, idMap) : undefined; if (!res) { // sep not resolvable clearly / unknown return undefined; } sepDefault = res; } const allArgs = args .filter(v => v !== r_function_call_1.EmptyArgument && v.name?.content !== 'sep' && v.value) .map(v => getAsString(v.value, env, idMap)); if (allArgs.some(assert_1.isUndefined)) { return undefined; } // return all cartesian products using the separator const result = []; const cartesianProducts = (0, arrays_1.cartesianProduct)(...allArgs); for (const sep of sepDefault) { for (const c of cartesianProducts) { result.push(c.join(sep)); } } return result; } //# sourceMappingURL=built-in-eval.js.map