in pxtcompiler/emitter/service.ts [439:610]
export function internalGetApiInfo(program: Program, jres?: pxt.Map<pxt.JRes>, legacyOnly = false) {
const res: ApisInfo = {
byQName: {},
jres: jres
}
const qNameToNode: pxt.Map<Declaration> = {};
const typechecker = program.getTypeChecker()
const collectDecls = (stmt: Node) => {
if (stmt.kind == SK.VariableStatement) {
let vs = stmt as VariableStatement
vs.declarationList.declarations.forEach(collectDecls)
return
}
if (isExported(stmt as Declaration)) {
if (!stmt.symbol) {
console.warn("no symbol", stmt)
return;
}
let qName = getFullName(typechecker, stmt.symbol)
if (stmt.kind == SK.SetAccessor)
qName += "@set" // otherwise we get a clash with the getter
qNameToNode[qName] = stmt as Declaration;
let si = createSymbolInfo(typechecker, qName, stmt)
if (si) {
let existing = U.lookup(res.byQName, qName)
if (existing) {
// we can have a function and an interface of the same name
if (existing.kind == SymbolKind.Interface && si.kind != SymbolKind.Interface) {
// save existing entry
res.byQName[qName + "@type"] = existing
} else if (existing.kind != SymbolKind.Interface && si.kind == SymbolKind.Interface) {
res.byQName[qName + "@type"] = si
si = existing
} else {
const foundSrc = existing.attributes._source?.trim();
const newSrc = si.attributes._source?.trim();
let source = foundSrc + "\n" + newSrc;
// Avoid duplicating source if possible
if (!!foundSrc && newSrc?.indexOf(foundSrc) >= 0) {
source = newSrc;
} else if (!!newSrc && foundSrc?.indexOf(newSrc) >= 0) {
source = foundSrc;
}
si.attributes = parseCommentString(source);
// Check if the colliding symbols are namespace definitions. The same namespace can be
// defined in different packages/extensions, so we want to keep track of that information.
// That way, we can make sure each cached extension has a copy of the namespace
if (existing.kind === SymbolKind.Module) {
// Reference the existing array of packages where this namespace has been defined
si.pkgs = existing.pkgs || []
if (existing.pkg !== si.pkg) {
if (!si.pkgs.find(element => element === existing.pkg)) {
si.pkgs.push(existing.pkg)
}
}
}
if (existing.extendsTypes) {
si.extendsTypes = si.extendsTypes || []
existing.extendsTypes.forEach(t => {
if (si.extendsTypes.indexOf(t) === -1) {
si.extendsTypes.push(t);
}
})
}
}
}
if (stmt.parent &&
(stmt.parent.kind == SK.ClassDeclaration || stmt.parent.kind == SK.InterfaceDeclaration) &&
!isStatic(stmt as Declaration))
si.isInstance = true
res.byQName[qName] = si
}
}
if (stmt.kind == SK.ModuleDeclaration) {
let mod = <ModuleDeclaration>stmt
if (mod.body.kind == SK.ModuleBlock) {
let blk = <ModuleBlock>mod.body
blk.statements.forEach(collectDecls)
} else if (mod.body.kind == SK.ModuleDeclaration) {
collectDecls(mod.body)
}
} else if (stmt.kind == SK.InterfaceDeclaration) {
let iface = stmt as InterfaceDeclaration
iface.members.forEach(collectDecls)
} else if (stmt.kind == SK.ClassDeclaration) {
let iface = stmt as ClassDeclaration
iface.members.forEach(collectDecls)
} else if (stmt.kind == SK.EnumDeclaration) {
let e = stmt as EnumDeclaration
e.members.forEach(collectDecls)
}
}
for (let srcFile of program.getSourceFiles()) {
srcFile.statements.forEach(collectDecls)
}
let toclose: SymbolInfo[] = []
// store qName in symbols
for (let qName in res.byQName) {
let si = res.byQName[qName]
si.qName = qName;
si.attributes._source = null
if (si.extendsTypes && si.extendsTypes.length) toclose.push(si)
let jrname = si.attributes.jres
if (jrname) {
if (jrname == "true") jrname = qName
let jr = U.lookup(jres || {}, jrname)
if (jr && jr.icon && !si.attributes.iconURL) {
si.attributes.iconURL = jr.icon
}
if (jr && jr.data && !si.attributes.jresURL) {
si.attributes.jresURL = "data:" + jr.mimeType + ";base64," + jr.data
}
}
if (si.pyName) {
let override = U.lookup(ts2PyFunNameMap, si.qName);
if (override && override.n) {
si.pyQName = override.n;
si.pySnippet = override.snippet;
si.pySnippetName = override.n;
si.pySnippetWithMarkers = undefined;
} else if (si.namespace) {
let par = res.byQName[si.namespace]
if (par) {
si.pyQName = par.pyQName + "." + si.pyName
} else {
// shouldn't happen
pxt.log("namespace missing: " + si.namespace)
si.pyQName = si.namespace + "." + si.pyName
}
} else {
si.pyQName = si.pyName
}
}
}
// transitive closure of inheritance
let closed: pxt.Map<boolean> = {}
let closeSi = (si: SymbolInfo) => {
if (U.lookup(closed, si.qName)) return;
closed[si.qName] = true
let mine: pxt.Map<boolean> = {}
mine[si.qName] = true
for (let e of si.extendsTypes || []) {
mine[e] = true
let psi = res.byQName[e]
if (psi) {
closeSi(psi)
for (let ee of psi.extendsTypes) mine[ee] = true
}
}
si.extendsTypes = Object.keys(mine)
}
toclose.forEach(closeSi)
if (legacyOnly) {
// conflicts with pins.map()
delete res.byQName["Array.map"]
}
return {
apis: res,
decls: qNameToNode
}
}