Skip to content

Commit c562383

Browse files
committed
BridgeJS: Move optional JSObject to stack ABI, enabling Optional<@jsclass>
1 parent 824c051 commit c562383

9 files changed

Lines changed: 198 additions & 10 deletions

File tree

Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,9 @@ public struct ImportTS {
128128
self.returnType = returnType
129129
self.context = context
130130
let liftingInfo = try returnType.liftingReturnInfo(context: context)
131-
if effects.isAsync || returnType == .void || returnType.usesSideChannelForOptionalReturn() {
131+
if effects.isAsync || returnType == .void || returnType.usesSideChannelForOptionalReturn()
132+
|| liftingInfo.valueToLift == nil
133+
{
132134
abiReturnType = nil
133135
} else {
134136
abiReturnType = liftingInfo.valueToLift
@@ -1032,6 +1034,10 @@ extension BridgeType {
10321034
case .namespaceEnum:
10331035
throw BridgeJSCoreError("Namespace enums cannot be used as return values")
10341036
case .nullable(let wrappedType, _):
1037+
// jsObject uses stack ABI for optionals — returns void, value goes through stacks
1038+
if case .jsObject = wrappedType {
1039+
return LiftingReturnInfo(valueToLift: nil)
1040+
}
10351041
let wrappedInfo = try wrappedType.liftingReturnInfo(context: context)
10361042
return LiftingReturnInfo(valueToLift: wrappedInfo.valueToLift)
10371043
case .array, .dictionary:

Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2648,7 +2648,7 @@ private extension BridgeType {
26482648
case .string:
26492649
return .sideChannelReturn(.storage)
26502650
case .jsObject:
2651-
return .sideChannelReturn(.retainedObject)
2651+
return .stackABI
26522652
case .jsValue:
26532653
return .inlineFlag
26542654
case .swiftHeapObject:
@@ -2685,7 +2685,7 @@ private extension BridgeType {
26852685

26862686
var nilSentinel: NilSentinel {
26872687
switch self {
2688-
case .jsObject, .swiftProtocol:
2688+
case .swiftProtocol:
26892689
return .i32(0)
26902690
case .swiftHeapObject:
26912691
return .pointer

Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,7 +1655,7 @@ extension BridgeType {
16551655
}
16561656

16571657
switch wrappedType {
1658-
case .string, .integer, .float, .double, .jsObject, .swiftProtocol:
1658+
case .string, .integer, .float, .double, .swiftProtocol:
16591659
return true
16601660
case .rawValueEnum(_, let rawType):
16611661
switch rawType {
@@ -1666,7 +1666,7 @@ extension BridgeType {
16661666
default:
16671667
return false
16681668
}
1669-
case .bool, .caseEnum, .swiftHeapObject, .associatedValueEnum:
1669+
case .bool, .caseEnum, .swiftHeapObject, .associatedValueEnum, .jsObject:
16701670
return false
16711671
default:
16721672
return false

Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/Optionals.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,10 @@ func testMixedOptionals(firstName: String?, lastName: String?, age: Int?, active
155155
@JSSetter func setIntOrUndefined(_ value: JSUndefinedOr<Int>) throws(JSException)
156156
@JSFunction func roundTripIntOrNull(value: Int?) throws(JSException) -> Int?
157157
@JSFunction func roundTripIntOrUndefined(value: JSUndefinedOr<Int>) throws(JSException) -> JSUndefinedOr<Int>
158+
159+
@JSGetter var childOrNull: WithOptionalJSClass?
160+
@JSSetter func setChildOrNull(_ value: WithOptionalJSClass?) throws(JSException)
161+
@JSFunction func roundTripChildOrNull(
162+
value: WithOptionalJSClass?
163+
) throws(JSException) -> WithOptionalJSClass?
158164
}

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,20 @@
11581158
"_1" : "undefined"
11591159
}
11601160
}
1161+
},
1162+
{
1163+
"accessLevel" : "internal",
1164+
"name" : "childOrNull",
1165+
"type" : {
1166+
"nullable" : {
1167+
"_0" : {
1168+
"jsObject" : {
1169+
"_0" : "WithOptionalJSClass"
1170+
}
1171+
},
1172+
"_1" : "null"
1173+
}
1174+
}
11611175
}
11621176
],
11631177
"methods" : [
@@ -1444,6 +1458,40 @@
14441458
"_1" : "undefined"
14451459
}
14461460
}
1461+
},
1462+
{
1463+
"accessLevel" : "internal",
1464+
"effects" : {
1465+
"isAsync" : false,
1466+
"isStatic" : false,
1467+
"isThrows" : true
1468+
},
1469+
"name" : "roundTripChildOrNull",
1470+
"parameters" : [
1471+
{
1472+
"name" : "value",
1473+
"type" : {
1474+
"nullable" : {
1475+
"_0" : {
1476+
"jsObject" : {
1477+
"_0" : "WithOptionalJSClass"
1478+
}
1479+
},
1480+
"_1" : "null"
1481+
}
1482+
}
1483+
}
1484+
],
1485+
"returnType" : {
1486+
"nullable" : {
1487+
"_0" : {
1488+
"jsObject" : {
1489+
"_0" : "WithOptionalJSClass"
1490+
}
1491+
},
1492+
"_1" : "null"
1493+
}
1494+
}
14471495
}
14481496
],
14491497
"name" : "WithOptionalJSClass",
@@ -1573,6 +1621,21 @@
15731621
"_1" : "undefined"
15741622
}
15751623
}
1624+
},
1625+
{
1626+
"accessLevel" : "internal",
1627+
"functionName" : "childOrNull_set",
1628+
"name" : "childOrNull",
1629+
"type" : {
1630+
"nullable" : {
1631+
"_0" : {
1632+
"jsObject" : {
1633+
"_0" : "WithOptionalJSClass"
1634+
}
1635+
},
1636+
"_1" : "null"
1637+
}
1638+
}
15761639
}
15771640
],
15781641
"staticMethods" : [

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.swift

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,18 @@ fileprivate func bjs_WithOptionalJSClass_intOrUndefined_get_extern(_ self: Int32
526526
return bjs_WithOptionalJSClass_intOrUndefined_get_extern(self)
527527
}
528528

529+
#if arch(wasm32)
530+
@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_childOrNull_get")
531+
fileprivate func bjs_WithOptionalJSClass_childOrNull_get_extern(_ self: Int32) -> Void
532+
#else
533+
fileprivate func bjs_WithOptionalJSClass_childOrNull_get_extern(_ self: Int32) -> Void {
534+
fatalError("Only available on WebAssembly")
535+
}
536+
#endif
537+
@inline(never) fileprivate func bjs_WithOptionalJSClass_childOrNull_get(_ self: Int32) -> Void {
538+
return bjs_WithOptionalJSClass_childOrNull_get_extern(self)
539+
}
540+
529541
#if arch(wasm32)
530542
@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_stringOrNull_set")
531543
fileprivate func bjs_WithOptionalJSClass_stringOrNull_set_extern(_ self: Int32, _ newValueIsSome: Int32, _ newValueBytes: Int32, _ newValueLength: Int32) -> Void
@@ -622,6 +634,18 @@ fileprivate func bjs_WithOptionalJSClass_intOrUndefined_set_extern(_ self: Int32
622634
return bjs_WithOptionalJSClass_intOrUndefined_set_extern(self, newValueIsSome, newValueValue)
623635
}
624636

637+
#if arch(wasm32)
638+
@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_childOrNull_set")
639+
fileprivate func bjs_WithOptionalJSClass_childOrNull_set_extern(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Int32) -> Void
640+
#else
641+
fileprivate func bjs_WithOptionalJSClass_childOrNull_set_extern(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Int32) -> Void {
642+
fatalError("Only available on WebAssembly")
643+
}
644+
#endif
645+
@inline(never) fileprivate func bjs_WithOptionalJSClass_childOrNull_set(_ self: Int32, _ newValueIsSome: Int32, _ newValueValue: Int32) -> Void {
646+
return bjs_WithOptionalJSClass_childOrNull_set_extern(self, newValueIsSome, newValueValue)
647+
}
648+
625649
#if arch(wasm32)
626650
@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_roundTripStringOrNull")
627651
fileprivate func bjs_WithOptionalJSClass_roundTripStringOrNull_extern(_ self: Int32, _ valueIsSome: Int32, _ valueBytes: Int32, _ valueLength: Int32) -> Void
@@ -718,6 +742,18 @@ fileprivate func bjs_WithOptionalJSClass_roundTripIntOrUndefined_extern(_ self:
718742
return bjs_WithOptionalJSClass_roundTripIntOrUndefined_extern(self, valueIsSome, valueValue)
719743
}
720744

745+
#if arch(wasm32)
746+
@_extern(wasm, module: "TestModule", name: "bjs_WithOptionalJSClass_roundTripChildOrNull")
747+
fileprivate func bjs_WithOptionalJSClass_roundTripChildOrNull_extern(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Int32) -> Void
748+
#else
749+
fileprivate func bjs_WithOptionalJSClass_roundTripChildOrNull_extern(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Int32) -> Void {
750+
fatalError("Only available on WebAssembly")
751+
}
752+
#endif
753+
@inline(never) fileprivate func bjs_WithOptionalJSClass_roundTripChildOrNull(_ self: Int32, _ valueIsSome: Int32, _ valueValue: Int32) -> Void {
754+
return bjs_WithOptionalJSClass_roundTripChildOrNull_extern(self, valueIsSome, valueValue)
755+
}
756+
721757
func _$WithOptionalJSClass_init(_ valueOrNull: Optional<String>, _ valueOrUndefined: JSUndefinedOr<String>) throws(JSException) -> JSObject {
722758
let ret0 = valueOrNull.bridgeJSWithLoweredParameter { (valueOrNullIsSome, valueOrNullBytes, valueOrNullLength) in
723759
let ret1 = valueOrUndefined.bridgeJSWithLoweredParameter { (valueOrUndefinedIsSome, valueOrUndefinedBytes, valueOrUndefinedLength) in
@@ -805,6 +841,15 @@ func _$WithOptionalJSClass_intOrUndefined_get(_ self: JSObject) throws(JSExcepti
805841
return JSUndefinedOr<Int>.bridgeJSLiftReturnFromSideChannel()
806842
}
807843

844+
func _$WithOptionalJSClass_childOrNull_get(_ self: JSObject) throws(JSException) -> Optional<WithOptionalJSClass> {
845+
let selfValue = self.bridgeJSLowerParameter()
846+
bjs_WithOptionalJSClass_childOrNull_get(selfValue)
847+
if let error = _swift_js_take_exception() {
848+
throw error
849+
}
850+
return Optional<WithOptionalJSClass>.bridgeJSLiftReturn()
851+
}
852+
808853
func _$WithOptionalJSClass_stringOrNull_set(_ self: JSObject, _ newValue: Optional<String>) throws(JSException) -> Void {
809854
let selfValue = self.bridgeJSLowerParameter()
810855
newValue.bridgeJSWithLoweredParameter { (newValueIsSome, newValueBytes, newValueLength) in
@@ -879,6 +924,15 @@ func _$WithOptionalJSClass_intOrUndefined_set(_ self: JSObject, _ newValue: JSUn
879924
}
880925
}
881926

927+
func _$WithOptionalJSClass_childOrNull_set(_ self: JSObject, _ newValue: Optional<WithOptionalJSClass>) throws(JSException) -> Void {
928+
let selfValue = self.bridgeJSLowerParameter()
929+
let (newValueIsSome, newValueValue) = newValue.bridgeJSLowerParameter()
930+
bjs_WithOptionalJSClass_childOrNull_set(selfValue, newValueIsSome, newValueValue)
931+
if let error = _swift_js_take_exception() {
932+
throw error
933+
}
934+
}
935+
882936
func _$WithOptionalJSClass_roundTripStringOrNull(_ self: JSObject, _ value: Optional<String>) throws(JSException) -> Optional<String> {
883937
let selfValue = self.bridgeJSLowerParameter()
884938
value.bridgeJSWithLoweredParameter { (valueIsSome, valueBytes, valueLength) in
@@ -959,4 +1013,14 @@ func _$WithOptionalJSClass_roundTripIntOrUndefined(_ self: JSObject, _ value: JS
9591013
throw error
9601014
}
9611015
return JSUndefinedOr<Int>.bridgeJSLiftReturnFromSideChannel()
1016+
}
1017+
1018+
func _$WithOptionalJSClass_roundTripChildOrNull(_ self: JSObject, _ value: Optional<WithOptionalJSClass>) throws(JSException) -> Optional<WithOptionalJSClass> {
1019+
let selfValue = self.bridgeJSLowerParameter()
1020+
let (valueIsSome, valueValue) = value.bridgeJSLowerParameter()
1021+
bjs_WithOptionalJSClass_roundTripChildOrNull(selfValue, valueIsSome, valueValue)
1022+
if let error = _swift_js_take_exception() {
1023+
throw error
1024+
}
1025+
return Optional<WithOptionalJSClass>.bridgeJSLiftReturn()
9621026
}

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export interface WithOptionalJSClass {
3030
roundTripBoolOrUndefined(value: boolean | undefined): boolean | undefined;
3131
roundTripIntOrNull(value: number | null): number | null;
3232
roundTripIntOrUndefined(value: number | undefined): number | undefined;
33+
roundTripChildOrNull(value: WithOptionalJSClass | null): WithOptionalJSClass | null;
3334
stringOrNull: string | null;
3435
stringOrUndefined: string | undefined;
3536
doubleOrNull: number | null;
@@ -38,6 +39,7 @@ export interface WithOptionalJSClass {
3839
boolOrUndefined: boolean | undefined;
3940
intOrNull: number | null;
4041
intOrUndefined: number | undefined;
42+
childOrNull: WithOptionalJSClass | null;
4143
}
4244
export type Exports = {
4345
Greeter: {

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Optionals.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,19 @@ export async function createInstantiator(options, swift) {
296296
setException(error);
297297
}
298298
}
299+
TestModule["bjs_WithOptionalJSClass_childOrNull_get"] = function bjs_WithOptionalJSClass_childOrNull_get(self) {
300+
try {
301+
let ret = swift.memory.getObject(self).childOrNull;
302+
const isSome = ret != null;
303+
if (isSome) {
304+
const objId = swift.memory.retain(ret);
305+
i32Stack.push(objId);
306+
}
307+
i32Stack.push(isSome ? 1 : 0);
308+
} catch (error) {
309+
setException(error);
310+
}
311+
}
299312
TestModule["bjs_WithOptionalJSClass_stringOrNull_set"] = function bjs_WithOptionalJSClass_stringOrNull_set(self, newValueIsSome, newValueBytes, newValueCount) {
300313
try {
301314
let optResult;
@@ -366,6 +379,22 @@ export async function createInstantiator(options, swift) {
366379
setException(error);
367380
}
368381
}
382+
TestModule["bjs_WithOptionalJSClass_childOrNull_set"] = function bjs_WithOptionalJSClass_childOrNull_set(self, newValue) {
383+
try {
384+
let optResult;
385+
if (newValue) {
386+
const objId = i32Stack.pop();
387+
const obj = swift.memory.getObject(objId);
388+
swift.memory.release(objId);
389+
optResult = obj;
390+
} else {
391+
optResult = null;
392+
}
393+
swift.memory.getObject(self).childOrNull = optResult;
394+
} catch (error) {
395+
setException(error);
396+
}
397+
}
369398
TestModule["bjs_WithOptionalJSClass_roundTripStringOrNull"] = function bjs_WithOptionalJSClass_roundTripStringOrNull(self, valueIsSome, valueBytes, valueCount) {
370399
try {
371400
let optResult;
@@ -452,6 +481,28 @@ export async function createInstantiator(options, swift) {
452481
setException(error);
453482
}
454483
}
484+
TestModule["bjs_WithOptionalJSClass_roundTripChildOrNull"] = function bjs_WithOptionalJSClass_roundTripChildOrNull(self, value) {
485+
try {
486+
let optResult;
487+
if (value) {
488+
const objId = i32Stack.pop();
489+
const obj = swift.memory.getObject(objId);
490+
swift.memory.release(objId);
491+
optResult = obj;
492+
} else {
493+
optResult = null;
494+
}
495+
let ret = swift.memory.getObject(self).roundTripChildOrNull(optResult);
496+
const isSome = ret != null;
497+
if (isSome) {
498+
const objId1 = swift.memory.retain(ret);
499+
i32Stack.push(objId1);
500+
}
501+
i32Stack.push(isSome ? 1 : 0);
502+
} catch (error) {
503+
setException(error);
504+
}
505+
}
455506
},
456507
setInstance: (i) => {
457508
instance = i;

Sources/JavaScriptKit/BridgeJSIntrinsics.swift

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1693,11 +1693,7 @@ extension _BridgedAsOptional where Wrapped == JSObject {
16931693
}
16941694

16951695
@_spi(BridgeJS) public consuming func bridgeJSLowerReturn() -> Void {
1696-
asOptional._bridgeJSLowerReturn(
1697-
noneValue: 0,
1698-
lowerWrapped: { $0.bridgeJSLowerReturn() },
1699-
write: _swift_js_return_optional_object
1700-
)
1696+
Wrapped.bridgeJSStackPushAsOptional(asOptional)
17011697
}
17021698
}
17031699

0 commit comments

Comments
 (0)