Skip to content

Commit 1603fce

Browse files
Merge pull request #429 from swiftwasm/yt/fix-skip-invalid-swift-id
BridgeJS: Fix build when using `@JS` with explicit access control
2 parents 753b8db + ef0c6cb commit 1603fce

File tree

15 files changed

+336
-1
lines changed

15 files changed

+336
-1
lines changed

Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,9 +426,14 @@ public class ExportSwift {
426426
let effectiveNamespace = computedNamespace ?? attributeNamespace
427427

428428
let swiftCallName = ExportSwift.computeSwiftCallName(for: node, itemName: name)
429+
let explicitAccessControl = computeExplicitAtLeastInternalAccessControl(
430+
for: node,
431+
message: "Class visibility must be at least internal"
432+
)
429433
let exportedClass = ExportedClass(
430434
name: name,
431435
swiftCallName: swiftCallName,
436+
explicitAccessControl: explicitAccessControl,
432437
constructor: nil,
433438
methods: [],
434439
properties: [],
@@ -520,9 +525,14 @@ public class ExportSwift {
520525
}
521526

522527
let swiftCallName = ExportSwift.computeSwiftCallName(for: node, itemName: enumName)
528+
let explicitAccessControl = computeExplicitAtLeastInternalAccessControl(
529+
for: node,
530+
message: "Enum visibility must be at least internal"
531+
)
523532
let exportedEnum = ExportedEnum(
524533
name: enumName,
525534
swiftCallName: swiftCallName,
535+
explicitAccessControl: explicitAccessControl,
526536
cases: currentEnum.cases,
527537
rawType: currentEnum.rawType,
528538
namespace: effectiveNamespace,
@@ -615,6 +625,25 @@ public class ExportSwift {
615625

616626
return namespace.isEmpty ? nil : namespace
617627
}
628+
629+
/// Requires the node to have at least internal access control.
630+
private func computeExplicitAtLeastInternalAccessControl(
631+
for node: some WithModifiersSyntax,
632+
message: String
633+
) -> String? {
634+
guard let accessControl = node.explicitAccessControl else {
635+
return nil
636+
}
637+
guard accessControl.isAtLeastInternal else {
638+
diagnose(
639+
node: accessControl,
640+
message: message,
641+
hint: "Use `internal`, `package` or `public` access control"
642+
)
643+
return nil
644+
}
645+
return accessControl.name.text
646+
}
618647
}
619648

620649
func parseSingleFile(_ sourceFile: SourceFileSyntax) throws -> [DiagnosticError] {
@@ -1130,9 +1159,11 @@ public class ExportSwift {
11301159
let wrapFunctionName = "_bjs_\(klass.name)_wrap"
11311160
let externFunctionName = "bjs_\(klass.name)_wrap"
11321161

1162+
// If the class has an explicit access control, we need to add it to the extension declaration.
1163+
let accessControl = klass.explicitAccessControl.map { "\($0) " } ?? ""
11331164
return """
11341165
extension \(raw: klass.swiftCallName): ConvertibleToJSValue, _BridgedSwiftHeapObject {
1135-
var jsValue: JSValue {
1166+
\(raw: accessControl)var jsValue: JSValue {
11361167
#if arch(wasm32)
11371168
@_extern(wasm, module: "\(raw: moduleName)", name: "\(raw: externFunctionName)")
11381169
func \(raw: wrapFunctionName)(_: UnsafeMutableRawPointer) -> Int32
@@ -1309,3 +1340,51 @@ extension BridgeType {
13091340
}
13101341
}
13111342
}
1343+
1344+
extension DeclModifierSyntax {
1345+
var isAccessControl: Bool {
1346+
switch self.name.tokenKind {
1347+
case .keyword(.private),
1348+
.keyword(.fileprivate),
1349+
.keyword(.internal),
1350+
.keyword(.package),
1351+
.keyword(.public),
1352+
.keyword(.open):
1353+
return true
1354+
default:
1355+
return false
1356+
}
1357+
}
1358+
1359+
var isAtLeastInternal: Bool {
1360+
switch self.name.tokenKind {
1361+
case .keyword(.private): false
1362+
case .keyword(.fileprivate): false
1363+
case .keyword(.internal): true
1364+
case .keyword(.package): true
1365+
case .keyword(.public): true
1366+
case .keyword(.open): true
1367+
default: false
1368+
}
1369+
}
1370+
1371+
var isAtLeastPackage: Bool {
1372+
switch self.name.tokenKind {
1373+
case .keyword(.private): false
1374+
case .keyword(.fileprivate): false
1375+
case .keyword(.internal): true
1376+
case .keyword(.package): true
1377+
case .keyword(.public): true
1378+
case .keyword(.open): true
1379+
default: false
1380+
}
1381+
}
1382+
}
1383+
1384+
extension WithModifiersSyntax {
1385+
var explicitAccessControl: DeclModifierSyntax? {
1386+
return self.modifiers.first { modifier in
1387+
modifier.isAccessControl
1388+
}
1389+
}
1390+
}

Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ public enum EnumEmitStyle: String, Codable {
104104
public struct ExportedEnum: Codable, Equatable {
105105
public let name: String
106106
public let swiftCallName: String
107+
public let explicitAccessControl: String?
107108
public let cases: [EnumCase]
108109
public let rawType: String?
109110
public let namespace: [String]?
@@ -121,13 +122,15 @@ public struct ExportedEnum: Codable, Equatable {
121122
public init(
122123
name: String,
123124
swiftCallName: String,
125+
explicitAccessControl: String?,
124126
cases: [EnumCase],
125127
rawType: String?,
126128
namespace: [String]?,
127129
emitStyle: EnumEmitStyle
128130
) {
129131
self.name = name
130132
self.swiftCallName = swiftCallName
133+
self.explicitAccessControl = explicitAccessControl
131134
self.cases = cases
132135
self.rawType = rawType
133136
self.namespace = namespace
@@ -172,6 +175,7 @@ public struct ExportedFunction: Codable {
172175
public struct ExportedClass: Codable {
173176
public var name: String
174177
public var swiftCallName: String
178+
public var explicitAccessControl: String?
175179
public var constructor: ExportedConstructor?
176180
public var methods: [ExportedFunction]
177181
public var properties: [ExportedProperty]
@@ -180,13 +184,15 @@ public struct ExportedClass: Codable {
180184
public init(
181185
name: String,
182186
swiftCallName: String,
187+
explicitAccessControl: String?,
183188
constructor: ExportedConstructor? = nil,
184189
methods: [ExportedFunction],
185190
properties: [ExportedProperty] = [],
186191
namespace: [String]? = nil
187192
) {
188193
self.name = name
189194
self.swiftCallName = swiftCallName
195+
self.explicitAccessControl = explicitAccessControl
190196
self.constructor = constructor
191197
self.methods = methods
192198
self.properties = properties

Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/EnumCase.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,7 @@
2424

2525
@JS func setTSDirection(_ direction: TSDirection)
2626
@JS func getTSDirection() -> TSDirection
27+
28+
@JS public enum PublicStatus {
29+
case success
30+
}

Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/SwiftClass.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@
1515
@JS func takeGreeter(greeter: Greeter) {
1616
print(greeter.greet())
1717
}
18+
19+
@JS public class PublicGreeter {}
20+
@JS package class PackageGreeter {}

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ export enum TSDirection {
2626
West = 3,
2727
}
2828

29+
export const PublicStatus: {
30+
readonly Success: 0;
31+
};
32+
export type PublicStatus = typeof PublicStatus[keyof typeof PublicStatus];
33+
2934
export type Exports = {
3035
setDirection(direction: Direction): void;
3136
getDirection(): Direction;

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ export const TSDirection = {
2424
West: 3,
2525
};
2626

27+
export const PublicStatus = {
28+
Success: 0,
29+
};
30+
2731

2832
export async function createInstantiator(options, swift) {
2933
let instance;

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,18 @@ export interface Greeter extends SwiftHeapObject {
1616
changeName(name: string): void;
1717
name: string;
1818
}
19+
export interface PublicGreeter extends SwiftHeapObject {
20+
}
21+
export interface PackageGreeter extends SwiftHeapObject {
22+
}
1923
export type Exports = {
2024
Greeter: {
2125
new(name: string): Greeter;
2226
}
27+
PublicGreeter: {
28+
}
29+
PackageGreeter: {
30+
}
2331
takeGreeter(greeter: Greeter): void;
2432
}
2533
export type Imports = {

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ export async function createInstantiator(options, swift) {
5757
const obj = Greeter.__construct(pointer);
5858
return swift.memory.retain(obj);
5959
};
60+
importObject["TestModule"]["bjs_PublicGreeter_wrap"] = function(pointer) {
61+
const obj = PublicGreeter.__construct(pointer);
62+
return swift.memory.retain(obj);
63+
};
64+
importObject["TestModule"]["bjs_PackageGreeter_wrap"] = function(pointer) {
65+
const obj = PackageGreeter.__construct(pointer);
66+
return swift.memory.retain(obj);
67+
};
6068

6169
},
6270
setInstance: (i) => {
@@ -126,8 +134,22 @@ export async function createInstantiator(options, swift) {
126134
swift.memory.release(valueId);
127135
}
128136
}
137+
class PublicGreeter extends SwiftHeapObject {
138+
static __construct(ptr) {
139+
return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PublicGreeter_deinit, PublicGreeter.prototype);
140+
}
141+
142+
}
143+
class PackageGreeter extends SwiftHeapObject {
144+
static __construct(ptr) {
145+
return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PackageGreeter_deinit, PackageGreeter.prototype);
146+
}
147+
148+
}
129149
return {
130150
Greeter,
151+
PublicGreeter,
152+
PackageGreeter,
131153
takeGreeter: function bjs_takeGreeter(greeter) {
132154
instance.exports.bjs_takeGreeter(greeter.pointer);
133155
},

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumCase.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,20 @@
8989
"emitStyle" : "tsEnum",
9090
"name" : "TSDirection",
9191
"swiftCallName" : "TSDirection"
92+
},
93+
{
94+
"cases" : [
95+
{
96+
"associatedValues" : [
97+
98+
],
99+
"name" : "success"
100+
}
101+
],
102+
"emitStyle" : "const",
103+
"explicitAccessControl" : "public",
104+
"name" : "PublicStatus",
105+
"swiftCallName" : "PublicStatus"
92106
}
93107
],
94108
"functions" : [

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumCase.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,37 @@ extension TSDirection {
131131
}
132132
}
133133

134+
extension PublicStatus {
135+
@_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 {
136+
return bridgeJSRawValue
137+
}
138+
@_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn(_ value: Int32) -> PublicStatus {
139+
return PublicStatus(bridgeJSRawValue: value)!
140+
}
141+
@_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ value: Int32) -> PublicStatus {
142+
return PublicStatus(bridgeJSRawValue: value)!
143+
}
144+
@_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() -> Int32 {
145+
return bridgeJSRawValue
146+
}
147+
148+
private init?(bridgeJSRawValue: Int32) {
149+
switch bridgeJSRawValue {
150+
case 0:
151+
self = .success
152+
default:
153+
return nil
154+
}
155+
}
156+
157+
private var bridgeJSRawValue: Int32 {
158+
switch self {
159+
case .success:
160+
return 0
161+
}
162+
}
163+
}
164+
134165
@_expose(wasm, "bjs_setDirection")
135166
@_cdecl("bjs_setDirection")
136167
public func _bjs_setDirection(direction: Int32) -> Void {

0 commit comments

Comments
 (0)