Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 731a047

Browse files
committed
BridgeJS: Naive implementation for swiftheapobject
BridgeJS: Improve SwiftHeapObject handling in import side
1 parent 395ca24 commit 731a047

File tree

16 files changed

+614
-44
lines changed

16 files changed

+614
-44
lines changed

‎Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2213,7 +2213,7 @@ public class ExportSwift {
22132213

22142214
var externParams: [String] = ["this: Int32"]
22152215
for param in method.parameters {
2216-
let loweringInfo = try param.type.loweringParameterInfo()
2216+
let loweringInfo = try param.type.loweringParameterInfo(context:.protocolExport)
22172217
assert(
22182218
loweringInfo.loweredParameters.count == 1,
22192219
"Protocol parameters must lower to a single WASM type"
@@ -2239,7 +2239,7 @@ public class ExportSwift {
22392239
"""
22402240
} else {
22412241
returnTypeStr = " -> \(method.returnType.swiftType)"
2242-
let liftingInfo = try method.returnType.liftingReturnInfo()
2242+
let liftingInfo = try method.returnType.liftingReturnInfo(context:.protocolExport)
22432243
if let abiType = liftingInfo.valueToLift {
22442244
externReturnType = " -> \(abiType.swiftType)"
22452245
} else {
@@ -2308,7 +2308,7 @@ public class ExportSwift {
23082308
)
23092309

23102310
// Generate getter
2311-
let liftingInfo = try property.type.liftingReturnInfo()
2311+
let liftingInfo = try property.type.liftingReturnInfo(context:.protocolExport)
23122312
let getterReturnType: String
23132313
let getterCallCode: String
23142314

@@ -2340,7 +2340,7 @@ public class ExportSwift {
23402340
"""
23412341
} else {
23422342
// Readwrite property - getter and setter
2343-
let loweringInfo = try property.type.loweringParameterInfo()
2343+
let loweringInfo = try property.type.loweringParameterInfo(context:.protocolExport)
23442344
assert(
23452345
loweringInfo.loweredParameters.count == 1,
23462346
"Protocol property setters must lower to a single WASM parameter"

‎Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift‎

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ extension BridgeType {
424424
static let void = LoweringParameterInfo(loweredParameters: [])
425425
}
426426

427-
func loweringParameterInfo() throws -> LoweringParameterInfo {
427+
func loweringParameterInfo(context:BridgeContext=.importTS) throws -> LoweringParameterInfo {
428428
switch self {
429429
case .bool: return .bool
430430
case .int: return .int
@@ -433,8 +433,18 @@ extension BridgeType {
433433
case .string: return .string
434434
case .jsObject: return .jsObject
435435
case .void: return .void
436-
case .swiftHeapObject:
437-
throw BridgeJSCoreError("swiftHeapObject is not supported in imported signatures")
436+
case .swiftHeapObject(let className):
437+
switch context {
438+
case .importTS:
439+
throw BridgeJSCoreError(
440+
"""
441+
swiftHeapObject '\(className)' is not supported in TypeScript imports.
442+
Swift classes can only be used in @JS protocols where Swift owns the instance.
443+
"""
444+
)
445+
case .protocolExport:
446+
return LoweringParameterInfo(loweredParameters: [("pointer", .pointer)])
447+
}
438448
case .swiftProtocol:
439449
throw BridgeJSCoreError("swiftProtocol is not supported in imported signatures")
440450
case .caseEnum, .rawValueEnum, .associatedValueEnum, .namespaceEnum:
@@ -456,7 +466,7 @@ extension BridgeType {
456466
static let void = LiftingReturnInfo(valueToLift: nil)
457467
}
458468

459-
func liftingReturnInfo() throws -> LiftingReturnInfo {
469+
func liftingReturnInfo(context:BridgeContext=.importTS) throws -> LiftingReturnInfo {
460470
switch self {
461471
case .bool: return .bool
462472
case .int: return .int
@@ -465,8 +475,18 @@ extension BridgeType {
465475
case .string: return .string
466476
case .jsObject: return .jsObject
467477
case .void: return .void
468-
case .swiftHeapObject:
469-
throw BridgeJSCoreError("swiftHeapObject is not supported in imported signatures")
478+
case .swiftHeapObject(let className):
479+
switch context {
480+
case .importTS:
481+
throw BridgeJSCoreError(
482+
"""
483+
swiftHeapObject '\(className)' cannot be returned from imported TypeScript functions.
484+
JavaScript cannot create Swift heap objects.
485+
"""
486+
)
487+
case .protocolExport:
488+
return LiftingReturnInfo(valueToLift: .pointer)
489+
}
470490
case .swiftProtocol:
471491
throw BridgeJSCoreError("swiftProtocol is not supported in imported signatures")
472492
case .caseEnum, .rawValueEnum, .associatedValueEnum, .namespaceEnum:

‎Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1856,21 +1856,23 @@ extension BridgeJSLink {
18561856
let body: CodeFragmentPrinter
18571857
let scope: JSGlueVariableScope
18581858
let cleanupCode: CodeFragmentPrinter
1859+
let context: BridgeContext
18591860
var parameterNames: [String] = []
18601861
var parameterForwardings: [String] = []
18611862

1862-
init() {
1863+
init(context:BridgeContext=.importTS) {
18631864
self.body = CodeFragmentPrinter()
18641865
self.scope = JSGlueVariableScope()
18651866
self.cleanupCode = CodeFragmentPrinter()
1867+
self.context = context
18661868
}
18671869

18681870
func liftSelf() {
18691871
parameterNames.append("self")
18701872
}
18711873

18721874
func liftParameter(param: Parameter) throws {
1873-
let liftingFragment = try IntrinsicJSFragment.liftParameter(type: param.type)
1875+
let liftingFragment = try IntrinsicJSFragment.liftParameter(type: param.type, context: context)
18741876
assert(
18751877
liftingFragment.parameters.count >= 1,
18761878
"Lifting fragment should have at least one parameter to lift"
@@ -1927,7 +1929,7 @@ extension BridgeJSLink {
19271929
}
19281930

19291931
private func call(callExpr: String, returnType: BridgeType) throws -> String? {
1930-
let loweringFragment = try IntrinsicJSFragment.lowerReturn(type: returnType)
1932+
let loweringFragment = try IntrinsicJSFragment.lowerReturn(type: returnType, context: context)
19311933
let returnExpr: String?
19321934
if loweringFragment.parameters.count == 0 {
19331935
body.write("\(callExpr);")
@@ -1947,7 +1949,7 @@ extension BridgeJSLink {
19471949
func callConstructor(name: String) throws -> String? {
19481950
let call = "new imports.\(name)(\(parameterForwardings.joined(separator: ", ")))"
19491951
let type: BridgeType = .jsObject(name)
1950-
let loweringFragment = try IntrinsicJSFragment.lowerReturn(type: type)
1952+
let loweringFragment = try IntrinsicJSFragment.lowerReturn(type: type, context: context)
19511953
return try lowerReturnValue(returnType: type, returnExpr: call, loweringFragment: loweringFragment)
19521954
}
19531955

@@ -2444,7 +2446,7 @@ extension BridgeJSLink {
24442446
className: `protocol`.name
24452447
)
24462448

2447-
let getterThunkBuilder = ImportedThunkBuilder()
2449+
let getterThunkBuilder = ImportedThunkBuilder(context:.protocolExport)
24482450
getterThunkBuilder.liftSelf()
24492451
let returnExpr = try getterThunkBuilder.callPropertyGetter(name: property.name, returnType: property.type)
24502452
let getterLines = getterThunkBuilder.renderFunction(
@@ -2462,7 +2464,7 @@ extension BridgeJSLink {
24622464
operation: "set",
24632465
className: `protocol`.name
24642466
)
2465-
let setterThunkBuilder = ImportedThunkBuilder()
2467+
let setterThunkBuilder = ImportedThunkBuilder(context:.protocolExport)
24662468
setterThunkBuilder.liftSelf()
24672469
try setterThunkBuilder.liftParameter(
24682470
param: Parameter(label: nil, name: "value", type: property.type)
@@ -2482,7 +2484,7 @@ extension BridgeJSLink {
24822484
protocol: ExportedProtocol,
24832485
method: ExportedFunction
24842486
) throws {
2485-
let thunkBuilder = ImportedThunkBuilder()
2487+
let thunkBuilder = ImportedThunkBuilder(context:.protocolExport)
24862488
thunkBuilder.liftSelf()
24872489
for param in method.parameters {
24882490
try thunkBuilder.liftParameter(param: param)

‎Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift‎

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,20 @@ struct IntrinsicJSFragment: Sendable {
219219
}
220220
)
221221
}
222+
static func swiftHeapObjectLiftParameter(_ name: String) -> IntrinsicJSFragment {
223+
return IntrinsicJSFragment(
224+
parameters: ["pointer"],
225+
printCode: { arguments, scope, printer, cleanupCode in
226+
return ["\(name).__construct(\(arguments[0]))"]
227+
}
228+
)
229+
}
230+
static let swiftHeapObjectLowerReturn = IntrinsicJSFragment(
231+
parameters: ["value"],
232+
printCode: { arguments, scope, printer, cleanupCode in
233+
return ["\(arguments[0]).pointer"]
234+
}
235+
)
222236

223237
static func associatedEnumLowerParameter(enumBase: String) -> IntrinsicJSFragment {
224238
IntrinsicJSFragment(
@@ -468,17 +482,23 @@ struct IntrinsicJSFragment: Sendable {
468482
// MARK: - ImportedJS
469483

470484
/// Returns a fragment that lifts Wasm core values to JS values for parameters
471-
static func liftParameter(type: BridgeType) throws -> IntrinsicJSFragment {
485+
///
486+
/// - Parameters:
487+
/// - type: The bridge type to lift
488+
/// - context: The bridge context (defaults to .importTS for backward compatibility)
489+
static func liftParameter(type: BridgeType, context: BridgeContext = .importTS) throws -> IntrinsicJSFragment {
472490
switch type {
473491
case .int, .float, .double: return .identity
474492
case .bool: return .boolLiftParameter
475493
case .string: return .stringLiftParameter
476494
case .jsObject: return .jsObjectLiftParameter
477495
case .swiftHeapObject(let name):
478-
throw BridgeJSLinkError(
479-
message:
480-
"Swift heap objects are not supported to be passed as parameters to imported JS functions: \(name)"
481-
)
496+
guard context == .protocolExport else {
497+
throw BridgeJSLinkError(
498+
message: "swiftHeapObject '\(name)' can only be used in protocol exports, not in \(context)"
499+
)
500+
}
501+
return .swiftHeapObjectLiftParameter(name)
482502
case .swiftProtocol: return .jsObjectLiftParameter
483503
case .void:
484504
throw BridgeJSLinkError(
@@ -509,16 +529,23 @@ struct IntrinsicJSFragment: Sendable {
509529
}
510530

511531
/// Returns a fragment that lowers a JS value to Wasm core values for return values
512-
static func lowerReturn(type: BridgeType) throws -> IntrinsicJSFragment {
532+
///
533+
/// - Parameters:
534+
/// - type: The bridge type to lower
535+
/// - context: The bridge context (defaults to .importTS for backward compatibility)
536+
static func lowerReturn(type: BridgeType, context: BridgeContext = .importTS) throws -> IntrinsicJSFragment {
513537
switch type {
514538
case .int, .float, .double: return .identity
515539
case .bool: return .boolLowerReturn
516540
case .string: return .stringLowerReturn
517541
case .jsObject: return .jsObjectLowerReturn
518-
case .swiftHeapObject:
519-
throw BridgeJSLinkError(
520-
message: "Swift heap objects are not supported to be returned from imported JS functions"
521-
)
542+
case .swiftHeapObject(let name):
543+
guard context == .protocolExport else {
544+
throw BridgeJSLinkError(
545+
message: "swiftHeapObject '\(name)' can only be used in protocol exports, not in \(context)"
546+
)
547+
}
548+
return .swiftHeapObjectLowerReturn
522549
case .swiftProtocol: return .jsObjectLowerReturn
523550
case .void: return .void
524551
case .optional(let wrappedType):

‎Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ public struct ABINameGenerator {
6767

6868
// MARK: - Types
6969

70+
/// Context for bridge operations that determines which types are valid
71+
public enum BridgeContext: Sendable {
72+
case importTS
73+
case protocolExport
74+
}
75+
7076
public enum BridgeType: Codable, Equatable, Sendable {
7177
case int, float, double, string, bool, jsObject(String?), swiftHeapObject(String), void
7278
indirect case optional(BridgeType)

‎Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/Protocol.swift‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
import JavaScriptKit
22

3+
@JS class Helper {
4+
@JS var value: Int
5+
6+
@JS init(value: Int) {
7+
self.value = value
8+
}
9+
10+
@JS func increment() {
11+
value += 1
12+
}
13+
}
14+
315
@JS protocol MyViewControllerDelegate {
416
var eventCount: Int { get set }
517
var delegateName: String { get }
@@ -8,6 +20,8 @@ import JavaScriptKit
820
func onCountUpdated(count: Int) -> Bool
921
func onLabelUpdated(_ prefix: String, _ suffix: String)
1022
func isCountEven() -> Bool
23+
func onHelperUpdated(_ helper: Helper)
24+
func createHelper() -> Helper
1125
}
1226

1327
@JS class MyViewController {
@@ -40,4 +54,8 @@ import JavaScriptKit
4054
@JS func checkEvenCount() -> Bool {
4155
return delegate.isCountEven()
4256
}
57+
58+
@JS func sendHelper(_ helper: Helper) {
59+
delegate.onHelperUpdated(helper)
60+
}
4361
}

‎Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Protocol.Export.d.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ export interface MyViewControllerDelegate {
1010
onCountUpdated(count: number): boolean;
1111
onLabelUpdated(prefix: string, suffix: string): void;
1212
isCountEven(): boolean;
13+
onHelperUpdated(helper: Helper): void;
14+
createHelper(): Helper;
1315
eventCount: number;
1416
readonly delegateName: string;
1517
}
@@ -21,16 +23,24 @@ export interface SwiftHeapObject {
2123
/// Note: Calling this method will release the heap object and it will no longer be accessible.
2224
release(): void;
2325
}
26+
export interface Helper extends SwiftHeapObject {
27+
increment(): void;
28+
value: number;
29+
}
2430
export interface MyViewController extends SwiftHeapObject {
2531
triggerEvent(): void;
2632
updateValue(value: string): void;
2733
updateCount(count: number): boolean;
2834
updateLabel(prefix: string, suffix: string): void;
2935
checkEvenCount(): boolean;
36+
sendHelper(helper: Helper): void;
3037
delegate: MyViewControllerDelegate;
3138
secondDelegate: MyViewControllerDelegate | null;
3239
}
3340
export type Exports = {
41+
Helper: {
42+
new(value: number): Helper;
43+
}
3444
MyViewController: {
3545
new(delegate: MyViewControllerDelegate): MyViewController;
3646
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /