Skip to content

Commit 4d01d11

Browse files
authored
Merge pull request #526 from swiftwasm/katei/5e79-add-more-complet
BridgeJS: support jsName for imported bindings
2 parents 7b0ef78 + 374d796 commit 4d01d11

File tree

16 files changed

+775
-83
lines changed

16 files changed

+775
-83
lines changed

Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1743,6 +1743,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
17431743
// Current type being collected (when in jsClassBody state)
17441744
private struct CurrentType {
17451745
let name: String
1746+
let jsName: String?
17461747
var constructor: ImportedConstructorSkeleton?
17471748
var methods: [ImportedFunctionSkeleton]
17481749
var getters: [ImportedGetterSkeleton]
@@ -1758,10 +1759,22 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
17581759
hasAttribute(attributes, name: "JSFunction")
17591760
}
17601761

1762+
static func firstJSFunctionAttribute(_ attributes: AttributeListSyntax?) -> AttributeSyntax? {
1763+
attributes?.first { attribute in
1764+
attribute.as(AttributeSyntax.self)?.attributeName.trimmedDescription == "JSFunction"
1765+
}?.as(AttributeSyntax.self)
1766+
}
1767+
17611768
static func hasJSGetterAttribute(_ attributes: AttributeListSyntax?) -> Bool {
17621769
hasAttribute(attributes, name: "JSGetter")
17631770
}
17641771

1772+
static func firstJSGetterAttribute(_ attributes: AttributeListSyntax?) -> AttributeSyntax? {
1773+
attributes?.first { attribute in
1774+
attribute.as(AttributeSyntax.self)?.attributeName.trimmedDescription == "JSGetter"
1775+
}?.as(AttributeSyntax.self)
1776+
}
1777+
17651778
static func hasJSSetterAttribute(_ attributes: AttributeListSyntax?) -> Bool {
17661779
hasAttribute(attributes, name: "JSSetter")
17671780
}
@@ -1776,6 +1789,12 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
17761789
hasAttribute(attributes, name: "JSClass")
17771790
}
17781791

1792+
static func firstJSClassAttribute(_ attributes: AttributeListSyntax?) -> AttributeSyntax? {
1793+
attributes?.first { attribute in
1794+
attribute.as(AttributeSyntax.self)?.attributeName.trimmedDescription == "JSClass"
1795+
}?.as(AttributeSyntax.self)
1796+
}
1797+
17791798
static func hasAttribute(_ attributes: AttributeListSyntax?, name: String) -> Bool {
17801799
guard let attributes else { return false }
17811800
return attributes.contains { attribute in
@@ -1784,7 +1803,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
17841803
}
17851804
}
17861805

1787-
/// Extracts the jsName argument value from a @JSSetter attribute, if present.
1806+
/// Extracts the `jsName` argument value from an attribute, if present.
17881807
static func extractJSName(from attribute: AttributeSyntax) -> String? {
17891808
guard let arguments = attribute.arguments?.as(LabeledExprListSyntax.self) else {
17901809
return nil
@@ -1883,22 +1902,15 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
18831902

18841903
// MARK: - Property Name Resolution
18851904

1886-
/// Helper for resolving property names from setter function names and jsName attributes
1905+
/// Helper for resolving property names from setter function names.
18871906
private struct PropertyNameResolver {
1888-
/// Resolves property name and function base name from a setter function and optional jsName
1889-
/// - Returns: (propertyName, functionBaseName) where propertyName preserves case for getter matching,
1890-
/// and functionBaseName has lowercase first char for ABI generation
1907+
/// Resolves property name and function base name from a setter function.
1908+
/// - Returns: (propertyName, functionBaseName) where `propertyName` is derived from the setter name,
1909+
/// and `functionBaseName` has lowercase first char for ABI generation.
18911910
static func resolve(
18921911
functionName: String,
1893-
jsName: String?,
18941912
normalizeIdentifier: (String) -> String
18951913
) -> (propertyName: String, functionBaseName: String)? {
1896-
if let jsName = jsName {
1897-
let propertyName = normalizeIdentifier(jsName)
1898-
let functionBaseName = propertyName.prefix(1).lowercased() + propertyName.dropFirst()
1899-
return (propertyName: propertyName, functionBaseName: functionBaseName)
1900-
}
1901-
19021914
let rawFunctionName =
19031915
functionName.hasPrefix("`") && functionName.hasSuffix("`") && functionName.count > 2
19041916
? String(functionName.dropFirst().dropLast())
@@ -1930,14 +1942,27 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
19301942

19311943
private func enterJSClass(_ typeName: String) {
19321944
stateStack.append(.jsClassBody(name: typeName))
1933-
currentType = CurrentType(name: typeName, constructor: nil, methods: [], getters: [], setters: [])
1945+
currentType = CurrentType(name: typeName, jsName: nil, constructor: nil, methods: [], getters: [], setters: [])
1946+
}
1947+
1948+
private func enterJSClass(_ typeName: String, jsName: String?) {
1949+
stateStack.append(.jsClassBody(name: typeName))
1950+
currentType = CurrentType(
1951+
name: typeName,
1952+
jsName: jsName,
1953+
constructor: nil,
1954+
methods: [],
1955+
getters: [],
1956+
setters: []
1957+
)
19341958
}
19351959

19361960
private func exitJSClass() {
19371961
if case .jsClassBody(let typeName) = state, let type = currentType, type.name == typeName {
19381962
importedTypes.append(
19391963
ImportedTypeSkeleton(
19401964
name: type.name,
1965+
jsName: type.jsName,
19411966
constructor: type.constructor,
19421967
methods: type.methods,
19431968
getters: type.getters,
@@ -1952,7 +1977,8 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
19521977

19531978
override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind {
19541979
if AttributeChecker.hasJSClassAttribute(node.attributes) {
1955-
enterJSClass(node.name.text)
1980+
let jsName = AttributeChecker.firstJSClassAttribute(node.attributes).flatMap(AttributeChecker.extractJSName)
1981+
enterJSClass(node.name.text, jsName: jsName)
19561982
}
19571983
return .visitChildren
19581984
}
@@ -1965,7 +1991,8 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
19651991

19661992
override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind {
19671993
if AttributeChecker.hasJSClassAttribute(node.attributes) {
1968-
enterJSClass(node.name.text)
1994+
let jsName = AttributeChecker.firstJSClassAttribute(node.attributes).flatMap(AttributeChecker.extractJSName)
1995+
enterJSClass(node.name.text, jsName: jsName)
19691996
}
19701997
return .visitChildren
19711998
}
@@ -2003,8 +2030,8 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
20032030
}
20042031

20052032
private func handleTopLevelFunction(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind {
2006-
if AttributeChecker.hasJSFunctionAttribute(node.attributes),
2007-
let function = parseFunction(node, enclosingTypeName: nil, isStaticMember: true)
2033+
if let jsFunction = AttributeChecker.firstJSFunctionAttribute(node.attributes),
2034+
let function = parseFunction(jsFunction, node, enclosingTypeName: nil, isStaticMember: true)
20082035
{
20092036
importedFunctions.append(function)
20102037
return .skipChildren
@@ -2028,13 +2055,13 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
20282055
isStaticMember: Bool,
20292056
type: inout CurrentType
20302057
) -> Bool {
2031-
if AttributeChecker.hasJSFunctionAttribute(node.attributes) {
2058+
if let jsFunction = AttributeChecker.firstJSFunctionAttribute(node.attributes) {
20322059
if isStaticMember {
2033-
parseFunction(node, enclosingTypeName: typeName, isStaticMember: true).map {
2060+
parseFunction(jsFunction, node, enclosingTypeName: typeName, isStaticMember: true).map {
20342061
importedFunctions.append($0)
20352062
}
20362063
} else {
2037-
parseFunction(node, enclosingTypeName: typeName, isStaticMember: false).map {
2064+
parseFunction(jsFunction, node, enclosingTypeName: typeName, isStaticMember: false).map {
20382065
type.methods.append($0)
20392066
}
20402067
}
@@ -2065,10 +2092,13 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
20652092
guard AttributeChecker.hasJSGetterAttribute(node.attributes) else {
20662093
return .visitChildren
20672094
}
2095+
guard let jsGetter = AttributeChecker.firstJSGetterAttribute(node.attributes) else {
2096+
return .skipChildren
2097+
}
20682098

20692099
switch state {
20702100
case .topLevel:
2071-
if let getter = parseGetterSkeleton(node) {
2101+
if let getter = parseGetterSkeleton(jsGetter, node) {
20722102
importedGlobalGetters.append(getter)
20732103
}
20742104
return .skipChildren
@@ -2085,7 +2115,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
20852115
"@JSGetter is not supported for static members. Use it only for instance members in @JSClass types."
20862116
)
20872117
)
2088-
} else if let getter = parseGetterSkeleton(node) {
2118+
} else if let getter = parseGetterSkeleton(jsGetter, node) {
20892119
type.getters.append(getter)
20902120
currentType = type
20912121
}
@@ -2128,8 +2158,8 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
21282158
private func collectStaticMembers(in members: MemberBlockItemListSyntax, typeName: String) {
21292159
for member in members {
21302160
if let function = member.decl.as(FunctionDeclSyntax.self) {
2131-
if AttributeChecker.hasJSFunctionAttribute(function.attributes),
2132-
let parsed = parseFunction(function, enclosingTypeName: typeName, isStaticMember: true)
2161+
if let jsFunction = AttributeChecker.firstJSFunctionAttribute(function.attributes),
2162+
let parsed = parseFunction(jsFunction, function, enclosingTypeName: typeName, isStaticMember: true)
21332163
{
21342164
importedFunctions.append(parsed)
21352165
} else if AttributeChecker.hasJSSetterAttribute(function.attributes) {
@@ -2173,6 +2203,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
21732203
}
21742204

21752205
private func parseFunction(
2206+
_ jsFunction: AttributeSyntax,
21762207
_ node: FunctionDeclSyntax,
21772208
enclosingTypeName: String?,
21782209
isStaticMember: Bool
@@ -2183,6 +2214,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
21832214
}
21842215

21852216
let baseName = SwiftToSkeleton.normalizeIdentifier(node.name.text)
2217+
let jsName = AttributeChecker.extractJSName(from: jsFunction)
21862218
let name: String
21872219
if isStaticMember, let enclosingTypeName {
21882220
name = "\(enclosingTypeName)_\(baseName)"
@@ -2202,6 +2234,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
22022234
}
22032235
return ImportedFunctionSkeleton(
22042236
name: name,
2237+
jsName: jsName,
22052238
parameters: parameters,
22062239
returnType: returnType,
22072240
documentation: nil
@@ -2223,16 +2256,21 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
22232256
return (identifier, typeAnnotation.type)
22242257
}
22252258

2226-
private func parseGetterSkeleton(_ node: VariableDeclSyntax) -> ImportedGetterSkeleton? {
2259+
private func parseGetterSkeleton(
2260+
_ jsGetter: AttributeSyntax,
2261+
_ node: VariableDeclSyntax
2262+
) -> ImportedGetterSkeleton? {
22272263
guard let (identifier, type) = extractPropertyInfo(node) else {
22282264
return nil
22292265
}
22302266
guard let propertyType = withLookupErrors({ parent.lookupType(for: type, errors: &$0) }) else {
22312267
return nil
22322268
}
22332269
let propertyName = SwiftToSkeleton.normalizeIdentifier(identifier.identifier.text)
2270+
let jsName = AttributeChecker.extractJSName(from: jsGetter)
22342271
return ImportedGetterSkeleton(
22352272
name: propertyName,
2273+
jsName: jsName,
22362274
type: propertyType,
22372275
documentation: nil,
22382276
functionName: nil
@@ -2252,7 +2290,6 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
22522290
guard
22532291
let (propertyName, functionBaseName) = PropertyNameResolver.resolve(
22542292
functionName: functionName,
2255-
jsName: validation.jsName,
22562293
normalizeIdentifier: SwiftToSkeleton.normalizeIdentifier
22572294
)
22582295
else {
@@ -2261,6 +2298,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
22612298

22622299
return ImportedSetterSkeleton(
22632300
name: propertyName,
2301+
jsName: validation.jsName,
22642302
type: validation.valueType,
22652303
documentation: nil,
22662304
functionName: "\(functionBaseName)_set"

0 commit comments

Comments
 (0)