16
import * as webidl2 from "webidl2"
17
import { indentedBy, isDefined, stringOrNone } from "./util";
41
export interface IDLExtendedAttribute {
46
export interface IDLEntry {
51
documentation?: string
52
extendedAttributes?: IDLExtendedAttribute[]
56
export interface IDLType {
60
extendedAttributes?: IDLExtendedAttribute[]
61
documentation?: string
64
export interface IDLTypedef extends IDLEntry {
70
export interface IDLPrimitiveType extends IDLType {
71
kind: IDLKind.PrimitiveType
74
export interface IDLContainerType extends IDLType {
75
kind: IDLKind.ContainerType
76
elementType: IDLType[]
79
export interface IDLReferenceType extends IDLType {
80
kind: IDLKind.ReferenceType
83
export interface IDLEnumType extends IDLType {
84
kind: IDLKind.EnumType
87
export interface IDLUnionType extends IDLType {
88
kind: IDLKind.UnionType
92
export interface IDLTypeParameterType extends IDLType {
93
kind: IDLKind.TypeParameterType
96
export interface IDLModuleType extends IDLType {
97
kind: IDLKind.ModuleType
100
export interface IDLVariable extends IDLEntry {
104
export interface IDLTypedEntry extends IDLEntry {
108
export interface IDLEnum extends IDLEntry {
111
elements: IDLEnumMember[]
114
export interface IDLEnumMember extends IDLEntry {
115
kind: IDLKind.EnumMember
117
type: IDLPrimitiveType
118
initializer: number | string | undefined
121
export interface IDLProperty extends IDLTypedEntry {
123
kind: IDLKind.Property
130
export interface IDLParameter extends IDLTypedEntry {
131
kind: IDLKind.Parameter
137
export interface IDLSignature extends IDLEntry {
138
parameters: IDLParameter[]
142
export interface IDLFunction extends IDLSignature {
145
export interface IDLMethod extends IDLFunction {
152
export interface IDLCallable extends IDLFunction {
153
kind: IDLKind.Callable
157
export interface IDLConstructor extends IDLSignature {
158
kind: IDLKind.Constructor
161
export interface IDLInterface extends IDLEntry {
163
kind: IDLKind.Interface | IDLKind.Class | IDLKind.AnonymousInterface
164
inheritance: IDLType[]
165
constructors: IDLConstructor[]
166
properties: IDLProperty[]
168
callables: IDLFunction[]
171
export interface IDLCallback extends IDLEntry, IDLSignature {
172
kind: IDLKind.Callback
177
export function forEachChild(node: IDLEntry, cb: (entry: IDLEntry) => void): void {
180
case IDLKind.Interface:
182
case IDLKind.AnonymousInterface: {
183
let iface = node as IDLInterface
184
iface.inheritance.forEach((value) => forEachChild(value, cb))
185
iface.constructors?.forEach((value) => forEachChild(value, cb))
186
iface.properties.forEach((value) => forEachChild(value, cb))
187
iface.methods.forEach((value) => forEachChild(value, cb))
188
iface.callables.forEach((value) => forEachChild(value, cb))
189
iface.scope?.forEach((value) => forEachChild(value, cb))
193
case IDLKind.Callable:
194
case IDLKind.Callback:
195
case IDLKind.Constructor: {
196
let param = node as IDLSignature
197
param.parameters?.forEach((value) => forEachChild(value, cb))
198
if (param.returnType) forEachChild(param.returnType, cb)
201
case IDLKind.UnionType: {
202
let param = node as IDLUnionType
203
param.types?.forEach((value) => forEachChild(value, cb))
209
case IDLKind.Property:
210
case IDLKind.Parameter:
211
case IDLKind.Typedef:
212
case IDLKind.EnumType:
213
case IDLKind.PrimitiveType:
214
case IDLKind.ContainerType:
215
case IDLKind.TypeParameterType:
216
case IDLKind.ReferenceType: {
219
case IDLKind.ModuleType: {
223
throw new Error(`Unhandled ${node.kind}`)
228
export function isPrimitiveType(type: IDLType): type is IDLPrimitiveType {
229
return type.kind == IDLKind.PrimitiveType
231
export function isContainerType(type: IDLType): type is IDLContainerType {
232
return type.kind == IDLKind.ContainerType
234
export function isReferenceType(type: IDLType): type is IDLReferenceType {
235
return type.kind == IDLKind.ReferenceType
237
export function isEnumType(type: IDLType): type is IDLEnumType {
238
return type.kind == IDLKind.EnumType
240
export function isEnum(type: IDLEntry): type is IDLEnum {
241
return type.kind == IDLKind.Enum
243
export function isUnionType(type: IDLType): type is IDLUnionType {
244
return type.kind == IDLKind.UnionType
246
export function isTypeParameterType(type: IDLType): type is IDLTypeParameterType {
247
return type.kind == IDLKind.TypeParameterType
249
export function isInterface(node: IDLEntry): node is IDLInterface {
250
return node.kind === IDLKind.Interface
252
export function isClass(node: IDLEntry): node is IDLInterface {
253
return node.kind === IDLKind.Class
255
export function isMethod(node: IDLEntry): node is IDLMethod {
256
return node.kind === IDLKind.Method
258
export function isConstructor(node: IDLEntry): node is IDLConstructor {
259
return node.kind === IDLKind.Constructor
261
export function isProperty(node: IDLEntry): node is IDLProperty {
262
return node.kind === IDLKind.Property
264
export function isCallback(node: IDLEntry): node is IDLCallback {
265
return node.kind === IDLKind.Callback
267
export function isTypedef(node: IDLEntry): node is IDLTypedef {
268
return node.kind === IDLKind.Typedef
271
export function isModuleType(node: IDLEntry): node is IDLModuleType {
272
return node.kind === IDLKind.ModuleType
275
export function createStringType(): IDLPrimitiveType {
277
kind: IDLKind.PrimitiveType,
282
export function createNumberType(): IDLPrimitiveType {
284
kind: IDLKind.PrimitiveType,
289
export function createUndefinedType(): IDLPrimitiveType {
291
kind: IDLKind.PrimitiveType,
296
export function createAnyType(documentation?: string): IDLPrimitiveType {
298
kind: IDLKind.PrimitiveType,
300
documentation: documentation
304
export function createReferenceType(name: string): IDLReferenceType {
306
kind: IDLKind.ReferenceType,
311
export function createEnumType(name: string): IDLEnumType {
313
kind: IDLKind.EnumType,
318
export function createContainerType(container: string, element: IDLType[]): IDLContainerType {
320
kind: IDLKind.ContainerType,
326
export function createUnionType(types: IDLType[]): IDLUnionType {
328
kind: IDLKind.UnionType,
334
export function createTypeParameterReference(name: string): IDLTypeParameterType {
336
kind: IDLKind.TypeParameterType,
341
export function createTypedef(name: string, type: IDLType): IDLTypedef {
343
kind: IDLKind.Typedef,
349
export function printType(type: IDLType | undefined): string {
350
if (!type) throw new Error("Missing type")
351
if (isPrimitiveType(type)) return type.name
352
if (isContainerType(type)) return `${type.name}<${type.elementType.map(printType).join(", ")}>`
353
if (isReferenceType(type)) return `${type.name}`
354
if (isUnionType(type)) return `(${type.types.map(printType).join(" or ")})`
355
if (isEnumType(type)) return type.name
356
if (isTypeParameterType(type)) return type.name
357
throw new Error(`Cannot map type: ${IDLKind[type.kind]}`)
360
export function printParameters(parameters: IDLParameter[] | undefined): string {
363
nameWithType(it, it.isVariadic, it.isOptional)
368
export function printConstructor(idl: IDLFunction): stringOrNone[] {
369
return [indentedBy(`constructor(${printParameters(idl.parameters)});`, 1)]
372
export function nameWithType(
374
isVariadic: boolean = false,
375
isOptional: boolean = false
377
const type = printType(idl.type)
378
const variadic = isVariadic ? "..." : ""
379
const optional = isOptional ? "optional " : ""
380
return `${optional}${type}${variadic} ${idl.name}`
383
function printProperty(idl: IDLProperty): stringOrNone[] {
384
const staticMod = idl.isStatic ? "static " : ""
385
const readonlyMod = idl.isReadonly ? "readonly " : ""
388
...printExtendedAttributes(idl, 1),
389
indentedBy(`${staticMod}${readonlyMod}attribute ${nameWithType(idl)};`, 1)
393
function escapeDocs(input: string): string {
394
return input.replaceAll('"', "'")
397
function printExtendedAttributes(idl: IDLEntry, indentLevel: number): stringOrNone[] {
398
let attributes = idl.extendedAttributes
399
if (idl.documentation) {
400
let docs: IDLExtendedAttribute = {
401
name: 'Documentation',
402
value: `"${escapeDocs(idl.documentation)}"`
405
attributes.push(docs)
409
return [attributes ? indentedBy(`[${attributes.map(it => `${it.name}${it.value ? "=" + it.value : ""}`).join(", ")}]`, indentLevel) : undefined]
412
export function printFunction(idl: IDLFunction): stringOrNone[] {
413
if (idl.name?.startsWith("__")) {
414
console.log(`Ignore ${idl.name}`)
418
...printExtendedAttributes(idl, 1),
419
indentedBy(`${printType(idl.returnType)} ${idl.name}(${printParameters(idl.parameters)});`, 1)
423
export function printMethod(idl: IDLMethod): stringOrNone[] {
424
if (idl.name?.startsWith("__")) {
425
console.log(`Ignore ${idl.name}`)
429
...printExtendedAttributes(idl, 1),
430
indentedBy(`${idl.isStatic ? "static " : ""}${printType(idl.returnType)} ${idl.name}(${printParameters(idl.parameters)});`, 1)
434
export function printModule(idl: IDLModuleType): stringOrNone[] {
437
...printExtendedAttributes(idl,0),
438
`namespace ${idl.name} {};`
442
export function printCallback(idl: IDLCallback): stringOrNone[] {
443
return [`callback ${idl.name} = ${printType(idl.returnType)} (${printParameters(idl.parameters)});`]
446
export function printScoped(idl: IDLEntry): stringOrNone[] {
447
if (idl.kind == IDLKind.Callback) return printCallback(idl as IDLCallback)
448
if (idl.kind == IDLKind.AnonymousInterface) return printInterface(idl as IDLInterface)
449
return [`/* Unexpected scoped: ${idl.kind} ${idl.name} */`]
452
export function printInterface(idl: IDLInterface): stringOrNone[] {
454
...printExtendedAttributes(idl, 0),
455
`interface ${idl.name} ${idl.inheritance.length > 0 ? ": " + printType(idl.inheritance[0]) : ""} {`,
458
.concat(idl.constructors.map(printConstructor).flat())
459
.concat(idl.properties.map(printProperty).flat())
460
.concat(idl.methods.map(printMethod).flat())
461
.concat(idl.callables.map(printFunction).flat())
465
export function printEnumMember(idl: IDLEnumMember): stringOrNone[] {
466
const type = printType(idl.type)
467
const initializer = type == "DOMString" ? `"${idl.initializer}"` : idl.initializer
468
return [indentedBy(`${type} ${idl.name}${initializer ? ` = ${initializer}` : ``};`, 1)]
471
export function printEnum(idl: IDLEnum, skipInitializers: boolean): stringOrNone[] {
472
if (skipInitializers) {
475
...printExtendedAttributes(idl, 0),
476
`enum ${idl.name!} {`,
477
...idl.elements.map(it => indentedBy(`${it.name} ${(it.initializer ? " /* " + it.initializer + " */" : "")}`, 1)),
483
...printExtendedAttributes(idl, 0),
484
`dictionary ${idl.name!} {`,
485
...idl.elements.map(printEnumMember) as any,
491
export function printTypedef(idl: IDLTypedef): stringOrNone[] {
494
...printExtendedAttributes(idl, 0),
495
`typedef ${printType(idl.type)} ${idl.name!};`
500
export function printIDL(idl: IDLEntry, options?: Partial<IDLPrintOptions>): stringOrNone[] {
501
if (idl.kind == IDLKind.Class
502
|| idl.kind == IDLKind.Interface
503
|| idl.kind == IDLKind.AnonymousInterface
504
) return printInterface(idl as IDLInterface)
505
if (idl.kind == IDLKind.Enum) return printEnum(idl as IDLEnum, options?.disableEnumInitializers ?? false)
506
if (idl.kind == IDLKind.Typedef) return printTypedef(idl as IDLTypedef)
507
if (idl.kind == IDLKind.Callback) return printCallback(idl as IDLCallback)
508
if (idl.kind == IDLKind.ModuleType) return printModule(idl as IDLModuleType)
509
return [`unexpected kind: ${idl.kind}`]
512
export interface IDLPrintOptions {
514
disableEnumInitializers: boolean
517
export function toIDLString(entries: IDLEntry[], options: Partial<IDLPrintOptions>): string {
518
const generatedScopes = printScopes(entries)
519
const generatedIdl = entries
520
.map(it => printIDL(it, options))
521
.concat(generatedScopes)
524
.filter(it => it.length > 0)
526
if (options.verifyIdl) webidl2.validate(webidl2.parse(generatedIdl))
530
function printScopes(entries: IDLEntry[]) {
532
.map((it: IDLEntry) => it.scope)
534
.flatMap((it: IDLEntry[]) => it.map(printScoped))
537
export function hasExtAttribute(node: IDLEntry, attribute: string): boolean {
538
return node.extendedAttributes?.find((it) => it.name == attribute) != undefined
541
export function getExtAttribute(node: IDLEntry, name: string): stringOrNone {
542
let value = undefined
543
node.extendedAttributes?.forEach(it => {
544
if (it.name == name) value = it.value
549
export function getVerbatimDts(node: IDLEntry): stringOrNone {
550
let value = getExtAttribute(node, "VerbatimDts")
551
return value ? value.substring(1, value.length - 1) : undefined