@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
399 lines • 24.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BuiltIns = exports.BuiltInEvalHandlerMapper = exports.BuiltInProcessorMapper = void 0;
exports.statesNonStandardEvaluation = statesNonStandardEvaluation;
const known_call_handling_1 = require("../internal/process/functions/call/known-call-handling");
const built_in_access_1 = require("../internal/process/functions/call/built-in/built-in-access");
const built_in_if_then_else_1 = require("../internal/process/functions/call/built-in/built-in-if-then-else");
const built_in_assignment_1 = require("../internal/process/functions/call/built-in/built-in-assignment");
const built_in_special_bin_op_1 = require("../internal/process/functions/call/built-in/built-in-special-bin-op");
const built_in_pipe_1 = require("../internal/process/functions/call/built-in/built-in-pipe");
const built_in_for_loop_1 = require("../internal/process/functions/call/built-in/built-in-for-loop");
const built_in_repeat_loop_1 = require("../internal/process/functions/call/built-in/built-in-repeat-loop");
const built_in_while_loop_1 = require("../internal/process/functions/call/built-in/built-in-while-loop");
const identifier_1 = require("./identifier");
const assert_1 = require("../../util/assert");
const built_in_replacement_1 = require("../internal/process/functions/call/built-in/built-in-replacement");
const built_in_quote_1 = require("../internal/process/functions/call/built-in/built-in-quote");
const built_in_function_definition_1 = require("../internal/process/functions/call/built-in/built-in-function-definition");
const built_in_expression_list_1 = require("../internal/process/functions/call/built-in/built-in-expression-list");
const built_in_get_1 = require("../internal/process/functions/call/built-in/built-in-get");
const r_function_call_1 = require("../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
const r_symbol_1 = require("../../r-bridge/lang-4.x/ast/model/nodes/r-symbol");
const r_argument_1 = require("../../r-bridge/lang-4.x/ast/model/nodes/r-argument");
const node_id_1 = require("../../r-bridge/lang-4.x/ast/model/processing/node-id");
const edge_1 = require("../graph/edge");
const built_in_library_1 = require("../internal/process/functions/call/built-in/built-in-library");
const built_in_source_1 = require("../internal/process/functions/call/built-in/built-in-source");
const built_in_apply_1 = require("../internal/process/functions/call/built-in/built-in-apply");
const built_in_list_1 = require("../internal/process/functions/call/built-in/built-in-list");
const built_in_vector_1 = require("../internal/process/functions/call/built-in/built-in-vector");
const built_in_rm_1 = require("../internal/process/functions/call/built-in/built-in-rm");
const built_in_eval_1 = require("../internal/process/functions/call/built-in/built-in-eval");
const vertex_1 = require("../graph/vertex");
const unknown_side_effect_1 = require("../graph/unknown-side-effect");
const resolve_1 = require("../eval/resolve/resolve");
const resolve_strings_1 = require("../eval/resolve/resolve-strings");
const resolve_operators_1 = require("../eval/resolve/resolve-operators");
const resolve_numbers_1 = require("../eval/resolve/resolve-numbers");
const built_in_eval_name_1 = require("./built-in-eval-name");
const built_in_stop_if_not_1 = require("../internal/process/functions/call/built-in/built-in-stop-if-not");
const built_in_try_catch_1 = require("../internal/process/functions/call/built-in/built-in-try-catch");
const built_in_register_hook_1 = require("../internal/process/functions/call/built-in/built-in-register-hook");
const built_in_local_1 = require("../internal/process/functions/call/built-in/built-in-local");
const built_in_s_three_dispatch_1 = require("../internal/process/functions/call/built-in/built-in-s-three-dispatch");
const built_in_recall_1 = require("../internal/process/functions/call/built-in/built-in-recall");
const built_in_s_seven_new_generic_1 = require("../internal/process/functions/call/built-in/built-in-s-seven-new-generic");
const built_in_s_seven_dispatch_1 = require("../internal/process/functions/call/built-in/built-in-s-seven-dispatch");
const r_string_1 = require("../../r-bridge/lang-4.x/ast/model/nodes/r-string");
const built_in_proc_name_1 = require("./built-in-proc-name");
const built_in_purrr_formula_1 = require("../internal/process/functions/call/built-in/built-in-purrr-formula");
const built_in_new_env_1 = require("../internal/process/functions/call/built-in/built-in-new-env");
const built_in_class_generator_1 = require("../internal/process/functions/call/built-in/built-in-class-generator");
const built_in_stack_env_1 = require("../internal/process/functions/call/built-in/built-in-stack-env");
const built_in_attach_1 = require("../internal/process/functions/call/built-in/built-in-attach");
const built_in_with_1 = require("../internal/process/functions/call/built-in/built-in-with");
const built_in_namespace_access_1 = require("../internal/process/functions/call/built-in/built-in-namespace-access");
const built_in_load_1 = require("../internal/process/functions/call/built-in/built-in-load");
const built_in_string_template_1 = require("../internal/process/functions/call/built-in/built-in-string-template");
const built_in_props_1 = require("./built-in-props");
/**
* Whether the definition states that the call does not evaluate some of its arguments the standard way, either
* by quoting them or by handing them a data mask. Only such a definition changes what the arguments read, so
* only it has to run in place of the default processor.
*/
function statesNonStandardEvaluation(definition) {
const config = definition?.config;
return config?.markArgsAsNSE !== undefined || config?.markArgsAsMasked !== undefined;
}
/**
* The symbols handed to an {@link ArgProp.Atomic} argument directly: only data works there, so `id` in `id > 2`
* names a variable and not a function `id` that happens to be in scope, even though `>` may dispatch on the
* operand's class. Anything bubbling up from a nested call is left alone, that call decided it already.
*/
function dataArgumentSymbols(args, sig) {
const layout = sig === undefined ? undefined : built_in_props_1.FnSig.layout(sig);
if (layout === undefined || (layout.any & built_in_props_1.ArgProp.Atomic) === 0) {
return undefined;
}
let symbols;
for (let i = 0; i < args.length; i++) {
const arg = args[i];
const prop = built_in_props_1.FnSig.propAt(layout, i);
if (arg === r_function_call_1.EmptyArgument || (prop & built_in_props_1.ArgProp.Atomic) === 0 || (prop & (built_in_props_1.ArgProp.Callee | built_in_props_1.ArgProp.Nse)) !== 0) {
continue;
}
const value = r_argument_1.RArgument.is(arg) ? arg.value : arg;
if (value !== undefined && r_symbol_1.RSymbol.is(value)) {
(symbols ??= new Set()).add(value.info.id);
}
}
return symbols;
}
function defaultBuiltInProcessor(name, args, rootId, data, { useAsProcessor = built_in_proc_name_1.BuiltInProcName.Default, forceArgs, readAllArguments, cfg, hasUnknownSideEffects, treatAsFnCall, markArgsAsNSE: nse, markArgsAsMasked: masked, keepArgumentOut, sig }) {
/* a signature states per argument what the individual options state for all of them at once */
const layout = sig !== undefined ? built_in_props_1.FnSig.layout(sig) : undefined;
if (layout !== undefined) {
forceArgs ??= (layout.any & built_in_props_1.ArgProp.Forced) !== 0 ? args.map((_, i) => (built_in_props_1.FnSig.propAt(layout, i) & built_in_props_1.ArgProp.Forced) !== 0) : undefined;
nse ??= (layout.any & built_in_props_1.ArgProp.Nse) !== 0 ? built_in_props_1.FnSig.posWith(layout, args.length, built_in_props_1.ArgProp.Nse) : undefined;
}
const nsePositions = nsePositionsOf(nse, args.length);
let lastEnv = data.environment;
const { information: res, processedArguments } = (0, known_call_handling_1.processKnownFunctionCall)({
name, args, rootId, data, forceArgs,
origin: useAsProcessor,
nonFunction: dataArgumentSymbols(args, sig),
/* an unevaluated argument must not read the current frame, so it is analyzed in a clean env like `quote` */
patchData: nsePositions === undefined ? undefined : (d, index) => {
if (nsePositions.has(index)) {
lastEnv = d.environment;
return { ...d, environment: d.ctx.env.makeCleanEnv() };
}
return { ...d, environment: lastEnv };
}
});
if (nsePositions !== undefined) {
dropQuotedReferences(res, processedArguments, nsePositions, lastEnv);
}
(0, known_call_handling_1.markArgumentsAsNonStandardEvaluation)(res.graph, rootId, processedArguments, nse);
(0, known_call_handling_1.markArgumentsAsNonStandardEvaluation)(res.graph, rootId, processedArguments, masked, { kind: known_call_handling_1.NseKind.DataMasked });
if (keepArgumentOut) {
res.out = [...res.out, ...processedArguments.flatMap(arg => arg?.out ?? [])];
}
if (layout !== undefined && layout.alias >= 0) {
const arg = processedArguments[layout.alias];
if (arg !== undefined) {
res.graph.addEdge(rootId, arg.entryPoint, edge_1.EdgeType.Returns);
}
}
if (readAllArguments) {
for (const arg of processedArguments) {
if (arg) {
res.graph.addEdge(rootId, arg.entryPoint, edge_1.EdgeType.Reads);
}
}
}
else if (layout !== undefined && (layout.any & (built_in_props_1.ArgProp.Value | built_in_props_1.ArgProp.Shape)) !== 0) {
for (const i of built_in_props_1.FnSig.posWith(layout, processedArguments.length, built_in_props_1.ArgProp.Value | built_in_props_1.ArgProp.Shape)) {
const arg = processedArguments[i];
if (arg) {
res.graph.addEdge(rootId, arg.entryPoint, edge_1.EdgeType.Reads);
}
}
}
if (hasUnknownSideEffects) {
if (typeof hasUnknownSideEffects === 'boolean') {
(0, unknown_side_effect_1.handleUnknownSideEffect)(res.graph, res.environment, rootId);
}
else {
(0, unknown_side_effect_1.handleUnknownSideEffect)(res.graph, res.environment, rootId, hasUnknownSideEffects);
}
}
const fnCallNames = treatAsFnCall?.[identifier_1.Identifier.getName(name.content)];
if (fnCallNames) {
for (const arg of args) {
if (arg !== r_function_call_1.EmptyArgument && arg.value && fnCallNames.includes(arg.name?.content)) {
const rhs = arg.value;
let fnName;
let fnId;
if (r_string_1.RString.is(rhs)) {
fnName = rhs.content.str;
fnId = rhs.info.id;
}
else if (r_symbol_1.RSymbol.is(rhs)) {
fnName = rhs.content;
fnId = rhs.info.id;
}
else {
continue;
}
res.graph.updateToFunctionCall({
tag: vertex_1.VertexType.FunctionCall,
id: fnId,
name: fnName,
args: [],
environment: data.environment,
onlyBuiltin: false,
cds: data.cds,
origin: [useAsProcessor]
});
}
}
}
if (cfg !== undefined) {
res.exitPoints.push({ type: cfg, nodeId: rootId, cds: data.cds });
}
return res;
}
/** The argument positions {@link markArgumentsAsNonStandardEvaluation} would mark as quoted. */
function nsePositionsOf(which, count) {
if (which === undefined) {
return undefined;
}
else if (typeof which !== 'string') {
return new Set(which);
}
const positions = new Set();
const end = which === known_call_handling_1.NseArguments.First ? Math.min(1, count) : count;
for (let i = which === known_call_handling_1.NseArguments.AllButFirst ? 1 : 0; i < end; i++) {
positions.add(i);
}
return positions;
}
/** Whatever a quoted argument saw in its clean env must not be resolved in the caller's frame afterwards. */
function dropQuotedReferences(res, processedArguments, nsePositions, lastEnv) {
const quoted = new Set();
for (const i of nsePositions) {
const arg = processedArguments[i];
for (const ref of [...arg?.in ?? [], ...arg?.unknownReferences ?? []]) {
quoted.add(ref.nodeId);
}
}
if (quoted.size > 0) {
res.in = res.in.filter(ref => !quoted.has(ref.nodeId));
res.unknownReferences = res.unknownReferences.filter(ref => !quoted.has(ref.nodeId));
}
if (nsePositions.has(processedArguments.length - 1)) {
/* the clean env of the last argument must not become the env the call leaves behind */
res.environment = lastEnv;
}
}
function defaultBuiltInProcessorReadallArgs(name, args, rootId, data, { useAsProcessor = built_in_proc_name_1.BuiltInProcName.Default, forceArgs, markArgsAsNSE: nse, markArgsAsMasked: masked, sig }) {
const { information, processedArguments } = (0, known_call_handling_1.processKnownFunctionCall)({
name, args, rootId, data, forceArgs, origin: useAsProcessor, nonFunction: dataArgumentSymbols(args, sig)
});
const g = information.graph;
for (const arg of processedArguments) {
if (arg) {
g.addEdge(rootId, arg.entryPoint, edge_1.EdgeType.Reads);
}
}
(0, known_call_handling_1.markArgumentsAsNonStandardEvaluation)(g, rootId, processedArguments, nse);
(0, known_call_handling_1.markArgumentsAsNonStandardEvaluation)(g, rootId, processedArguments, masked, { kind: known_call_handling_1.NseKind.DataMasked });
return information;
}
exports.BuiltInProcessorMapper = {
[built_in_proc_name_1.BuiltInProcName.Access]: built_in_access_1.processAccess,
[built_in_proc_name_1.BuiltInProcName.Apply]: built_in_apply_1.processApply,
[built_in_proc_name_1.BuiltInProcName.Assignment]: built_in_assignment_1.processAssignment,
[built_in_proc_name_1.BuiltInProcName.AssignmentLike]: built_in_assignment_1.processAssignmentLike,
[built_in_proc_name_1.BuiltInProcName.DefineArgument]: built_in_assignment_1.processDefineArgument,
[built_in_proc_name_1.BuiltInProcName.Default]: defaultBuiltInProcessor,
[built_in_proc_name_1.BuiltInProcName.DefaultReadAllArgs]: defaultBuiltInProcessorReadallArgs,
[built_in_proc_name_1.BuiltInProcName.StringTemplate]: built_in_string_template_1.processStringTemplate,
[built_in_proc_name_1.BuiltInProcName.Eval]: built_in_eval_1.processEvalCall,
[built_in_proc_name_1.BuiltInProcName.ExpressionList]: built_in_expression_list_1.processExpressionList,
[built_in_proc_name_1.BuiltInProcName.ForLoop]: built_in_for_loop_1.processForLoop,
[built_in_proc_name_1.BuiltInProcName.FunctionDefinition]: built_in_function_definition_1.processFunctionDefinition,
[built_in_proc_name_1.BuiltInProcName.Get]: built_in_get_1.processGet,
[built_in_proc_name_1.BuiltInProcName.IfThenElse]: built_in_if_then_else_1.processIfThenElse,
[built_in_proc_name_1.BuiltInProcName.Library]: built_in_library_1.processLibrary,
[built_in_proc_name_1.BuiltInProcName.List]: built_in_list_1.processList,
[built_in_proc_name_1.BuiltInProcName.Load]: built_in_load_1.processLoadCall,
[built_in_proc_name_1.BuiltInProcName.Local]: built_in_local_1.processLocal,
[built_in_proc_name_1.BuiltInProcName.NamespaceAccess]: built_in_namespace_access_1.processNamespaceAccess,
[built_in_proc_name_1.BuiltInProcName.Pipe]: built_in_pipe_1.processPipe,
[built_in_proc_name_1.BuiltInProcName.PurrrFormula]: built_in_purrr_formula_1.processPurrrFormula,
[built_in_proc_name_1.BuiltInProcName.Quote]: built_in_quote_1.processQuote,
[built_in_proc_name_1.BuiltInProcName.Recall]: built_in_recall_1.processRecall,
[built_in_proc_name_1.BuiltInProcName.RegisterHook]: built_in_register_hook_1.processRegisterHook,
[built_in_proc_name_1.BuiltInProcName.RepeatLoop]: built_in_repeat_loop_1.processRepeatLoop,
[built_in_proc_name_1.BuiltInProcName.Replacement]: built_in_replacement_1.processReplacementFunction,
[built_in_proc_name_1.BuiltInProcName.Rm]: built_in_rm_1.processRm,
[built_in_proc_name_1.BuiltInProcName.S3Dispatch]: built_in_s_three_dispatch_1.processS3Dispatch,
[built_in_proc_name_1.BuiltInProcName.S7NewGeneric]: built_in_s_seven_new_generic_1.processS7NewGeneric,
[built_in_proc_name_1.BuiltInProcName.S7MakeConstructor]: built_in_s_seven_new_generic_1.processMakeConstructor,
[built_in_proc_name_1.BuiltInProcName.S7Dispatch]: built_in_s_seven_dispatch_1.processS7Dispatch,
[built_in_proc_name_1.BuiltInProcName.Source]: built_in_source_1.processSourceCall,
[built_in_proc_name_1.BuiltInProcName.SpecialBinOp]: built_in_special_bin_op_1.processSpecialBinOp,
[built_in_proc_name_1.BuiltInProcName.StopIfNot]: built_in_stop_if_not_1.processStopIfNot,
[built_in_proc_name_1.BuiltInProcName.Try]: built_in_try_catch_1.processTryCatch,
[built_in_proc_name_1.BuiltInProcName.Attach]: built_in_attach_1.processAttach,
[built_in_proc_name_1.BuiltInProcName.NewEnv]: built_in_new_env_1.processNewEnv,
[built_in_proc_name_1.BuiltInProcName.ClassGenerator]: built_in_class_generator_1.processClassGenerator,
[built_in_proc_name_1.BuiltInProcName.StackEnv]: built_in_stack_env_1.processStackEnv,
[built_in_proc_name_1.BuiltInProcName.With]: built_in_with_1.processWithEnv,
[built_in_proc_name_1.BuiltInProcName.Vector]: built_in_vector_1.processVector,
[built_in_proc_name_1.BuiltInProcName.WhileLoop]: built_in_while_loop_1.processWhileLoop,
};
/**
* The value-solver function behind every {@link BuiltInEvalName}, the counterpart of the {@link BuiltInProcessorMapper}.
* A built-in picks one of these by name with its serializable {@link BuiltInFunctionDefinition#evalHandler}.
*/
exports.BuiltInEvalHandlerMapper = {
[built_in_eval_name_1.BuiltInEvalName.Vector]: resolve_1.resolveAsVector,
[built_in_eval_name_1.BuiltInEvalName.Seq]: resolve_1.resolveAsSeq,
[built_in_eval_name_1.BuiltInEvalName.Numeric]: resolve_numbers_1.resolveAsNumeric,
[built_in_eval_name_1.BuiltInEvalName.Comparison]: resolve_operators_1.resolveAsComparison,
[built_in_eval_name_1.BuiltInEvalName.Logical]: resolve_operators_1.resolveAsLogical,
[built_in_eval_name_1.BuiltInEvalName.StringFn]: resolve_strings_1.resolveAsStringFn,
[built_in_eval_name_1.BuiltInEvalName.Group]: resolve_operators_1.resolveAsGroup
};
class BuiltIns {
/**
* Register a built-in constant (like `NULL` or `TRUE`) to the given {@link BuiltIns}
*/
registerBuiltInConstant({ names, value, assumePrimitive }) {
for (const name of names) {
const n = identifier_1.Identifier.getName(name);
const id = node_id_1.NodeId.toBuiltIn(n);
const d = [{
type: identifier_1.ReferenceType.BuiltInConstant,
definedAt: id,
cds: undefined,
value,
name,
nodeId: id
}];
this.set(n, d, assumePrimitive);
}
}
/**
* Register a built-in function (like `print` or `c`) to the given {@link BuiltIns}
*/
registerBuiltInFunctions({ names, processor, config, assumePrimitive, evalHandler }) {
(0, assert_1.guard)(processor !== undefined, () => `Processor for ${JSON.stringify(names)} is undefined, maybe you have an import loop? You may run 'npm run detect-circular-deps' - although by far not all are bad`);
const mappedProcessor = exports.BuiltInProcessorMapper[processor];
(0, assert_1.guard)(mappedProcessor !== undefined, () => `Processor for ${processor} is undefined! Please pass a valid builtin name ${JSON.stringify(Object.keys(exports.BuiltInProcessorMapper))}!`);
const mappedEval = evalHandler === undefined ? undefined : exports.BuiltInEvalHandlerMapper[evalHandler];
(0, assert_1.guard)(evalHandler === undefined || mappedEval !== undefined, () => `Eval handler ${evalHandler} is unknown! Please pass a valid one of ${JSON.stringify(Object.keys(exports.BuiltInEvalHandlerMapper))}!`);
for (const name of names) {
const n = identifier_1.Identifier.getName(name);
const id = node_id_1.NodeId.toBuiltIn(n);
const d = [{
type: identifier_1.ReferenceType.BuiltInFunction,
definedAt: id,
cds: undefined,
/* eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-argument */
processor: (name, args, rootId, data) => mappedProcessor(name, args, rootId, data, config),
config,
evalHandler: mappedEval,
name,
nodeId: id
}];
this.set(n, d, assumePrimitive);
}
}
/**
* Registers all combinations of replacements
*/
registerReplacementFunctions({ names, suffixes, assumePrimitive, config }) {
const replacer = exports.BuiltInProcessorMapper[built_in_proc_name_1.BuiltInProcName.Replacement];
(0, assert_1.guard)(replacer !== undefined, () => `Processor for ${built_in_proc_name_1.BuiltInProcName.Replacement} is undefined!`);
for (const assignment of names) {
for (const suffix of suffixes) {
const effectiveName = `${identifier_1.Identifier.getName(assignment)}${suffix}`;
const id = node_id_1.NodeId.toBuiltIn(effectiveName);
const d = [{
type: identifier_1.ReferenceType.BuiltInFunction,
definedAt: id,
processor: (name, args, rootId, data) => replacer(name, args, rootId, data, { makeMaybe: true, assignmentOperator: suffix, readIndices: config.readIndices }),
config: {
...config,
assignmentOperator: suffix,
makeMaybe: true
},
name: assignment,
cds: undefined,
nodeId: id
}];
this.set(effectiveName, d, assumePrimitive);
}
}
}
/**
* Register a single {@link BuiltInDefinition} to the given memories in {@link BuiltIns}
*/
registerBuiltInDefinition(definition) {
switch (definition.type) {
case 'constant':
return this.registerBuiltInConstant(definition);
case 'function':
return this.registerBuiltInFunctions(definition);
case 'replacement':
return this.registerReplacementFunctions(definition);
}
}
/**
* The built-in {@link REnvironmentInformation|environment} is the root of all environments.
*
* For its default content (when not overwritten by a flowR config),
* see the {@link DefaultBuiltinConfig}.
*/
builtInMemory = new Map();
/**
* The twin of the {@link builtInMemory} but with less built ins defined for
* cases in which we want some commonly overwritten variables to remain open.
* If you do not know if you need the empty environment, you do not need the empty environment (right now).
* @see {@link builtInMemory}
*/
emptyBuiltInMemory = new Map();
set(identifier, definition, includeInEmptyMemory) {
this.builtInMemory.set(identifier, definition);
if (includeInEmptyMemory) {
this.emptyBuiltInMemory.set(identifier, definition);
}
}
}
exports.BuiltIns = BuiltIns;
//# sourceMappingURL=built-in.js.map