@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
113 lines • 6.56 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.processStackEnv = processStackEnv;
exports.resolveNodeToStackEnv = resolveNodeToStackEnv;
exports.stackEnvStateFromSource = stackEnvStateFromSource;
exports.stackEnvInheritsFields = stackEnvInheritsFields;
const known_call_handling_1 = require("../known-call-handling");
const built_in_proc_name_1 = require("../../../../../environments/built-in-proc-name");
const environment_1 = require("../../../../../environments/environment");
const vertex_1 = require("../../../../../graph/vertex");
const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type");
const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
const default_builtin_config_1 = require("../../../../../environments/default-builtin-config");
/** The rlang pronoun for the scope surrounding a data mask. */
const EnvPronoun = '.env';
/** The stack env kind a builtin/constant `name` denotes, or `undefined`. */
function stackEnvKind(name) {
return default_builtin_config_1.StackEnvBuiltins[name];
}
/**
* Processes the env-returning builtins (`globalenv`/`baseenv`/`emptyenv`), tagging the call with the
* {@link BuiltInProcName.StackEnv} origin so an assignment (`e <- globalenv()`) attaches the stack env as the
* variable's `envState`. Direct forms (`globalenv()$x`, `.GlobalEnv`) go through {@link resolveNodeToStackEnv}.
*/
function processStackEnv(name, args, rootId, data) {
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, origin: built_in_proc_name_1.BuiltInProcName.StackEnv }).information;
}
/** The fixed stack environment for a `Global`/`Base`/`Empty` kind; `undefined` for kinds that need the call's arguments. */
function fixedStackEnv(kind, data) {
switch (kind) {
case default_builtin_config_1.StackEnvKind.Global:
return { current: environment_1.REnvironment.findGlobal(data.environment.current), level: 0 };
case default_builtin_config_1.StackEnvKind.Base:
return { current: data.ctx.env.builtInEnvironment, level: 0 };
case default_builtin_config_1.StackEnvKind.Empty:
return data.ctx.env.makeEmptyEnv();
default:
return undefined;
}
}
/**
* The stack environment an AST `node` denotes directly: a `.GlobalEnv`/`.BaseEnv` symbol, a
* `globalenv()`/`baseenv()`/`emptyenv()` call, the current env via `environment()`, the parent via `parent.env(e)`,
* or a named search-path entry via `as.environment("package:x")`. Returns `undefined` otherwise.
*/
function resolveNodeToStackEnv(node, data) {
if (node === undefined) {
return undefined;
}
const name = node.type === type_1.RType.Symbol ? String(node.content)
: r_function_call_1.RFunctionCall.isNamed(node) && node.functionName.type === type_1.RType.Symbol ? String(node.functionName.content)
: undefined;
const kind = name !== undefined ? stackEnvKind(name) : undefined;
if (kind === undefined) {
return undefined;
}
const firstArg = node.type === type_1.RType.FunctionCall && node.arguments.length > 0 && node.arguments[0] !== r_function_call_1.EmptyArgument ? node.arguments[0].value : undefined;
switch (kind) {
case default_builtin_config_1.StackEnvKind.Global:
case default_builtin_config_1.StackEnvKind.Base:
case default_builtin_config_1.StackEnvKind.Empty:
return fixedStackEnv(kind, data);
case default_builtin_config_1.StackEnvKind.CallerFrame: // parent.frame(): the caller's frame, over-approximated to global (exact at a top-level call)
return fixedStackEnv(default_builtin_config_1.StackEnvKind.Global, data);
case default_builtin_config_1.StackEnvKind.Current: // environment() with no argument is the current environment
return firstArg === undefined ? { current: data.environment.current, level: data.environment.level } : undefined;
case default_builtin_config_1.StackEnvKind.Parent: {
const inner = resolveNodeToStackEnv(firstArg, data);
return inner !== undefined && !inner.current.builtInEnv ? { current: inner.current.parent, level: inner.level } : undefined;
}
case default_builtin_config_1.StackEnvKind.Named:
return firstArg?.type === type_1.RType.String ? asSearchPathEnv(firstArg.content.str, data) : undefined;
}
}
/** Resolves a named search-path entry: a `.GlobalEnv`/`.BaseEnv` name, or `package:<name>` to that package's namespace layer. */
function asSearchPathEnv(name, data) {
const fixed = fixedStackEnv(stackEnvKind(name), data);
if (fixed !== undefined) {
return fixed;
}
if (name.startsWith(environment_1.SearchPathPackagePrefix)) {
const pkg = name.slice(environment_1.SearchPathPackagePrefix.length);
for (let env = environment_1.REnvironment.findGlobal(data.environment.current).parent; env !== undefined && !env.builtInEnv; env = env.parent) {
if (env.n === pkg && env.t === environment_1.EnvType.Namespace) {
return { current: env, level: 0 };
}
}
}
return undefined;
}
/** The stack environment `sourceInfo`'s entry call refers to (`e <- globalenv()`, `e <- environment()`), or `undefined`. */
function stackEnvStateFromSource(sourceInfo, data) {
const vertex = sourceInfo.graph.getVertex(sourceInfo.entryPoint);
if (!vertex_1.FunctionCallVertex.is(vertex) || vertex.name === undefined) {
return undefined;
}
const kind = stackEnvKind(String(vertex.name));
if (vertex.origin.includes(built_in_proc_name_1.BuiltInProcName.StackEnv)) {
return fixedStackEnv(kind, data);
}
/* `environment()` goes through the default processor; with an argument it denotes a closure's env instead */
return kind === default_builtin_config_1.StackEnvKind.Current && vertex.args.length === 0 && vertex.origin.includes(built_in_proc_name_1.BuiltInProcName.Default)
? { current: data.environment.current, level: data.environment.level }
: undefined;
}
/**
* Whether a `$`/`[[` on the stack env `node` denotes reads through the enclosing scopes.
* Only the rlang pronoun `.env` does: it names the surrounding scope, while `$` on a real environment stays in its frame.
*/
function stackEnvInheritsFields(node) {
return node?.type === type_1.RType.Symbol && String(node.content) === EnvPronoun;
}
//# sourceMappingURL=built-in-stack-env.js.map