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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 80 additions & 1 deletion Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -426,9 +426,14 @@ public class ExportSwift {
let effectiveNamespace = computedNamespace ?? attributeNamespace

let swiftCallName = ExportSwift.computeSwiftCallName(for: node, itemName: name)
let explicitAccessControl = computeExplicitAtLeastInternalAccessControl(
for: node,
message: "Class visibility must be at least internal"
)
let exportedClass = ExportedClass(
name: name,
swiftCallName: swiftCallName,
explicitAccessControl: explicitAccessControl,
constructor: nil,
methods: [],
properties: [],
Expand Down Expand Up @@ -520,9 +525,14 @@ public class ExportSwift {
}

let swiftCallName = ExportSwift.computeSwiftCallName(for: node, itemName: enumName)
let explicitAccessControl = computeExplicitAtLeastInternalAccessControl(
for: node,
message: "Enum visibility must be at least internal"
)
let exportedEnum = ExportedEnum(
name: enumName,
swiftCallName: swiftCallName,
explicitAccessControl: explicitAccessControl,
cases: currentEnum.cases,
rawType: currentEnum.rawType,
namespace: effectiveNamespace,
Expand Down Expand Up @@ -615,6 +625,25 @@ public class ExportSwift {

return namespace.isEmpty ? nil : namespace
}

/// Requires the node to have at least internal access control.
private func computeExplicitAtLeastInternalAccessControl(
for node: some WithModifiersSyntax,
message: String
) -> String? {
guard let accessControl = node.explicitAccessControl else {
return nil
}
guard accessControl.isAtLeastInternal else {
diagnose(
node: accessControl,
message: message,
hint: "Use `internal`, `package` or `public` access control"
)
return nil
}
return accessControl.name.text
}
}

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

// If the class has an explicit access control, we need to add it to the extension declaration.
let accessControl = klass.explicitAccessControl.map { "\($0) " } ?? ""
return """
extension \(raw: klass.swiftCallName): ConvertibleToJSValue, _BridgedSwiftHeapObject {
var jsValue: JSValue {
\(raw: accessControl)var jsValue: JSValue {
#if arch(wasm32)
@_extern(wasm, module: "\(raw: moduleName)", name: "\(raw: externFunctionName)")
func \(raw: wrapFunctionName)(_: UnsafeMutableRawPointer) -> Int32
Expand Down Expand Up @@ -1309,3 +1340,51 @@ extension BridgeType {
}
}
}

extension DeclModifierSyntax {
var isAccessControl: Bool {
switch self.name.tokenKind {
case .keyword(.private),
.keyword(.fileprivate),
.keyword(.internal),
.keyword(.package),
.keyword(.public),
.keyword(.open):
return true
default:
return false
}
}

var isAtLeastInternal: Bool {
switch self.name.tokenKind {
case .keyword(.private): false
case .keyword(.fileprivate): false
case .keyword(.internal): true
case .keyword(.package): true
case .keyword(.public): true
case .keyword(.open): true
default: false
}
}

var isAtLeastPackage: Bool {
switch self.name.tokenKind {
case .keyword(.private): false
case .keyword(.fileprivate): false
case .keyword(.internal): true
case .keyword(.package): true
case .keyword(.public): true
case .keyword(.open): true
default: false
}
}
}

extension WithModifiersSyntax {
var explicitAccessControl: DeclModifierSyntax? {
return self.modifiers.first { modifier in
modifier.isAccessControl
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ public enum EnumEmitStyle: String, Codable {
public struct ExportedEnum: Codable, Equatable {
public let name: String
public let swiftCallName: String
public let explicitAccessControl: String?
public let cases: [EnumCase]
public let rawType: String?
public let namespace: [String]?
Expand All @@ -121,13 +122,15 @@ public struct ExportedEnum: Codable, Equatable {
public init(
name: String,
swiftCallName: String,
explicitAccessControl: String?,
cases: [EnumCase],
rawType: String?,
namespace: [String]?,
emitStyle: EnumEmitStyle
) {
self.name = name
self.swiftCallName = swiftCallName
self.explicitAccessControl = explicitAccessControl
self.cases = cases
self.rawType = rawType
self.namespace = namespace
Expand Down Expand Up @@ -172,6 +175,7 @@ public struct ExportedFunction: Codable {
public struct ExportedClass: Codable {
public var name: String
public var swiftCallName: String
public var explicitAccessControl: String?
public var constructor: ExportedConstructor?
public var methods: [ExportedFunction]
public var properties: [ExportedProperty]
Expand All @@ -180,13 +184,15 @@ public struct ExportedClass: Codable {
public init(
name: String,
swiftCallName: String,
explicitAccessControl: String?,
constructor: ExportedConstructor? = nil,
methods: [ExportedFunction],
properties: [ExportedProperty] = [],
namespace: [String]? = nil
) {
self.name = name
self.swiftCallName = swiftCallName
self.explicitAccessControl = explicitAccessControl
self.constructor = constructor
self.methods = methods
self.properties = properties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@

@JS func setTSDirection(_ direction: TSDirection)
@JS func getTSDirection() -> TSDirection

@JS public enum PublicStatus {
case success
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@
@JS func takeGreeter(greeter: Greeter) {
print(greeter.greet())
}

@JS public class PublicGreeter {}
@JS package class PackageGreeter {}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ export enum TSDirection {
West = 3,
}

export const PublicStatus: {
readonly Success: 0;
};
export type PublicStatus = typeof PublicStatus[keyof typeof PublicStatus];

export type Exports = {
setDirection(direction: Direction): void;
getDirection(): Direction;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ export const TSDirection = {
West: 3,
};

export const PublicStatus = {
Success: 0,
};


export async function createInstantiator(options, swift) {
let instance;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,18 @@ export interface Greeter extends SwiftHeapObject {
changeName(name: string): void;
name: string;
}
export interface PublicGreeter extends SwiftHeapObject {
}
export interface PackageGreeter extends SwiftHeapObject {
}
export type Exports = {
Greeter: {
new(name: string): Greeter;
}
PublicGreeter: {
}
PackageGreeter: {
}
takeGreeter(greeter: Greeter): void;
}
export type Imports = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ export async function createInstantiator(options, swift) {
const obj = Greeter.__construct(pointer);
return swift.memory.retain(obj);
};
importObject["TestModule"]["bjs_PublicGreeter_wrap"] = function(pointer) {
const obj = PublicGreeter.__construct(pointer);
return swift.memory.retain(obj);
};
importObject["TestModule"]["bjs_PackageGreeter_wrap"] = function(pointer) {
const obj = PackageGreeter.__construct(pointer);
return swift.memory.retain(obj);
};

},
setInstance: (i) => {
Expand Down Expand Up @@ -126,8 +134,22 @@ export async function createInstantiator(options, swift) {
swift.memory.release(valueId);
}
}
class PublicGreeter extends SwiftHeapObject {
static __construct(ptr) {
return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PublicGreeter_deinit, PublicGreeter.prototype);
}

}
class PackageGreeter extends SwiftHeapObject {
static __construct(ptr) {
return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PackageGreeter_deinit, PackageGreeter.prototype);
}

}
return {
Greeter,
PublicGreeter,
PackageGreeter,
takeGreeter: function bjs_takeGreeter(greeter) {
instance.exports.bjs_takeGreeter(greeter.pointer);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,20 @@
"emitStyle" : "tsEnum",
"name" : "TSDirection",
"swiftCallName" : "TSDirection"
},
{
"cases" : [
{
"associatedValues" : [

],
"name" : "success"
}
],
"emitStyle" : "const",
"explicitAccessControl" : "public",
"name" : "PublicStatus",
"swiftCallName" : "PublicStatus"
}
],
"functions" : [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,37 @@ extension TSDirection {
}
}

extension PublicStatus {
@_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 {
return bridgeJSRawValue
}
@_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn(_ value: Int32) -> PublicStatus {
return PublicStatus(bridgeJSRawValue: value)!
}
@_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ value: Int32) -> PublicStatus {
return PublicStatus(bridgeJSRawValue: value)!
}
@_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() -> Int32 {
return bridgeJSRawValue
}

private init?(bridgeJSRawValue: Int32) {
switch bridgeJSRawValue {
case 0:
self = .success
default:
return nil
}
}

private var bridgeJSRawValue: Int32 {
switch self {
case .success:
return 0
}
}
}

@_expose(wasm, "bjs_setDirection")
@_cdecl("bjs_setDirection")
public func _bjs_setDirection(direction: Int32) -> Void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,28 @@
}
],
"swiftCallName" : "Greeter"
},
{
"explicitAccessControl" : "public",
"methods" : [

],
"name" : "PublicGreeter",
"properties" : [

],
"swiftCallName" : "PublicGreeter"
},
{
"explicitAccessControl" : "package",
"methods" : [

],
"name" : "PackageGreeter",
"properties" : [

],
"swiftCallName" : "PackageGreeter"
}
],
"enums" : [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,44 @@ extension Greeter: ConvertibleToJSValue, _BridgedSwiftHeapObject {
#endif
return .object(JSObject(id: UInt32(bitPattern: _bjs_Greeter_wrap(Unmanaged.passRetained(self).toOpaque()))))
}
}

@_expose(wasm, "bjs_PublicGreeter_deinit")
@_cdecl("bjs_PublicGreeter_deinit")
public func _bjs_PublicGreeter_deinit(pointer: UnsafeMutableRawPointer) {
Unmanaged<PublicGreeter>.fromOpaque(pointer).release()
}

extension PublicGreeter: ConvertibleToJSValue, _BridgedSwiftHeapObject {
public var jsValue: JSValue {
#if arch(wasm32)
@_extern(wasm, module: "TestModule", name: "bjs_PublicGreeter_wrap")
func _bjs_PublicGreeter_wrap(_: UnsafeMutableRawPointer) -> Int32
#else
func _bjs_PublicGreeter_wrap(_: UnsafeMutableRawPointer) -> Int32 {
fatalError("Only available on WebAssembly")
}
#endif
return .object(JSObject(id: UInt32(bitPattern: _bjs_PublicGreeter_wrap(Unmanaged.passRetained(self).toOpaque()))))
}
}

@_expose(wasm, "bjs_PackageGreeter_deinit")
@_cdecl("bjs_PackageGreeter_deinit")
public func _bjs_PackageGreeter_deinit(pointer: UnsafeMutableRawPointer) {
Unmanaged<PackageGreeter>.fromOpaque(pointer).release()
}

extension PackageGreeter: ConvertibleToJSValue, _BridgedSwiftHeapObject {
package var jsValue: JSValue {
#if arch(wasm32)
@_extern(wasm, module: "TestModule", name: "bjs_PackageGreeter_wrap")
func _bjs_PackageGreeter_wrap(_: UnsafeMutableRawPointer) -> Int32
#else
func _bjs_PackageGreeter_wrap(_: UnsafeMutableRawPointer) -> Int32 {
fatalError("Only available on WebAssembly")
}
#endif
return .object(JSObject(id: UInt32(bitPattern: _bjs_PackageGreeter_wrap(Unmanaged.passRetained(self).toOpaque()))))
}
}
Loading