idlize

Форк
0
/
idl.ts 
607 строк · 17.4 Кб
1
/*
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
6
 *
7
 * http://www.apache.org/licenses/LICENSE-2.0
8
 *
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.
14
 */
15

16
import * as webidl2 from "webidl2"
17
import { indentedBy, isDefined, stringOrNone } from "./util";
18

19
export enum IDLKind {
20
    Interface,
21
    Class,
22
    AnonymousInterface,
23
    Callback,
24
    Const,
25
    Property,
26
    Parameter,
27
    Method,
28
    Callable,
29
    Constructor,
30
    Enum,
31
    EnumMember,
32
    Typedef,
33
    PrimitiveType,
34
    ContainerType,
35
    ReferenceType,
36
    EnumType,
37
    UnionType,
38
    TypeParameterType,
39
    ModuleType,
40
    TupleInterface
41
}
42

43
export enum IDLEntity {
44
    Class = "Class",
45
    Interface = "Interface",
46
    Literal = "Literal",
47
    NamedTuple = "NamedTuple",
48
    Tuple = "Tuple"
49
}
50

51
export interface IDLExtendedAttribute {
52
    name: string
53
    value?: string
54
}
55

56
export interface IDLEntry {
57
    name?: string
58
    kind?: IDLKind
59
    fileName?: string
60
    comment?: string
61
    documentation?: string
62
    extendedAttributes?: IDLExtendedAttribute[]
63
    scope?: IDLEntry[]
64
}
65

66
export interface IDLType {
67
    name: string
68
    kind: IDLKind
69
    fileName?: string
70
    extendedAttributes?: IDLExtendedAttribute[]
71
    documentation?: string
72
}
73

74
export interface IDLTypedef extends IDLEntry {
75
    kind: IDLKind.Typedef
76
    name: string
77
    type: IDLType
78
}
79

80
export interface IDLPrimitiveType extends IDLType {
81
    kind: IDLKind.PrimitiveType
82
}
83

84
export interface IDLContainerType extends IDLType {
85
    kind: IDLKind.ContainerType
86
    elementType: IDLType[]
87
}
88

89
export interface IDLReferenceType extends IDLType {
90
    kind: IDLKind.ReferenceType
91
}
92

93
export interface IDLEnumType extends IDLType {
94
    kind: IDLKind.EnumType
95
}
96

97
export interface IDLUnionType extends IDLType {
98
    kind: IDLKind.UnionType
99
    types: IDLType[]
100
}
101

102
export interface IDLTypeParameterType extends IDLType {
103
    kind: IDLKind.TypeParameterType
104
}
105

106
export interface IDLModuleType extends IDLType {
107
    kind: IDLKind.ModuleType
108
}
109

110
export interface IDLVariable extends IDLEntry {
111
    type?: IDLType;
112
}
113

114
export interface IDLTypedEntry extends IDLEntry {
115
    type?: IDLType;
116
}
117

118
export interface IDLEnum extends IDLEntry {
119
    kind: IDLKind.Enum
120
    name: string
121
    elements: IDLEnumMember[]
122
}
123

124
export interface IDLEnumMember extends IDLEntry {
125
    kind: IDLKind.EnumMember
126
    name: string
127
    type: IDLPrimitiveType
128
    initializer: number | string | undefined
129
}
130

131
export interface IDLConstant extends IDLTypedEntry {
132
    name: string
133
    kind: IDLKind.Const
134
    type: IDLType
135
    value: string
136
}
137

138
export interface IDLProperty extends IDLTypedEntry {
139
    name: string
140
    kind: IDLKind.Property
141
    type: IDLType
142
    isReadonly: boolean
143
    isStatic: boolean
144
    isOptional: boolean
145
}
146

147
export interface IDLParameter extends IDLTypedEntry {
148
    kind: IDLKind.Parameter
149
    name: string
150
    isVariadic: boolean
151
    isOptional: boolean
152
}
153

154
export interface IDLSignature extends IDLEntry {
155
    parameters: IDLParameter[]
156
    returnType?: IDLType
157
}
158

159
export interface IDLFunction extends IDLSignature {
160
}
161

162
export interface IDLMethod extends IDLFunction {
163
    kind: IDLKind.Method
164
    name: string
165
    returnType: IDLType
166
    isStatic: boolean
167
    isOptional: boolean
168
}
169

170
export interface IDLCallable extends IDLFunction {
171
    kind: IDLKind.Callable
172
    isStatic: boolean
173
}
174

175
export interface IDLConstructor extends IDLSignature {
176
    kind: IDLKind.Constructor
177
}
178

179
export interface IDLInterface extends IDLEntry {
180
    name: string
181
    kind: IDLKind.Interface | IDLKind.Class | IDLKind.AnonymousInterface | IDLKind.TupleInterface
182
    inheritance: IDLType[]
183
    constructors: IDLConstructor[]
184
    constants: IDLConstant[]
185
    properties: IDLProperty[]
186
    methods: IDLMethod[]
187
    callables: IDLFunction[]
188
}
189

190
export interface IDLCallback extends IDLEntry, IDLSignature {
191
    kind: IDLKind.Callback
192
    name: string
193
    returnType: IDLType
194
}
195

196
export function forEachChild(node: IDLEntry, cb: (entry: IDLEntry) => void): void {
197
    cb(node)
198
    switch (node.kind) {
199
        case IDLKind.Interface:
200
        case IDLKind.Class:
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))
210
            break
211
        }
212
        case IDLKind.Method:
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)
219
            break
220
        }
221
        case IDLKind.UnionType: {
222
            let param = node as IDLUnionType
223
            param.types?.forEach((value) => forEachChild(value, cb))
224
            break
225
        }
226
        case IDLKind.Enum: {
227
            break
228
        }
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: {
237
            break
238
        }
239
        case IDLKind.ModuleType: {
240
            break
241
        }
242
        default: {
243
            throw new Error(`Unhandled ${node.kind}`)
244
        }
245
    }
246
}
247

248
export function isPrimitiveType(type: IDLType): type is IDLPrimitiveType {
249
    return type.kind == IDLKind.PrimitiveType
250
}
251
export function isContainerType(type: IDLType): type is IDLContainerType {
252
    return type.kind == IDLKind.ContainerType
253
}
254
export function isReferenceType(type: IDLType): type is IDLReferenceType {
255
    return type.kind == IDLKind.ReferenceType
256
}
257
export function isEnumType(type: IDLType): type is IDLEnumType {
258
    return type.kind == IDLKind.EnumType
259
}
260
export function isEnum(type: IDLEntry): type is IDLEnum {
261
    return type.kind == IDLKind.Enum
262
}
263
export function isUnionType(type: IDLType): type is IDLUnionType {
264
    return type.kind == IDLKind.UnionType
265
}
266
export function isTypeParameterType(type: IDLType): type is IDLTypeParameterType {
267
    return type.kind == IDLKind.TypeParameterType
268
}
269
export function isInterface(node: IDLEntry): node is IDLInterface {
270
    return node.kind === IDLKind.Interface
271
}
272
export function isClass(node: IDLEntry): node is IDLInterface {
273
    return node.kind === IDLKind.Class
274
}
275
export function isMethod(node: IDLEntry): node is IDLMethod {
276
    return node.kind === IDLKind.Method
277
}
278
export function isConstructor(node: IDLEntry): node is IDLConstructor {
279
    return node.kind === IDLKind.Constructor
280
}
281
export function isProperty(node: IDLEntry): node is IDLProperty {
282
    return node.kind === IDLKind.Property
283
}
284
export function isCallback(node: IDLEntry): node is IDLCallback {
285
    return node.kind === IDLKind.Callback
286
}
287
export function isTypedef(node: IDLEntry): node is IDLTypedef {
288
    return node.kind === IDLKind.Typedef
289
}
290

291
export function isModuleType(node: IDLEntry): node is IDLModuleType {
292
    return node.kind === IDLKind.ModuleType
293
}
294

295
export function createStringType(): IDLPrimitiveType {
296
    return {
297
        kind: IDLKind.PrimitiveType,
298
        name: "DOMString"
299
    }
300
}
301

302
export function createNumberType(): IDLPrimitiveType {
303
    return {
304
        kind: IDLKind.PrimitiveType,
305
        name: "number"
306
    }
307
}
308

309
export function createBooleanType(): IDLPrimitiveType {
310
    return {
311
        kind: IDLKind.PrimitiveType,
312
        name: "boolean"
313
    }
314
}
315

316
export function createUndefinedType(): IDLPrimitiveType {
317
    return {
318
        kind: IDLKind.PrimitiveType,
319
        name: "undefined"
320
    }
321
}
322

323
export function createAnyType(documentation?: string): IDLPrimitiveType {
324
    return {
325
        kind: IDLKind.PrimitiveType,
326
        name: "any",
327
        documentation: documentation
328
    }
329
}
330

331
export function createReferenceType(name: string): IDLReferenceType {
332
    return {
333
        kind: IDLKind.ReferenceType,
334
        name: name
335
    }
336
}
337

338
export function createEnumType(name: string): IDLEnumType {
339
    return {
340
        kind: IDLKind.EnumType,
341
        name: name
342
    }
343
}
344

345
export function createContainerType(container: string, element: IDLType[]): IDLContainerType {
346
    return {
347
        kind: IDLKind.ContainerType,
348
        name: container,
349
        elementType: element
350
    }
351
}
352

353
export function createUnionType(types: IDLType[]): IDLUnionType {
354
    return {
355
        kind: IDLKind.UnionType,
356
        name: "or",
357
        types: types
358
    }
359
}
360

361
export function createTypeParameterReference(name: string): IDLTypeParameterType {
362
    return {
363
        kind: IDLKind.TypeParameterType,
364
        name: name
365
    }
366
}
367

368
export function createTypedef(name: string, type: IDLType): IDLTypedef {
369
    return {
370
        kind: IDLKind.Typedef,
371
        name: name,
372
        type: type
373
    }
374
}
375

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}`
384
    }
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]}`)
389
}
390

391
export function printParameters(parameters: IDLParameter[] | undefined): string {
392
    return parameters
393
        ?.map(it =>
394
            nameWithType(it, it.isVariadic, it.isOptional)
395
        )
396
        ?.join(", ") ?? ""
397
}
398

399
export function printConstructor(idl: IDLFunction): stringOrNone[] {
400
    return [indentedBy(`constructor(${printParameters(idl.parameters)});`, 1)]
401
}
402

403
export function nameWithType(
404
    idl: IDLVariable,
405
    isVariadic: boolean = false,
406
    isOptional: boolean = false
407
): string {
408
    const type = printType(idl.type)
409
    const variadic = isVariadic ? "..." : ""
410
    const optional = isOptional ? "optional " : ""
411
    return `${optional}${type}${variadic} ${idl.name}`
412
}
413

414
function printConstant(idl: IDLConstant): stringOrNone[] {
415
    return [
416
        ...printExtendedAttributes(idl, 1),
417
        indentedBy(`const ${nameWithType(idl)} = ${idl.value};`, 1)
418
    ]
419
}
420

421
function printProperty(idl: IDLProperty): stringOrNone[] {
422
    const staticMod = idl.isStatic ? "static " : ""
423
    const readonlyMod = idl.isReadonly ? "readonly " : ""
424

425
    return [
426
        ...printExtendedAttributes(idl, 1),
427
        indentedBy(`${staticMod}${readonlyMod}attribute ${nameWithType(idl)};`, 1)
428
    ]
429
}
430

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
437
        }
438
        if (attributes)
439
            attributes.push(docs)
440
        else
441
            attributes = [docs]
442
    }
443
    const attrSpec = quoteAttributeValues(attributes)
444
    return attrSpec ? [indentedBy(`[${attrSpec}]`, indentLevel)] : []
445
}
446

447
const attributesToQuote = new Set(["Documentation", "Import", "TypeParameters"])
448

449
function quoteAttributeValues(attributes?: IDLExtendedAttribute[]): stringOrNone {
450
    return attributes
451
        ?.map(it => {
452
            let attr = it.name
453
            if (it.value) {
454
                const value = it.value.replaceAll('"', "'")
455
                attr += `=${attributesToQuote.has(it.name) ? `"${value}"` : it.value}`
456
            }
457
            return attr})
458
        .join(", ")
459
}
460

461
export function printFunction(idl: IDLFunction): stringOrNone[] {
462
    if (idl.name?.startsWith("__")) {
463
        console.log(`Ignore ${idl.name}`)
464
        return []
465
    }
466
    return [
467
        ...printExtendedAttributes(idl, 1),
468
        indentedBy(`${printType(idl.returnType)} ${idl.name}(${printParameters(idl.parameters)});`, 1)
469
    ]
470
}
471

472
export function printMethod(idl: IDLMethod): stringOrNone[] {
473
    if (idl.name?.startsWith("__")) {
474
        console.log(`Ignore ${idl.name}`)
475
        return []
476
    }
477
    return [
478
        ...printExtendedAttributes(idl, 1),
479
        indentedBy(`${idl.isStatic ? "static " : ""}${printType(idl.returnType)} ${idl.name}(${printParameters(idl.parameters)});`, 1)
480
    ]
481
}
482

483
export function printModule(idl: IDLModuleType): stringOrNone[] {
484
    // May changes later to deal with namespace. currently just VerbatimDts
485
    return [
486
        ...printExtendedAttributes(idl,0),
487
        `namespace ${idl.name} {};`
488
    ]
489
}
490

491
export function printCallback(idl: IDLCallback): stringOrNone[] {
492
    return [`callback ${idl.name} = ${printType(idl.returnType)} (${printParameters(idl.parameters)});`]
493
}
494

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} */`]
501
}
502

503
export function printInterface(idl: IDLInterface): stringOrNone[] {
504
    return [
505
        ...printExtendedAttributes(idl, 0),
506
        `interface ${idl.name} ${idl.inheritance.length > 0 ? ": " + printType(idl.inheritance[0]) : ""} {`,
507
        // TODO: type system hack!
508
    ]
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())
514
        .concat(["};"])
515
}
516

517
export function printEnumMember(idl: IDLEnumMember): stringOrNone[] {
518
    const type = printType(idl.type)
519
    const initializer = type == "DOMString" ? `"${idl.initializer}"` : idl.initializer
520
    return [
521
        idl.documentation,
522
        ...printExtendedAttributes(idl, 0),
523
        `${type} ${idl.name}${initializer ? ` = ${initializer}` : ``};`
524
    ].map(it => it ? indentedBy(it, 1) : undefined)
525
}
526

527
export function printEnum(idl: IDLEnum, skipInitializers: boolean): stringOrNone[] {
528
    if (skipInitializers) {
529
        return [
530
            idl.documentation,
531
            ...printExtendedAttributes(idl, 0),
532
            `enum ${idl.name!} {`,
533
            ...idl.elements.map(it => indentedBy(`${it.name} ${(it.initializer ? " /* " + it.initializer + " */" : "")}`, 1)),
534
            "};"
535
        ]
536
    } else {
537
        return [
538
            idl.documentation,
539
            ...printExtendedAttributes(idl, 0),
540
            `dictionary ${idl.name!} {`,
541
            ...idl.elements.map(printEnumMember) as any,
542
            "};"
543
        ].flat()
544
    }
545
}
546

547
export function printTypedef(idl: IDLTypedef): stringOrNone[] {
548
    return [
549
        idl.documentation,
550
        ...printExtendedAttributes(idl, 0),
551
        `typedef ${printType(idl.type)} ${idl.name!};`
552
    ]
553
}
554

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}`]
566
}
567

568
export interface IDLPrintOptions {
569
    verifyIdl: boolean
570
    disableEnumInitializers: boolean
571
}
572

573
export function toIDLString(entries: IDLEntry[], options: Partial<IDLPrintOptions>): string {
574
    const generatedIdl = entries
575
        .map(it => printIDL(it, options))
576
        .concat(printScopes(entries))
577
        .flat()
578
        .filter(isDefined)
579
        .filter(it => it.length > 0)
580
        .join("\n")
581
    if (options.verifyIdl) webidl2.validate(webidl2.parse(generatedIdl))
582
    return generatedIdl
583
}
584

585
function printScopes(entries: IDLEntry[]) {
586
    return entries
587
        .map((it: IDLEntry) => it.scope)
588
        .filter(isDefined)
589
        .flatMap((it: IDLEntry[]) => it.map(printScoped))
590
}
591

592
export function hasExtAttribute(node: IDLEntry, attribute: string): boolean {
593
    return node.extendedAttributes?.find((it) => it.name == attribute) != undefined
594
}
595

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
600
    })
601
    return value
602
}
603

604
export function getVerbatimDts(node: IDLEntry): stringOrNone {
605
    let value = getExtAttribute(node, "VerbatimDts")
606
    return value ? value.substring(1, value.length - 1) : undefined
607
}

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.