UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

580 lines 28.6 kB
"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