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
3 changes: 3 additions & 0 deletions Benchmarks/Sources/Generated/JavaScript/BridgeJS.json
Original file line number Diff line number Diff line change
Expand Up @@ -3339,6 +3339,7 @@
{
"functions" : [
{
"accessLevel" : "internal",
"effects" : {
"isAsync" : false,
"isStatic" : false,
Expand All @@ -3355,6 +3356,7 @@
}
},
{
"accessLevel" : "internal",
"effects" : {
"isAsync" : false,
"isStatic" : false,
Expand All @@ -3378,6 +3380,7 @@
}
},
{
"accessLevel" : "internal",
"effects" : {
"isAsync" : false,
"isStatic" : false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@
{
"functions" : [
{
"accessLevel" : "internal",
"effects" : {
"isAsync" : false,
"isStatic" : false,
Expand All @@ -260,11 +261,13 @@
],
"types" : [
{
"accessLevel" : "internal",
"getters" : [

],
"methods" : [
{
"accessLevel" : "internal",
"effects" : {
"isAsync" : false,
"isStatic" : false,
Expand Down
17 changes: 11 additions & 6 deletions Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ public struct ClosureCodegen {
return "(\(closureParams))\(swiftEffects) -> \(swiftReturnType)"
}

func renderClosureHelpers(_ signature: ClosureSignature) throws -> [DeclSyntax] {
func renderClosureHelpers(
_ signature: ClosureSignature,
accessLevel: BridgeJSAccessLevel = .internal
) throws -> [DeclSyntax] {
let mangledName = signature.mangleName
let helperName = "_BJS_Closure_\(mangledName)"
let swiftClosureType = swiftClosureType(for: signature)
Expand Down Expand Up @@ -99,9 +102,10 @@ public struct ClosureCodegen {

let helperEnumDecl: DeclSyntax = "\(raw: helperEnumDeclPrinter.lines.joined(separator: "\n"))"

let initAccessModifier = accessLevel.modifierKeyword.map { "\($0) " } ?? ""
let typedClosureExtension: DeclSyntax = """
extension JSTypedClosure where Signature == \(raw: swiftClosureType) {
init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping \(raw: swiftClosureType)) {
\(raw: initAccessModifier)init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping \(raw: swiftClosureType)) {
self.init(
makeClosure: \(raw: externABIName),
body: body,
Expand Down Expand Up @@ -192,12 +196,13 @@ public struct ClosureCodegen {
let collector = ClosureSignatureCollectorVisitor(moduleName: skeleton.moduleName)
var walker = BridgeSkeletonWalker(visitor: collector)
walker.walk(skeleton)
let closureSignatures = walker.visitor.signatures
guard !closureSignatures.isEmpty else { return nil }
let signatureAccessLevels = walker.visitor.signatureAccessLevels
guard !signatureAccessLevels.isEmpty else { return nil }

var decls: [DeclSyntax] = []
for signature in closureSignatures.sorted(by: { $0.mangleName < $1.mangleName }) {
decls.append(contentsOf: try renderClosureHelpers(signature))
for signature in signatureAccessLevels.keys.sorted(by: { $0.mangleName < $1.mangleName }) {
let accessLevel = signatureAccessLevels[signature] ?? .internal
decls.append(contentsOf: try renderClosureHelpers(signature, accessLevel: accessLevel))
decls.append(try renderClosureInvokeHandler(signature))
}

Expand Down
64 changes: 56 additions & 8 deletions Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2056,6 +2056,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
let name: String
let jsName: String?
let from: JSImportFrom?
let accessLevel: BridgeJSAccessLevel
var constructor: ImportedConstructorSkeleton?
var methods: [ImportedFunctionSkeleton]
var staticMethods: [ImportedFunctionSkeleton]
Expand Down Expand Up @@ -2271,6 +2272,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
name: typeName,
jsName: nil,
from: nil,
accessLevel: .internal,
constructor: nil,
methods: [],
staticMethods: [],
Expand All @@ -2279,12 +2281,18 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
)
}

private func enterJSClass(_ typeName: String, jsName: String?, from: JSImportFrom?) {
private func enterJSClass(
_ typeName: String,
jsName: String?,
from: JSImportFrom?,
accessLevel: BridgeJSAccessLevel
) {
stateStack.append(.jsClassBody(name: typeName))
currentType = CurrentType(
name: typeName,
jsName: jsName,
from: from,
accessLevel: accessLevel,
constructor: nil,
methods: [],
staticMethods: [],
Expand All @@ -2305,7 +2313,8 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
staticMethods: type.staticMethods,
getters: type.getters,
setters: type.setters,
documentation: nil
documentation: nil,
accessLevel: type.accessLevel
)
)
currentType = nil
Expand All @@ -2318,7 +2327,8 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
let attribute = AttributeChecker.firstJSClassAttribute(node.attributes)
let jsName = attribute.flatMap(AttributeChecker.extractJSName)
let from = attribute.flatMap(AttributeChecker.extractJSImportFrom)
enterJSClass(node.name.text, jsName: jsName, from: from)
let accessLevel = Self.bridgeAccessLevel(from: node.modifiers)
enterJSClass(node.name.text, jsName: jsName, from: from, accessLevel: accessLevel)
}
return .visitChildren
}
Expand All @@ -2334,7 +2344,8 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
let attribute = AttributeChecker.firstJSClassAttribute(node.attributes)
let jsName = attribute.flatMap(AttributeChecker.extractJSName)
let from = attribute.flatMap(AttributeChecker.extractJSImportFrom)
enterJSClass(node.name.text, jsName: jsName, from: from)
let accessLevel = Self.bridgeAccessLevel(from: node.modifiers)
enterJSClass(node.name.text, jsName: jsName, from: from, accessLevel: accessLevel)
}
return .visitChildren
}
Expand Down Expand Up @@ -2499,8 +2510,14 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
else {
return nil
}
// Initializers without an explicit modifier inherit access from the
// enclosing `@JSClass` (the user's example pattern: `public init(...)`
// inside `public struct JSDocument`).
let parentLevel = currentType?.accessLevel ?? .internal
let accessLevel = Self.bridgeAccessLevel(from: initializer.modifiers, default: parentLevel)
return ImportedConstructorSkeleton(
parameters: parseParameters(from: initializer.signature.parameterClause)
parameters: parseParameters(from: initializer.signature.parameterClause),
accessLevel: accessLevel
)
}

Expand Down Expand Up @@ -2533,14 +2550,16 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
} else {
returnType = .void
}
let accessLevel = Self.bridgeAccessLevel(from: node.modifiers)
return ImportedFunctionSkeleton(
name: name,
jsName: jsName,
from: from,
parameters: parameters,
returnType: returnType,
effects: effects,
documentation: nil
documentation: nil,
accessLevel: accessLevel
)
}

Expand Down Expand Up @@ -2572,13 +2591,15 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
let propertyName = SwiftToSkeleton.normalizeIdentifier(identifier.identifier.text)
let jsName = AttributeChecker.extractJSName(from: jsGetter)
let from = AttributeChecker.extractJSImportFrom(from: jsGetter)
let accessLevel = Self.bridgeAccessLevel(from: node.modifiers)
return ImportedGetterSkeleton(
name: propertyName,
jsName: jsName,
from: from,
type: propertyType,
documentation: nil,
functionName: nil
functionName: nil,
accessLevel: accessLevel
)
}

Expand All @@ -2601,12 +2622,14 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
return nil
}

let accessLevel = Self.bridgeAccessLevel(from: node.modifiers)
return ImportedSetterSkeleton(
name: propertyName,
jsName: validation.jsName,
type: validation.valueType,
documentation: nil,
functionName: "\(functionBaseName)_set"
functionName: "\(functionBaseName)_set",
accessLevel: accessLevel
)
}

Expand Down Expand Up @@ -2652,6 +2675,31 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor {
modifier.name.tokenKind == .keyword(.static) || modifier.name.tokenKind == .keyword(.class)
}
}

/// Maps Swift's declaration modifiers to a `BridgeJSAccessLevel` for
/// recording on imported skeleton entries. Falls back to `default` when no
/// access modifier is present (typically `.internal`, but the caller may
/// override — e.g. an `init` inheriting from its enclosing `@JSClass`).
/// `private`/`fileprivate` are mapped to the fallback because the macros
/// already reject those access levels for `@JS*` declarations.
fileprivate static func bridgeAccessLevel(
from modifiers: DeclModifierListSyntax,
default fallback: BridgeJSAccessLevel = .internal
) -> BridgeJSAccessLevel {
for modifier in modifiers {
switch modifier.name.tokenKind {
case .keyword(.public), .keyword(.open):
return .public
case .keyword(.package):
return .package
case .keyword(.internal):
return .internal
default:
continue
}
}
return fallback
}
}

extension GenericArgumentListSyntax {
Expand Down
Loading
Loading