Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions Benchmarks/Sources/Generated/BridgeJS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1750,20 +1750,21 @@ func _$benchmarkHelperNoopWithNumber(_ n: Double) throws(JSException) -> Void {

#if arch(wasm32)
@_extern(wasm, module: "Benchmarks", name: "bjs_benchmarkRunner")
fileprivate func bjs_benchmarkRunner_extern(_ name: Int32, _ body: Int32) -> Void
fileprivate func bjs_benchmarkRunner_extern(_ nameBytes: Int32, _ nameLength: Int32, _ body: Int32) -> Void
#else
fileprivate func bjs_benchmarkRunner_extern(_ name: Int32, _ body: Int32) -> Void {
fileprivate func bjs_benchmarkRunner_extern(_ nameBytes: Int32, _ nameLength: Int32, _ body: Int32) -> Void {
fatalError("Only available on WebAssembly")
}
#endif
@inline(never) fileprivate func bjs_benchmarkRunner(_ name: Int32, _ body: Int32) -> Void {
return bjs_benchmarkRunner_extern(name, body)
@inline(never) fileprivate func bjs_benchmarkRunner(_ nameBytes: Int32, _ nameLength: Int32, _ body: Int32) -> Void {
return bjs_benchmarkRunner_extern(nameBytes, nameLength, body)
}

func _$benchmarkRunner(_ name: String, _ body: JSObject) throws(JSException) -> Void {
let nameValue = name.bridgeJSLowerParameter()
let bodyValue = body.bridgeJSLowerParameter()
bjs_benchmarkRunner(nameValue, bodyValue)
_swift_js_with_borrowed_utf8(name) { nameBytes, nameLength in
bjs_benchmarkRunner(nameBytes, nameLength, bodyValue)
}
if let error = _swift_js_take_exception() {
throw error
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,20 +249,21 @@ func _$createTS2Swift() throws(JSException) -> TS2Swift {

#if arch(wasm32)
@_extern(wasm, module: "PlayBridgeJS", name: "bjs_TS2Swift_convert")
fileprivate func bjs_TS2Swift_convert_extern(_ self: Int32, _ ts: Int32) -> Int32
fileprivate func bjs_TS2Swift_convert_extern(_ self: Int32, _ tsBytes: Int32, _ tsLength: Int32) -> Int32
#else
fileprivate func bjs_TS2Swift_convert_extern(_ self: Int32, _ ts: Int32) -> Int32 {
fileprivate func bjs_TS2Swift_convert_extern(_ self: Int32, _ tsBytes: Int32, _ tsLength: Int32) -> Int32 {
fatalError("Only available on WebAssembly")
}
#endif
@inline(never) fileprivate func bjs_TS2Swift_convert(_ self: Int32, _ ts: Int32) -> Int32 {
return bjs_TS2Swift_convert_extern(self, ts)
@inline(never) fileprivate func bjs_TS2Swift_convert(_ self: Int32, _ tsBytes: Int32, _ tsLength: Int32) -> Int32 {
return bjs_TS2Swift_convert_extern(self, tsBytes, tsLength)
}

func _$TS2Swift_convert(_ self: JSObject, _ ts: String) throws(JSException) -> String {
let selfValue = self.bridgeJSLowerParameter()
let tsValue = ts.bridgeJSLowerParameter()
let ret = bjs_TS2Swift_convert(selfValue, tsValue)
let ret = _swift_js_with_borrowed_utf8(ts) { tsBytes, tsLength in
bjs_TS2Swift_convert(selfValue, tsBytes, tsLength)
}
if let error = _swift_js_take_exception() {
throw error
}
Expand Down
96 changes: 86 additions & 10 deletions Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ public struct ImportTS {
}

class CallJSEmission {
private struct BorrowedStringArgument {
let expression: String
let forwardedParameterNames: [String]
let isOptional: Bool
}

let abiName: String
let moduleName: String
let context: BridgeContext
Expand All @@ -95,13 +101,35 @@ public struct ImportTS {
var stackLoweringStmts: [String] = []
// Values to extend lifetime during call
var valuesToExtendLifetimeDuringCall: [String] = []
// String-like parameters lowered through borrowed UTF-8 wrappers at call site.
private var borrowedStringArguments: [BorrowedStringArgument] = []

init(moduleName: String, abiName: String, context: BridgeContext = .importTS) {
self.moduleName = moduleName
self.abiName = abiName
self.context = context
}

private func borrowedStringInfo(for param: Parameter) -> (expression: String, isOptional: Bool)? {
switch param.type {
case .string:
return (param.name, false)
case .rawValueEnum(_, .string):
return ("\(param.name).rawValue", false)
case .nullable(let wrappedType, _):
switch wrappedType {
case .string:
return ("\(param.name).asOptional", true)
case .rawValueEnum(_, .string):
return ("\(param.name).asOptional?.rawValue", true)
default:
return nil
}
default:
return nil
}
}

func lowerParameter(param: Parameter) throws {
let loweringInfo = try param.type.loweringParameterInfo(context: context)

Expand All @@ -115,6 +143,35 @@ public struct ImportTS {
default:
break
}

if let borrowed = borrowedStringInfo(for: param) {
let destructuredNames = loweringInfo.loweredParameters.map {
"\(param.name)\($0.name.capitalizedFirstLetter)"
}
if loweringInfo.loweredParameters.count != destructuredNames.count || destructuredNames.isEmpty {
throw BridgeJSCoreError("Unexpected borrowed string lowering shape for parameter \(param.name)")
}

for (index, (paramName, type)) in loweringInfo.loweredParameters.enumerated() {
let abiParamName: String
if loweringInfo.loweredParameters.count == 1 {
abiParamName = param.name
} else {
abiParamName = "\(param.name)\(paramName.capitalizedFirstLetter)"
}
abiParameterSignatures.append((abiParamName, type))
abiParameterForwardings.append(destructuredNames[index])
}
borrowedStringArguments.append(
BorrowedStringArgument(
expression: borrowed.expression,
forwardedParameterNames: destructuredNames,
isOptional: borrowed.isOptional
)
)
return
}

let initializerExpr = ExprSyntax("\(raw: param.name).bridgeJSLowerParameter()")

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

if !valuesToExtendLifetimeDuringCall.isEmpty {
body.write(
"\(assign)withExtendedLifetime((\(valuesToExtendLifetimeDuringCall.joined(separator: ", ")))) {"
)
body.indent {
body.write(callExpr)
callExpr =
"withExtendedLifetime((\(valuesToExtendLifetimeDuringCall.joined(separator: ", ")))) { \(callExpr) }"
}

if !borrowedStringArguments.isEmpty {
for argument in borrowedStringArguments.reversed() {
if argument.isOptional {
guard argument.forwardedParameterNames.count == 3 else {
throw BridgeJSCoreError(
"Optional borrowed string argument must have 3 lowered values: \(argument.expression)"
)
}
callExpr =
"_swift_js_with_optional_borrowed_utf8(\(argument.expression)) { \(argument.forwardedParameterNames[0]), \(argument.forwardedParameterNames[1]), \(argument.forwardedParameterNames[2]) in \(callExpr) }"
} else {
guard argument.forwardedParameterNames.count == 2 else {
throw BridgeJSCoreError(
"Borrowed string argument must have 2 lowered values: \(argument.expression)"
)
}
callExpr =
"_swift_js_with_borrowed_utf8(\(argument.expression)) { \(argument.forwardedParameterNames[0]), \(argument.forwardedParameterNames[1]) in \(callExpr) }"
}
}
body.write("}")
} else {
body.write("\(assign)\(callExpr)")
}
body.write("\(assign)\(callExpr)")

// Add exception check for ImportTS context
if context == .importTS {
Expand Down Expand Up @@ -724,7 +797,7 @@ extension BridgeType {
static let int = LoweringParameterInfo(loweredParameters: [("value", .i32)])
static let float = LoweringParameterInfo(loweredParameters: [("value", .f32)])
static let double = LoweringParameterInfo(loweredParameters: [("value", .f64)])
static let string = LoweringParameterInfo(loweredParameters: [("value", .i32)])
static let string = LoweringParameterInfo(loweredParameters: [("bytes", .i32), ("length", .i32)])
static let jsObject = LoweringParameterInfo(loweredParameters: [("value", .i32)])
static let jsValue = LoweringParameterInfo(loweredParameters: [
("kind", .i32),
Expand Down Expand Up @@ -761,6 +834,9 @@ extension BridgeType {
return LoweringParameterInfo(loweredParameters: [("value", .i32)])
}
case .rawValueEnum(_, let rawType):
if rawType == .string {
return .string
}
let wasmType = rawType.wasmCoreType ?? .i32
return LoweringParameterInfo(loweredParameters: [("value", wasmType)])
case .associatedValueEnum:
Expand Down
21 changes: 14 additions & 7 deletions Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -261,17 +261,24 @@ struct IntrinsicJSFragment: Sendable {
}
)
static let stringLiftParameter = IntrinsicJSFragment(
parameters: ["objectId"],
parameters: ["bytes", "count"],
printCode: { arguments, context in
let (scope, printer) = (context.scope, context.printer)
let objectId = arguments[0]
let objectLabel = scope.variable("\(objectId)Object")
// TODO: Implement "take" operation
let bytesExpr = arguments[0]
let countExpr = arguments[1]
let bytesLabel = scope.variable("bytesView")
let bytesToDecodeLabel = scope.variable("bytesToDecode")
let stringLabel = scope.variable("string")
printer.write(
"const \(bytesLabel) = new Uint8Array(\(JSGlueVariableScope.reservedMemory).buffer, \(bytesExpr), \(countExpr));"
)
printer.write(
"const \(bytesToDecodeLabel) = (typeof SharedArrayBuffer !== \"undefined\" && \(bytesLabel).buffer instanceof SharedArrayBuffer) ? \(bytesLabel).slice() : \(bytesLabel);"
)
printer.write(
"const \(objectLabel) = \(JSGlueVariableScope.reservedSwift).memory.getObject(\(objectId));"
"const \(stringLabel) = \(JSGlueVariableScope.reservedTextDecoder).decode(\(bytesToDecodeLabel));"
)
printer.write("\(JSGlueVariableScope.reservedSwift).memory.release(\(objectId));")
return [objectLabel]
return [stringLabel]
}
)
static let stringLowerReturn = IntrinsicJSFragment(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -453,19 +453,20 @@ public func _bjs_validateSession(_ session: Int32) -> Void {

#if arch(wasm32)
@_extern(wasm, module: "TestModule", name: "bjs_takesFeatureFlag")
fileprivate func bjs_takesFeatureFlag_extern(_ flag: Int32) -> Void
fileprivate func bjs_takesFeatureFlag_extern(_ flagBytes: Int32, _ flagLength: Int32) -> Void
#else
fileprivate func bjs_takesFeatureFlag_extern(_ flag: Int32) -> Void {
fileprivate func bjs_takesFeatureFlag_extern(_ flagBytes: Int32, _ flagLength: Int32) -> Void {
fatalError("Only available on WebAssembly")
}
#endif
@inline(never) fileprivate func bjs_takesFeatureFlag(_ flag: Int32) -> Void {
return bjs_takesFeatureFlag_extern(flag)
@inline(never) fileprivate func bjs_takesFeatureFlag(_ flagBytes: Int32, _ flagLength: Int32) -> Void {
return bjs_takesFeatureFlag_extern(flagBytes, flagLength)
}

func _$takesFeatureFlag(_ flag: FeatureFlag) throws(JSException) -> Void {
let flagValue = flag.bridgeJSLowerParameter()
bjs_takesFeatureFlag(flagValue)
_swift_js_with_borrowed_utf8(flag.rawValue) { flagBytes, flagLength in
bjs_takesFeatureFlag(flagBytes, flagLength)
}
if let error = _swift_js_take_exception() {
throw error
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,21 @@ func _$console_get() throws(JSException) -> JSConsole {

#if arch(wasm32)
@_extern(wasm, module: "TestModule", name: "bjs_JSConsole_log")
fileprivate func bjs_JSConsole_log_extern(_ self: Int32, _ message: Int32) -> Void
fileprivate func bjs_JSConsole_log_extern(_ self: Int32, _ messageBytes: Int32, _ messageLength: Int32) -> Void
#else
fileprivate func bjs_JSConsole_log_extern(_ self: Int32, _ message: Int32) -> Void {
fileprivate func bjs_JSConsole_log_extern(_ self: Int32, _ messageBytes: Int32, _ messageLength: Int32) -> Void {
fatalError("Only available on WebAssembly")
}
#endif
@inline(never) fileprivate func bjs_JSConsole_log(_ self: Int32, _ message: Int32) -> Void {
return bjs_JSConsole_log_extern(self, message)
@inline(never) fileprivate func bjs_JSConsole_log(_ self: Int32, _ messageBytes: Int32, _ messageLength: Int32) -> Void {
return bjs_JSConsole_log_extern(self, messageBytes, messageLength)
}

func _$JSConsole_log(_ self: JSObject, _ message: String) throws(JSException) -> Void {
let selfValue = self.bridgeJSLowerParameter()
let messageValue = message.bridgeJSLowerParameter()
bjs_JSConsole_log(selfValue, messageValue)
_swift_js_with_borrowed_utf8(message) { messageBytes, messageLength in
bjs_JSConsole_log(selfValue, messageBytes, messageLength)
}
if let error = _swift_js_take_exception() {
throw error
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,20 @@ func _$console_get() throws(JSException) -> JSConsole {

#if arch(wasm32)
@_extern(wasm, module: "TestModule", name: "bjs_parseInt")
fileprivate func bjs_parseInt_extern(_ string: Int32) -> Float64
fileprivate func bjs_parseInt_extern(_ stringBytes: Int32, _ stringLength: Int32) -> Float64
#else
fileprivate func bjs_parseInt_extern(_ string: Int32) -> Float64 {
fileprivate func bjs_parseInt_extern(_ stringBytes: Int32, _ stringLength: Int32) -> Float64 {
fatalError("Only available on WebAssembly")
}
#endif
@inline(never) fileprivate func bjs_parseInt(_ string: Int32) -> Float64 {
return bjs_parseInt_extern(string)
@inline(never) fileprivate func bjs_parseInt(_ stringBytes: Int32, _ stringLength: Int32) -> Float64 {
return bjs_parseInt_extern(stringBytes, stringLength)
}

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

#if arch(wasm32)
@_extern(wasm, module: "TestModule", name: "bjs_JSConsole_log")
fileprivate func bjs_JSConsole_log_extern(_ self: Int32, _ message: Int32) -> Void
fileprivate func bjs_JSConsole_log_extern(_ self: Int32, _ messageBytes: Int32, _ messageLength: Int32) -> Void
#else
fileprivate func bjs_JSConsole_log_extern(_ self: Int32, _ message: Int32) -> Void {
fileprivate func bjs_JSConsole_log_extern(_ self: Int32, _ messageBytes: Int32, _ messageLength: Int32) -> Void {
fatalError("Only available on WebAssembly")
}
#endif
@inline(never) fileprivate func bjs_JSConsole_log(_ self: Int32, _ message: Int32) -> Void {
return bjs_JSConsole_log_extern(self, message)
@inline(never) fileprivate func bjs_JSConsole_log(_ self: Int32, _ messageBytes: Int32, _ messageLength: Int32) -> Void {
return bjs_JSConsole_log_extern(self, messageBytes, messageLength)
}

func _$JSConsole_log(_ self: JSObject, _ message: String) throws(JSException) -> Void {
let selfValue = self.bridgeJSLowerParameter()
let messageValue = message.bridgeJSLowerParameter()
bjs_JSConsole_log(selfValue, messageValue)
_swift_js_with_borrowed_utf8(message) { messageBytes, messageLength in
bjs_JSConsole_log(selfValue, messageBytes, messageLength)
}
if let error = _swift_js_take_exception() {
throw error
}
}

#if arch(wasm32)
@_extern(wasm, module: "TestModule", name: "bjs_WebSocket_init")
fileprivate func bjs_WebSocket_init_extern(_ url: Int32) -> Int32
fileprivate func bjs_WebSocket_init_extern(_ urlBytes: Int32, _ urlLength: Int32) -> Int32
#else
fileprivate func bjs_WebSocket_init_extern(_ url: Int32) -> Int32 {
fileprivate func bjs_WebSocket_init_extern(_ urlBytes: Int32, _ urlLength: Int32) -> Int32 {
fatalError("Only available on WebAssembly")
}
#endif
@inline(never) fileprivate func bjs_WebSocket_init(_ url: Int32) -> Int32 {
return bjs_WebSocket_init_extern(url)
@inline(never) fileprivate func bjs_WebSocket_init(_ urlBytes: Int32, _ urlLength: Int32) -> Int32 {
return bjs_WebSocket_init_extern(urlBytes, urlLength)
}

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

func _$WebSocket_init(_ url: String) throws(JSException) -> JSObject {
let urlValue = url.bridgeJSLowerParameter()
let ret = bjs_WebSocket_init(urlValue)
let ret = _swift_js_with_borrowed_utf8(url) { urlBytes, urlLength in
bjs_WebSocket_init(urlBytes, urlLength)
}
if let error = _swift_js_take_exception() {
throw error
}
Expand Down
Loading