UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

282 lines 11.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.S7SyntheticFunArgSuffix = void 0; exports.processS7NewGeneric = processS7NewGeneric; exports.processMakeConstructor = processMakeConstructor; const known_call_handling_1 = require("../known-call-handling"); const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call"); const built_in_apply_1 = require("./built-in-apply"); const logger_1 = require("../../../../../logger"); const linker_1 = require("../../../../linker"); const common_1 = require("../common"); const unpack_argument_1 = require("../argument/unpack-argument"); const r_argument_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-argument"); 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 identifier_1 = require("../../../../../environments/identifier"); const node_value_1 = require("../../../../../eval/resolve/node-value"); const r_value_1 = require("../../../../../eval/values/r-value"); const vertex_1 = require("../../../../../graph/vertex"); const range_1 = require("../../../../../../util/range"); const built_in_proc_name_1 = require("../../../../../environments/built-in-proc-name"); /** * Process an S7 new generic dispatch call like `new_generic` or `setGeneric`. */ function processS7NewGeneric(name, args, rootId, data, config) { if (args.length < 1) { logger_1.dataflowLogger.warn('empty s7 new_generic, skipping'); return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: 'default' }).information; } const params = { [config.args.name]: 'name', }; if (config.args.dispatchArg) { params[config.args.dispatchArg] = 'dispatchArg'; } params[config.args.fun] = 'fun'; params['...'] = '...'; const argMaps = (0, linker_1.pMatch)((0, common_1.convertFnArguments)(args), params); const genName = (0, unpack_argument_1.unpackArg)(r_argument_1.RArgument.getWithId(args, argMaps.get('name')?.[0])); if (!genName) { return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: 'default' }).information; } const n = node_value_1.NodeValue.of(genName.info.id, data); const accessedIdentifiers = []; if (n.type === 'set') { for (const elem of n.elements) { if (elem.type === 'string' && (0, r_value_1.isValue)(elem.value)) { accessedIdentifiers.push(elem.value.str); } } } if (accessedIdentifiers.length === 0) { logger_1.dataflowLogger.warn('s7 new_generic non-resolvable skipping'); return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: 'default' }).information; } data = { ...data, currentS7name: accessedIdentifiers }; let funArg = (0, unpack_argument_1.unpackArg)(r_argument_1.RArgument.getWithId(args, argMaps.get('fun')?.[0]))?.info.id; const effectiveArgs = args.slice(); if (!funArg) { const dispatchArg = (0, unpack_argument_1.unpackArg)(r_argument_1.RArgument.getWithId(args, argMaps.get('dispatchArg')?.[0])); const newFun = makeS7DispatchFDef(name, [dispatchArg?.lexeme ?? undefined], rootId, args.length, data.completeAst.idMap); // fake it 'function([dispatch_args],...) S7_dispatch()' effectiveArgs.push(newFun[0]); funArg = newFun[1]; } const info = (0, known_call_handling_1.processKnownFunctionCall)({ name, forceArgs: 'all', args: effectiveArgs, rootId, data, origin: built_in_proc_name_1.BuiltInProcName.S7NewGeneric }).information; info.graph.addEdge(rootId, funArg, edge_1.EdgeType.Returns); info.entryPoint = funArg; const fArg = info.graph.getVertex(funArg); if (vertex_1.FunctionDefinitionVertex.is(fArg)) { fArg.mode ??= ['s4', 's7']; } return info; } /** * Process a call that **returns a function**: S7/S4 constructor factories (`make_constructor`, `new_class`, * `setClass`) and generic function factories (`Negate`, `Vectorize`, `partial`, …). We model the result as a * synthetic function definition so the assigned symbol is recognized as a **function** rather than a plain * constant. */ function processMakeConstructor(name, args, rootId, data, config) { // synthesise `function(...) S7_dispatch()` and make the call return it const [funArg, funId] = makeS7DispatchFDef(name, [], rootId, args.length, data.completeAst.idMap); const info = (0, known_call_handling_1.processKnownFunctionCall)({ name, forceArgs: 'all', args: [...args, funArg], rootId, data, origin: built_in_proc_name_1.BuiltInProcName.S7MakeConstructor }).information; info.graph.addEdge(rootId, funId, edge_1.EdgeType.Returns); info.entryPoint = funId; const fArg = info.graph.getVertex(funId); if (vertex_1.FunctionDefinitionVertex.is(fArg) && config?.mode) { fArg.mode ??= config.mode.slice(); // copy: mode is mutated in place later, config.mode is shared } if (config?.wrapIndex !== undefined) { linkWrappedFunction(info, args, config.wrapIndex, config.wrapName, data); } return info; } /** Mark the wrapped function of an eager higher-order wrapper (`Negate`/`Vectorize`/`partial`) as called. */ function linkWrappedFunction(info, args, wrapIndex, wrapName, data) { let wrapped = undefined; if (wrapName !== undefined) { wrapped = args.find(a => a !== r_function_call_1.EmptyArgument && a.name?.content === wrapName); } if (wrapped === undefined) { let pos = 0; for (const a of args) { if (a === r_function_call_1.EmptyArgument || a.name) { continue; } if (pos === wrapIndex) { wrapped = a; break; } pos++; } } if (wrapped === undefined || wrapped === r_function_call_1.EmptyArgument || !wrapped.value) { return; } const resolved = (0, built_in_apply_1.resolveFunctionArgument)(wrapped.value, data, {}); if (resolved === undefined || resolved.anonymous) { return; } const vertex = info.graph.getVertex(resolved.functionId); if (!vertex_1.UseVertex.is(vertex)) { return; } info.graph.updateToFunctionCall({ tag: vertex_1.VertexType.FunctionCall, id: resolved.functionId, name: resolved.functionName, args: [], environment: data.environment, onlyBuiltin: false, cds: data.cds, origin: [built_in_proc_name_1.BuiltInProcName.Function] }); } /** * Node-id suffix of the `fun = function(...) S7_dispatch()` argument flowR synthesizes for S7 calls that do not * carry one (`new_class`, `new_generic`, ...). It stands for no argument in the source, so consumers that reason * about how the *code* calls a function (e.g. matching a call against a package's signature history) must skip it. */ exports.S7SyntheticFunArgSuffix = '-s7-new-generic-fun-arg'; // 'function([dispatch_args],...) S7_dispatch()'; returns the value id function makeS7DispatchFDef(name, names, rootId, args, idMap) { const argNameId = rootId + '-s7-new-generic-fun-arg-name'; const r = name.location ?? range_1.SourceRange.invalid(); const argName = { type: type_1.RType.Symbol, lexeme: 'fun', content: 'fun', info: { id: argNameId, nest: name.info.nest, role: "arg-n" /* RoleInParent.ArgumentName */, fullRange: r, adToks: undefined, file: name.info.file, parent: rootId, index: 0 }, location: r, }; idMap.set(argNameId, argName); const funcNameId = rootId + '-s7-new-generic-fun-name'; const funcName = { type: type_1.RType.Symbol, lexeme: 'S7_dispatch', info: { id: funcNameId, nest: name.info.nest, role: "call-name" /* RoleInParent.FunctionCallName */, fullRange: r, adToks: undefined, file: name.info.file, parent: rootId, index: 0 }, location: r, content: identifier_1.Identifier.make('S7_dispatch', 'S7'), }; const funcBody = { type: type_1.RType.FunctionCall, location: r, lexeme: 'S7_dispatch', named: true, functionName: funcName, arguments: [], info: { id: rootId + '-s7-new-generic-fun-body', nest: name.info.nest, role: "fun-b" /* RoleInParent.FunctionDefinitionBody */, fullRange: r, adToks: undefined, file: name.info.file, parent: rootId, index: 0 } }; const fdefId = rootId + '-s7-new-generic-fun-fdef'; const argValue = { type: type_1.RType.FunctionDefinition, info: { file: name.info.file, id: fdefId, nest: name.info.nest, role: "arg-v" /* RoleInParent.ArgumentValue */, parent: rootId, index: args + 1, adToks: undefined, fullRange: r, }, lexeme: 'function', location: r, parameters: [...names.filter(assert_1.isNotUndefined), '...'].map((n, i) => { const paramId = fdefId + `-param-${i}`; const paramNameId = paramId + '-name'; const paramName = { type: type_1.RType.Symbol, lexeme: n, content: n, info: { id: paramNameId, nest: name.info.nest, role: "param-n" /* RoleInParent.ParameterName */, fullRange: r, adToks: undefined, file: name.info.file, index: i, parent: paramId }, location: r, }; const param = { type: type_1.RType.Parameter, location: r, lexeme: n, name: paramName, defaultValue: undefined, special: n === '...', info: { id: paramId, nest: name.info.nest, role: "param" /* RoleInParent.FunctionDefinitionParameter */, parent: fdefId, index: i, adToks: undefined, file: name.info.file, fullRange: r, } }; idMap.set(paramNameId, paramName); idMap.set(paramId, param); return param; }), body: funcBody, }; idMap.set(funcNameId, funcName); idMap.set(funcBody.info.id, funcBody); idMap.set(fdefId, argValue); const argId = rootId + exports.S7SyntheticFunArgSuffix; const argument = { type: type_1.RType.Argument, lexeme: 'fun', location: r, info: { id: argId, nest: name.info.nest, role: "call-arg" /* RoleInParent.FunctionCallArgument */, fullRange: r, adToks: undefined, file: name.info.file, parent: rootId, index: args + 1 }, name: argName, value: argValue }; idMap.set(argument.info.id, argument); return [argument, argValue.info.id]; } //# sourceMappingURL=built-in-s-seven-new-generic.js.map