@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
148 lines • 8.06 kB
JavaScript
;
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 append_1 = require("../../../../../environments/append");
const assert_1 = require("../../../../../../util/assert");
const general_1 = require("../../../../../eval/values/general");
const string_constants_1 = require("../../../../../eval/values/string/string-constants");
const unknown_side_effect_1 = require("../../../../../graph/unknown-side-effect");
const alias_tracking_1 = require("../../../../../eval/resolve/alias-tracking");
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");
/**
* Process a call to `eval()`, trying to resolve the code being evaluated if possible.
*/
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, origin: 'default' }).information;
}
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);
const evalArgument = args[0];
if (config.includeFunctionCall) {
information.graph.addEdge(rootId, args[0].value.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.value, data.environment, data.completeAst.idMap, data.ctx);
if (code) {
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 {
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.concat(result.flatMap(r => r.out)),
in: information.in.concat(result.flatMap(r => r.in)),
unknownReferences: information.unknownReferences.concat(result.flatMap(r => r.unknownReferences)),
exitPoints: information.exitPoints.concat(result.flatMap(r => r.exitPoints)),
hooks: information.hooks.concat(result.flatMap(r => r.hooks)),
};
}
(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, env, idMap, ctx) {
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 resolved = (0, general_1.valueSetGuard)((0, alias_tracking_1.resolveIdToValue)(arg.value.info.id, { environment: env, idMap: idMap, resolve: ctx.config.solver.variables, ctx }));
if (resolved) {
return (0, string_constants_1.collectStrings)(resolved.elements);
}
}
else if (arg.value?.type === type_1.RType.FunctionCall && arg.value.named && ['paste', 'paste0'].includes(identifier_1.Identifier.getName(arg.value.functionName.content))) {
return handlePaste(ctx.config, arg.value.arguments, env, idMap, arg.value.functionName.content === 'paste' ? [' '] : [''], ctx);
}
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(config, val, env, idMap, ctx) {
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, general_1.valueSetGuard)((0, alias_tracking_1.resolveIdToValue)(val.info.id, { environment: env, idMap: idMap, resolve: config.solver.variables, ctx }));
if (resolved) {
return (0, string_constants_1.collectStrings)(resolved.elements);
}
}
return undefined;
}
function handlePaste(config, args, env, idMap, sepDefault, ctx) {
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(config, sepArg.value, env, idMap, ctx) : 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(config, v.value, env, idMap, ctx));
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