2
* Copyright (c) 2024 Huawei Device Co., Ltd.
3
* Licensed under the Apache License, Version 2.0 (the "License");
4
* you may not use this file except in compliance with the License.
5
* You may obtain a copy of the License at
7
* http://www.apache.org/licenses/LICENSE-2.0
9
* Unless required by applicable law or agreed to in writing, software
10
* distributed under the License is distributed on an "AS IS" BASIS,
11
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
* See the License for the specific language governing permissions and
13
* limitations under the License.
15
import { indentedBy, stringOrNone } from "../util"
16
import { IDLCallback, IDLConstructor, IDLEntity, IDLEntry, IDLEnum, IDLInterface, IDLKind, IDLMethod, IDLModuleType, IDLParameter, IDLProperty, IDLType, IDLTypedef, getExtAttribute,
20
isClass, isConstructor, isContainerType, isEnum, isEnumType, isInterface, isMethod, isModuleType, isPrimitiveType, isProperty, isReferenceType, isTypeParameterType, isTypedef, isUnionType } from "../idl"
21
import * as webidl2 from "webidl2"
22
import { toIDLNode } from "./deserialize"
25
export class CustomPrintVisitor {
28
visit(node: IDLEntry) {
29
if (isInterface(node) || isClass(node)) {
30
this.printInterface(node)
31
} else if (isMethod(node) || isConstructor(node)) {
32
this.printMethod(node)
33
} else if (isProperty(node)) {
34
this.printProperty(node)
35
} else if (isTypedef(node)) {
36
this.printTypedef(node)
37
} else if (isEnum(node)) {
39
} else if (isCallback(node)) {
40
this.printCallback(node)
41
} else if (isModuleType(node)) {
42
this.printModuleType(node)
44
throw new Error(`Unexpected node kind: ${IDLKind[node.kind!]}`)
48
currentInterface?: IDLInterface
50
printInterface(node: IDLInterface) {
51
let typeSpec = toTypeName(node)
52
const entity = getExtAttribute(node, "Entity")
53
if (entity === IDLEntity.Literal) {
54
this.print(`declare type ${typeSpec} = { ${
55
node.properties.map(it => `${it.name}: ${printTypeForTS(it.type)}`).join(", ")
57
} else if (entity === IDLEntity.Tuple) {
58
this.print(`declare type ${typeSpec} = [ ${
59
node.properties.map(it => `${printTypeForTS(it.type)}`).join(", ")
61
} else if (entity === IDLEntity.NamedTuple) {
62
this.print(`declare type ${typeSpec} = [ ${
63
node.properties.map(it => `${it.name}: ${printTypeForTS(it.type)}`).join(", ")
66
// restore globalScope
67
if (hasExtAttribute(node,"GlobalScope")) {
68
node.methods.map(it => this.printMethod(it, true))
71
if (hasExtAttribute(node, "Component")) {
72
typeSpec = node.name + (node.name == "CommonMethod"
74
: ` extends CommonMethod<${getExtAttribute(node, "Component")}Attribute>`)
76
this.print(`declare ${entity!.toLowerCase()} ${typeSpec} {`)
77
this.currentInterface = node
79
node.constructors.map(it => this.visit(it))
80
node.properties.map(it => this.visit(it))
81
node.methods.map(it => this.visit(it))
82
node.callables.map(it => this.visit(it))
83
let verbatim = getVerbatimDts(node)
87
.map(it => this.print(it))
92
if (hasExtAttribute(node, "Component")) {
93
let name = getExtAttribute(node, "Component")
94
this.print(`declare const ${name}Instance: ${name}Attribute;`)
95
this.print(`declare const ${name}: ${name}Interface;`)
100
printMethod(node: IDLMethod|IDLConstructor, isGlobal : boolean = false) {
101
let returnType = node.returnType ? `: ${printTypeForTS(node.returnType, true)}` : ""
102
let isStatic = isMethod(node) && node.isStatic
103
let name = isConstructor(node) ? "constructor" : node.name
104
let isOptional = isMethod(node) && node.isOptional
105
if (hasExtAttribute(node, "CallSignature")) name = ""
106
if (hasExtAttribute(node,"DtsName")) {
107
let dtsName = getExtAttribute(node, "DtsName")
108
name = dtsName ? dtsName.replaceAll("\"","") : ""
110
this.print(`${isGlobal ? "declare function ": ""}${isStatic ? "static " : ""}${name}${isOptional ?"?":""}(${node.parameters.map(p => this.paramText(p)).join(", ")})${returnType};`)
112
paramText(param: IDLParameter): string {
113
return `${param.isVariadic ? "..." : ""}${param.name}${param.isOptional ? "?" : ""}: ${printTypeForTS(param.type)}`
115
printProperty(node: IDLProperty) {
116
const isCommonMethod = hasExtAttribute(node, "CommonMethod")
117
if (isCommonMethod) {
118
let returnType = this.currentInterface!.name == "CommonMethod" ? "T" : this.currentInterface!.name
119
this.print(`${node.name}(value: ${printTypeForTS(node.type)}): ${returnType};`)
121
this.print(`${node.isStatic ? "static " : ""}${node.isReadonly ? "readonly " : ""}${node.name}${node.isOptional ? "?" : ""}: ${printTypeForTS(node.type)};`)
125
printEnum(node: IDLEnum) {
126
this.print(`declare enum ${node.name} {`)
128
node.elements.forEach((it, index) => {
129
this.print(`${it.name}${it.initializer ? " = " + it.initializer : ""}${index < node.elements.length - 1 ? "," : ""}`)
134
printCallback(node: IDLCallback) {
135
// TODO: is it correct.
136
this.print(`declare type ${(node.name)} = (${node.parameters.map(it => `${it.isVariadic ? "..." : ""}${it.name}: ${printTypeForTS(it.type)}`).join(", ")}) => ${printTypeForTS(node.returnType)};`)
138
printTypedef(node: IDLTypedef) {
139
let text = getVerbatimDts(node) ?? printTypeForTS(node.type)
140
this.print(`declare type ${(node.name)} = ${text};`)
143
printModuleType(node: IDLModuleType) {
144
let text = getVerbatimDts(node) ?? ""
145
this.print(`${text}`)
148
checkVerbatim(node: IDLEntry) {
149
let verbatim = getExtAttribute(node, "VerbatimDts")
152
.substring(1, verbatim.length - 2)
154
.forEach(it => this.print(it))
159
indented(input: string): string {
160
return indentedBy(input, this.indent)
168
print(value: stringOrNone) {
169
if (value) this.output.push(this.indented(value))
173
export function idlToString(name: string, content: string): string {
174
let printer = new CustomPrintVisitor()
175
webidl2.parse(content)
176
.map(it => toIDLNode(name, it))
177
.map(it => printer.visit(it))
178
return printer.output.join("\n")
181
export function printTypeForTS(type: IDLType | undefined, undefinedToVoid?: boolean): string {
182
if (!type) throw new Error("Missing type")
183
if (type.name == "undefined" && undefinedToVoid) return "void"
184
if (type.name == "int32" || type.name == "float32") return "number"
185
if (type.name == "DOMString") return "string"
186
if (type.name == "this") return "T"
187
if (isPrimitiveType(type)) return type.name
188
if (isContainerType(type))
189
return `${mapContainerType(type.name)}<${type.elementType.map(it => printTypeForTS(it)).join(",")}>`
190
if (isReferenceType(type)) return toTypeName(type)
191
if (isUnionType(type)) return `(${type.types.map(it => printTypeForTS(it)).join("|")})`
192
if (isEnumType(type)) return type.name
193
if (isTypeParameterType(type)) return type.name
194
throw new Error(`Cannot map type: ${IDLKind[type.kind]}`)
197
function mapContainerType(idlName: string): string {
199
case "sequence": return "Array"
200
case "record": return "Map"
201
case "Promise": return "Promise"
202
default: throw new Error(`Unmapped container type: ${idlName}`)
206
function toTypeName(node: IDLEntry): string {
207
const importAttr = getExtAttribute(node, "Import")
209
return importAttr.slice(1, -1)
211
const typeParamsAttr = getExtAttribute(node, "TypeParameters")
212
if (typeParamsAttr) {
213
return `${node.name}<${typeParamsAttr.slice(1, -1)}>`
215
return node.name ?? "MISSING_TYPE_NAME"