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