* Copyright (c) 2024-2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as idl from "@idlizer/core/idl"
import { generatorConfiguration, Language, LibraryInterface, isMaterialized, cleanPrefix, isInIdlize, isStaticMaterialized, isInCurrentModule, PeerLibrary, maybeTransformManagedCallback } from "@idlizer/core";
import { isComponentDeclaration } from "./ComponentsCollector";
import { UnionFlatteningMode, DependencySorter } from "./idl/DependencySorter";
import { IdlNameConvertor } from "@idlizer/core";
import { getHookMethod, peerGeneratorConfiguration } from "../DefaultConfiguration";
import { collectUniqueCallbacks } from "./printers/CallbacksPrinter";
const collectDeclarationTargets_cache = new Map<LibraryInterface, idl.IDLNode[]>()
const collectDeclarationTargets_cache_flatten = new Map<LibraryInterface, idl.IDLNode[]>()
const collectDeclarationTargets_cache_both = new Map<LibraryInterface, idl.IDLNode[]>()
export function collectDeclarationTargets(library: LibraryInterface, unionFlatteningMode: UnionFlatteningMode = false): idl.IDLNode[] {
const cache = (unionFlatteningMode==="both" ? collectDeclarationTargets_cache_both : (unionFlatteningMode ? collectDeclarationTargets_cache_flatten : collectDeclarationTargets_cache))
if (!cache.has(library))
cache.set(library, collectDeclarationTargetsUncached(library, { synthesizeCallbacks: true, unionFlatteningMode}))
return cache.get(library)!
}
export function collectDeclarationTargetsUncached(library: LibraryInterface, options: { synthesizeCallbacks: boolean, unionFlatteningMode: UnionFlatteningMode }): idl.IDLNode[] {
const generateUnused = peerGeneratorConfiguration().GenerateUnused
let orderer = new DependencySorter(library, options.unionFlatteningMode)
for (const file of library.files) {
if (!file.entries.length || !isInCurrentModule(file.entries[0]))
continue
for (const entry of idl.linearizeNamespaceMembers(file.entries)) {
if (peerGeneratorConfiguration().ignoreEntry(entry.name, library.language) ||
isInIdlize(entry) ||
idl.hasExtAttribute(entry, idl.IDLExtendedAttributes.HandWrittenImplementation) ||
idl.hasExtAttribute(entry, idl.IDLExtendedAttributes.TransformOnSerialize)
)
continue
if (idl.isInterface(entry)) {
if (isComponentDeclaration(library, entry) || isMaterialized(entry, library)) {
if (isMaterialized(entry, library) && !isStaticMaterialized(entry, library))
orderer.addDep(entry)
for (const property of entry.properties) {
if (peerGeneratorConfiguration().components.ignorePeerMethod.includes(property.name)
|| getHookMethod(entry.name, property.name)?.replaceImplementation)
continue
orderer.addDep(library.toDeclaration(idl.maybeOptional(property.type, property.isOptional)))
}
for (const method of entry.methods) {
if (peerGeneratorConfiguration().components.ignorePeerMethod.includes(method.name)
|| getHookMethod(entry.name, method.name)?.replaceImplementation)
continue
for (const parameter of method.parameters)
orderer.addDep(library.toDeclaration(idl.maybeOptional(parameter.type!, parameter.isOptional)))
orderer.addDep(library.toDeclaration(method.returnType))
}
for (const constructor of entry.constructors) {
for (const parameter of constructor.parameters)
orderer.addDep(library.toDeclaration(idl.maybeOptional(parameter.type!, parameter.isOptional)))
}
for (const callable of entry.callables) {
for (const parameter of callable.parameters)
orderer.addDep(library.toDeclaration(idl.maybeOptional(parameter.type!, parameter.isOptional)))
}
} else if (generateUnused && !isInIdlize(entry) && !idl.hasTypeParameters(entry)) {
orderer.addDep(library.toDeclaration(entry))
for (const property of entry.properties) {
if (peerGeneratorConfiguration().components.ignorePeerMethod.includes(property.name))
continue
orderer.addDep(library.toDeclaration(idl.maybeOptional(property.type, property.isOptional)))
}
}
}
else if (idl.isEnum(entry)) {
orderer.addDep(library.toDeclaration(entry))
} else if (idl.isMethod(entry) && !idl.hasTypeParameters(entry)) {
for (const parameter of entry.parameters)
orderer.addDep(library.toDeclaration(idl.maybeOptional(parameter.type!, parameter.isOptional)))
orderer.addDep(library.toDeclaration(entry.returnType))
} else if (idl.isConstant(entry)) {
orderer.addDep(library.toDeclaration(entry.type))
}
}
}
if (options.synthesizeCallbacks) {
synthesizeCallbacks(library, orderer)
}
let orderedDependencies = orderer.getToposorted()
orderedDependencies.unshift(idl.IDLI32Type)
return orderedDependencies
}
function synthesizeCallbacks(library: LibraryInterface, orderer: DependencySorter): void {
const nameConvertor = library.createTypeNameConvertor(Language.CPP)
const foundCallbacksNames = new Set<string>()
const foundCallbacks: idl.IDLCallback[] = []
const addCallback = (callback: idl.IDLCallback) => {
callback = maybeTransformManagedCallback(callback, library) ?? callback
const name = nameConvertor.convert(callback)
if (foundCallbacksNames.has(name))
return
foundCallbacksNames.add(name)
foundCallbacks.push(callback)
}
for (const decl of orderer.getToposorted()) {
if (idl.isCallback(decl)) {
addCallback(decl)
}
}
for (const file of library.files) {
for (const entry of file.entries) {
if (isInCurrentModule(entry))
idl.forEachFunction(entry, function_ => {
const promise = idl.asPromise(function_.returnType)
if (promise) {
const reference = library.createContinuationCallbackReference(promise)
const callback = library.resolveTypeReference(reference)
if (callback)
addCallback(callback as idl.IDLCallback)
}
})
}
}
for (const foundCallback of [...foundCallbacks]) {
const continuationRef = library.createContinuationCallbackReference(foundCallback.returnType)
const continuation = library.resolveTypeReference(continuationRef)
if (continuation && idl.isCallback(continuation))
addCallback(continuation)
}
foundCallbacks
.sort((a, b) => nameConvertor.convert(a).localeCompare(nameConvertor.convert(b)))
.filter(callback => {
const subtypes = callback.parameters.map(it => it.type!).concat(callback.returnType)
.flatMap(it => {
if (idl.isContainerType(it))
return it.elementType
if (idl.isUnionType(it))
return it.types
if (idl.isOptionalType(it))
return it.type
return it
})
if (subtypes.some(it => idl.isNamedNode(it) && peerGeneratorConfiguration().isHandWritten(it.name)))
return false
if (subtypes.some(it => idl.isReferenceType(it) && it.typeArguments))
return false
if (subtypes.some(it => idl.isNamedNode(it) && peerGeneratorConfiguration().ignoreEntry(it.name, library.language)))
return false
if (subtypes.some(it => idl.isNamedNode(it) && it.name === "this"))
return false
return true
})
.forEach(it => orderer.addDep(it))
}
export namespace DeclarationTargets {
function allTypes<T extends idl.IDLType>(library: LibraryInterface, predicate: (e: idl.IDLNode) => e is T, unionFlatteningMode: UnionFlatteningMode = false): T[] {
return collectDeclarationTargets(library, unionFlatteningMode).filter(predicate)
}
function allEntries<T extends idl.IDLEntry>(library: LibraryInterface, predicate: (e: idl.IDLNode) => e is T, unionFlatteningMode: UnionFlatteningMode = false): T[] {
return collectDeclarationTargets(library, unionFlatteningMode).filter(predicate)
}
export function allUnionTypes(library: LibraryInterface, unionFlatteningMode: UnionFlatteningMode = false): Map<string, {id: number, name: string}[]> {
const nativeNameConvertorInstance: IdlNameConvertor = library.createTypeNameConvertor(Language.CPP)
const data: Array<[string, {id: number, name: string}[]]> =
allTypes(library, idl.isUnionType, unionFlatteningMode)
.map(it => [
nativeNameConvertorInstance.convert(it),
it.types.map((e, index) => { return {id: index, name: "value" + index }})])
return new Map(data)
}
export function allLiteralTypes(library: LibraryInterface, unionFlatteningMode: UnionFlatteningMode = false): Map<string, string[]> {
const nativeNameConvertorInstance: IdlNameConvertor = library.createTypeNameConvertor(Language.CPP)
const data: Array<[string, string[]]> =
allEntries(library, (it): it is idl.IDLInterface => idl.isInterface(it) && it.subkind === idl.IDLInterfaceSubkind.AnonymousInterface, unionFlatteningMode)
.map(it => [
nativeNameConvertorInstance.convert(it),
it.properties.map(p => p.name)])
return new Map(data)
}
export function allOptionalTypes(library: LibraryInterface, unionFlatteningMode: UnionFlatteningMode = false): Set<string> {
const nativeNameConvertorInstance: IdlNameConvertor = library.createTypeNameConvertor(Language.CPP)
const data = collectDeclarationTargets(library, unionFlatteningMode)
.filter(it => it !== idl.IDLVoidType)
.filter(it => {
if (idl.isOptionalType(it)) it = it.type
if (idl.isReferenceType(it)) {
it = library.resolveTypeReference(it)!
}
if (!it) {
return false
}
if (idl.isNamedNode(it) && peerGeneratorConfiguration().isResource(it.name)) return false
if (idl.isInterface(it) && peerGeneratorConfiguration().serializer.ignore.includes(it.name)) return false
return true
})
.map(it => idl.isType(it)
? nativeNameConvertorInstance.convert(idl.createOptionalType(it))
: generatorConfiguration().OptionalPrefix + cleanPrefix(nativeNameConvertorInstance.convert(it), generatorConfiguration().TypePrefix)
)
return new Set(data)
}
}