UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

232 lines 10.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.processLoadCall = processLoadCall; const known_call_handling_1 = require("../known-call-handling"); const identifier_1 = require("../../../../../environments/identifier"); const flowr_rda_file_1 = require("../../../../../../project/plugins/file-plugins/files/flowr-rda-file"); const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type"); const retriever_1 = require("../../../../../../r-bridge/retriever"); const built_in_source_1 = require("./built-in-source"); const vertex_1 = require("../../../../../graph/vertex"); const logger_1 = require("../../../../../logger"); const unknown_side_effect_1 = require("../../../../../graph/unknown-side-effect"); const edge_1 = require("../../../../../graph/edge"); const unpack_argument_1 = require("../argument/unpack-argument"); const node_value_1 = require("../../../../../eval/resolve/node-value"); const r_value_1 = require("../../../../../eval/values/r-value"); const assert_1 = require("../../../../../../util/assert"); const range_1 = require("../../../../../../util/range"); const built_in_proc_name_1 = require("../../../../../environments/built-in-proc-name"); const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call"); const built_in_envir_utils_1 = require("./built-in-envir-utils"); /** * Processes a built-in 'load' function call by retrieving the names of the variables loaded by the given file. * Example: `load(test.rda)` with two variables 'x' and 'y'. processLoadCall adds 'x' and 'y' to the dataflow graph and * adds control dependencies between the variables and the loaded file. */ function processLoadCall(name, args, rootId, data) { const { fileArg, envirArg } = getArguments(args, data); if (!fileArg) { const fn = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: 'default' }); (0, unknown_side_effect_1.handleUnknownSideEffect)(fn.information.graph, fn.information.environment, rootId); return fn.information; } const fn = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: built_in_proc_name_1.BuiltInProcName.Load }); if (data.ctx.config.ignoreLoadCalls) { logger_1.dataflowLogger.warn(`Skipping load call ${JSON.stringify(fileArg)} (disabled in config file)`); (0, unknown_side_effect_1.handleUnknownSideEffect)(fn.information.graph, fn.information.environment, rootId); return fn.information; } const envirResolution = envirArg ? (0, built_in_envir_utils_1.resolveArgToEnvir)(envirArg, data) : undefined; if (envirResolution) { fn.information.graph.addEdge(rootId, envirResolution.envirNodeId, edge_1.EdgeType.Reads); } let sourceFile; if (fileArg.type === type_1.RType.String) { sourceFile = [(0, retriever_1.removeRQuotes)(fileArg.lexeme)]; } else { const resolved = node_value_1.NodeValue.setOf(fileArg.info.id, data, { environment: envirResolution?.envirData.environment ?? data.environment }); sourceFile = resolved?.elements.map(r => r.type === 'string' && (0, r_value_1.isValue)(r.value) ? r.value.str : undefined).filter(assert_1.isNotUndefined); } if (sourceFile) { for (const candidate of sourceFile) { const path = (0, retriever_1.removeRQuotes)(candidate); let filepath = path ? (0, built_in_source_1.findSource)(data.ctx.config.solver.resolveSource, path, data) : path; if (Array.isArray(filepath)) { if (filepath.length > 1) { logger_1.dataflowLogger.warn(`Found multiple candidate files for load(${JSON.stringify(path)}): ${JSON.stringify(filepath)}, using first match`); } filepath = filepath.find(assert_1.isNotUndefined); } if (filepath === undefined) { continue; } let variables; try { const file = data.ctx.files.resolveFile(filepath); variables = file instanceof flowr_rda_file_1.FlowrRDAFile ? file.content() : null; } catch (e) { logger_1.dataflowLogger.warn(`Failed to parse RDA file ${JSON.stringify(filepath)}: ${String(e)}`); continue; } if (variables === null) { logger_1.dataflowLogger.warn(`Could not read ${JSON.stringify(filepath)} as an RDA file, treating the load as unknown`); continue; } if (variables.length === 0) { return fn.information; } let envir = envirResolution ? envirResolution.envirData.environment : fn.information.environment; const loadLocation = name.location ?? name.fullRange ?? range_1.SourceRange.invalid(); const loadCds = [...(data.cds ?? []), { id: rootId, when: true, file: filepath }]; for (const variable of variables) { if (variable.name) { envir = defineLoadedVariable({ ...variable, name: variable.name }, rootId, fn, envir, loadLocation, loadCds, data); } } return { ...fn.information, environment: envir }; } } (0, unknown_side_effect_1.handleUnknownSideEffect)(fn.information.graph, fn.information.environment, rootId); return fn.information; } function defineLoadedVariable(variable, rootId, fn, envir, loadLocation, loadCds, data) { const syntheticId = `${rootId}:loaded:${variable.name}`; const isClosure = variable.type === flowr_rda_file_1.SexpType.CloSxp; const rootInfo = data.completeAst.idMap.get(rootId)?.info; data.completeAst.idMap.set(syntheticId, { type: type_1.RType.Symbol, content: variable.name, lexeme: variable.name, location: loadLocation, namespace: undefined, info: { ...rootInfo, id: syntheticId, parent: rootId, role: "el-c" /* RoleInParent.ExpressionListChild */, } }); if (isClosure) { defineLoadedClosure(syntheticId, variable, fn, envir, loadLocation, loadCds, data, rootInfo); } else { fn.information.graph.addVertex({ tag: vertex_1.VertexType.VariableDefinition, id: syntheticId, cds: loadCds, }, envir); } const nodeToDefine = { nodeId: syntheticId, name: variable.name, type: sexpTypeToReferenceType(variable.type), definedAt: rootId, cds: loadCds, }; const newCurrent = envir.current.define(nodeToDefine); return { ...envir, current: newCurrent }; } function defineLoadedClosure(syntheticId, variable, fn, envir, loadLocation, loadCds, data, rootInfo) { const fdefId = `${syntheticId}:fdef`; const cleanEnv = data.ctx.env.makeCleanEnv(); const bodyId = `${fdefId}:body`; const body = { type: type_1.RType.ExpressionList, lexeme: undefined, grouping: undefined, children: [], location: loadLocation, info: { ...rootInfo, id: bodyId, parent: fdefId, role: "fun-b" /* RoleInParent.FunctionDefinitionBody */, } }; data.completeAst.idMap.set(bodyId, body); data.completeAst.idMap.set(fdefId, { type: type_1.RType.FunctionDefinition, parameters: [], lexeme: variable.name, location: loadLocation, body, info: { ...rootInfo, id: fdefId, parent: syntheticId, role: "el-c" /* RoleInParent.ExpressionListChild */, } }); const flow = { entryPoint: fdefId, graph: new Set(), out: [], in: [], unknownReferences: [], hooks: [], environment: cleanEnv }; fn.information.graph.addVertex({ tag: vertex_1.VertexType.FunctionDefinition, id: fdefId, cds: loadCds, environment: cleanEnv, subflow: flow, exitPoints: [], params: {}, }, cleanEnv); fn.information.graph.addVertex({ tag: vertex_1.VertexType.VariableDefinition, id: syntheticId, cds: loadCds, }, envir); fn.information.graph.addEdge(syntheticId, fdefId, edge_1.EdgeType.DefinedBy); } function sexpTypeToReferenceType(type) { if (type === undefined) { return identifier_1.ReferenceType.Unknown; } switch (type) { case flowr_rda_file_1.SexpType.NilSxp: return identifier_1.ReferenceType.Unknown; case flowr_rda_file_1.SexpType.SymSxp: case flowr_rda_file_1.SexpType.CharSxp: case flowr_rda_file_1.SexpType.LglSxp: case flowr_rda_file_1.SexpType.IntSxp: case flowr_rda_file_1.SexpType.RealSxp: case flowr_rda_file_1.SexpType.CplxSxp: case flowr_rda_file_1.SexpType.StrSxp: case flowr_rda_file_1.SexpType.RawSxp: case flowr_rda_file_1.SexpType.ListSxp: case flowr_rda_file_1.SexpType.EnvSxp: case flowr_rda_file_1.SexpType.PromSxp: case flowr_rda_file_1.SexpType.LangSxp: case flowr_rda_file_1.SexpType.DotSxp: case flowr_rda_file_1.SexpType.VecSxp: case flowr_rda_file_1.SexpType.ExprSxp: case flowr_rda_file_1.SexpType.ObjSxp: return identifier_1.ReferenceType.Variable; case flowr_rda_file_1.SexpType.CloSxp: return identifier_1.ReferenceType.Function; case flowr_rda_file_1.SexpType.SpecialSxp: case flowr_rda_file_1.SexpType.BuiltInSxp: return identifier_1.ReferenceType.BuiltInFunction; default: return identifier_1.ReferenceType.Unknown; } } function getArguments(args, data) { // prefer R's real `base::load` signature from the database, falling back to the known formals when it is absent const loadParams = (0, built_in_envir_utils_1.signatureParamNames)(data, identifier_1.Identifier.make('load', "base" /* PkgName.Base */), ['file', 'envir', 'verbose']); const bound = r_function_call_1.RFunctionCall.matchArgsToParams(args, loadParams); const fileArgBound = bound.get('file'); const envirArg = bound.get('envir'); const verboseArgBound = bound.get('verbose'); const fileArg = fileArgBound ? (0, unpack_argument_1.unpackArg)(fileArgBound) : undefined; const verboseArg = verboseArgBound ? (0, unpack_argument_1.unpackArg)(verboseArgBound) : undefined; return { fileArg, envirArg, verboseArg }; } //# sourceMappingURL=built-in-load.js.map