From de5d553b34f5cbf0496231dfb39e3b14cc709ed7 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 5 Jun 2025 19:18:51 +0100 Subject: [PATCH 1/5] 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. --- Sources/WebIDLToSwift/IDLBuilder.swift | 4 +- Sources/WebIDLToSwift/Module.swift | 3 + Sources/WebIDLToSwift/PackageManifest.swift | 9 +-- Sources/WebIDLToSwift/Shell.swift | 2 +- .../WebIDL+SwiftRepresentation.swift | 64 ++++++++++--------- 5 files changed, 45 insertions(+), 37 deletions(-) diff --git a/Sources/WebIDLToSwift/IDLBuilder.swift b/Sources/WebIDLToSwift/IDLBuilder.swift index 9cb8ac64..ae82c44e 100644 --- a/Sources/WebIDLToSwift/IDLBuilder.swift +++ b/Sources/WebIDLToSwift/IDLBuilder.swift @@ -3,7 +3,7 @@ import WebIDL enum IDLBuilder { static let basicDependencies = ["ECMAScript", "JavaScriptKit"] - static let optionalDependencies = ["JavaScriptEventLoop"] + static let optionalDependencies = ["JavaScriptEventLoop", "_Concurrency"] static let preamble = """ // This file was auto-generated by WebIDLToSwift. DO NOT EDIT! @@ -44,7 +44,7 @@ enum IDLBuilder { } let formedPreamble = preamble + (optionalDependencies.map { """ - #if canImport\($0) + #if canImport(\($0)) import \($0) #endif """ } + dependencies.map { "import \($0)" }).joined(separator: "\n") diff --git a/Sources/WebIDLToSwift/Module.swift b/Sources/WebIDLToSwift/Module.swift index 5c15aecf..61c30890 100644 --- a/Sources/WebIDLToSwift/Module.swift +++ b/Sources/WebIDLToSwift/Module.swift @@ -27,6 +27,7 @@ let domModule = Module( "css-pseudo", "geometry", "cssom-view", + "css-view-transitions", "hr-time", "FileAPI", "xhr", @@ -42,6 +43,8 @@ let domModule = Module( "performance-timeline", "permissions", "mathml-core", + "trusted-types", + "urlpattern", ], dependencies: ["WebAPIBase"] ) diff --git a/Sources/WebIDLToSwift/PackageManifest.swift b/Sources/WebIDLToSwift/PackageManifest.swift index c8b8ed2d..1ff48470 100644 --- a/Sources/WebIDLToSwift/PackageManifest.swift +++ b/Sources/WebIDLToSwift/PackageManifest.swift @@ -1,14 +1,14 @@ // swiftlint:disable function_body_length func generateManifest(_ modules: [Module]) -> String { #""" - // swift-tools-version:5.5 + // swift-tools-version: 6.1 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "WebAPIKit", - platforms: [.macOS(.v10_13)], + platforms: [.macOS(.v10_15)], products: [ .executable( name: "WebAPIKitDemo", @@ -29,7 +29,7 @@ func generateManifest(_ modules: [Module]) -> String { dependencies: [ .package( url: "https://github.com/swiftwasm/JavaScriptKit.git", - .upToNextMajor(from: "0.16.0") + .upToNextMajor(from: "0.29.0") ), ], targets: [ @@ -62,7 +62,8 @@ func generateManifest(_ modules: [Module]) -> String { name: "WebAPIKitTests", dependencies: ["DOM"] ), - ] + ], + swiftLanguageModes: [.v5] ) """# } diff --git a/Sources/WebIDLToSwift/Shell.swift b/Sources/WebIDLToSwift/Shell.swift index eb7891aa..fcaf28e5 100644 --- a/Sources/WebIDLToSwift/Shell.swift +++ b/Sources/WebIDLToSwift/Shell.swift @@ -3,7 +3,7 @@ import Foundation enum Shell { static func format(source: String) { print("Formatting generated Swift files...") - run(executable: "swiftformat", arguments: ["--swiftversion", "5.5", source]) + run(executable: "swift", arguments: ["format", "format", "--parallel", "--in-place", source]) } private static let projectRoot = URL(fileURLWithPath: #file) diff --git a/Sources/WebIDLToSwift/WebIDL+SwiftRepresentation.swift b/Sources/WebIDLToSwift/WebIDL+SwiftRepresentation.swift index 9b7e499f..c5ca9df6 100644 --- a/Sources/WebIDLToSwift/WebIDL+SwiftRepresentation.swift +++ b/Sources/WebIDLToSwift/WebIDL+SwiftRepresentation.swift @@ -59,18 +59,11 @@ extension IDLAttribute: SwiftRepresentable { """ } } - - var initializer: SwiftSource? { - assert(!ModuleState.static) - return """ - \(wrapperName) = \(idlType.propertyWrapper(readonly: readonly))(jsObject: jsObject, name: \(ModuleState.source(for: name))) - """ - } } extension IDLDictionary.Member { var isOptional: Bool { - !required && !idlType.nullable && !idlType.isFunction + !required && !idlType.nullable } var optionalSuffix: String { @@ -263,7 +256,7 @@ extension MergedInterface: SwiftRepresentable { \(hasAsyncSequence ? """ #if canImport(JavaScriptEventLoop) - public extension \(name): AsyncSequence {} + extension \(name): AsyncSequence {} #endif """ : "" @@ -427,7 +420,7 @@ extension IDLOperation: SwiftRepresentable, Initializable { case "stringifier": return """ @inlinable public var description: String { - \(ModuleState.this)[Strings.toString]!().fromJSValue()! + \(ModuleState.this)[Strings.toString].function!().fromJSValue()! } """ case "static": @@ -505,7 +498,11 @@ extension IDLOperation: SwiftRepresentable, Initializable { fileprivate var nameAndParams: SwiftSource { let accessModifier: SwiftSource = ModuleState.static ? (ModuleState.inClass ? " class" : " static") : "" - let overrideModifier: SwiftSource = ModuleState.override ? "override " : "" + let overrideModifier: SwiftSource = if ModuleState.static && ModuleState.inClass { + ModuleState.override ? "override " : "" + } else { + ModuleState.inClass && !ModuleState.current.inProtocol ? "final " : "" + } return """ \(overrideModifier)public\(accessModifier) func \(name)(\(sequence: arguments.map(\.swiftRepresentation))) """ @@ -513,10 +510,14 @@ extension IDLOperation: SwiftRepresentable, Initializable { private var defaultRepresentation: SwiftSource { guard let idlType = idlType else { fatalError() } - var returnType = idlType.swiftRepresentation - if returnType == ModuleState.className { - returnType = "Self" + + // skip overrides, since ancestor functions are final and JS will do dynamic dispatch to overrides anyway + // FIXME: still emit overrides that have a different number of arguments than an ancestor method, just without `override` keyword + guard !ModuleState.override || ModuleState.static else { + return "" } + + let returnType = idlType.swiftRepresentation if ModuleState.override, ModuleState.static, !ModuleState.inClass { preconditionFailure("Cannot override static method in non-class") } @@ -572,12 +573,14 @@ extension AsyncOperation: SwiftRepresentable, Initializable { result = "return try await _promise.value\(returnType.fromJSValue)" } return """ + #if canImport(JavaScriptEventLoop) @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) - @inlinable \(operation.nameAndParams) async throws -> \(returnType) { + @inlinable \(operation.nameAndParams) async throws(JSException) -> \(returnType) { \(prep) let _promise: JSPromise = \(call).fromJSValue()! \(result) } + #endif """ } @@ -673,7 +676,7 @@ extension IDLType: SwiftRepresentable { case "Promise": return "JSPromise" case "record": - return "[\(args[0]): \(args[1])]" + return "JSObject" default: fatalError("Unsupported generic type: \(name)") } @@ -712,39 +715,38 @@ extension IDLType: SwiftRepresentable { return false } - func propertyWrapper(readonly: Bool) -> SwiftSource { - // TODO: handle readonly closure properties - // (should they be a JSFunction? or a closure? or something else?)) + var closurePattern: ClosurePattern? { if case let .single(name) = value { if let callback = ModuleState.types[name] as? IDLCallback { - precondition(!readonly, "readonly closure properties are not supported") - return "\(closureWrapper(callback, optional: false))" + return closureWrapper(callback, optional: false) } if let ref = ModuleState.types[name] as? IDLTypedef, case let .single(name) = ref.idlType.value, let callback = ModuleState.types[name] as? IDLCallback { - precondition(!readonly, "readonly closure properties are not supported") precondition(ref.idlType.nullable) - return "\(closureWrapper(callback, optional: true))" + return closureWrapper(callback, optional: true) } } - if readonly { - return .readOnlyAttribute - } else { - return .readWriteAttribute - } + return nil } - private func closureWrapper(_ callback: IDLCallback, optional: Bool) -> SwiftSource { + private func closureWrapper(_ callback: IDLCallback, optional: Bool) -> ClosurePattern { let returnsVoid = callback.idlType.swiftRepresentation == "Void" let argCount = callback.arguments.count - return ClosurePattern(nullable: optional, void: returnsVoid, argCount: argCount).name + return ClosurePattern(nullable: optional, void: returnsVoid, argCount: argCount) } } extension IDLTypedef: SwiftRepresentable { + static let typeNameMap: [String: SwiftSource] = [ + // FIXME: WebIDL specifies these as `unsigned long long`, but major browsers expect + // a JS number and not BigInt here, so we have to keep it `Int32` on the Swift side. + "GLintptr": "Int32", + "GPUSize64": "UInt32", + ] + var unionType: UnionType? { if case let .union(types) = idlType.value { return ModuleState.union(for: Set(types.map(SlimIDLType.init)), defaultName: name) @@ -753,6 +755,8 @@ extension IDLTypedef: SwiftRepresentable { } var swiftRepresentation: SwiftSource { + let aliasedType: SwiftSource + if let unionType = unionType { guard unionType.friendlyName != name else { return "" } if let existingName = unionType.friendlyName { From a533f690a9f322b6c8517bd173de2c5d800cd559 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 5 Jun 2025 19:21:26 +0100 Subject: [PATCH 2/5] More fixes for compatibility with Embedded Swift. The change improves compatibility with Embedded Swift. Specifically: 1. Cleans up accessors for properties to avoid use of property wrappers, which allows binary size to be slightly reduced. 2. Fixes dictionary-like types that previously had incorrect initializers not inheriting properties from superclasses. --- .../WebIDL+SwiftRepresentation.swift | 101 ++++++++++++------ Sources/WebIDLToSwift/WebIDLToSwift.swift | 12 ++- 2 files changed, 74 insertions(+), 39 deletions(-) diff --git a/Sources/WebIDLToSwift/WebIDL+SwiftRepresentation.swift b/Sources/WebIDLToSwift/WebIDL+SwiftRepresentation.swift index c5ca9df6..ee43f203 100644 --- a/Sources/WebIDLToSwift/WebIDL+SwiftRepresentation.swift +++ b/Sources/WebIDLToSwift/WebIDL+SwiftRepresentation.swift @@ -71,59 +71,90 @@ extension IDLDictionary.Member { } } -extension MergedDictionary: SwiftRepresentable { +extension IDLDictionary.Member: SwiftRepresentable { var swiftRepresentation: SwiftSource { - """ - public class \(name): BridgedDictionary { - \(swiftInit) - \(swiftMembers.joined(separator: "\n\n")) + if ModuleState.override { + assert(!ModuleState.static) + return "" + } else { + let stringKey = ModuleState.source(for: name) + let getter: SwiftSource + let setter: SwiftSource + + if let closure = idlType.closurePattern { + getter = "get { \(closure.getter(name: stringKey)) }" + setter = "set { \(closure.setter(name: stringKey)) }" + } else { + getter = "get { jsObject[\(stringKey)]\(idlType.fromJSValue) }" + setter = "set { jsObject[\(stringKey)] = _toJSValue(newValue) }" + } + + return """ + @inlinable public\(raw: ModuleState.static ? " static" : "") var \(name): \(idlType) { + \(getter) + \(setter) + } + """ } - """ } +} - private func membersWithPropertyWrapper(_ members: [IDLDictionary.Member]) -> [(IDLDictionary.Member, SwiftSource)] { - members.map { - ($0, $0.idlType.propertyWrapper(readonly: false)) - } +extension MergedDictionary: SwiftRepresentable { + var swiftRepresentation: SwiftSource { + let inheritanceClause: SwiftSource = + if self.inheritance.isEmpty { + ": JSDictionaryCompatible" + } else { + ": \(self.inheritance.joined(separator: ", "))" + } + + return """ + open class \(name)\(inheritanceClause) { + \(self.inheritance.isEmpty ? "public let jsObject: JSObject" : "") + + \(swiftInit) + + \(lines: self.members.map(\.swiftRepresentation)) + } + """ } - private var swiftInit: SwiftSource { - let params: [SwiftSource] = members.map { - "\($0.name): \($0.idlType.isFunction ? "@escaping " : "")\($0.idlType)" + private var convenienceInit: SwiftSource { + let inheritedMembers: [IDLDictionary.Member] = self.inheritance.reduce(into: []) { + // FIXME: if dictionary is not found in the current module, it's coming from a dependency, + // but for now we have to skip it, since `ModuleState` doesn't seem to handle dependencies. + return $0.append(contentsOf: ModuleState.dictionaries[$1]?.members ?? []) + } + + let params: [SwiftSource] = (inheritedMembers + members).map { + let escaping = if let closurePattern = $0.idlType.closurePattern { + closurePattern.nullable || $0.isOptional ? "" : "@escaping " + } else { "" } + return "\($0.name): \(escaping)\($0.idlType)\($0.isOptional ? "? = nil" : "")" } + return """ public convenience init(\(sequence: params)) { let object = JSObject.global[\(ModuleState.source(for: "Object"))].function!.new() - \(lines: membersWithPropertyWrapper(members).map { member, wrapper in - if member.idlType.isFunction { - return """ - \(wrapper)[\(ModuleState.source(for: member.name)), in: object] = \(member.name) - """ - } else { + \(lines: (inheritedMembers + members).map { member in return """ object[\(ModuleState.source(for: member.name))] = _toJSValue(\(member.name)) """ - } }) - self.init(unsafelyWrapping: object) - } - public required init(unsafelyWrapping object: JSObject) { - \(lines: membersWithPropertyWrapper(members).map { member, wrapper in - "_\(raw: member.name) = \(wrapper)(jsObject: object, name: \(ModuleState.source(for: member.name)))" - }) - super.init(unsafelyWrapping: object) + self.init(unsafelyWrapping: object) } """ } - private var swiftMembers: [SwiftSource] { - self.membersWithPropertyWrapper(members).map { member, wrapper in - """ - @\(wrapper) - public var \(member.name): \(member.idlType)\(member.optionalSuffix) + private var swiftInit: SwiftSource { + return """ + \(self.convenienceInit) + + public required init(unsafelyWrapping object: JSObject) { + \(self.inheritance.isEmpty ? "self.jsObject = object" : "super.init(unsafelyWrapping: object)") + } """ - } } } @@ -765,8 +796,8 @@ extension IDLTypedef: SwiftRepresentable { unionType.friendlyName = name return "" } - } - return "public typealias \(name) = \(idlType)" + } else if let type = Self.typeNameMap[name] { aliasedType = type } else { aliasedType = "\(idlType)" } + return "public typealias \(name) = \(aliasedType)" } } diff --git a/Sources/WebIDLToSwift/WebIDLToSwift.swift b/Sources/WebIDLToSwift/WebIDLToSwift.swift index 1e43fc1d..f1524a4c 100644 --- a/Sources/WebIDLToSwift/WebIDLToSwift.swift +++ b/Sources/WebIDLToSwift/WebIDLToSwift.swift @@ -11,7 +11,7 @@ enum WebIDLToSwift { static func main() { do { - let mode = parseArgs() + let (mode, moduleNames) = parseArgs() let packageDir = URL(fileURLWithPath: #file) .deletingLastPathComponent() .deletingLastPathComponent() @@ -22,6 +22,9 @@ enum WebIDLToSwift { // Collect closure patterns from all modules to subsequently write them together with `baseModule`. var closurePatterns = Set() + let modules = if moduleNames.isEmpty { modules } else { + modules.filter { moduleNames.contains($0.swiftModule) } + } for module in modules { try generate(module, packageDir: packageDir, domTypes: domTypes, patch: mode != .noPatch) closurePatterns.formUnion(ModuleState.closurePatterns) @@ -39,8 +42,9 @@ enum WebIDLToSwift { } } - private static func parseArgs() -> Mode? { + private static func parseArgs() -> (Mode?, moduleNames: [String]) { var mode: Mode? + var moduleNames = [String]() for arg in CommandLine.arguments.dropFirst() { if arg.starts(with: "--") { if let parsed = Mode(rawValue: String(arg.dropFirst(2))) { @@ -49,10 +53,10 @@ enum WebIDLToSwift { print("Unknown option: \(arg)") } } else { - print("Unknown argument: \(arg)") + moduleNames.append(arg) } } - return mode + return (mode, moduleNames) } private static func generate( From 6a3c16425d5ea5645702e723934bae2940a27a43 Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 5 Jun 2025 19:23:54 +0100 Subject: [PATCH 3/5] Fix CSSOM.patch for compatibility with Embedded Swift The CSSOM module has different context after previously applied changes for Embedded Swift support. `override` modifiers are not applicable with embedded Swift and need to be replaced with `final`. Similarly `Self` is replaced with explicit class names to avoid use of implicit metatypes currently incompatible with Embedded Swift. Certain initializers also had to become `public` to be usable in library clients. --- Patches/CSSOM.patch | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/Patches/CSSOM.patch b/Patches/CSSOM.patch index ff22788d..c98927bd 100644 --- a/Patches/CSSOM.patch +++ b/Patches/CSSOM.patch @@ -1,41 +1,42 @@ +diff --git a/Sources/CSSOM/Generated.swift b/Sources/CSSOM/Generated.swift +index 9cd2fb3..7aec5a8 100644 --- a/Sources/CSSOM/Generated.swift +++ b/Sources/CSSOM/Generated.swift -@@ -379,7 +379,8 @@ public class CSSColorValue: CSSStyleValue { - super.init(unsafelyWrapping: jsObject) - } +@@ -411,7 +411,7 @@ public class CSSColorValue: CSSStyleValue { + + public required init(unsafelyWrapping jsObject: JSObject) { super.init(unsafelyWrapping: jsObject) } - @inlinable override public class func parse(cssText: String) -> CSSColorValue_or_CSSStyleValue { -+ // returns CSSStyleValue | CSSColorValue -+ @inlinable public class func parse(cssText: String) -> CSSStyleValue { ++ @inlinable public class func parse(cssText: String) -> CSSColorValue { let this = constructor! return this[Strings.parse].function!(this: this, arguments: [_toJSValue(cssText)]).fromJSValue()! } -@@ -944,7 +945,7 @@ public class CSSNumericValue: CSSStyleValue { +@@ -947,7 +947,7 @@ public class CSSNumericValue: CSSStyleValue { return this[Strings.type].function!(this: this, arguments: []).fromJSValue()! } -- @inlinable override public class func parse(cssText: String) -> Self { -+ @inlinable public class func parse(cssText: String) -> Self { +- @inlinable override public class func parse(cssText: String) -> CSSNumericValue { ++ @inlinable public class func parse(cssText: String) -> CSSNumericValue { let this = constructor! return this[Strings.parse].function!(this: this, arguments: [_toJSValue(cssText)]).fromJSValue()! } -@@ -1974,9 +1975,10 @@ public class StylePropertyMapReadOnly: JSBridgedClass, Sequence { +@@ -2072,9 +2072,10 @@ public class StylePropertyMapReadOnly: JSBridgedClass, Sequence { ValueIterableIterator(sequence: self) } -- @inlinable public func get(property: String) -> CSSStyleValue_or_Void { +- @inlinable final public func get(property: String) -> CSSStyleValue_or_Void { + // TODO: remove patch once https://github.com/w3c/css-houdini-drafts/issues/1095 is fixed -+ @inlinable public func get(property: String) -> CSSStyleValue? { ++ @inlinable final public func get(property: String) -> CSSStyleValue? { let this = jsObject - return this[Strings.get].function!(this: this, arguments: [_toJSValue(property)]).fromJSValue()! + return this[Strings.get].function!(this: this, arguments: [_toJSValue(property)]).fromJSValue() } - @inlinable public func getAll(property: String) -> [CSSStyleValue] { -@@ -2646,58 +2648,6 @@ public enum CSSStyleValue_or_String: JSValueCompatible, Any_CSSStyleValue_or_Str + @inlinable final public func getAll(property: String) -> [CSSStyleValue] { +@@ -2706,49 +2707,6 @@ public enum CSSStyleValue_or_String: JSValueCompatible, Any_CSSStyleValue_or_Str + } } } - -public protocol Any_CSSStyleValue_or_Void: ConvertibleToJSValue {} -extension CSSStyleValue: Any_CSSStyleValue_or_Void {} -extension Void: Any_CSSStyleValue_or_Void {} @@ -44,12 +45,11 @@ - case cssStyleValue(CSSStyleValue) - case void(Void) - -- init(_ cssStyleValue: CSSStyleValue) { +- public init(_ cssStyleValue: CSSStyleValue) { - let val: CSSStyleValue_or_Void = .cssStyleValue(cssStyleValue) - self = val - } -- -- init(_ void: Void) { +- public init(_ void: Void) { - let val: CSSStyleValue_or_Void = .void(void) - self = val - } @@ -60,7 +60,6 @@ - default: return nil - } - } -- - public var void: Void? { - switch self { - case let .void(void): return void From 7bb59073f518208842d9bdedee3dba6b9e00524d Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 5 Jun 2025 19:24:50 +0100 Subject: [PATCH 4/5] Fix `DOM.patch` for compatibility with Embedded Swift 1. Use of a property wrapper replaced with direct accessors use; 2. Use of `override` is avoided in computed properties and functions; 3. Initializers made `public` for adoption in library clients --- Patches/DOM.patch | 90 ++++++++++++++--------------------------------- 1 file changed, 26 insertions(+), 64 deletions(-) diff --git a/Patches/DOM.patch b/Patches/DOM.patch index 494dbe46..7aa1ece2 100644 --- a/Patches/DOM.patch +++ b/Patches/DOM.patch @@ -1,90 +1,52 @@ +diff --git a/Sources/DOM/Generated.swift b/Sources/DOM/Generated.swift +index 9fa75ba..0339568 100644 --- a/Sources/DOM/Generated.swift +++ b/Sources/DOM/Generated.swift -@@ -1007,8 +1007,15 @@ public class BeforeUnloadEvent: Event { - super.init(unsafelyWrapping: jsObject) - } +@@ -1094,6 +1094,17 @@ public class BeforeUnloadEvent: Event { + + public required init(unsafelyWrapping jsObject: JSObject) { super.init(unsafelyWrapping: jsObject) } + @available(*, unavailable) + override public var returnValue: Bool { -+ get { !_returnValue.wrappedValue.isEmpty } ++ get { !self.returnValueAsString.isEmpty } + set {} + } + - @usableFromInline let _returnValue: ReadWriteAttribute -- @inlinable override public var returnValue: String { + // renamed because `String` is not compatible with `Bool` + @inlinable public var returnValueAsString: String { - get { _returnValue.wrappedValue } - set { _returnValue.wrappedValue = newValue } - } -@@ -7095,7 +7102,8 @@ public class HTMLFormControlsCollection: HTMLCollection { - jsObject[key].fromJSValue() - } - -- @inlinable override public func namedItem(name: String) -> Element_or_RadioNodeList? { -+ // `override` removed since the superclass returns a more constrained type `Element` -+ @inlinable func namedItem(name: String) -> Element_or_RadioNodeList? { - let this = jsObject - return this[Strings.namedItem].function!(this: this, arguments: [_toJSValue(name)]).fromJSValue() - } -@@ -17153,7 +17161,6 @@ public class VisibilityStateEntry: PerformanceEntry { - _name = ReadonlyAttribute(jsObject: jsObject, name: Strings.name) - _entryType = ReadonlyAttribute(jsObject: jsObject, name: Strings.entryType) - _startTime = ReadonlyAttribute(jsObject: jsObject, name: Strings.startTime) -- _duration = ReadonlyAttribute(jsObject: jsObject, name: Strings.duration) - super.init(unsafelyWrapping: jsObject) - } - -@@ -17166,8 +17173,8 @@ public class VisibilityStateEntry: PerformanceEntry { - @usableFromInline let _startTime: ReadonlyAttribute - @inlinable override public var startTime: DOMHighResTimeStamp { _startTime.wrappedValue } - -- @usableFromInline let _duration: ReadonlyAttribute -- @inlinable override public var duration: UInt32 { _duration.wrappedValue } -+ // XXX: override of property `duration` removed because the type here is UInt32 but the -+ // type in the superclass is DOMHighResTimestamp (Double). ++ get { self.jsObject[Strings.returnValue].string! } ++ set { self.jsObject[Strings.returnValue] = .string(newValue) } ++ } } - public class VisualViewport: EventTarget { -@@ -20687,19 +20694,9 @@ public enum CanvasImageSource: JSValueCompatible, Any_CanvasImageSource { + public enum BitrateMode: JSString, JSValueCompatible { +@@ -24231,14 +24242,6 @@ public enum CanvasImageSource: JSValueCompatible, Any_CanvasImageSource { + let val: CanvasImageSource = .htmlOrSVGImageElement(htmlOrSVGImageElement) self = val } - -- init(_ htmlOrSVGImageElement: HTMLOrSVGImageElement) { -- let val: CanvasImageSource = .htmlOrSVGImageElement(htmlOrSVGImageElement) -- self = val -- } -- - init(_ htmlImageElement: HTMLImageElement) { +- public init(_ htmlImageElement: HTMLImageElement) { - let val: HTMLOrSVGImageElement = .htmlImageElement(htmlImageElement) - self = .init(val) - } -- -- init(_ svgImageElement: SVGImageElement) { +- public init(_ svgImageElement: SVGImageElement) { - let val: HTMLOrSVGImageElement = .svgImageElement(svgImageElement) - self = .init(val) -+ let val: CanvasImageSource = .htmlOrSVGImageElement(htmlImageElement) -+ self = val - } - - init(_ htmlVideoElement: HTMLVideoElement) { -@@ -21947,18 +21944,8 @@ public enum ImageBitmapSource: JSValueCompatible, Any_ImageBitmapSource { +- } + public init(_ htmlVideoElement: HTMLVideoElement) { + let val: CanvasImageSource = .htmlVideoElement(htmlVideoElement) + self = val +@@ -25289,14 +25292,6 @@ public enum ImageBitmapSource: JSValueCompatible, Any_ImageBitmapSource { + let val: CanvasImageSource = .htmlOrSVGImageElement(htmlOrSVGImageElement) self = .init(val) } - -- init(_ htmlOrSVGImageElement: HTMLOrSVGImageElement) { -- let val: CanvasImageSource = .htmlOrSVGImageElement(htmlOrSVGImageElement) -- self = .init(val) -- } -- - init(_ htmlImageElement: HTMLImageElement) { +- public init(_ htmlImageElement: HTMLImageElement) { - let val: HTMLOrSVGImageElement = .htmlImageElement(htmlImageElement) - self = .init(val) - } -- -- init(_ svgImageElement: SVGImageElement) { +- public init(_ svgImageElement: SVGImageElement) { - let val: HTMLOrSVGImageElement = .svgImageElement(svgImageElement) -+ let val: CanvasImageSource = .htmlOrSVGImageElement(htmlImageElement) +- self = .init(val) +- } + public init(_ htmlVideoElement: HTMLVideoElement) { + let val: CanvasImageSource = .htmlVideoElement(htmlVideoElement) self = .init(val) - } - From c4af2adaa18470b93d34a383a32a63a18e5aeb2f Mon Sep 17 00:00:00 2001 From: Max Desiatov Date: Thu, 5 Jun 2025 19:28:28 +0100 Subject: [PATCH 5/5] Fix compatibility with Embedded Swift in remaining patched modules 1. Direct accessors are used instead of property wrappers in both DOM and SVG modules. 2. Avoiding use of `override` function declaration modifier in favor of `final` with an explicit generic types to avoid using implicit metatypes incompatible with Embedded Swift. 3. Formatting changes to make code compatible with the standard swift-format tool, that fixes automation incompatible with use of SwiftFormat instead of swift-format. --- Patches/CSSOM.patch | 25 +++++++++---------------- Patches/DOM.patch | 6 +++--- Patches/SVG.patch | 22 ++++++++-------------- Patches/WebAudio.patch | 30 ++++++++++++++---------------- 4 files changed, 34 insertions(+), 49 deletions(-) diff --git a/Patches/CSSOM.patch b/Patches/CSSOM.patch index c98927bd..c5ed5648 100644 --- a/Patches/CSSOM.patch +++ b/Patches/CSSOM.patch @@ -3,9 +3,9 @@ index 9cd2fb3..7aec5a8 100644 --- a/Sources/CSSOM/Generated.swift +++ b/Sources/CSSOM/Generated.swift @@ -411,7 +411,7 @@ public class CSSColorValue: CSSStyleValue { - + public required init(unsafelyWrapping jsObject: JSObject) { super.init(unsafelyWrapping: jsObject) } - + - @inlinable override public class func parse(cssText: String) -> CSSColorValue_or_CSSStyleValue { + @inlinable public class func parse(cssText: String) -> CSSColorValue { let this = constructor! @@ -14,7 +14,7 @@ index 9cd2fb3..7aec5a8 100644 @@ -947,7 +947,7 @@ public class CSSNumericValue: CSSStyleValue { return this[Strings.type].function!(this: this, arguments: []).fromJSValue()! } - + - @inlinable override public class func parse(cssText: String) -> CSSNumericValue { + @inlinable public class func parse(cssText: String) -> CSSNumericValue { let this = constructor! @@ -23,7 +23,7 @@ index 9cd2fb3..7aec5a8 100644 @@ -2072,9 +2072,10 @@ public class StylePropertyMapReadOnly: JSBridgedClass, Sequence { ValueIterableIterator(sequence: self) } - + - @inlinable final public func get(property: String) -> CSSStyleValue_or_Void { + // TODO: remove patch once https://github.com/w3c/css-houdini-drafts/issues/1095 is fixed + @inlinable final public func get(property: String) -> CSSStyleValue? { @@ -31,7 +31,7 @@ index 9cd2fb3..7aec5a8 100644 - return this[Strings.get].function!(this: this, arguments: [_toJSValue(property)]).fromJSValue()! + return this[Strings.get].function!(this: this, arguments: [_toJSValue(property)]).fromJSValue() } - + @inlinable final public func getAll(property: String) -> [CSSStyleValue] { @@ -2706,49 +2707,6 @@ public enum CSSStyleValue_or_String: JSValueCompatible, Any_CSSStyleValue_or_Str } @@ -68,25 +68,18 @@ index 9cd2fb3..7aec5a8 100644 - } - - public static func construct(from value: JSValue) -> Self? { -- if let cssStyleValue: CSSStyleValue = value.fromJSValue() { -- return .cssStyleValue(cssStyleValue) -- } -- if let void: Void = value.fromJSValue() { -- return .void(void) -- } +- if let cssStyleValue: CSSStyleValue = value.fromJSValue() { return .cssStyleValue(cssStyleValue) } +- if let void: Void = value.fromJSValue() { return .void(void) } - return nil - } - - public var jsValue: JSValue { - switch self { -- case let .cssStyleValue(cssStyleValue): -- return cssStyleValue.jsValue -- case let .void(void): -- return void.jsValue +- case let .cssStyleValue(cssStyleValue): return cssStyleValue.jsValue +- case let .void(void): return void.jsValue - } - } -} -- public protocol Any_CSSUnparsedSegment: ConvertibleToJSValue {} extension CSSVariableReferenceValue: Any_CSSUnparsedSegment {} extension String: Any_CSSUnparsedSegment {} diff --git a/Patches/DOM.patch b/Patches/DOM.patch index 7aa1ece2..21e09baa 100644 --- a/Patches/DOM.patch +++ b/Patches/DOM.patch @@ -3,9 +3,9 @@ index 9fa75ba..0339568 100644 --- a/Sources/DOM/Generated.swift +++ b/Sources/DOM/Generated.swift @@ -1094,6 +1094,17 @@ public class BeforeUnloadEvent: Event { - + public required init(unsafelyWrapping jsObject: JSObject) { super.init(unsafelyWrapping: jsObject) } - + + @available(*, unavailable) + override public var returnValue: Bool { + get { !self.returnValueAsString.isEmpty } @@ -18,7 +18,7 @@ index 9fa75ba..0339568 100644 + set { self.jsObject[Strings.returnValue] = .string(newValue) } + } } - + public enum BitrateMode: JSString, JSValueCompatible { @@ -24231,14 +24242,6 @@ public enum CanvasImageSource: JSValueCompatible, Any_CanvasImageSource { let val: CanvasImageSource = .htmlOrSVGImageElement(htmlOrSVGImageElement) diff --git a/Patches/SVG.patch b/Patches/SVG.patch index e8d7fdd7..99ed7816 100644 --- a/Patches/SVG.patch +++ b/Patches/SVG.patch @@ -1,21 +1,15 @@ +diff --git a/Sources/SVG/Generated.swift b/Sources/SVG/Generated.swift +index 176385b..bf8f1dd 100644 --- a/Sources/SVG/Generated.swift +++ b/Sources/SVG/Generated.swift -@@ -544,14 +544,16 @@ public class SVGElement: Element, GlobalEventHandlers, DocumentAndElementEventHa - @inlinable override public class var constructor: JSFunction? { JSObject.global[Strings.SVGElement].function } +@@ -524,7 +524,9 @@ public class SVGElement: Element, GlobalEventHandlers, SVGElementInstance, HTMLO - public required init(unsafelyWrapping jsObject: JSObject) { -- _className = ReadonlyAttribute(jsObject: jsObject, name: Strings.className) -+ _svgClassName = ReadonlyAttribute(jsObject: jsObject, name: Strings.className) - _ownerSVGElement = ReadonlyAttribute(jsObject: jsObject, name: Strings.ownerSVGElement) - _viewportElement = ReadonlyAttribute(jsObject: jsObject, name: Strings.viewportElement) - super.init(unsafelyWrapping: jsObject) - } + public required init(unsafelyWrapping jsObject: JSObject) { super.init(unsafelyWrapping: jsObject) } +- @inlinable public var className: SVGAnimatedString { jsObject[Strings.className].fromJSValue()! } + // Renamed because superclass has a `className` property of type `String` + // NOTE! Accessing `className` on an SVGElement will crash your app - @ReadonlyAttribute -- public var className: SVGAnimatedString -+ public var svgClassName: SVGAnimatedString ++ @inlinable public var svgClassName: SVGAnimatedString { jsObject[Strings.className].fromJSValue()! } + + @inlinable public var ownerSVGElement: SVGSVGElement? { jsObject[Strings.ownerSVGElement].fromJSValue() } - @ReadonlyAttribute - public var ownerSVGElement: SVGSVGElement? diff --git a/Patches/WebAudio.patch b/Patches/WebAudio.patch index 9222a713..5fd47ea9 100644 --- a/Patches/WebAudio.patch +++ b/Patches/WebAudio.patch @@ -1,21 +1,19 @@ +diff --git a/Sources/WebAudio/Generated.swift b/Sources/WebAudio/Generated.swift +index fff1563..d672d8c 100644 --- a/Sources/WebAudio/Generated.swift +++ b/Sources/WebAudio/Generated.swift -@@ -195,7 +195,8 @@ public class AudioBufferSourceNode: AudioScheduledSourceNode { - @ReadWriteAttribute - public var loopEnd: Double +@@ -535,11 +535,11 @@ public class AudioNode: EventTarget { -- @inlinable override public func start(when: Double? = nil, offset: Double? = nil, duration: Double? = nil) { -+ // `override` removed since the superclass function has fewer parameters -+ @inlinable func start(when: Double? = nil, offset: Double? = nil, duration: Double? = nil) { - let this = jsObject - _ = this[Strings.start].function!(this: this, arguments: [_toJSValue(when), _toJSValue(offset), _toJSValue(duration)]) - } -@@ -492,7 +493,7 @@ public class AudioNode: EventTarget { - super.init(unsafelyWrapping: jsObject) - } + public required init(unsafelyWrapping jsObject: JSObject) { super.init(unsafelyWrapping: jsObject) } -- @inlinable public func connect(destinationNode: AudioNode, output: UInt32? = nil, input: UInt32? = nil) -> Self { -+ @discardableResult @inlinable public func connect(destinationNode: NodeType, output: UInt32? = nil, input: UInt32? = nil) -> NodeType { +- @inlinable final public func connect( +- destinationNode: AudioNode, ++ @discardableResult @inlinable public final func connect( ++ destinationNode: NodeType, + output: UInt32? = nil, + input: UInt32? = nil +- ) -> AudioNode { ++ ) -> NodeType { let this = jsObject - return this[Strings.connect].function!(this: this, arguments: [_toJSValue(destinationNode), _toJSValue(output), _toJSValue(input)]).fromJSValue()! - } + return this[Strings.connect].function!( + this: this,