* Copyright (c) 2024 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 { generatorConfiguration } from '../config'
import * as idl from '../idl'
import { Language } from '../Language'
import { ArgConvertor, VoidConvertor } from '../LanguageWriters/ArgConvertors'
import { CppReturnTypeConvertor } from '../LanguageWriters/convertors/CppConvertors'
import { copyMethod, Field, FieldModifier, Method, MethodModifier, NamedMethodSignature } from '../LanguageWriters/LanguageWriter'
import { capitalize } from '../util'
import { qualifiedName } from './idl/common'
import { PeerClassBase } from './PeerClass'
import { PeerMethod, PeerMethodSignature } from './PeerMethod'
import { ReferenceResolver } from './ReferenceResolver'
export class MaterializedField {
constructor(
public field: Field,
public argConvertor: ArgConvertor,
public outArgConvertor?: ArgConvertor,
public isNullableOriginalTypeField?: boolean,
public extraMethodName: string | undefined = undefined
) {
const isReadonly = field.modifiers.includes(FieldModifier.READONLY)
const isGetter = field.modifiers.includes(FieldModifier.GET)
const isSetter = field.modifiers.includes(FieldModifier.SET)
if (isReadonly && (isGetter || isSetter))
throw new Error(`Unsupported modifiers combination: field can be either readonly or getter/setter or mutable for field ${field.name}`)
if (isSetter && !isGetter)
throw new Error(`Unsupported modifiers combination: if setter is defined getter must be defined too for field ${field.name}`)
}
get state(): PropertyState {
const hasGetter = this.field.modifiers.includes(FieldModifier.GET)
const hasSetter = this.field.modifiers.includes(FieldModifier.SET)
if (hasGetter || hasSetter) {
return {
isAccessor: true,
hasGetter,
hasSetter
}
} else {
const isReadonly = this.field.modifiers.includes(FieldModifier.READONLY)
return {
isAccessor: false,
isReadonly
}
}
}
}
export class MaterializedMethod extends PeerMethod {
constructor(
public decl: idl.IDLConstructor | idl.IDLMethod | undefined,
sig: PeerMethodSignature,
originalParentName: string,
public implementationParentName: string,
returnType: idl.IDLType,
isCallSignature: boolean,
uniqueOverloadName: string,
method: Method,
) {
super(decl, sig, originalParentName, returnType, isCallSignature, uniqueOverloadName, method)
}
tsReturnType(): idl.IDLType | undefined {
return this.method.signature.returnType
}
getPrivateMethod(asProtected: boolean = false) {
let privateMethod: MaterializedMethod = this
if (!privateMethod.method.modifiers?.includes(MethodModifier.PRIVATE)) {
privateMethod = copyMaterializedMethod(this, {
method: copyMethod(this.method, {
modifiers: (this.method.modifiers ?? [])
.filter(it => it !== MethodModifier.PUBLIC)
.filter(it => it !== MethodModifier.OVERRIDE)
.filter(it => !asProtected || (it !== MethodModifier.PRIVATE))
.concat([asProtected ? MethodModifier.PROTECTED : MethodModifier.PRIVATE])
})
})
}
return privateMethod
}
withReturnType(returnType: idl.IDLType): MaterializedMethod {
const s = this.method.signature
const argNames = s.args.map((_, i) => s.argName(i))
const signature = new NamedMethodSignature(
returnType, s.args, argNames, s.defaults, s.argsModifiers, s.printHints)
const method = copyMethod(this.method, { signature: signature })
return copyMaterializedMethod(this, { method: method })
}
}
export function copyMaterializedMethod(method: MaterializedMethod, overrides: {
method?: Method,
// add more if you need
}) {
return new MaterializedMethod(
method.decl,
method.sig,
method.originalParentName,
method.implementationParentName,
method.returnType,
method.isCallSignature,
method.uniqueOverloadName,
overrides.method ?? method.method)
}
export class MaterializedClass implements PeerClassBase {
constructor(
public readonly decl: idl.IDLInterface,
public readonly className: string,
public readonly isInterface: boolean,
public readonly isStaticMaterialized: boolean,
public readonly superClass: idl.IDLReferenceType | undefined,
public readonly interfaces: idl.IDLReferenceType[] | undefined,
public readonly generics: string[] | undefined,
public readonly fields: MaterializedField[],
public readonly ctors: MaterializedMethod[],
public readonly finalizer: MaterializedMethod | undefined,
public readonly methods: MaterializedMethod[],
public readonly needBeGenerated: boolean = true,
public readonly taggedMethods: idl.IDLMethod[] = [],
public readonly isRefCounted: boolean = false
) {}
getImplementationName(): string {
return this.isInterface ? getInternalClassName(this.className) : this.className
}
generatedName(isCallSignature: boolean): string{
return this.getImplementationName()
}
private _isGlobal = false
setGlobalScope() {
this._isGlobal = true
}
isGlobalScope() {
return this._isGlobal
}
}
export function createDestroyPeerMethod(clazz: MaterializedClass): MaterializedMethod | undefined {
if (clazz.isGlobalScope() || clazz.isStaticMaterialized) {
return undefined
}
return new MaterializedMethod(
undefined,
new PeerMethodSignature(
PeerMethodSignature.DESTROY,
'%NEVER_USED$',
[],
idl.IDLVoidType,
clazz.decl,
),
idl.getQualifiedName(clazz.decl, "namespace.name").split('.').join('_'),
clazz.getImplementationName(),
idl.IDLVoidType,
false,
PeerMethodSignature.DESTROY,
new Method(
PeerMethodSignature.DESTROY,
new NamedMethodSignature(
idl.IDLVoidType,
[idl.createReferenceType(clazz.decl)],
['peer']
)
)
)
}
export function getInternalClassName(name: string): string {
return `${name}Internal`
}
export function getInternalClassQualifiedName(target: idl.IDLEntry, pattern: idl.QNPattern = "package.namespace.name", language?: Language): string {
return getInternalClassName(qualifiedName(target, language ?? ".", pattern))
}
export function getMaterializedFileName(name:string): string {
const pascalCase = name.split('_').map(x => capitalize(x)).join('')
return `Ark${pascalCase}Materialized`
}
type PropertyState = {
isAccessor: true,
hasGetter: boolean,
hasSetter: boolean
} | {
isAccessor: false,
isReadonly: boolean
}
function toAccessor(state: PropertyState): PropertyState {
return state.isAccessor
? state
: {
isAccessor: true,
hasGetter: true,
hasSetter: !state.isReadonly
}
}
export function stateToAccessor(decl: idl.IDLInterface, lang: Language, state: PropertyState): PropertyState {
if (lang == Language.KOTLIN) return toAccessor(state)
if (idl.isClassSubkind(decl)) return state
return toAccessor(state)
}