@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
154 lines • 7.67 kB
JavaScript
;
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