diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index a40f9c2b..0718a1a7 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -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())" @@ -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: @@ -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 { @@ -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)>" @@ -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: [ @@ -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): @@ -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) @@ -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 diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index dd6c9d27..ad5cdaf0 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -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: @@ -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: diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift index f18788e7..f5712b68 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift @@ -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) @@ -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 { diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 929e6e4c..176eff57 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -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): diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift index d9223faf..e14eb4ee 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift @@ -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: @@ -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) @@ -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: @@ -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: @@ -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"], @@ -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: [], diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index 28e4b6dc..10c63ddc 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -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`). + 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) @@ -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 } @@ -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: @@ -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), diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/UnsafePointer.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/UnsafePointer.swift new file mode 100644 index 00000000..eb6ca007 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/UnsafePointer.swift @@ -0,0 +1,39 @@ +@JS func takeUnsafeRawPointer(_ p: UnsafeRawPointer) {} +@JS func takeUnsafeMutableRawPointer(_ p: UnsafeMutableRawPointer) {} +@JS func takeOpaquePointer(_ p: OpaquePointer) {} +@JS func takeUnsafePointer(_ p: UnsafePointer) {} +@JS func takeUnsafeMutablePointer(_ p: UnsafeMutablePointer) {} + +@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 { + UnsafeRawPointer(bitPattern: 1)!.assumingMemoryBound(to: UInt8.self) +} +@JS func returnUnsafeMutablePointer() -> UnsafeMutablePointer { + UnsafeMutableRawPointer(bitPattern: 1)!.assumingMemoryBound(to: UInt8.self) +} + +@JS struct PointerFields { + var raw: UnsafeRawPointer + var mutRaw: UnsafeMutableRawPointer + var opaque: OpaquePointer + var ptr: UnsafePointer + var mutPtr: UnsafeMutablePointer + + @JS init( + raw: UnsafeRawPointer, + mutRaw: UnsafeMutableRawPointer, + opaque: OpaquePointer, + ptr: UnsafePointer, + mutPtr: UnsafeMutablePointer + ) { + self.raw = raw + self.mutRaw = mutRaw + self.opaque = opaque + self.ptr = ptr + self.mutPtr = mutPtr + } +} + +@JS func roundTripPointerFields(_ value: PointerFields) -> PointerFields { value } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/UnsafePointer.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/UnsafePointer.Export.d.ts new file mode 100644 index 00000000..4f9aa4e4 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/UnsafePointer.Export.d.ts @@ -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; +}>; \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/UnsafePointer.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/UnsafePointer.Export.js new file mode 100644 index 00000000..1e1c7349 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/UnsafePointer.Export.js @@ -0,0 +1,283 @@ +// 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 async function createInstantiator(options, swift) { + let instance; + let memory; + let setException; + const textDecoder = new TextDecoder("utf-8"); + const textEncoder = new TextEncoder("utf-8"); + let tmpRetString; + let tmpRetBytes; + let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpParamInts = []; + let tmpParamF32s = []; + let tmpParamF64s = []; + let tmpRetPointers = []; + let tmpParamPointers = []; + const enumHelpers = {}; + const structHelpers = {}; + + let _exports = null; + let bjs = null; + const __bjs_createPointerFieldsHelpers = () => { + return (tmpParamInts, tmpParamF32s, tmpParamF64s, tmpParamPointers, tmpRetPointers, textEncoder, swift, enumHelpers) => ({ + lower: (value) => { + tmpParamPointers.push((value.raw | 0)); + tmpParamPointers.push((value.mutRaw | 0)); + tmpParamPointers.push((value.opaque | 0)); + tmpParamPointers.push((value.ptr | 0)); + tmpParamPointers.push((value.mutPtr | 0)); + return { cleanup: undefined }; + }, + raise: (tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s, tmpRetPointers) => { + const pointer = tmpRetPointers.pop(); + const pointer1 = tmpRetPointers.pop(); + const pointer2 = tmpRetPointers.pop(); + const pointer3 = tmpRetPointers.pop(); + const pointer4 = tmpRetPointers.pop(); + return { raw: pointer4, mutRaw: pointer3, opaque: pointer2, ptr: pointer1, mutPtr: pointer }; + } + }); + }; + + return { + /** + * @param {WebAssembly.Imports} importObject + */ + addImports: (importObject, importsContext) => { + bjs = {}; + importObject["bjs"] = bjs; + bjs["swift_js_return_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["swift_js_make_js_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + return swift.memory.retain(textDecoder.decode(bytes)); + } + bjs["swift_js_init_memory_with_result"] = function(ptr, len) { + const target = new Uint8Array(memory.buffer, ptr, len); + target.set(tmpRetBytes); + tmpRetBytes = undefined; + } + bjs["swift_js_throw"] = function(id) { + tmpRetException = swift.memory.retainByRef(id); + } + bjs["swift_js_retain"] = function(id) { + return swift.memory.retainByRef(id); + } + bjs["swift_js_release"] = function(id) { + swift.memory.release(id); + } + bjs["swift_js_push_tag"] = function(tag) { + tmpRetTag = tag; + } + bjs["swift_js_push_int"] = function(v) { + tmpRetInts.push(v | 0); + } + bjs["swift_js_push_f32"] = function(v) { + tmpRetF32s.push(Math.fround(v)); + } + bjs["swift_js_push_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_push_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + const value = textDecoder.decode(bytes); + tmpRetStrings.push(value); + } + bjs["swift_js_pop_param_int32"] = function() { + return tmpParamInts.pop(); + } + bjs["swift_js_pop_param_f32"] = function() { + return tmpParamF32s.pop(); + } + bjs["swift_js_pop_param_f64"] = function() { + return tmpParamF64s.pop(); + } + bjs["swift_js_push_pointer"] = function(pointer) { + tmpRetPointers.push(pointer); + } + bjs["swift_js_pop_param_pointer"] = function() { + return tmpParamPointers.pop(); + } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } + bjs["swift_js_get_optional_int_presence"] = function() { + return tmpRetOptionalInt != null ? 1 : 0; + } + bjs["swift_js_get_optional_int_value"] = function() { + const value = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return value; + } + bjs["swift_js_get_optional_string"] = function() { + const str = tmpRetString; + tmpRetString = undefined; + if (str == null) { + return -1; + } else { + const bytes = textEncoder.encode(str); + tmpRetBytes = bytes; + return bytes.length; + } + } + bjs["swift_js_get_optional_float_presence"] = function() { + return tmpRetOptionalFloat != null ? 1 : 0; + } + bjs["swift_js_get_optional_float_value"] = function() { + const value = tmpRetOptionalFloat; + tmpRetOptionalFloat = undefined; + return value; + } + bjs["swift_js_get_optional_double_presence"] = function() { + return tmpRetOptionalDouble != null ? 1 : 0; + } + bjs["swift_js_get_optional_double_value"] = function() { + const value = tmpRetOptionalDouble; + tmpRetOptionalDouble = undefined; + return value; + } + bjs["swift_js_get_optional_heap_object_pointer"] = function() { + const pointer = tmpRetOptionalHeapObject; + tmpRetOptionalHeapObject = undefined; + return pointer || 0; + } + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + const PointerFieldsHelpers = __bjs_createPointerFieldsHelpers()(tmpParamInts, tmpParamF32s, tmpParamF64s, tmpParamPointers, tmpRetPointers, textEncoder, swift, enumHelpers); + structHelpers.PointerFields = PointerFieldsHelpers; + + const exports = { + takeUnsafeRawPointer: function bjs_takeUnsafeRawPointer(p) { + instance.exports.bjs_takeUnsafeRawPointer(p); + }, + takeUnsafeMutableRawPointer: function bjs_takeUnsafeMutableRawPointer(p) { + instance.exports.bjs_takeUnsafeMutableRawPointer(p); + }, + takeOpaquePointer: function bjs_takeOpaquePointer(p) { + instance.exports.bjs_takeOpaquePointer(p); + }, + takeUnsafePointer: function bjs_takeUnsafePointer(p) { + instance.exports.bjs_takeUnsafePointer(p); + }, + takeUnsafeMutablePointer: function bjs_takeUnsafeMutablePointer(p) { + instance.exports.bjs_takeUnsafeMutablePointer(p); + }, + returnUnsafeRawPointer: function bjs_returnUnsafeRawPointer() { + const ret = instance.exports.bjs_returnUnsafeRawPointer(); + return ret; + }, + returnUnsafeMutableRawPointer: function bjs_returnUnsafeMutableRawPointer() { + const ret = instance.exports.bjs_returnUnsafeMutableRawPointer(); + return ret; + }, + returnOpaquePointer: function bjs_returnOpaquePointer() { + const ret = instance.exports.bjs_returnOpaquePointer(); + return ret; + }, + returnUnsafePointer: function bjs_returnUnsafePointer() { + const ret = instance.exports.bjs_returnUnsafePointer(); + return ret; + }, + returnUnsafeMutablePointer: function bjs_returnUnsafeMutablePointer() { + const ret = instance.exports.bjs_returnUnsafeMutablePointer(); + return ret; + }, + roundTripPointerFields: function bjs_roundTripPointerFields(value) { + const { cleanup: cleanup } = structHelpers.PointerFields.lower(value); + instance.exports.bjs_roundTripPointerFields(); + const structValue = structHelpers.PointerFields.raise(tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s, tmpRetPointers); + if (cleanup) { cleanup(); } + return structValue; + }, + PointerFields: { + init: function(raw, mutRaw, opaque, ptr, mutPtr) { + instance.exports.bjs_PointerFields_init(raw, mutRaw, opaque, ptr, mutPtr); + const structValue = structHelpers.PointerFields.raise(tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s, tmpRetPointers); + return structValue; + }, + }, + }; + _exports = exports; + return exports; + }, + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/UnsafePointer.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/UnsafePointer.json new file mode 100644 index 00000000..f38cd944 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/UnsafePointer.json @@ -0,0 +1,413 @@ +{ + "classes" : [ + + ], + "enums" : [ + + ], + "exposeToGlobal" : false, + "functions" : [ + { + "abiName" : "bjs_takeUnsafeRawPointer", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "takeUnsafeRawPointer", + "parameters" : [ + { + "label" : "_", + "name" : "p", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeRawPointer" + } + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_takeUnsafeMutableRawPointer", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "takeUnsafeMutableRawPointer", + "parameters" : [ + { + "label" : "_", + "name" : "p", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeMutableRawPointer" + } + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_takeOpaquePointer", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "takeOpaquePointer", + "parameters" : [ + { + "label" : "_", + "name" : "p", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "opaquePointer" + } + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_takeUnsafePointer", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "takeUnsafePointer", + "parameters" : [ + { + "label" : "_", + "name" : "p", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafePointer", + "pointee" : "UInt8" + } + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_takeUnsafeMutablePointer", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "takeUnsafeMutablePointer", + "parameters" : [ + { + "label" : "_", + "name" : "p", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeMutablePointer", + "pointee" : "UInt8" + } + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_returnUnsafeRawPointer", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "returnUnsafeRawPointer", + "parameters" : [ + + ], + "returnType" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeRawPointer" + } + } + } + }, + { + "abiName" : "bjs_returnUnsafeMutableRawPointer", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "returnUnsafeMutableRawPointer", + "parameters" : [ + + ], + "returnType" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeMutableRawPointer" + } + } + } + }, + { + "abiName" : "bjs_returnOpaquePointer", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "returnOpaquePointer", + "parameters" : [ + + ], + "returnType" : { + "unsafePointer" : { + "_0" : { + "kind" : "opaquePointer" + } + } + } + }, + { + "abiName" : "bjs_returnUnsafePointer", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "returnUnsafePointer", + "parameters" : [ + + ], + "returnType" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafePointer", + "pointee" : "UInt8" + } + } + } + }, + { + "abiName" : "bjs_returnUnsafeMutablePointer", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "returnUnsafeMutablePointer", + "parameters" : [ + + ], + "returnType" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeMutablePointer", + "pointee" : "UInt8" + } + } + } + }, + { + "abiName" : "bjs_roundTripPointerFields", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "roundTripPointerFields", + "parameters" : [ + { + "label" : "_", + "name" : "value", + "type" : { + "swiftStruct" : { + "_0" : "PointerFields" + } + } + } + ], + "returnType" : { + "swiftStruct" : { + "_0" : "PointerFields" + } + } + } + ], + "protocols" : [ + + ], + "structs" : [ + { + "constructor" : { + "abiName" : "bjs_PointerFields_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + { + "label" : "raw", + "name" : "raw", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeRawPointer" + } + } + } + }, + { + "label" : "mutRaw", + "name" : "mutRaw", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeMutableRawPointer" + } + } + } + }, + { + "label" : "opaque", + "name" : "opaque", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "opaquePointer" + } + } + } + }, + { + "label" : "ptr", + "name" : "ptr", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafePointer", + "pointee" : "UInt8" + } + } + } + }, + { + "label" : "mutPtr", + "name" : "mutPtr", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeMutablePointer", + "pointee" : "UInt8" + } + } + } + } + ] + }, + "methods" : [ + + ], + "name" : "PointerFields", + "properties" : [ + { + "isReadonly" : true, + "isStatic" : false, + "name" : "raw", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeRawPointer" + } + } + } + }, + { + "isReadonly" : true, + "isStatic" : false, + "name" : "mutRaw", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeMutableRawPointer" + } + } + } + }, + { + "isReadonly" : true, + "isStatic" : false, + "name" : "opaque", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "opaquePointer" + } + } + } + }, + { + "isReadonly" : true, + "isStatic" : false, + "name" : "ptr", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafePointer", + "pointee" : "UInt8" + } + } + } + }, + { + "isReadonly" : true, + "isStatic" : false, + "name" : "mutPtr", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeMutablePointer", + "pointee" : "UInt8" + } + } + } + } + ], + "swiftCallName" : "PointerFields" + } + ] +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/UnsafePointer.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/UnsafePointer.swift new file mode 100644 index 00000000..503fed02 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/UnsafePointer.swift @@ -0,0 +1,145 @@ +extension PointerFields: _BridgedSwiftStruct { + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter() -> PointerFields { + let mutPtr = UnsafeMutablePointer.bridgeJSLiftParameter(_swift_js_pop_param_pointer()) + let ptr = UnsafePointer.bridgeJSLiftParameter(_swift_js_pop_param_pointer()) + let opaque = OpaquePointer.bridgeJSLiftParameter(_swift_js_pop_param_pointer()) + let mutRaw = UnsafeMutableRawPointer.bridgeJSLiftParameter(_swift_js_pop_param_pointer()) + let raw = UnsafeRawPointer.bridgeJSLiftParameter(_swift_js_pop_param_pointer()) + return PointerFields(raw: raw, mutRaw: mutRaw, opaque: opaque, ptr: ptr, mutPtr: mutPtr) + } + + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() { + _swift_js_push_pointer(self.raw.bridgeJSLowerReturn()) + _swift_js_push_pointer(self.mutRaw.bridgeJSLowerReturn()) + _swift_js_push_pointer(self.opaque.bridgeJSLowerReturn()) + _swift_js_push_pointer(self.ptr.bridgeJSLowerReturn()) + _swift_js_push_pointer(self.mutPtr.bridgeJSLowerReturn()) + } +} + +@_expose(wasm, "bjs_PointerFields_init") +@_cdecl("bjs_PointerFields_init") +public func _bjs_PointerFields_init(_ raw: UnsafeMutableRawPointer, _ mutRaw: UnsafeMutableRawPointer, _ opaque: UnsafeMutableRawPointer, _ ptr: UnsafeMutableRawPointer, _ mutPtr: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = PointerFields(raw: UnsafeRawPointer.bridgeJSLiftParameter(raw), mutRaw: UnsafeMutableRawPointer.bridgeJSLiftParameter(mutRaw), opaque: OpaquePointer.bridgeJSLiftParameter(opaque), ptr: UnsafePointer.bridgeJSLiftParameter(ptr), mutPtr: UnsafeMutablePointer.bridgeJSLiftParameter(mutPtr)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_takeUnsafeRawPointer") +@_cdecl("bjs_takeUnsafeRawPointer") +public func _bjs_takeUnsafeRawPointer(_ p: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + takeUnsafeRawPointer(_: UnsafeRawPointer.bridgeJSLiftParameter(p)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_takeUnsafeMutableRawPointer") +@_cdecl("bjs_takeUnsafeMutableRawPointer") +public func _bjs_takeUnsafeMutableRawPointer(_ p: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + takeUnsafeMutableRawPointer(_: UnsafeMutableRawPointer.bridgeJSLiftParameter(p)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_takeOpaquePointer") +@_cdecl("bjs_takeOpaquePointer") +public func _bjs_takeOpaquePointer(_ p: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + takeOpaquePointer(_: OpaquePointer.bridgeJSLiftParameter(p)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_takeUnsafePointer") +@_cdecl("bjs_takeUnsafePointer") +public func _bjs_takeUnsafePointer(_ p: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + takeUnsafePointer(_: UnsafePointer.bridgeJSLiftParameter(p)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_takeUnsafeMutablePointer") +@_cdecl("bjs_takeUnsafeMutablePointer") +public func _bjs_takeUnsafeMutablePointer(_ p: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + takeUnsafeMutablePointer(_: UnsafeMutablePointer.bridgeJSLiftParameter(p)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_returnUnsafeRawPointer") +@_cdecl("bjs_returnUnsafeRawPointer") +public func _bjs_returnUnsafeRawPointer() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = returnUnsafeRawPointer() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_returnUnsafeMutableRawPointer") +@_cdecl("bjs_returnUnsafeMutableRawPointer") +public func _bjs_returnUnsafeMutableRawPointer() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = returnUnsafeMutableRawPointer() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_returnOpaquePointer") +@_cdecl("bjs_returnOpaquePointer") +public func _bjs_returnOpaquePointer() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = returnOpaquePointer() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_returnUnsafePointer") +@_cdecl("bjs_returnUnsafePointer") +public func _bjs_returnUnsafePointer() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = returnUnsafePointer() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_returnUnsafeMutablePointer") +@_cdecl("bjs_returnUnsafeMutablePointer") +public func _bjs_returnUnsafeMutablePointer() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = returnUnsafeMutablePointer() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripPointerFields") +@_cdecl("bjs_roundTripPointerFields") +public func _bjs_roundTripPointerFields() -> Void { + #if arch(wasm32) + let ret = roundTripPointerFields(_: PointerFields.bridgeJSLiftParameter()) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} \ No newline at end of file diff --git a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift index 92ab52f0..be763b7a 100644 --- a/Sources/JavaScriptKit/BridgeJSIntrinsics.swift +++ b/Sources/JavaScriptKit/BridgeJSIntrinsics.swift @@ -644,6 +644,134 @@ func _swift_js_return_optional_double(_ isSome: Int32, _ value: Float64) { } #endif +// MARK: - UnsafePointer family + +extension UnsafeMutableRawPointer { + // MARK: ImportTS + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> UnsafeMutableRawPointer { self } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn( + _ pointer: UnsafeMutableRawPointer + ) + -> UnsafeMutableRawPointer + { + pointer + } + + // MARK: ExportSwift + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter( + _ pointer: UnsafeMutableRawPointer + ) + -> UnsafeMutableRawPointer + { + pointer + } + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() -> UnsafeMutableRawPointer { self } +} + +extension UnsafeRawPointer { + // MARK: ImportTS + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> UnsafeMutableRawPointer { + UnsafeMutableRawPointer(mutating: self) + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn( + _ pointer: UnsafeMutableRawPointer + ) + -> UnsafeRawPointer + { + UnsafeRawPointer(pointer) + } + + // MARK: ExportSwift + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter( + _ pointer: UnsafeMutableRawPointer + ) + -> UnsafeRawPointer + { + UnsafeRawPointer(pointer) + } + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() -> UnsafeMutableRawPointer { + bridgeJSLowerParameter() + } +} + +extension OpaquePointer { + // MARK: ImportTS + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> UnsafeMutableRawPointer { + UnsafeMutableRawPointer(mutating: UnsafeRawPointer(self)) + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn( + _ pointer: UnsafeMutableRawPointer + ) + -> OpaquePointer + { + OpaquePointer(UnsafeRawPointer(pointer)) + } + + // MARK: ExportSwift + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter( + _ pointer: UnsafeMutableRawPointer + ) + -> OpaquePointer + { + OpaquePointer(UnsafeRawPointer(pointer)) + } + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() -> UnsafeMutableRawPointer { + bridgeJSLowerParameter() + } +} + +extension UnsafePointer { + // MARK: ImportTS + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> UnsafeMutableRawPointer { + UnsafeMutableRawPointer(mutating: UnsafeRawPointer(self)) + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn( + _ pointer: UnsafeMutableRawPointer + ) + -> UnsafePointer + { + UnsafeRawPointer(pointer).assumingMemoryBound(to: Pointee.self) + } + + // MARK: ExportSwift + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter( + _ pointer: UnsafeMutableRawPointer + ) + -> UnsafePointer + { + UnsafeRawPointer(pointer).assumingMemoryBound(to: Pointee.self) + } + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() -> UnsafeMutableRawPointer { + bridgeJSLowerParameter() + } +} + +extension UnsafeMutablePointer { + // MARK: ImportTS + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> UnsafeMutableRawPointer { + UnsafeMutableRawPointer(self) + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn( + _ pointer: UnsafeMutableRawPointer + ) + -> UnsafeMutablePointer + { + pointer.assumingMemoryBound(to: Pointee.self) + } + + // MARK: ExportSwift + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter( + _ pointer: UnsafeMutableRawPointer + ) + -> UnsafeMutablePointer + { + pointer.assumingMemoryBound(to: Pointee.self) + } + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() -> UnsafeMutableRawPointer { + bridgeJSLowerParameter() + } +} + extension Optional where Wrapped == Bool { // MARK: ImportTS diff --git a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift index 0df421df..8edf8a6d 100644 --- a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift @@ -29,10 +29,50 @@ func runJsWorks() -> Void return v } +@JS func roundTripUnsafeRawPointer(v: UnsafeRawPointer) -> UnsafeRawPointer { + return v +} +@JS func roundTripUnsafeMutableRawPointer(v: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + return v +} +@JS func roundTripOpaquePointer(v: OpaquePointer) -> OpaquePointer { + return v +} +@JS func roundTripUnsafePointer(v: UnsafePointer) -> UnsafePointer { + return v +} +@JS func roundTripUnsafeMutablePointer(v: UnsafeMutablePointer) -> UnsafeMutablePointer { + return v +} + @JS func roundTripJSObject(v: JSObject) -> JSObject { return v } +@JS struct PointerFields { + var raw: UnsafeRawPointer + var mutRaw: UnsafeMutableRawPointer + var opaque: OpaquePointer + var ptr: UnsafePointer + var mutPtr: UnsafeMutablePointer + + @JS init( + raw: UnsafeRawPointer, + mutRaw: UnsafeMutableRawPointer, + opaque: OpaquePointer, + ptr: UnsafePointer, + mutPtr: UnsafeMutablePointer + ) { + self.raw = raw + self.mutRaw = mutRaw + self.opaque = opaque + self.ptr = ptr + self.mutPtr = mutPtr + } +} + +@JS func roundTripPointerFields(_ value: PointerFields) -> PointerFields { value } + @JSClass struct Foo { @JSGetter var value: String @JSFunction init(_ value: String) throws(JSException) diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift index 5195bee1..e80388b6 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift @@ -2212,6 +2212,36 @@ public func _bjs_StaticPropertyNamespace_NestedProperties_static_nestedDouble_se #endif } +extension PointerFields: _BridgedSwiftStruct { + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter() -> PointerFields { + let mutPtr = UnsafeMutablePointer.bridgeJSLiftParameter(_swift_js_pop_param_pointer()) + let ptr = UnsafePointer.bridgeJSLiftParameter(_swift_js_pop_param_pointer()) + let opaque = OpaquePointer.bridgeJSLiftParameter(_swift_js_pop_param_pointer()) + let mutRaw = UnsafeMutableRawPointer.bridgeJSLiftParameter(_swift_js_pop_param_pointer()) + let raw = UnsafeRawPointer.bridgeJSLiftParameter(_swift_js_pop_param_pointer()) + return PointerFields(raw: raw, mutRaw: mutRaw, opaque: opaque, ptr: ptr, mutPtr: mutPtr) + } + + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() { + _swift_js_push_pointer(self.raw.bridgeJSLowerReturn()) + _swift_js_push_pointer(self.mutRaw.bridgeJSLowerReturn()) + _swift_js_push_pointer(self.opaque.bridgeJSLowerReturn()) + _swift_js_push_pointer(self.ptr.bridgeJSLowerReturn()) + _swift_js_push_pointer(self.mutPtr.bridgeJSLowerReturn()) + } +} + +@_expose(wasm, "bjs_PointerFields_init") +@_cdecl("bjs_PointerFields_init") +public func _bjs_PointerFields_init(_ raw: UnsafeMutableRawPointer, _ mutRaw: UnsafeMutableRawPointer, _ opaque: UnsafeMutableRawPointer, _ ptr: UnsafeMutableRawPointer, _ mutPtr: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = PointerFields(raw: UnsafeRawPointer.bridgeJSLiftParameter(raw), mutRaw: UnsafeMutableRawPointer.bridgeJSLiftParameter(mutRaw), opaque: OpaquePointer.bridgeJSLiftParameter(opaque), ptr: UnsafePointer.bridgeJSLiftParameter(ptr), mutPtr: UnsafeMutablePointer.bridgeJSLiftParameter(mutPtr)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + extension DataPoint: _BridgedSwiftStruct { @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter() -> DataPoint { let optFlag = Optional.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32()) @@ -2595,6 +2625,61 @@ public func _bjs_roundTripSwiftHeapObject(_ v: UnsafeMutableRawPointer) -> Unsaf #endif } +@_expose(wasm, "bjs_roundTripUnsafeRawPointer") +@_cdecl("bjs_roundTripUnsafeRawPointer") +public func _bjs_roundTripUnsafeRawPointer(_ v: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = roundTripUnsafeRawPointer(v: UnsafeRawPointer.bridgeJSLiftParameter(v)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripUnsafeMutableRawPointer") +@_cdecl("bjs_roundTripUnsafeMutableRawPointer") +public func _bjs_roundTripUnsafeMutableRawPointer(_ v: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = roundTripUnsafeMutableRawPointer(v: UnsafeMutableRawPointer.bridgeJSLiftParameter(v)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripOpaquePointer") +@_cdecl("bjs_roundTripOpaquePointer") +public func _bjs_roundTripOpaquePointer(_ v: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = roundTripOpaquePointer(v: OpaquePointer.bridgeJSLiftParameter(v)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripUnsafePointer") +@_cdecl("bjs_roundTripUnsafePointer") +public func _bjs_roundTripUnsafePointer(_ v: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = roundTripUnsafePointer(v: UnsafePointer.bridgeJSLiftParameter(v)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_roundTripUnsafeMutablePointer") +@_cdecl("bjs_roundTripUnsafeMutablePointer") +public func _bjs_roundTripUnsafeMutablePointer(_ v: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = roundTripUnsafeMutablePointer(v: UnsafeMutablePointer.bridgeJSLiftParameter(v)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_roundTripJSObject") @_cdecl("bjs_roundTripJSObject") public func _bjs_roundTripJSObject(_ v: Int32) -> Int32 { @@ -2606,6 +2691,17 @@ public func _bjs_roundTripJSObject(_ v: Int32) -> Int32 { #endif } +@_expose(wasm, "bjs_roundTripPointerFields") +@_cdecl("bjs_roundTripPointerFields") +public func _bjs_roundTripPointerFields() -> Void { + #if arch(wasm32) + let ret = roundTripPointerFields(_: PointerFields.bridgeJSLiftParameter()) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_makeImportedFoo") @_cdecl("bjs_makeImportedFoo") public func _bjs_makeImportedFoo(_ valueBytes: Int32, _ valueLength: Int32) -> Int32 { diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json index 66a87b06..c4e82a6d 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json @@ -4704,6 +4704,155 @@ } } }, + { + "abiName" : "bjs_roundTripUnsafeRawPointer", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "roundTripUnsafeRawPointer", + "parameters" : [ + { + "label" : "v", + "name" : "v", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeRawPointer" + } + } + } + } + ], + "returnType" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeRawPointer" + } + } + } + }, + { + "abiName" : "bjs_roundTripUnsafeMutableRawPointer", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "roundTripUnsafeMutableRawPointer", + "parameters" : [ + { + "label" : "v", + "name" : "v", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeMutableRawPointer" + } + } + } + } + ], + "returnType" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeMutableRawPointer" + } + } + } + }, + { + "abiName" : "bjs_roundTripOpaquePointer", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "roundTripOpaquePointer", + "parameters" : [ + { + "label" : "v", + "name" : "v", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "opaquePointer" + } + } + } + } + ], + "returnType" : { + "unsafePointer" : { + "_0" : { + "kind" : "opaquePointer" + } + } + } + }, + { + "abiName" : "bjs_roundTripUnsafePointer", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "roundTripUnsafePointer", + "parameters" : [ + { + "label" : "v", + "name" : "v", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafePointer", + "pointee" : "UInt8" + } + } + } + } + ], + "returnType" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafePointer", + "pointee" : "UInt8" + } + } + } + }, + { + "abiName" : "bjs_roundTripUnsafeMutablePointer", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "roundTripUnsafeMutablePointer", + "parameters" : [ + { + "label" : "v", + "name" : "v", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeMutablePointer", + "pointee" : "UInt8" + } + } + } + } + ], + "returnType" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeMutablePointer", + "pointee" : "UInt8" + } + } + } + }, { "abiName" : "bjs_roundTripJSObject", "effects" : { @@ -4729,6 +4878,31 @@ } } }, + { + "abiName" : "bjs_roundTripPointerFields", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "roundTripPointerFields", + "parameters" : [ + { + "label" : "_", + "name" : "value", + "type" : { + "swiftStruct" : { + "_0" : "PointerFields" + } + } + } + ], + "returnType" : { + "swiftStruct" : { + "_0" : "PointerFields" + } + } + }, { "abiName" : "bjs_makeImportedFoo", "effects" : { @@ -8313,6 +8487,144 @@ } ], "structs" : [ + { + "constructor" : { + "abiName" : "bjs_PointerFields_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + { + "label" : "raw", + "name" : "raw", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeRawPointer" + } + } + } + }, + { + "label" : "mutRaw", + "name" : "mutRaw", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeMutableRawPointer" + } + } + } + }, + { + "label" : "opaque", + "name" : "opaque", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "opaquePointer" + } + } + } + }, + { + "label" : "ptr", + "name" : "ptr", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafePointer", + "pointee" : "UInt8" + } + } + } + }, + { + "label" : "mutPtr", + "name" : "mutPtr", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeMutablePointer", + "pointee" : "UInt8" + } + } + } + } + ] + }, + "methods" : [ + + ], + "name" : "PointerFields", + "properties" : [ + { + "isReadonly" : true, + "isStatic" : false, + "name" : "raw", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeRawPointer" + } + } + } + }, + { + "isReadonly" : true, + "isStatic" : false, + "name" : "mutRaw", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeMutableRawPointer" + } + } + } + }, + { + "isReadonly" : true, + "isStatic" : false, + "name" : "opaque", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "opaquePointer" + } + } + } + }, + { + "isReadonly" : true, + "isStatic" : false, + "name" : "ptr", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafePointer", + "pointee" : "UInt8" + } + } + } + }, + { + "isReadonly" : true, + "isStatic" : false, + "name" : "mutPtr", + "type" : { + "unsafePointer" : { + "_0" : { + "kind" : "unsafeMutablePointer", + "pointee" : "UInt8" + } + } + } + } + ], + "swiftCallName" : "PointerFields" + }, { "constructor" : { "abiName" : "bjs_DataPoint_init", diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index 66283b72..15a491f6 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -166,6 +166,14 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { assert.equal(exports.roundTripString(v), v); } + for (const p of [1, 4, 1024, 65536, 2147483647]) { + assert.equal(exports.roundTripUnsafeRawPointer(p), p); + assert.equal(exports.roundTripUnsafeMutableRawPointer(p), p); + assert.equal(exports.roundTripOpaquePointer(p), p); + assert.equal(exports.roundTripUnsafePointer(p), p); + assert.equal(exports.roundTripUnsafeMutablePointer(p), p); + } + const g = new exports.Greeter("John"); assert.equal(g.greet(), "Hello, John!"); @@ -945,6 +953,9 @@ function testStructSupport(exports) { const data2 = { x: 0.0, y: 0.0, label: "", optCount: null, optFlag: null }; assert.deepEqual(exports.roundTripDataPoint(data2), data2); + const pointerFields1 = { raw: 1, mutRaw: 4, opaque: 1024, ptr: 65536, mutPtr: 2 }; + assert.deepEqual(exports.roundTripPointerFields(pointerFields1), pointerFields1); + const contact1 = { name: "Alice", age: 30,