@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
160 lines • 6.55 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.blobTuple = void 0;
exports.cranBlobUrl = cranBlobUrl;
exports.tupleToBlob = tupleToBlob;
exports.signatureParameterNames = signatureParameterNames;
exports.transitiveCallees = transitiveCallees;
exports.decodeFunction = decodeFunction;
exports.decodeDependencies = decodeDependencies;
exports.versionFnIndices = versionFnIndices;
exports.deriveLibraryExports = deriveLibraryExports;
/**
* Decoding a {@link PkgBlob} into the reader-facing views: the on-disk tuple form, the decoded function/
* dependency records, and the {@link LibraryExports} export view. Pure -- no I/O, no mutation of inputs.
* Split out of `../sigdb` so the reader/writer there does not carry the per-record decoding.
*/
const schema_1 = require("./schema");
const sigdb_version_1 = require("./sigdb-version");
/**
* Reconstruct the CRAN source blob link which is the newest non-archived version lives under `src/contrib`,
* every other under `Archive`.
*/
function cranBlobUrl(cranBase, pkg, version, opts) {
if (!opts.cran) {
return undefined;
}
const base = cranBase.endsWith('/') ? cranBase : cranBase + '/';
return version === opts.latest && !opts.archived
? `${base}${pkg}_${version}.tar.gz`
: `${base}Archive/${pkg}/${pkg}_${version}.tar.gz`;
}
/** a {@link PkgBlob} in its compact on-disk tuple form (drops the trailing `dates` when empty) */
const blobTuple = (b) => Object.keys(b.dates).length > 0
? [b.sigs, b.cgs, b.fns, b.versions, b.noncran ?? [], b.deps, b.depsByVersion, b.dates]
: [b.sigs, b.cgs, b.fns, b.versions, b.noncran ?? [], b.deps, b.depsByVersion];
exports.blobTuple = blobTuple;
/** the inverse of {@link blobTuple}: rebuild a {@link PkgBlob} from its on-disk tuple */
function tupleToBlob(t) {
return { sigs: t[0], cgs: t[1], fns: t[2], versions: t[3], noncran: t[4]?.length ? t[4] : undefined, deps: t[5] ?? [], depsByVersion: t[6] ?? {}, dates: t[7] ?? {} };
}
/**
* The formal parameter names of a known signature, ready for `RFunctionCall.matchArgumentsToParameters`: the
* `...` parameter is excluded so partial (`pmatch`) matches against the remaining names stay unambiguous.
*/
function signatureParameterNames(signature) {
return signature.map(p => p.name).filter(n => n !== '...');
}
/**
* The transitive callees of `name`. Each local callee that is itself a function of `functions` (one package
* version's set) is expanded, names outside the set stay as leaves. Deduplicated and ascending.
*/
function transitiveCallees(functions, name) {
const local = new Map(functions.map(f => [f.name, f.callees]));
const reached = new Set();
const queue = [...(local.get(name) ?? [])];
while (queue.length > 0) {
const callee = queue.pop();
if (reached.has(callee)) {
continue;
}
reached.add(callee);
const inner = local.get(callee);
if (inner !== undefined) {
queue.push(...inner);
}
}
return [...reached].sort();
}
/** decode one of a blob's function records against the global string dictionary */
function decodeFunction(strings, blob, fnIdx) {
const [nameIdx, sigIdx, cgIdx, bits, fileIdx, line, topicIdx] = blob.fns[fnIdx];
const signature = (sigIdx >= 0 ? blob.sigs[sigIdx] : []).map(p => {
const [n, flags, def] = Array.isArray(p) ? [p[0], p[1], p.length === 3 ? p[2] : -1] : [p, 0, -1];
return {
name: strings[n],
forced: Boolean(flags & 1 /* ParamFlag.Forced */),
optional: !(flags & 2 /* ParamFlag.Missing */),
...(def >= 0 ? { default: strings[def] } : {})
};
});
let callees = [];
if (cgIdx >= 0) {
let prev = 0;
callees = blob.cgs[cgIdx].map(d => strings[prev += d]);
}
return {
name: strings[nameIdx],
...(topicIdx !== undefined && topicIdx >= 0 ? { topic: strings[topicIdx] } : {}),
...(fileIdx >= 0 ? { file: strings[fileIdx] } : {}),
line,
exported: Boolean(bits & 1 /* FnProp.Exported */),
props: Object.entries(schema_1.FnPropNames).filter(([m]) => bits & Number(m)).map(([, n]) => n),
signature,
callees
};
}
/** decode the declared dependencies of one blob version (empty when it declares none / the bundle omits them) */
function decodeDependencies(strings, blob, ver) {
const idx = blob.depsByVersion[ver];
if (idx === undefined) {
return [];
}
return blob.deps[idx].map(d => ({
name: strings[d[0]],
type: d[1],
...(d.length === 3 ? { constraint: strings[d[2]] } : {})
}));
}
/** the function indices of a blob version (undoing the delta encoding) */
function versionFnIndices(blob, ver) {
const list = blob.versions[ver];
if (list === undefined) {
return undefined;
}
const out = [];
let prev = 0;
for (const d of list) {
out.push(prev += d);
}
return out;
}
/** derive the {@link LibraryExports} export view of one package version from its blob + metadata */
function deriveLibraryExports(strings, blob, meta, pkg, version, cranBase = schema_1.DefaultCranBase) {
const [latest, archived] = meta;
const ver = (0, sigdb_version_1.resolveVersion)(blob, latest, version);
if (ver === undefined) {
return undefined;
}
const idxs = versionFnIndices(blob, ver) ?? [];
const exported = [];
const internal = [];
const deprecated = [];
const s3Classes = [];
const s4Classes = [];
const locations = new Map();
for (const i of idxs) {
const [nameIdx, , , bits, fileIdx, line] = blob.fns[i];
const name = strings[nameIdx];
(bits & 1 /* FnProp.Exported */ ? exported : internal).push(name);
if (bits & 32 /* FnProp.Deprecated */) {
deprecated.push(name);
}
if (bits & 1024 /* FnProp.S3Owner */) {
s3Classes.push(name);
}
if (bits & 2048 /* FnProp.S4Owner */) {
s4Classes.push(name);
}
if (fileIdx >= 0) {
locations.set(name, { file: strings[fileIdx], line });
}
}
const cran = !blob.noncran?.includes(ver);
return {
version: ver, exported, internal, deprecated, s3Classes, s4Classes, cran,
cranUrl: cranBlobUrl(cranBase, pkg, ver, { latest, archived: archived === 1, cran }),
...(locations.size > 0 ? { locations } : {})
};
}
//# sourceMappingURL=decode.js.map