UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

148 lines 8.06 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 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