UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

141 lines 7.38 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.processEvalCall = processEvalCall; const info_1 = require("../../../../../info"); 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 assert_1 = require("../../../../../../util/assert"); const unknown_side_effect_1 = require("../../../../../graph/unknown-side-effect"); const node_value_1 = require("../../../../../eval/resolve/node-value"); const arrays_1 = require("../../../../../../util/collections/arrays"); const identifier_1 = require("../../../../../environments/identifier"); const built_in_proc_name_1 = require("../../../../../environments/built-in-proc-name"); /** the formals of `eval(expr, envir, enclos)` */ const EvalParameterNames = ['expr', 'envir', 'enclos']; /** * Process a call to `eval()`, trying to resolve the code being evaluated if possible. */ function processEvalCall(name, args, rootId, data, config) { const bound = r_function_call_1.RFunctionCall.matchArgsToParams(args, EvalParameterNames); /* `evalText` names its formal differently, so a lone argument is the expression whatever it is called */ const evalArgument = (bound.get('expr') ?? r_function_call_1.RFunctionCall.soleArgument(args))?.value; const envirArg = bound.get('envir'); if (evalArgument === undefined) { logger_1.dataflowLogger.warn(`Expected an expression argument for eval, but got ${args.length} argument(s), skipping`); const bail = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: 'default' }).information; (0, unknown_side_effect_1.handleUnknownSideEffect)(bail.graph, bail.environment, rootId); return bail; } const information = config.includeFunctionCall ? (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, forceArgs: [true], origin: built_in_proc_name_1.BuiltInProcName.Eval }).information : info_1.DataflowInformation.initialize(rootId, data); if (config.includeFunctionCall) { information.graph.addEdge(rootId, evalArgument.info.id, edge_1.EdgeType.Returns); } if (!data.ctx.config.solver.evalStrings) { (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Skipping eval call ${JSON.stringify(evalArgument)} (disabled in config file)`); (0, unknown_side_effect_1.handleUnknownSideEffect)(information.graph, information.environment, rootId); return information; } const code = resolveEvalToCode(evalArgument, config, data); if (code) { if (envirArg !== undefined) { /* the code runs in another environment, so its definitions do not land in the current one and * pretending they do would produce wrong edges */ (0, unknown_side_effect_1.handleUnknownSideEffect)(information.graph, information.environment, rootId); } const idGenerator = (0, decorate_1.sourcedDeterministicCountingIdGenerator)(name.lexeme + '::' + rootId, name.location); data = { ...data, cds: code.length > 1 ? [...(data.cds ?? []), { id: rootId, when: true }] : data.cds }; 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, code.length > 1, 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.nodeId, edge_1.EdgeType.Returns); } } return (0, built_in_source_1.mergeSourced)({ ...information, entryPoint: rootId }, result); } (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Non-constant argument ${JSON.stringify(args)} for eval is currently not supported, skipping`); (0, unknown_side_effect_1.handleUnknownSideEffect)(information.graph, information.environment, rootId); return information; } function resolveEvalToCode(evalArgument, config, data) { const val = evalArgument; if (config.supportFunctionCall) { return getAsString(val, data); } else { 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.FunctionCall && arg.value.named && ['paste', 'paste0'].includes(identifier_1.Identifier.getName(arg.value.functionName.content))) { return handlePaste(arg.value.arguments, data, arg.value.functionName.content === 'paste' ? [' '] : ['']); } return getAsString(arg.value, data); } 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, data) { if (!val) { return undefined; } if (val.type === type_1.RType.String) { return [val.content.str]; } else if (val.type === type_1.RType.Symbol) { return node_value_1.NodeValue.stringsOf(val.info.id, data); } return undefined; } function handlePaste(args, data, 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, data) : 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, data)); 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