From 648566b64fa147046bb2bacedc08bdf080d4604e Mon Sep 17 00:00:00 2001 From: Matthew Ayers Date: Sat, 25 Apr 2026 17:46:25 -0400 Subject: [PATCH 1/4] [BridgeJS] Synthesize typed-closure init access from declaration surface Resolves swiftwasm/JavaScriptKit#709: a public `@JSClass` exposing a `JSTypedClosure<...>` parameter could not be consumed from another target because the synthesized `extension JSTypedClosure { init(...) }` was always internal, leaving downstream callers no way to construct the closure value without hand-rolling a public wrapper. Imported skeleton entries now record the source access level (`public`/`package`/`internal`); the closure-signature collector takes the maximum across every surface that references a given signature, and `ClosureCodegen` prefixes the synthesized init with the resulting modifier (internal stays bare). This matches the pattern `JSClassMacro` already uses for `init(unsafelyWrapping:)`. --- .../Sources/BridgeJSCore/ClosureCodegen.swift | 17 +- .../BridgeJSCore/SwiftToSkeleton.swift | 64 +++- .../BridgeJSSkeleton/BridgeJSSkeleton.swift | 259 +++++++++++--- .../MacroSwift/SwiftTypedClosureAccess.swift | 26 ++ .../BridgeJSCodegenTests/ArrayTypes.json | 7 + .../BridgeJSCodegenTests/AsyncImport.json | 6 + .../AsyncStaticImport.json | 3 + .../CrossFileSkipsEmptySkeletons.json | 1 + .../BridgeJSCodegenTests/DictionaryTypes.json | 1 + .../BridgeJSCodegenTests/EnumRawType.json | 2 + .../FixedWidthIntegers.json | 8 + .../BridgeJSCodegenTests/GlobalGetter.json | 3 + .../GlobalThisImports.json | 7 + .../BridgeJSCodegenTests/ImportArray.json | 2 + .../ImportedTypeInExportedInterface.json | 2 + .../InvalidPropertyNames.json | 24 ++ .../BridgeJSCodegenTests/JSClass.json | 11 + .../JSClassStaticFunctions.json | 9 + .../BridgeJSCodegenTests/JSValue.json | 2 + .../BridgeJSCodegenTests/Optionals.json | 26 ++ .../PrimitiveParameters.json | 1 + .../BridgeJSCodegenTests/PrimitiveReturn.json | 2 + .../BridgeJSCodegenTests/StringParameter.json | 2 + .../BridgeJSCodegenTests/StringReturn.json | 1 + .../BridgeJSCodegenTests/SwiftClass.json | 2 + .../SwiftClosureImports.json | 2 + .../SwiftStructImports.json | 1 + .../SwiftTypedClosureAccess.json | 285 +++++++++++++++ .../SwiftTypedClosureAccess.swift | 266 ++++++++++++++ .../VoidParameterVoidReturn.json | 1 + .../SwiftTypedClosureAccess.d.ts | 33 ++ .../SwiftTypedClosureAccess.js | 329 ++++++++++++++++++ 32 files changed, 1333 insertions(+), 72 deletions(-) create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/SwiftTypedClosureAccess.swift create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftTypedClosureAccess.json create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftTypedClosureAccess.swift create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftTypedClosureAccess.d.ts create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftTypedClosureAccess.js diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift index f3ed97ba3..45cfb73f1 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ClosureCodegen.swift @@ -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) @@ -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, @@ -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)) } diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift index 3d8f417e3..f100c7c86 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/SwiftToSkeleton.swift @@ -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] @@ -2271,6 +2272,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { name: typeName, jsName: nil, from: nil, + accessLevel: .internal, constructor: nil, methods: [], staticMethods: [], @@ -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: [], @@ -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 @@ -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 } @@ -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 } @@ -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 ) } @@ -2533,6 +2550,7 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { } else { returnType = .void } + let accessLevel = Self.bridgeAccessLevel(from: node.modifiers) return ImportedFunctionSkeleton( name: name, jsName: jsName, @@ -2540,7 +2558,8 @@ private final class ImportSwiftMacrosAPICollector: SyntaxAnyVisitor { parameters: parameters, returnType: returnType, effects: effects, - documentation: nil + documentation: nil, + accessLevel: accessLevel ) } @@ -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 ) } @@ -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 ) } @@ -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 { diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index 03e6d676a..856933008 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -89,6 +89,38 @@ public enum BridgeContext: Sendable { case exportSwift } +/// Access level applied to bridge-generated declarations. +/// +/// Ordering (`Comparable`) reflects visibility breadth, so taking `max` of two +/// levels yields the more permissive one — used to merge a single closure +/// signature seen across surfaces with different declared access. +public enum BridgeJSAccessLevel: String, Codable, Equatable, Hashable, Sendable, Comparable { + case `internal` + case package + case `public` + + public static func < (lhs: BridgeJSAccessLevel, rhs: BridgeJSAccessLevel) -> Bool { + lhs.order < rhs.order + } + + private var order: Int { + switch self { + case .internal: return 0 + case .package: return 1 + case .public: return 2 + } + } + + /// Returns the modifier keyword to emit, or nil for the default (internal). + public var modifierKeyword: String? { + switch self { + case .internal: return nil + case .package: return "package" + case .public: return "public" + } + } +} + public struct ClosureSignature: Codable, Equatable, Hashable, Sendable { public let parameters: [BridgeType] public let returnType: BridgeType @@ -398,17 +430,34 @@ public struct Parameter: Codable, Equatable, Sendable { // MARK: - BridgeSkeleton Visitor public protocol BridgeSkeletonVisitor { - mutating func visitClosure(_ signature: ClosureSignature, useJSTypedClosure: Bool) + /// Called when a closure type is encountered during traversal. + /// + /// `accessLevel` reflects the source access of the enclosing declaration, + /// so visitors can derive an appropriate access level for any + /// bridge-generated helpers tied to this signature (e.g. typed closure + /// inits in `ClosureCodegen`). + mutating func visitClosure( + _ signature: ClosureSignature, + useJSTypedClosure: Bool, + accessLevel: BridgeJSAccessLevel + ) mutating func visitImportedFunction(_ function: ImportedFunctionSkeleton) } public extension BridgeSkeletonVisitor { - mutating func visitClosure(_ signature: ClosureSignature, useJSTypedClosure: Bool) {} + mutating func visitClosure( + _ signature: ClosureSignature, + useJSTypedClosure: Bool, + accessLevel: BridgeJSAccessLevel + ) {} mutating func visitImportedFunction(_ function: ImportedFunctionSkeleton) {} } public struct BridgeSkeletonWalker { public var visitor: Visitor + /// Tracks the access level of the enclosing declaration during traversal. + /// Saved/restored around each declaration that introduces an access boundary. + private var currentAccessLevel: BridgeJSAccessLevel = .internal public init(visitor: Visitor) { self.visitor = visitor @@ -417,7 +466,11 @@ public struct BridgeSkeletonWalker { public mutating func walk(_ type: BridgeType) { switch type { case .closure(let signature, let useJSTypedClosure): - visitor.visitClosure(signature, useJSTypedClosure: useJSTypedClosure) + visitor.visitClosure( + signature, + useJSTypedClosure: useJSTypedClosure, + accessLevel: currentAccessLevel + ) for paramType in signature.parameters { walk(paramType) } @@ -449,14 +502,16 @@ public struct BridgeSkeletonWalker { walk(function) } for klass in skeleton.classes { - if let constructor = klass.constructor { - walk(constructor.parameters) - } - for method in klass.methods { - walk(method) - } - for property in klass.properties { - walk(property.type) + withAccessLevel(klass.explicitAccessControl) { + if let constructor = klass.constructor { + $0.walk(constructor.parameters) + } + for method in klass.methods { + $0.walk(method) + } + for property in klass.properties { + $0.walk(property.type) + } } } for proto in skeleton.protocols { @@ -468,58 +523,66 @@ public struct BridgeSkeletonWalker { } } for structDecl in skeleton.structs { - for property in structDecl.properties { - walk(property.type) - } - if let constructor = structDecl.constructor { - walk(constructor.parameters) - } - for method in structDecl.methods { - walk(method) + withAccessLevel(structDecl.explicitAccessControl) { + for property in structDecl.properties { + $0.walk(property.type) + } + if let constructor = structDecl.constructor { + $0.walk(constructor.parameters) + } + for method in structDecl.methods { + $0.walk(method) + } } } for enumDecl in skeleton.enums { - for enumCase in enumDecl.cases { - for associatedValue in enumCase.associatedValues { - walk(associatedValue.type) + withAccessLevel(enumDecl.explicitAccessControl) { + for enumCase in enumDecl.cases { + for associatedValue in enumCase.associatedValues { + $0.walk(associatedValue.type) + } + } + for method in enumDecl.staticMethods { + $0.walk(method) + } + for property in enumDecl.staticProperties { + $0.walk(property.type) } - } - for method in enumDecl.staticMethods { - walk(method) - } - for property in enumDecl.staticProperties { - walk(property.type) } } } public mutating func walk(_ function: ImportedFunctionSkeleton) { visitor.visitImportedFunction(function) - walk(function.parameters) - walk(function.returnType) + withAccessLevel(function.accessLevel) { + $0.walk(function.parameters) + $0.walk(function.returnType) + } } public mutating func walk(_ skeleton: ImportedModuleSkeleton) { for fileSkeleton in skeleton.children { for getter in fileSkeleton.globalGetters { - walk(getter.type) + withAccessLevel(getter.accessLevel) { $0.walk(getter.type) } } for setter in fileSkeleton.globalSetters { - walk(setter.type) + withAccessLevel(setter.accessLevel) { $0.walk(setter.type) } } for function in fileSkeleton.functions { walk(function) } for type in fileSkeleton.types { - if let constructor = type.constructor { - walk(constructor.parameters) - } - for getter in type.getters { - walk(getter.type) - } - for setter in type.setters { - walk(setter.type) - } - for method in type.methods + type.staticMethods { - walk(method) + withAccessLevel(type.accessLevel) { + if let constructor = type.constructor { + $0.withAccessLevel(constructor.accessLevel) { $0.walk(constructor.parameters) } + } + for getter in type.getters { + $0.withAccessLevel(getter.accessLevel) { $0.walk(getter.type) } + } + for setter in type.setters { + $0.withAccessLevel(setter.accessLevel) { $0.walk(setter.type) } + } + for method in type.methods + type.staticMethods { + $0.walk(method) + } } } } @@ -532,6 +595,30 @@ public struct BridgeSkeletonWalker { walk(imported) } } + + /// Sets `currentAccessLevel` to `level` for the duration of `body`, restoring + /// the prior value afterward. A nil level (e.g. for exported decls without + /// an explicit modifier) inherits the outer level rather than overwriting it. + private mutating func withAccessLevel( + _ level: BridgeJSAccessLevel?, + _ body: (inout BridgeSkeletonWalker) -> Void + ) { + let saved = currentAccessLevel + if let level { + currentAccessLevel = level + } + body(&self) + currentAccessLevel = saved + } + + /// String-typed convenience: maps `"public"`/`"package"`/`"internal"` from + /// `Exported*.explicitAccessControl` to the typed enum. + private mutating func withAccessLevel( + _ rawLevel: String?, + _ body: (inout BridgeSkeletonWalker) -> Void + ) { + withAccessLevel(rawLevel.flatMap(BridgeJSAccessLevel.init(rawValue:)), body) + } } public struct Effects: Codable, Equatable, Sendable { @@ -951,6 +1038,10 @@ public struct ImportedFunctionSkeleton: Codable { public let returnType: BridgeType public let effects: Effects public let documentation: String? + /// Source access level of the originating Swift declaration. Used to + /// determine the access level of bridge-generated helpers (e.g. typed + /// closure inits) that surface through this function's signature. + public let accessLevel: BridgeJSAccessLevel public init( name: String, @@ -959,7 +1050,8 @@ public struct ImportedFunctionSkeleton: Codable { parameters: [Parameter], returnType: BridgeType, effects: Effects = Effects(isAsync: false, isThrows: true), - documentation: String? = nil + documentation: String? = nil, + accessLevel: BridgeJSAccessLevel = .internal ) { self.name = name self.jsName = jsName @@ -968,6 +1060,7 @@ public struct ImportedFunctionSkeleton: Codable { self.returnType = returnType self.effects = effects self.documentation = documentation + self.accessLevel = accessLevel } public func abiName(context: ImportedTypeSkeleton?) -> String { @@ -985,9 +1078,13 @@ public struct ImportedFunctionSkeleton: Codable { public struct ImportedConstructorSkeleton: Codable { public let parameters: [Parameter] + /// Source access level of the originating Swift `init`. Inherits from the + /// enclosing `@JSClass` type when not annotated explicitly. + public let accessLevel: BridgeJSAccessLevel - public init(parameters: [Parameter]) { + public init(parameters: [Parameter], accessLevel: BridgeJSAccessLevel = .internal) { self.parameters = parameters + self.accessLevel = accessLevel } public func abiName(context: ImportedTypeSkeleton) -> String { @@ -1008,6 +1105,8 @@ public struct ImportedGetterSkeleton: Codable { public let documentation: String? /// Name of the getter function if it's a separate function (from @JSGetter) public let functionName: String? + /// Source access level of the originating Swift declaration. + public let accessLevel: BridgeJSAccessLevel public init( name: String, @@ -1015,7 +1114,8 @@ public struct ImportedGetterSkeleton: Codable { from: JSImportFrom? = nil, type: BridgeType, documentation: String? = nil, - functionName: String? = nil + functionName: String? = nil, + accessLevel: BridgeJSAccessLevel = .internal ) { self.name = name self.jsName = jsName @@ -1023,6 +1123,7 @@ public struct ImportedGetterSkeleton: Codable { self.type = type self.documentation = documentation self.functionName = functionName + self.accessLevel = accessLevel } public func abiName(context: ImportedTypeSkeleton?) -> String { @@ -1049,19 +1150,23 @@ public struct ImportedSetterSkeleton: Codable { public let documentation: String? /// Name of the setter function if it's a separate function (from @JSSetter) public let functionName: String? + /// Source access level of the originating Swift declaration. + public let accessLevel: BridgeJSAccessLevel public init( name: String, jsName: String? = nil, type: BridgeType, documentation: String? = nil, - functionName: String? = nil + functionName: String? = nil, + accessLevel: BridgeJSAccessLevel = .internal ) { self.name = name self.jsName = jsName self.type = type self.documentation = documentation self.functionName = functionName + self.accessLevel = accessLevel } public func abiName(context: ImportedTypeSkeleton?) -> String { @@ -1093,6 +1198,8 @@ public struct ImportedTypeSkeleton: Codable { public let getters: [ImportedGetterSkeleton] public let setters: [ImportedSetterSkeleton] public let documentation: String? + /// Source access level of the originating Swift `@JSClass` declaration. + public let accessLevel: BridgeJSAccessLevel public init( name: String, @@ -1103,7 +1210,8 @@ public struct ImportedTypeSkeleton: Codable { staticMethods: [ImportedFunctionSkeleton] = [], getters: [ImportedGetterSkeleton] = [], setters: [ImportedSetterSkeleton] = [], - documentation: String? = nil + documentation: String? = nil, + accessLevel: BridgeJSAccessLevel = .internal ) { self.name = name self.jsName = jsName @@ -1114,6 +1222,7 @@ public struct ImportedTypeSkeleton: Codable { self.getters = getters self.setters = setters self.documentation = documentation + self.accessLevel = accessLevel } } @@ -1180,16 +1289,32 @@ public struct ImportedModuleSkeleton: Codable { // MARK: - Closure signature collection visitor public struct ClosureSignatureCollectorVisitor: BridgeSkeletonVisitor { - public var signatures: Set = [] + /// Each unique closure signature mapped to the most-permissive access level + /// observed across all surfaces that reference it. The codegen reads this + /// to choose the access modifier for the synthesized typed-closure init. + public private(set) var signatureAccessLevels: [ClosureSignature: BridgeJSAccessLevel] = [:] + /// Convenience view for callers (e.g. `BridgeJSLink`) that only need the + /// set of unique signatures, without access metadata. + public var signatures: Set { Set(signatureAccessLevels.keys) } let moduleName: String public init(moduleName: String, signatures: Set = []) { self.moduleName = moduleName - self.signatures = signatures + for signature in signatures { + signatureAccessLevels[signature] = .internal + } } - public mutating func visitClosure(_ signature: ClosureSignature, useJSTypedClosure: Bool) { - signatures.insert(signature) + public mutating func visitClosure( + _ signature: ClosureSignature, + useJSTypedClosure: Bool, + accessLevel: BridgeJSAccessLevel + ) { + if let existing = signatureAccessLevels[signature] { + signatureAccessLevels[signature] = max(existing, accessLevel) + } else { + signatureAccessLevels[signature] = accessLevel + } } public mutating func visitImportedFunction(_ function: ImportedFunctionSkeleton) { guard function.effects.isAsync else { return } @@ -1202,34 +1327,52 @@ public struct ClosureSignatureCollectorVisitor: BridgeSkeletonVisitor { // transferred through the checked continuation without Sendable constraints. // Reject callback - signatures.insert( + recordInjectedSignature( ClosureSignature( parameters: [.jsValue], returnType: .void, moduleName: moduleName, sendingParameters: true - ) + ), + for: function ) // Resolve callback (typed per return type) if function.returnType == .void { - signatures.insert( + recordInjectedSignature( ClosureSignature( parameters: [], returnType: .void, moduleName: moduleName - ) + ), + for: function ) } else { - signatures.insert( + recordInjectedSignature( ClosureSignature( parameters: [function.returnType], returnType: .void, moduleName: moduleName, sendingParameters: true - ) + ), + for: function ) } } + + /// Inject a closure signature derived from an async import (e.g. Promise + /// resolve/reject callbacks). The injected signature inherits the access + /// level of the originating function so its synthesized init matches the + /// visibility of the async API surface. + private mutating func recordInjectedSignature( + _ signature: ClosureSignature, + for function: ImportedFunctionSkeleton + ) { + if let existing = signatureAccessLevels[signature] { + signatureAccessLevels[signature] = max(existing, function.accessLevel) + } else { + signatureAccessLevels[signature] = function.accessLevel + } + } } // MARK: - Unified Skeleton diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/SwiftTypedClosureAccess.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/SwiftTypedClosureAccess.swift new file mode 100644 index 000000000..6487d343b --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/MacroSwift/SwiftTypedClosureAccess.swift @@ -0,0 +1,26 @@ +// Verifies that `JSTypedClosure` initializers synthesized by BridgeJS adopt the +// access level of the originating `@JSClass`/`@JSFunction` surface, so that +// downstream targets can construct typed closures for public APIs (issue #709). + +@JSClass(jsName: "PublicEvent") public struct JSPublicEvent {} +@JSClass(jsName: "PackageEvent") package struct JSPackageEvent {} +@JSClass(jsName: "InternalEvent") struct JSInternalEvent {} + +@JSClass(jsName: "PublicTarget") public struct JSPublicTarget { + // A public method taking a typed closure must yield a `public` synthesized init, + // since downstream modules may construct the closure value. + @JSFunction public func addPublicListener(_ handler: JSTypedClosure<(JSPublicEvent) -> Void>) throws(JSException) + // Same closure shape on an internal method — the synthesized init merges to public, + // because at most one extension per signature is generated. + @JSFunction func addInternalListener(_ handler: JSTypedClosure<(JSPublicEvent) -> Void>) throws(JSException) +} + +@JSClass(jsName: "PackageTarget") package struct JSPackageTarget { + // A package-level surface yields a `package` synthesized init. + @JSFunction package func addPackageListener(_ handler: JSTypedClosure<(JSPackageEvent) -> Void>) throws(JSException) +} + +@JSClass(jsName: "InternalTarget") struct JSInternalTarget { + // No public/package surface for this signature — the synthesized init stays internal. + @JSFunction func addInternalListener(_ handler: JSTypedClosure<(JSInternalEvent) -> Void>) throws(JSException) +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json index d071d8c52..6868d59ed 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ArrayTypes.json @@ -1331,6 +1331,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -1354,6 +1355,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -1385,6 +1387,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -1412,6 +1415,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -1432,6 +1436,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -1463,6 +1468,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -1494,6 +1500,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.json index 263578d20..0ada95550 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncImport.json @@ -4,6 +4,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, @@ -20,6 +21,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, @@ -49,6 +51,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, @@ -72,6 +75,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, @@ -95,6 +99,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, @@ -118,6 +123,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncStaticImport.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncStaticImport.json index 972a532c6..a08671ada 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncStaticImport.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/AsyncStaticImport.json @@ -7,6 +7,7 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], @@ -19,6 +20,7 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, @@ -42,6 +44,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileSkipsEmptySkeletons.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileSkipsEmptySkeletons.json index a0c2c80c6..ace23422b 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileSkipsEmptySkeletons.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/CrossFileSkipsEmptySkeletons.json @@ -4,6 +4,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DictionaryTypes.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DictionaryTypes.json index e18586e1c..1e3f4f31d 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DictionaryTypes.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/DictionaryTypes.json @@ -300,6 +300,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumRawType.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumRawType.json index fc4a7ae52..cf092737e 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumRawType.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/EnumRawType.json @@ -1526,6 +1526,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -1550,6 +1551,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/FixedWidthIntegers.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/FixedWidthIntegers.json index 15a20f72e..b0ae238ab 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/FixedWidthIntegers.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/FixedWidthIntegers.json @@ -269,6 +269,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -298,6 +299,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -327,6 +329,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -356,6 +359,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -385,6 +389,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -414,6 +419,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -443,6 +449,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -472,6 +479,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalGetter.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalGetter.json index f750fc6a5..33799f361 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalGetter.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalGetter.json @@ -7,6 +7,7 @@ ], "globalGetters" : [ { + "accessLevel" : "internal", "name" : "console", "type" : { "jsObject" : { @@ -17,11 +18,13 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], "methods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalThisImports.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalThisImports.json index 809a9ad99..99ff37c5a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalThisImports.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/GlobalThisImports.json @@ -4,6 +4,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -31,6 +32,7 @@ ], "globalGetters" : [ { + "accessLevel" : "internal", "from" : "global", "name" : "console", "type" : { @@ -42,11 +44,13 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], "methods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -79,7 +83,9 @@ ] }, { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "url", @@ -97,6 +103,7 @@ ], "methods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.json index 3f9cb8e32..e5c33ee90 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportArray.json @@ -4,6 +4,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -41,6 +42,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportedTypeInExportedInterface.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportedTypeInExportedInterface.json index f57e77d21..3424b8145 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportedTypeInExportedInterface.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/ImportedTypeInExportedInterface.json @@ -175,7 +175,9 @@ ], "types" : [ { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ ] diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/InvalidPropertyNames.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/InvalidPropertyNames.json index 1ad99f397..7c57b3fdc 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/InvalidPropertyNames.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/InvalidPropertyNames.json @@ -4,6 +4,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -20,6 +21,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -38,8 +40,10 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ { + "accessLevel" : "internal", "name" : "normalProperty", "type" : { "string" : { @@ -48,6 +52,7 @@ } }, { + "accessLevel" : "internal", "jsName" : "property-with-dashes", "name" : "property_with_dashes", "type" : { @@ -57,6 +62,7 @@ } }, { + "accessLevel" : "internal", "jsName" : "123invalidStart", "name" : "_123invalidStart", "type" : { @@ -66,6 +72,7 @@ } }, { + "accessLevel" : "internal", "jsName" : "property with spaces", "name" : "property_with_spaces", "type" : { @@ -75,6 +82,7 @@ } }, { + "accessLevel" : "internal", "jsName" : "@specialChar", "name" : "_specialChar", "type" : { @@ -84,6 +92,7 @@ } }, { + "accessLevel" : "internal", "name" : "constructor", "type" : { "string" : { @@ -92,6 +101,7 @@ } }, { + "accessLevel" : "internal", "name" : "for", "type" : { "string" : { @@ -100,6 +110,7 @@ } }, { + "accessLevel" : "internal", "name" : "Any", "type" : { "string" : { @@ -110,6 +121,7 @@ ], "methods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -126,6 +138,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -145,6 +158,7 @@ "name" : "WeirdNaming", "setters" : [ { + "accessLevel" : "internal", "functionName" : "normalProperty_set", "name" : "normalProperty", "type" : { @@ -154,6 +168,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "property_with_dashes_set", "jsName" : "property-with-dashes", "name" : "property_with_dashes", @@ -164,6 +179,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "_123invalidStart_set", "jsName" : "123invalidStart", "name" : "_123invalidStart", @@ -174,6 +190,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "property_with_spaces_set", "jsName" : "property with spaces", "name" : "property_with_spaces", @@ -184,6 +201,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "_specialChar_set", "jsName" : "@specialChar", "name" : "_specialChar", @@ -194,6 +212,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "constructor_set", "name" : "constructor", "type" : { @@ -203,6 +222,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "for_set", "name" : "for", "type" : { @@ -212,6 +232,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "any_set", "jsName" : "Any", "name" : "any", @@ -227,7 +248,9 @@ ] }, { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ ] @@ -238,6 +261,7 @@ "jsName" : "$Weird", "methods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClass.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClass.json index ef8eba9ba..4bbf3602c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClass.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClass.json @@ -4,6 +4,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -22,7 +23,9 @@ ], "types" : [ { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "name", @@ -36,6 +39,7 @@ }, "getters" : [ { + "accessLevel" : "internal", "name" : "name", "type" : { "string" : { @@ -44,6 +48,7 @@ } }, { + "accessLevel" : "internal", "name" : "age", "type" : { "double" : { @@ -54,6 +59,7 @@ ], "methods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -70,6 +76,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -96,6 +103,7 @@ "name" : "Greeter", "setters" : [ { + "accessLevel" : "internal", "functionName" : "name_set", "name" : "name", "type" : { @@ -110,11 +118,13 @@ ] }, { + "accessLevel" : "internal", "getters" : [ ], "methods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -146,6 +156,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClassStaticFunctions.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClassStaticFunctions.json index 18f7cfaac..b6afc5485 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClassStaticFunctions.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSClassStaticFunctions.json @@ -7,11 +7,13 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], "methods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -34,6 +36,7 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -57,6 +60,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -73,6 +77,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -89,6 +94,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -108,7 +114,9 @@ ] }, { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "value", @@ -132,6 +140,7 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSValue.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSValue.json index fb8601ae7..b5719295f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSValue.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/JSValue.json @@ -321,6 +321,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -344,6 +345,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json index 3e6d6c60c..885fab83a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/Optionals.json @@ -1008,7 +1008,9 @@ ], "types" : [ { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "valueOrNull", @@ -1040,6 +1042,7 @@ }, "getters" : [ { + "accessLevel" : "internal", "name" : "stringOrNull", "type" : { "nullable" : { @@ -1053,6 +1056,7 @@ } }, { + "accessLevel" : "internal", "name" : "stringOrUndefined", "type" : { "nullable" : { @@ -1066,6 +1070,7 @@ } }, { + "accessLevel" : "internal", "name" : "doubleOrNull", "type" : { "nullable" : { @@ -1079,6 +1084,7 @@ } }, { + "accessLevel" : "internal", "name" : "doubleOrUndefined", "type" : { "nullable" : { @@ -1092,6 +1098,7 @@ } }, { + "accessLevel" : "internal", "name" : "boolOrNull", "type" : { "nullable" : { @@ -1105,6 +1112,7 @@ } }, { + "accessLevel" : "internal", "name" : "boolOrUndefined", "type" : { "nullable" : { @@ -1118,6 +1126,7 @@ } }, { + "accessLevel" : "internal", "name" : "intOrNull", "type" : { "nullable" : { @@ -1134,6 +1143,7 @@ } }, { + "accessLevel" : "internal", "name" : "intOrUndefined", "type" : { "nullable" : { @@ -1152,6 +1162,7 @@ ], "methods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -1185,6 +1196,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -1218,6 +1230,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -1251,6 +1264,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -1284,6 +1298,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -1317,6 +1332,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -1350,6 +1366,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -1389,6 +1406,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -1431,6 +1449,7 @@ "name" : "WithOptionalJSClass", "setters" : [ { + "accessLevel" : "internal", "functionName" : "stringOrNull_set", "name" : "stringOrNull", "type" : { @@ -1445,6 +1464,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "stringOrUndefined_set", "name" : "stringOrUndefined", "type" : { @@ -1459,6 +1479,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "doubleOrNull_set", "name" : "doubleOrNull", "type" : { @@ -1473,6 +1494,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "doubleOrUndefined_set", "name" : "doubleOrUndefined", "type" : { @@ -1487,6 +1509,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "boolOrNull_set", "name" : "boolOrNull", "type" : { @@ -1501,6 +1524,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "boolOrUndefined_set", "name" : "boolOrUndefined", "type" : { @@ -1515,6 +1539,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "intOrNull_set", "name" : "intOrNull", "type" : { @@ -1532,6 +1557,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "intOrUndefined_set", "name" : "intOrUndefined", "type" : { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveParameters.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveParameters.json index cf76f3878..4b0ccfbad 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveParameters.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveParameters.json @@ -88,6 +88,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveReturn.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveReturn.json index b0398c161..cc0984536 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveReturn.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/PrimitiveReturn.json @@ -112,6 +112,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -128,6 +129,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringParameter.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringParameter.json index 75462af81..3e0fffba2 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringParameter.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringParameter.json @@ -71,6 +71,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -94,6 +95,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringReturn.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringReturn.json index 1088a5cab..2201d1721 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringReturn.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/StringReturn.json @@ -38,6 +38,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClass.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClass.json index ceda64904..25bb21080 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClass.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClass.json @@ -213,6 +213,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -236,6 +237,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosureImports.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosureImports.json index a78b1bf5d..088685b1b 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosureImports.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftClosureImports.json @@ -4,6 +4,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -66,6 +67,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStructImports.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStructImports.json index ccd3043ac..f7ddc4783 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStructImports.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftStructImports.json @@ -56,6 +56,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftTypedClosureAccess.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftTypedClosureAccess.json new file mode 100644 index 000000000..e602989a1 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftTypedClosureAccess.json @@ -0,0 +1,285 @@ +{ + "imported" : { + "children" : [ + { + "functions" : [ + + ], + "types" : [ + { + "accessLevel" : "public", + "getters" : [ + + ], + "jsName" : "PublicEvent", + "methods" : [ + + ], + "name" : "JSPublicEvent", + "setters" : [ + + ], + "staticMethods" : [ + + ] + }, + { + "accessLevel" : "package", + "getters" : [ + + ], + "jsName" : "PackageEvent", + "methods" : [ + + ], + "name" : "JSPackageEvent", + "setters" : [ + + ], + "staticMethods" : [ + + ] + }, + { + "accessLevel" : "internal", + "getters" : [ + + ], + "jsName" : "InternalEvent", + "methods" : [ + + ], + "name" : "JSInternalEvent", + "setters" : [ + + ], + "staticMethods" : [ + + ] + }, + { + "accessLevel" : "public", + "getters" : [ + + ], + "jsName" : "PublicTarget", + "methods" : [ + { + "accessLevel" : "public", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "addPublicListener", + "parameters" : [ + { + "name" : "handler", + "type" : { + "closure" : { + "_0" : { + "isAsync" : false, + "isThrows" : false, + "mangleName" : "10TestModule13JSPublicEventC_y", + "moduleName" : "TestModule", + "parameters" : [ + { + "jsObject" : { + "_0" : "JSPublicEvent" + } + } + ], + "returnType" : { + "void" : { + + } + }, + "sendingParameters" : false + }, + "useJSTypedClosure" : true + } + } + } + ], + "returnType" : { + "void" : { + + } + } + }, + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "addInternalListener", + "parameters" : [ + { + "name" : "handler", + "type" : { + "closure" : { + "_0" : { + "isAsync" : false, + "isThrows" : false, + "mangleName" : "10TestModule13JSPublicEventC_y", + "moduleName" : "TestModule", + "parameters" : [ + { + "jsObject" : { + "_0" : "JSPublicEvent" + } + } + ], + "returnType" : { + "void" : { + + } + }, + "sendingParameters" : false + }, + "useJSTypedClosure" : true + } + } + } + ], + "returnType" : { + "void" : { + + } + } + } + ], + "name" : "JSPublicTarget", + "setters" : [ + + ], + "staticMethods" : [ + + ] + }, + { + "accessLevel" : "package", + "getters" : [ + + ], + "jsName" : "PackageTarget", + "methods" : [ + { + "accessLevel" : "package", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "addPackageListener", + "parameters" : [ + { + "name" : "handler", + "type" : { + "closure" : { + "_0" : { + "isAsync" : false, + "isThrows" : false, + "mangleName" : "10TestModule14JSPackageEventC_y", + "moduleName" : "TestModule", + "parameters" : [ + { + "jsObject" : { + "_0" : "JSPackageEvent" + } + } + ], + "returnType" : { + "void" : { + + } + }, + "sendingParameters" : false + }, + "useJSTypedClosure" : true + } + } + } + ], + "returnType" : { + "void" : { + + } + } + } + ], + "name" : "JSPackageTarget", + "setters" : [ + + ], + "staticMethods" : [ + + ] + }, + { + "accessLevel" : "internal", + "getters" : [ + + ], + "jsName" : "InternalTarget", + "methods" : [ + { + "accessLevel" : "internal", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : true + }, + "name" : "addInternalListener", + "parameters" : [ + { + "name" : "handler", + "type" : { + "closure" : { + "_0" : { + "isAsync" : false, + "isThrows" : false, + "mangleName" : "10TestModule15JSInternalEventC_y", + "moduleName" : "TestModule", + "parameters" : [ + { + "jsObject" : { + "_0" : "JSInternalEvent" + } + } + ], + "returnType" : { + "void" : { + + } + }, + "sendingParameters" : false + }, + "useJSTypedClosure" : true + } + } + } + ], + "returnType" : { + "void" : { + + } + } + } + ], + "name" : "JSInternalTarget", + "setters" : [ + + ], + "staticMethods" : [ + + ] + } + ] + } + ] + }, + "moduleName" : "TestModule" +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftTypedClosureAccess.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftTypedClosureAccess.swift new file mode 100644 index 000000000..fbd181fcc --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/SwiftTypedClosureAccess.swift @@ -0,0 +1,266 @@ +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModule13JSPublicEventC_y") +fileprivate func invoke_js_callback_TestModule_10TestModule13JSPublicEventC_y_extern(_ callback: Int32, _ param0: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModule13JSPublicEventC_y_extern(_ callback: Int32, _ param0: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModule13JSPublicEventC_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModule13JSPublicEventC_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModule13JSPublicEventC_y") +fileprivate func make_swift_closure_TestModule_10TestModule13JSPublicEventC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModule13JSPublicEventC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModule13JSPublicEventC_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModule13JSPublicEventC_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModule13JSPublicEventC_y { + static func bridgeJSLift(_ callbackId: Int32) -> (JSPublicEvent) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModule13JSPublicEventC_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (JSPublicEvent) -> Void { + public init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (JSPublicEvent) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModule13JSPublicEventC_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModule13JSPublicEventC_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModule13JSPublicEventC_y") +public func _invoke_swift_closure_TestModule_10TestModule13JSPublicEventC_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(JSPublicEvent) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(JSPublicEvent.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModule14JSPackageEventC_y") +fileprivate func invoke_js_callback_TestModule_10TestModule14JSPackageEventC_y_extern(_ callback: Int32, _ param0: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModule14JSPackageEventC_y_extern(_ callback: Int32, _ param0: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModule14JSPackageEventC_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModule14JSPackageEventC_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModule14JSPackageEventC_y") +fileprivate func make_swift_closure_TestModule_10TestModule14JSPackageEventC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModule14JSPackageEventC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModule14JSPackageEventC_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModule14JSPackageEventC_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModule14JSPackageEventC_y { + static func bridgeJSLift(_ callbackId: Int32) -> (JSPackageEvent) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModule14JSPackageEventC_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (JSPackageEvent) -> Void { + package init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (JSPackageEvent) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModule14JSPackageEventC_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModule14JSPackageEventC_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModule14JSPackageEventC_y") +public func _invoke_swift_closure_TestModule_10TestModule14JSPackageEventC_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(JSPackageEvent) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(JSPackageEvent.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "invoke_js_callback_TestModule_10TestModule15JSInternalEventC_y") +fileprivate func invoke_js_callback_TestModule_10TestModule15JSInternalEventC_y_extern(_ callback: Int32, _ param0: Int32) -> Void +#else +fileprivate func invoke_js_callback_TestModule_10TestModule15JSInternalEventC_y_extern(_ callback: Int32, _ param0: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func invoke_js_callback_TestModule_10TestModule15JSInternalEventC_y(_ callback: Int32, _ param0: Int32) -> Void { + return invoke_js_callback_TestModule_10TestModule15JSInternalEventC_y_extern(callback, param0) +} + +#if arch(wasm32) +@_extern(wasm, module: "bjs", name: "make_swift_closure_TestModule_10TestModule15JSInternalEventC_y") +fileprivate func make_swift_closure_TestModule_10TestModule15JSInternalEventC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 +#else +fileprivate func make_swift_closure_TestModule_10TestModule15JSInternalEventC_y_extern(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func make_swift_closure_TestModule_10TestModule15JSInternalEventC_y(_ boxPtr: UnsafeMutableRawPointer, _ file: UnsafePointer, _ line: UInt32) -> Int32 { + return make_swift_closure_TestModule_10TestModule15JSInternalEventC_y_extern(boxPtr, file, line) +} + +private enum _BJS_Closure_10TestModule15JSInternalEventC_y { + static func bridgeJSLift(_ callbackId: Int32) -> (JSInternalEvent) -> Void { + let callback = JSObject.bridgeJSLiftParameter(callbackId) + return { [callback] param0 in + #if arch(wasm32) + let callbackValue = callback.bridgeJSLowerParameter() + let param0Value = param0.bridgeJSLowerParameter() + invoke_js_callback_TestModule_10TestModule15JSInternalEventC_y(callbackValue, param0Value) + #else + fatalError("Only available on WebAssembly") + #endif + } + } +} + +extension JSTypedClosure where Signature == (JSInternalEvent) -> Void { + init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (JSInternalEvent) -> Void) { + self.init( + makeClosure: make_swift_closure_TestModule_10TestModule15JSInternalEventC_y, + body: body, + fileID: fileID, + line: line + ) + } +} + +@_expose(wasm, "invoke_swift_closure_TestModule_10TestModule15JSInternalEventC_y") +@_cdecl("invoke_swift_closure_TestModule_10TestModule15JSInternalEventC_y") +public func _invoke_swift_closure_TestModule_10TestModule15JSInternalEventC_y(_ boxPtr: UnsafeMutableRawPointer, _ param0: Int32) -> Void { + #if arch(wasm32) + let closure = Unmanaged<_BridgeJSTypedClosureBox<(JSInternalEvent) -> Void>>.fromOpaque(boxPtr).takeUnretainedValue().closure + closure(JSInternalEvent.bridgeJSLiftParameter(param0)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_JSPublicTarget_addPublicListener") +fileprivate func bjs_JSPublicTarget_addPublicListener_extern(_ self: Int32, _ handler: Int32) -> Void +#else +fileprivate func bjs_JSPublicTarget_addPublicListener_extern(_ self: Int32, _ handler: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_JSPublicTarget_addPublicListener(_ self: Int32, _ handler: Int32) -> Void { + return bjs_JSPublicTarget_addPublicListener_extern(self, handler) +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_JSPublicTarget_addInternalListener") +fileprivate func bjs_JSPublicTarget_addInternalListener_extern(_ self: Int32, _ handler: Int32) -> Void +#else +fileprivate func bjs_JSPublicTarget_addInternalListener_extern(_ self: Int32, _ handler: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_JSPublicTarget_addInternalListener(_ self: Int32, _ handler: Int32) -> Void { + return bjs_JSPublicTarget_addInternalListener_extern(self, handler) +} + +func _$JSPublicTarget_addPublicListener(_ self: JSObject, _ handler: JSTypedClosure<(JSPublicEvent) -> Void>) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let handlerFuncRef = handler.bridgeJSLowerParameter() + bjs_JSPublicTarget_addPublicListener(selfValue, handlerFuncRef) + if let error = _swift_js_take_exception() { + throw error + } +} + +func _$JSPublicTarget_addInternalListener(_ self: JSObject, _ handler: JSTypedClosure<(JSPublicEvent) -> Void>) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let handlerFuncRef = handler.bridgeJSLowerParameter() + bjs_JSPublicTarget_addInternalListener(selfValue, handlerFuncRef) + if let error = _swift_js_take_exception() { + throw error + } +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_JSPackageTarget_addPackageListener") +fileprivate func bjs_JSPackageTarget_addPackageListener_extern(_ self: Int32, _ handler: Int32) -> Void +#else +fileprivate func bjs_JSPackageTarget_addPackageListener_extern(_ self: Int32, _ handler: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_JSPackageTarget_addPackageListener(_ self: Int32, _ handler: Int32) -> Void { + return bjs_JSPackageTarget_addPackageListener_extern(self, handler) +} + +func _$JSPackageTarget_addPackageListener(_ self: JSObject, _ handler: JSTypedClosure<(JSPackageEvent) -> Void>) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let handlerFuncRef = handler.bridgeJSLowerParameter() + bjs_JSPackageTarget_addPackageListener(selfValue, handlerFuncRef) + if let error = _swift_js_take_exception() { + throw error + } +} + +#if arch(wasm32) +@_extern(wasm, module: "TestModule", name: "bjs_JSInternalTarget_addInternalListener") +fileprivate func bjs_JSInternalTarget_addInternalListener_extern(_ self: Int32, _ handler: Int32) -> Void +#else +fileprivate func bjs_JSInternalTarget_addInternalListener_extern(_ self: Int32, _ handler: Int32) -> Void { + fatalError("Only available on WebAssembly") +} +#endif +@inline(never) fileprivate func bjs_JSInternalTarget_addInternalListener(_ self: Int32, _ handler: Int32) -> Void { + return bjs_JSInternalTarget_addInternalListener_extern(self, handler) +} + +func _$JSInternalTarget_addInternalListener(_ self: JSObject, _ handler: JSTypedClosure<(JSInternalEvent) -> Void>) throws(JSException) -> Void { + let selfValue = self.bridgeJSLowerParameter() + let handlerFuncRef = handler.bridgeJSLowerParameter() + bjs_JSInternalTarget_addInternalListener(selfValue, handlerFuncRef) + if let error = _swift_js_take_exception() { + throw error + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/VoidParameterVoidReturn.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/VoidParameterVoidReturn.json index 7f19c18bf..a7ca663cc 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/VoidParameterVoidReturn.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSCodegenTests/VoidParameterVoidReturn.json @@ -38,6 +38,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftTypedClosureAccess.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftTypedClosureAccess.d.ts new file mode 100644 index 000000000..99adf95b6 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftTypedClosureAccess.d.ts @@ -0,0 +1,33 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export interface JSPublicEvent { +} +export interface JSPackageEvent { +} +export interface JSInternalEvent { +} +export interface JSPublicTarget { + addPublicListener(handler: (arg0: JSPublicEvent) => void): void; + addInternalListener(handler: (arg0: JSPublicEvent) => void): void; +} +export interface JSPackageTarget { + addPackageListener(handler: (arg0: JSPackageEvent) => void): void; +} +export interface JSInternalTarget { + addInternalListener(handler: (arg0: JSInternalEvent) => void): void; +} +export type Exports = { +} +export type Imports = { +} +export function createInstantiator(options: { + imports: Imports; +}, swift: any): Promise<{ + addImports: (importObject: WebAssembly.Imports) => void; + setInstance: (instance: WebAssembly.Instance) => void; + createExports: (instance: WebAssembly.Instance) => Exports; +}>; \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftTypedClosureAccess.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftTypedClosureAccess.js new file mode 100644 index 000000000..4e0fd8341 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftTypedClosureAccess.js @@ -0,0 +1,329 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export async function createInstantiator(options, swift) { + let instance; + let memory; + let setException; + let decodeString; + const textDecoder = new TextDecoder("utf-8"); + const textEncoder = new TextEncoder("utf-8"); + let tmpRetString; + let tmpRetBytes; + let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; + let strStack = []; + let i32Stack = []; + let i64Stack = []; + let f32Stack = []; + let f64Stack = []; + let ptrStack = []; + const enumHelpers = {}; + const structHelpers = {}; + + let _exports = null; + let bjs = null; + const swiftClosureRegistry = (typeof FinalizationRegistry === "undefined") ? { register: () => {}, unregister: () => {} } : new FinalizationRegistry((state) => { + if (state.unregistered) { return; } + instance?.exports?.bjs_release_swift_closure(state.pointer); + }); + const makeClosure = (pointer, file, line, func) => { + const state = { pointer, file, line, unregistered: false }; + const real = (...args) => { + if (state.unregistered) { + const bytes = new Uint8Array(memory.buffer, state.file); + let length = 0; + while (bytes[length] !== 0) { length += 1; } + const fileID = decodeString(state.file, length); + throw new Error(`Attempted to call a released JSTypedClosure created at ${fileID}:${state.line}`); + } + return func(...args); + }; + real.__unregister = () => { + if (state.unregistered) { return; } + state.unregistered = true; + swiftClosureRegistry.unregister(state); + }; + swiftClosureRegistry.register(real, state, state); + return swift.memory.retain(real); + }; + + + return { + /** + * @param {WebAssembly.Imports} importObject + */ + addImports: (importObject, importsContext) => { + bjs = {}; + importObject["bjs"] = bjs; + bjs["swift_js_return_string"] = function(ptr, len) { + tmpRetString = decodeString(ptr, len); + } + bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + swift.memory.release(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["swift_js_make_js_string"] = function(ptr, len) { + return swift.memory.retain(decodeString(ptr, len)); + } + bjs["swift_js_init_memory_with_result"] = function(ptr, len) { + const target = new Uint8Array(memory.buffer, ptr, len); + target.set(tmpRetBytes); + tmpRetBytes = undefined; + } + bjs["swift_js_throw"] = function(id) { + tmpRetException = swift.memory.retainByRef(id); + } + bjs["swift_js_retain"] = function(id) { + return swift.memory.retainByRef(id); + } + bjs["swift_js_release"] = function(id) { + swift.memory.release(id); + } + bjs["swift_js_push_i32"] = function(v) { + i32Stack.push(v | 0); + } + bjs["swift_js_push_f32"] = function(v) { + f32Stack.push(Math.fround(v)); + } + bjs["swift_js_push_f64"] = function(v) { + f64Stack.push(v); + } + bjs["swift_js_push_string"] = function(ptr, len) { + const value = decodeString(ptr, len); + strStack.push(value); + } + bjs["swift_js_pop_i32"] = function() { + return i32Stack.pop(); + } + bjs["swift_js_pop_f32"] = function() { + return f32Stack.pop(); + } + bjs["swift_js_pop_f64"] = function() { + return f64Stack.pop(); + } + bjs["swift_js_push_pointer"] = function(pointer) { + ptrStack.push(pointer); + } + bjs["swift_js_pop_pointer"] = function() { + return ptrStack.pop(); + } + bjs["swift_js_push_i64"] = function(v) { + i64Stack.push(v); + } + bjs["swift_js_pop_i64"] = function() { + return i64Stack.pop(); + } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = decodeString(ptr, len); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } + bjs["swift_js_get_optional_int_presence"] = function() { + return tmpRetOptionalInt != null ? 1 : 0; + } + bjs["swift_js_get_optional_int_value"] = function() { + const value = tmpRetOptionalInt; + tmpRetOptionalInt = undefined; + return value; + } + bjs["swift_js_get_optional_string"] = function() { + const str = tmpRetString; + tmpRetString = undefined; + if (str == null) { + return -1; + } else { + const bytes = textEncoder.encode(str); + tmpRetBytes = bytes; + return bytes.length; + } + } + bjs["swift_js_get_optional_float_presence"] = function() { + return tmpRetOptionalFloat != null ? 1 : 0; + } + bjs["swift_js_get_optional_float_value"] = function() { + const value = tmpRetOptionalFloat; + tmpRetOptionalFloat = undefined; + return value; + } + bjs["swift_js_get_optional_double_presence"] = function() { + return tmpRetOptionalDouble != null ? 1 : 0; + } + bjs["swift_js_get_optional_double_value"] = function() { + const value = tmpRetOptionalDouble; + tmpRetOptionalDouble = undefined; + return value; + } + bjs["swift_js_get_optional_heap_object_pointer"] = function() { + const pointer = tmpRetOptionalHeapObject; + tmpRetOptionalHeapObject = undefined; + return pointer || 0; + } + bjs["swift_js_closure_unregister"] = function(funcRef) {} + bjs["swift_js_closure_unregister"] = function(funcRef) { + const func = swift.memory.getObject(funcRef); + func.__unregister(); + } + bjs["invoke_js_callback_TestModule_10TestModule13JSPublicEventC_y"] = function(callbackId, param0) { + try { + const callback = swift.memory.getObject(callbackId); + callback(swift.memory.getObject(param0)); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModule13JSPublicEventC_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModule13JSPublicEventC_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModule13JSPublicEventC_y(boxPtr, swift.memory.retain(param0)); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModule13JSPublicEventC_y); + } + bjs["invoke_js_callback_TestModule_10TestModule14JSPackageEventC_y"] = function(callbackId, param0) { + try { + const callback = swift.memory.getObject(callbackId); + callback(swift.memory.getObject(param0)); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModule14JSPackageEventC_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModule14JSPackageEventC_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModule14JSPackageEventC_y(boxPtr, swift.memory.retain(param0)); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModule14JSPackageEventC_y); + } + bjs["invoke_js_callback_TestModule_10TestModule15JSInternalEventC_y"] = function(callbackId, param0) { + try { + const callback = swift.memory.getObject(callbackId); + callback(swift.memory.getObject(param0)); + } catch (error) { + setException(error); + } + } + bjs["make_swift_closure_TestModule_10TestModule15JSInternalEventC_y"] = function(boxPtr, file, line) { + const lower_closure_TestModule_10TestModule15JSInternalEventC_y = function(param0) { + instance.exports.invoke_swift_closure_TestModule_10TestModule15JSInternalEventC_y(boxPtr, swift.memory.retain(param0)); + if (tmpRetException) { + const error = swift.memory.getObject(tmpRetException); + swift.memory.release(tmpRetException); + tmpRetException = undefined; + throw error; + } + }; + return makeClosure(boxPtr, file, line, lower_closure_TestModule_10TestModule15JSInternalEventC_y); + } + const TestModule = importObject["TestModule"] = importObject["TestModule"] || {}; + TestModule["bjs_JSPublicTarget_addPublicListener"] = function bjs_JSPublicTarget_addPublicListener(self, handler) { + try { + swift.memory.getObject(self).addPublicListener(swift.memory.getObject(handler)); + } catch (error) { + setException(error); + } + } + TestModule["bjs_JSPublicTarget_addInternalListener"] = function bjs_JSPublicTarget_addInternalListener(self, handler) { + try { + swift.memory.getObject(self).addInternalListener(swift.memory.getObject(handler)); + } catch (error) { + setException(error); + } + } + TestModule["bjs_JSPackageTarget_addPackageListener"] = function bjs_JSPackageTarget_addPackageListener(self, handler) { + try { + swift.memory.getObject(self).addPackageListener(swift.memory.getObject(handler)); + } catch (error) { + setException(error); + } + } + TestModule["bjs_JSInternalTarget_addInternalListener"] = function bjs_JSInternalTarget_addInternalListener(self, handler) { + try { + swift.memory.getObject(self).addInternalListener(swift.memory.getObject(handler)); + } catch (error) { + setException(error); + } + } + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + decodeString = (ptr, len) => { const bytes = new Uint8Array(memory.buffer, ptr >>> 0, len >>> 0); return textDecoder.decode(bytes); } + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + const exports = { + }; + _exports = exports; + return exports; + }, + } +} \ No newline at end of file From e10b33e713554d0d7be989ccb2042b9e36026f18 Mon Sep 17 00:00:00 2001 From: Matthew Ayers Date: Wed, 29 Apr 2026 11:37:07 -0400 Subject: [PATCH 2/4] [BridgeJS] Address PR feedback and refresh generated artifacts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Make `accessLevel` decode-tolerant on imported skeleton structs (`ImportedFunctionSkeleton`, `ImportedConstructorSkeleton`, `ImportedGetterSkeleton`, `ImportedSetterSkeleton`, `ImportedTypeSkeleton`) by writing explicit `init(from:)` decoders that fall back to `.internal` when the key is missing. Without this, any pre-existing skeleton JSON without the new field fails decoding — the `build-examples` CI job hit `DecodingError.keyNotFound` for `accessLevel` against externally consumed skeletons. - Extract a private `recordSignature` helper so `visitClosure` and `recordInjectedSignature` share a single merge implementation. - Assert in `withAccessLevel(rawLevel:)` so unknown access strings ("open", "private", future schema additions) surface in debug builds instead of silently inheriting the outer level. - Document the `.internal` seeding assumption on `ClosureSignatureCollectorVisitor.init(moduleName:signatures:)`. - Regenerate the BridgeJS pre-generated artifacts under Benchmarks/, Examples/PlayBridgeJS/, Tests/BridgeJSIdentityTests/, and Tests/BridgeJSRuntimeTests/ via `./Utilities/bridge-js-generate.sh`, per CONTRIBUTING.md. The runtime-tests Swift output now emits `public init` on three `JSTypedClosure` extensions whose signatures surface through public exported types. --- .../Generated/JavaScript/BridgeJS.json | 3 + .../Generated/JavaScript/BridgeJS.json | 3 + .../BridgeJSSkeleton/BridgeJSSkeleton.swift | 111 +++++++++++- .../Generated/JavaScript/BridgeJS.json | 2 + .../Generated/BridgeJS.swift | 6 +- .../Generated/JavaScript/BridgeJS.json | 164 ++++++++++++++++++ 6 files changed, 279 insertions(+), 10 deletions(-) diff --git a/Benchmarks/Sources/Generated/JavaScript/BridgeJS.json b/Benchmarks/Sources/Generated/JavaScript/BridgeJS.json index 0bddddfb6..cc1f6f933 100644 --- a/Benchmarks/Sources/Generated/JavaScript/BridgeJS.json +++ b/Benchmarks/Sources/Generated/JavaScript/BridgeJS.json @@ -3339,6 +3339,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -3355,6 +3356,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -3378,6 +3380,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.json b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.json index 1a21916ee..8dcf73f79 100644 --- a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.json +++ b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.json @@ -242,6 +242,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -260,11 +261,13 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], "methods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index 856933008..853fb3f98 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -612,12 +612,22 @@ public struct BridgeSkeletonWalker { } /// String-typed convenience: maps `"public"`/`"package"`/`"internal"` from - /// `Exported*.explicitAccessControl` to the typed enum. + /// `Exported*.explicitAccessControl` to the typed enum. Unknown strings + /// (e.g. `"open"`, `"private"`) hit the assert in debug builds and inherit + /// the outer level in release — the `@JSExport` macros reject those cases + /// upstream, so this is a defensive guard against future schema drift. private mutating func withAccessLevel( _ rawLevel: String?, _ body: (inout BridgeSkeletonWalker) -> Void ) { - withAccessLevel(rawLevel.flatMap(BridgeJSAccessLevel.init(rawValue:)), body) + let level: BridgeJSAccessLevel? + if let rawLevel { + level = BridgeJSAccessLevel(rawValue: rawLevel) + assert(level != nil, "Unexpected access level string: \(rawLevel)") + } else { + level = nil + } + withAccessLevel(level, body) } } @@ -1063,6 +1073,22 @@ public struct ImportedFunctionSkeleton: Codable { self.accessLevel = accessLevel } + private enum CodingKeys: String, CodingKey { + case name, jsName, from, parameters, returnType, effects, documentation, accessLevel + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.name = try container.decode(String.self, forKey: .name) + self.jsName = try container.decodeIfPresent(String.self, forKey: .jsName) + self.from = try container.decodeIfPresent(JSImportFrom.self, forKey: .from) + self.parameters = try container.decode([Parameter].self, forKey: .parameters) + self.returnType = try container.decode(BridgeType.self, forKey: .returnType) + self.effects = try container.decode(Effects.self, forKey: .effects) + self.documentation = try container.decodeIfPresent(String.self, forKey: .documentation) + self.accessLevel = try container.decodeIfPresent(BridgeJSAccessLevel.self, forKey: .accessLevel) ?? .internal + } + public func abiName(context: ImportedTypeSkeleton?) -> String { return abiName(context: context, operation: nil) } @@ -1087,6 +1113,16 @@ public struct ImportedConstructorSkeleton: Codable { self.accessLevel = accessLevel } + private enum CodingKeys: String, CodingKey { + case parameters, accessLevel + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.parameters = try container.decode([Parameter].self, forKey: .parameters) + self.accessLevel = try container.decodeIfPresent(BridgeJSAccessLevel.self, forKey: .accessLevel) ?? .internal + } + public func abiName(context: ImportedTypeSkeleton) -> String { return ABINameGenerator.generateImportedABIName( baseName: "init", @@ -1126,6 +1162,21 @@ public struct ImportedGetterSkeleton: Codable { self.accessLevel = accessLevel } + private enum CodingKeys: String, CodingKey { + case name, jsName, from, type, documentation, functionName, accessLevel + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.name = try container.decode(String.self, forKey: .name) + self.jsName = try container.decodeIfPresent(String.self, forKey: .jsName) + self.from = try container.decodeIfPresent(JSImportFrom.self, forKey: .from) + self.type = try container.decode(BridgeType.self, forKey: .type) + self.documentation = try container.decodeIfPresent(String.self, forKey: .documentation) + self.functionName = try container.decodeIfPresent(String.self, forKey: .functionName) + self.accessLevel = try container.decodeIfPresent(BridgeJSAccessLevel.self, forKey: .accessLevel) ?? .internal + } + public func abiName(context: ImportedTypeSkeleton?) -> String { if let functionName = functionName { return ABINameGenerator.generateImportedABIName( @@ -1169,6 +1220,20 @@ public struct ImportedSetterSkeleton: Codable { self.accessLevel = accessLevel } + private enum CodingKeys: String, CodingKey { + case name, jsName, type, documentation, functionName, accessLevel + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.name = try container.decode(String.self, forKey: .name) + self.jsName = try container.decodeIfPresent(String.self, forKey: .jsName) + self.type = try container.decode(BridgeType.self, forKey: .type) + self.documentation = try container.decodeIfPresent(String.self, forKey: .documentation) + self.functionName = try container.decodeIfPresent(String.self, forKey: .functionName) + self.accessLevel = try container.decodeIfPresent(BridgeJSAccessLevel.self, forKey: .accessLevel) ?? .internal + } + public func abiName(context: ImportedTypeSkeleton?) -> String { if let functionName = functionName { return ABINameGenerator.generateImportedABIName( @@ -1224,6 +1289,24 @@ public struct ImportedTypeSkeleton: Codable { self.documentation = documentation self.accessLevel = accessLevel } + + private enum CodingKeys: String, CodingKey { + case name, jsName, from, constructor, methods, staticMethods, getters, setters, documentation, accessLevel + } + + public init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.name = try container.decode(String.self, forKey: .name) + self.jsName = try container.decodeIfPresent(String.self, forKey: .jsName) + self.from = try container.decodeIfPresent(JSImportFrom.self, forKey: .from) + self.constructor = try container.decodeIfPresent(ImportedConstructorSkeleton.self, forKey: .constructor) + self.methods = try container.decode([ImportedFunctionSkeleton].self, forKey: .methods) + self.staticMethods = try container.decode([ImportedFunctionSkeleton].self, forKey: .staticMethods) + self.getters = try container.decode([ImportedGetterSkeleton].self, forKey: .getters) + self.setters = try container.decode([ImportedSetterSkeleton].self, forKey: .setters) + self.documentation = try container.decodeIfPresent(String.self, forKey: .documentation) + self.accessLevel = try container.decodeIfPresent(BridgeJSAccessLevel.self, forKey: .accessLevel) ?? .internal + } } public struct ImportedFileSkeleton: Codable { @@ -1298,6 +1381,12 @@ public struct ClosureSignatureCollectorVisitor: BridgeSkeletonVisitor { public var signatures: Set { Set(signatureAccessLevels.keys) } let moduleName: String + /// Convenience for callers that only need to seed signatures without + /// access metadata (e.g. exported-side walking, where closure init access + /// is irrelevant because the synthesized init isn't surfaced to consumers). + /// All seeded signatures default to `.internal`; if a seeded signature is + /// later observed with a more permissive access level, the merge in + /// `recordSignature` upgrades it. public init(moduleName: String, signatures: Set = []) { self.moduleName = moduleName for signature in signatures { @@ -1309,6 +1398,18 @@ public struct ClosureSignatureCollectorVisitor: BridgeSkeletonVisitor { _ signature: ClosureSignature, useJSTypedClosure: Bool, accessLevel: BridgeJSAccessLevel + ) { + recordSignature(signature, accessLevel: accessLevel) + } + + /// Insert `signature` at `accessLevel`, or upgrade the existing level to + /// the more permissive of the two. Centralizing the merge here keeps + /// `visitClosure` and `recordInjectedSignature` in lockstep — if the + /// merge policy ever needs to change (e.g. adding a diagnostic for + /// conflicting levels), there's only one place to update. + private mutating func recordSignature( + _ signature: ClosureSignature, + accessLevel: BridgeJSAccessLevel ) { if let existing = signatureAccessLevels[signature] { signatureAccessLevels[signature] = max(existing, accessLevel) @@ -1367,11 +1468,7 @@ public struct ClosureSignatureCollectorVisitor: BridgeSkeletonVisitor { _ signature: ClosureSignature, for function: ImportedFunctionSkeleton ) { - if let existing = signatureAccessLevels[signature] { - signatureAccessLevels[signature] = max(existing, function.accessLevel) - } else { - signatureAccessLevels[signature] = function.accessLevel - } + recordSignature(signature, accessLevel: function.accessLevel) } } diff --git a/Tests/BridgeJSIdentityTests/Generated/JavaScript/BridgeJS.json b/Tests/BridgeJSIdentityTests/Generated/JavaScript/BridgeJS.json index d30ca00f8..a052f23d7 100644 --- a/Tests/BridgeJSIdentityTests/Generated/JavaScript/BridgeJS.json +++ b/Tests/BridgeJSIdentityTests/Generated/JavaScript/BridgeJS.json @@ -410,6 +410,7 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], @@ -422,6 +423,7 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift index a37a7e4c5..db8962089 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.swift @@ -369,7 +369,7 @@ private enum _BJS_Closure_20BridgeJSRuntimeTests7GreeterC_SS { } extension JSTypedClosure where Signature == (Greeter) -> String { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Greeter) -> String) { + public init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (Greeter) -> String) { self.init( makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTests7GreeterC_SS, body: body, @@ -687,7 +687,7 @@ private enum _BJS_Closure_20BridgeJSRuntimeTestsSS_7GreeterC { } extension JSTypedClosure where Signature == (String) -> Greeter { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (String) -> Greeter) { + public init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (String) -> Greeter) { self.init( makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_7GreeterC, body: body, @@ -753,7 +753,7 @@ private enum _BJS_Closure_20BridgeJSRuntimeTestsSS_SS { } extension JSTypedClosure where Signature == (String) -> String { - init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (String) -> String) { + public init(fileID: StaticString = #fileID, line: UInt32 = #line, _ body: @escaping (String) -> String) { self.init( makeClosure: make_swift_closure_BridgeJSRuntimeTests_20BridgeJSRuntimeTestsSS_SS, body: body, diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json index dd4362fc1..c7e2c6008 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.json @@ -16410,7 +16410,9 @@ ], "types" : [ { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "id", @@ -16424,6 +16426,7 @@ }, "getters" : [ { + "accessLevel" : "internal", "name" : "id", "type" : { "string" : { @@ -16444,6 +16447,7 @@ ] }, { + "accessLevel" : "internal", "getters" : [ ], @@ -16456,6 +16460,7 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -16489,6 +16494,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -16526,6 +16532,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -16557,6 +16564,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -16588,6 +16596,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -16619,6 +16628,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -16650,6 +16660,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -16681,6 +16692,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -16712,6 +16724,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -16759,6 +16772,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -16800,6 +16814,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -16841,6 +16856,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -16882,6 +16898,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -16923,6 +16940,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -16964,6 +16982,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -16991,6 +17010,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -17011,6 +17031,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -17036,6 +17057,7 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], @@ -17048,6 +17070,7 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, @@ -17064,6 +17087,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, @@ -17087,6 +17111,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, @@ -17110,6 +17135,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, @@ -17133,6 +17159,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, @@ -17166,6 +17193,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, @@ -17199,6 +17227,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, @@ -17230,6 +17259,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, @@ -17261,6 +17291,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, @@ -17292,6 +17323,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, @@ -17326,6 +17358,7 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], @@ -17338,6 +17371,7 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -17376,6 +17410,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -17414,6 +17449,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -17476,6 +17512,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -17526,6 +17563,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -17576,6 +17614,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -17626,6 +17665,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -17677,6 +17717,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -17719,6 +17760,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -17761,6 +17803,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -17820,6 +17863,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -17879,6 +17923,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -17946,6 +17991,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -17984,6 +18030,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18027,6 +18074,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18065,6 +18113,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18081,6 +18130,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18100,6 +18150,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18125,6 +18176,7 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], @@ -18137,6 +18189,7 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18162,6 +18215,7 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], @@ -18174,6 +18228,7 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18211,6 +18266,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18242,6 +18298,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18273,6 +18330,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18304,6 +18362,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18335,6 +18394,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18383,7 +18443,9 @@ ], "types" : [ { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "value", @@ -18397,6 +18459,7 @@ }, "getters" : [ { + "accessLevel" : "internal", "name" : "value", "type" : { "string" : { @@ -18421,6 +18484,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18437,6 +18501,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18460,6 +18525,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18483,6 +18549,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18506,6 +18573,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18529,6 +18597,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18552,6 +18621,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18575,6 +18645,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18598,6 +18669,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18621,6 +18693,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18646,6 +18719,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, @@ -18662,6 +18736,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : true, "isStatic" : false, @@ -18685,6 +18760,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18702,6 +18778,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18728,6 +18805,7 @@ ], "globalGetters" : [ { + "accessLevel" : "internal", "from" : "global", "name" : "globalObject1", "type" : { @@ -18739,7 +18817,9 @@ ], "types" : [ { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "name", @@ -18761,6 +18841,7 @@ }, "getters" : [ { + "accessLevel" : "internal", "name" : "name", "type" : { "string" : { @@ -18769,6 +18850,7 @@ } }, { + "accessLevel" : "internal", "name" : "prefix", "type" : { "string" : { @@ -18779,6 +18861,7 @@ ], "methods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18795,6 +18878,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18821,6 +18905,7 @@ "name" : "JsGreeter", "setters" : [ { + "accessLevel" : "internal", "functionName" : "name_set", "name" : "name", "type" : { @@ -18835,8 +18920,10 @@ ] }, { + "accessLevel" : "internal", "getters" : [ { + "accessLevel" : "internal", "name" : "temperature", "type" : { "double" : { @@ -18845,6 +18932,7 @@ } }, { + "accessLevel" : "internal", "name" : "description", "type" : { "string" : { @@ -18853,6 +18941,7 @@ } }, { + "accessLevel" : "internal", "name" : "humidity", "type" : { "double" : { @@ -18867,6 +18956,7 @@ "name" : "WeatherData", "setters" : [ { + "accessLevel" : "internal", "functionName" : "temperature_set", "name" : "temperature", "type" : { @@ -18876,6 +18966,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "description_set", "name" : "description", "type" : { @@ -18885,6 +18976,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "humidity_set", "name" : "humidity", "type" : { @@ -18899,7 +18991,9 @@ ] }, { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ ] @@ -18910,6 +19004,7 @@ "jsName" : "$WeirdClass", "methods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18936,7 +19031,9 @@ ] }, { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "value", @@ -18953,6 +19050,7 @@ ], "methods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18975,6 +19073,7 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -18998,6 +19097,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19014,6 +19114,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19030,6 +19131,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19049,7 +19151,9 @@ ] }, { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "name", @@ -19080,6 +19184,7 @@ "from" : "global", "getters" : [ { + "accessLevel" : "internal", "name" : "name", "type" : { "string" : { @@ -19088,6 +19193,7 @@ } }, { + "accessLevel" : "internal", "name" : "age", "type" : { "double" : { @@ -19096,6 +19202,7 @@ } }, { + "accessLevel" : "internal", "name" : "isCat", "type" : { "bool" : { @@ -19106,6 +19213,7 @@ ], "methods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19122,6 +19230,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19141,6 +19250,7 @@ "name" : "Animal", "setters" : [ { + "accessLevel" : "internal", "functionName" : "name_set", "name" : "name", "type" : { @@ -19150,6 +19260,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "age_set", "name" : "age", "type" : { @@ -19159,6 +19270,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "isCat_set", "name" : "isCat", "type" : { @@ -19177,6 +19289,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19232,6 +19345,7 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], @@ -19244,6 +19358,7 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19273,6 +19388,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19302,6 +19418,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19331,6 +19448,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19360,6 +19478,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19389,6 +19508,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19418,6 +19538,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19447,6 +19568,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19476,6 +19598,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19505,6 +19628,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19534,6 +19658,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19559,7 +19684,9 @@ ], "types" : [ { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ { "name" : "numbers", @@ -19592,6 +19719,7 @@ }, "getters" : [ { + "accessLevel" : "internal", "name" : "numbers", "type" : { "array" : { @@ -19607,6 +19735,7 @@ } }, { + "accessLevel" : "internal", "name" : "labels", "type" : { "array" : { @@ -19621,6 +19750,7 @@ ], "methods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19658,6 +19788,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19689,6 +19820,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19719,6 +19851,7 @@ "name" : "JSClassWithArrayMembers", "setters" : [ { + "accessLevel" : "internal", "functionName" : "numbers_set", "name" : "numbers", "type" : { @@ -19735,6 +19868,7 @@ } }, { + "accessLevel" : "internal", "functionName" : "labels_set", "name" : "labels", "type" : { @@ -19753,6 +19887,7 @@ ] }, { + "accessLevel" : "internal", "getters" : [ ], @@ -19765,6 +19900,7 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19816,7 +19952,9 @@ ], "types" : [ { + "accessLevel" : "internal", "constructor" : { + "accessLevel" : "internal", "parameters" : [ ] @@ -19837,7 +19975,9 @@ ] }, { + "accessLevel" : "public", "constructor" : { + "accessLevel" : "public", "parameters" : [ ] @@ -19858,7 +19998,9 @@ ] }, { + "accessLevel" : "package", "constructor" : { + "accessLevel" : "package", "parameters" : [ ] @@ -19883,6 +20025,7 @@ { "functions" : [ { + "accessLevel" : "package", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19899,6 +20042,7 @@ } }, { + "accessLevel" : "public", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19915,6 +20059,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19931,6 +20076,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19947,6 +20093,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -19973,6 +20120,7 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], @@ -19985,6 +20133,7 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -20024,6 +20173,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -20063,6 +20213,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -20096,6 +20247,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -20129,6 +20281,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -20170,6 +20323,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -20211,6 +20365,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -20252,6 +20407,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -20293,6 +20449,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -20315,6 +20472,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -20334,6 +20492,7 @@ ], "types" : [ { + "accessLevel" : "internal", "getters" : [ ], @@ -20346,6 +20505,7 @@ ], "staticMethods" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -20369,6 +20529,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -20392,6 +20553,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -20425,6 +20587,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, @@ -20448,6 +20611,7 @@ } }, { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, From bd1828dd2b0b6c912750274eb0ec9e388da8feaf Mon Sep 17 00:00:00 2001 From: Matthew Ayers Date: Wed, 29 Apr 2026 18:43:43 -0400 Subject: [PATCH 3/4] [BridgeJS] Refresh identity tests skeleton after merge with main #731 added the GC lifecycle test (with new imported function entries) to main while this branch was open. Re-running the BridgeJS regen against the merged tree fills in the `accessLevel` field on the new entries that were absent at merge time. --- Tests/BridgeJSIdentityTests/Generated/JavaScript/BridgeJS.json | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/BridgeJSIdentityTests/Generated/JavaScript/BridgeJS.json b/Tests/BridgeJSIdentityTests/Generated/JavaScript/BridgeJS.json index 640b47934..851aa035a 100644 --- a/Tests/BridgeJSIdentityTests/Generated/JavaScript/BridgeJS.json +++ b/Tests/BridgeJSIdentityTests/Generated/JavaScript/BridgeJS.json @@ -407,6 +407,7 @@ { "functions" : [ { + "accessLevel" : "internal", "effects" : { "isAsync" : false, "isStatic" : false, From d434b3185a7d52b80d77c1af14bc27bf708298b9 Mon Sep 17 00:00:00 2001 From: Matthew Ayers Date: Wed, 29 Apr 2026 22:02:11 -0400 Subject: [PATCH 4/4] ci: retry flaky JSPromiseTests.testPromiseAndTimer