From 4bf331052ab5cee20d2723a03a108eb962185118 Mon Sep 17 00:00:00 2001 From: Kagami Sascha Rosylight Date: Fri, 27 Apr 2018 11:31:56 +0900 Subject: [PATCH] autoexpose all types --- baselines/webworker.generated.d.ts | 43 +-------- inputfiles/addedTypes.json | 8 +- inputfiles/knownWorkerTypes.json | 134 +---------------------------- src/emitter.ts | 44 +++------- src/helpers.ts | 81 +++++++++++++++++ src/index.ts | 9 +- src/types.d.ts | 4 +- src/widlprocess.ts | 4 +- 8 files changed, 114 insertions(+), 213 deletions(-) diff --git a/baselines/webworker.generated.d.ts b/baselines/webworker.generated.d.ts index c8b690b91..df461f303 100644 --- a/baselines/webworker.generated.d.ts +++ b/baselines/webworker.generated.d.ts @@ -2051,20 +2051,8 @@ declare var XMLHttpRequestUpload: { declare type EventListenerOrEventListenerObject = EventListener | EventListenerObject; -interface ErrorEventHandler { - (event: Event | string, source?: string, fileno?: number, columnNumber?: number, error?: Error): void; -} - -interface ForEachCallback { - (keyId: Int8Array | Int16Array | Int32Array | Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray | Float32Array | Float64Array | DataView | ArrayBuffer | null, status: MediaKeyStatus): void; -} - -interface FunctionStringCallback { - (data: string): void; -} - -interface NotificationPermissionCallback { - (permission: NotificationPermission): void; +interface EventHandlerNonNull { + (event: Event): any; } interface PerformanceObserverCallback { @@ -2107,43 +2095,20 @@ type HeadersInit = Headers | string[][] | Record; type BodyInit = Blob | BufferSource | FormData | URLSearchParams | ReadableStream | string; type RequestInfo = Request | string; type BlobPart = BufferSource | Blob | string; +type DOMHighResTimeStamp = number; type PerformanceEntryList = PerformanceEntry[]; type PushMessageDataInit = BufferSource | string; type VibratePattern = number | number[]; type BufferSource = ArrayBufferView | ArrayBuffer; +type DOMTimeStamp = number; type FormDataEntryValue = File | string; type IDBValidKey = number | string | Date | BufferSource | IDBArrayKey; -type AlgorithmIdentifier = string | Algorithm; -type AAGUID = string; -type ByteString = string; -type CryptoOperationData = ArrayBufferView; -type GLbitfield = number; -type GLboolean = boolean; -type GLbyte = number; -type GLclampf = number; -type GLenum = number; -type GLfloat = number; -type GLint = number; -type GLintptr = number; -type GLshort = number; -type GLsizei = number; -type GLsizeiptr = number; -type GLubyte = number; -type GLuint = number; -type GLushort = number; -type IDBKeyPath = string; -type USVString = string; -type payloadtype = number; type MessageEventSource = object | MessagePort | ServiceWorker; type BinaryType = "blob" | "arraybuffer"; type ClientTypes = "window" | "worker" | "sharedworker" | "all"; type IDBCursorDirection = "next" | "nextunique" | "prev" | "prevunique"; type IDBRequestReadyState = "pending" | "done"; type IDBTransactionMode = "readonly" | "readwrite" | "versionchange"; -type KeyFormat = "raw" | "spki" | "pkcs8" | "jwk"; -type KeyType = "public" | "private" | "secret"; -type KeyUsage = "encrypt" | "decrypt" | "sign" | "verify" | "deriveKey" | "deriveBits" | "wrapKey" | "unwrapKey"; -type MediaKeyStatus = "usable" | "expired" | "output-downscaled" | "output-not-allowed" | "status-pending" | "internal-error"; type NotificationDirection = "auto" | "ltr" | "rtl"; type NotificationPermission = "default" | "denied" | "granted"; type PushEncryptionKeyName = "p256dh" | "auth"; diff --git a/inputfiles/addedTypes.json b/inputfiles/addedTypes.json index 989c64493..4665f6d30 100644 --- a/inputfiles/addedTypes.json +++ b/inputfiles/addedTypes.json @@ -515,8 +515,8 @@ "[index: number]: TNode" ], "iterator": { - "type": "iterable", - "subtype": [{ + "kind": "iterable", + "type": [{ "override-type": "TNode" }] } @@ -546,8 +546,8 @@ "[index: number]: T" ], "iterator": { - "type": "iterable", - "subtype": [{ + "kind": "iterable", + "type": [{ "override-type": "T" }] } diff --git a/inputfiles/knownWorkerTypes.json b/inputfiles/knownWorkerTypes.json index e9a3115ae..b4f9eeb8c 100644 --- a/inputfiles/knownWorkerTypes.json +++ b/inputfiles/knownWorkerTypes.json @@ -1,135 +1,7 @@ [ - "ClientQueryOptions", - "ExtendableEventInit", - "ExtendableMessageEventInit", - "FetchEventInit", - "NotificationEventInit", - "PushEventInit", - "SyncEventInit", - "Algorithm", - "CacheQueryOptions", - "CloseEventInit", - "EventInit", - "GetNotificationOptions", - "IDBIndexParameters", - "IDBObjectStoreParameters", - "KeyAlgorithm", - "MessageEventInit", - "NotificationOptions", - "PushSubscriptionOptionsInit", - "RequestInit", - "ResponseInit", - "DOMMatrixInit", - "DOMMatrix2DInit", - "DOMPointInit", - "DOMQuadInit", - "DOMRectInit", - "BlobPart", - "IDBKeyRange", - "MessageEventSource", - "MessagePort", - "NavigationPreloadState", - "NotificationAction", - "PerformanceEntryList", - "PerformanceObserverCallback", - "PerformanceObserverInit", - "PromiseRejectionEventInit", - "PushSubscriptionJSON", - "RegistrationOptions", - "ServiceWorkerUpdateViaCache", - "VibratePattern", - "WorkerType", - "AbstractWorker", - "Body", - "GlobalFetch", - "NavigatorBeacon", - "NavigatorConcurrentHardware", - "NavigatorID", - "NavigatorOnLine", - "WindowBase64", - "WindowConsole", - "Client", - "Clients", - "DedicatedWorkerGlobalScope", - "ServiceWorkerGlobalScope", - "WindowClient", - "WorkerGlobalScope", - "WorkerLocation", - "WorkerNavigator", - "WorkerUtils", - "EventListener", - "ErrorEventHandler", - "ForEachCallback", - "FunctionStringCallback", - "NotificationPermissionCallback", - "PushMessageDataInit", - "AAGUID", - "AlgorithmIdentifier", - "BodyInit", - "ByteString", - "CryptoOperationData", - "GLbitfield", - "GLboolean", - "GLbyte", - "GLclampf", - "GLenum", - "GLfloat", - "GLint", - "GLintptr", - "GLshort", - "GLsizei", - "GLsizeiptr", - "GLubyte", - "GLuint", - "GLushort", - "HeadersInit", - "IDBKeyPath", - "JSON", - "KeyFormat", - "KeyType", - "KeyUsage", - "RequestInfo", - "USVString", - "payloadtype", - "IDBCursorDirection", - "IDBRequestReadyState", - "IDBTransactionMode", - "MediaKeyStatus", - "NotificationDirection", - "NotificationPermission", - "PushEncryptionKeyName", - "PushPermissionState", - "ReferrerPolicy", - "RequestCache", - "RequestCredentials", - "RequestDestination", - "RequestMode", - "RequestRedirect", - "RequestType", - "ResponseType", - "ServiceWorkerState", - "TextDecodeOptions", - "TextDecoderOptions", - "VisibilityState", - "XMLHttpRequestResponseType", "ClientTypes", - "FrameType", - "BinaryType", - "ProgressEventInit", - "EventListenerOptions", - "AddEventListenerOptions", - "ErrorEventInit", - "PushSubscriptionChangeEvent", - "PushSubscriptionChangeInit", - "ImageBitmap", - "ImageBitmapOptions", - "FormDataEntryValue", - "EventListenerObject", - "URLSearchParams", - "BlobPropertyBag", - "FilePropertyBag", - "IDBValidKey", "IDBArrayKey", - "IDBVersionChangeEventInit", - "BufferSource" + "IDBValidKey", + "MessageEventSource", + "PromiseRejectionEventInit" ] diff --git a/src/emitter.ts b/src/emitter.ts index 3fa0c951f..adb902c18 100644 --- a/src/emitter.ts +++ b/src/emitter.ts @@ -1,5 +1,5 @@ import * as Browser from "./types"; -import { mapToArray, distinct, map, toNameMap, mapDefined, arrayToMap, flatMap } from "./helpers"; +import { mapToArray, distinct, map, toNameMap, mapDefined, arrayToMap, flatMap, integerTypes, baseTypeConversionMap } from "./helpers"; export const enum Flavor { Web, @@ -20,9 +20,6 @@ enum EmitScope { } const defaultEventType = "Event"; -// Extended types used but not defined in the spec -const extendedTypes = new Set(["ArrayBuffer", "ArrayBufferView", "DataView", "Int8Array", "Uint8Array", "Int16Array", "Uint16Array", "Uint8ClampedArray", "Int32Array", "Uint32Array", "Float32Array", "Float64Array"]); -const integerTypes = new Set(["byte", "octet", "short", "unsigned short", "long", "unsigned long", "long long", "unsigned long long"]); const tsKeywords = new Set(["default", "delete", "continue"]); const extendConflictsBaseTypes: Record }> = { "AudioContext": { extendType: ["OfflineContext"], memberNames: new Set(["suspend"]) }, @@ -308,31 +305,14 @@ export function emitWebIDl(webidl: Browser.WebIdl, flavor: Flavor) { } function convertDomTypeToTsTypeSimple(objDomType: string): string { + if (baseTypeConversionMap.has(objDomType)) { + return baseTypeConversionMap.get(objDomType)!; + } switch (objDomType) { case "DOMHighResTimeStamp": return "number"; case "DOMTimeStamp": return "number"; case "EventListener": return "EventListenerOrEventListenerObject"; - case "double": - case "unrestricted double": return "number"; - case "float": return "number"; - case "object": return "any"; - case "ByteString": - case "DOMString": - case "USVString": return "string"; - case "sequence": return "Array"; - case "record": return "Record"; - case "FrozenArray": return "ReadonlyArray"; - case "WindowProxy": return "Window"; - case "any": - case "boolean": - case "BufferSource": - case "Date": - case "Function": - case "Promise": - case "void": return objDomType; default: - if (integerTypes.has(objDomType)) return "number"; - if (extendedTypes.has(objDomType)) return objDomType; if (flavor === Flavor.Worker && (objDomType === "Element" || objDomType === "Window" || objDomType === "Document" || objDomType === "AbortSignal" || objDomType === "HTMLFormElement")) return "object"; if (flavor === Flavor.Web && objDomType === "Client") return "object"; // Name of an interface / enum / dict. Just return itself @@ -692,10 +672,10 @@ export function emitWebIDl(webidl: Browser.WebIdl, flavor: Flavor) { if (!i.iterator) { return; } - const subtype = i.iterator.subtype.map(convertDomTypeToTsType); + const subtype = i.iterator.type.map(convertDomTypeToTsType); const value = subtype[subtype.length - 1]; const key = subtype.length > 1 ? subtype[0] : - i.iterator.type === "iterable" ? "number" : value; + i.iterator.kind === "iterable" ? "number" : value; const name = i.name.replace(/ extends \w+/, ""); printer.printLine(`forEach(callbackfn: (value: ${value}, key: ${key}, parent: ${name}) => void, thisArg?: any): void;`); } @@ -1102,10 +1082,10 @@ export function emitWebIDl(webidl: Browser.WebIdl, flavor: Flavor) { function getIteratorSubtypes() { if (i.iterator) { - if (i.iterator.subtype.length === 1) { - return [convertDomTypeToTsType(i.iterator.subtype[0])]; + if (i.iterator.type.length === 1) { + return [convertDomTypeToTsType(i.iterator.type[0])]; } - return i.iterator.subtype.map(convertDomTypeToTsType); + return i.iterator.type.map(convertDomTypeToTsType); } else if (i.name !== "Window" && i.properties) { const iterableGetter = findIterableGetter(); @@ -1138,8 +1118,8 @@ export function emitWebIDl(webidl: Browser.WebIdl, flavor: Flavor) { if (!iterator) { return ""; } - const base = iterator.type === "maplike" ? `Map<${subtypes[0]}, ${subtypes[1]}>` : - iterator.type === "setlike" ? `Set<${subtypes[0]}>` : undefined; + const base = iterator.kind === "maplike" ? `Map<${subtypes[0]}, ${subtypes[1]}>` : + iterator.kind === "setlike" ? `Set<${subtypes[0]}>` : undefined; if (!base) { return ""; } @@ -1156,7 +1136,7 @@ export function emitWebIDl(webidl: Browser.WebIdl, flavor: Flavor) { if (!iteratorExtends) { printer.printLine(`[Symbol.iterator](): IterableIterator<${stringifySingleOrTupleTypes(subtypes)}>`); } - if (i.iterator && i.iterator.type === "iterable") { + if (i.iterator && i.iterator.kind === "iterable") { emitIterableDeclarationMethods(subtypes); } printer.decreaseIndent(); diff --git a/src/helpers.ts b/src/helpers.ts index c572bedb9..ba33218cd 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -1,5 +1,25 @@ import * as Browser from "./types"; +// Extended types used but not defined in the spec +export const bufferSourceTypes = new Set(["ArrayBuffer", "ArrayBufferView", "DataView", "Int8Array", "Uint8Array", "Int16Array", "Uint16Array", "Uint8ClampedArray", "Int32Array", "Uint32Array", "Float32Array", "Float64Array"]); +export const integerTypes = new Set(["byte", "octet", "short", "unsigned short", "long", "unsigned long", "long long", "unsigned long long"]); +export const stringTypes = new Set(["ByteString", "DOMString", "USVString"]); +const floatTypes = new Set(["float", "unrestricted float", "double", "unrestricted double"]); +const sameTypes = new Set(["any", "boolean", "Date", "Function", "Promise", "void"]); +export const baseTypeConversionMap = new Map([ + ...[...bufferSourceTypes].map(type => [type, type] as [string, string]), + ...[...integerTypes].map(type => [type, "number"] as [string, string]), + ...[...floatTypes].map(type => [type, "number"] as [string, string]), + ...[...stringTypes].map(type => [type, "string"] as [string, string]), + ...[...sameTypes].map(type => [type, type] as [string, string]), + ["object", "any"], + ["sequence", "Array"], + ["record", "Record"], + ["FrozenArray", "ReadonlyArray"], + ["WindowProxy", "Window"], + ["EventHandler", "EventHandler"] +]); + export function filter(obj: any, fn: (o: any, n: string | undefined) => boolean): any { if (typeof obj === "object") { if (Array.isArray(obj)) { @@ -194,3 +214,64 @@ export function resolveExposure(obj: any, exposure: string, override?: boolean) } } } + +function collectTypeReferences(obj: any): string[] { + const collection: string[] = []; + if (typeof obj !== "object") { + return collection; + } + if (Array.isArray(obj)) { + return collection.concat(...obj.map(collectTypeReferences)); + } + + if (typeof obj.type === "string") { + collection.push(obj.type); + } + if (Array.isArray(obj.implements)) { + collection.push(...obj.implements); + } + if (typeof obj.extends === "string") { + collection.push(obj.extends); + } + + for (const e in obj) { + collection.push(...collectTypeReferences(obj[e])); + } + return collection; +} + +function getNonValueTypeMap(webidl: Browser.WebIdl) { + const namedTypes: { name: string }[] = [ + ...mapToArray(webidl["callback-functions"]!["callback-function"]), + ...mapToArray(webidl["callback-interfaces"]!.interface), + ...mapToArray(webidl.dictionaries!.dictionary), + ...mapToArray(webidl.enums!.enum), + ...mapToArray(webidl.mixins!.mixin) + ]; + const map = new Map(namedTypes.map(t => [t.name, t] as [string, any])); + webidl.typedefs!.typedef.map(typedef => map.set(typedef["new-type"], typedef)); + return map; +} + +export function followTypeReferences(webidl: Browser.WebIdl, filteredInterfaces: Record) { + const set = new Set(); + const map = getNonValueTypeMap(webidl); + + new Set(collectTypeReferences(filteredInterfaces)).forEach(follow); + return set; + + function follow(reference: string) { + if (baseTypeConversionMap.has(reference) || + reference in filteredInterfaces) { + return; + } + const type = map.get(reference); + if (!type) { + return; + } + if (!set.has(type.name || type["new-type"])) { + set.add(type.name || type["new-type"]); + collectTypeReferences(type).forEach(follow); + } + } +} diff --git a/src/index.ts b/src/index.ts index f30b5d35a..ed9859c28 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,12 +1,16 @@ import * as Browser from "./types"; import * as fs from "fs"; import * as path from "path"; -import { filter, merge, filterProperties, exposesTo, getEmptyWebIDL, resolveExposure } from "./helpers"; +import { filter, merge, filterProperties, exposesTo, getEmptyWebIDL, resolveExposure, followTypeReferences } from "./helpers"; import { Flavor, emitWebIDl } from "./emitter"; import { convert } from "./widlprocess"; -function emitDomWorker(webidl: Browser.WebIdl, knownWorkerTypes: Set, tsWorkerOutput: string) { +function emitDomWorker(webidl: Browser.WebIdl, forceKnownWorkerTypes: Set, tsWorkerOutput: string) { const worker = getEmptyWebIDL(); + if (webidl.interfaces) worker.interfaces!.interface = filter(webidl.interfaces.interface, o => exposesTo(o, "Worker")); + + const knownWorkerTypes = followTypeReferences(webidl, worker.interfaces!.interface); + forceKnownWorkerTypes.forEach(t => knownWorkerTypes.add(t)); const isKnownWorkerName = (o: { name: string }) => knownWorkerTypes.has(o.name); if (webidl["callback-functions"]) worker["callback-functions"]!["callback-function"] = filterProperties(webidl["callback-functions"]!["callback-function"], isKnownWorkerName); @@ -14,7 +18,6 @@ function emitDomWorker(webidl: Browser.WebIdl, knownWorkerTypes: Set, ts if (webidl.dictionaries) worker.dictionaries!.dictionary = filterProperties(webidl.dictionaries.dictionary, isKnownWorkerName); if (webidl.enums) worker.enums!.enum = filterProperties(webidl.enums.enum, isKnownWorkerName); if (webidl.mixins) worker.mixins!.mixin = filterProperties(webidl.mixins.mixin, isKnownWorkerName); - if (webidl.interfaces) worker.interfaces!.interface = filter(webidl.interfaces.interface, o => exposesTo(o, "Worker")); if (webidl.typedefs) worker.typedefs!.typedef = webidl.typedefs.typedef.filter(t => knownWorkerTypes.has(t["new-type"])); const result = emitWebIDl(worker, Flavor.Worker); diff --git a/src/types.d.ts b/src/types.d.ts index e0b74f006..7519e3e99 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -178,9 +178,9 @@ export interface Interface { } export interface Iterator { - type: "iterable" | "setlike" | "maplike"; + kind: "iterable" | "setlike" | "maplike"; readonly: boolean; - subtype: Typed[]; + type: Typed[]; } export interface Enum { diff --git a/src/widlprocess.ts b/src/widlprocess.ts index 48ee0966f..cb51a4759 100644 --- a/src/widlprocess.ts +++ b/src/widlprocess.ts @@ -118,9 +118,9 @@ function convertInterfaceCommon(i: webidl2.InterfaceType | webidl2.InterfaceMixi } else if (member.type === "iterable" || member.type === "maplike" || member.type === "setlike") { result.iterator = { - type: member.type, + kind: member.type, readonly: member.readonly, - subtype: member.idlType.map(convertIdlType) + type: member.idlType.map(convertIdlType) }; } }