Skip to content

Commit 95e259c

Browse files
committed
pass unretained strings as tuple
1 parent 3eff48a commit 95e259c

File tree

24 files changed

+657
-472
lines changed

24 files changed

+657
-472
lines changed

Benchmarks/Sources/Generated/BridgeJS.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1750,20 +1750,21 @@ func _$benchmarkHelperNoopWithNumber(_ n: Double) throws(JSException) -> Void {
17501750

17511751
#if arch(wasm32)
17521752
@_extern(wasm, module: "Benchmarks", name: "bjs_benchmarkRunner")
1753-
fileprivate func bjs_benchmarkRunner_extern(_ name: Int32, _ body: Int32) -> Void
1753+
fileprivate func bjs_benchmarkRunner_extern(_ nameBytes: Int32, _ nameLength: Int32, _ body: Int32) -> Void
17541754
#else
1755-
fileprivate func bjs_benchmarkRunner_extern(_ name: Int32, _ body: Int32) -> Void {
1755+
fileprivate func bjs_benchmarkRunner_extern(_ nameBytes: Int32, _ nameLength: Int32, _ body: Int32) -> Void {
17561756
fatalError("Only available on WebAssembly")
17571757
}
17581758
#endif
1759-
@inline(never) fileprivate func bjs_benchmarkRunner(_ name: Int32, _ body: Int32) -> Void {
1760-
return bjs_benchmarkRunner_extern(name, body)
1759+
@inline(never) fileprivate func bjs_benchmarkRunner(_ nameBytes: Int32, _ nameLength: Int32, _ body: Int32) -> Void {
1760+
return bjs_benchmarkRunner_extern(nameBytes, nameLength, body)
17611761
}
17621762

17631763
func _$benchmarkRunner(_ name: String, _ body: JSObject) throws(JSException) -> Void {
1764-
let nameValue = name.bridgeJSLowerParameter()
17651764
let bodyValue = body.bridgeJSLowerParameter()
1766-
bjs_benchmarkRunner(nameValue, bodyValue)
1765+
_swift_js_with_borrowed_utf8(name) { nameBytes, nameLength in
1766+
bjs_benchmarkRunner(nameBytes, nameLength, bodyValue)
1767+
}
17671768
if let error = _swift_js_take_exception() {
17681769
throw error
17691770
}

Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/BridgeJS.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -249,20 +249,21 @@ func _$createTS2Swift() throws(JSException) -> TS2Swift {
249249

250250
#if arch(wasm32)
251251
@_extern(wasm, module: "PlayBridgeJS", name: "bjs_TS2Swift_convert")
252-
fileprivate func bjs_TS2Swift_convert_extern(_ self: Int32, _ ts: Int32) -> Int32
252+
fileprivate func bjs_TS2Swift_convert_extern(_ self: Int32, _ tsBytes: Int32, _ tsLength: Int32) -> Int32
253253
#else
254-
fileprivate func bjs_TS2Swift_convert_extern(_ self: Int32, _ ts: Int32) -> Int32 {
254+
fileprivate func bjs_TS2Swift_convert_extern(_ self: Int32, _ tsBytes: Int32, _ tsLength: Int32) -> Int32 {
255255
fatalError("Only available on WebAssembly")
256256
}
257257
#endif
258-
@inline(never) fileprivate func bjs_TS2Swift_convert(_ self: Int32, _ ts: Int32) -> Int32 {
259-
return bjs_TS2Swift_convert_extern(self, ts)
258+
@inline(never) fileprivate func bjs_TS2Swift_convert(_ self: Int32, _ tsBytes: Int32, _ tsLength: Int32) -> Int32 {
259+
return bjs_TS2Swift_convert_extern(self, tsBytes, tsLength)
260260
}
261261

262262
func _$TS2Swift_convert(_ self: JSObject, _ ts: String) throws(JSException) -> String {
263263
let selfValue = self.bridgeJSLowerParameter()
264-
let tsValue = ts.bridgeJSLowerParameter()
265-
let ret = bjs_TS2Swift_convert(selfValue, tsValue)
264+
let ret = _swift_js_with_borrowed_utf8(ts) { tsBytes, tsLength in
265+
bjs_TS2Swift_convert(selfValue, tsBytes, tsLength)
266+
}
266267
if let error = _swift_js_take_exception() {
267268
throw error
268269
}

Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift

Lines changed: 86 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ public struct ImportTS {
8181
}
8282

8383
class CallJSEmission {
84+
private struct BorrowedStringArgument {
85+
let expression: String
86+
let forwardedParameterNames: [String]
87+
let isOptional: Bool
88+
}
89+
8490
let abiName: String
8591
let moduleName: String
8692
let context: BridgeContext
@@ -95,13 +101,35 @@ public struct ImportTS {
95101
var stackLoweringStmts: [String] = []
96102
// Values to extend lifetime during call
97103
var valuesToExtendLifetimeDuringCall: [String] = []
104+
// String-like parameters lowered through borrowed UTF-8 wrappers at call site.
105+
private var borrowedStringArguments: [BorrowedStringArgument] = []
98106

99107
init(moduleName: String, abiName: String, context: BridgeContext = .importTS) {
100108
self.moduleName = moduleName
101109
self.abiName = abiName
102110
self.context = context
103111
}
104112

113+
private func borrowedStringInfo(for param: Parameter) -> (expression: String, isOptional: Bool)? {
114+
switch param.type {
115+
case .string:
116+
return (param.name, false)
117+
case .rawValueEnum(_, .string):
118+
return ("\(param.name).rawValue", false)
119+
case .nullable(let wrappedType, _):
120+
switch wrappedType {
121+
case .string:
122+
return ("\(param.name).asOptional", true)
123+
case .rawValueEnum(_, .string):
124+
return ("\(param.name).asOptional?.rawValue", true)
125+
default:
126+
return nil
127+
}
128+
default:
129+
return nil
130+
}
131+
}
132+
105133
func lowerParameter(param: Parameter) throws {
106134
let loweringInfo = try param.type.loweringParameterInfo(context: context)
107135

@@ -115,6 +143,35 @@ public struct ImportTS {
115143
default:
116144
break
117145
}
146+
147+
if let borrowed = borrowedStringInfo(for: param) {
148+
let destructuredNames = loweringInfo.loweredParameters.map {
149+
"\(param.name)\($0.name.capitalizedFirstLetter)"
150+
}
151+
if loweringInfo.loweredParameters.count != destructuredNames.count || destructuredNames.isEmpty {
152+
throw BridgeJSCoreError("Unexpected borrowed string lowering shape for parameter \(param.name)")
153+
}
154+
155+
for (index, (paramName, type)) in loweringInfo.loweredParameters.enumerated() {
156+
let abiParamName: String
157+
if loweringInfo.loweredParameters.count == 1 {
158+
abiParamName = param.name
159+
} else {
160+
abiParamName = "\(param.name)\(paramName.capitalizedFirstLetter)"
161+
}
162+
abiParameterSignatures.append((abiParamName, type))
163+
abiParameterForwardings.append(destructuredNames[index])
164+
}
165+
borrowedStringArguments.append(
166+
BorrowedStringArgument(
167+
expression: borrowed.expression,
168+
forwardedParameterNames: destructuredNames,
169+
isOptional: borrowed.isOptional
170+
)
171+
)
172+
return
173+
}
174+
118175
let initializerExpr = ExprSyntax("\(raw: param.name).bridgeJSLowerParameter()")
119176

120177
if loweringInfo.loweredParameters.isEmpty {
@@ -164,19 +221,35 @@ public struct ImportTS {
164221
let assign =
165222
(returnType == .void || returnType.usesSideChannelForOptionalReturn() || liftingInfo.valueToLift == nil)
166223
? "" : "let ret = "
167-
let callExpr = "\(abiName)(\(abiParameterForwardings.joined(separator: ", ")))"
224+
var callExpr = "\(abiName)(\(abiParameterForwardings.joined(separator: ", ")))"
168225

169226
if !valuesToExtendLifetimeDuringCall.isEmpty {
170-
body.write(
171-
"\(assign)withExtendedLifetime((\(valuesToExtendLifetimeDuringCall.joined(separator: ", ")))) {"
172-
)
173-
body.indent {
174-
body.write(callExpr)
227+
callExpr =
228+
"withExtendedLifetime((\(valuesToExtendLifetimeDuringCall.joined(separator: ", ")))) { \(callExpr) }"
229+
}
230+
231+
if !borrowedStringArguments.isEmpty {
232+
for argument in borrowedStringArguments.reversed() {
233+
if argument.isOptional {
234+
guard argument.forwardedParameterNames.count == 3 else {
235+
throw BridgeJSCoreError(
236+
"Optional borrowed string argument must have 3 lowered values: \(argument.expression)"
237+
)
238+
}
239+
callExpr =
240+
"_swift_js_with_optional_borrowed_utf8(\(argument.expression)) { \(argument.forwardedParameterNames[0]), \(argument.forwardedParameterNames[1]), \(argument.forwardedParameterNames[2]) in \(callExpr) }"
241+
} else {
242+
guard argument.forwardedParameterNames.count == 2 else {
243+
throw BridgeJSCoreError(
244+
"Borrowed string argument must have 2 lowered values: \(argument.expression)"
245+
)
246+
}
247+
callExpr =
248+
"_swift_js_with_borrowed_utf8(\(argument.expression)) { \(argument.forwardedParameterNames[0]), \(argument.forwardedParameterNames[1]) in \(callExpr) }"
249+
}
175250
}
176-
body.write("}")
177-
} else {
178-
body.write("\(assign)\(callExpr)")
179251
}
252+
body.write("\(assign)\(callExpr)")
180253

181254
// Add exception check for ImportTS context
182255
if context == .importTS {
@@ -724,7 +797,7 @@ extension BridgeType {
724797
static let int = LoweringParameterInfo(loweredParameters: [("value", .i32)])
725798
static let float = LoweringParameterInfo(loweredParameters: [("value", .f32)])
726799
static let double = LoweringParameterInfo(loweredParameters: [("value", .f64)])
727-
static let string = LoweringParameterInfo(loweredParameters: [("value", .i32)])
800+
static let string = LoweringParameterInfo(loweredParameters: [("bytes", .i32), ("length", .i32)])
728801
static let jsObject = LoweringParameterInfo(loweredParameters: [("value", .i32)])
729802
static let jsValue = LoweringParameterInfo(loweredParameters: [
730803
("kind", .i32),
@@ -761,6 +834,9 @@ extension BridgeType {
761834
return LoweringParameterInfo(loweredParameters: [("value", .i32)])
762835
}
763836
case .rawValueEnum(_, let rawType):
837+
if rawType == .string {
838+
return .string
839+
}
764840
let wasmType = rawType.wasmCoreType ?? .i32
765841
return LoweringParameterInfo(loweredParameters: [("value", wasmType)])
766842
case .associatedValueEnum:

Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -261,17 +261,24 @@ struct IntrinsicJSFragment: Sendable {
261261
}
262262
)
263263
static let stringLiftParameter = IntrinsicJSFragment(
264-
parameters: ["objectId"],
264+
parameters: ["bytes", "count"],
265265
printCode: { arguments, context in
266266
let (scope, printer) = (context.scope, context.printer)
267-
let objectId = arguments[0]
268-
let objectLabel = scope.variable("\(objectId)Object")
269-
// TODO: Implement "take" operation
267+
let bytesExpr = arguments[0]
268+
let countExpr = arguments[1]
269+
let bytesLabel = scope.variable("bytesView")
270+
let bytesToDecodeLabel = scope.variable("bytesToDecode")
271+
let stringLabel = scope.variable("string")
272+
printer.write(
273+
"const \(bytesLabel) = new Uint8Array(\(JSGlueVariableScope.reservedMemory).buffer, \(bytesExpr), \(countExpr));"
274+
)
275+
printer.write(
276+
"const \(bytesToDecodeLabel) = (typeof SharedArrayBuffer !== \"undefined\" && \(bytesLabel).buffer instanceof SharedArrayBuffer) ? \(bytesLabel).slice() : \(bytesLabel);"
277+
)
270278
printer.write(
271-
"const \(objectLabel) = \(JSGlueVariableScope.reservedSwift).memory.getObject(\(objectId));"
279+
"const \(stringLabel) = \(JSGlueVariableScope.reservedTextDecoder).decode(\(bytesToDecodeLabel));"
272280
)
273-
printer.write("\(JSGlueVariableScope.reservedSwift).memory.release(\(objectId));")
274-
return [objectLabel]
281+
return [stringLabel]
275282
}
276283
)
277284
static let stringLowerReturn = IntrinsicJSFragment(

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -453,19 +453,20 @@ public func _bjs_validateSession(_ session: Int32) -> Void {
453453

454454
#if arch(wasm32)
455455
@_extern(wasm, module: "TestModule", name: "bjs_takesFeatureFlag")
456-
fileprivate func bjs_takesFeatureFlag_extern(_ flag: Int32) -> Void
456+
fileprivate func bjs_takesFeatureFlag_extern(_ flagBytes: Int32, _ flagLength: Int32) -> Void
457457
#else
458-
fileprivate func bjs_takesFeatureFlag_extern(_ flag: Int32) -> Void {
458+
fileprivate func bjs_takesFeatureFlag_extern(_ flagBytes: Int32, _ flagLength: Int32) -> Void {
459459
fatalError("Only available on WebAssembly")
460460
}
461461
#endif
462-
@inline(never) fileprivate func bjs_takesFeatureFlag(_ flag: Int32) -> Void {
463-
return bjs_takesFeatureFlag_extern(flag)
462+
@inline(never) fileprivate func bjs_takesFeatureFlag(_ flagBytes: Int32, _ flagLength: Int32) -> Void {
463+
return bjs_takesFeatureFlag_extern(flagBytes, flagLength)
464464
}
465465

466466
func _$takesFeatureFlag(_ flag: FeatureFlag) throws(JSException) -> Void {
467-
let flagValue = flag.bridgeJSLowerParameter()
468-
bjs_takesFeatureFlag(flagValue)
467+
_swift_js_with_borrowed_utf8(flag.rawValue) { flagBytes, flagLength in
468+
bjs_takesFeatureFlag(flagBytes, flagLength)
469+
}
469470
if let error = _swift_js_take_exception() {
470471
throw error
471472
}

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,21 @@ func _$console_get() throws(JSException) -> JSConsole {
2020

2121
#if arch(wasm32)
2222
@_extern(wasm, module: "TestModule", name: "bjs_JSConsole_log")
23-
fileprivate func bjs_JSConsole_log_extern(_ self: Int32, _ message: Int32) -> Void
23+
fileprivate func bjs_JSConsole_log_extern(_ self: Int32, _ messageBytes: Int32, _ messageLength: Int32) -> Void
2424
#else
25-
fileprivate func bjs_JSConsole_log_extern(_ self: Int32, _ message: Int32) -> Void {
25+
fileprivate func bjs_JSConsole_log_extern(_ self: Int32, _ messageBytes: Int32, _ messageLength: Int32) -> Void {
2626
fatalError("Only available on WebAssembly")
2727
}
2828
#endif
29-
@inline(never) fileprivate func bjs_JSConsole_log(_ self: Int32, _ message: Int32) -> Void {
30-
return bjs_JSConsole_log_extern(self, message)
29+
@inline(never) fileprivate func bjs_JSConsole_log(_ self: Int32, _ messageBytes: Int32, _ messageLength: Int32) -> Void {
30+
return bjs_JSConsole_log_extern(self, messageBytes, messageLength)
3131
}
3232

3333
func _$JSConsole_log(_ self: JSObject, _ message: String) throws(JSException) -> Void {
3434
let selfValue = self.bridgeJSLowerParameter()
35-
let messageValue = message.bridgeJSLowerParameter()
36-
bjs_JSConsole_log(selfValue, messageValue)
35+
_swift_js_with_borrowed_utf8(message) { messageBytes, messageLength in
36+
bjs_JSConsole_log(selfValue, messageBytes, messageLength)
37+
}
3738
if let error = _swift_js_take_exception() {
3839
throw error
3940
}

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

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,20 @@ func _$console_get() throws(JSException) -> JSConsole {
2020

2121
#if arch(wasm32)
2222
@_extern(wasm, module: "TestModule", name: "bjs_parseInt")
23-
fileprivate func bjs_parseInt_extern(_ string: Int32) -> Float64
23+
fileprivate func bjs_parseInt_extern(_ stringBytes: Int32, _ stringLength: Int32) -> Float64
2424
#else
25-
fileprivate func bjs_parseInt_extern(_ string: Int32) -> Float64 {
25+
fileprivate func bjs_parseInt_extern(_ stringBytes: Int32, _ stringLength: Int32) -> Float64 {
2626
fatalError("Only available on WebAssembly")
2727
}
2828
#endif
29-
@inline(never) fileprivate func bjs_parseInt(_ string: Int32) -> Float64 {
30-
return bjs_parseInt_extern(string)
29+
@inline(never) fileprivate func bjs_parseInt(_ stringBytes: Int32, _ stringLength: Int32) -> Float64 {
30+
return bjs_parseInt_extern(stringBytes, stringLength)
3131
}
3232

3333
func _$parseInt(_ string: String) throws(JSException) -> Double {
34-
let stringValue = string.bridgeJSLowerParameter()
35-
let ret = bjs_parseInt(stringValue)
34+
let ret = _swift_js_with_borrowed_utf8(string) { stringBytes, stringLength in
35+
bjs_parseInt(stringBytes, stringLength)
36+
}
3637
if let error = _swift_js_take_exception() {
3738
throw error
3839
}
@@ -41,35 +42,36 @@ func _$parseInt(_ string: String) throws(JSException) -> Double {
4142

4243
#if arch(wasm32)
4344
@_extern(wasm, module: "TestModule", name: "bjs_JSConsole_log")
44-
fileprivate func bjs_JSConsole_log_extern(_ self: Int32, _ message: Int32) -> Void
45+
fileprivate func bjs_JSConsole_log_extern(_ self: Int32, _ messageBytes: Int32, _ messageLength: Int32) -> Void
4546
#else
46-
fileprivate func bjs_JSConsole_log_extern(_ self: Int32, _ message: Int32) -> Void {
47+
fileprivate func bjs_JSConsole_log_extern(_ self: Int32, _ messageBytes: Int32, _ messageLength: Int32) -> Void {
4748
fatalError("Only available on WebAssembly")
4849
}
4950
#endif
50-
@inline(never) fileprivate func bjs_JSConsole_log(_ self: Int32, _ message: Int32) -> Void {
51-
return bjs_JSConsole_log_extern(self, message)
51+
@inline(never) fileprivate func bjs_JSConsole_log(_ self: Int32, _ messageBytes: Int32, _ messageLength: Int32) -> Void {
52+
return bjs_JSConsole_log_extern(self, messageBytes, messageLength)
5253
}
5354

5455
func _$JSConsole_log(_ self: JSObject, _ message: String) throws(JSException) -> Void {
5556
let selfValue = self.bridgeJSLowerParameter()
56-
let messageValue = message.bridgeJSLowerParameter()
57-
bjs_JSConsole_log(selfValue, messageValue)
57+
_swift_js_with_borrowed_utf8(message) { messageBytes, messageLength in
58+
bjs_JSConsole_log(selfValue, messageBytes, messageLength)
59+
}
5860
if let error = _swift_js_take_exception() {
5961
throw error
6062
}
6163
}
6264

6365
#if arch(wasm32)
6466
@_extern(wasm, module: "TestModule", name: "bjs_WebSocket_init")
65-
fileprivate func bjs_WebSocket_init_extern(_ url: Int32) -> Int32
67+
fileprivate func bjs_WebSocket_init_extern(_ urlBytes: Int32, _ urlLength: Int32) -> Int32
6668
#else
67-
fileprivate func bjs_WebSocket_init_extern(_ url: Int32) -> Int32 {
69+
fileprivate func bjs_WebSocket_init_extern(_ urlBytes: Int32, _ urlLength: Int32) -> Int32 {
6870
fatalError("Only available on WebAssembly")
6971
}
7072
#endif
71-
@inline(never) fileprivate func bjs_WebSocket_init(_ url: Int32) -> Int32 {
72-
return bjs_WebSocket_init_extern(url)
73+
@inline(never) fileprivate func bjs_WebSocket_init(_ urlBytes: Int32, _ urlLength: Int32) -> Int32 {
74+
return bjs_WebSocket_init_extern(urlBytes, urlLength)
7375
}
7476

7577
#if arch(wasm32)
@@ -85,8 +87,9 @@ fileprivate func bjs_WebSocket_close_extern(_ self: Int32) -> Void {
8587
}
8688

8789
func _$WebSocket_init(_ url: String) throws(JSException) -> JSObject {
88-
let urlValue = url.bridgeJSLowerParameter()
89-
let ret = bjs_WebSocket_init(urlValue)
90+
let ret = _swift_js_with_borrowed_utf8(url) { urlBytes, urlLength in
91+
bjs_WebSocket_init(urlBytes, urlLength)
92+
}
9093
if let error = _swift_js_take_exception() {
9194
throw error
9295
}

0 commit comments

Comments
 (0)