Skip to content

Commit

Permalink
Bring code generation into this repo, update to latest specs, support…
Browse files Browse the repository at this point in the history
… async functions natively (#10)

I rebuilt the code generation using the official (JS-based) WebIDL parser. Taking inspiration from `webidl2swift`, this will hopefully improve on that package by not having to maintain the parser component. I also:

- Added `async` versions of Promise-returning functions
- Added enormous numbers of Web APIs that were previously not included
  - both existing and new APIs! The full list of included specs is in `IDLBuilder`, and it would be great to expand that.
- Expanded support for wrapped closure properties (using code generation for now, until we get variadic generics)
- Moved the code generation pipeline into this repo, to make it easier to make changes

TODOs:
- [x] Add back support for union types
  - This will probably happen by calculating the Cartesian product of all union-typed function parameters, to avoid unnecessary user-side ceremony
  - Union return types (if present) will still have to be enums, I think, though.
- [x] Add back support for type-erased protocol types (`AnyParentNode`)
  - [ ] mark parameters as taking the protocol rather than the enum
- [x] Rewrite dictionaries to not subclass `JSObject` because that is bad
- [ ] Do something about the fact that JS can run in different contexts (e.g. window-side code vs the plethora of worker types)
- [x] Implement iterators and async iterators properly
  - this requires upstream changes to JavaScriptKit that I will be tackling later to enable usage of Symbol keys
- [ ] add support for readonly closure properties
- [ ] re-evaluate entries in the `ignored` dictionary in `IDLBuilder`, and make as many APIs as possible work (or manually re-implement them)
- [ ] Add and error-check remaining web APIs
- [ ] test with real code
- [ ] configure GitHub Actions to periodically update the generated code
  • Loading branch information
j-f1 authored May 2, 2022
1 parent e45d37f commit 45ed26d
Show file tree
Hide file tree
Showing 505 changed files with 20,192 additions and 4,757 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ on:

jobs:
macos_test:
runs-on: macos-11.0
runs-on: macos-12

steps:
- uses: actions/checkout@v2
- name: Run the test suite on macOS
shell: bash
run: |
set -ex
sudo xcode-select --switch /Applications/Xcode_12.3.app/Contents/Developer/
sudo xcode-select --switch /Applications/Xcode_13.3.1.app/Contents/Developer/
brew install swiftwasm/tap/carton
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
/Packages
/*.xcodeproj
xcuserdata/
node_modules
6 changes: 3 additions & 3 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
"package": "JavaScriptKit",
"repositoryURL": "https://github.com/swiftwasm/JavaScriptKit.git",
"state": {
"branch": null,
"revision": "b7a02434c6e973c08c3fd5069105ef44dd82b891",
"version": "0.9.0"
"branch": "main",
"revision": "95d0c4cd78b48ffc7e19c618d57c3244917be09a",
"version": null
}
}
]
Expand Down
14 changes: 9 additions & 5 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,25 @@ let package = Package(
.library(
name: "DOMKit",
targets: ["DOMKit"]),
.library(name: "WebIDL", targets: ["WebIDL"]),
.executable(name: "WebIDLToSwift", targets: ["WebIDLToSwift"]),
],
dependencies: [
.package(
name: "JavaScriptKit",
url: "https://github.com/swiftwasm/JavaScriptKit.git",
.upToNextMinor(from: "0.9.0")),
.branch("main")),
],
targets: [
.target(
name: "DOMKitDemo",
dependencies: ["DOMKit"]
),
dependencies: ["DOMKit"]),
.target(
name: "DOMKit",
dependencies: ["JavaScriptKit"]),
dependencies: ["JavaScriptKit", .product(name: "JavaScriptEventLoop", package: "JavaScriptKit")]),
.target(name: "WebIDL"),
.target(
name: "WebIDLToSwift",
dependencies: ["WebIDL"]),
.testTarget(
name: "DOMKitTests",
dependencies: ["DOMKit"]),
Expand Down
1 change: 0 additions & 1 deletion Sources/DOMKit/ECMAScript/ArrayBuffer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ public typealias Int32Array = JSTypedArray<Int32>
public typealias Uint8Array = JSTypedArray<UInt8>
public typealias Uint16Array = JSTypedArray<UInt16>
public typealias Uint32Array = JSTypedArray<UInt32>
//public typealias Uint8ClampedArray = JSTypedArray<Uint8Clamped>
public typealias Float32Array = JSTypedArray<Float32>
public typealias Float64Array = JSTypedArray<Float64>

Expand Down
93 changes: 93 additions & 0 deletions Sources/DOMKit/ECMAScript/ArrayBufferView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// This file was auto-generated by WebIDLToSwift. DO NOT EDIT!

import JavaScriptEventLoop
import JavaScriptKit

// TODO: expand this to BigInt arrays
public protocol AnyArrayBufferView: ConvertibleToJSValue {}
extension DataView: AnyArrayBufferView {}
extension JSTypedArray: AnyArrayBufferView {}

public enum ArrayBufferView: JSValueCompatible, AnyArrayBufferView {
case dataView(DataView)
// case bigInt64Array(BigInt64Array)
// case bigUint64Array(BigUint64Array)
case float32Array(Float32Array)
case float64Array(Float64Array)
case int16Array(Int16Array)
case int32Array(Int32Array)
case int8Array(Int8Array)
case uint16Array(Uint16Array)
case uint32Array(Uint32Array)
case uint8Array(Uint8Array)
case uint8ClampedArray(Uint8ClampedArray)

public static func construct(from value: JSValue) -> Self? {
// if let bigInt64Array: BigInt64Array = value.fromJSValue() {
// return .bigInt64Array(bigInt64Array)
// }
// if let bigUint64Array: BigUint64Array = value.fromJSValue() {
// return .bigUint64Array(bigUint64Array)
// }
if let dataView: DataView = value.fromJSValue() {
return .dataView(dataView)
}
if let float32Array: Float32Array = value.fromJSValue() {
return .float32Array(float32Array)
}
if let float64Array: Float64Array = value.fromJSValue() {
return .float64Array(float64Array)
}
if let int16Array: Int16Array = value.fromJSValue() {
return .int16Array(int16Array)
}
if let int32Array: Int32Array = value.fromJSValue() {
return .int32Array(int32Array)
}
if let int8Array: Int8Array = value.fromJSValue() {
return .int8Array(int8Array)
}
if let uint16Array: Uint16Array = value.fromJSValue() {
return .uint16Array(uint16Array)
}
if let uint32Array: Uint32Array = value.fromJSValue() {
return .uint32Array(uint32Array)
}
if let uint8Array: Uint8Array = value.fromJSValue() {
return .uint8Array(uint8Array)
}
if let uint8ClampedArray: Uint8ClampedArray = value.fromJSValue() {
return .uint8ClampedArray(uint8ClampedArray)
}
return nil
}

public var jsValue: JSValue {
switch self {
// case let .bigInt64Array(bigInt64Array):
// return bigInt64Array.jsValue
// case let .bigUint64Array(bigUint64Array):
// return bigUint64Array.jsValue
case let .dataView(dataView):
return dataView.jsValue
case let .float32Array(float32Array):
return float32Array.jsValue
case let .float64Array(float64Array):
return float64Array.jsValue
case let .int16Array(int16Array):
return int16Array.jsValue
case let .int32Array(int32Array):
return int32Array.jsValue
case let .int8Array(int8Array):
return int8Array.jsValue
case let .uint16Array(uint16Array):
return uint16Array.jsValue
case let .uint32Array(uint32Array):
return uint32Array.jsValue
case let .uint8Array(uint8Array):
return uint8Array.jsValue
case let .uint8ClampedArray(uint8ClampedArray):
return uint8ClampedArray.jsValue
}
}
}
39 changes: 39 additions & 0 deletions Sources/DOMKit/ECMAScript/Attributes.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import JavaScriptKit

@propertyWrapper public struct ReadWriteAttribute<Wrapped: JSValueCompatible> {
@usableFromInline let jsObject: JSObject
@usableFromInline let name: JSString

public init(jsObject: JSObject, name: JSString) {
self.jsObject = jsObject
self.name = name
}

@inlinable public var wrappedValue: Wrapped {
get { ReadWriteAttribute[name, in: jsObject] }
nonmutating set { ReadWriteAttribute[name, in: jsObject] = newValue }
}

@inlinable public static subscript(name: JSString, in jsObject: JSObject) -> Wrapped {
get { jsObject[name].fromJSValue()! }
set { jsObject[name] = newValue.jsValue }
}
}

@propertyWrapper public struct ReadonlyAttribute<Wrapped: ConstructibleFromJSValue> {
@usableFromInline let jsObject: JSObject
@usableFromInline let name: JSString

public init(jsObject: JSObject, name: JSString) {
self.jsObject = jsObject
self.name = name
}

@inlinable public var wrappedValue: Wrapped {
ReadonlyAttribute[name, in: jsObject]
}

@inlinable public static subscript(name: JSString, in jsObject: JSObject) -> Wrapped {
jsObject[name].fromJSValue()!
}
}
24 changes: 24 additions & 0 deletions Sources/DOMKit/ECMAScript/BridgedDictionary.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import JavaScriptKit

public class BridgedDictionary: JSValueCompatible {
public let jsObject: JSObject

public var jsValue: JSValue {
jsObject.jsValue
}

public required init(unsafelyWrapping jsObject: JSObject) {
self.jsObject = jsObject
}

public static func construct(from value: JSValue) -> Self? {
if let object = value.object {
return Self.construct(from: object)
}
return nil
}

public static func construct(from object: JSObject) -> Self? {
Self(unsafelyWrapping: object)
}
}
102 changes: 102 additions & 0 deletions Sources/DOMKit/ECMAScript/CanvasImageSource.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// This file was auto-generated by WebIDLToSwift. DO NOT EDIT!

import JavaScriptEventLoop
import JavaScriptKit

public protocol Any_CanvasImageSource: ConvertibleToJSValue {}
extension HTMLCanvasElement: Any_CanvasImageSource {}
extension HTMLOrSVGImageElement: Any_CanvasImageSource {}
extension HTMLVideoElement: Any_CanvasImageSource {}
extension ImageBitmap: Any_CanvasImageSource {}
extension OffscreenCanvas: Any_CanvasImageSource {}
//extension VideoFrame: Any_CanvasImageSource {}

public enum CanvasImageSource: JSValueCompatible, Any_CanvasImageSource {
case htmlCanvasElement(HTMLCanvasElement)
case htmlOrSVGImageElement(HTMLOrSVGImageElement)
case htmlVideoElement(HTMLVideoElement)
case imageBitmap(ImageBitmap)
case offscreenCanvas(OffscreenCanvas)
// case videoFrame(VideoFrame)

var htmlCanvasElement: HTMLCanvasElement? {
switch self {
case let .htmlCanvasElement(htmlCanvasElement): return htmlCanvasElement
default: return nil
}
}

var htmlOrSVGImageElement: HTMLOrSVGImageElement? {
switch self {
case let .htmlOrSVGImageElement(htmlOrSVGImageElement): return htmlOrSVGImageElement
default: return nil
}
}

var htmlVideoElement: HTMLVideoElement? {
switch self {
case let .htmlVideoElement(htmlVideoElement): return htmlVideoElement
default: return nil
}
}

var imageBitmap: ImageBitmap? {
switch self {
case let .imageBitmap(imageBitmap): return imageBitmap
default: return nil
}
}

var offscreenCanvas: OffscreenCanvas? {
switch self {
case let .offscreenCanvas(offscreenCanvas): return offscreenCanvas
default: return nil
}
}

// var videoFrame: VideoFrame? {
// switch self {
// case let .videoFrame(videoFrame): return videoFrame
// default: return nil
// }
// }

public static func construct(from value: JSValue) -> Self? {
if let htmlCanvasElement: HTMLCanvasElement = value.fromJSValue() {
return .htmlCanvasElement(htmlCanvasElement)
}
if let htmlOrSVGImageElement: HTMLOrSVGImageElement = value.fromJSValue() {
return .htmlOrSVGImageElement(htmlOrSVGImageElement)
}
if let htmlVideoElement: HTMLVideoElement = value.fromJSValue() {
return .htmlVideoElement(htmlVideoElement)
}
if let imageBitmap: ImageBitmap = value.fromJSValue() {
return .imageBitmap(imageBitmap)
}
if let offscreenCanvas: OffscreenCanvas = value.fromJSValue() {
return .offscreenCanvas(offscreenCanvas)
}
// if let videoFrame: VideoFrame = value.fromJSValue() {
// return .videoFrame(videoFrame)
// }
return nil
}

public var jsValue: JSValue {
switch self {
case let .htmlCanvasElement(htmlCanvasElement):
return htmlCanvasElement.jsValue
case let .htmlOrSVGImageElement(htmlOrSVGImageElement):
return htmlOrSVGImageElement.jsValue
case let .htmlVideoElement(htmlVideoElement):
return htmlVideoElement.jsValue
case let .imageBitmap(imageBitmap):
return imageBitmap.jsValue
case let .offscreenCanvas(offscreenCanvas):
return offscreenCanvas.jsValue
// case let .videoFrame(videoFrame):
// return videoFrame.jsValue
}
}
}
Loading

0 comments on commit 45ed26d

Please sign in to comment.