Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,8 @@ struct StackCodegen {
return "JSObject.bridgeJSLiftParameter(_swift_js_pop_param_int32())"
case .swiftHeapObject(let className):
return "\(raw: className).bridgeJSLiftParameter(_swift_js_pop_param_pointer())"
case .unsafePointer:
return "\(raw: type.swiftType).bridgeJSLiftParameter(_swift_js_pop_param_pointer())"
case .swiftProtocol:
// Protocols are handled via JSObject
return "JSObject.bridgeJSLiftParameter(_swift_js_pop_param_int32())"
Expand Down Expand Up @@ -872,6 +874,8 @@ struct StackCodegen {
return ["_swift_js_push_int(\(raw: accessor).bridgeJSLowerParameter())"]
case .swiftHeapObject:
return ["_swift_js_push_pointer(\(raw: accessor).bridgeJSLowerReturn())"]
case .unsafePointer:
return ["_swift_js_push_pointer(\(raw: accessor).bridgeJSLowerReturn())"]
case .swiftProtocol:
return ["_swift_js_push_int(\(raw: accessor).bridgeJSLowerParameter())"]
case .caseEnum:
Expand Down Expand Up @@ -1421,6 +1425,23 @@ extension WasmCoreType {
}
}

extension UnsafePointerType {
var swiftType: String {
switch kind {
case .unsafePointer:
return "UnsafePointer<\(pointee ?? "Never")>"
case .unsafeMutablePointer:
return "UnsafeMutablePointer<\(pointee ?? "Never")>"
case .unsafeRawPointer:
return "UnsafeRawPointer"
case .unsafeMutableRawPointer:
return "UnsafeMutableRawPointer"
case .opaquePointer:
return "OpaquePointer"
}
}
}

extension BridgeType {
var swiftType: String {
switch self {
Expand All @@ -1432,6 +1453,7 @@ extension BridgeType {
case .jsObject(nil): return "JSObject"
case .jsObject(let name?): return name
case .swiftHeapObject(let name): return name
case .unsafePointer(let ptr): return ptr.swiftType
case .swiftProtocol(let name): return "Any\(name)"
case .void: return "Void"
case .optional(let wrappedType): return "Optional<\(wrappedType.swiftType)>"
Expand All @@ -1457,6 +1479,7 @@ extension BridgeType {
static let string = LiftingIntrinsicInfo(parameters: [("bytes", .i32), ("length", .i32)])
static let jsObject = LiftingIntrinsicInfo(parameters: [("value", .i32)])
static let swiftHeapObject = LiftingIntrinsicInfo(parameters: [("value", .pointer)])
static let unsafePointer = LiftingIntrinsicInfo(parameters: [("pointer", .pointer)])
static let void = LiftingIntrinsicInfo(parameters: [])
static let caseEnum = LiftingIntrinsicInfo(parameters: [("value", .i32)])
static let associatedValueEnum = LiftingIntrinsicInfo(parameters: [
Expand All @@ -1473,6 +1496,7 @@ extension BridgeType {
case .string: return .string
case .jsObject: return .jsObject
case .swiftHeapObject: return .swiftHeapObject
case .unsafePointer: return .unsafePointer
case .swiftProtocol: return .jsObject
case .void: return .void
case .optional(let wrappedType):
Expand Down Expand Up @@ -1503,6 +1527,7 @@ extension BridgeType {
static let string = LoweringIntrinsicInfo(returnType: nil)
static let jsObject = LoweringIntrinsicInfo(returnType: .i32)
static let swiftHeapObject = LoweringIntrinsicInfo(returnType: .pointer)
static let unsafePointer = LoweringIntrinsicInfo(returnType: .pointer)
static let void = LoweringIntrinsicInfo(returnType: nil)
static let caseEnum = LoweringIntrinsicInfo(returnType: .i32)
static let rawValueEnum = LoweringIntrinsicInfo(returnType: .i32)
Expand All @@ -1520,6 +1545,7 @@ extension BridgeType {
case .string: return .string
case .jsObject: return .jsObject
case .swiftHeapObject: return .swiftHeapObject
case .unsafePointer: return .unsafePointer
case .swiftProtocol: return .jsObject
case .void: return .void
case .optional: return .optional
Expand Down
4 changes: 4 additions & 0 deletions Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,8 @@ extension BridgeType {
case .closure:
// Swift closure is boxed and passed to JS as a pointer.
return LoweringParameterInfo(loweredParameters: [("pointer", .pointer)])
case .unsafePointer:
return LoweringParameterInfo(loweredParameters: [("pointer", .pointer)])
case .swiftHeapObject(let className):
switch context {
case .importTS:
Expand Down Expand Up @@ -959,6 +961,8 @@ extension BridgeType {
case .closure:
// JS returns a callback ID for closures, which Swift lifts to a typed closure.
return LiftingReturnInfo(valueToLift: .i32)
case .unsafePointer:
return LiftingReturnInfo(valueToLift: .pointer)
case .swiftHeapObject(let className):
switch context {
case .importTS:
Expand Down
54 changes: 54 additions & 0 deletions Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ public final class SwiftToSkeleton {
}
}

// UnsafePointer family
if let unsafePointerType = Self.parseUnsafePointerType(type) {
return .unsafePointer(unsafePointerType)
}

let typeName: String
if let identifier = type.as(IdentifierTypeSyntax.self) {
typeName = Self.normalizeIdentifier(identifier.name.text)
Expand Down Expand Up @@ -248,6 +253,55 @@ public final class SwiftToSkeleton {
return .swiftHeapObject(swiftCallName)
}

fileprivate static func parseUnsafePointerType(_ type: TypeSyntax) -> UnsafePointerType? {
func parse(baseName: String, genericArg: TypeSyntax?) -> UnsafePointerType? {
let pointee = genericArg?.trimmedDescription
switch baseName {
case "UnsafePointer":
return .init(kind: .unsafePointer, pointee: pointee)
case "UnsafeMutablePointer":
return .init(kind: .unsafeMutablePointer, pointee: pointee)
case "UnsafeRawPointer":
return .init(kind: .unsafeRawPointer)
case "UnsafeMutableRawPointer":
return .init(kind: .unsafeMutableRawPointer)
case "OpaquePointer":
return .init(kind: .opaquePointer)
default:
return nil
}
}

if let identifier = type.as(IdentifierTypeSyntax.self) {
let baseName = identifier.name.text
if (baseName == "UnsafePointer" || baseName == "UnsafeMutablePointer"),
let genericArgs = identifier.genericArgumentClause?.arguments,
genericArgs.count == 1,
let argType = TypeSyntax(genericArgs.first?.argument)
{
return parse(baseName: baseName, genericArg: argType)
}
return parse(baseName: baseName, genericArg: nil)
}

if let member = type.as(MemberTypeSyntax.self),
let base = member.baseType.as(IdentifierTypeSyntax.self),
base.name.text == "Swift"
{
let baseName = member.name.text
if (baseName == "UnsafePointer" || baseName == "UnsafeMutablePointer"),
let genericArgs = member.genericArgumentClause?.arguments,
genericArgs.count == 1,
let argType = TypeSyntax(genericArgs.first?.argument)
{
return parse(baseName: baseName, genericArg: argType)
}
return parse(baseName: baseName, genericArg: nil)
}

return nil
}

/// Computes the full Swift call name by walking up the AST hierarchy to find all parent enums
/// This generates the qualified name needed for Swift code generation (e.g., "Networking.API.HTTPServer")
fileprivate static func computeSwiftCallName(for node: some SyntaxProtocol, itemName: String) -> String {
Expand Down
2 changes: 2 additions & 0 deletions Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3258,6 +3258,8 @@ extension BridgeType {
return name ?? "any"
case .swiftHeapObject(let name):
return name
case .unsafePointer:
return "number"
case .optional(let wrappedType):
return "\(wrappedType.tsType) | null"
case .caseEnum(let name):
Expand Down
22 changes: 21 additions & 1 deletion Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1300,7 +1300,7 @@ struct IntrinsicJSFragment: Sendable {
/// Returns a fragment that lowers a JS value to Wasm core values for parameters
static func lowerParameter(type: BridgeType) throws -> IntrinsicJSFragment {
switch type {
case .int, .float, .double, .bool: return .identity
case .int, .float, .double, .bool, .unsafePointer: return .identity
case .string: return .stringLowerParameter
case .jsObject: return .jsObjectLowerParameter
case .swiftHeapObject:
Expand Down Expand Up @@ -1346,6 +1346,7 @@ struct IntrinsicJSFragment: Sendable {
case .string: return .stringLiftReturn
case .jsObject: return .jsObjectLiftReturn
case .swiftHeapObject(let name): return .swiftHeapObjectLiftReturn(name)
case .unsafePointer: return .identity
case .swiftProtocol: return .jsObjectLiftReturn
case .void: return .void
case .optional(let wrappedType): return .optionalLiftReturn(wrappedType: wrappedType)
Expand Down Expand Up @@ -1388,6 +1389,7 @@ struct IntrinsicJSFragment: Sendable {
case .bool: return .boolLiftParameter
case .string: return .stringLiftParameter
case .jsObject: return .jsObjectLiftParameter
case .unsafePointer: return .identity
case .swiftHeapObject(let name):
switch context {
case .importTS:
Expand Down Expand Up @@ -1482,6 +1484,7 @@ struct IntrinsicJSFragment: Sendable {
case .bool: return .boolLowerReturn
case .string: return .stringLowerReturn
case .jsObject: return .jsObjectLowerReturn
case .unsafePointer: return .identity
case .swiftHeapObject(let name):
switch context {
case .importTS:
Expand Down Expand Up @@ -2266,6 +2269,14 @@ struct IntrinsicJSFragment: Sendable {
return []
}
)
case .unsafePointer:
return IntrinsicJSFragment(
parameters: ["value"],
printCode: { arguments, scope, printer, cleanup in
printer.write("\(JSGlueVariableScope.reservedTmpParamPointers).push((\(arguments[0]) | 0));")
return []
}
)
case .jsObject:
return IntrinsicJSFragment(
parameters: ["value"],
Expand Down Expand Up @@ -2698,6 +2709,15 @@ struct IntrinsicJSFragment: Sendable {
return [dVar]
}
)
case .unsafePointer:
return IntrinsicJSFragment(
parameters: [],
printCode: { arguments, scope, printer, cleanup in
let pVar = scope.variable("pointer")
printer.write("const \(pVar) = \(JSGlueVariableScope.reservedTmpRetPointers).pop();")
return [pVar]
}
)
case .optional(let wrappedType):
return IntrinsicJSFragment(
parameters: [],
Expand Down
45 changes: 45 additions & 0 deletions Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,28 @@ public struct ClosureSignature: Codable, Equatable, Hashable, Sendable {
}
}

public enum UnsafePointerKind: String, Codable, Equatable, Hashable, Sendable {
case unsafePointer
case unsafeMutablePointer
case unsafeRawPointer
case unsafeMutableRawPointer
case opaquePointer
}

public struct UnsafePointerType: Codable, Equatable, Hashable, Sendable {
public let kind: UnsafePointerKind
/// The pointee type name for generic pointer types (e.g. `UInt8` for `UnsafePointer<UInt8>`).
public let pointee: String?

public init(kind: UnsafePointerKind, pointee: String? = nil) {
self.kind = kind
self.pointee = pointee
}
}

public enum BridgeType: Codable, Equatable, Hashable, Sendable {
case int, float, double, string, bool, jsObject(String?), swiftHeapObject(String), void
case unsafePointer(UnsafePointerType)
indirect case optional(BridgeType)
case caseEnum(String)
case rawValueEnum(String, SwiftEnumRawType)
Expand Down Expand Up @@ -810,6 +830,12 @@ extension BridgeType {
self = .void
case "JSObject":
self = .jsObject(nil)
case "UnsafeRawPointer":
self = .unsafePointer(.init(kind: .unsafeRawPointer))
case "UnsafeMutableRawPointer":
self = .unsafePointer(.init(kind: .unsafeMutableRawPointer))
case "OpaquePointer":
self = .unsafePointer(.init(kind: .opaquePointer))
default:
return nil
}
Expand All @@ -827,6 +853,8 @@ extension BridgeType {
case .swiftHeapObject:
// UnsafeMutableRawPointer is returned as an i32 pointer
return .pointer
case .unsafePointer:
return .pointer
case .optional(_):
return nil
case .caseEnum:
Expand Down Expand Up @@ -870,6 +898,23 @@ extension BridgeType {
return "\(typeName.count)\(typeName)C"
case .swiftHeapObject(let name):
return "\(name.count)\(name)C"
case .unsafePointer(let ptr):
func sanitize(_ s: String) -> String {
s.filter { $0.isNumber || $0.isLetter }
}
let kindCode: String =
switch ptr.kind {
case .unsafePointer: "Sup"
case .unsafeMutablePointer: "Sump"
case .unsafeRawPointer: "Surp"
case .unsafeMutableRawPointer: "Sumrp"
case .opaquePointer: "Sop"
}
if let pointee = ptr.pointee, !pointee.isEmpty {
let p = sanitize(pointee)
return "\(kindCode)\(p.count)\(p)"
}
return kindCode
case .optional(let wrapped):
return "Sq\(wrapped.mangleTypeName)"
case .caseEnum(let name),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
@JS func takeUnsafeRawPointer(_ p: UnsafeRawPointer) {}
@JS func takeUnsafeMutableRawPointer(_ p: UnsafeMutableRawPointer) {}
@JS func takeOpaquePointer(_ p: OpaquePointer) {}
@JS func takeUnsafePointer(_ p: UnsafePointer<UInt8>) {}
@JS func takeUnsafeMutablePointer(_ p: UnsafeMutablePointer<UInt8>) {}

@JS func returnUnsafeRawPointer() -> UnsafeRawPointer { UnsafeRawPointer(bitPattern: 1)! }
@JS func returnUnsafeMutableRawPointer() -> UnsafeMutableRawPointer { UnsafeMutableRawPointer(bitPattern: 1)! }
@JS func returnOpaquePointer() -> OpaquePointer { OpaquePointer(bitPattern: 1)! }
@JS func returnUnsafePointer() -> UnsafePointer<UInt8> {
UnsafeRawPointer(bitPattern: 1)!.assumingMemoryBound(to: UInt8.self)
}
@JS func returnUnsafeMutablePointer() -> UnsafeMutablePointer<UInt8> {
UnsafeMutableRawPointer(bitPattern: 1)!.assumingMemoryBound(to: UInt8.self)
}

@JS struct PointerFields {
var raw: UnsafeRawPointer
var mutRaw: UnsafeMutableRawPointer
var opaque: OpaquePointer
var ptr: UnsafePointer<UInt8>
var mutPtr: UnsafeMutablePointer<UInt8>

@JS init(
raw: UnsafeRawPointer,
mutRaw: UnsafeMutableRawPointer,
opaque: OpaquePointer,
ptr: UnsafePointer<UInt8>,
mutPtr: UnsafeMutablePointer<UInt8>
) {
self.raw = raw
self.mutRaw = mutRaw
self.opaque = opaque
self.ptr = ptr
self.mutPtr = mutPtr
}
}

@JS func roundTripPointerFields(_ value: PointerFields) -> PointerFields { value }
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit,
// DO NOT EDIT.
//
// To update this file, just rebuild your project or run
// `swift package bridge-js`.

export interface PointerFields {
raw: number;
mutRaw: number;
opaque: number;
ptr: number;
mutPtr: number;
}
export type Exports = {
takeUnsafeRawPointer(p: number): void;
takeUnsafeMutableRawPointer(p: number): void;
takeOpaquePointer(p: number): void;
takeUnsafePointer(p: number): void;
takeUnsafeMutablePointer(p: number): void;
returnUnsafeRawPointer(): number;
returnUnsafeMutableRawPointer(): number;
returnOpaquePointer(): number;
returnUnsafePointer(): number;
returnUnsafeMutablePointer(): number;
roundTripPointerFields(value: PointerFields): PointerFields;
PointerFields: {
init(raw: number, mutRaw: number, opaque: number, ptr: number, mutPtr: number): PointerFields;
}
}
export type Imports = {
}
export function createInstantiator(options: {
imports: Imports;
}, swift: any): Promise<{
addImports: (importObject: WebAssembly.Imports) => void;
setInstance: (instance: WebAssembly.Instance) => void;
createExports: (instance: WebAssembly.Instance) => Exports;
}>;
Loading