@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
141 lines • 7.38 kB
JavaScript
"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