UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

224 lines 9.59 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FlowrAnalyzerDependenciesContext = void 0; const abstract_flowr_analyzer_context_1 = require("./abstract-flowr-analyzer-context"); const flowr_analyzer_package_versions_plugin_1 = require("../plugins/package-version-plugins/flowr-analyzer-package-versions-plugin"); const package_1 = require("../plugins/package-version-plugins/package"); const identifier_1 = require("../../dataflow/environments/identifier"); const assert_1 = require("../../util/assert"); const config_1 = require("../../config"); const r_version_1 = require("../../util/r-version"); /** * Manages the project's dependencies, their versions, and their interplay with {@link FlowrAnalyzerPackageVersionsPlugin}s. */ class FlowrAnalyzerDependenciesContext extends abstract_flowr_analyzer_context_1.AbstractFlowrAnalyzerContext { name = 'flowr-analyzer-dependencies-context'; functionsContext; dependencies = new Map(); /** what {@link getDependency} resolved for a package the project does not depend on, so a lookup never makes it one */ resolvedOnly = new Map(); staticsLoaded = false; /** resolvers consulted lazily to fill in exports; `existing` carries version info from other plugins */ lazyResolvers = []; resolvedMisses = new Set(); reset() { this.dependencies = new Map(); this.resolvedOnly = new Map(); this.staticsLoaded = false; this.lazyResolvers = []; this.resolvedMisses = new Set(); } /** Register a resolver consulted by {@link getDependency} to fill in a package's exports lazily. */ addLazyResolver(resolver) { this.lazyResolvers.push(resolver); } loadedSignatureDatabases() { if (!(0, config_1.isSigDbEnabled)(this.ctx.config)) { return []; } return this.plugins.flatMap(p => p.loadedDatabases()); } availableSignatureDatabases() { return [...new Set(this.loadedSignatureDatabases().map(d => d.scope))]; } hasSignatureDatabase() { return (0, config_1.isSigDbEnabled)(this.ctx.config) && this.plugins.some(p => p.loadedDatabases().length > 0); } packagesExporting(name) { if (!(0, config_1.isSigDbEnabled)(this.ctx.config)) { return []; } const out = new Set(); for (const p of this.plugins) { for (const pkg of p.packagesExporting(name)) { out.add(pkg); } } return [...out]; } signatureSources() { if (!(0, config_1.isSigDbEnabled)(this.ctx.config)) { return []; } return this.plugins.flatMap(p => [...p.signatureSources(this.ctx.config)]); } signatureOf(id, version) { const pkg = identifier_1.Identifier.getNamespace(id); if (pkg === undefined) { return undefined; // a qualified `pkg::fn` identifier is required to know which package to look in } const name = identifier_1.Identifier.getName(id); const ver = version ?? this.getDependency(pkg)?.resolvedVersion; for (const src of this.signatureSources()) { if (!src.has(pkg)) { continue; // avoids touching a package this source lacks } // decode only the one function (not the whole package), falling back to the source's latest version const fn = src.functionByName(pkg, name, ver) ?? src.functionByName(pkg, name); if (fn) { return fn; } } return undefined; } /** Whether any version plugin can resolve the base-R packages (a versioned signature source is available). */ hasBaseRSource() { return (0, config_1.isSigDbEnabled)(this.ctx.config) && this.plugins.some(p => p.providesBaseRPackages()); } /** Cheap fingerprint of only the base-R-providing databases, so base-R-derived caches survive unrelated database changes. */ baseRSourceFingerprint() { if (!(0, config_1.isSigDbEnabled)(this.ctx.config)) { return ''; } return this.plugins.filter(p => p.providesBaseRPackages()) .flatMap(p => p.loadedDatabases()).map(d => d.hash).join(','); } /** Mount an additional signature database/source by path (a plain `.sigs.ndjson`, a `.br`, or a manifest). */ async addDatabaseSource(source) { for (const p of this.plugins) { await p.addDatabaseSource(source); } } /** Eagerly mount every version plugin's signature database up front (see `solver.sigdb.eagerlyLoad`). */ eagerlyLoadSignatureDatabases() { for (const p of this.plugins) { p.preloadDatabasesSync(); } } receive(event) { const type = event.type; switch (type) { case "full" /* InvalidationEventType.Full */: this.reset(); break; case "single-file-invalidate" /* InvalidationEventType.SingleFileInvalidate */: // nothing to do break; default: (0, assert_1.assertUnreachable)(type); } } constructor(functionsContext, plugins) { super(functionsContext.getAttachedContext(), flowr_analyzer_package_versions_plugin_1.FlowrAnalyzerPackageVersionsPlugin.defaultPlugin(), plugins); this.functionsContext = functionsContext; } resolveStaticDependencies() { this.applyPlugins(undefined); this.staticsLoaded = true; } /** Runs the static plugins once. They fill this context and the project metadata, so both gate their reads on it. */ ensureStaticsLoaded() { if (!this.staticsLoaded) { this.resolveStaticDependencies(); } } /** * Register a dependency declared by a project metadata file (`DESCRIPTION`, `renv.lock`, `rv.lock`, `uvr.lock`). Gated by * `solver.sigdb.loadProjectDependencies`: when project-dependency loading is disabled this is a no-op, so the * declared deps never enter the context (unlike {@link addDependency}, used for on-demand signature-database * resolution). */ addDeclaredDependency(pkg) { if (!this.ctx.config.solver.sigdb.loadProjectDependencies) { return this; } return this.addDependency(pkg); } addDependency(pkg) { mergeIntoMap(this.dependencies, pkg); return this; } getDependency(name, version) { if (!this.staticsLoaded) { this.resolveStaticDependencies(); } if (version !== undefined) { return this.resolvePinnedDependency(name, typeof version === 'string' ? r_version_1.RRange.parse('=' + version) : version); } const existing = this.dependencies.get(name) ?? this.resolvedOnly.get(name); // a package already carrying exports is complete; a version-only one is still enriched below if (existing?.namespaceInfo || (!existing && this.resolvedMisses.has(name))) { return existing; } if (!this.resolvedMisses.has(name)) { for (const resolve of this.lazyResolvers) { const resolved = resolve(name, existing); if (resolved) { // merges exports into any existing version info, of a dependency or of a mere lookup const into = this.dependencies.has(name) ? this.dependencies : this.resolvedOnly; mergeIntoMap(into, resolved); return into.get(name); } } this.resolvedMisses.add(name); } return existing; } /** Like {@link getDependency}, but for a package the code pulls in (`library(p)`): the result joins the project's {@link getDependencies|dependencies}. */ loadDependency(name) { const found = this.getDependency(name); const cached = this.resolvedOnly.get(name); if (cached !== undefined) { this.resolvedOnly.delete(name); mergeIntoMap(this.dependencies, cached); } return this.dependencies.get(name) ?? found; } /** Resolve `name` constrained to `range` via the plugins (uncached); falls back to the cached dependency. */ resolvePinnedDependency(name, range) { const pin = new package_1.Package({ name, versionConstraints: range ? [range] : [] }); for (const resolve of this.lazyResolvers) { const resolved = resolve(name, pin); if (resolved) { return resolved; } } return this.getDependency(name); } inferredRange(name) { const pkg = this.getDependency(name); return pkg?.hasSatisfiableVersion() ? pkg.derivedRange : undefined; } getDependencies() { if (!this.staticsLoaded) { this.resolveStaticDependencies(); } return Array.from(this.dependencies.values()); } declaredPackageNames() { this.ensureStaticsLoaded(); const declared = Object.values(this.ctx.meta.getDeclaredPackages()); return [...new Set(declared.flatMap(group => group?.map(p => p.name) ?? []))]; } } exports.FlowrAnalyzerDependenciesContext = FlowrAnalyzerDependenciesContext; function mergeIntoMap(map, pkg) { const existing = map.get(pkg.name); if (existing) { existing.mergeInPlace(pkg); } else { map.set(pkg.name, pkg); } } //# sourceMappingURL=flowr-analyzer-dependencies-context.js.map