@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
580 lines • 28.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.processLibrary = processLibrary;
exports.attachExportVertex = attachExportVertex;
exports.loadNodesForNamespace = loadNodesForNamespace;
exports.attachDependencyToEnvironment = attachDependencyToEnvironment;
exports.attachBaseRNamespaces = attachBaseRNamespaces;
exports.attachDeclaredDependencies = attachDeclaredDependencies;
exports.attachProjectImports = attachProjectImports;
exports.attachProject = attachProject;
const r_value_1 = require("../../../../../eval/values/r-value");
const known_call_handling_1 = require("../known-call-handling");
const r_function_call_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call");
const r_access_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-access");
const r_logical_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-logical");
const node_id_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/processing/node-id");
const logger_1 = require("../../../../../logger");
const type_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/type");
const make_argument_1 = require("../argument/make-argument");
const identifier_1 = require("../../../../../environments/identifier");
const built_in_proc_name_1 = require("../../../../../environments/built-in-proc-name");
const environment_1 = require("../../../../../environments/environment");
const edge_1 = require("../../../../../graph/edge");
const assert_1 = require("../../../../../../util/assert");
const r_argument_1 = require("../../../../../../r-bridge/lang-4.x/ast/model/nodes/r-argument");
const node_value_1 = require("../../../../../eval/resolve/node-value");
const package_1 = require("../../../../../../project/plugins/package-version-plugins/package");
const attached_packages_1 = require("../../../../../../project/attached-packages");
const flowr_namespace_file_1 = require("../../../../../../project/plugins/file-plugins/files/flowr-namespace-file");
const common_1 = require("../common");
const linker_1 = require("../../../../linker");
const vertex_1 = require("../../../../../graph/vertex");
const r_base_packages_1 = require("../../../../../../util/r-base-packages");
const built_in_envir_utils_1 = require("./built-in-envir-utils");
/**
* Process a library call like `library` or `require`
*/
function processLibrary(name, args, rootId, data, config = {}) {
/* we do not really know what loading the library does and what side effects it causes, hence we mark it as an unknown side effect */
if (args.length === 0) {
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, hasUnknownSideEffect: true, origin: 'default' }).information;
}
if (config.boxUse) {
return processUse(name, args, rootId, data);
}
/* parse the import selection before the library flow rewrites `args` below */
const parsedSpec = config.fromImports ? parseFromSpec(args) : { namespaceOnly: config.namespaceOnly };
const params = {
'package': 'pkg',
'character.only': 'char',
/* last, so the positional fallback keeps its previous order */
'pos': 'pos'
};
const argMaps = (0, linker_1.pMatch)((0, common_1.convertFnArguments)(args), params);
const packageId = Array.from(new Set(argMaps.get('pkg')));
const charId = Array.from(new Set(argMaps.get('char')));
/* `import::from` has no `pos`; its extra arguments name exports */
const spec = { ...parsedSpec, pos: config.fromImports ? undefined : (0, built_in_envir_utils_1.resolveAttachPosition)(argMaps.get('pos')?.[0], data) };
let namesToLoad = packageId.map(v => r_argument_1.RArgument.getValue(args, v));
//check if library name provided
namesToLoad = namesToLoad.filter(v => v !== undefined && (v.type === type_1.RType.Symbol || v.type === type_1.RType.String));
if (namesToLoad.length === 0) {
logger_1.dataflowLogger.warn('No library name provided, skipping');
return (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, hasUnknownSideEffect: true, origin: 'default' }).information;
}
for (const nameToLoad of namesToLoad) {
if (nameToLoad !== undefined && (nameToLoad.type === type_1.RType.Symbol || nameToLoad.type === type_1.RType.String) && identifier_1.Identifier.getNamespace(nameToLoad.type === type_1.RType.String ? nameToLoad.content.str : nameToLoad.content) !== undefined) {
logger_1.dataflowLogger.warn('Namespaced library names are not supported, ignoring namespace of library: ', nameToLoad);
}
}
let isCharacterOnly = config.characterOnly === true;
if (!config.characterOnly && charId.length >= 1) {
const values = node_value_1.NodeValue.setOf(charId[0], data);
if (values?.type === 'set' && values?.elements.length > 0) {
let hasTrue = 0;
let hasFalse = 0;
let hasMaybe = 0;
for (const elem of values.elements) {
if (elem.type === 'logical') {
switch (elem.value) {
case true:
hasTrue++;
break;
case false:
hasFalse++;
break;
default:
hasMaybe++;
break;
}
}
}
if (hasMaybe > 0) {
isCharacterOnly = 'maybe';
}
else if (hasTrue === 0 && hasFalse > 0) {
isCharacterOnly = false;
}
else if (hasTrue > 0 && hasFalse === 0) {
isCharacterOnly = true;
}
else {
isCharacterOnly = 'maybe';
}
}
}
const packetName = [];
//case: true or maybe
if (isCharacterOnly) {
for (const nameToLoad of namesToLoad) {
const values = node_value_1.NodeValue.setOf(nameToLoad.info.id, data);
if (values?.type === 'set' && values.elements.length !== 0) {
for (const elem of values.elements) {
const name = r_value_1.RValue.stringOf(elem);
if (name !== undefined) {
packetName.push(name);
}
}
}
}
}
if (!isCharacterOnly || isCharacterOnly === 'maybe') {
for (const nameToLoad of namesToLoad) {
// a quoted literal (`requireNamespace("pkg")`) carries its name in `content.str`, not the quoted `lexeme`
const packageName = nameToLoad.type === type_1.RType.String ? nameToLoad.content.str : nameToLoad.lexeme;
if ((0, assert_1.isNotUndefined)(packageName)) {
packetName.push(packageName);
}
}
}
if (!isCharacterOnly || isCharacterOnly === 'maybe') {
// treat as a function call but convert the first argument to a string
const newArgs = [];
for (const nameToLoad of namesToLoad) {
if (!(nameToLoad.type === type_1.RType.Symbol || nameToLoad.type === type_1.RType.String)) {
continue;
}
const newArg = nameToLoad.type === type_1.RType.String ? nameToLoad : {
type: type_1.RType.String,
info: nameToLoad.info,
lexeme: nameToLoad.lexeme,
location: nameToLoad.location,
content: {
quotes: 'none',
str: identifier_1.Identifier.getName(nameToLoad.content)
}
};
newArgs.push(newArg);
}
args = (0, make_argument_1.wrapArgumentsUnnamed)([...newArgs, ...args.slice(1)], data.completeAst.idMap);
}
const info = (0, known_call_handling_1.processKnownFunctionCall)({
name,
args, rootId, data,
hasUnknownSideEffect: false,
origin: built_in_proc_name_1.BuiltInProcName.Library
}).information;
for (const p of packetName) {
const dependency = data.ctx.deps.loadDependency(p);
if (dependency) {
linkLibrary(dependency, info, rootId, data, spec);
}
else {
if (!data.ctx.env.knowsPackage(p)) {
info.graph.markIdForUnknownSideEffects(rootId);
}
if (info.environment.level >= 0) {
info.environment = recordUnresolvedLibraryLoad(info.environment, p, rootId, spec.pos, data.cds);
}
}
}
if (packetName.length === 0) {
info.graph.markIdForUnknownSideEffects(rootId);
}
return info;
}
/** The name of a symbol or string literal node, or `undefined` for anything else. */
function symbolOrStringName(node) {
if (node?.type === type_1.RType.Symbol) {
return identifier_1.Identifier.getName(node.content);
}
if (node?.type === type_1.RType.String) {
return node.content.str;
}
return undefined;
}
/** The string literals of a `"x"` or `c("x", "y")` node (used for `import::from`'s `.except`). */
function stringLiterals(node) {
if (node.type === type_1.RType.String) {
return [node.content.str];
}
if (r_function_call_1.RFunctionCall.isNamed(node) && identifier_1.Identifier.getName(node.functionName.content) === 'c') {
return node.arguments.flatMap(a => a !== r_function_call_1.EmptyArgument && a.value?.type === type_1.RType.String ? [a.value.content.str] : []);
}
return [];
}
/** Parse `import::from(pkg, a, keep = filter, .all = TRUE, .except = c(...))` into which exports to attach. */
function parseFromSpec(args) {
const include = new Map();
const exclude = new Set();
let all = false;
for (let i = 1; i < args.length; i++) {
const arg = args[i];
if (arg === r_function_call_1.EmptyArgument || arg.value === undefined) {
continue;
}
const argName = arg.name?.lexeme;
if (argName === '.all') {
all ||= r_logical_1.RLogical.isTrue(arg.value);
continue;
}
if (argName === '.except') {
for (const s of stringLiterals(arg.value)) {
exclude.add(s);
}
all = true;
continue;
}
if (argName?.startsWith('.')) {
continue; // other control args (.into, .library, ...) do not affect which exports resolve
}
const exported = symbolOrStringName(arg.value);
if (exported !== undefined) {
include.set(argName ?? exported, exported);
}
}
return {
include: include.size > 0 ? include : undefined,
exclude: exclude.size > 0 ? exclude : undefined,
all: all || exclude.size > 0
};
}
/** Parse a `box::use` bracket argument (`pkg[a, b]` or `pkg[...]`) into a package and its attach spec; `undefined` if not a bracket. */
function parseBoxSpec(first) {
if (first === undefined || !r_access_1.RAccess.isIndex(first)) {
return undefined;
}
const pack = symbolOrStringName(first.accessed);
if (pack === undefined) {
return undefined;
}
const include = new Map();
let all = false;
for (const el of first.access) {
if (el === r_function_call_1.EmptyArgument || el.value === undefined) {
continue;
}
if (el.value.type === type_1.RType.Symbol && identifier_1.Identifier.getName(el.value.content) === '...') {
all = true; // use(pkg[...]) attaches every export
continue;
}
const exported = symbolOrStringName(el.value);
if (exported !== undefined) {
include.set(el.name?.lexeme ?? exported, exported);
}
}
return { pack, spec: { include: include.size > 0 ? include : undefined, all } };
}
/** Whether `use` should be read as `box::use` here: a `box::`-qualified call, or `box` is a loaded dependency. */
function usesBoxSemantics(name, data) {
if (identifier_1.Identifier.getNamespace(name.content) === "box" /* PkgName.Box */) {
return true;
}
return data.ctx.deps.getDependency("box" /* PkgName.Box */) !== undefined;
}
/**
* Process a `use` call, library-sensitively: `pkg[...]` uses box's bracket selection; a bare `pkg` is box's
* namespace-only member access when box is loaded, otherwise `import::from`-style extra-argument selection.
*/
function processUse(name, args, rootId, data) {
const info = (0, known_call_handling_1.processKnownFunctionCall)({ name, args, rootId, data, hasUnknownSideEffect: false, origin: built_in_proc_name_1.BuiltInProcName.Library }).information;
const first = args[0] === r_function_call_1.EmptyArgument ? undefined : args[0]?.value;
const parsed = parseUseSpec(name, first, args, data);
const dependency = parsed && data.ctx.deps.getDependency(parsed.pack);
if (parsed && dependency) {
linkLibrary(dependency, info, rootId, data, parsed.spec);
}
else {
info.graph.markIdForUnknownSideEffects(rootId);
}
return info;
}
/** The package and attach spec for a `use` call (see {@link processUse}). */
function parseUseSpec(name, first, args, data) {
const bracket = parseBoxSpec(first);
if (bracket !== undefined) {
return bracket;
}
const pack = symbolOrStringName(first);
if (pack === undefined) {
return undefined;
}
if (usesBoxSemantics(name, data)) {
return { pack, spec: { namespaceOnly: true } }; // box: use(pkg) is member access via pkg$fn
}
return { pack, spec: parseFromSpec(args) }; // extra-argument selection: use(pkg, a, b) / use(pkg)
}
/** Materialize the empty built-in function-definition vertex for a package export (idempotent). */
function attachExportVertex(graph, builtInId, environment, ctx, cds) {
if (graph.hasVertex(builtInId)) {
return;
}
graph.addVertex({
tag: vertex_1.VertexType.FunctionDefinition,
id: builtInId,
environment, cds, params: {},
subflow: { graph: new Set(), unknownReferences: [], in: [], out: [], environment, entryPoint: builtInId, hooks: [] },
exitPoints: [],
}, ctx.env.makeCleanEnv());
}
/** Reserved marker binding recording an unresolved `library()`/`require()` load; the leading space cannot collide with a real export name. */
const libraryLoadMarker = ' library-load';
/**
* Record a syntactically known but database-unresolved package load below the global environment: a bare
* {@link EnvType.LoadedNamespace} layer for `pack` carrying only the reserved {@link libraryLoadMarker} whose
* `definedAt` is the load call. This lets an explicit `pack::fn` link back via {@link loadNodesForNamespace}
* even without a signature database.
*/
function recordUnresolvedLibraryLoad(envInfo, pack, rootId, pos, cds) {
const layer = new environment_1.Environment(envInfo.current).asLibrary(pack, environment_1.EnvType.LoadedNamespace).define({
name: identifier_1.Identifier.make(libraryLoadMarker, pack),
type: identifier_1.ReferenceType.Function,
nodeId: rootId,
definedAt: rootId,
cds: cds?.slice()
});
return { level: envInfo.level, current: environment_1.REnvironment.attachAt(envInfo.current, layer, layer, pos) };
}
/**
* The load calls (`library()`/`require()`) that brought package `pack` into scope without a database, collected from
* the {@link libraryLoadMarker} of every matching {@link EnvType.LoadedNamespace} layer below the global environment.
*/
function loadNodesForNamespace(env, pack) {
const nodes = [];
if (env.current.builtInEnv) {
return nodes; // resolving straight in the built-in environment (`get(x, envir = baseenv())`): no search path above it
}
for (let e = environment_1.REnvironment.findGlobal(env.current).parent; e.t !== undefined && !e.builtInEnv; e = e.parent) {
if (e.n !== pack) {
continue;
}
for (const def of e.memory.get(libraryLoadMarker) ?? []) {
const definedAt = def.definedAt;
if (definedAt !== undefined) {
nodes.push(definedAt);
}
}
}
return nodes;
}
function linkLibrary(dependency, info, rootId, data, spec = {}) {
if (info.environment.level < 0 || (0, assert_1.isUndefined)(dependency.namespaceInfo)) {
return;
}
const pack = dependency.name;
// re-loading an already attached package is a no-op, cf. R's `search()`
if (isAttached(info.environment.current, pack, spec.namespaceOnly)) {
return;
}
// by default only the environment carries the exports; their built-in vertices are materialized on
// demand when a call resolves to one (see attachExportVertex). Eager mode registers them all upfront.
if (data.ctx.config.solver.sigdb.eagerlyLoadExports) {
for (const { exported: func } of selectExports((0, flowr_namespace_file_1.getCallables)(dependency.namespaceInfo), spec)) {
const builtInId = node_id_1.NodeId.fromPkgFn(pack, func);
attachExportVertex(info.graph, builtInId, info.environment, data.ctx, data.cds);
info.graph.addEdge(builtInId, rootId, edge_1.EdgeType.Reads | edge_1.EdgeType.Calls);
}
}
info.environment = attachDependencyToEnvironment(dependency, info.environment, data.ctx, spec, rootId);
}
/** The exports of `callables` to attach under `spec` (see {@link AttachSpec}), resolving selection and aliasing. */
function selectExports(callables, spec) {
if (spec.include !== undefined && !spec.all) {
const available = new Set(callables);
return Array.from(spec.include, ([as, exported]) => ({ exported, as })).filter(e => available.has(e.exported));
}
return callables.filter(c => !spec.exclude?.has(c)).map(c => ({ exported: c, as: c }));
}
/** The identifier definition binding a package export (or its alias) to its built-in function-definition. */
function exportDefinition(pack, exp, definedAt = node_id_1.NodeId.toBuiltIn(pack)) {
return {
name: identifier_1.Identifier.make(exp.as, pack),
type: identifier_1.ReferenceType.Function,
nodeId: node_id_1.NodeId.fromPkgFn(pack, exp.exported),
definedAt,
};
}
/** Whether a subset import restricts the attached exports, so no imports layer is materialized. */
function isSubsetAttach(spec) {
return spec.include !== undefined && !spec.all;
}
/**
* Attaches `dependency`'s exports at `spec`'s {@link AttachSpec#pos|search position} (below the global environment by
* default, see {@link REnvironment.attachAt|attachPackageAt}) and returns the
* enriched environment (the graph is untouched). Used by `library()`, `import::from`, `box::use`, `requireNamespace`,
* and the transitive side-effect propagation.
*/
function attachDependencyToEnvironment(dependency, envInfo, ctx, spec = {}, definedAt) {
const pack = dependency.name;
if ((0, assert_1.isUndefined)(dependency.namespaceInfo) || isAttached(envInfo.current, pack, spec.namespaceOnly)) {
return envInfo;
}
const exports = selectExports((0, flowr_namespace_file_1.getCallables)(dependency.namespaceInfo), spec);
if (spec.namespaceOnly || isSubsetAttach(spec)) {
const layerType = spec.namespaceOnly ? environment_1.EnvType.LoadedNamespace : environment_1.EnvType.Namespace;
const layer = new environment_1.Environment(envInfo.current).asLibrary(pack, layerType)
.defineAll(exports.map(exp => exportDefinition(pack, exp, definedAt)));
return { level: envInfo.level, current: environment_1.REnvironment.attachAt(envInfo.current, layer, layer, spec.pos) };
}
// full attach: imports layer at the bottom, namespace (exports) layer on top
let importsEnv = new environment_1.Environment(envInfo.current).asLibrary(pack, environment_1.EnvType.Imports);
importsEnv = recImports(importsEnv, dependency.namespaceInfo, ctx, new Set());
const namespaceEnv = new environment_1.Environment(importsEnv).asLibrary(pack, environment_1.EnvType.Namespace)
.defineAll(exports.map(exp => exportDefinition(pack, exp, definedAt)));
const attached = { level: envInfo.level, current: environment_1.REnvironment.attachAt(envInfo.current, namespaceEnv, importsEnv, spec.pos) };
/* whatever R puts on the search path with it, `pack` first so a dependency cycle stays finite (the guard above stops it) */
return (0, attached_packages_1.attachedAlongside)(pack, ctx.deps.signatureSources()).reduce((env, alongside) => {
const dependency = ctx.deps.getDependency(alongside);
return dependency === undefined ? env : attachDependencyToEnvironment(dependency, env, ctx, spec, definedAt);
}, attached);
}
/** A namespace-only load is subsumed by any layer for `pack`; a full attach ignores a mere {@link EnvType.LoadedNamespace}. */
function blocksAttach(layer, namespaceOnly) {
if (namespaceOnly) {
return true;
}
return layer.t !== environment_1.EnvType.LoadedNamespace;
}
/** Whether package `pack` is already attached below the global env in a way that makes this (re-)attach a no-op. */
function isAttached(env, pack, namespaceOnly) {
for (let e = environment_1.REnvironment.findGlobal(env).parent; e.t !== undefined && !e.builtInEnv; e = e.parent) {
if (e.n === pack && blocksAttach(e, namespaceOnly)) {
return true;
}
}
return false;
}
/** Immutable base-layer chains, shared across analyses (layers clone before mutating); building one is O(N^2) in exports, so cache and reparent. */
const baseNamespaceLayerCache = new Map();
function baseNamespaceCacheKey(ctx, basePackages) {
return `${String(ctx.env.getCleanEnvFingerprint())}|${ctx.resolvedRVersion}|${basePackages.join(',')}|${ctx.deps.baseRSourceFingerprint()}`;
}
/**
* Attach the {@link baseRPackages|base-R} exports below the global so bare base calls resolve without `library()`.
* Names with a registered built-in are skipped, it is a no-op when no database resolves a base package, and the
* built layer is cached per {@link baseNamespaceCacheKey}.
*/
function attachBaseRNamespaces(env, ctx) {
if (!ctx.config.solver.sigdb.linkBaseR || !ctx.deps.hasBaseRSource()) {
return env;
}
const basePackages = ctx.config.project.basePackages ?? (0, r_base_packages_1.baseRPackages)(ctx.resolvedRVersion);
const key = baseNamespaceCacheKey(ctx, basePackages);
const cached = baseNamespaceLayerCache.get(key);
if (cached !== undefined) {
env.current.parent = cached;
return env;
}
let built = env;
let builtinNames;
for (const pkg of basePackages) {
const dependency = ctx.deps.getDependency(pkg);
if (dependency?.namespaceInfo === undefined) {
continue;
}
builtinNames ??= new Set([...ctx.env.builtInEnvironment.memory.keys()].map(String));
built = attachDependencyToEnvironment(dependency, built, ctx, { exclude: builtinNames }, node_id_1.NodeId.toBuiltIn(pkg));
}
if (built.current.parent !== env.current.parent) {
baseNamespaceLayerCache.set(key, built.current.parent);
}
return built;
}
/**
* Attach the exports of the project's declared `DESCRIPTION` dependencies (Imports/Depends, registered by the
* package-version plugins into {@link FlowrAnalyzerContext.deps|deps}) below the global so their bare calls resolve
* without an explicit `library()`, mirroring base-R auto-attach. A dependency whose {@link Package.namespaceInfo|
* namespaceInfo} no database resolves is skipped, and a package base-R or an earlier iteration already attached is a
* no-op via the {@link isAttached} guard inside {@link attachDependencyToEnvironment}.
*/
function attachDeclaredDependencies(env, ctx) {
if (!ctx.config.solver.sigdb.linkDescriptionDependencies) {
return env;
}
let built = env;
for (const declared of ctx.deps.getDependencies()) {
// getDependency triggers lazy export resolution the raw declared record may still be missing
const dependency = ctx.deps.getDependency(declared.name);
if (dependency?.namespaceInfo === undefined) {
continue;
}
built = attachDependencyToEnvironment(dependency, built, ctx, {}, node_id_1.NodeId.toBuiltIn(dependency.name));
}
return built;
}
/** attach the analyzed package's own `NAMESPACE importFrom(...)` symbols (by their bare name) below the global, so a bare imported call resolves to its source package */
function attachProjectImports(env, ctx) {
const own = ctx.deps.getDependency('current')?.namespaceInfo;
if (own === undefined || own.importedPackages.size === 0) {
return env;
}
const layerNamespace = 'current';
const toDefine = [];
for (const [pkg, funcs] of own.importedPackages) {
// an explicit `importFrom(pkg, a, b)` names the symbols directly; `import(pkg)` needs the package's own export list
let names;
if (funcs === 'all') {
const imported = ctx.deps.getDependency(pkg)?.namespaceInfo;
if (imported === undefined) {
continue;
}
names = (0, flowr_namespace_file_1.getCallables)(imported);
}
else {
names = funcs;
}
for (const fn of names) {
toDefine.push({
name: identifier_1.Identifier.make(fn, layerNamespace),
type: identifier_1.ReferenceType.Function,
nodeId: node_id_1.NodeId.fromPkgFn(pkg, fn),
definedAt: node_id_1.NodeId.toBuiltIn(pkg)
});
}
}
if (toDefine.length === 0) {
return env;
}
const layer = new environment_1.Environment(env.current).asLibrary(layerNamespace, environment_1.EnvType.Imports).defineAll(toDefine);
return { level: env.level, current: environment_1.REnvironment.attachAt(env.current, layer, layer) };
}
/** attach every project-level environment layer in order: base R namespaces, the project's own `importFrom` symbols, then its declared dependencies */
function attachProject(env, ctx) {
return attachDeclaredDependencies(attachProjectImports(attachBaseRNamespaces(env, ctx), ctx), ctx);
}
function recImports(importsEnv, namespaceInfo, ctx, alreadyImportedAll) {
for (const imp of namespaceInfo.importedPackages) {
const importedDependency = ctx.deps.getDependency(imp[0]);
if ((0, assert_1.isUndefined)(importedDependency)) {
continue;
}
const importedNs = importedDependency.namespaceInfo;
const funcToImport = importedNs === undefined ? undefined
: imp[1] === 'all' ? (0, flowr_namespace_file_1.getCallables)(importedNs) : (0, flowr_namespace_file_1.getCallables)(importedNs).filter(v => imp[1].includes(v));
if ((0, assert_1.isUndefined)(funcToImport)) {
continue;
}
if (alreadyImportedAll.has(importedDependency.name)) {
continue;
}
/* collect first and define in one go, as defining one by one copies the (growing) memory every time */
const toDefine = [];
const queued = new Set();
for (const func of funcToImport) {
const identifier = package_1.Package.functionIdentifier(importedDependency.name, func);
if (importsEnv.memory.has(identifier) || queued.has(identifier)) {
continue;
}
queued.add(identifier);
toDefine.push({
name: identifier_1.Identifier.make(identifier, importsEnv.n),
type: identifier_1.ReferenceType.Function,
nodeId: node_id_1.NodeId.fromPkgFn(importedDependency.name, func),
definedAt: node_id_1.NodeId.toBuiltIn(importedDependency.name)
});
}
if (toDefine.length > 0) {
importsEnv = importsEnv.defineAll(toDefine);
}
if (imp[1] === 'all') {
alreadyImportedAll.add(importedDependency.name);
}
//if only importFrom() we don't have to recursively import
if (imp[1] === 'all' && importedDependency?.namespaceInfo) {
importsEnv = recImports(importsEnv, importedDependency.namespaceInfo, ctx, alreadyImportedAll);
}
}
return importsEnv;
}
//# sourceMappingURL=built-in-library.js.map