Skip to content

Commit 44d4c22

Browse files
committed
BridgeJS: Support case enums as imported function parameters and returns
Case enums (enums without raw values or associated values) already bridged across the export boundary as their Int32 tag, but the TypeScript import path rejected them with "Enum types are not yet supported in TypeScript imports". The JS glue already round-trips the tag in both directions, so enable case enums as imported (@JSFunction) parameters and return values by lowering and lifting that Int32 tag in the import context, matching the export side. Adds a CaseEnumImports round-trip test and an EnumCaseImport codegen snapshot.
1 parent 2388473 commit 44d4c22

10 files changed

Lines changed: 691 additions & 12 deletions

File tree

Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -928,12 +928,7 @@ extension BridgeType {
928928
return LoweringParameterInfo(loweredParameters: [("objectId", .i32)])
929929
}
930930
case .caseEnum:
931-
switch context {
932-
case .importTS:
933-
throw BridgeJSCoreError("Enum types are not yet supported in TypeScript imports")
934-
case .exportSwift:
935-
return LoweringParameterInfo(loweredParameters: [("value", .i32)])
936-
}
931+
return LoweringParameterInfo(loweredParameters: [("value", .i32)])
937932
case .rawValueEnum(_, let rawType):
938933
if rawType == .string {
939934
return .string
@@ -1011,12 +1006,7 @@ extension BridgeType {
10111006
return LiftingReturnInfo(valueToLift: .i32)
10121007
}
10131008
case .caseEnum:
1014-
switch context {
1015-
case .importTS:
1016-
throw BridgeJSCoreError("Enum types are not yet supported in TypeScript imports")
1017-
case .exportSwift:
1018-
return LiftingReturnInfo(valueToLift: .i32)
1019-
}
1009+
return LiftingReturnInfo(valueToLift: .i32)
10201010
case .rawValueEnum(_, let rawType):
10211011
let wasmType = rawType.wasmCoreType ?? .i32
10221012
return LiftingReturnInfo(valueToLift: wasmType)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@JS enum Signal {
2+
case start
3+
case stop
4+
}
5+
6+
// Case enums (no raw value) bridge as their `Int32` tag as imported-function
7+
// parameters and return values.
8+
@JSClass struct SignalControls {
9+
@JSFunction func send(_ signal: Signal) throws(JSException)
10+
@JSFunction func current() throws(JSException) -> Signal
11+
@JSFunction static func roundTrip(_ signal: Signal) throws(JSException) -> Signal
12+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
{
2+
"exported" : {
3+
"classes" : [
4+
5+
],
6+
"enums" : [
7+
{
8+
"cases" : [
9+
{
10+
"associatedValues" : [
11+
12+
],
13+
"name" : "start"
14+
},
15+
{
16+
"associatedValues" : [
17+
18+
],
19+
"name" : "stop"
20+
}
21+
],
22+
"emitStyle" : "const",
23+
"name" : "Signal",
24+
"staticMethods" : [
25+
26+
],
27+
"staticProperties" : [
28+
29+
],
30+
"swiftCallName" : "Signal",
31+
"tsFullPath" : "Signal"
32+
}
33+
],
34+
"exposeToGlobal" : false,
35+
"functions" : [
36+
37+
],
38+
"protocols" : [
39+
40+
],
41+
"structs" : [
42+
43+
]
44+
},
45+
"imported" : {
46+
"children" : [
47+
{
48+
"functions" : [
49+
50+
],
51+
"types" : [
52+
{
53+
"accessLevel" : "internal",
54+
"getters" : [
55+
56+
],
57+
"methods" : [
58+
{
59+
"accessLevel" : "internal",
60+
"effects" : {
61+
"isAsync" : false,
62+
"isStatic" : false,
63+
"isThrows" : true
64+
},
65+
"name" : "send",
66+
"parameters" : [
67+
{
68+
"name" : "signal",
69+
"type" : {
70+
"caseEnum" : {
71+
"_0" : "Signal"
72+
}
73+
}
74+
}
75+
],
76+
"returnType" : {
77+
"void" : {
78+
79+
}
80+
}
81+
},
82+
{
83+
"accessLevel" : "internal",
84+
"effects" : {
85+
"isAsync" : false,
86+
"isStatic" : false,
87+
"isThrows" : true
88+
},
89+
"name" : "current",
90+
"parameters" : [
91+
92+
],
93+
"returnType" : {
94+
"caseEnum" : {
95+
"_0" : "Signal"
96+
}
97+
}
98+
}
99+
],
100+
"name" : "SignalControls",
101+
"setters" : [
102+
103+
],
104+
"staticMethods" : [
105+
{
106+
"accessLevel" : "internal",
107+
"effects" : {
108+
"isAsync" : false,
109+
"isStatic" : false,
110+
"isThrows" : true
111+
},
112+
"name" : "roundTrip",
113+
"parameters" : [
114+
{
115+
"name" : "signal",
116+
"type" : {
117+
"caseEnum" : {
118+
"_0" : "Signal"
119+
}
120+
}
121+
}
122+
],
123+
"returnType" : {
124+
"caseEnum" : {
125+
"_0" : "Signal"
126+
}
127+
}
128+
}
129+
]
130+
}
131+
]
132+
}
133+
]
134+
},
135+
"moduleName" : "TestModule",
136+
"usedExternalModules" : [
137+
138+
]
139+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
extension Signal: _BridgedSwiftCaseEnum {
2+
@_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 {
3+
return bridgeJSRawValue
4+
}
5+
@_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn(_ value: Int32) -> Signal {
6+
return bridgeJSLiftParameter(value)
7+
}
8+
@_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ value: Int32) -> Signal {
9+
return Signal(bridgeJSRawValue: value)!
10+
}
11+
@_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() -> Int32 {
12+
return bridgeJSLowerParameter()
13+
}
14+
15+
@_spi(BridgeJS) @usableFromInline init?(bridgeJSRawValue: Int32) {
16+
switch bridgeJSRawValue {
17+
case 0:
18+
self = .start
19+
case 1:
20+
self = .stop
21+
default:
22+
return nil
23+
}
24+
}
25+
26+
@_spi(BridgeJS) @usableFromInline var bridgeJSRawValue: Int32 {
27+
switch self {
28+
case .start:
29+
return 0
30+
case .stop:
31+
return 1
32+
}
33+
}
34+
}
35+
36+
#if arch(wasm32)
37+
@_extern(wasm, module: "TestModule", name: "bjs_SignalControls_roundTrip_static")
38+
fileprivate func bjs_SignalControls_roundTrip_static_extern(_ signal: Int32) -> Int32
39+
#else
40+
fileprivate func bjs_SignalControls_roundTrip_static_extern(_ signal: Int32) -> Int32 {
41+
fatalError("Only available on WebAssembly")
42+
}
43+
#endif
44+
@inline(never) fileprivate func bjs_SignalControls_roundTrip_static(_ signal: Int32) -> Int32 {
45+
return bjs_SignalControls_roundTrip_static_extern(signal)
46+
}
47+
48+
#if arch(wasm32)
49+
@_extern(wasm, module: "TestModule", name: "bjs_SignalControls_send")
50+
fileprivate func bjs_SignalControls_send_extern(_ self: Int32, _ signal: Int32) -> Void
51+
#else
52+
fileprivate func bjs_SignalControls_send_extern(_ self: Int32, _ signal: Int32) -> Void {
53+
fatalError("Only available on WebAssembly")
54+
}
55+
#endif
56+
@inline(never) fileprivate func bjs_SignalControls_send(_ self: Int32, _ signal: Int32) -> Void {
57+
return bjs_SignalControls_send_extern(self, signal)
58+
}
59+
60+
#if arch(wasm32)
61+
@_extern(wasm, module: "TestModule", name: "bjs_SignalControls_current")
62+
fileprivate func bjs_SignalControls_current_extern(_ self: Int32) -> Int32
63+
#else
64+
fileprivate func bjs_SignalControls_current_extern(_ self: Int32) -> Int32 {
65+
fatalError("Only available on WebAssembly")
66+
}
67+
#endif
68+
@inline(never) fileprivate func bjs_SignalControls_current(_ self: Int32) -> Int32 {
69+
return bjs_SignalControls_current_extern(self)
70+
}
71+
72+
func _$SignalControls_roundTrip(_ signal: Signal) throws(JSException) -> Signal {
73+
let signalValue = signal.bridgeJSLowerParameter()
74+
let ret = bjs_SignalControls_roundTrip_static(signalValue)
75+
if let error = _swift_js_take_exception() {
76+
throw error
77+
}
78+
return Signal.bridgeJSLiftReturn(ret)
79+
}
80+
81+
func _$SignalControls_send(_ self: JSObject, _ signal: Signal) throws(JSException) -> Void {
82+
let selfValue = self.bridgeJSLowerParameter()
83+
let signalValue = signal.bridgeJSLowerParameter()
84+
bjs_SignalControls_send(selfValue, signalValue)
85+
if let error = _swift_js_take_exception() {
86+
throw error
87+
}
88+
}
89+
90+
func _$SignalControls_current(_ self: JSObject) throws(JSException) -> Signal {
91+
let selfValue = self.bridgeJSLowerParameter()
92+
let ret = bjs_SignalControls_current(selfValue)
93+
if let error = _swift_js_take_exception() {
94+
throw error
95+
}
96+
return Signal.bridgeJSLiftReturn(ret)
97+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit,
2+
// DO NOT EDIT.
3+
//
4+
// To update this file, just rebuild your project or run
5+
// `swift package bridge-js`.
6+
7+
export const SignalValues: {
8+
readonly Start: 0;
9+
readonly Stop: 1;
10+
};
11+
export type SignalTag = typeof SignalValues[keyof typeof SignalValues];
12+
13+
export type SignalObject = typeof SignalValues;
14+
15+
export interface SignalControls {
16+
send(signal: SignalTag): void;
17+
current(): SignalTag;
18+
}
19+
export type Exports = {
20+
Signal: SignalObject
21+
}
22+
export type Imports = {
23+
SignalControls: {
24+
roundTrip(signal: SignalTag): SignalTag;
25+
}
26+
}
27+
export function createInstantiator(options: {
28+
imports: Imports;
29+
}, swift: any): Promise<{
30+
addImports: (importObject: WebAssembly.Imports) => void;
31+
setInstance: (instance: WebAssembly.Instance) => void;
32+
createExports: (instance: WebAssembly.Instance) => Exports;
33+
}>;

0 commit comments

Comments
 (0)