Skip to content

Commit de5d553

Browse files
committed
Fixes for compatibility with Embedded Swift
The change improves support for Embedded Swift mode for broader set of APIs in the WebAPIKit library. Specifically: 1. `_Concurrency` module has to be imported explicitly for Embedded Swift 2. Fixed missing parentheses in the `canImport` statement, which led to incorrect conditional compilation checks; 3. Added support for missing WebIDL modules; 4. Bumped package manifest to Swift 6.1, since older versions of Swift aren't supported; 6. Bumped deployment target OS version requirement to align it with the JavaScriptKit dependency; 7. Bumped JavaScriptKit dependency to the latest version that contains necessary fixes for Embedded Swift; 8. Enabled compatibility with Swift 5 language mode 9. Using typed throws and correct integer width in relevant APIs as required for Embedded Swift. 10. Fixes closure wrappers for code that utilizes callbacks.
1 parent 51d7fc7 commit de5d553

File tree

5 files changed

+45
-37
lines changed

5 files changed

+45
-37
lines changed

Sources/WebIDLToSwift/IDLBuilder.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import WebIDL
33

44
enum IDLBuilder {
55
static let basicDependencies = ["ECMAScript", "JavaScriptKit"]
6-
static let optionalDependencies = ["JavaScriptEventLoop"]
6+
static let optionalDependencies = ["JavaScriptEventLoop", "_Concurrency"]
77

88
static let preamble = """
99
// This file was auto-generated by WebIDLToSwift. DO NOT EDIT!
@@ -44,7 +44,7 @@ enum IDLBuilder {
4444
}
4545

4646
let formedPreamble = preamble + (optionalDependencies.map { """
47-
#if canImport\($0)
47+
#if canImport(\($0))
4848
import \($0)
4949
#endif
5050
""" } + dependencies.map { "import \($0)" }).joined(separator: "\n")

Sources/WebIDLToSwift/Module.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ let domModule = Module(
2727
"css-pseudo",
2828
"geometry",
2929
"cssom-view",
30+
"css-view-transitions",
3031
"hr-time",
3132
"FileAPI",
3233
"xhr",
@@ -42,6 +43,8 @@ let domModule = Module(
4243
"performance-timeline",
4344
"permissions",
4445
"mathml-core",
46+
"trusted-types",
47+
"urlpattern",
4548
],
4649
dependencies: ["WebAPIBase"]
4750
)

Sources/WebIDLToSwift/PackageManifest.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
// swiftlint:disable function_body_length
22
func generateManifest(_ modules: [Module]) -> String {
33
#"""
4-
// swift-tools-version:5.5
4+
// swift-tools-version: 6.1
55
// The swift-tools-version declares the minimum version of Swift required to build this package.
66
77
import PackageDescription
88
99
let package = Package(
1010
name: "WebAPIKit",
11-
platforms: [.macOS(.v10_13)],
11+
platforms: [.macOS(.v10_15)],
1212
products: [
1313
.executable(
1414
name: "WebAPIKitDemo",
@@ -29,7 +29,7 @@ func generateManifest(_ modules: [Module]) -> String {
2929
dependencies: [
3030
.package(
3131
url: "https://github.com/swiftwasm/JavaScriptKit.git",
32-
.upToNextMajor(from: "0.16.0")
32+
.upToNextMajor(from: "0.29.0")
3333
),
3434
],
3535
targets: [
@@ -62,7 +62,8 @@ func generateManifest(_ modules: [Module]) -> String {
6262
name: "WebAPIKitTests",
6363
dependencies: ["DOM"]
6464
),
65-
]
65+
],
66+
swiftLanguageModes: [.v5]
6667
)
6768
"""#
6869
}

Sources/WebIDLToSwift/Shell.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Foundation
33
enum Shell {
44
static func format(source: String) {
55
print("Formatting generated Swift files...")
6-
run(executable: "swiftformat", arguments: ["--swiftversion", "5.5", source])
6+
run(executable: "swift", arguments: ["format", "format", "--parallel", "--in-place", source])
77
}
88

99
private static let projectRoot = URL(fileURLWithPath: #file)

Sources/WebIDLToSwift/WebIDL+SwiftRepresentation.swift

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,11 @@ extension IDLAttribute: SwiftRepresentable {
5959
"""
6060
}
6161
}
62-
63-
var initializer: SwiftSource? {
64-
assert(!ModuleState.static)
65-
return """
66-
\(wrapperName) = \(idlType.propertyWrapper(readonly: readonly))(jsObject: jsObject, name: \(ModuleState.source(for: name)))
67-
"""
68-
}
6962
}
7063

7164
extension IDLDictionary.Member {
7265
var isOptional: Bool {
73-
!required && !idlType.nullable && !idlType.isFunction
66+
!required && !idlType.nullable
7467
}
7568

7669
var optionalSuffix: String {
@@ -263,7 +256,7 @@ extension MergedInterface: SwiftRepresentable {
263256
\(hasAsyncSequence ?
264257
"""
265258
#if canImport(JavaScriptEventLoop)
266-
public extension \(name): AsyncSequence {}
259+
extension \(name): AsyncSequence {}
267260
#endif
268261
""" :
269262
""
@@ -427,7 +420,7 @@ extension IDLOperation: SwiftRepresentable, Initializable {
427420
case "stringifier":
428421
return """
429422
@inlinable public var description: String {
430-
\(ModuleState.this)[Strings.toString]!().fromJSValue()!
423+
\(ModuleState.this)[Strings.toString].function!().fromJSValue()!
431424
}
432425
"""
433426
case "static":
@@ -505,18 +498,26 @@ extension IDLOperation: SwiftRepresentable, Initializable {
505498

506499
fileprivate var nameAndParams: SwiftSource {
507500
let accessModifier: SwiftSource = ModuleState.static ? (ModuleState.inClass ? " class" : " static") : ""
508-
let overrideModifier: SwiftSource = ModuleState.override ? "override " : ""
501+
let overrideModifier: SwiftSource = if ModuleState.static && ModuleState.inClass {
502+
ModuleState.override ? "override " : ""
503+
} else {
504+
ModuleState.inClass && !ModuleState.current.inProtocol ? "final " : ""
505+
}
509506
return """
510507
\(overrideModifier)public\(accessModifier) func \(name)(\(sequence: arguments.map(\.swiftRepresentation)))
511508
"""
512509
}
513510

514511
private var defaultRepresentation: SwiftSource {
515512
guard let idlType = idlType else { fatalError() }
516-
var returnType = idlType.swiftRepresentation
517-
if returnType == ModuleState.className {
518-
returnType = "Self"
513+
514+
// skip overrides, since ancestor functions are final and JS will do dynamic dispatch to overrides anyway
515+
// FIXME: still emit overrides that have a different number of arguments than an ancestor method, just without `override` keyword
516+
guard !ModuleState.override || ModuleState.static else {
517+
return ""
519518
}
519+
520+
let returnType = idlType.swiftRepresentation
520521
if ModuleState.override, ModuleState.static, !ModuleState.inClass {
521522
preconditionFailure("Cannot override static method in non-class")
522523
}
@@ -572,12 +573,14 @@ extension AsyncOperation: SwiftRepresentable, Initializable {
572573
result = "return try await _promise.value\(returnType.fromJSValue)"
573574
}
574575
return """
576+
#if canImport(JavaScriptEventLoop)
575577
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
576-
@inlinable \(operation.nameAndParams) async throws -> \(returnType) {
578+
@inlinable \(operation.nameAndParams) async throws(JSException) -> \(returnType) {
577579
\(prep)
578580
let _promise: JSPromise = \(call).fromJSValue()!
579581
\(result)
580582
}
583+
#endif
581584
"""
582585
}
583586

@@ -673,7 +676,7 @@ extension IDLType: SwiftRepresentable {
673676
case "Promise":
674677
return "JSPromise"
675678
case "record":
676-
return "[\(args[0]): \(args[1])]"
679+
return "JSObject"
677680
default:
678681
fatalError("Unsupported generic type: \(name)")
679682
}
@@ -712,39 +715,38 @@ extension IDLType: SwiftRepresentable {
712715
return false
713716
}
714717

715-
func propertyWrapper(readonly: Bool) -> SwiftSource {
716-
// TODO: handle readonly closure properties
717-
// (should they be a JSFunction? or a closure? or something else?))
718+
var closurePattern: ClosurePattern? {
718719
if case let .single(name) = value {
719720
if let callback = ModuleState.types[name] as? IDLCallback {
720-
precondition(!readonly, "readonly closure properties are not supported")
721-
return "\(closureWrapper(callback, optional: false))"
721+
return closureWrapper(callback, optional: false)
722722
}
723723
if let ref = ModuleState.types[name] as? IDLTypedef,
724724
case let .single(name) = ref.idlType.value,
725725
let callback = ModuleState.types[name] as? IDLCallback
726726
{
727-
precondition(!readonly, "readonly closure properties are not supported")
728727
precondition(ref.idlType.nullable)
729-
return "\(closureWrapper(callback, optional: true))"
728+
return closureWrapper(callback, optional: true)
730729
}
731730
}
732731

733-
if readonly {
734-
return .readOnlyAttribute
735-
} else {
736-
return .readWriteAttribute
737-
}
732+
return nil
738733
}
739734

740-
private func closureWrapper(_ callback: IDLCallback, optional: Bool) -> SwiftSource {
735+
private func closureWrapper(_ callback: IDLCallback, optional: Bool) -> ClosurePattern {
741736
let returnsVoid = callback.idlType.swiftRepresentation == "Void"
742737
let argCount = callback.arguments.count
743-
return ClosurePattern(nullable: optional, void: returnsVoid, argCount: argCount).name
738+
return ClosurePattern(nullable: optional, void: returnsVoid, argCount: argCount)
744739
}
745740
}
746741

747742
extension IDLTypedef: SwiftRepresentable {
743+
static let typeNameMap: [String: SwiftSource] = [
744+
// FIXME: WebIDL specifies these as `unsigned long long`, but major browsers expect
745+
// a JS number and not BigInt here, so we have to keep it `Int32` on the Swift side.
746+
"GLintptr": "Int32",
747+
"GPUSize64": "UInt32",
748+
]
749+
748750
var unionType: UnionType? {
749751
if case let .union(types) = idlType.value {
750752
return ModuleState.union(for: Set(types.map(SlimIDLType.init)), defaultName: name)
@@ -753,6 +755,8 @@ extension IDLTypedef: SwiftRepresentable {
753755
}
754756

755757
var swiftRepresentation: SwiftSource {
758+
let aliasedType: SwiftSource
759+
756760
if let unionType = unionType {
757761
guard unionType.friendlyName != name else { return "" }
758762
if let existingName = unionType.friendlyName {

0 commit comments

Comments
 (0)