UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

154 lines 7.67 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.flowrConfigFileSchema = exports.defaultConfigOptions = exports.VariableResolve = void 0; exports.setConfigFile = setConfigFile; exports.parseConfig = parseConfig; exports.setConfig = setConfig; exports.amendConfig = amendConfig; exports.getConfig = getConfig; exports.getEngineConfig = getEngineConfig; const objects_1 = require("./util/objects"); const path_1 = __importDefault(require("path")); const fs_1 = __importDefault(require("fs")); const log_1 = require("./util/log"); const files_1 = require("./util/files"); const joi_1 = __importDefault(require("joi")); var VariableResolve; (function (VariableResolve) { /** Don't resolve constants at all */ VariableResolve["Disabled"] = "disabled"; /** Use alias tracking to resolve */ VariableResolve["Alias"] = "alias"; /** Only resolve directly assigned builtin constants */ VariableResolve["Builtin"] = "builtin"; })(VariableResolve || (exports.VariableResolve = VariableResolve = {})); const defaultEngineConfigs = { 'tree-sitter': { type: 'tree-sitter' }, 'r-shell': { type: 'r-shell' } }; exports.defaultConfigOptions = { ignoreSourceCalls: false, semantics: { environment: { overwriteBuiltIns: { loadDefaults: true, definitions: [] } } }, engines: [], defaultEngine: 'r-shell', solver: { variables: VariableResolve.Alias, pointerTracking: true } }; exports.flowrConfigFileSchema = joi_1.default.object({ ignoreSourceCalls: joi_1.default.boolean().optional().description('Whether source calls should be ignored, causing {@link processSourceCall}\'s behavior to be skipped.'), semantics: joi_1.default.object({ environment: joi_1.default.object({ overwriteBuiltIns: joi_1.default.object({ loadDefaults: joi_1.default.boolean().optional().description('Should the default configuration still be loaded?'), definitions: joi_1.default.array().items(joi_1.default.object()).optional().description('The definitions to load/overwrite.') }).optional().description('Do you want to overwrite (parts) of the builtin definition?') }).optional().description('Semantics regarding the handlings of the environment.') }).description('Configure language semantics and how flowR handles them.'), engines: joi_1.default.array().items(joi_1.default.alternatives(joi_1.default.object({ type: joi_1.default.string().required().valid('tree-sitter').description('Use the tree sitter engine.'), wasmPath: joi_1.default.string().optional().description('The path to the tree-sitter-r WASM binary to use. If this is undefined, this uses the default path.'), treeSitterWasmPath: joi_1.default.string().optional().description('The path to the tree-sitter WASM binary to use. If this is undefined, this uses the default path.') }).description('The configuration for the tree sitter engine.'), joi_1.default.object({ type: joi_1.default.string().required().valid('r-shell').description('Use the R shell engine.'), rPath: joi_1.default.string().optional().description('The path to the R executable to use. If this is undefined, this uses the default path.') }).description('The configuration for the R shell engine.'))).min(1).description('The engine or set of engines to use for interacting with R code. An empty array means all available engines will be used.'), defaultEngine: joi_1.default.string().optional().valid('tree-sitter', 'r-shell').description('The default engine to use for interacting with R code. If this is undefined, an arbitrary engine from the specified list will be used.'), solver: joi_1.default.object({ variables: joi_1.default.string().valid(...Object.values(VariableResolve)).description('How to resolve variables and their values.'), pointerTracking: joi_1.default.boolean().description('Whether to track pointers in the dataflow graph, if not, the graph will be over-approximated wrt. containers and accesses.') }).description('How to resolve constants, constraints, cells, ...') }).description('The configuration file format for flowR.'); // we don't load from a config file at all by default unless setConfigFile is called let configFile = undefined; let configWorkingDirectory = process.cwd(); let currentConfig = undefined; function setConfigFile(file, workingDirectory = process.cwd(), forceLoad = false) { configFile = file; configWorkingDirectory = workingDirectory; // reset the config so it gets reloaded currentConfig = undefined; if (forceLoad) { getConfig(); } } function parseConfig(jsonString) { try { const parsed = JSON.parse(jsonString); const validate = exports.flowrConfigFileSchema.validate(parsed); if (!validate.error) { // assign default values to all config options except for the specified ones return (0, objects_1.deepMergeObject)(exports.defaultConfigOptions, parsed); } else { log_1.log.error(`Failed to validate config ${jsonString}: ${validate.error.message}`); return undefined; } } catch (e) { log_1.log.error(`Failed to parse config ${jsonString}: ${e.message}`); } } function setConfig(config) { currentConfig = config; } function amendConfig(amendment) { setConfig((0, objects_1.deepMergeObject)(getConfig(), amendment)); log_1.log.trace(`Amending config with ${JSON.stringify(amendment)}, resulting in ${JSON.stringify(getConfig())}}`); } function getConfig() { // lazy-load the config based on the current settings if (currentConfig === undefined) { setConfig(loadConfigFromFile(configFile, configWorkingDirectory)); } return currentConfig; } function getEngineConfig(engine) { const config = getConfig().engines; if (!config.length) { return defaultEngineConfigs[engine]; } else { return config.find(e => e.type == engine); } } function loadConfigFromFile(configFile, workingDirectory) { if (configFile !== undefined) { if (path_1.default.isAbsolute(configFile) && fs_1.default.existsSync(configFile)) { log_1.log.trace(`Found config at ${configFile} (absolute)`); const ret = parseConfig(fs_1.default.readFileSync(configFile, { encoding: 'utf-8' })); if (ret) { log_1.log.info(`Using config ${JSON.stringify(ret)}`); return ret; } } let searchPath = path_1.default.resolve(workingDirectory); do { const configPath = path_1.default.join(searchPath, configFile); if (fs_1.default.existsSync(configPath)) { log_1.log.trace(`Found config at ${configPath}`); const ret = parseConfig(fs_1.default.readFileSync(configPath, { encoding: 'utf-8' })); if (ret) { log_1.log.info(`Using config ${JSON.stringify(ret)}`); return ret; } } // move up to parent directory searchPath = (0, files_1.getParentDirectory)(searchPath); } while (fs_1.default.existsSync(searchPath)); } log_1.log.info(`Using default config ${JSON.stringify(exports.defaultConfigOptions)}`); return exports.defaultConfigOptions; } //# sourceMappingURL=config.js.map