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.
16
import * as webidl2 from "webidl2"
17
import { indentedBy, isDefined, stringOrNone } from "./util";
43
export enum IDLEntity {
45
Interface = "Interface",
47
NamedTuple = "NamedTuple",
51
export interface IDLExtendedAttribute {
56
export interface IDLEntry {
61
documentation?: string
62
extendedAttributes?: IDLExtendedAttribute[]
66
export interface IDLType {
70
extendedAttributes?: IDLExtendedAttribute[]
71
documentation?: string
74
export interface IDLTypedef extends IDLEntry {
80
export interface IDLPrimitiveType extends IDLType {
81
kind: IDLKind.PrimitiveType
84
export interface IDLContainerType extends IDLType {
85
kind: IDLKind.ContainerType
86
elementType: IDLType[]
89
export interface IDLReferenceType extends IDLType {
90
kind: IDLKind.ReferenceType
93
export interface IDLEnumType extends IDLType {
94
kind: IDLKind.EnumType
97
export interface IDLUnionType extends IDLType {
98
kind: IDLKind.UnionType
102
export interface IDLTypeParameterType extends IDLType {
103
kind: IDLKind.TypeParameterType
106
export interface IDLModuleType extends IDLType {
107
kind: IDLKind.ModuleType
110
export interface IDLVariable extends IDLEntry {
114
export interface IDLTypedEntry extends IDLEntry {
118
export interface IDLEnum extends IDLEntry {
121
elements: IDLEnumMember[]
124
export interface IDLEnumMember extends IDLEntry {
125
kind: IDLKind.EnumMember
127
type: IDLPrimitiveType
128
initializer: number | string | undefined
131
export interface IDLConstant extends IDLTypedEntry {
138
export interface IDLProperty extends IDLTypedEntry {
140
kind: IDLKind.Property
147
export interface IDLParameter extends IDLTypedEntry {
148
kind: IDLKind.Parameter
154
export interface IDLSignature extends IDLEntry {
155
parameters: IDLParameter[]
159
export interface IDLFunction extends IDLSignature {
162
export interface IDLMethod extends IDLFunction {
170
export interface IDLCallable extends IDLFunction {
171
kind: IDLKind.Callable
175
export interface IDLConstructor extends IDLSignature {
176
kind: IDLKind.Constructor
179
export interface IDLInterface extends IDLEntry {
181
kind: IDLKind.Interface | IDLKind.Class | IDLKind.AnonymousInterface | IDLKind.TupleInterface
182
inheritance: IDLType[]
183
constructors: IDLConstructor[]
184
constants: IDLConstant[]
185
properties: IDLProperty[]
187
callables: IDLFunction[]
190
export interface IDLCallback extends IDLEntry, IDLSignature {
191
kind: IDLKind.Callback
196
export function forEachChild(node: IDLEntry, cb: (entry: IDLEntry) => void): void {
199
case IDLKind.Interface:
201
case IDLKind.TupleInterface:
202
case IDLKind.AnonymousInterface: {
203
let iface = node as IDLInterface
204
iface.inheritance.forEach((value) => forEachChild(value, cb))
205
iface.constructors?.forEach((value) => forEachChild(value, cb))
206
iface.properties.forEach((value) => forEachChild(value, cb))
207
iface.methods.forEach((value) => forEachChild(value, cb))
208
iface.callables.forEach((value) => forEachChild(value, cb))
209
iface.scope?.forEach((value) => forEachChild(value, cb))
213
case IDLKind.Callable:
214
case IDLKind.Callback:
215
case IDLKind.Constructor: {
216
let param = node as IDLSignature
217
param.parameters?.forEach((value) => forEachChild(value, cb))
218
if (param.returnType) forEachChild(param.returnType, cb)
221
case IDLKind.UnionType: {
222
let param = node as IDLUnionType
223
param.types?.forEach((value) => forEachChild(value, cb))
229
case IDLKind.Property:
230
case IDLKind.Parameter:
231
case IDLKind.Typedef:
232
case IDLKind.EnumType:
233
case IDLKind.PrimitiveType:
234
case IDLKind.ContainerType:
235
case IDLKind.TypeParameterType:
236
case IDLKind.ReferenceType: {
239
case IDLKind.ModuleType: {
243
throw new Error(`Unhandled ${node.kind}`)
248
export function isPrimitiveType(type: IDLType): type is IDLPrimitiveType {
249
return type.kind == IDLKind.PrimitiveType
251
export function isContainerType(type: IDLType): type is IDLContainerType {
252
return type.kind == IDLKind.ContainerType
254
export function isReferenceType(type: IDLType): type is IDLReferenceType {
255
return type.kind == IDLKind.ReferenceType
257
export function isEnumType(type: IDLType): type is IDLEnumType {
258
return type.kind == IDLKind.EnumType
260
export function isEnum(type: IDLEntry): type is IDLEnum {
261
return type.kind == IDLKind.Enum
263
export function isUnionType(type: IDLType): type is IDLUnionType {
264
return type.kind == IDLKind.UnionType
266
export function isTypeParameterType(type: IDLType): type is IDLTypeParameterType {
267
return type.kind == IDLKind.TypeParameterType
269
export function isInterface(node: IDLEntry): node is IDLInterface {
270
return node.kind === IDLKind.Interface
272
export function isClass(node: IDLEntry): node is IDLInterface {
273
return node.kind === IDLKind.Class
275
export function isMethod(node: IDLEntry): node is IDLMethod {
276
return node.kind === IDLKind.Method
278
export function isConstructor(node: IDLEntry): node is IDLConstructor {
279
return node.kind === IDLKind.Constructor
281
export function isProperty(node: IDLEntry): node is IDLProperty {
282
return node.kind === IDLKind.Property
284
export function isCallback(node: IDLEntry): node is IDLCallback {
285
return node.kind === IDLKind.Callback
287
export function isTypedef(node: IDLEntry): node is IDLTypedef {
288
return node.kind === IDLKind.Typedef
291
export function isModuleType(node: IDLEntry): node is IDLModuleType {
292
return node.kind === IDLKind.ModuleType
295
export function createStringType(): IDLPrimitiveType {
297
kind: IDLKind.PrimitiveType,
302
export function createNumberType(): IDLPrimitiveType {
304
kind: IDLKind.PrimitiveType,
309
export function createBooleanType(): IDLPrimitiveType {
311
kind: IDLKind.PrimitiveType,
316
export function createUndefinedType(): IDLPrimitiveType {
318
kind: IDLKind.PrimitiveType,
323
export function createAnyType(documentation?: string): IDLPrimitiveType {
325
kind: IDLKind.PrimitiveType,
327
documentation: documentation
331
export function createReferenceType(name: string): IDLReferenceType {
333
kind: IDLKind.ReferenceType,
338
export function createEnumType(name: string): IDLEnumType {
340
kind: IDLKind.EnumType,
345
export function createContainerType(container: string, element: IDLType[]): IDLContainerType {
347
kind: IDLKind.ContainerType,
353
export function createUnionType(types: IDLType[]): IDLUnionType {
355
kind: IDLKind.UnionType,
361
export function createTypeParameterReference(name: string): IDLTypeParameterType {
363
kind: IDLKind.TypeParameterType,
368
export function createTypedef(name: string, type: IDLType): IDLTypedef {
370
kind: IDLKind.Typedef,
376
export function printType(type: IDLType | undefined): string {
377
if (!type) throw new Error("Missing type")
378
if (isPrimitiveType(type)) return type.name
379
if (isContainerType(type)) return `${type.name}<${type.elementType.map(printType).join(", ")}>`
380
if (isReferenceType(type)) {
381
const attrs = quoteAttributeValues(type.extendedAttributes)
382
const attrSpec = attrs ? `[${attrs}] ` : ""
383
return `${attrSpec}${type.name}`
385
if (isUnionType(type)) return `(${type.types.map(printType).join(" or ")})`
386
if (isEnumType(type)) return type.name
387
if (isTypeParameterType(type)) return type.name
388
throw new Error(`Cannot map type: ${IDLKind[type.kind]}`)
391
export function printParameters(parameters: IDLParameter[] | undefined): string {
394
nameWithType(it, it.isVariadic, it.isOptional)
399
export function printConstructor(idl: IDLFunction): stringOrNone[] {
400
return [indentedBy(`constructor(${printParameters(idl.parameters)});`, 1)]
403
export function nameWithType(
405
isVariadic: boolean = false,
406
isOptional: boolean = false
408
const type = printType(idl.type)
409
const variadic = isVariadic ? "..." : ""
410
const optional = isOptional ? "optional " : ""
411
return `${optional}${type}${variadic} ${idl.name}`
414
function printConstant(idl: IDLConstant): stringOrNone[] {
416
...printExtendedAttributes(idl, 1),
417
indentedBy(`const ${nameWithType(idl)} = ${idl.value};`, 1)
421
function printProperty(idl: IDLProperty): stringOrNone[] {
422
const staticMod = idl.isStatic ? "static " : ""
423
const readonlyMod = idl.isReadonly ? "readonly " : ""
426
...printExtendedAttributes(idl, 1),
427
indentedBy(`${staticMod}${readonlyMod}attribute ${nameWithType(idl)};`, 1)
431
function printExtendedAttributes(idl: IDLEntry, indentLevel: number): stringOrNone[] {
432
let attributes = idl.extendedAttributes
433
if (idl.documentation) {
434
let docs: IDLExtendedAttribute = {
435
name: 'Documentation',
436
value: idl.documentation
439
attributes.push(docs)
443
const attrSpec = quoteAttributeValues(attributes)
444
return attrSpec ? [indentedBy(`[${attrSpec}]`, indentLevel)] : []
447
const attributesToQuote = new Set(["Documentation", "Import", "TypeParameters"])
449
function quoteAttributeValues(attributes?: IDLExtendedAttribute[]): stringOrNone {
454
const value = it.value.replaceAll('"', "'")
455
attr += `=${attributesToQuote.has(it.name) ? `"${value}"` : it.value}`
461
export function printFunction(idl: IDLFunction): stringOrNone[] {
462
if (idl.name?.startsWith("__")) {
463
console.log(`Ignore ${idl.name}`)
467
...printExtendedAttributes(idl, 1),
468
indentedBy(`${printType(idl.returnType)} ${idl.name}(${printParameters(idl.parameters)});`, 1)
472
export function printMethod(idl: IDLMethod): stringOrNone[] {
473
if (idl.name?.startsWith("__")) {
474
console.log(`Ignore ${idl.name}`)
478
...printExtendedAttributes(idl, 1),
479
indentedBy(`${idl.isStatic ? "static " : ""}${printType(idl.returnType)} ${idl.name}(${printParameters(idl.parameters)});`, 1)
483
export function printModule(idl: IDLModuleType): stringOrNone[] {
484
// May changes later to deal with namespace. currently just VerbatimDts
486
...printExtendedAttributes(idl,0),
487
`namespace ${idl.name} {};`
491
export function printCallback(idl: IDLCallback): stringOrNone[] {
492
return [`callback ${idl.name} = ${printType(idl.returnType)} (${printParameters(idl.parameters)});`]
495
export function printScoped(idl: IDLEntry): stringOrNone[] {
496
if (idl.kind == IDLKind.Callback) return printCallback(idl as IDLCallback)
497
if (idl.kind == IDLKind.AnonymousInterface) return printInterface(idl as IDLInterface)
498
if (idl.kind == IDLKind.TupleInterface) return printInterface(idl as IDLInterface)
499
if (idl.kind == IDLKind.Typedef) return printTypedef(idl as IDLTypedef)
500
return [`/* Unexpected scoped: ${idl.kind} ${idl.name} */`]
503
export function printInterface(idl: IDLInterface): stringOrNone[] {
505
...printExtendedAttributes(idl, 0),
506
`interface ${idl.name} ${idl.inheritance.length > 0 ? ": " + printType(idl.inheritance[0]) : ""} {`,
507
// TODO: type system hack!
509
.concat(idl.constructors.map(printConstructor).flat())
510
.concat(idl.constants.map(printConstant).flat())
511
.concat(idl.properties.map(printProperty).flat())
512
.concat(idl.methods.map(printMethod).flat())
513
.concat(idl.callables.map(printFunction).flat())
517
export function printEnumMember(idl: IDLEnumMember): stringOrNone[] {
518
const type = printType(idl.type)
519
const initializer = type == "DOMString" ? `"${idl.initializer}"` : idl.initializer
522
...printExtendedAttributes(idl, 0),
523
`${type} ${idl.name}${initializer ? ` = ${initializer}` : ``};`
524
].map(it => it ? indentedBy(it, 1) : undefined)
527
export function printEnum(idl: IDLEnum, skipInitializers: boolean): stringOrNone[] {
528
if (skipInitializers) {
531
...printExtendedAttributes(idl, 0),
532
`enum ${idl.name!} {`,
533
...idl.elements.map(it => indentedBy(`${it.name} ${(it.initializer ? " /* " + it.initializer + " */" : "")}`, 1)),
539
...printExtendedAttributes(idl, 0),
540
`dictionary ${idl.name!} {`,
541
...idl.elements.map(printEnumMember) as any,
547
export function printTypedef(idl: IDLTypedef): stringOrNone[] {
550
...printExtendedAttributes(idl, 0),
551
`typedef ${printType(idl.type)} ${idl.name!};`
555
export function printIDL(idl: IDLEntry, options?: Partial<IDLPrintOptions>): stringOrNone[] {
556
if (idl.kind == IDLKind.Class
557
|| idl.kind == IDLKind.Interface
558
|| idl.kind == IDLKind.AnonymousInterface
559
|| idl.kind == IDLKind.TupleInterface
560
) return printInterface(idl as IDLInterface)
561
if (idl.kind == IDLKind.Enum) return printEnum(idl as IDLEnum, options?.disableEnumInitializers ?? false)
562
if (idl.kind == IDLKind.Typedef) return printTypedef(idl as IDLTypedef)
563
if (idl.kind == IDLKind.Callback) return printCallback(idl as IDLCallback)
564
if (idl.kind == IDLKind.ModuleType) return printModule(idl as IDLModuleType)
565
return [`unexpected kind: ${idl.kind}`]
568
export interface IDLPrintOptions {
570
disableEnumInitializers: boolean
573
export function toIDLString(entries: IDLEntry[], options: Partial<IDLPrintOptions>): string {
574
const generatedIdl = entries
575
.map(it => printIDL(it, options))
576
.concat(printScopes(entries))
579
.filter(it => it.length > 0)
581
if (options.verifyIdl) webidl2.validate(webidl2.parse(generatedIdl))
585
function printScopes(entries: IDLEntry[]) {
587
.map((it: IDLEntry) => it.scope)
589
.flatMap((it: IDLEntry[]) => it.map(printScoped))
592
export function hasExtAttribute(node: IDLEntry, attribute: string): boolean {
593
return node.extendedAttributes?.find((it) => it.name == attribute) != undefined
596
export function getExtAttribute(node: IDLEntry, name: string): stringOrNone {
597
let value = undefined
598
node.extendedAttributes?.forEach(it => {
599
if (it.name == name) value = it.value
604
export function getVerbatimDts(node: IDLEntry): stringOrNone {
605
let value = getExtAttribute(node, "VerbatimDts")
606
return value ? value.substring(1, value.length - 1) : undefined