UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

118 lines 6.31 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.processReplacementFunction = processReplacementFunction; const info_1 = require("../../../../../info"); const known_call_handling_1 = require("../known-call-handling"); const log_1 = require("../../../../../../util/log"); const common_1 = require("../common"); const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call"); const node_id_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id"); const logger_1 = require("../../../../../logger"); const vertex_1 = require("../../../../../graph/vertex"); const edge_1 = require("../../../../../graph/edge"); const unpack_argument_1 = require("../argument/unpack-argument"); const built_in_access_1 = require("./built-in-access"); const built_in_1 = require("../../../../../environments/built-in"); const identifier_1 = require("../../../../../environments/identifier"); const unknown_replacement_1 = require("../../../../../graph/unknown-replacement"); const built_in_s_seven_dispatch_1 = require("./built-in-s-seven-dispatch"); const make_argument_1 = require("../argument/make-argument"); const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type"); const graph_1 = require("../../../../../graph/graph"); const range_1 = require("../../../../../../util/range"); const built_in_proc_name_1 = require("../../../../../environments/built-in-proc-name"); /** * Process a replacement function call like `<-`, `[[<-`, `$<-`, etc. * These are automatically created when doing assignments like `x[y] <- value` or in general `fun(x) <- value` will call `fun<- (x, value)`. */ function processReplacementFunction(name, /** The last one has to be the value */ args, rootId, data, config) { if (args.length < 2) { logger_1.dataflowLogger.warn(`Replacement ${identifier_1.Identifier.getName(name.content)} has less than 2 arguments, skipping`); return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: 'default' }).information; } /* we only get here if <-, <<-, ... or whatever is part of the replacement is not overwritten */ (0, log_1.expensiveTrace)(logger_1.dataflowLogger, () => `Replacement ${identifier_1.Identifier.getName(name.content)} with ${JSON.stringify(args)}, processing`); let targetArg = args[0]; if (config.constructName === 's7' && targetArg !== r_function_call_1.EmptyArgument) { let tarName = targetArg.lexeme; if (args.length > 2 && args[1] !== r_function_call_1.EmptyArgument) { tarName += built_in_s_seven_dispatch_1.S7DispatchSeparator + args[1].lexeme; } const uArg = (0, unpack_argument_1.unpackArg)(targetArg) ?? targetArg; targetArg = (0, make_argument_1.toUnnamedArgument)({ content: tarName, type: type_1.RType.Symbol, info: uArg.info, lexeme: tarName, location: uArg.location ?? targetArg.location ?? name.location ?? range_1.SourceRange.invalid() }, data.completeAst.idMap); } /* we assign the first argument by the last for now and maybe mark as maybe!, we can keep the symbol as we now know we have an assignment */ let res = built_in_1.BuiltInProcessorMapper[built_in_proc_name_1.BuiltInProcName.Assignment](name, [targetArg, args.at(-1)], rootId, data, { superAssignment: config.assignmentOperator === '<<-', makeMaybe: config.makeMaybe, canBeReplacement: true }); const createdVert = res.graph.getVertex(rootId); if (createdVert?.tag === vertex_1.VertexType.FunctionCall) { createdVert.origin = [built_in_proc_name_1.BuiltInProcName.Replacement]; } const targetVert = res.graph.getVertex((0, unpack_argument_1.unpackArg)(args[0])?.info.id); if (targetVert?.tag === vertex_1.VertexType.VariableDefinition) { targetVert.par = true; } const convertedArgs = config.readIndices ? args.slice(1, -1) : (0, built_in_access_1.symbolArgumentsToStrings)(args.slice(1, -1), 0); /* now, we soft-inject other arguments, so that calls like `x[y] <- 3` are linked correctly */ const { callArgs } = (0, common_1.processAllArguments)({ functionName: info_1.DataflowInformation.initialize(rootId, data), args: convertedArgs, data, functionRootId: rootId, finalGraph: res.graph, forceArgs: config.forceArgs, }); (0, common_1.patchFunctionCall)({ nextGraph: res.graph, data, rootId, name, argumentProcessResult: args.map(a => a === r_function_call_1.EmptyArgument ? undefined : { entryPoint: (0, unpack_argument_1.unpackNonameArg)(a)?.info.id }), origin: built_in_proc_name_1.BuiltInProcName.Replacement, link: config.assignRootId ? { origin: [config.assignRootId] } : undefined }); const firstArg = (0, unpack_argument_1.unpackNonameArg)(args[0]); (0, unknown_replacement_1.handleReplacementOperator)({ operator: name.content, target: firstArg?.lexeme, env: res.environment, id: rootId }); if (firstArg) { res.graph.addEdge(firstArg.info.id, rootId, edge_1.EdgeType.DefinedBy | edge_1.EdgeType.Reads); } /* a replacement reads all of its call args as well, at least as far as I am aware of */ for (const arg of callArgs) { const ref = graph_1.FunctionArgument.getReference(arg); if (ref !== undefined) { res.graph.addEdge(rootId, ref, edge_1.EdgeType.Reads); } } const fa = (0, unpack_argument_1.unpackNonameArg)(args[0]); if (fa) { res = { ...res, in: [...res.in, { name: fa.lexeme, type: identifier_1.ReferenceType.Variable, nodeId: fa.info.id, cds: data.cds }] }; } // dispatches actually as S3: const fns = res.in.filter(i => i.nodeId === rootId); for (const fn of fns) { fn.type = identifier_1.ReferenceType.S3MethodPrefix; } // link the built-in replacement op res.graph.addEdge(rootId, node_id_1.NodeId.toBuiltIn(identifier_1.Identifier.getName(name.content)), edge_1.EdgeType.Calls | edge_1.EdgeType.Reads); return res; } //# sourceMappingURL=built-in-replacement.js.map